5

I was wrestling with a weird "UnboundLocalError: local variable referenced before assignment" issue in a multi-submodule project I am working on and slimmed it down to this snippet (using the logging module from standard library):

import logging

def foo():
    logging.info('foo')

def bar():
    logging.info('bar')
    if False:
        import logging.handlers
        # With these alternatives things work:
        # import logging.handlers as logginghandlers
        # from logging.handlers import SocketHandler

logging.basicConfig(level=logging.INFO)
foo()
bar()

Which has this output (I tried python 2.7 and 3.3):

INFO:root:foo
Traceback (most recent call last):
  File "import-test01.py", line 16, in <module>
    bar()
  File "import-test01.py", line 7, in bar
    logging.info('bar')
UnboundLocalError: local variable 'logging' referenced before assignment

Apparently the presence of an import statement inside a function hides already existing variables with the same name in the function scope, even if the import is not executed.

This feels counter-intuitive and non-pythonic. I tried to find some information or documentation about this, without much success thus far. Has someone more information/insights about this behaviour?

thanks

2
  • One Way to deal with masking issue is by using 'as' when importing.... if False: import logging.handlers as handler Commented Jun 19, 2014 at 13:24
  • @Stefaan It looks like good reason to follow the PEP 008 advice on imports: "Imports are always put at the top of the file, just after any module comments and docstrings, and before module globals and constants." I know, there are often reasons to break the rules (at your own risk). Commented Jun 19, 2014 at 13:27

2 Answers 2

4

The problem you're having is just a restatement of the same ol' way python deals with locals masking globals.

to understand it, import foo is (approximately) syntactic sugar for:

foo = __import__("foo")

thus, your code is:

x = 1
def bar():
    print x
    if False:
        x = 2

since the name logging appears on the left side of an assignment statement inside bar, it is taken to be a local variable reference, and so python won't look for a global by the same name in that scope, even though the line that sets it can never be called.

The usual workarounds for dealing with globals apply: use another name, as you have found, or:

def bar():
    global logging
    logging.info('bar')
    if False:
        import logging.handlers

so that python won't think logging is local.

Sign up to request clarification or add additional context in comments.

Comments

1

By that import statement you introduce logging as a local variable in the function and you call that local variable before it's initialized.

def bar():
    import logging.handlers
    print locals()

>>> foo()
{'logging': <module 'logging' from '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.pyc'>}

3 Comments

Indeed, but it's only in locals() after execution of the import statement, which is totally as expected. My problem is that "logging" is not in locals() before the import statement (not necessarily to be executed), it is in globals(), and still I get UnboundLocalError, as if was a still undefined local variable.
oh I see, I considered it as a common knowledge: Python compiles a function before its execution and there's a tuple for all local variables presented in that function. So when it tries to reach for that variable into this tuple it's not there yet -> UnboundLocalError
Yeah, with IfLoop's answer (about an import being equivalent with a local variable assignment) I now understand what you mean, thanks.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.