oc_chat
Loading...
Searching...
No Matches
import_graph Namespace Reference

Classes

class  ImportGraphError
class  Module
class  Edge
class  BlindSpot
class  Graph
class  Cut
class  Closure
class  _Extractor

Functions

tuple[str, bool] _module_name (Path root, Path path)
bool _excluded (str relative, Sequence[str] excludes)
tuple[dict[str, Module], dict[str, Path], list[BlindSpot]] discover (Sequence[Path] roots, *, Sequence[str] excludes=())
str _read (Path path)
int _count_lines (str text)
str classify (str target, *, str package, Sequence[str] siblings)
str|None _resolve_relative (str module, bool is_package, int level, str|None tail)
tuple[set[str], set[str]] _importlib_aliases (ast.AST tree)
bool _is_type_checking (ast.expr test)
Graph build_graph (Sequence[Path|str] roots, *, str package, Sequence[str] siblings=(), Sequence[str] excludes=())
tuple[str, tuple[str,...]] parse_layer (str spec)
Cut parse_cut (str spec)
Closure closure (Graph graph, Sequence[str] seeds, *, str name="closure", Sequence[Cut] cuts=())
dict[str, tuple[str,...]] reverse_edges (Graph graph)
dict[str, int] _tally (Iterable[str] values)
dict[str, object] _closure_payload (Closure result)
dict[str, object] _edge_payload (Edge edge)
dict[str, object] receipt (Graph graph, Sequence[Closure] closures, *, str label, dict[str, object] params)
list[str] _closure_block (Graph graph, Closure result)
str render_table (Graph graph, Sequence[Closure] closures, *, str label, Sequence[str] roots)
argparse.ArgumentParser build_parser ()
int main (Sequence[str]|None argv=None)

Variables

str SCHEMA_VERSION = "1.0"
str STDLIB = "stdlib"
str THIRD_PARTY = "third_party"
str SAME_PACKAGE = "same_package"
str SIBLING = "sibling_package"
str KIND_MODULE = "module"
str KIND_LAZY = "lazy"
str KIND_STRING = "string"
str SCOPE_RUNTIME = "runtime"
str SCOPE_TYPE_CHECKING = "type_checking"
str REASON_NON_LITERAL = "import_module called with a non-literal argument"
str REASON_RELATIVE = "relative import escapes the scanned roots"
str REASON_UNPARSABLE = "file could not be parsed"
str REASON_SHADOWED = "module name already claimed by an earlier root"
 _SKIP_DIRS

Detailed Description

AST import-graph extractor: closures, reverse edges, blind spots.

Purpose
-------
Answer "what does this set of modules actually drag in?" with a number
somebody else can reproduce. Built for plan 256, which had to re-derive
the `oc_orchestrator` extraction closure and found that the V0.1
analysis (2026-07-08) was computed by a script left in a session
scratchpad, so none of its numbers could be re-run. This tool is the
permanent fix: it is committed, it takes its seed set and its roots as
arguments rather than hardcoding one question, and it is deterministic,
so a reviewer can re-run it and diff the receipt.

It parses with :mod:`ast` and NEVER imports the code it analyses. A
module that raises at import time, or that does not even parse, is
analysed (or listed as a blind spot) rather than crashing the run.

Responsibilities
----------------
- :class:`Module` / :class:`Edge` / :class:`BlindSpot` / :class:`Graph` —
  the data model: one node per file, one edge per resolved import, one
  blind spot per import this tool provably cannot resolve.
- :func:`build_graph` — discover modules under the roots and extract
  every edge, resolving relative imports and classifying each target as
  standard library, third party, same package or sibling package.
- :func:`closure` — the transitive closure of same-package edges from a
  seed set, honouring `--cut` edge suppressions.
- :func:`parse_layer` — several named seed sets measured against one
  parsed tree, so a multi-layer question costs one receipt rather than
  one whole-graph copy per layer.
- :func:`reverse_edges` — who imports each in-tree module.
- :func:`receipt` / :func:`render_table` — the JSON receipt and the
  human table.
- :func:`main` — the CLI.

Diagnostics
-----------
Domain: OC_CHAT.MAIN
Levels:
    L2 - lifecycle events (graph built, closure computed)
    L3 - semantic details (per-module edge counts)
    L4 - deep tracing (per-edge)

