|
oc_chat
|
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 | |
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)
The human table for one closure.
Definition at line 724 of file import_graph.py.
|
protected |
One closure, as receipt JSON.
Definition at line 653 of file import_graph.py.
|
protected |
Physical line count, counting a final unterminated line.
Definition at line 271 of file import_graph.py.
|
protected |
One edge, as receipt JSON.
Definition at line 668 of file import_graph.py.
|
protected |
True when the root-relative POSIX path matches an exclude glob.
Definition at line 223 of file import_graph.py.
|
protected |
Names bound to ``importlib`` and to ``import_module``.
Definition at line 314 of file import_graph.py.
|
protected |
True for ``if TYPE_CHECKING:`` in either spelling.
Definition at line 330 of file import_graph.py.
|
protected |
Dotted name for *path* under *root*, and whether it is a package.
Definition at line 214 of file import_graph.py.
|
protected |
Read *path* as UTF-8, tolerating a stray decoding error.
Definition at line 266 of file import_graph.py.
|
protected |
Absolute name for a relative import, or None when it escapes.
Definition at line 298 of file import_graph.py.
|
protected |
Sorted count-by-value, for the receipt's summary blocks.
Definition at line 645 of file import_graph.py.
| 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.
| 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.
| 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.
| 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.
| 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.
| 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.
| 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.
| 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.
| 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.
| 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.
| 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.
|
protected |
Definition at line 116 of file import_graph.py.
| str import_graph.KIND_LAZY = "lazy" |
Definition at line 102 of file import_graph.py.
| str import_graph.KIND_MODULE = "module" |
Definition at line 101 of file import_graph.py.
| str import_graph.KIND_STRING = "string" |
Definition at line 103 of file import_graph.py.
| str import_graph.REASON_NON_LITERAL = "import_module called with a non-literal argument" |
Definition at line 110 of file import_graph.py.
| str import_graph.REASON_RELATIVE = "relative import escapes the scanned roots" |
Definition at line 111 of file import_graph.py.
| str import_graph.REASON_SHADOWED = "module name already claimed by an earlier root" |
Definition at line 113 of file import_graph.py.
| str import_graph.REASON_UNPARSABLE = "file could not be parsed" |
Definition at line 112 of file import_graph.py.
| str import_graph.SAME_PACKAGE = "same_package" |
Definition at line 95 of file import_graph.py.
| str import_graph.SCHEMA_VERSION = "1.0" |
Definition at line 90 of file import_graph.py.
| str import_graph.SCOPE_RUNTIME = "runtime" |
Definition at line 106 of file import_graph.py.
| str import_graph.SCOPE_TYPE_CHECKING = "type_checking" |
Definition at line 107 of file import_graph.py.
| str import_graph.SIBLING = "sibling_package" |
Definition at line 96 of file import_graph.py.
| str import_graph.STDLIB = "stdlib" |
Definition at line 93 of file import_graph.py.
| str import_graph.THIRD_PARTY = "third_party" |
Definition at line 94 of file import_graph.py.