Import a Python module dynamically by name or path

Use importlib for runtime module names, load a trusted file with a checked module spec, and understand discovery, caching, and reload limits.

Best practices
Python programming and application logging

Use importlib.import_module() when the module's name is known only at runtime:

import importlib

itertools = importlib.import_module("itertools")
path_module = importlib.import_module("os.path")
print(path_module.basename("/tmp/example.txt"))  # example.txt

The returned object is the requested module. Normal import search paths and caching still apply, and importing a module executes its initialization code.

Check whether a module can be found

import importlib.util

spec = importlib.util.find_spec("itertools")
print(spec is not None)

A spec indicates that the import machinery can locate a module; it does not prove that executing the module will succeed. For a dotted name, find_spec() can import the parent package, so it is not a side-effect-free check in every case.

Load a trusted Python file

import importlib.util
import sys
from pathlib import Path

def load_module(name, path):
    if name in sys.modules:
        raise ValueError(f"Module name already loaded: {name}")
    spec = importlib.util.spec_from_file_location(name, Path(path))
    if spec is None or spec.loader is None:
        raise ImportError(f"No loader for {path}")
    module = importlib.util.module_from_spec(spec)
    sys.modules[name] = module
    try:
        spec.loader.exec_module(module)
    except BaseException:
        sys.modules.pop(name, None)
        raise
    return module

# Use a trusted existing source file:
# module = load_module("my_plugin", "/path/to/my_plugin.py")

Registering the module before execution supports code that looks itself up during initialization. The cleanup removes that registration if execution fails; it cannot undo arbitrary side effects or every dependency imported by the file. The broad exception catch is used only for cleanup and immediately re-raises.

File-path loading does not automatically create the package context needed by relative imports. For packaged plugins, normal named imports are usually easier to maintain. Never load an untrusted file as if importing were a sandbox.

Discover modules in a plugin package

When plugins is a trusted importable package whose plugin modules expose register():

import importlib
import pkgutil
import plugins

for _, name, _ in pkgutil.iter_modules(plugins.__path__):
    module = importlib.import_module(f"plugins.{name}")
    module.register()

This discovers immediate children, including subpackages; it is not a recursive search through every nested file. Decide whether a failed plugin should stop startup or be logged and skipped, and validate its expected interface.

FAQ

Why not use import()? With its default arguments, __import__("package.module") returns the top-level package. import_module() returns the requested module and is easier to use here.

Can I reload it? importlib.reload() re-executes a discoverable module, but existing references, imported names, and class instances do not all change automatically. A manually loaded file may lack a discoverable spec for reload. Treat reload as an explicit development mechanism, not a complete production update system.

Which failures should I catch? ImportError includes missing-module failures, but module initialization can also raise unrelated exceptions. Preserve the original error and traceback so a broken dependency is not mistaken for a missing plugin.

References

Get a tailored plan

Contact us

Join the community

Scan with WeChat
to join the community

Try Guance

Start online and pay only for what you use.

Get started

Choose a Guance plan

Code hosting