Contracts
---------
- The code under measurement is never imported, only parsed.
- Determinism is a requirement, not a nicety: two runs over an unchanged
  tree produce byte-identical stdout and byte-identical JSON. The
  receipt therefore carries no clock and no absolute path -- roots
  appear as their directory NAME and module paths are root-relative
  POSIX, so a receipt is machine-independent. The tree the numbers
  belong to is pinned by the caller through ``--label``.
- A lazy (function-body) import is a dependency and is counted as one;
  it is flagged ``deferred`` so a reader can see which couplings are
  cheap to cut. A ``TYPE_CHECKING`` import is likewise counted and
  flagged through ``scope``.
- Known limitation, stated rather than hidden: only the
  ``importlib.import_module`` call shape is recognised as a string
  import. ``__import__``, ``importlib.util.spec_from_file_location`` and
  hand-rolled loaders are invisible to this tool. Grep for them
  separately before trusting a closure to be complete.
- Console output is ASCII only.

Dependencies
------------
- oc_diagnostics (structured debug logging via _dbg)

Function Documentation

◆ _closure_block()

list[str] import_graph._closure_block ( Graph graph,
Closure result )
protected
The human table for one closure.

Definition at line 724 of file import_graph.py.

Here is the caller graph for this function:

◆ _closure_payload()

dict[str, object] import_graph._closure_payload ( Closure result)
protected
One closure, as receipt JSON.

Definition at line 653 of file import_graph.py.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _count_lines()

int import_graph._count_lines ( str text)
protected
Physical line count, counting a final unterminated line.

Definition at line 271 of file import_graph.py.

Here is the caller graph for this function:

◆ _edge_payload()

dict[str, object] import_graph._edge_payload ( Edge edge)
protected
One edge, as receipt JSON.

Definition at line 668 of file import_graph.py.

Here is the caller graph for this function:

◆ _excluded()

bool import_graph._excluded ( str relative,
Sequence[str] excludes )
protected
True when the root-relative POSIX path matches an exclude glob.

Definition at line 223 of file import_graph.py.

Here is the caller graph for this function:

◆ _importlib_aliases()

tuple[set[str], set[str]] import_graph._importlib_aliases ( ast.AST tree)
protected
Names bound to ``importlib`` and to ``import_module``.

Definition at line 314 of file import_graph.py.

Here is the caller graph for this function:

◆ _is_type_checking()

bool import_graph._is_type_checking ( ast.expr test)
protected
True for ``if TYPE_CHECKING:`` in either spelling.

Definition at line 330 of file import_graph.py.

Here is the caller graph for this function:

◆ _module_name()

tuple[str, bool] import_graph._module_name ( Path root,
Path path )
protected
Dotted name for *path* under *root*, and whether it is a package.

Definition at line 214 of file import_graph.py.

Here is the caller graph for this function:

◆ _read()

str import_graph._read ( Path path)
protected
Read *path* as UTF-8, tolerating a stray decoding error.

Definition at line 266 of file import_graph.py.

Here is the caller graph for this function:

◆ _resolve_relative()

str | None import_graph._resolve_relative ( str module,
bool is_package,
int level,
str | None tail )
protected
Absolute name for a relative import, or None when it escapes.

Definition at line 298 of file import_graph.py.

Here is the caller graph for this function:

◆ _tally()

dict[str, int] import_graph._tally ( Iterable[str] values)
protected
Sorted count-by-value, for the receipt's summary blocks.

Definition at line 645 of file import_graph.py.

Here is the caller graph for this function:

◆ build_graph()

Graph import_graph.build_graph ( Sequence[Path | str] roots,
* ,
str package,
Sequence[str] siblings = (),
Sequence[str] excludes = () )
Discover every module under *roots* and extract its import edges.

Definition at line 496 of file import_graph.py.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ build_parser()

argparse.ArgumentParser import_graph.build_parser ( )
The CLI surface; every question is an argument, none is baked in.

Definition at line 789 of file import_graph.py.

Here is the caller graph for this function:

◆ classify()

str import_graph.classify ( str target,
* ,
str package,
Sequence[str] siblings )
Classify an import target by its top-level name.

Definition at line 282 of file import_graph.py.

Here is the caller graph for this function:

◆ closure()

