ModuleNotFoundError when using importlib.import_module

Viewed 11102

I have the following folder structure and I have a test method in util.py. When the util method is run, I see an error with a module that is imported within the module where I am trying to get all classes.

Parent
--report <dir>
----__init__.py
----AReport.py
----names_list.py
--util.py

util.py

import inspect
import importlib
import importlib.util

def get_class_names(fileName):
    for name, cls in inspect.getmembers(importlib.import_module(fileName, package='report'), inspect.isclass):
        print(name, cls)

if __name__ == '__main__':
    get_class_names('report.names_list')

names_list.py

from AReport import AReport

class Team:
    name = ""
    def __init__(self, name):
        self.name = name

class Names_List(AReport):
    def __init__(self, name=None):
        AReport.__init__(self, name)

    def test(self):
        print('In test')        

AReport.py

from abc import ABCMeta, abstractmethod

class AReport(metaclass=ABCMeta):
    def __init__(self, name=None):
        if name:
            self.name = name

    def test(self):
        pass

When I run my test method from util, I get the following error:

ModuleNotFoundError: No module named AReport
2 Answers

Assuming you did not change anything with sys.path or with PYTHONPATH, the problem is that AReport module is not "visible" from util.py.

You can check this by adding this at the top of util.py:

import sys
print(sys.path)

That's going to print out a list of all paths where the interpreter will look for modules. You'll see that only the path to the Parent module is there, because this is where the util.py was run from. This is explained in The Module Search Path documentation:

When a module named spam is imported, the interpreter first searches for a built-in module with that name. If not found, it then searches for a file named spam.py in a list of directories given by the variable sys.path. sys.path is initialized from these locations:

  • The directory containing the input script (or the current directory when no file is specified).
  • PYTHONPATH (a list of directory names, with the same syntax as the shell variable PATH).
  • The installation-dependent default.

When you run util.py from the Parent directory (= "The directory containing the input script"), and you do

from AReport import AReport

it will look for a AReport module from the Parent directory, but it's not there because only the report package is directly under the /path/to/Parent directory. That is why Python raises the ModuleNotFoundError. If you do instead

from report.AReport import AReport

it's going to work because the report package is under /path/to/Parent.

If you want to avoid the report. prefix when importing, one option is to add the report package to the sys.path on util.py:

import sys
sys.path.append("./report")

print(sys.path)
# should now show the /path/to/Parent/report on the list

Then your from AReport import will now work. Another option is to add /path/to/Parent/report to your PYTHONPATH environment variable before running util.py.

export PYTHONPATH=$PYTHONPATH:/path/to/Parent/report

I usually go with the PYTHONPATH option for tests, so that I don't need to modify the code.

try:

from report.AReport import AReport
Related