Package 01 / pyarchgraph.domain
Domain
Understands imports and decides what the collected information means.
Its role
The domain gives meaning to the information collected from a Python project. It knows what a source module, import fact, dependency, and finding represent, and the rules that connect them.
Describe the source
Keep identities, import statements, context, and evidence in a shared vocabulary.
Interpret relationships
Resolve imports, build views, and explain cycles or unresolved targets.
Protect meaning
Check evidence and decide whether the declared analysis scope is complete.
The application supplies collected observations and asks these rules to work on them. The domain performs no file access and imports no graph library. Its graph contracts let an adapter supply the graph operations.
Process flow
Follow the collected sources through domain rules to the final report. The application coordinates these steps; the arrows show the main data flow, rather than parallel execution or every method call. Request and root validation happen before the observations shown here.
- Domain rules · square corners
- Application steps · rounded corners
- Adapter observations · slanted sides
Scroll sideways to follow both branches of the diagram.
The shared snapshot carries sources, import facts, selected dependencies, original resolver dependencies, and external or unresolved imports. Each registered view then receives its own cycle analysis and selected checks. Cycle counts are computed even when no checks are selected.
Coverage has its own rules. Collection and resolution diagnostics, layout observations, and referenced target boundaries determine completeness. The application calculates this after successful view evaluation and combines it with all view reports. A complete report can contain error findings.
Recoverable source failures continue as diagnostics alongside the available results. A registered view or check that raises or returns invalid output produces ExtensionError and stops the analysis without a report.
Which classes carry out these steps?
| Stage | Main classes |
|---|---|
| Collect and combine | SourceCatalogBuilder coordinates SourceDiscovery and ImportFactSource, then returns SourceCatalog. |
| Bindings and fact IDs | BindingReconciler checks cross-root bindings; FactCanonicalizer assigns stable IDs to collected drafts. |
| Targets and resolution | TargetReconciler combines discovered and configured declarations; StaticImportResolver interprets imports. |
| Dependencies and snapshot | ArchitectureDependencyPolicy selects relationships; AnalyseProject assembles the AnalysisSnapshot. |
| Normalise the snapshot | SnapshotNormalizer prepares consistent shared values; GraphValidator prepares the evidence used to validate views. |
| Build and validate views | GraphViewStrategy builds each view; the validation session from GraphValidator checks it. |
| Explain cycles | CycleAnalyzer uses GraphAlgorithms to identify cyclic groups and explain their evidence. |
| Run selected checks | CheckStrategy produces findings; CheckResultValidator validates them. StrategyRegistry controls which checks run. |
| Assemble view reports | StrategyEngine combines findings and counts into each ViewReport. |
| Diagnostics and completeness | AnalyseProject gathers diagnostics, including LayoutDiagnosticService observations. CoveragePolicy determines completeness. |
| Return or stop | AnalyseProject assembles the AnalysisReport. View and check failures become ExtensionError instead. |
Follow an import
Imagine analysing the demo directory from its parent. Both files contain an ordinary, top-level import.
# demo/orders.py
import inventory# demo/inventory.py
import ordersGive the observations stable identities
Discovery supplies two
SourceModulevalues. Their IDs becomesource:demo/orders.pyandsource:demo/inventory.py. Once the application has aligned paths,FactCanonicalizerturns the two drafts into identifiedImportFactvalues.Match each import to its target
StaticImportResolveruses known bindings to connectorderstoinventory, and back again. EachDependencyEdgekeeps the fact ID and resolution kind explaining why that relationship exists.Choose the relationships for analysis
ArchitectureDependencyPolicyremoves redundant package-base evidence where a fact already identifies a probable child. These plain imports keep both edges. The application places selected dependencies, the original resolver output, sources, and facts in oneAnalysisSnapshot.Build a view and explain its cycle
A view turns the snapshot into a
ViewGraph.CycleAnalyzerasks the graph contract for cyclic groups, interprets the supporting facts, and selects a witness:inventory → orders → inventory.Turn the observation into a finding
CycleCheckreturns aCheckResultcontaining an error-levelCycleFinding. The application adds the check ID and includes it in the view report.
A fact records an imported item. Resolution explains its target; evidence keeps the link back to the original statement. These are separate steps so findings stay explainable.
Views and findings
The default composition includes source and package views for each of three import filters. Source views keep one node per file; package views group those sources after filtering.
| Source view | Package view | Imports retained |
|---|---|---|
structural | package-structural | All explicit import statements. |
non-typing | package-non-typing | Everything except recognised TYPE_CHECKING bodies. |
module-body | package-module-body | Also omits imports inside functions or methods; class bodies outside functions remain. |
PackageView wraps a source strategy. A module belongs to its immediate containing package; an initializer belongs to its own package. Nodes use package:<dotted-name> IDs with disjoint memberships. Parent packages do not duplicate child-package sources. Standalone modules and sources without reliable bindings remain individual source nodes, so the walkthrough's two standalone files retain their cycle in all six views.
An optional max_depth caps the package's dotted name: depth 1 groups acme.orders and acme.inventory under acme; depth 2 keeps them separate. Projection drops dependencies inside a node, merges original evidence between shared endpoints and retains isolated nodes. A package cycle may exist even if no individual source modules form a cycle.
Namespace package sources group without __init__.py. An import of only the namespace container has no source target, so it does not create a graph edge. GraphValidator verifies original evidence and disjoint source memberships for package views and extensions alike.
The snapshot's optional resolved_dependencies preserves resolver evidence removed by module-level dependency selection. Package projection restores exact-base evidence only for the same retained import fact whose probable-child evidence maps to the same target package. This retains certainty without adding an unrelated package relationship. Older snapshots without the extra collection remain supported.
A cycle finding describes one cyclic group and one witness. A group can contain several cycles. “Definite” means it contains a cycle supported by exact resolution; it does not predict a crash when Python executes the project.
UnresolvedImportCheck reports unresolved imports retained by the view, except unmodelled namespace bases. Extensions may return a RuleFinding for other observations. CheckResultValidator checks their shape, source references, and evidence.
Coverage is a separate decision. CoveragePolicy requires no error diagnostics and acknowledgement of every encountered boundary. A complete analysis can still contain error findings, as this two-file example does.
Module map
Choose a module to jump to its classes below. Package initialisers are intentionally empty; callers import from the defining modules.
| Module | Responsibility |
|---|---|
models.py | Sources, import facts, evidence, diagnostics, targets, and findings. |
bindings.py | Conflicting source bindings across selected roots. |
canonicalization.py | Stable identities and ordering for collected import facts. |
targets.py | Agreement between target declarations and source package layout. |
resolution.py | Matching imports to known sources and classifying other outcomes. |
graph.py | The shared analysis snapshot, view graphs, and check context. |
strategies.py | Built-in views, cycle interpretation, and checks. |
graph_algorithms.py | Contracts for preparing graphs and asking cycle questions. |
validation.py | Consistent snapshots, supported view evidence, and valid check results. |
coverage.py | The rules for complete analysis within the declared scope. |
Every class
All 55 classes in this package, including value types, contracts, and internal helpers. Names link to the source file; descriptions match the role summaries in the code.
models.py
Sources, import facts, evidence, diagnostics, targets, and findings.
Severity- Label how seriously a diagnostic or check result should be treated.
ImportSyntax- Distinguish a plain import from a from-import statement.
ResolutionKind- Describe how an import was matched to a module, base or probable submodule.
ExternalClassification- Distinguish standard library imports from other imports outside the graph.
UnresolvedReason- Explain why an import could not be connected to an analysed source.
SourceModule- Identify a source file and record its import binding and analysis status.
ImportContext- Describe the surroundings of an import in source code for view filtering.
ImportFactDraft- Record one imported item before assigning its stable fact ID.
ImportFact- Record one imported item with its source location, context and stable ID.
Diagnostic- Record a concern about source scope, collection or resolution.
DependencyEvidence- Link a dependency to the import fact and resolution kind that support it.
DependencyEdge- Represent an import dependency between source files, with supporting facts.
ExternalImport- Record an import outside the source graph without claiming it is installed.
UnresolvedImport- Record an unmatched import, its reason and the facts that requested it.
EvidenceLocation- Locate supporting source text in a finding or target boundary.
TargetDeclaration- Describe an unanalysed target and whether its coverage limit is accepted.
TargetBoundary- Record an imported target outside analysis, with evidence and acknowledgement.
ResolutionResult- Collect resolved dependencies, other import outcomes and coverage concerns.
FindingDependency- Describe a dependency reported in a finding, with readable source evidence.
CycleFinding- Describe one cyclic group, its certainty and a representative cycle.
ImportFinding- Report a problem with an unresolved import and its supporting source evidence.
RuleFinding- Report a check's rule violation or observation about sources and view nodes.
CheckResult- Pair a finding with the severity assigned by its check.
bindings.py
Conflicting source bindings across selected roots.
BindingReconciler- Mark conflicting import bindings across selected source roots.
canonicalization.py
Stable identities and ordering for collected import facts.
FactCanonicalizer- Turn drafts into ordered import facts with stable, collision-safe IDs.
targets.py
Agreement between target declarations and source package layout.
TargetReconciler- Combine target declarations and check they fit the source package layout.
resolution.py
Matching imports to known sources and classifying other outcomes.
BindingIndex- Hold immutable source bindings, ownership and targets for one resolution.
_ResolutionSessionInternal helper- Keep results accumulated during one resolution separate from later analyses.
ImportResolver- Define how import facts are interpreted using sources and target declarations.
StaticImportResolver- Match imports using declared bindings without inspecting installed packages.
ArchitectureDependencyPolicy- Select dependencies before views, removing redundant package-base edges.
graph.py
The shared analysis snapshot, view graphs, and check context.
ViewNode- Represent one view node and the source files it groups.
ViewEvidence- Keep original source endpoints and their supporting fact visible in a view.
ViewEdge- Connect two view nodes using evidence from original source dependencies.
ViewGraph- Hold the nodes, dependencies and import fact IDs retained by one view.
AnalysisSnapshot- Provide shared source facts and the dependencies selected for module views.
CheckContext- Provide a check with one view's graph, retained imports and cycle analysis.
strategies.py
Built-in views, cycle interpretation, and checks.
GraphViewStrategy- Define how a shared analysis snapshot becomes a graph view.
CheckStrategy- Define how a view is inspected to produce findings with severity.
SourceGraphProjection- Build a view with one node per source and only the chosen import facts.
StructuralView- Include all explicit imports in a source graph view.
NonTypingView- Build a source graph view without recognised typing-only imports.
ModuleBodyView- Exclude recognised typing-only imports and those inside functions or methods.
PackageView- Group a source view by immediate package, optionally rolling up ancestors.
EvidenceInterpreter- Produce readable source evidence and remove repeated locations per dependency.
CycleAnalyzer- Interpret cycle certainty and evidence, choosing one stable witness per group.
CycleCheck- Report every analysed cycle group as an error finding.
UnresolvedImportCheck- Report unresolved imports as errors, except unmodelled namespace bases.
graph_algorithms.py
Contracts for preparing graphs and asking cycle questions.
GraphHandle- Provide reusable component and witness queries for one analysis's graph.
GraphAlgorithms- Define how cycle analysis prepares a graph without choosing a graph library.
validation.py
Consistent snapshots, supported view evidence, and valid check results.
GraphValidator- Prepare original source evidence for validating views before checks run.
_GraphValidationSessionInternal helper- Validate and order a view using one snapshot's source membership and evidence.
SnapshotNormalizer- Order and deduplicate snapshot values before they are shared with views.
CheckResultValidator- Validate immutable check results and their evidence against the analysed view.
coverage.py
The rules for complete analysis within the declared scope.
CoveragePolicy- Order diagnostics and decide whether the declared analysis scope is complete.