Closure import_graph.closure ( Graph graph,
Sequence[str] seeds,
* ,
str name = "closure",
Sequence[Cut] cuts = () )
Transitive same-package reach of *seeds*, honouring *cuts*.

Definition at line 567 of file import_graph.py.

Here is the caller graph for this function:

◆ discover()

tuple[dict[str, Module], dict[str, Path], list[BlindSpot]] import_graph.discover ( Sequence[Path] roots,
* ,
Sequence[str] excludes = () )
Find every ``*.py`` under *roots*, first root wins on a clash.

Definition at line 228 of file import_graph.py.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ main()

int import_graph.main ( Sequence[str] | None argv = None)
Run one measurement. 0 on success, 1 on a named refusal.

Definition at line 840 of file import_graph.py.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ parse_cut()

Cut import_graph.parse_cut ( str spec)
Parse ``source_glob->target_glob`` into a :class:`Cut`.

Definition at line 557 of file import_graph.py.

Here is the caller graph for this function:

◆ parse_layer()

tuple[str, tuple[str, ...]] import_graph.parse_layer ( str spec)
Parse ``name=mod1,mod2`` into a named seed set.

Definition at line 546 of file import_graph.py.

Here is the caller graph for this function:

◆ receipt()

dict[str, object] import_graph.receipt ( Graph graph,
Sequence[Closure] closures,
* ,
str label,
dict[str, object] params )
Build the JSON receipt: no clock, no absolute path, fully sorted.

Definition at line 677 of file import_graph.py.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ render_table()

str import_graph.render_table ( Graph graph,
Sequence[Closure] closures,
* ,
str label,
Sequence[str] roots )
The human report. ASCII only, and stable across runs.

Definition at line 754 of file import_graph.py.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ reverse_edges()

dict[str, tuple[str, ...]] import_graph.reverse_edges ( Graph graph)
For every in-tree module, the sorted set of modules importing it.

Definition at line 629 of file import_graph.py.

Here is the caller graph for this function:

Variable Documentation

◆ _SKIP_DIRS

import_graph._SKIP_DIRS
protected
Initial value:
1= frozenset({
2 "__pycache__", ".git", ".venv", ".mypy_cache", ".pytest_cache",
3 ".ruff_cache", "node_modules", ".formatter_archive",
4})

Definition at line 116 of file import_graph.py.

◆ KIND_LAZY

str import_graph.KIND_LAZY = "lazy"

Definition at line 102 of file import_graph.py.

◆ KIND_MODULE

str import_graph.KIND_MODULE = "module"

Definition at line 101 of file import_graph.py.

◆ KIND_STRING

str import_graph.KIND_STRING = "string"

Definition at line 103 of file import_graph.py.

◆ REASON_NON_LITERAL

str import_graph.REASON_NON_LITERAL = "import_module called with a non-literal argument"

Definition at line 110 of file import_graph.py.

◆ REASON_RELATIVE

str import_graph.REASON_RELATIVE = "relative import escapes the scanned roots"

Definition at line 111 of file import_graph.py.

◆ REASON_SHADOWED

str import_graph.REASON_SHADOWED = "module name already claimed by an earlier root"

Definition at line 113 of file import_graph.py.

◆ REASON_UNPARSABLE

str import_graph.REASON_UNPARSABLE = "file could not be parsed"

Definition at line 112 of file import_graph.py.

◆ SAME_PACKAGE

str import_graph.SAME_PACKAGE = "same_package"

Definition at line 95 of file import_graph.py.

◆ SCHEMA_VERSION

str import_graph.SCHEMA_VERSION = "1.0"

Definition at line 90 of file import_graph.py.

◆ SCOPE_RUNTIME

str import_graph.SCOPE_RUNTIME = "runtime"

Definition at line 106 of file import_graph.py.

◆ SCOPE_TYPE_CHECKING

str import_graph.SCOPE_TYPE_CHECKING = "type_checking"

Definition at line 107 of file import_graph.py.

◆ SIBLING

str import_graph.SIBLING = "sibling_package"

Definition at line 96 of file import_graph.py.

◆ STDLIB

str import_graph.STDLIB = "stdlib"

Definition at line 93 of file import_graph.py.

◆ THIRD_PARTY

str import_graph.THIRD_PARTY = "third_party"

Definition at line 94 of file import_graph.py.