Diagnose which finders handle imports and how sys.meta_path changes
Project description
metapathology
Diagnose Python import hooks without changing import outcomes.
[!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.
metapathology is a stdlib-only diagnostic tool for CPython imports. It
reports:
- which finder located each imported module;
- where code changed
sys.meta_path, with a stack trace; and - which modules were found without going through the usual
sys.pathandsys.path_hookssearch.
This is useful when two packages customize imports and one prevents the other
from seeing a module. The original example was
beartype#556: an editable
install finder located a module before beartype.claw's path hook could see
it.
Usage
Primary: run your program under observation, no code changes needed —
$ python -m metapathology myscript.py --my-args
$ python -m metapathology -m pytest tests/
A report is printed at exit. Prefer python -m metapathology over a bare
metapathology command: it guarantees the hooks land in the same interpreter
and venv as the code under investigation.
Library API, for when a wrapper isn't possible (notebooks, embedded
interpreters, "I can only touch conftest.py"):
import metapathology
metapathology.install() # as early as possible
There are no runtime dependencies or configuration options.
How Python finds an imported module
Python first checks whether the module is already present in sys.modules. If
it is not, Python reads sys.meta_path,
a list of objects called finders. It asks each finder in order whether it can
locate the module by calling the finder's find_spec() method.
A finder returns None when it cannot locate the module, and Python moves on
to the next finder. A finder that can locate the module returns a module
spec: a small object describing the module and how to load it. Python stops
asking other finders once it receives a spec. The Python documentation calls
this process the meta path.
One of Python's standard finders is PathFinder. It performs the familiar
search through sys.path and, as part of that search, consults
sys.path_hooks. A custom finder placed before PathFinder can return a spec
first. That may be intentional, but it also means that PathFinder and its
path hooks do not see that module. See
the path-based finder
for the full protocol.
How it works
metapathology observes that process in three ways:
- It registers a
sys.addaudithook()callback for CPython'simportaudit event. On each event, it checks whether code replaced the entiresys.meta_pathlist, for example withsys.meta_path = [...]. This event says that an import is starting; it does not say which finder will succeed. - It temporarily replaces
sys.meta_pathwith a compatiblelistsubclass. This records calls such asappend(),insert(), andremove(), including the stack trace showing where the change came from. - It wraps each finder's existing
find_spec()method. The wrapper records whether the finder returnedNoneor a module spec, then returns the same result. It does not supply a spec of its own or load a module.
At exit, the report compares the recorded result with what
PathFinder.find_spec() finds. If PathFinder cannot find the module or would
use a different kind of loader, the report notes that the normal
sys.path_hooks route was skipped.
Caveats
- CPython only (relies on the
importaudit event and import-system internals). - Monitoring begins when
metapathologyis installed. Finders added earlier by.pthfiles are shown in the initialsys.meta_pathlist, but there can be no stack trace for when they were added because that happened during Python startup. - The temporary changes to finders and
sys.meta_pathare reversed byuninstall(). Python does not provide a way to remove an audit hook, so the installed callback remains as an inactive no-op after uninstalling. - This tool changes
sys.meta_pathwhile it is running. Use it for debugging, not as part of an application's normal runtime.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file metapathology-0.1.2.tar.gz.
File metadata
- Download URL: metapathology-0.1.2.tar.gz
- Upload date:
- Size: 16.8 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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
02585f26fae1c72c095764ec5cf63f031bc1642bfa8534ba7d1b2a69df42e789
|
|
| MD5 |
40e0662c8af8c4782bf00f9dbd9a24b7
|
|
| BLAKE2b-256 |
cb370f3e9f31431e1c40368bf382f04bab46bb216cc93b81f691a85c0a13b478
|
File details
Details for the file metapathology-0.1.2-py3-none-any.whl.
File metadata
- Download URL: metapathology-0.1.2-py3-none-any.whl
- Upload date:
- Size: 19.1 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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
100980f0074797ff1991ce19c7b4a932cfde48a97cffe6d96d8d0822913da8de
|
|
| MD5 |
ce079b7ad7c0a9810850d63de2283d81
|
|
| BLAKE2b-256 |
7737d0fd1be3ce0816e3a9b59756c42537d325429e57eca103057a2448d33d42
|