[Tutor] Variables

Martin A. Brown martin at linux-ip.net
Wed Aug 3 01:33:58 EDT 2016


Hello Palmer,

>> I'm trying to write a program w/ python that runs once a day and every time
>> it does it adds 20 to a variable. How do I do this so it doesn't reset the
>> variable to the original value every time I run it?
>
>You have to read the variable from a file saved to disk, add 20, 
>then write back to the file.
>
>Does that answer your question? Do you need help reading and 
>writing files?

Steven has given you a pointer in the right direction.

Given your description, it sounds like you do not know how to make a 
program/computer remember the (incrementing) number (value) between 
program runs.  So, to my ear, it sounds like each time your program 
runs, it starts with a value that is 0 (or nonexistent).

So, you need to come up with a strategy for persistence.  You need 
to save the final value of your variable at the end of the program, 
in short, you need to write that to disk (somehow).

The most basic way is to write to a simple file, but....there by 
dragons.  Files disappear.  Files become corrupt.  Files sometimes 
change permissions.  A file that was there a moment ago may not be 
any longer.  So, code that accesses files needs to be 
...risk-averse.

With the protections of programming languages, you often do not have 
to deal in memory with the race conditions that plague access to 
filesystems.  People sometimes use databases, but understanding the 
general race conditions that come with persistent storage will help 
you whether you are trying to store values in a database or a 
filesystem.

Below is a Python2 (and Python3) program I have written to 
demonstrate how to read a variable from a file, increment the 
variable and store that variable again.  It may seem a bit long at 
first, but I'm hoping from the function names that you can see how a 
simple function like increment()

  def increment(var, step):
      return var + step

...can be a bit trickier when you need to save that data between 
invocations of the program.  The program can be run three different 
ways:

  # -- put data in nonexistent file, should end up with a '1'
  #
  python proggie.py stored-variable.txt

  # -- put data in existing file, should end up with '2'
  #
  python proggie.py stored-variable.txt

  # -- put data in existing file, should end up with '22'  (2 + 20)
  #
  python proggie.py stored-variable.txt 20     # -- increment by 20

  # -- increment by 20 and sleep 1 second between read/write cycle
  #    first time should get 42, then 62 ....
  #
  python proggie.py stored-variable.txt 20 1   # -- sleep 

Enjoy, and I hope this little program is instructive,

-Martin



#! /usr/bin/python
#
# -- read the contents of a file
#    stuff into a variable
#    increment the variable
#    write to a file

from __future__ import print_function

import os
import sys
import errno
import time

import logging
logging.basicConfig(stream=sys.stderr, level=logging.INFO)
logger = logging.getLogger(__name__)


def increment(var, step):
    return var + step


def read_value(fname):
    '''open a file, read an integer, close file, return integer

    args:  filename
    rtrn:  an integer (integer representation of file contents)
    s-fx:  ValueError if contents are not an integer
           IOError if cannot read file
    '''
    with open(fname, 'r') as f:
        var = int(f.read())
    return var


def read_value_or_return_default(fname, default=0):
    '''return value from file or a default if file does not exist (ENOENT)

    args: filename (and optional default value)
    rtrn: a value
    s-fx: catches an ENOENT type of IOError and returns default
          IOError if permissions or other issue
    '''
    try:
        var = read_value(fname)
        logger.info('Read      %10d (file %s existed)', var, fname)
    except IOError as e:
        if e.errno == errno.ENOENT:
            var = default
            logger.info('New value %10d (file %s does not exist)', var, fname)
        else:
            raise
    return var


def write_value(fname, var):
    '''open a file, write contents of a var, close file

    args:  filename
    rtrn:  None
    s-fx:  IOError if cannot write file
    '''
    with open(fname, 'w') as f:
        f.write(str(var))
    logger.info('Stored    %10d (file %s tempfile)', var, fname)


def ris(fname, step):
    '''read, increment, store in tempfile, atomically replace permanent file
    squawk to logging when file has been swapped into place
    '''
    var = read_value_or_return_default(fname, default=0)
    var = increment(var, step)
    newfname = fname + '.' + str(time.time())
    write_value(newfname, var)
    os.rename(newfname, fname)
    logger.info('Swapped   %10d (file %s)', var, fname)


def cli_ris(fname, step=1):
    ris(fname, step)
    return os.EX_OK  # -- return success to operating system


def cli_loop_ris(fname, step=1, sleeptime=1):
    '''run forever reading, incrementing and saving variable to a file'''
    while True:
        ris(fname, step)
        time.sleep(sleeptime)
    return os.EX_OK  # -- return success to operating system


if __name__ == '__main__':
    proggie, args = sys.argv[0], sys.argv[1:]
    if len(args) == 1:
        fname = args.pop()
        sys.exit(cli_ris(fname))
    elif len(args) == 2:
        fname, step = args
        step = int(step)
        sys.exit(cli_ris(fname, step=step))
    elif len(args) == 3:
        fname, step, sleeptime = args
        step, sleeptime = int(step), int(sleeptime)
        sys.exit(cli_loop_ris(fname, step=step, sleeptime=sleeptime))
    else:
        sys.exit('Usage: %s <filename> [<step> [<sleeptime>]]' % (proggie))

# -- end of file



-- 
Martin A. Brown
http://linux-ip.net/


More information about the Tutor mailing list