Reputation: 54163
With the following code:
foo.py
:
#!/usr/bin/env python
import package
package.go()
package/__init__.py
:
from __future__ import absolute_import
import logging
from .logging import config
def go():
config(level=logging.DEBUG)
logging.getLogger().debug('this is a test')
package/logging.py
:
from __future__ import absolute_import
import logging
def config(*args, **kwargs):
return logging.basicConfig(*args, **kwargs)
I get the following error:
$ ./foo.py
Traceback (most recent call last):
File "./foo.py", line 3, in <module>
package.go()
File "/tmp/test/package/__init__.py", line 5, in go
config(level=logging.DEBUG)
AttributeError: 'module' object has no attribute 'DEBUG'
It is as if the from .logging import config
line is importing .logging
as logging
even though I don't want that.
If I swap the import lines in package/__init__.py
so that it looks like this:
from __future__ import absolute_import
from .logging import config
import logging
def go():
config(level=logging.DEBUG)
logging.getLogger().debug('this is a test')
then it works:
$ ./foo.py
DEBUG:root:this is a test
What's wrong with the original version? Or is this a bug in Python 2.7.8?
Upvotes: 2
Views: 625
Reputation: 1122182
Modules inside packages, once imported, are set as attributes on their parent package.
The moment you import .logging
, it is added to the package
namespace, e.g. in the globals()
dictionary of __init__
in the package
directory.
By swapping the imports, you then replaced the logging
global in the __init__
module namespace with the top-level package again; first the sub-module was imported and added to the globals, then the logging
package was imported, replacing global.
Demo:
$ mkdir package
$ cat > package/__init__.py << EOF
> from __future__ import absolute_import
> from .logging import foo
> print globals().keys()
> print logging
> import logging
> print globals().keys()
> print logging
> EOF
$ cat > package/logging.py << EOF
> foo = 'bar'
> print 'imported package.logging'
> EOF
$ python -c 'import package'
imported package.logging
['logging', '__builtins__', '__file__', 'absolute_import', '__package__', '__path__', '__name__', 'foo', '__doc__']
<module 'package.logging' from 'package/logging.pyc'>
['logging', '__builtins__', '__file__', 'absolute_import', '__package__', '__path__', '__name__', 'foo', '__doc__']
<module 'logging' from '/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/lib/python2.7/logging/__init__.py'>
Right after importing foo
from the .logging
package, the logging
global shows up. Importing the top-level logging
package then rebinds the name again.
Upvotes: 3