rfc: a self-editing script

Discussion in 'Python' started by gb345, Oct 10, 2009.

  1. gb345

    gb345 Guest

    The following fragment is from a tiny maintenance script that,
    among other things, edits itself, by rewriting the line that ends
    with '### REPLACE'.

    ######################################################################

    import re
    import fileinput

    LAST_VERSION = 'VERSION 155' ### REPLACE

    service = Service(url='http://url.to.service')

    if service.version_string == LAST_VERSION:
    sys.exit(0)

    for line in fileinput.input(sys.argv[0], inplace=True):
    if re.search(r"### REPLACE$", line):
    print ("LAST_VERSION = '%s' ### REPLACE" %
    service.version_string)
    else:
    print line,

    # ...and goes on to do more stuff...

    ######################################################################

    This script is meant to run periodically (via cron), and "do more
    stuff" whenever the fetched value of service.version_string differs
    from what it was at the time of the script's prior invocation.
    (The interval of time between such changes of value varies from
    one change to the next, but it is always of the order of several
    weeks.)

    Hence this script needs to preserve state between invocations.
    The rationale for the acrobatics with fileinput above is to make
    this script completely self-contained, by circumventing the need
    some external means (e.g. a second file, or a DB) of preserving
    state between invocations.

    Is there a better way to circumvent the requirement for an external
    repository of state information?

    G
     
    gb345, Oct 10, 2009
    #1
    1. Advertising

  2. On Fri, 09 Oct 2009 23:30:16 +0000, gb345 wrote:

    > The following fragment is from a tiny maintenance script that, among
    > other things, edits itself, by rewriting the line that ends with '###
    > REPLACE'.
    >
    > ######################################################################
    >
    > import re
    > import fileinput
    >
    > LAST_VERSION = 'VERSION 155' ### REPLACE
    >
    > service = Service(url='http://url.to.service')
    >
    > if service.version_string == LAST_VERSION:
    > sys.exit(0)
    >
    > for line in fileinput.input(sys.argv[0], inplace=True):
    > if re.search(r"### REPLACE$", line):
    > print ("LAST_VERSION = '%s' ### REPLACE" %
    > service.version_string)
    > else:
    > print line,
    >
    > # ...and goes on to do more stuff...
    >
    > ######################################################################
    >
    > This script is meant to run periodically (via cron), and "do more stuff"
    > whenever the fetched value of service.version_string differs from what
    > it was at the time of the script's prior invocation. (The interval of
    > time between such changes of value varies from one change to the next,
    > but it is always of the order of several weeks.)
    >
    > Hence this script needs to preserve state between invocations. The
    > rationale for the acrobatics with fileinput above is to make this script
    > completely self-contained, by circumventing the need some external means
    > (e.g. a second file, or a DB) of preserving state between invocations.
    >
    > Is there a better way to circumvent the requirement for an external
    > repository of state information?


    Yes -- change the requirement. What's wrong with having a separate file
    to store state?

    Self-modifying code is almost always the wrong solution, unless the
    problem is "how do I generate an unmaintainable mess?".

    But if you absolutely have to write to the program file, then append your
    data to the end of the file (as a comment) and later read that, rather
    than modifying the actual code in place. That is, you fetch the
    LAST_VERSION by reading the last non-empty line in the file, something
    like this:


    # Untested
    def get_last_version(filename):
    """Retrieves the last version number from the given filename,
    taken from the last non-empty line."""
    candidate = ''
    for line in open(filename, 'r'):
    line = line.strip()
    if line and line.startswith('#'):
    candidate = line.lstrip('# \t')
    # error checking goes here
    return candidate

    LAST_VERSION = get_last_version(sys.argv[0])

    ....
    more code goes here
    ....


    # ==================================================
    # === Version number history goes here. ===
    # === DO NOT insert any code after this point!!! ===
    # ==================================================
    # 1.0.1
    # 1.0.2a
    # 1.0.2
    # 1.0.5


    This has the added advantage that you can track the updates made to the
    version number.



    --
    Steven
     
    Steven D'Aprano, Oct 10, 2009
    #2
    1. Advertising

  3. gb345

    Guest

    Steven D'Aprano <> wrote:
    ....
    > But if you absolutely have to write to the program file, then append your
    > data to the end of the file (as a comment) and later read that, rather
    > than modifying the actual code in place. That is, you fetch the
    > LAST_VERSION by reading the last non-empty line in the file, something
    > like this:


    ....

    > # ==================================================
    > # === Version number history goes here. ===
    > # === DO NOT insert any code after this point!!! ===
    > # ==================================================
    > # 1.0.1
    > # 1.0.2a
    > # 1.0.2
    > # 1.0.5
    >
    >
    > This has the added advantage that you can track the updates made to the
    > version number.
    >


    And my experience taught me that is pays off to include also date (and
    time) of each change, preferably in the RFC 3339 format. Priceless when
    you hunt for an unnoticed bug or a crash that happened sometime in the
    past...

    e.g.:

    # 1.0.1 2006-08-07 12:34:56-06:00
    # 1.0.2a 2006-08-08 13:35:57-07:00
    # 1.0.2 2006-08-10 01:04:56-06:00

    etc...

    --
    -----------------------------------------------------------
    | Radovan Garabík http://kassiopeia.juls.savba.sk/~garabik/ |
    | __..--^^^--..__ garabik @ kassiopeia.juls.savba.sk |
    -----------------------------------------------------------
    Antivirus alert: file .signature infected by signature virus.
    Hi! I'm a signature virus! Copy me into your signature file to help me spread!
     
    , Oct 10, 2009
    #3
  4. gb345

    gb345 Guest

    In <0059c2b1$0$26930$> Steven D'Aprano <> writes:

    >But if you absolutely have to write to the program file...


    No, don't have to, beyond the urge to satisfy a very idiosyncratic
    aesthetic imperative...

    >then append your
    >data to the end of the file (as a comment) and later read that, rather
    >than modifying the actual code in place. That is, you fetch the
    >LAST_VERSION by reading the last non-empty line in the file, something
    >like this:


    ># Untested
    >def get_last_version(filename):
    > """Retrieves the last version number from the given filename,
    > taken from the last non-empty line."""
    > candidate = ''
    > for line in open(filename, 'r'):
    > line = line.strip()
    > if line and line.startswith('#'):
    > candidate = line.lstrip('# \t')
    > # error checking goes here
    > return candidate


    >LAST_VERSION = get_last_version(sys.argv[0])


    >...
    >more code goes here
    >...



    ># ==================================================
    ># === Version number history goes here. ===
    ># === DO NOT insert any code after this point!!! ===
    ># ==================================================
    ># 1.0.1
    ># 1.0.2a
    ># 1.0.2
    ># 1.0.5



    >This has the added advantage that you can track the updates made to the
    >version number.



    Thanks, these are great ideas. Just the feedback I was looking for.

    G.
     
    gb345, Oct 10, 2009
    #4
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Ralf W. Grosse-Kunstleve
    Replies:
    16
    Views:
    620
    Lonnie Princehouse
    Jul 11, 2005
  2. Ralf W. Grosse-Kunstleve
    Replies:
    18
    Views:
    621
    Bengt Richter
    Jul 11, 2005
  3. Ralf W. Grosse-Kunstleve
    Replies:
    2
    Views:
    428
    Dan Sommers
    Jul 12, 2005
  4. falcon
    Replies:
    0
    Views:
    402
    falcon
    Jul 31, 2005
  5. Bart Kastermans
    Replies:
    6
    Views:
    425
    Bart Kastermans
    Jul 13, 2008
Loading...

Share This Page