Search code examples
pythonpackagepython-importpython-module

How do I load all modules under a subdirectory in Python?


I place my frequently used modules in a subdirectory lib/ and expect to load all modules into main.py by: (refer to Python: how to import from all modules in dir?)

from lib import *

but encounter the issue TypeError: 'module' object is not callable. More specifically, in main.py:

#!/usr/bin/env python

from lib import * # cause: TypeError: 'module' object is not callable
#from lib.DominatingSets import *   # it works

dominatingSets = DominatingSets()

The full exception traceback:

$ python main.py 
Traceback (most recent call last):
  File "main.py", line 6, in <module>
    dominatingSets = DominatingSets()
TypeError: 'module' object is not callable

The directories in a tree-like format.

$ tree -P '*.py' .
.
├── __init__.py
├── lib
│   ├── AnalyzeGraph.py
│   ├── AutoVivification.py
│   ├── DominatingSets.py
│   ├── __init__.py
│   ├── Output.py
│   ├── PlotGraph.py
│   ├── ProcessDatasets.py
│   └── ReadGTFS.py
├── main.py

The contents of lib/__init__.py are as follows. (refer to Loading all modules in a folder in Python)

from os.path import dirname, basename, isfile
import glob
modules = glob.glob(dirname(__file__)+"/*.py")
__all__ = [ basename(f)[:-3] for f in modules if isfile(f) and not basename(f).startswith('__')] # exclude __init__.py

Solution

  • This confusion happened, in part, because your module names are the same as the names of the classes you want to load from them. (At least, that's what makes it more confusing.) Your code does correctly load the modules that your classes are in. However, it doesn't load the classes out of those modules, and this is what you actually wanted to do.

    Because your class DominatingSets is in the module lib.DominatingSets, its full path from root is lib.DominatingSets.DominatingSets.

    from lib import *
    

    in your case will do the same thing as

    from lib import DominatingSets
    from lib import AnalyzeGraph
    # ...
    

    However,

    from lib import DominatingSets
    

    is equivalent to

    import lib.DominatingSets
    DominatingSets = lib.DominatingSets
    

    but lib.DominatingSets is a module (lib/DominatingSets.py), not the class you want.

    from lib.DominatingSets import DominatingSets
    

    is equivalent to

    import lib.DominatingSets
    DominatingSets = lib.DominatingSets.DominatingSets
    

    which is why it works: this is the class you want imported into the name DominatingSets.

    If you want to have from lib import * import all the classes in the submodules, you need to import these classes into the lib module. For example, in lib/__init__.py:

    from DominatingSets import *
    from AnalyzeGraph import *
    # ...
    

    While you're making changes, I'd suggest (as others have) using normal Python naming conventions, and have your module names in lowercase: change DominatingSets.py to dominatingsets.py. Then this code would become

    from dominatingsets import *
    from analyzegraph import *
    # ...