Skip to content

🛎 Test-import all modules 🛎

Running impall as a unit test

Just inherit from the base class and it will automatically find and import each file, like this.

import impall

class ImpAllTest(impall.ImpAllTest):
    pass

(You can copy this file into your project if you like.)

Tests are customized by overriding one of these following properties in the derived class.

CLEAR_SYS_MODULES, EXCLUDE, FAILING, INCLUDE, MODULES, PATHS,
RAISE_EXCEPTIONS, and WARNINGS_ACTION.

For example, to turn warnings into errors, set the property WARNINGS_ACTION in the derived class definition, like this.

class ImpAllTest(impall.ImpAllTest):
    WARNINGS_ACTION = 'error'

Running impall as a command-line utility

$ impall.py --warnings_action=error
$ impall.py -w error

The properties INCLUDE, EXCLUDE, and PROJECT_PATH can be lists of strings, or a string separated with colons like 'foo.mod1:foo.mod2'

INCLUDE and EXCLUDE match modules, and also allow * as a wildcard. A single * matches any module segment, and a double ** matches any remaining segments. For example,

INCLUDE = 'foo', 'bar.', 'baz.*'

  • matches foo but not foo.foo
  • matches bar.foo but not bar or bar.foo.bar
  • matches baz.foo as well as baz.foo.bar but not baz

A note on side-effects

to reduce side-effects, sys.modules is restored to its original condition after each import if CLEAR_SYS_MODULES is true, but there might be other side-effects from loading some specific module.

Use the EXCLUDE property to exclude modules with undesirable side effects. In general, it is probably a bad idea to have significant side-effects just from loading a module.

import_file(path)

Given a path to a file or directory, imports it from the correct root and returns the module

Source code in impall/impall.py
267
268
269
270
271
272
273
274
275
276
277
278
279
280
def import_file(path):
    """
    Given a path to a file or directory, imports it from the correct root
    and returns the module
    """

    root, module_path = path_to_import(path)
    old_path = sys.path[:]
    sys.path.insert(0, root or '.')

    try:
        return importlib.import_module(module_path)
    finally:
        sys.path[:] = old_path

path_to_import(path) cached

Return a (path, module) pair that allows you to import the Python file or directory at location path

Source code in impall/impall.py
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
@functools.lru_cache()
def path_to_import(path):
    """
    Return a (path, module) pair that allows you to import the Python file or
    directory at location path
    """
    parts = []

    if not os.path.exists(path):
        raise FileNotFoundError(path)

    path = str(path)  # Might be a Path
    if path.endswith('.py'):
        path = path[:-3]

    def isdir(p):
        return os.path.isdir(p) and not os.path.exists(p + '.py')

    while not isdir(path) or _is_python_dir(path):
        path, part = os.path.split(path)
        if not part:
            path and parts.append(path)
            break
        parts.append(part)

    return path, '.'.join(reversed(parts))

About this project