Skip to content

Library API

Prefer python -m metapathology when possible. It installs before target code and handles target outcomes and automatic reporting. Use the API for notebooks, embedded interpreters, or code that cannot be wrapped.

Monitor a reproduction

import metapathology

monitor = metapathology.install()
try:
    reproduce_problem()
finally:
    metapathology.write_report("diagnosis.txt")
    metapathology.uninstall()

install() is process-wide. Repeating it with the same resolved capture and analysis settings returns the existing monitor. Changing those settings while the monitor is active raises before import state is changed.

uninstall() restores ordinary list objects and removes owned finder instrumentation. Python cannot remove a sys.addaudithook() hook, so the hook remains installed but becomes inert.

For code with a clear reproduction boundary, a context manager handles cleanup:

with metapathology.monitoring() as monitor:
    reproduce_problem()

metapathology.write_report("diagnosis.json", format="json")

Nested and overlapping regions share the process monitor. A context that did not create an existing installation does not remove it.

Choose what to capture

Defaults need no object:

metapathology.install()

Change core capture or enable all detailed capture with one object:

metapathology.install(
    capture=metapathology.CaptureConfig(
        sys_path=True,
        detailed=True,
    )
)

Use a nested object only for fine-grained detailed capture:

metapathology.CaptureConfig(
    detailed=metapathology.DetailedCaptureConfig(
        loaders=True,
        import_results=True,
    )
)

Configuration records are immutable and value-comparable. Their fields are tri-state: True enables a mechanism, False disables it, and None means “use the environment or normal default.” In DetailedCaptureConfig, enabled supplies the value for detailed fields left as None.

unsafe_explore_import_branches=True calls skipped finders and hooks during the import. Use it only in a disposable process or container. Returned specs are discarded, but other side effects are not undone. See Unsafe import-branch exploration.

Choose report-time checks

Analysis controls checks run while a report is built. Checks may call existing finder code, so they are separate from passive capture:

analysis = metapathology.AnalysisConfig(
    standard_path_check=True,
    displaced_finder_check=False,
)

Pass it to install() for the default policy or override one report:

text = metapathology.render_report(
    analysis=metapathology.AnalysisConfig(checks=False)
)

metapathology.write_report(
    "diagnosis.json",
    format="json",
    analysis=metapathology.AnalysisConfig(displaced_finder_check=True),
)

A report override does not mutate the installed default.

Produce a report

destination=None writes to standard error. A path is replaced atomically; streams are written directly.

render_report() returns the human-readable text report. get_report() returns the structured ReportJSON document for programmatic inspection. JSON encoding happens when write_report(..., format="json") writes to a destination.

Calling get_report(), render_report(), or write_report() before installation raises RuntimeError. I/O errors from an explicit write_report() call are re-raised. Automatic exit reporting suppresses them so a diagnostic cannot replace the target's exit behavior.

Automatic output can be configured through install() with report_destination, report_text, report_json, report_color, and report_at_exit.

Inspect captured evidence

monitor.events() returns immutable event records copied from the monitor. Public record names describe the observation directly, including ImportSearchStarted, MetaPathFinderCall, ImportMechanismCall, ImporterCacheChange, and MonitoringError.

Treat these records as low-level evidence. Integrations usually want the JSON report instead.

Monitor properties report which capture mechanisms are active and why detailed evidence may be unavailable. In particular, unsafe_import_branch_exploration_status is complete, partial, disabled, or uninstalled. Partial coverage means a profiler was already installed or a prerequisite was disabled, so some skipped calls may be missing.

Reference

The signatures and parameter descriptions below are generated from the library source. Configuration fields accept True, False, or None unless stated otherwise; None uses the corresponding environment setting or normal default.

Lifecycle

install

install(*, report_at_exit: bool = True, report_destination: str | PathLike[str] | Sequence[str | PathLike[str]] | None = None, report_text: str | PathLike[str] | Sequence[str | PathLike[str]] | None = None, report_json: str | PathLike[str] | Sequence[str | PathLike[str]] | None = None, report_color: Literal['auto', 'always', 'never'] | None = None, report_verbosity: Literal['summary', 'standard', 'full'] | None = None, capture: CaptureConfig | None = None, analysis: AnalysisConfig | None = None, unsafe_explore_import_branches: bool | None = None) -> Monitor

Install the process-wide monitor and configure automatic reporting.

Repeating the same resolved capture and analysis configuration is idempotent. While monitoring is active, requesting a different configuration fails before import state is changed.

Parameters:

