local variable referenced before assignment

J

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()

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 "*",
 
A

Alf P. Steinbach

* 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
 
A

Alf P. Steinbach

* Stephen Hansen:
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
 
J

John Nagle

Alf said:
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
 
R

Robert Kern

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
 
S

Steven D'Aprano

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.
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top