Skip to main content

Consolidated strict Python linting and static analysis tools - AST-based DTO discipline, facade-ban enforcement, and complexity metrics.

Project description

strict-suite

PyPI CI License: Apache 2.0 Python 3.11+

AST-based linter for Python DTO discipline and facade-ban enforcement. Consolidated strict Python linting and static analysis tools with complexity metrics.

Consolidated from strict-module 0.5.0. This monorepo consolidates the strict-module package and related linting infrastructure. Backward-compatible CLI entry points (strict-module and dto-strict) are preserved.

Python only. The strict-component (npm/TypeScript) stays in its own repo. This distribution is Python 3.11+ only.

Overview

Data Transfer Objects (DTOs) and facade discipline are critical for clean API boundaries, especially in regulated environments (healthcare, compliance, financial systems). stricts enforces these patterns via static AST analysis, with 6 focused rules:

  1. R001 (HIGH): Detect Dict[str, Any] or bare dict/list/tuple in service-layer function signatures.
  2. R002 (MEDIUM): Flag inline dict literals with 3+ string keys; exception tags can require justification.
  3. R003 (MEDIUM): Flag repr=False in dataclasses (canonical: plain @dataclass(frozen=True, slots=True)).
  4. R004 (HIGH): Demand exception tags on module-level functions (e.g., # facade - celery schedule).
  5. R005 (LOW): Encourage validators to use DTO.from_dict() pattern.
  6. R006 (HIGH): Detect typing.Any in function signatures (parameters and return types).

All rules are configurable; violations can be disabled, severity overridden, or paths scoped.

Install

pip install strict-suite

Or with optional development dependencies:

pip install strict-suite[dev]

Both CLI entry points (strict-module and dto-strict) are available post-install.

Quick Start

Basic CLI Usage

# Lint a single file
strict-module apps/compliance/services.py

# Lint a directory
strict-module apps/

# Output as GitHub Actions annotations
strict-module apps/ --format github

# Output as JSON
strict-module apps/ --format json

# Use the compat alias
dto-strict apps/

Run-Verified Example

Create a test file example.py with conformant code:

from dataclasses import dataclass

@dataclass(frozen=True, slots=True)
class UserDTO:
    """User data transfer object."""
    user_id: int
    email: str

class UserService:
    """Service to process users."""

    def process_user(self, user: UserDTO) -> None:
        """Process a user."""
        print(f"Processing {user.email}")

Then lint it:

$ strict-module example.py
# (no output = clean)

Now create a violating example. Save this as apps/test/services/bad_user.py:

from typing import Any

class UserService:
    def process_user(self, config: dict[str, Any]) -> None:  # R001: Dict[str, Any] in signature
        """Process a user."""
        return config.get("user_id")

Running the linter (from the repo root):

$ strict-module apps/test/services/bad_user.py
apps/test/services/bad_user.py:4: R001 Dict[str, Any] in signature: process_user
apps/test/services/bad_user.py:4: R006 typing.Any in parameter: process_user

All Rules Demonstrated

R002 (inline dict literals with 3+ keys):

# violation
def example():
    data = {"key1": "val1", "key2": "val2", "key3": "val3"}

R003 (anti-canonical repr=False on dataclass):

# violation
from dataclasses import dataclass

@dataclass(repr=False)
class MyDTO:
    value: int

R004 (module-level function without exception tag):

# violation (in a non-conftest file)
def process_data(item):
    return item["id"]

R007 (pytest fixtures outside conftest.py):

# violation
import pytest

@pytest.fixture
def my_fixture():
    return "value"

R008 (bare module-level test functions):

# violation
def test_something():
    assert True

LOC Cap Enforcement

Check lines-of-code limits:

$ strict-module loc-cap src/
src/long_module.py: soft-target 500 exceeded (523 lines)
src/another.py: hard-cap 694 exceeded (750 lines) FAIL

Generate baseline:

$ strict-module loc-cap src/ --generate-baseline > .loc-cap-baseline.txt

Using Both CLI Entry Points

Both strict-module and dto-strict are equivalent aliases:

strict-module apps/
dto-strict apps/

Configuration

Configuration lives in pyproject.toml:

[tool.strict-module]
service_paths = ["apps/*/services/*.py", "**/services/*.py"]
dto_paths = ["**/dtos.py", "**/dtos/*.py"]
exception_tags = [
    "facade - celery schedule",
    "FRAMEWORK",
    "aws-boundary"
]
disabled_rules = []
severity_overrides = {}
  • service_paths: Glob patterns for files to lint with R001 (strict mode).
  • dto_paths: Glob patterns for files expected to contain DTO definitions.
  • exception_tags: Recognized justification tags for module-level functions (R004 and R009). Example: aws-boundary exempts a function from the module-level function rules.
  • disabled_rules: List of rule IDs to disable (e.g., ["R005"]).
  • severity_overrides: Dict mapping rule IDs to override severity (e.g., {"R002": "HIGH", "R011": "INFO"}).

Backward compatibility: [tool.strict-module] config and .strict-module-baseline.json baseline files are supported.

Suppressing Violations

Use # noqa comments to suppress violations on specific lines. Supported forms:

  • Bare # noqa: Suppress any rule on that line.

    x = {1: 2, 3: 4, 5: 6}  # noqa
    
  • Rule-specific # noqa: R006: Suppress a single rule by ID.

    def process(x: Any) -> None:  # noqa: R006
        pass
    
  • Namespaced # noqa: strict-module-R006: Use the ruff-external-friendly namespaced form (recommended when running ruff alongside).

    def process(x: Any) -> None:  # noqa: strict-module-R006
        pass
    
  • Multiple rules # noqa: R006, R011: Suppress multiple rules on one line.

    def process(x: Any) -> None:  # noqa: R006, R011
        pass
    

When using ruff in parallel, declare [tool.ruff.lint] external = ["strict-module"] in pyproject.toml to prevent ruff from warning on the external rule codes.

Public API

The package exports the following public symbols:

  • __version__: Package version string (e.g., "0.1.0")
  • DtoStrictLinter: Main linter class for running AST analysis
  • Rule: Rule definition dataclass
  • RuleSeverity: Enum for rule severity levels (HIGH, MEDIUM, LOW)

Example usage:

from strict_module import DtoStrictLinter, Rule, RuleSeverity, __version__

print(f"strict-suite {__version__}")

linter = DtoStrictLinter()
violations = linter.lint_file("mymodule.py")
for v in violations:
    print(f"{v.rule_id} ({v.severity.value}): {v.message}")

Documentation

Full documentation, including rule details, configuration options, and integration guides, is available in the docs/ directory.

Historical changelog: see CHANGELOG-history.md for strict-module v0.1.0 through v0.5.0 release history.

Development

Install dev dependencies:

pip install -e ".[dev]"

Run tests:

python3.12 -m pytest strict_module/tests/ -v

Run the self-lint check:

strict-module strict_module/ --format text
dto-strict strict_module/

Run format and style checks:

ruff check strict_module/
ruff format --check strict_module/

Run LOC cap check:

strict-module loc-cap strict_module/

License

Apache License 2.0. See LICENSE for full text.

Contributing

Contributions are welcome. Please ensure:

  • All tests pass.
  • Code follows the project's linting rules (run strict-module on your changes).
  • Commit messages are clear and describe the change.
  • No AI attribution in commit messages.

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

strict_suite-0.3.0.tar.gz (106.9 kB view details)

Uploaded Source

Built Distribution

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

strict_suite-0.3.0-py3-none-any.whl (94.4 kB view details)

Uploaded Python 3

File details

Details for the file strict_suite-0.3.0.tar.gz.

File metadata

  • Download URL: strict_suite-0.3.0.tar.gz
  • Upload date:
  • Size: 106.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for strict_suite-0.3.0.tar.gz
Algorithm Hash digest
SHA256 1c81428d6e9529bc23c126e5e5e9d0da0975051a419d55133046087ad00be9b8
MD5 ba8c01f5675886c54405f9f11cf9e429
BLAKE2b-256 bfdc90763e6d2afe7b4c3e3b0345054fc50cdaf269ebb48d35851c8faca86fb8

See more details on using hashes here.

Provenance

The following attestation bundles were made for strict_suite-0.3.0.tar.gz:

Publisher: publish.yml on jekhator/strict-suite

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file strict_suite-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: strict_suite-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 94.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for strict_suite-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 453ec7ecd3b9fabfa82032417c5d7f761c9a52e85216d53f9388d22893ef2ff3
MD5 9d44ec52b061e88c9a8143d65c536321
BLAKE2b-256 7af9991363e09695bd94580142df03a58753e8804f7c161159e55ba1d092159c

See more details on using hashes here.

Provenance

The following attestation bundles were made for strict_suite-0.3.0-py3-none-any.whl:

Publisher: publish.yml on jekhator/strict-suite

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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