Name Type Description Default
report_at_exit bool

Write configured reports during normal interpreter shutdown.

True
report_destination str | PathLike[str] | Sequence[str | PathLike[str]] | None

Output path or paths whose format is inferred from the extension. "-" means standard error.

None
report_text str | PathLike[str] | Sequence[str | PathLike[str]] | None

Path or paths forced to text format.

None
report_json str | PathLike[str] | Sequence[str | PathLike[str]] | None

Path or paths forced to JSON format.

None
report_color Literal['auto', 'always', 'never'] | None

Color policy for automatic text reports.

None
report_verbosity Literal['summary', 'standard', 'full'] | None

Text-report detail level: "summary", "standard", or "full". None first checks METAPATHOLOGY_REPORT_VERBOSITY, then falls back to a destination-sensitive default (a terminal defaults to "summary", a file to "standard") resolved when the report is written. Never affects the JSON report.

None
capture CaptureConfig | None

Capture mechanisms to enable. None uses normal defaults and environment settings.

None
analysis AnalysisConfig | None

Report-time checks to enable. None uses normal defaults and environment settings.

None
unsafe_explore_import_branches bool | None

Call skipped finders and hooks during imports. This can run arbitrary foreign side effects; use it only in a disposable process.

None

Returns:

Type Description
Monitor

The process-wide monitor.

Raises:

Type Description
RuntimeError

An active installation uses different capture or analysis settings.

TypeError

A configuration value or report destination is invalid.

ValueError

A report format cannot be inferred, or a color mode or verbosity level is invalid.

monitoring

monitoring(*, capture: CaptureConfig | None = None, analysis: AnalysisConfig | None = None, unsafe_explore_import_branches: bool | None = None) -> Iterator[Monitor]

Monitor imports within a context-managed region.

Nested and overlapping regions share one process-wide monitor. If monitoring was already installed before the first region, leaving the region does not uninstall it.

Parameters:

Name Type Description Default
capture CaptureConfig | None

Capture mechanisms to enable.

None
analysis AnalysisConfig | None

Default report-time checks.

None
unsafe_explore_import_branches bool | None

Call skipped finders and hooks during imports. Use only in a disposable process.

None

Yields:

Type Description
Monitor

The process-wide monitor.

Warns:

Type Description
RuntimeWarning

Monitoring was already active before the outermost region.

Raises:

Type Description
RuntimeError

A shared active monitor uses different settings.

uninstall

uninstall() -> None

Stop monitoring and restore the import state owned by metapathology.

Ordinary lists replace installed list observers and finder instrumentation is removed. The Python audit hook cannot be removed, so it remains inert. Calling this function when no monitor exists is safe.

get_monitor

get_monitor() -> Monitor | None

Return the process-wide monitor if one has been created.

The returned monitor may be disabled after uninstall(). Check monitor.enabled to distinguish that state from active monitoring.

Reports

write_report

write_report(destination: TextIO | str | PathLike[str] | None = None, *, format: Literal['text', 'json'] = 'text', color: Literal['auto', 'always', 'never'] = 'auto', verbosity: Literal['summary', 'standard', 'full'] | None = None, analysis: AnalysisConfig | None = None) -> None

Write a report from the current capture.

Parameters:

Name Type Description Default
destination TextIO | str | PathLike[str] | None

Text stream, exact output path, or None for standard error. Paths are replaced atomically.

None
format Literal['text', 'json']

Output format.

'text'
color Literal['auto', 'always', 'never']

Color policy for text output.

'auto'
verbosity Literal['summary', 'standard', 'full'] | None

Text-report detail level. None resolves to a destination-sensitive default: a terminal defaults to "summary", a file to "standard". Never affects the JSON report.

None
analysis AnalysisConfig | None

Per-report analysis override. It does not change the installation default.

None

Raises:

Type Description
RuntimeError

Monitoring has not been installed.

OSError

The destination cannot be written.

TypeError

analysis is not an AnalysisConfig.

ValueError

format, color, or verbosity is invalid.

get_report

get_report(*, analysis: AnalysisConfig | None = None) -> ReportJSON

Return a structured report from the current capture.

Parameters:

Name Type Description Default
analysis AnalysisConfig | None

Per-report analysis override. It does not change the installation default.

None

Returns:

Type Description
ReportJSON

A document conforming to the bundled JSON schema.

Raises:

Type Description
RuntimeError

Monitoring has not been installed.

TypeError

analysis is not an AnalysisConfig.

render_report

