Skip to content

Spike: evaluate ArchUnitPython, pytestarch, pytest-imports, deply for architecture enforcement #134

Description

@kvithayathil

Context

#133 covers import-linter and basic candidates for module-level import constraints. This spike extends the evaluation to four additional Python architecture-testing frameworks, plus insights from Mark Richards' "Fitness Function-Driven Architecture" (lesson 215).

The video reinforces the concept of architecture fitness functions — automated, objective integrity checks that describe architectural goals teams strive toward — and mentions ArchUnit (Java), ArchUnit.NET, ArchUnit TS, pytest-archon, and pytestarch as Python options.

Candidates

1. ArchUnitPythonpip install archunitpython

Attribute Detail
Stars 150
Latest release v1.1.0 (Apr 26, 2026)
Commits 39
License MIT
Python 3.10+
Dependencies Zero runtime deps
Test integration pytest (recommended), unittest, any framework
Approach Fluent API — project_files("src/").should().have_no_cycles()

Strengths:

  • Richest feature set: circular deps, layer deps, naming conventions, code metrics (LCOM, distance from main sequence), custom rules/metrics, UML diagram adherence (PlantUML), architecture slices, HTML reports
  • Zero runtime dependencies — uses Python ast module
  • Clean fluent API with assert_passes() helper
  • Actively maintained (released today), inspired by ArchUnitTS
  • Self-hosting (uses itself to test its own architecture)

Weaknesses:

  • Very new project (39 commits, 2 forks, single maintainer)
  • File-level analysis only (not class/method granularity for dependencies)
  • No pre-commit hook integration documented
  • Small community, no established track record

2. pytestarchpip install pytestarch

Attribute Detail
Stars 157
Latest release v4.0.1 (Aug 8, 2025)
Commits 264
License Apache-2.0
Python 3.9+ (based on noxfile)
Dependencies networkx, matplotlib (optional for viz)
Test integration pytest-native
Approach Module-level rule DSL — Rule().modules_that().are_named(...)

Strengths:

  • Most mature of the four (27 releases, 264 commits)
  • Module-level granularity for import rules
  • Visualization support (dependency graphs via matplotlib)
  • Separate evaluable architecture object (can reuse across tests)
  • Well-documented with mkdocs site

Weaknesses:

  • Focused purely on import dependencies (no metrics, no naming conventions, no custom rules)
  • Uses poetry for build (dependency on networkx heavier than needed)
  • Less active recently (last release Aug 2025 — 8 months ago)
  • 10 open issues, 4 open PRs

3. pytest-importspip install pytest-imports

Attribute Detail
Stars 17
Commits 60
License Apache-2.0
Python 3.10+ (uses uv)
Dependencies None beyond pytest plugin
Test integration pytest plugin (native fixture-based)
Approach Dict-based rules — imports.check({'foo': must_import('bar')})

Strengths:

  • Most pythonic/ergonomic API — pytest fixture imports, declarative dict-based rules
  • Handles absolute vs relative imports (via via='absolute'|'relative')
  • Built-in predicates: must_not_import_private(), must_not_import_within_parent()
  • scope() helper for excluding subpackages
  • Lightweight, uses ast module from stdlib
  • Lists related tools in README (import-linter, pytestarch, pytest-archon, pytest-importson, findimports, pydeps)

Weaknesses:

  • Very small community (17 stars, 0 forks)
  • No releases on PyPI yet (install from git or test PyPI)
  • Import-only (no metrics, naming, cycles)
  • Single maintainer
  • No pre-commit integration documented

4. deplypip install deply

Attribute Detail
Stars 170
Latest release v0.8.2 (stable badge)
Commits 133
License BSD-3-Clause
Python 3.8–3.14
Dependencies Minimal (see requirements.txt)
Test integration Standalone CLIdeply analyze
Approach YAML config — declarative layers + ruleset

Strengths:

  • Highest star count of the four (170)
  • Declarative YAML config (not test-code — can run independently in CI)
  • Rich collector system: file_regex, class_name_regex, class_inherits, class_decorator, logical AND/OR/NOT
  • Layer-based rules: disallow_layer_dependencies, enforce_inheritance, etc.
  • Mermaid diagram generation for visualization
  • Error suppression via # deply:ignore inline comments
  • CI-friendly CLI (no test framework dependency)
  • Broad Python version support (3.8–3.14)
  • Pre-commit config included
  • Custom collectors system
  • Parallel file analysis

Weaknesses:

  • Newer project (133 commits, 4 forks)
  • Layer-based only (no code metrics)
  • YAML config adds another config file to the repo
  • Single main maintainer
  • Roadmap items still pending (custom rules, external import restrictions)

Also Noted (from video & snip research)

Tool Status
import-linter Evaluated in #133 — strongest established option
pytest-archon Mentioned in pytest-imports README — small, import-focused
pytest-importson Another small import-only option
ArchUnit (Java) / ArchUnit.NET / ArchUnit TS Reference implementations from the video

Comparison Matrix

Criterion ArchUnitPython pytestarch pytest-imports deply import-linter (#133)
Import layer rules Yes Yes Yes Yes Yes
Circular dependency detection Yes No No No Yes
Code metrics Yes (LCOM, distance) No No No No
Naming conventions Yes No No No No
Custom rules Yes (predicates) No No Planned Limited
CLI / standalone No (test-only) No (test-only) No (test-only) Yes Yes
Declarative config No (code-based) No (code-based) No (code-based) YAML INI
Visualization HTML reports matplotlib graphs No Mermaid diagrams Dep graph
Runtime dependencies Zero networkx Zero Minimal Minimal
Pre-commit ready Via pytest Via pytest Via pytest Yes Yes
Maturity (releases) 3 27 0 35 Many (est. 2019)
Community (stars) 150 157 17 170 ~300+
Active maintenance Very active (2026) Slowing (Aug 2025) Active Active Established

Spike Deliverables

  • Install each candidate in a clean venv, run against app/ codebase
  • Write equivalent rules for our known constraints (see Spike: enforce module-level import architecture constraints #133):
    • app/routes/ must not import app/db/
    • app/models/ must not import app/routes/
    • No circular deps between app/services/, app/models/, app/routes/
    • External import restrictions for domain layers
  • Compare ergonomics, error messages, CI integration
  • Assess security: supply chain risk of each dep, code review of AST parsing logic
  • Evaluate maintenance risk: bus factor, release cadence, issue response time
  • Cross-reference with import-linter evaluation from Spike: enforce module-level import architecture constraints #133
  • Recommend: adopt one, combine, or write custom ruff plugin

Preliminary Assessment

import-linter (from #133) remains the strongest established choice for pure import contract enforcement. Among the new candidates:

  1. deply — best fit for standalone CI + YAML-driven config, but less mature than import-linter. Mermaid viz is a nice differentiator.
  2. ArchUnitPython — most feature-rich (metrics + naming + UML), but very new and code-only (no declarative config). Could complement import-linter for fitness functions beyond imports.
  3. pytestarch — mature but narrow (imports only) and slowing. No clear advantage over import-linter.
  4. pytest-imports — nicest API but too early (no releases, tiny community).

Likely recommendation: import-linter for import contracts + deply or ArchUnitPython for broader architectural fitness functions (if needed beyond imports).

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:backendBackend (Python/FastAPI) changesdevexDeveloper and contributor experiencepriority:mediumNormal priorityspikeResearch and investigation task

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions