archetype-py 0.2.3__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {archetype_py-0.2.3 → archetype_py-0.3.0}/.github/workflows/archetype.yml +18 -3
- {archetype_py-0.2.3 → archetype_py-0.3.0}/.github/workflows/ci.yml +10 -1
- {archetype_py-0.2.3 → archetype_py-0.3.0}/.github/workflows/publish.yml +12 -2
- {archetype_py-0.2.3 → archetype_py-0.3.0}/CHANGELOG.md +16 -7
- archetype_py-0.3.0/PKG-INFO +101 -0
- archetype_py-0.3.0/PYPI.md +77 -0
- archetype_py-0.2.3/PKG-INFO → archetype_py-0.3.0/README.md +212 -24
- {archetype_py-0.2.3 → archetype_py-0.3.0}/archetype/__init__.py +1 -2
- {archetype_py-0.2.3 → archetype_py-0.3.0}/archetype/analysis/cache.py +13 -1
- {archetype_py-0.2.3 → archetype_py-0.3.0}/archetype/analysis/git_utils.py +40 -1
- {archetype_py-0.2.3 → archetype_py-0.3.0}/archetype/analysis/imports.py +128 -5
- {archetype_py-0.2.3 → archetype_py-0.3.0}/archetype/analysis/models.py +1 -0
- archetype_py-0.3.0/archetype/analysis/path_filters.py +92 -0
- archetype_py-0.3.0/archetype/baseline.py +186 -0
- archetype_py-0.3.0/archetype/check.py +540 -0
- archetype_py-0.3.0/archetype/config.py +145 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/archetype/dsl/query.py +12 -4
- {archetype_py-0.2.3 → archetype_py-0.3.0}/archetype/reporter.py +83 -2
- {archetype_py-0.2.3 → archetype_py-0.3.0}/archetype/rule.py +66 -63
- {archetype_py-0.2.3 → archetype_py-0.3.0}/pyproject.toml +2 -2
- archetype_py-0.3.0/tests/fixtures/monorepo_project/libs/shared/src/acme/shared/utils.py +5 -0
- archetype_py-0.3.0/tests/fixtures/monorepo_project/services/billing/src/acme/billing/api.py +7 -0
- archetype_py-0.3.0/tests/fixtures/namespace_project/company/payments/api.py +7 -0
- archetype_py-0.3.0/tests/fixtures/namespace_project/company/shared/utils.py +5 -0
- archetype_py-0.3.0/tests/test_cli.py +1410 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/tests/test_decorator_since.py +10 -2
- {archetype_py-0.2.3 → archetype_py-0.3.0}/tests/test_graph.py +125 -11
- archetype_py-0.3.0/tests/test_public_api.py +12 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/tests/test_reporter.py +108 -1
- archetype_py-0.2.3/README.md +0 -321
- archetype_py-0.2.3/archetype/check.py +0 -182
- archetype_py-0.2.3/tests/test_cli.py +0 -668
- {archetype_py-0.2.3 → archetype_py-0.3.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/.github/pull_request_template.md +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/.gitignore +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/BENCHMARKS.md +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/CODE_OF_CONDUCT.md +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/CONTRIBUTING.md +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/LICENSE +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/archetype/analysis/__init__.py +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/archetype/analysis/ast_utils.py +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/archetype/analysis/init.py +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/archetype/analysis/pattern.py +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/archetype/dsl/__init__.py +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/archetype/dsl/init.py +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/archetype/init.py +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/archetype/plugin/__init__.py +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/archetype/plugin/init.py +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/archetype/plugin/pytest_plugin.py +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/archetype/rules/__init__.py +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/archetype/rules/boundaries.py +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/archetype/rules/cycles.py +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/archetype/rules/layers.py +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/archetype/rules/naming.py +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/architecture.py +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/assets/Pytest.png +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/assets/Rule_Execution_Flow.png +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/assets/Violation_Lifecycle.png +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/assets/architecture.png +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/assets/ci_integration.png +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/assets/logo.png +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/benchmarks/README.md +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/benchmarks/fixtures.py +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/benchmarks/init.py +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/benchmarks/run_benchmarks.py +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/benchmarks/test_performance_regression.py +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/tests/fixtures/simple_project/.gitignore +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/tests/fixtures/simple_project/simple_project/api.py +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/tests/fixtures/simple_project/simple_project/db.py +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/tests/fixtures/simple_project/simple_project/internal/init.py +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/tests/fixtures/simple_project/simple_project/internal/tokens.py +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/tests/fixtures/simple_project/simple_project/main.py +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/tests/fixtures/simple_project/simple_project/services.py +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/tests/test_dsl.py +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/tests/test_grouping.py +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/tests/test_init.py +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/tests/test_pattern.py +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/tests/test_plugin.py +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/tests/test_rule.py +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/tests/test_rules_boundaries.py +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/tests/test_rules_cycles.py +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/tests/test_rules_layers.py +0 -0
- {archetype_py-0.2.3 → archetype_py-0.3.0}/tests/test_rules_naming.py +0 -0
|
@@ -4,8 +4,13 @@ on:
|
|
|
4
4
|
push:
|
|
5
5
|
branches:
|
|
6
6
|
- main
|
|
7
|
+
tags:
|
|
8
|
+
- "v*"
|
|
7
9
|
pull_request:
|
|
8
10
|
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
|
|
9
14
|
jobs:
|
|
10
15
|
archetype:
|
|
11
16
|
runs-on: ubuntu-latest
|
|
@@ -13,7 +18,11 @@ jobs:
|
|
|
13
18
|
steps:
|
|
14
19
|
# Pull down your repository code so CI can inspect it.
|
|
15
20
|
- name: Checkout repository
|
|
16
|
-
uses: actions/checkout@
|
|
21
|
+
uses: actions/checkout@v5
|
|
22
|
+
with:
|
|
23
|
+
token: ${{ github.token }}
|
|
24
|
+
ref: ${{ github.ref }}
|
|
25
|
+
fetch-depth: 0
|
|
17
26
|
|
|
18
27
|
# Install Python 3.11 so the Archetype CLI can run.
|
|
19
28
|
- name: Set up Python
|
|
@@ -28,9 +37,15 @@ jobs:
|
|
|
28
37
|
pip install archetype-py
|
|
29
38
|
|
|
30
39
|
# Run architecture rules defined in architecture.py.
|
|
31
|
-
#
|
|
40
|
+
# If the installed Archetype supports GitHub annotations, emit inline
|
|
41
|
+
# PR annotations. Otherwise, fall back to standard checks.
|
|
32
42
|
- name: Run architecture checks
|
|
33
|
-
run:
|
|
43
|
+
run: |
|
|
44
|
+
if archetype check --help | grep -q -- '--github-annotations'; then
|
|
45
|
+
archetype check . --github-annotations
|
|
46
|
+
else
|
|
47
|
+
archetype check .
|
|
48
|
+
fi
|
|
34
49
|
|
|
35
50
|
# If your team already runs pytest in CI, you can skip this workflow file.
|
|
36
51
|
# Archetype's pytest plugin runs architecture.py rules automatically during pytest.
|
|
@@ -4,8 +4,13 @@ on:
|
|
|
4
4
|
push:
|
|
5
5
|
branches:
|
|
6
6
|
- main
|
|
7
|
+
tags:
|
|
8
|
+
- "v*"
|
|
7
9
|
pull_request:
|
|
8
10
|
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
|
|
9
14
|
jobs:
|
|
10
15
|
test:
|
|
11
16
|
runs-on: ubuntu-latest
|
|
@@ -16,7 +21,11 @@ jobs:
|
|
|
16
21
|
|
|
17
22
|
steps:
|
|
18
23
|
- name: Checkout repository
|
|
19
|
-
uses: actions/checkout@
|
|
24
|
+
uses: actions/checkout@v5
|
|
25
|
+
with:
|
|
26
|
+
token: ${{ github.token }}
|
|
27
|
+
ref: ${{ github.ref }}
|
|
28
|
+
fetch-depth: 0
|
|
20
29
|
|
|
21
30
|
- name: Set up Python
|
|
22
31
|
uses: actions/setup-python@v5
|
|
@@ -8,11 +8,17 @@ on:
|
|
|
8
8
|
jobs:
|
|
9
9
|
test:
|
|
10
10
|
runs-on: ubuntu-latest
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
11
13
|
|
|
12
14
|
steps:
|
|
13
15
|
# Checkout repository contents for test execution.
|
|
14
16
|
- name: Checkout repository
|
|
15
|
-
uses: actions/checkout@
|
|
17
|
+
uses: actions/checkout@v5
|
|
18
|
+
with:
|
|
19
|
+
token: ${{ github.token }}
|
|
20
|
+
ref: ${{ github.ref }}
|
|
21
|
+
fetch-depth: 0
|
|
16
22
|
|
|
17
23
|
# Install Python runtime used for tests and packaging checks.
|
|
18
24
|
- name: Set up Python
|
|
@@ -40,7 +46,11 @@ jobs:
|
|
|
40
46
|
steps:
|
|
41
47
|
# Checkout repository contents so the package can be built.
|
|
42
48
|
- name: Checkout repository
|
|
43
|
-
uses: actions/checkout@
|
|
49
|
+
uses: actions/checkout@v5
|
|
50
|
+
with:
|
|
51
|
+
token: ${{ github.token }}
|
|
52
|
+
ref: ${{ github.ref }}
|
|
53
|
+
fetch-depth: 0
|
|
44
54
|
|
|
45
55
|
# Set up Python for building distribution artifacts.
|
|
46
56
|
- name: Set up Python
|
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.0 - 2026-05-26
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Git pre-commit hook integration via `archetype install-hook`.
|
|
7
|
+
- GitHub PR inline annotations support with `--github-annotations`.
|
|
8
|
+
- Baseline mode for legacy repositories via `--write-baseline` and `--baseline`.
|
|
9
|
+
- Project config defaults through `archetype.toml`.
|
|
10
|
+
- Exclude paths support via CLI (`--exclude`) and config.
|
|
11
|
+
- Versioned JSON contract with explicit `schema_version`.
|
|
12
|
+
- Namespace package and monorepo layout support improvements.
|
|
13
|
+
- Changed-files mode with `--changed-from <git-ref>`.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- CI/release workflows expanded to cover new check and packaging behaviors.
|
|
17
|
+
|
|
3
18
|
## 0.1.0 - 2026-05-09
|
|
4
19
|
|
|
5
20
|
### Added
|
|
@@ -48,12 +63,6 @@
|
|
|
48
63
|
## 0.2.3 — 2026-05-16
|
|
49
64
|
|
|
50
65
|
### Added
|
|
51
|
-
- @warn decorator for non-blocking rule violations
|
|
52
|
-
- @skip decorator to temporarily disable rules
|
|
53
|
-
- @since decorator for date-scoped enforcement
|
|
54
|
-
- @rule timeout parameter for pathological graphs
|
|
55
|
-
- Glob pattern support in module matching
|
|
56
|
-
- Rule grouping with group context manager
|
|
57
66
|
- archetype init command for scaffolding
|
|
58
67
|
- --quiet flag to show only failures and warnings
|
|
59
68
|
- --format json flag for machine-readable output
|
|
@@ -67,4 +76,4 @@
|
|
|
67
76
|
- src layout detection in archetype init
|
|
68
77
|
- Verbose violation messages now concise and scannable
|
|
69
78
|
- --quiet flag correctly filters JSON output
|
|
70
|
-
- Naming convention violation message format cleaned up
|
|
79
|
+
- Naming convention violation message format cleaned up
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: archetype-py
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Enforce architectural rules as code. Catch structural violations before they merge.
|
|
5
|
+
Project-URL: Homepage, https://github.com/MossabArektout/archetype-py
|
|
6
|
+
Project-URL: Documentation, https://github.com/MossabArektout/archetype-py
|
|
7
|
+
Author-email: Mossab Arektout <mossabarektout2000@gmail.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
15
|
+
Requires-Python: >=3.11
|
|
16
|
+
Requires-Dist: click
|
|
17
|
+
Requires-Dist: networkx
|
|
18
|
+
Requires-Dist: rich
|
|
19
|
+
Provides-Extra: benchmarks
|
|
20
|
+
Provides-Extra: dev
|
|
21
|
+
Requires-Dist: hatch; extra == 'dev'
|
|
22
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# archetype-py
|
|
26
|
+
|
|
27
|
+
Enforce architectural rules as code. Catch structural violations before they merge.
|
|
28
|
+
|
|
29
|
+
[GitHub Repository](https://github.com/MossabArektout/archetype-py)
|
|
30
|
+
[Documentation](https://github.com/MossabArektout/archetype-py#readme)
|
|
31
|
+
|
|
32
|
+
## Why archetype-py
|
|
33
|
+
|
|
34
|
+
Most tooling checks formatting, typing, and tests, but not architecture drift.
|
|
35
|
+
`archetype-py` lets you codify structural boundaries and enforce them in local runs, CI, and pytest.
|
|
36
|
+
|
|
37
|
+
## When to use / When not to use
|
|
38
|
+
|
|
39
|
+
Use archetype-py when:
|
|
40
|
+
- You want architecture rules to run automatically in CI and pytest.
|
|
41
|
+
- You need to prevent forbidden imports, layer violations, or import cycles.
|
|
42
|
+
- You are adopting architecture checks incrementally in a legacy codebase (baseline mode).
|
|
43
|
+
|
|
44
|
+
Do not use archetype-py when:
|
|
45
|
+
- You only need style/type checks (linters and type checkers are enough).
|
|
46
|
+
- Your project is a very small script with no meaningful module boundaries.
|
|
47
|
+
- You are looking for runtime policy enforcement instead of static import-graph checks.
|
|
48
|
+
|
|
49
|
+
## Install
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pip install archetype-py
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Quick Start
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
archetype init .
|
|
59
|
+
archetype check .
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Create an `architecture.py` file and define your rules:
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
from archetype import rule
|
|
66
|
+
from archetype.rules import layers
|
|
67
|
+
|
|
68
|
+
@rule("layers are ordered")
|
|
69
|
+
def layer_order() -> None:
|
|
70
|
+
layers(["myapp.api", "myapp.services", "myapp.db"]).are_ordered()
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Core Features
|
|
74
|
+
|
|
75
|
+
- Architecture rules for forbidden imports, allowlisted imports, and protected boundaries
|
|
76
|
+
- Transitive dependency checks with `must_not_depend_on`
|
|
77
|
+
- Layer order enforcement with `layers(...).are_ordered()`
|
|
78
|
+
- Import cycle detection with `no_cycles(...)`
|
|
79
|
+
- Rule decorators: `@rule`, `@warn`, `@skip`, `@since`
|
|
80
|
+
- Rule grouping via `group("...")` and targeted execution with `--group`
|
|
81
|
+
- `archetype init` scaffolding for starter `architecture.py`
|
|
82
|
+
- JSON and text reporting (`--format json|text`) with stable JSON contract versioning
|
|
83
|
+
- Quiet output mode (`--quiet`) for CI-friendly logs
|
|
84
|
+
- Import graph caching for faster repeated runs (`--cache`, `--no-cache`)
|
|
85
|
+
- Parallel execution control with `--workers`
|
|
86
|
+
- Baseline adoption for legacy codebases (`--write-baseline`, `--baseline`)
|
|
87
|
+
- Changed-files scope mode (`--changed-from <git-ref>`)
|
|
88
|
+
- GitHub Actions inline PR annotations (`--github-annotations`)
|
|
89
|
+
- Path exclusions via CLI (`--exclude`) and config
|
|
90
|
+
- Project config defaults through `archetype.toml`
|
|
91
|
+
- Git pre-commit hook installer (`archetype install-hook`)
|
|
92
|
+
- Project layout support for flat, `src/`, namespace packages (PEP 420), and monorepos
|
|
93
|
+
- CLI command and pytest plugin support for local and CI enforcement
|
|
94
|
+
|
|
95
|
+
## CI Example
|
|
96
|
+
|
|
97
|
+
```yaml
|
|
98
|
+
- run: archetype check .
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
For full guides, examples, and release notes, use the GitHub README and changelog.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# archetype-py
|
|
2
|
+
|
|
3
|
+
Enforce architectural rules as code. Catch structural violations before they merge.
|
|
4
|
+
|
|
5
|
+
[GitHub Repository](https://github.com/MossabArektout/archetype-py)
|
|
6
|
+
[Documentation](https://github.com/MossabArektout/archetype-py#readme)
|
|
7
|
+
|
|
8
|
+
## Why archetype-py
|
|
9
|
+
|
|
10
|
+
Most tooling checks formatting, typing, and tests, but not architecture drift.
|
|
11
|
+
`archetype-py` lets you codify structural boundaries and enforce them in local runs, CI, and pytest.
|
|
12
|
+
|
|
13
|
+
## When to use / When not to use
|
|
14
|
+
|
|
15
|
+
Use archetype-py when:
|
|
16
|
+
- You want architecture rules to run automatically in CI and pytest.
|
|
17
|
+
- You need to prevent forbidden imports, layer violations, or import cycles.
|
|
18
|
+
- You are adopting architecture checks incrementally in a legacy codebase (baseline mode).
|
|
19
|
+
|
|
20
|
+
Do not use archetype-py when:
|
|
21
|
+
- You only need style/type checks (linters and type checkers are enough).
|
|
22
|
+
- Your project is a very small script with no meaningful module boundaries.
|
|
23
|
+
- You are looking for runtime policy enforcement instead of static import-graph checks.
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install archetype-py
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Quick Start
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
archetype init .
|
|
35
|
+
archetype check .
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Create an `architecture.py` file and define your rules:
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from archetype import rule
|
|
42
|
+
from archetype.rules import layers
|
|
43
|
+
|
|
44
|
+
@rule("layers are ordered")
|
|
45
|
+
def layer_order() -> None:
|
|
46
|
+
layers(["myapp.api", "myapp.services", "myapp.db"]).are_ordered()
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Core Features
|
|
50
|
+
|
|
51
|
+
- Architecture rules for forbidden imports, allowlisted imports, and protected boundaries
|
|
52
|
+
- Transitive dependency checks with `must_not_depend_on`
|
|
53
|
+
- Layer order enforcement with `layers(...).are_ordered()`
|
|
54
|
+
- Import cycle detection with `no_cycles(...)`
|
|
55
|
+
- Rule decorators: `@rule`, `@warn`, `@skip`, `@since`
|
|
56
|
+
- Rule grouping via `group("...")` and targeted execution with `--group`
|
|
57
|
+
- `archetype init` scaffolding for starter `architecture.py`
|
|
58
|
+
- JSON and text reporting (`--format json|text`) with stable JSON contract versioning
|
|
59
|
+
- Quiet output mode (`--quiet`) for CI-friendly logs
|
|
60
|
+
- Import graph caching for faster repeated runs (`--cache`, `--no-cache`)
|
|
61
|
+
- Parallel execution control with `--workers`
|
|
62
|
+
- Baseline adoption for legacy codebases (`--write-baseline`, `--baseline`)
|
|
63
|
+
- Changed-files scope mode (`--changed-from <git-ref>`)
|
|
64
|
+
- GitHub Actions inline PR annotations (`--github-annotations`)
|
|
65
|
+
- Path exclusions via CLI (`--exclude`) and config
|
|
66
|
+
- Project config defaults through `archetype.toml`
|
|
67
|
+
- Git pre-commit hook installer (`archetype install-hook`)
|
|
68
|
+
- Project layout support for flat, `src/`, namespace packages (PEP 420), and monorepos
|
|
69
|
+
- CLI command and pytest plugin support for local and CI enforcement
|
|
70
|
+
|
|
71
|
+
## CI Example
|
|
72
|
+
|
|
73
|
+
```yaml
|
|
74
|
+
- run: archetype check .
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
For full guides, examples, and release notes, use the GitHub README and changelog.
|
|
@@ -1,27 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: archetype-py
|
|
3
|
-
Version: 0.2.3
|
|
4
|
-
Summary: Enforce architectural rules as code. Catch structural violations before they merge.
|
|
5
|
-
Project-URL: Homepage, https://github.com/MossabArektout/archetype-py
|
|
6
|
-
Project-URL: Documentation, https://github.com/MossabArektout/archetype-py
|
|
7
|
-
Author-email: Mossab Arektout <mossabarektout2000@gmail.com>
|
|
8
|
-
License: MIT
|
|
9
|
-
License-File: LICENSE
|
|
10
|
-
Classifier: Development Status :: 3 - Alpha
|
|
11
|
-
Classifier: Intended Audience :: Developers
|
|
12
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
-
Classifier: Programming Language :: Python :: 3
|
|
14
|
-
Classifier: Topic :: Software Development :: Quality Assurance
|
|
15
|
-
Requires-Python: >=3.11
|
|
16
|
-
Requires-Dist: click
|
|
17
|
-
Requires-Dist: networkx
|
|
18
|
-
Requires-Dist: rich
|
|
19
|
-
Provides-Extra: benchmarks
|
|
20
|
-
Provides-Extra: dev
|
|
21
|
-
Requires-Dist: hatch; extra == 'dev'
|
|
22
|
-
Requires-Dist: pytest; extra == 'dev'
|
|
23
|
-
Description-Content-Type: text/markdown
|
|
24
|
-
|
|
25
1
|
[](https://pypi.org/project/archetype-py/)
|
|
26
2
|
[](https://pypi.org/project/archetype-py/)
|
|
27
3
|
[](https://github.com/MossabArektout/archetype-py/blob/main/LICENSE)
|
|
@@ -40,6 +16,7 @@ Description-Content-Type: text/markdown
|
|
|
40
16
|
- [Minimum `architecture.py` Example](#minimum-architecturepy-example)
|
|
41
17
|
- [Features](#features)
|
|
42
18
|
- [Decorators and Commands](#decorators-and-commands)
|
|
19
|
+
- [Baseline Mode](#baseline-mode)
|
|
43
20
|
- [Perfect For](#perfect-for)
|
|
44
21
|
- [Installation](#installation)
|
|
45
22
|
- [Build & Test](#build--test)
|
|
@@ -229,11 +206,21 @@ def no_import_cycles() -> None:
|
|
|
229
206
|
- Import cycle detection
|
|
230
207
|
- Protected module boundaries
|
|
231
208
|
|
|
209
|
+
### Project Layout Support
|
|
210
|
+
- Flat package layouts
|
|
211
|
+
- Single `src/` layouts
|
|
212
|
+
- Namespace packages (PEP 420, without `__init__.py`)
|
|
213
|
+
- Monorepos with multiple `src` roots
|
|
214
|
+
|
|
232
215
|
### Workflow Features
|
|
233
216
|
- Rule grouping
|
|
234
217
|
- Warning-level rules
|
|
235
218
|
- Temporary rule skips with context
|
|
236
219
|
- Changed-file enforcement (`since`)
|
|
220
|
+
- Legacy baseline snapshot/suppression (`--write-baseline`, `--baseline`)
|
|
221
|
+
- Diff-scoped checks (`--changed-from <ref>`)
|
|
222
|
+
- First-class project config defaults (`archetype.toml`)
|
|
223
|
+
- Path exclusions (`--exclude`, `archetype.toml`, legacy `[tool.archetype].exclude`)
|
|
237
224
|
- Pytest integration
|
|
238
225
|
- CI-friendly exit codes
|
|
239
226
|
|
|
@@ -251,13 +238,211 @@ Rules are written in `architecture.py` with decorators. Below are all decorator-
|
|
|
251
238
|
|
|
252
239
|
Decorator order tip: place `@rule(...)` first, then wrappers like `@warn`, `@skip`, or `@since`.
|
|
253
240
|
|
|
241
|
+
### `@since` Date Format
|
|
242
|
+
|
|
243
|
+
`@since(...)` only accepts ISO calendar dates in `YYYY-MM-DD` format. The
|
|
244
|
+
decorator validates this when `architecture.py` is loaded, so invalid values
|
|
245
|
+
raise a clear `ValueError` instead of being ignored.
|
|
246
|
+
|
|
247
|
+
```python
|
|
248
|
+
@since("2026-01-01") # valid
|
|
249
|
+
def recent_violations_only() -> None:
|
|
250
|
+
...
|
|
251
|
+
|
|
252
|
+
@since("01-01-2026") # invalid: expected YYYY-MM-DD
|
|
253
|
+
def ambiguous_date() -> None:
|
|
254
|
+
...
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Invalid dates show the expectation in the error message:
|
|
258
|
+
|
|
259
|
+
```text
|
|
260
|
+
Invalid date '01-01-2026'. Expected format: YYYY-MM-DD.
|
|
261
|
+
```
|
|
262
|
+
|
|
254
263
|
| Command | Description | Example |
|
|
255
264
|
|---|---|---|
|
|
256
265
|
| `archetype init [path]` | Detects project structure and generates a starter `architecture.py` file. | `archetype init .` |
|
|
257
266
|
| `archetype check [path]` | Loads `architecture.py` and runs all registered architecture rules. | `archetype check .` |
|
|
258
267
|
| `archetype check [path] --group <name>` | Runs only rules that belong to the specified group. | `archetype check . --group core` |
|
|
268
|
+
| `archetype check [path] --exclude <pattern>` | Excludes paths from analysis and reporting (repeatable). | `archetype check . --exclude /vendor/ --exclude /migrations/` |
|
|
269
|
+
| `archetype check [path] --write-baseline <file> --baseline <file>` | Writes a baseline snapshot and suppresses matching legacy violations so only new ones fail. | `archetype check . --baseline archetype-baseline.json` |
|
|
270
|
+
| `archetype check [path] --changed-from <ref>` | Limits reported violations to files changed since `<ref>` (branch name or commit SHA). | `archetype check . --changed-from origin/main` |
|
|
271
|
+
| `archetype check [path] --github-annotations` | Emits GitHub Actions inline annotations (`::error`/`::warning`) for PR diffs. | `archetype check . --github-annotations` |
|
|
272
|
+
| `archetype install-hook [path]` | Installs (or updates) a managed git pre-commit hook that runs `archetype check` before each commit. | `archetype install-hook .` |
|
|
273
|
+
|
|
274
|
+
### Excluding Paths
|
|
275
|
+
|
|
276
|
+
Exclude noisy folders such as generated code, migrations, or vendored dependencies:
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
archetype check . --exclude /vendor/ --exclude /migrations/
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
You can also define defaults in `archetype.toml`:
|
|
283
|
+
|
|
284
|
+
```toml
|
|
285
|
+
exclude = ["/vendor/", "/migrations/"]
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Legacy compatibility: if `archetype.toml` is missing, Archetype still reads
|
|
289
|
+
`[tool.archetype]` from `pyproject.toml`.
|
|
290
|
+
|
|
291
|
+
### Project Config (`archetype.toml`)
|
|
292
|
+
|
|
293
|
+
Archetype auto-discovers `archetype.toml` in the project root passed to
|
|
294
|
+
`archetype check [path]`.
|
|
295
|
+
|
|
296
|
+
Supported defaults:
|
|
297
|
+
|
|
298
|
+
- `format` (`"text"` or `"json"`)
|
|
299
|
+
- `quiet` (`true`/`false`)
|
|
300
|
+
- `group` (`string`)
|
|
301
|
+
- `exclude` (`string` or `string[]`)
|
|
302
|
+
- `workers` (`int >= 1`)
|
|
303
|
+
- `cache` (`true`/`false`)
|
|
304
|
+
|
|
305
|
+
Example:
|
|
306
|
+
|
|
307
|
+
```toml
|
|
308
|
+
format = "json"
|
|
309
|
+
quiet = true
|
|
310
|
+
group = "Layer boundaries"
|
|
311
|
+
exclude = ["/vendor/", "/migrations/"]
|
|
312
|
+
workers = 4
|
|
313
|
+
cache = true
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
Precedence:
|
|
317
|
+
|
|
318
|
+
- CLI flags override `archetype.toml`.
|
|
319
|
+
- `archetype.toml` overrides built-in defaults.
|
|
320
|
+
- If config is missing, behavior remains unchanged.
|
|
321
|
+
|
|
322
|
+
### Pre-commit Hook
|
|
323
|
+
|
|
324
|
+
Install a git pre-commit hook in one command:
|
|
259
325
|
|
|
326
|
+
```bash
|
|
327
|
+
archetype install-hook .
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
Behavior:
|
|
331
|
+
|
|
332
|
+
- Creates `.git/hooks/pre-commit` if missing.
|
|
333
|
+
- Appends an Archetype-managed block if a custom pre-commit hook already exists.
|
|
334
|
+
- Updates the managed block if Archetype already installed it.
|
|
335
|
+
- Blocks the commit when `archetype check` fails, and prints violations directly in the commit terminal output.
|
|
336
|
+
|
|
337
|
+
### Changed-files Mode
|
|
338
|
+
|
|
339
|
+
Use diff scope to speed up checks in large repositories:
|
|
340
|
+
|
|
341
|
+
```bash
|
|
342
|
+
archetype check . --changed-from origin/main
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
`<ref>` can be a branch name (for example `origin/main`) or a commit SHA.
|
|
346
|
+
|
|
347
|
+
When enabled:
|
|
348
|
+
- Text output shows a scope banner with mode, ref, and changed file count.
|
|
349
|
+
- JSON output includes a `scope` object with mode/ref/file metadata.
|
|
350
|
+
|
|
351
|
+
### GitHub PR Annotations
|
|
352
|
+
|
|
353
|
+
Use GitHub Actions annotations to surface violations directly on PR diff lines:
|
|
354
|
+
|
|
355
|
+
```bash
|
|
356
|
+
archetype check . --github-annotations
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
In GitHub Actions, these appear as inline file/line annotations for each violation
|
|
360
|
+
while preserving the normal non-zero exit code when checks fail.
|
|
260
361
|
|
|
362
|
+
## Baseline Mode
|
|
363
|
+
|
|
364
|
+
Use baseline mode to adopt archetype in legacy repos without failing on pre-existing violations.
|
|
365
|
+
|
|
366
|
+
Create a baseline snapshot:
|
|
367
|
+
|
|
368
|
+
```bash
|
|
369
|
+
archetype check . --write-baseline archetype-baseline.json
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
Run checks against that baseline (matching old violations are suppressed):
|
|
373
|
+
|
|
374
|
+
```bash
|
|
375
|
+
archetype check . --baseline archetype-baseline.json
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
You can combine with JSON output to track counts:
|
|
379
|
+
|
|
380
|
+
```bash
|
|
381
|
+
archetype check . --baseline archetype-baseline.json --format json
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
JSON output includes:
|
|
385
|
+
- `schema_version`: top-level contract version for machine consumers
|
|
386
|
+
- `summary`: rule-level pass/fail/warn/skip counts
|
|
387
|
+
- `violations.total`: total current violations before suppression
|
|
388
|
+
- `violations.new`: violations not found in the baseline
|
|
389
|
+
- `violations.suppressed`: violations matched and suppressed by baseline
|
|
390
|
+
|
|
391
|
+
### JSON Contract (Versioned)
|
|
392
|
+
|
|
393
|
+
Archetype JSON output is versioned for CI and other integrations.
|
|
394
|
+
|
|
395
|
+
Current contract version:
|
|
396
|
+
|
|
397
|
+
- `schema_version: 1`
|
|
398
|
+
|
|
399
|
+
Versioning policy:
|
|
400
|
+
|
|
401
|
+
- Non-breaking additions (for example new optional fields) keep the same `schema_version`.
|
|
402
|
+
- Breaking shape changes (rename/remove/type changes) must increment `schema_version`.
|
|
403
|
+
- Contract tests in CI enforce the current schema shape.
|
|
404
|
+
|
|
405
|
+
Field definitions:
|
|
406
|
+
|
|
407
|
+
- `schema_version` (`int`): machine-readable contract version.
|
|
408
|
+
- `summary` (`object`): counts by rule status.
|
|
409
|
+
- `violations` (`object`): aggregate violation counts.
|
|
410
|
+
- `rules` (`array`): per-rule results with status and violations.
|
|
411
|
+
- `scope` (`object`, optional): present when `--changed-from` is used.
|
|
412
|
+
|
|
413
|
+
Example (`--format json`):
|
|
414
|
+
|
|
415
|
+
```json
|
|
416
|
+
{
|
|
417
|
+
"schema_version": 1,
|
|
418
|
+
"summary": {
|
|
419
|
+
"passed": 2,
|
|
420
|
+
"failed": 1,
|
|
421
|
+
"warned": 0,
|
|
422
|
+
"skipped": 0,
|
|
423
|
+
"total": 3
|
|
424
|
+
},
|
|
425
|
+
"violations": {
|
|
426
|
+
"total": 1,
|
|
427
|
+
"new": 1,
|
|
428
|
+
"suppressed": 0
|
|
429
|
+
},
|
|
430
|
+
"rules": [
|
|
431
|
+
{
|
|
432
|
+
"name": "api-must-not-import-db",
|
|
433
|
+
"status": "failed",
|
|
434
|
+
"group": "core",
|
|
435
|
+
"since_date": null,
|
|
436
|
+
"violations": [
|
|
437
|
+
{
|
|
438
|
+
"module": "simple_project.api",
|
|
439
|
+
"message": "Module 'simple_project.api' must not import 'simple_project.db'"
|
|
440
|
+
}
|
|
441
|
+
]
|
|
442
|
+
}
|
|
443
|
+
]
|
|
444
|
+
}
|
|
445
|
+
```
|
|
261
446
|
|
|
262
447
|
## Perfect For
|
|
263
448
|
|
|
@@ -298,6 +483,8 @@ archetype check .
|
|
|
298
483
|
- `0`: no blocking failures (passes and warnings only)
|
|
299
484
|
- `1`: one or more blocking rule failures
|
|
300
485
|
|
|
486
|
+
When `--baseline` is used, exit code `1` means there are **new** blocking violations not present in the baseline.
|
|
487
|
+
|
|
301
488
|
---
|
|
302
489
|
|
|
303
490
|
## Troubleshooting
|
|
@@ -306,6 +493,7 @@ archetype check .
|
|
|
306
493
|
- Rules seem to do nothing: confirm your rules are decorated with `@rule("...")`; undecorated functions are not registered.
|
|
307
494
|
- `@since(...)` behavior is unexpected: verify the date format is `YYYY-MM-DD` and that your git history is available in the checked path.
|
|
308
495
|
- Import path mismatches: use fully qualified module paths (`myapp.api`, not file paths like `src/api.py`).
|
|
496
|
+
- Namespace package imports not showing up: ensure modules live under discovered package roots (repo root, top-level `src/`, or nested monorepo `*/src` roots).
|
|
309
497
|
|
|
310
498
|
---
|
|
311
499
|
|
|
@@ -4,19 +4,27 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import pickle
|
|
6
6
|
from pathlib import Path
|
|
7
|
+
from typing import Iterable
|
|
7
8
|
|
|
8
9
|
import networkx as nx
|
|
9
10
|
|
|
11
|
+
from archetype.analysis.path_filters import is_path_excluded, normalize_exclude_patterns
|
|
12
|
+
|
|
10
13
|
|
|
11
14
|
def get_cache_path(project_root: Path) -> Path:
|
|
12
15
|
"""Return the cache file path for a project."""
|
|
13
16
|
return project_root.resolve() / ".archetype_cache"
|
|
14
17
|
|
|
15
18
|
|
|
16
|
-
def compute_file_signatures(
|
|
19
|
+
def compute_file_signatures(
|
|
20
|
+
project_root: Path,
|
|
21
|
+
*,
|
|
22
|
+
exclude_patterns: Iterable[str] | None = None,
|
|
23
|
+
) -> dict[str, float]:
|
|
17
24
|
"""Compute mtime signatures for Python files under project_root."""
|
|
18
25
|
signatures: dict[str, float] = {}
|
|
19
26
|
root = project_root.resolve()
|
|
27
|
+
normalized_excludes = normalize_exclude_patterns(exclude_patterns)
|
|
20
28
|
|
|
21
29
|
for file_path in root.rglob("*.py"):
|
|
22
30
|
parts = file_path.parts
|
|
@@ -24,7 +32,11 @@ def compute_file_signatures(project_root: Path) -> dict[str, float]:
|
|
|
24
32
|
continue
|
|
25
33
|
if ".venv" in parts or "venv" in parts or "site-packages" in parts:
|
|
26
34
|
continue
|
|
35
|
+
if is_path_excluded(file_path, root, normalized_excludes):
|
|
36
|
+
continue
|
|
27
37
|
signatures[str(file_path.resolve())] = file_path.stat().st_mtime
|
|
38
|
+
for excluded in normalized_excludes:
|
|
39
|
+
signatures[f"__archetype_exclude__:{excluded}"] = -1.0
|
|
28
40
|
|
|
29
41
|
return signatures
|
|
30
42
|
|