Skip to main content

Enforce architectural rules as code. Catch structural violations before they merge.

Project description

PyPI Python License CI

Architectural rules should not live in people’s heads

Architectural rules usually exist in engineers’ heads but nowhere in the codebase. Archetype turns those rules into executable Python checks that run in archetype check and pytest.

# architecture.py
from archetype import imports, rule

@rule("api does not import db")
def api_not_db() -> None:
    imports("myapp.api").must_not_import("myapp.db")

@rule("services only import db")
def services_only_db() -> None:
    imports("myapp.services").must_only_import_from("myapp.db")
$ archetype check .
✓ api does not import db
✗ services only import db
  - myapp.services.user -> myapp.cache: Module 'myapp.services.user' imports 'myapp.cache', which is outside the allowed set: ('myapp.db',).
Summary: 1 passed, 1 failed, 2 total rules.

Installation

pip install archetype-py

Quickstart

  1. Install Archetype.
pip install archetype-py
  1. Create architecture.py in your project root.
touch architecture.py
  1. Add your first rule with the imports DSL.
# architecture.py
from archetype import imports, rule

@rule("api does not import db")
def api_not_db() -> None:
    imports("myapp.api").must_not_import("myapp.db")
  1. Run the checker.
archetype check .
  1. Read the output and fix violations.
✓ api does not import db
Summary: 1 passed, 0 failed, 1 total rules.

If your project already runs pytest, running pytest is sufficient because Archetype rules are collected and executed by the pytest plugin.

Why Archetype exists

Style tools enforce how code looks. Type tools enforce what values can flow through code. Architectural tools enforce which parts of the system are allowed to depend on which other parts.

Pylint and similar linters are strong at local code quality checks, and Mypy is strong at static type correctness. Neither is designed to express team-level dependency contracts like “API cannot import DB” or “internal modules are private outside their package boundary.”

Archetype keeps rules in architecture.py as normal Python functions, not static YAML declarations. That makes rules executable, reviewable, testable, and easy to evolve with the codebase using the same language and tooling your team already uses.

Built-in rules reference

layers

Enforces that lower layers do not import upper layers.

from archetype import rule
from archetype.rules import layers

@rule("layers are ordered")
def layer_order() -> None:
    layers(["myapp.api", "myapp.services", "myapp.db"]).are_ordered()

module (module boundaries)

Enforces that a protected internal module is only imported from an allowed parent scope.

from archetype import rule
from archetype.rules import module

@rule("internal auth is private")
def auth_boundary() -> None:
    module("myapp.auth.internal").only_imported_within("myapp.auth")

classes_in and functions_in (naming conventions)

Enforces class naming patterns and required top-level functions in matched modules.

from archetype import rule
from archetype.rules import classes_in, functions_in

@rule("service classes end with Service")
def class_names() -> None:
    classes_in("myapp.services").all_match(r".*Service$")

@rule("api modules expose handle")
def api_handle_exists() -> None:
    functions_in("myapp.api").must_include("handle")

no_cycles

Enforces that there are no import cycles in the whole project or in a selected module scope.

from archetype import rule
from archetype.rules import no_cycles

@rule("no cycles in services")
def services_no_cycles() -> None:
    no_cycles("myapp.services")

Writing custom rules

from archetype import imports, rule

@rule("custom architecture policy")
def custom_policy() -> None:
    imports("myapp.api").must_not_import("myapp.db")
    imports("myapp.services").has_no_cycles()
    imports("myapp.services").must_only_import_from("myapp.db", "myapp.shared")

Any Python function decorated with @rule that returns without raising is a passing rule. Rules can use the full Python language, so you can encode architecture constraints that do not fit generic linters or static config.

CI integration

name: Archetype Check

on:
  push:
    branches: [main]
  pull_request:

jobs:
  archetype:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.11"
      - run: |
          python -m pip install --upgrade pip
          pip install archetype-py
      - run: archetype check .

If your CI already runs pytest, no additional CI configuration is required.

Contributing

Source code and issue tracking are in the GitHub repository: https://github.com/MossabArektout/archetype-py. Contributions are welcome; open an issue first to discuss scope before submitting a pull request.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

archetype_py-0.1.1.tar.gz (16.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

archetype_py-0.1.1-py3-none-any.whl (18.3 kB view details)

Uploaded Python 3

File details

Details for the file archetype_py-0.1.1.tar.gz.

File metadata

  • Download URL: archetype_py-0.1.1.tar.gz
  • Upload date:
  • Size: 16.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: Hatch/1.16.5 cpython/3.11.15 HTTPX/0.28.1

File hashes

Hashes for archetype_py-0.1.1.tar.gz
Algorithm Hash digest
SHA256 0499e8096709234e83c38c3d36ab0bea463bd476d583c44b1876d1fd6e993035
MD5 3309dae4a53f3094ab2636c0cb20d044
BLAKE2b-256 e9b829c584518e3d1044e974932460eac1b66b4d0ebb5fe879923e47005a1303

See more details on using hashes here.

File details

Details for the file archetype_py-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: archetype_py-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 18.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: Hatch/1.16.5 cpython/3.11.15 HTTPX/0.28.1

File hashes

Hashes for archetype_py-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 c6feedb56405ae82cd7a0f6c3c7de3e275bd53a4f291a02033f6d6c038a5d450
MD5 fb32efde06e764fa86670293c44cde3e
BLAKE2b-256 b7f4d59c1bd9b52f23d89d3b5b6168bf054a384fce54f0ed201d18594301e6e9

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page