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)