Skip to main content

Diagnose which finders handle imports and how sys.meta_path changes

Project description

metapathology

Diagnose Python import hooks without changing import outcomes.

PyPI version Supported Python versions Tests Documentation Quality gate status

[!IMPORTANT] This project is mostly AI generated (for now). I do understand what it does and how it works, but I only skimmed the code. I can attest the explanations in README is accurate though.

Some Python packages customize how imports work: pytest rewrites test assertions, editable installs redirect imports to your source tree, beartype instruments modules as they load. These customizations plug into Python's import system through sys.meta_path and sys.path_hooks — and when two of them are active at once, one can silently prevent the other from ever seeing a module. The symptom is usually confusing: a feature quietly does nothing, or a module that clearly exists fails to import.

metapathology runs your program and reports what the import system actually did:

  • which finder located each imported module;
  • where code changed sys.meta_path, sys.path_hooks, or sys.path_importer_cache, with a stack trace; and
  • which modules were found without going through the usual sys.path search.

It only observes. It never loads a module, returns a spec, or changes which finder wins, and everything it installs is removed on exit (except a CPython audit hook, which becomes a no-op because Python cannot unregister it).

Full documentation: glinte.github.io/metapathology.

Usage

Requires CPython 3.10+. No runtime dependencies — it works even in an environment where other packages fail to import.

$ pip install metapathology
$ python -m metapathology myscript.py --my-args
$ python -m metapathology -m pytest tests/
$ python -m metapathology                      # monitored interactive interpreter

Your program runs normally; the report is printed to standard error when it exits. Use --report diagnostic.json to write a JSON file instead. Prefer python -m metapathology over the metapathology command so the tool runs in the same interpreter and virtual environment as your program.

If you cannot wrap the program (a notebook, an embedded interpreter, a conftest.py), call the library API:

import metapathology

metapathology.install()  # as early as possible

See Using metapathology for all CLI options, report files, environment variables, and lifecycle control.

Reading the report

A real example: beartype#556, where beartype.claw silently did nothing in a scikit-build-core editable install. The report shows why in its first comparison — the editable-install finder found the module first, so beartype's path hook never saw it:

== metapathology report ==
target outcome: completed (exit status 0)
verdict: no problems found; some modules were found by a custom finder instead of the standard path search — listed below for review
...
-- modules found by a custom finder (1) --
'myproject':
    during the run: ScikitBuildRedirectingFinder, loader _ScikitBuildLoaderWrapper, origin 'src\myproject\__init__.py'
    standard search at report time: PathFinder, loader BeartypeSourceFileLoader, same origin
    differences: loader type
    note: this finder ran before PathFinder, so the standard path search never saw the module

-- finder calls --
ScikitBuildRedirectingFinder: called 365 times, found 1 module
    myproject

The report leads with a verdict, then numbered findings when something looks wrong, then the supporting evidence: comparisons like the one above, finder call counts, an event timeline, and stack traces for every sys.meta_path and sys.path_hooks change. Reading the report explains every section and finding category.

Overhead

Rough figures from the benchmarks (GitHub-hosted runners, CPython 3.10 and 3.14):

  • Default monitoring: imports take roughly 1.1–1.7× as long (median ~1.1× when only standard finders run, ~1.35× with a custom finder), and each import retains roughly 0.3–1 KB of memory for the report.
  • --deep diagnostics: imports take roughly 7–15× as long and retain ~4 KB each. Use deep mode only in a controlled reproduction.

Every event is kept until the report is written, so memory grows with import activity. For a long-running process, install just before the behavior of interest and call write_report() and uninstall() once it is captured.

How it works

metapathology observes imports through several cooperating mechanisms: a sys.addaudithook() callback records each import as it starts, sys.meta_path and sys.path_hooks are temporarily replaced with list subclasses that record every change with a stack trace, each finder's find_spec() method is wrapped to record whether it found the module, and sys.path_importer_cache is snapshotted at key points. At report time, modules found by a custom finder are compared with a fresh PathFinder search to reveal bypasses.

How it works walks through Python's import machinery and exactly what each mechanism can and cannot see.

Caveats

  • CPython only. Monitoring on other implementations emits a RuntimeWarning because it relies on CPython's import audit event.
  • Monitoring starts when metapathology does. Finders installed earlier by .pth files (this is how scikit-build-core's finder arrives) appear in the initial snapshot without a stack trace. An optional bootstrap can start monitoring during interpreter startup to catch some of them.
  • This is a debugging tool. It temporarily modifies sys.meta_path and sys.path_hooks; do not leave it enabled in production.
  • Reports contain paths, command lines, and stack file names — review before sharing.

See Limitations for the complete list of observation boundaries. For frozen executables (PyInstaller, Nuitka, cx_Freeze) and embedded interpreters, see the frozen application guide.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

metapathology-0.4.3.tar.gz (94.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

metapathology-0.4.3-py3-none-any.whl (101.5 kB view details)

Uploaded Python 3

File details

Details for the file metapathology-0.4.3.tar.gz.

File metadata

  • Download URL: metapathology-0.4.3.tar.gz
  • Upload date:
  • Size: 94.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.13 {"installer":{"name":"uv","version":"0.11.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for metapathology-0.4.3.tar.gz
Algorithm Hash digest
SHA256 ced95ee147d19cc16c10b95b51201e2207bf7ea0603ee2b543d0a4b08cdc2239
MD5 1d6b449cff9a2ee4bdef7e6635037a61
BLAKE2b-256 158c91cfdb35f088e5c10448efc413d763ab07cb33c1e7f96dca54bfb5fd6307

See more details on using hashes here.

File details

Details for the file metapathology-0.4.3-py3-none-any.whl.

File metadata

  • Download URL: metapathology-0.4.3-py3-none-any.whl
  • Upload date:
  • Size: 101.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.13 {"installer":{"name":"uv","version":"0.11.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for metapathology-0.4.3-py3-none-any.whl
Algorithm Hash digest
SHA256 64029b4cf4b6f48001261d78144b68245febd1227c5d9fdd93f23173fdf569ba
MD5 9bbf0f9bf95f592bf30982d52c1b77cb
BLAKE2b-256 7d81bd447644d41e41f1b597cb788cf89aa7287d4458f5a454cca375960a5fec

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page