Consider the following program to load all modules in a directory:
import pkgutil
import os.path
import sys
def load_all(directory):
for loader, name, ispkg in pkgutil.walk_packages([directory]):
loader.find_module(name).load_module(name)
if __name__ == '__main__':
here = os.path.dirname(os.path.realpath(__file__))
path = os.path.join(here, 'lib')
sys.path.append(path)
load_all(path)
Now consider that we have the following files.
lib/magic/imho.py:
"""This is an empty module.
"""
lib/magic/wtf.py:
import magic.imho
print "magic contents:", dir(magic)
lib/magic/__init__.py:
"Empty package init file"
When executing the program above, it output:
magic contents: ['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__']
In other words, despite the import magic.imho
there is no attribute imho
on the package magic
which causes any later references to magic.imho
to fail.
Running the same code (more or less) directly in python shows a different output, which is the one I expected from running the loading program.
$ PYTHONPATH=lib ipython
Python 2.7.6 (default, Mar 22 2014, 22:59:38)
Type "copyright", "credits" or "license" for more information.
IPython 1.2.1 -- An enhanced Interactive Python.
? -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help -> Python's own help system.
object? -> Details about 'object', use 'object??' for extra details.
In [1]: import magic.wtf
magic contents: ['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', 'imho']
Why is there a difference?
Update: The point here is that the symbol magic.imho
does not exist inside magic.wft
despite the fact that it was explicitly imported. So, what need to be done to the custom load procedure to make it behave correctly and ensure that the symbol is visible after it has been imported in magic.wtf
.
Solution using setattr
: BartoszKP gave the solution below using an exec
to assign the value to the package. Since I do not usually like using exec
(for a lot of reasons, injections is the first one) I used setattr
and rpartition
as follows:
def load_all(directory):
for loader, name, ispkg in pkgutil.walk_packages([directory]):
module = loader.find_module(name).load_module(name)
pkg_name, _, mod_name = name.rpartition('.')
if pkg_name:
setattr(sys.modules[pkg], mod_name, module)