render_report(*, color: bool = False, verbosity: Literal['summary', 'standard', 'full'] = 'standard', analysis: AnalysisConfig | None = None) -> str

Render a human-readable report from the current capture.

Parameters:

Name Type Description Default
color bool

Include terminal color escapes in the output.

False
verbosity Literal['summary', 'standard', 'full']

Text-report detail level: "summary", "standard", or "full". Unlike write_report, there is no destination to infer a default from, so this always defaults to "standard".

'standard'
analysis AnalysisConfig | None

Per-report analysis override. It does not change the installation default.

None

Returns:

Type Description
str

The complete text report.

Raises:

Type Description
RuntimeError

Monitoring has not been installed.

TypeError

analysis is not an AnalysisConfig.

ValueError

verbosity is invalid.

Configuration

CaptureConfig

Bases: _ConfigRecord

Capture settings for one installation.

Core mechanisms default to enabled. Pass detailed=True to enable every detailed mechanism, or use DetailedCaptureConfig to select them individually.

Attributes:

Name Type Description
import_audit bool | None

Observe import starts and direct replacement of monitored import-state lists.

meta_path bool | None

Observe mutations to sys.meta_path.

finder_attribution bool | None

Record calls to writable meta-path finder instances.

path_hooks bool | None

Observe mutations to sys.path_hooks.

importer_cache bool | None

Observe changes to sys.path_importer_cache.

sys_path bool | None

Observe mutations to sys.path. Disabled by default unless all detailed capture is enabled.

detailed bool | DetailedCaptureConfig | None

Detailed capture selection. True enables every detailed mechanism; a nested configuration selects individual mechanisms.

DetailedCaptureConfig

Bases: _ConfigRecord

Select individual detailed capture mechanisms.

Detailed capture adds wrappers to more of Python's import machinery and retains evidence for every observed call. Enable only the evidence needed for the reproduction.

Attributes:

Name Type Description
enabled bool | None

Default for any field left as None.

path_hooks bool | None

Record calls to sys.path_hooks entries.

path_entry_finders bool | None

Record calls to path-entry finders.

loaders bool | None

Record loader execution calls.

import_results bool | None

Match import attempts to exact outcomes where possible.

import_calls bool | None

Record calls through builtins.__import__.

AnalysisConfig

Bases: _ConfigRecord

Choose current-state checks performed while building a report.

These checks may call finder code at report time. They do not affect the monitored import outcomes.

Attributes:

Name Type Description
checks bool | None

Default for check fields left as None.

standard_path_check bool | None

Compare captured custom-finder results with a current-state PathFinder lookup. Enabled by default.

displaced_finder_check bool | None

Call finders that are no longer in their captured position. Disabled by default.

Monitor

Monitor

Captured import-machinery evidence for the current process.

Obtain this object from install() or monitoring(); constructing it directly does not start monitoring. Event accessors return snapshots, so callers cannot mutate the monitor's internal evidence.

enabled property

enabled: bool

Whether the monitor is currently observing.

import_audit_enabled property

import_audit_enabled: bool

Whether import audit starts and reassignment recovery are active.

meta_path_enabled property

meta_path_enabled: bool

Whether reversible sys.meta_path list observation is active.

finder_attribution_enabled property

finder_attribution_enabled: bool

Whether writable finder instances are shadowed for attribution.

path_hooks_enabled property

path_hooks_enabled: bool

Whether sys.path_hooks mutation monitoring is currently active.

importer_cache_enabled property

importer_cache_enabled: bool

Whether passive sys.path_importer_cache monitoring is active.

sys_path_enabled property

sys_path_enabled: bool

Whether opt-in sys.path mutation monitoring is active.

detailed_capture property

detailed_capture: tuple[str, ...]

Names of active detailed capture mechanisms.

import_results_capture_status property

import_results_capture_status: ImportResultsCaptureStatus

Activation state and thread scope of exact import outcomes.

import_calls_capture_status property

import_calls_capture_status: ImportCallsCaptureStatus

Activation state of builtins.__import__ call observation.

path_finder_capture_status property

path_finder_capture_status: PathFinderCaptureStatus

Availability of exact aggregate standard-finder evidence.

unsafe_import_branch_exploration_status property

unsafe_import_branch_exploration_status: str

Coverage of unsafe branch exploration.

The value is "complete", "partial", "disabled", or "uninstalled". Partial coverage means prerequisite capture was disabled or an existing profiler prevented some calls from being observed.

events

events

Return a snapshot of all recorded events in capture order.

The list is independent of the monitor. Its event records are immutable.