local variable referenced before assignment

Discussion in 'Python' started by johngilbrough, Apr 4, 2010.

  1. I cannot make sense of what's happening here ... I'm getting the
    following error:

    initializing last modified time
    /home/john/Dropbox/Projects/python/scripts/src 29
    referencing last modified time
    /home/john/Dropbox/Projects/python/scripts/src 29
    referencing last modified time
    Traceback (most recent call last):
    File "/home/john/Dropbox/Projects/python/scripts/src/file-watch.py",
    line 42, in <module>
    time.sleep(10000)
    File "/home/john/Dropbox/Projects/python/scripts/src/file-watch.py",
    line 18, in handler
    if modifiedTime <> lastModifiedTime:
    UnboundLocalError: local variable 'lastModifiedTime' referenced before
    assignment

    From this logic:

    #!/usr/bin/python
    def watchFile(filePath, callback):
    ###
    # calls callback whenever file is changed
    ###
    import fcntl
    import os
    import signal
    print "initializing last modified time"
    lastModifiedTime = os.path.getmtime(filePath)

    def handler(signum, frame):
    ## this gets called twice whenever a file changes
    print filePath + " " + str(signum)
    modifiedTime = os.path.getmtime(filePath)
    print "referencing last modified time"
    if modifiedTime <> lastModifiedTime:
    lastModifiedTime = modifiedTime
    callback()

    signal.signal(signal.SIGIO, handler)
    fd = os.open(filePath, os.O_RDONLY)
    fcntl.fcntl(fd, fcntl.F_SETSIG, 0)
    fcntl.fcntl(fd, fcntl.F_NOTIFY,
    0
    | fcntl.DN_MODIFY
    | fcntl.DN_CREATE
    | fcntl.DN_MULTISHOT # what's this?
    )

    def doSomething ():
    print "in doSomething"

    import time
    filePath = "/home/john/Dropbox/Projects/python/scripts/src"

    watchFile (filePath, doSomething)

    while True:
    # needed to keep this alive - gets interrupted when a file changes
    time.sleep(10000)
    print "*",
    johngilbrough, Apr 4, 2010
    #1
    1. Advertising

  2. * johngilbrough:
    > I cannot make sense of what's happening here ... I'm getting the
    > following error:
    >
    > initializing last modified time
    > /home/john/Dropbox/Projects/python/scripts/src 29
    > referencing last modified time
    > /home/john/Dropbox/Projects/python/scripts/src 29
    > referencing last modified time
    > Traceback (most recent call last):
    > File "/home/john/Dropbox/Projects/python/scripts/src/file-watch.py",
    > line 42, in <module>
    > time.sleep(10000)
    > File "/home/john/Dropbox/Projects/python/scripts/src/file-watch.py",
    > line 18, in handler
    > if modifiedTime <> lastModifiedTime:
    > UnboundLocalError: local variable 'lastModifiedTime' referenced before
    > assignment
    >
    > From this logic:
    >
    > #!/usr/bin/python
    > def watchFile(filePath, callback):
    > ###
    > # calls callback whenever file is changed
    > ###
    > import fcntl
    > import os
    > import signal
    > print "initializing last modified time"
    > lastModifiedTime = os.path.getmtime(filePath)
    >
    > def handler(signum, frame):
    > ## this gets called twice whenever a file changes
    > print filePath + " " + str(signum)
    > modifiedTime = os.path.getmtime(filePath)
    > print "referencing last modified time"
    > if modifiedTime <> lastModifiedTime:
    > lastModifiedTime = modifiedTime
    > callback()


    Since 'handler' has an assignment to 'lastModifiedTime' that name becomes the
    name of a local variable. It's not the execution of the assignment that creates
    the variable. It's the /presence/ of the assignment (this helps the compiler
    generate code that allocates all local variables on entry to the function).

    There are a couple of ways around.

    (1)
    At least in Py3 you can declare the variable as 'global', like this:

    global lastModifiedTime

    within the function.

    (2)
    Or, you can apply some indirection, which is nearly always a solution to any
    computer science and programming problem, and declare your variable like so:

    class Object: pass

    g = Object()
    g.lastModifiedTime = os.path.getmtime( filePath )

    Then when you assign to 'g.lastModifiedTime' in 'handler' you're not creating a
    variable, because you're assigning to an attribute of an object.

    (3)
    Best is however to recognize that you have some state (your variable) and some
    operations on that state (your callback), and that that is what objects are all
    about. I.e. wrap your logic in a class. Then 'lastModifiedTime' becomes an
    instance attribute, and 'handler' becomes a method.

    It doesn't matter that there will only ever be one object (instance) of that class.

    Classes were meant for just this sort of thing, state + operations.


    Cheers & hth.,

    - Alf
    Alf P. Steinbach, Apr 4, 2010
    #2
    1. Advertising

  3. * Stephen Hansen:
    > On 2010-04-04 15:22:48 -0700, Alf P. Steinbach said:
    >
    >> * johngilbrough:
    >>> I cannot make sense of what's happening here ... I'm getting the
    >>> following error:

    >> (1)
    >> At least in Py3 you can declare the variable as 'global', like this:
    >>
    >> global lastModifiedTime
    >>
    >> within the function.

    >
    > Actually, what you're looking for in py3 is the "nonlocal" keyword,
    > which addresses this precise situation. Using "global" would mark the
    > variable as *global* -- top-level module namespace.


    Thanks, I didn't see that. I thought it was a global.


    > nonlocal (within "handler") would make the assignment apply to the
    > enclosing scope lastModifiedTime, instead.



    Cheers,

    - Alf
    Alf P. Steinbach, Apr 4, 2010
    #3
  4. johngilbrough

    John Nagle Guest

    Alf P. Steinbach wrote:

    > Best is however to recognize that you have some state (your variable)
    > and some operations on that state (your callback), and that that is what
    > objects are all about. I.e. wrap your logic in a class. Then
    > 'lastModifiedTime' becomes an instance attribute, and 'handler' becomes
    > a method.
    >
    > It doesn't matter that there will only ever be one object (instance) of
    > that class.
    >
    > Classes were meant for just this sort of thing, state + operations.


    Yes. Functions with persistent state are generally a bad idea.

    Unfortunately, the "signal" module requires a callback parameter
    which is a plain function. So you have to send it a function,
    closure, or lambda. Here, it's being sent a closure - "handler"
    bound to the state that existed when "signal.signal" was called.

    John Nagle
    John Nagle, Apr 5, 2010
    #4
  5. johngilbrough

    Robert Kern Guest

    On 2010-04-05 12:08 PM, John Nagle wrote:
    > Alf P. Steinbach wrote:
    >
    >> Best is however to recognize that you have some state (your variable)
    >> and some operations on that state (your callback), and that that is
    >> what objects are all about. I.e. wrap your logic in a class. Then
    >> 'lastModifiedTime' becomes an instance attribute, and 'handler'
    >> becomes a method.
    >>
    >> It doesn't matter that there will only ever be one object (instance)
    >> of that class.
    >>
    >> Classes were meant for just this sort of thing, state + operations.

    >
    > Yes. Functions with persistent state are generally a bad idea.
    >
    > Unfortunately, the "signal" module requires a callback parameter
    > which is a plain function. So you have to send it a function,
    > closure, or lambda.


    Does it? The docs say that it just needs a callable object. An instance with a
    __call__() method would suffice.

    --
    Robert Kern

    "I have come to believe that the whole world is an enigma, a harmless enigma
    that is made terrible by our own mad attempt to interpret it as though it had
    an underlying truth."
    -- Umberto Eco
    Robert Kern, Apr 5, 2010
    #5
  6. On Mon, 05 Apr 2010 10:08:51 -0700, John Nagle wrote:

    > Alf P. Steinbach wrote:
    >
    >> Best is however to recognize that you have some state (your variable)
    >> and some operations on that state (your callback), and that that is
    >> what objects are all about. I.e. wrap your logic in a class. Then
    >> 'lastModifiedTime' becomes an instance attribute, and 'handler' becomes
    >> a method.
    >>
    >> It doesn't matter that there will only ever be one object (instance) of
    >> that class.
    >>
    >> Classes were meant for just this sort of thing, state + operations.

    >
    > Yes. Functions with persistent state are generally a bad idea.



    Persistent state is generally a bad idea, unless you need it. If you
    think you need it, you probably don't. But if you do actually need
    persistent state, it is better to hide it in some sort of routine
    (perhaps a function, perhaps a callable instance, perhaps something else)
    that can encapsulate the state, rather than store it in a global.



    > Unfortunately, the "signal" module requires a callback parameter
    > which is a plain function. So you have to send it a function, closure,
    > or lambda. Here, it's being sent a closure - "handler" bound to the
    > state that existed when "signal.signal" was called.



    Help on built-in function signal in module signal:

    signal(...)
    signal(sig, action) -> action

    Set the action for the given signal. The action can be SIG_DFL,
    SIG_IGN, or a callable Python object. [...]


    Doesn't seem like there is any requirement for it to be a regular
    function. Anything callable with the right signature should work, and if
    it doesn't, that's a bug.



    --
    Steven
    Steven D'Aprano, Apr 6, 2010
    #6
    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. Peter Moscatt
    Replies:
    2
    Views:
    9,800
    Peter Moscatt
    Mar 19, 2005
  2. Replies:
    2
    Views:
    5,285
    Diez B. Roggisch
    Feb 26, 2006
  3. Pete Bartonly
    Replies:
    10
    Views:
    792
    Dennis Lee Bieber
    Oct 25, 2007
  4. Gabriel Rossetti
    Replies:
    3
    Views:
    950
    Hrvoje Niksic
    Dec 9, 2009
  5. ch1zra
    Replies:
    9
    Views:
    3,087
    danieldelay
    Jun 8, 2010
Loading...

Share This Page