Skip to content
Python Basics: Discover Local Module Names with pkgutil

Python Basics: Discover Local Module Names with pkgutil

October 27, 2026

A diagnostic tool can list candidate modules without importing each one. pkgutil.iter_modules inspects a search path and reports names plus a package flag. A fixture containing import-time exceptions demonstrates the boundary between discovery and execution.

Prepare a scratch directory

Tested on Linux with CPython 3.12.14. You need the named runtime, Bash, a text editor, and the standard mktemp, rm, and rmdir utilities. Run Python without -O so the assertions remain enabled. Any generated fixture files live inside an automatically cleaned temporary directory.

Keep using the same terminal. Stop if setup fails, before saving the source:

start_dir="$PWD"
work_dir="$(mktemp -d)" || exit 1
cd -- "$work_dir" || exit 1

Run the example

Save this as demonstrate-pkgutil.py:

import pkgutil
import sys
from pathlib import Path
from tempfile import TemporaryDirectory

with TemporaryDirectory() as directory:
    root = Path(directory)
    (root / "example_leaf.py").write_text(
        'raise RuntimeError("do not import")\n', encoding="utf-8")
    package = root / "example_group"
    package.mkdir()
    (package / "__init__.py").write_text(
        'raise RuntimeError("do not import package")\n', encoding="utf-8")
    (package / "child.py").write_text("VALUE = 3\n", encoding="utf-8")
    found = sorted((item.name, item.ispkg) for item in pkgutil.iter_modules([directory]))
    assert found == [("example_group", True), ("example_leaf", False)]
    for name, is_package in found:
        print(name, "package=" + str(is_package))
    assert "example_leaf" not in sys.modules
    assert "example_group" not in sys.modules
    children = list(pkgutil.iter_modules([str(package)], prefix="example_group."))
    assert [(item.name, item.ispkg) for item in children] == [("example_group.child", False)]
    print("Explicit child path:", children[0].name)
    print("Fixture modules imported:", False)

Run it:

python3 -B demonstrate-pkgutil.py

Observed output:

example_group package=True
example_leaf package=False
Explicit child path: example_group.child
Fixture modules imported: False

Read the result

The first call inspects only the supplied top-level directory, so it finds the package and leaf module but not the child inside the package. The second call supplies the child directory directly. Its prefix changes the reported name; it does not import the parent. Neither deliberate import-time exception runs, and the module-cache assertions verify the fixture names were not imported.

Apply it carefully

iter_modules is shallow. walk_packages is recursive but needs to import packages to discover their paths, so it has different side effects. Discovery also depends on importer support; it is not an exhaustive inventory of everything importable, especially with namespace packages or custom import hooks. Do not treat a discovered name as permission to load untrusted code.

The official reference describes the API and its options. The checks above cover the specific fixture, not every possible input or environment.

Clean up

Remove the source and then the empty practice directory:

rm -- demonstrate-pkgutil.py
cd -- "$start_dir" || exit 1
rmdir -- "$work_dir"
unset start_dir work_dir

If the directory is not empty, inspect the remaining files rather than deleting unknown contents.

Acknowledgements

This post was created with assistance from AI.

Last updated on