### Install ArchUnitPython Source: https://github.com/lukasniessen/archunitpython/blob/main/README.md Install the ArchUnitPython package using pip. This is the first step to start enforcing architectural rules. ```bash pip install archunitpython ``` -------------------------------- ### Example: Rule for External Module Dependencies Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/project_files.md This example demonstrates how to build a rule that asserts certain files should not depend on specific external modules like 'requests' or any module starting with 'django'. ```python rule = ( project_files("src/") .in_folder("**/core/**") .should_not() .depend_on_external_modules() .matching("requests") .matching("django*") ) ``` -------------------------------- ### LCOM96b metric example Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/metrics.md Example demonstrating how to use the LCOM96b metric to set a threshold. ```python # LCOM96b is most commonly used rule = metrics("src/").lcom().lcom96b().should_be_below(0.7) ``` -------------------------------- ### Project Setup and Commands Source: https://github.com/lukasniessen/archunitpython/blob/main/CONTRIBUTING.md Commands for setting up the development environment, running tests, and linting the code. ```bash git clone https://github.com/LukasNiessen/ArchUnitPython.git ``` ```bash pip install -e ".[dev]" ``` ```bash pytest ``` ```bash ruff check src/ ``` ```bash mypy src/archunitpython/ ``` -------------------------------- ### Example: Understanding Import Patterns Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/graph-extraction.md This example demonstrates various Python import statements and how they are interpreted by ArchUnit Python for dependency graph extraction. It shows absolute, relative, and type-only imports. ```python # myproject/api/handlers.py import os from typing import TYPE_CHECKING from myproject.business import logic # Absolute within project from . import schemas # Relative (same package) from ..database import models # Relative (parent package) if TYPE_CHECKING: from myproject.database import Database # Type-only # Extracted as edges: # 1. myproject/api/handlers.py → os (external, IMPORT) # 2. myproject/api/handlers.py → myproject/business/logic.py (internal, FROM_IMPORT) # 3. myproject/api/handlers.py → myproject/api/schemas.py (internal, RELATIVE_IMPORT) # 4. myproject/api/handlers.py → myproject/database/models.py (internal, RELATIVE_IMPORT) # 5. myproject/api/handlers.py → myproject/database/__init__.py (internal, TYPE_IMPORT) ``` -------------------------------- ### Common Glob Pattern Examples Source: https://github.com/lukasniessen/archunitpython/blob/main/README.md Illustrates common usage of glob patterns for matching filenames, folders, and paths. These examples cover basic wildcards like '*' and '**'. ```python # Filename patterns .with_name("*.py") # All Python files .with_name("*_service.py") # Files ending with _service.py .with_name("test_*.py") # Files starting with test_ # Folder patterns .in_folder("**/services") # Any services folder at any depth .in_folder("src/services") # Exact src/services folder .in_folder("**/test/**") # Any folder containing test in path # Path patterns .in_path("src/**/*.py") # Python files anywhere under src .in_path("**/test/**/*_test.py") # Test files in any test folder ``` -------------------------------- ### Local Documentation Generation Source: https://github.com/lukasniessen/archunitpython/blob/main/CONTRIBUTING.md Commands to install pdoc, generate documentation locally, and serve it with live reload. ```bash pip install pdoc ``` ```bash pdoc src/archunitpython/ -o docs/ ``` ```bash pdoc src/archunitpython/ ``` -------------------------------- ### File Dependency Violation Example Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/errors.md Illustrates a file dependency violation message. ```text File dependency violation 'src/api/handlers.py' depends on 'src/database/models.py' ``` -------------------------------- ### CI/CD Integration with GitHub Actions Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/testing.md Set up GitHub Actions to automatically run ArchUnitPy tests on code pushes and pull requests. This example installs dependencies and executes pytest. ```yaml # GitHub Actions example name: Architecture Tests on: [push, pull_request] jobs: architecture: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 with: python-version: '3.10' - name: Install dependencies run: | pip install archunitpython pytest - name: Run architecture tests run: pytest tests/test_architecture.py -v - name: Upload architecture logs if: always() uses: actions/upload-artifact@v2 with: name: architecture-logs path: logs/ ``` -------------------------------- ### Regex Pattern Examples Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/common.md Provides examples of compiled regular expressions for pattern matching. These are suitable for more complex matching requirements beyond simple wildcards. ```python import re re.compile(r".*Service\.py$") # Regex alternative to "*_service.py" ``` ```python re.compile(r"^src/.*/(model|view)") # More complex patterns ``` ```python re.compile(r"[A-Z][a-z]*Util\.py") # Camel case utilities ``` -------------------------------- ### Abstractness Rule Example Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/metrics.md Example of how to create a rule to check if the abstractness of modules is above a certain threshold. ```python rule = metrics("src/").distance().abstractness().should_be_above(0.3) ``` -------------------------------- ### Glob String Examples Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/common.md Illustrates various glob string patterns for matching file names and paths. These are useful for simple, fnmatch-style wildcard matching. ```python "*.py" # Any Python file ``` ```python "*_service.py" # Files ending with _service.py ``` ```python "src/**/*.py" # Any .py under src/ at any depth ``` ```python "**/test/**" # Any path containing /test/ ``` ```python "test_*.py" # Files starting with test_ ``` -------------------------------- ### Example Usage of EmptyTestViolation Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/common.md Demonstrates how EmptyTestViolation is raised when a rule's filter patterns match no files. This example assumes allow_empty_tests=False. ```python from archunitpython import project_files, CheckOptions rule = project_files("src/").in_folder("**/nonexistent/**").should().have_no_cycles() violations = rule.check() # violations[0] is EmptyTestViolation if allow_empty_tests=False ``` -------------------------------- ### Basic Rule Check with Defaults Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/common.md Shows a basic example of defining a rule using 'project_files' and asserting its validity with default check options. Raises an AssertionError if any violations are found. ```python from archunitpython import project_files, assert_passes rule = project_files("src/").should().have_no_cycles() assert_passes(rule) # Raises AssertionError if violations found ``` -------------------------------- ### Complete ArchUnitPython Configuration Example Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/configuration.md Demonstrates setting up comprehensive configuration including test pass conditions, detailed logging, cache management, and import handling. Apply these options to a rule check for customized analysis. ```python from archunitpython import ( project_files, assert_passes, CheckOptions, LoggingOptions, ) # Create comprehensive configuration options = CheckOptions( # Allow tests with no matching files to pass allow_empty_tests=True, # Enable detailed logging to file logging=LoggingOptions( enabled=True, level="debug", log_file=True, append_to_log_file=False, ), # Force re-analysis of the project clear_cache=True, # Ignore type-checking-only imports ignore_type_checking_imports=True, ) # Apply configuration to rule check rule = ( project_files("src/") .in_folder("**/services/**") .should() .have_no_cycles() ) # Check with configuration assert_passes(rule, options) # Debug logs available in logs/archunit-*.log ``` -------------------------------- ### Example of ArchUnitPython Violation Output Source: https://github.com/lukasniessen/archunitpython/blob/main/README.md This is an example of the informative output provided when ArchUnitPython tests fail, detailing file paths and specific violation types. ```text Found 2 architecture violation(s): 1. File dependency violation 'src/api/bad_shortcut.py' depends on 'src/retrieval/vector_store.py' 2. File dependency violation 'src/api/bad_shortcut.py' depends on 'src/retrieval/embedder.py' ``` -------------------------------- ### Slice Dependency Violation Example Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/errors.md Illustrates a slice architecture violation message. ```text Slice dependency violation Slice 'presentation' depends on slice 'database' (not allowed by diagram) ``` -------------------------------- ### Fixture-Based Setup for ArchUnitPy Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/testing.md Configure ArchUnitPy check options and source paths using pytest fixtures with session or module scope for efficient test execution. ```python import pytest from archunitpython import project_files, CheckOptions @pytest.fixture(scope="session") def arch_check_options(): return CheckOptions( allow_empty_tests=False, clear_cache=True, ignore_type_checking_imports=True, ) @pytest.fixture(scope="module") def src_path(): return "src/" def test_cycles(src_path, arch_check_options): from archunitpython import assert_passes rule = project_files(src_path).should().have_no_cycles() assert_passes(rule, arch_check_options) ``` -------------------------------- ### Format Violations Example Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/errors.md Demonstrates how to format ArchUnitPy violations into a human-readable string. Ensure violations are collected before formatting. ```python from archunitpython import project_files, format_violations rule = project_files("src/").should().have_no_cycles() violations = rule.check() if violations: print(format_violations(violations)) # Output: # Found 3 architecture violation(s): # # 1. Circular dependency violation # src/a.py → src/b.py → src/a.py # # 2. File dependency violation # 'src/api/bad_shortcut.py' depends on 'src/database/models.py' # # 3. Pattern violation # File 'src/handler.py' does not match pattern '*_service.py' ``` -------------------------------- ### Extract and Print Graph Edges Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/graph-extraction.md Example demonstrating how to extract the project's dependency graph and iterate through its edges to print import relationships. ```python from archunitpython import extract_graph graph = extract_graph("src/") for edge in graph: print(f"{edge.source} imports {edge.target}") if edge.external: print(f" (external module)") print(f" Import types: {edge.import_kinds}") ``` -------------------------------- ### Glob Patterns for File Matching Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/INDEX.md Examples of glob patterns used to match file paths. These patterns support wildcards for flexible file selection. ```text "*.py" # All Python files "*_service.py" # Files ending with _service.py "src/**/*.py" # Any .py under src/ at any depth "**/test/**" # Any path containing /test/ ``` -------------------------------- ### Conventional Commits Examples Source: https://github.com/lukasniessen/archunitpython/blob/main/CONTRIBUTING.md Examples of commit messages following the Conventional Commits specification for automated versioning and changelog generation. ```git fix: handle empty project in graph extraction ``` ```git feat: add support for namespace packages ``` ```git feat!: remove deprecated API ``` -------------------------------- ### Verifying Project Path to Resolve File Errors Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/errors.md Ensure the specified project path is correct and accessible to prevent TechnicalErrors like 'No such file or directory'. This example demonstrates verifying the directory's existence before checking rules. ```python # 3. Verify project path rule = project_files("correct/path/src").in_folder("**/services/**").should().have_no_cycles() ``` ```python import os # Verify path exists if os.path.isdir("src/"): rule = project_files("src/").should().have_no_cycles() else: print("Project path not found") ``` -------------------------------- ### Validate Architecture Against PlantUML Diagram from File Source: https://github.com/lukasniessen/archunitpython/blob/main/README.md This example demonstrates how to validate your architecture against a PlantUML diagram stored in a file. Ensure the file path is correct and the diagram is properly formatted. ```python def test_from_file(): rule = ( project_slices("src/") .defined_by("src/(**)/**") .should() .adhere_to_diagram_in_file("docs/architecture.puml") ) assert_passes(rule) ``` -------------------------------- ### Handle MetricViolation Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/errors.md Example of checking for MetricViolations, such as a class exceeding the maximum method count, and printing violation details. ```python from archunitpython import metrics, MetricViolation rule = metrics("src/").count().method_count().should_be_below(20) violations = rule.check() for v in violations: if isinstance(v, MetricViolation): print(f"Class {v.class_name} has {v.metric_value} methods (max: {v.threshold})") print(f"File: {v.file_path}") ``` -------------------------------- ### External Dependency Violation Example Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/errors.md Illustrates an external dependency violation message. ```text External dependency violation 'src/core/logic.py' depends on 'requests' (external module) ``` -------------------------------- ### Pattern Violation Example Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/errors.md Illustrates a pattern matching violation message. ```text Pattern violation File 'src/handler.py' does not match pattern '*_service.py' ``` -------------------------------- ### Begin positive assertion Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/project_files.md Call `should()` to start building a positive assertion, indicating that the selected files SHOULD possess certain properties. This method returns a builder for positive assertions. ```python def should(self) -> PositiveMatchPatternFileConditionBuilder: ``` -------------------------------- ### Check for Circular Dependencies Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/INDEX.md Use `project_files` to define rules for file dependencies. This example checks for circular dependencies within the 'src/' directory. Ensure `archunitpython` is imported. ```python from archunitpython import project_files, assert_passes # Check for circular dependencies rule = project_files("src/").should().have_no_cycles() assert_passes(rule) ``` -------------------------------- ### Circular Dependency Violation Example Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/errors.md Illustrates a circular dependency violation message. ```text Circular dependency violation src/a.py → src/b.py → src/c.py → src/a.py ``` -------------------------------- ### Core Functions Entry Points Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/README.md These are the primary functions to use when starting with ArchUnit Python. They serve as entry points for various analysis tasks. ```python project_files(path) → FileConditionBuilder metrics(path) → MetricsBuilder project_slices(path) → SliceConditionBuilder extract_graph(path) → Graph assert_passes(rule, options) → None format_violations(violations) → str clear_graph_cache() → None ``` -------------------------------- ### Example: Asserting No Slice Dependency Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/project_slices.md Demonstrates how to use NegativeConditionBuilder to assert that the 'services' slice does not depend on the 'controllers' slice. This is useful for enforcing architectural rules. ```python rule = ( project_slices("src/") .defined_by("src/(**)/**") .should_not() .contain_dependency("services", "controllers") ) assert_passes(rule) ``` -------------------------------- ### Handle FileCountViolation Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/errors.md Example of checking for FileCountViolations, such as a file exceeding the maximum lines of code, and printing violation details. ```python from archunitpython import metrics, FileCountViolation rule = metrics("src/").count().lines_of_code().should_be_below(500) violations = rule.check() for v in violations: if isinstance(v, FileCountViolation): print(f"{v.file_path}: {v.metric_value} lines (max: {v.threshold})") ``` -------------------------------- ### Custom Condition Violation Example Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/errors.md Illustrates a custom condition violation message. ```text Custom rule violation 'src/utils.py' does not satisfy: Python files should have docstrings ``` -------------------------------- ### Fixing EmptyTestViolation by Adjusting Patterns Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/errors.md When a pattern matches no files, leading to an EmptyTestViolation, correct the pattern to include the intended files. This example shows how to fix a pattern that incorrectly targets a non-existent directory. ```python rule = project_files("src/").in_folder("**/nonexistent/**").should().have_no_cycles() violations = rule.check() # violations contains EmptyTestViolation ``` ```python # 1. Fix the pattern rule = project_files("src/").in_folder("**/services/**").should().have_no_cycles() ``` -------------------------------- ### Regex Patterns for File Matching Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/INDEX.md Examples of regular expressions used for more complex file path matching. These leverage Python's `re` module. ```python import re re.compile(r".*Service\.py$") re.compile(r"^src/(api|core)") ``` -------------------------------- ### Limit File Lines of Code Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/INDEX.md Use the `metrics` API to enforce code quality standards. This example limits the lines of code per file to below 500. Ensure `archunitpython` is imported. ```python from archunitpython import metrics, assert_passes # Limit file size rule = metrics("src/").count().lines_of_code().should_be_below(500) assert_passes(rule) ``` -------------------------------- ### Begin negative assertion Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/project_files.md Call `should_not()` to start building a negative assertion, indicating that the selected files SHOULD NOT possess certain properties. This method returns a builder for negative assertions. ```python def should_not(self) -> NegatedMatchPatternFileConditionBuilder: ``` -------------------------------- ### CI Integration with GitHub Actions Source: https://github.com/lukasniessen/archunitpython/blob/main/README.md Integrate ArchUnitPython tests into your GitHub Actions workflow. This example shows how to run pytest, including architecture tests, as part of your CI pipeline. Ensure the test file path matches your project structure. ```yaml # GitHub Actions - name: Run Architecture Tests run: pytest tests/test_architecture.py -v ``` -------------------------------- ### Enforce Layered Architecture Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/INDEX.md Define rules to enforce architectural layers using `project_files`. This example prevents the 'presentation' layer from depending on the 'database' layer. Ensure `archunitpython` is imported. ```python from archunitpython import project_files, assert_passes # Enforce layered architecture rule = ( project_files("src/") .in_folder("**/presentation/**") .should_not() .depend_on_files() .in_folder("**/database/**") ) assert_passes(rule) ``` -------------------------------- ### Descriptive Architecture Test Names Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/testing.md Provides examples of good test names for architecture tests. Descriptive names make it easier to understand the purpose of each test at a glance. ```python # Good test names def test_presentation_layer_isolation(): ... def test_services_cycle_free(): ... def test_database_models_are_small(): ... ``` -------------------------------- ### Project Navigation Structure Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/README.md This markdown code block illustrates the hierarchical structure of the ArchUnitPython documentation files, showing the main README.md and its subdirectories for API Reference and Reference Guides. ```markdown README.md (this file) ├── INDEX.md (master index) │ ├── API Reference │ ├── project_files.md (1,400 words) │ ├── metrics.md (1,587 words) │ ├── project_slices.md (1,198 words) │ ├── common.md (1,550 words) │ ├── testing.md (964 words) │ └── graph-extraction.md (1,522 words) │ └── Reference Guides ├── types.md (1,355 words) ├── configuration.md (1,111 words) └── errors.md (1,203 words) ``` -------------------------------- ### Configure Logging for Rule Checks Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/configuration.md Example of how to enable debug logging to a file for ArchUnit Python rule checks. This configuration sets logging to debug level and directs output to a new log file. ```python from archunitpython import CheckOptions, LoggingOptions # Enable all debug logging to file options = CheckOptions( logging=LoggingOptions( enabled=True, level="debug", log_file=True, append_to_log_file=False, ) ) rule = project_files("src/").should().have_no_cycles() violations = rule.check(options) ``` -------------------------------- ### Running Architecture Tests Separately with Pytest Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/testing.md Provides configuration and command-line examples for running architecture tests separately from unit tests using Pytest. This is achieved by organizing tests into different files and using specific command-line arguments. ```yaml # pytest.ini [pytest] testpaths = tests python_files = test_*.py # tests/test_unit.py (fast unit tests) # tests/test_architecture.py (slower architecture tests) ``` ```bash # Run only architecture tests pytest tests/test_architecture.py -v # Run all tests pytest tests/ -v ``` -------------------------------- ### Using Absolute Path to Resolve File Access Errors Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/errors.md To ensure reliable file access and avoid 'No such file or directory' errors, use absolute paths for your project directory. This example demonstrates converting a relative path to an absolute path using `os.path.abspath`. ```python # Use absolute path import os abs_path = os.path.abspath("src/") rule = project_files(abs_path).should().have_no_cycles() ``` -------------------------------- ### Pattern Matching for File Selection Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/common.md Demonstrates various ways to define rules using pattern matching for file selection, including glob patterns, regular expressions, complex path specifications, and class name matching. ```python from archunitpython import project_files import re # Glob pattern rule1 = project_files("src/").with_name("*.py") # Regex pattern rule2 = project_files("src/").with_name(re.compile(r".*Service\.py$")) # Complex paths rule3 = project_files("src/").in_path("src/**/models/**/*.py") # Class name matching rule4 = project_files("src/").for_classes_matching("*Repository") ``` -------------------------------- ### Configure Logging Options for Debugging Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/configuration.md Set up debug logging output using CheckOptions and LoggingOptions. Logs are written to a timestamped file in the logs/ directory. ```python from archunitpython import CheckOptions, LoggingOptions options = CheckOptions( logging=LoggingOptions( enabled=True, level="debug", log_file=True, ) ) rule = project_files("src/").should().have_no_cycles() violations = rule.check(options) # Logs written to logs/archunit-YYYY-MM-DD_HH-MM-SS.log ``` -------------------------------- ### Handle EmptyTestViolation Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/errors.md Demonstrates how an EmptyTestViolation is raised when patterns match no files and how to bypass it using allow_empty_tests. ```python from archunitpython import project_files, CheckOptions, EmptyTestViolation rule = project_files("src/").in_folder("**/nonexistent/**").should().have_no_cycles() # Without allow_empty_tests violations = rule.check() # violations[0] is EmptyTestViolation # With allow_empty_tests options = CheckOptions(allow_empty_tests=True) violations = rule.check(options) # violations is [] (passes) ``` -------------------------------- ### Checkable Object Creation and Checking Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/common.md Demonstrates how fluent API chains produce Checkable objects and how these objects can be checked for violations or asserted directly. ```python # These all return Checkable objects: rule1 = project_files("src/").should().have_no_cycles() rule2 = metrics("src/").count().lines_of_code().should_be_below(500) rule3 = project_slices("src/").defined_by("src/(**)/**").should().have_no_cycles() # All can be checked the same way: violations = rule1.check() assert_passes(rule2) ``` -------------------------------- ### Configure Rule Check Options Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/common.md Use `CheckOptions` to customize rule execution. Set `allow_empty_tests` to true to pass rules that match no files, or `clear_cache` to true to refresh the dependency graph before checking. Configure logging with `LoggingOptions`. ```python from archunitpython import CheckOptions, LoggingOptions options = CheckOptions( allow_empty_tests=True, clear_cache=True, logging=LoggingOptions( enabled=True, level="debug", log_file=True, ), ) violations = rule.check(options) ``` -------------------------------- ### Check for Violation Instances Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/errors.md Example of checking if a returned violation is an instance of the base Violation class. ```python from archunitpython import project_files, Violation rule = project_files("src/").should().have_no_cycles() violations = rule.check() if violations: for v in violations: if isinstance(v, Violation): print(f"Rule violated: {v}") ``` -------------------------------- ### Rule Check with Custom Options Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/common.md Demonstrates how to perform a rule check with custom 'CheckOptions', including enabling logging and ignoring type checking imports. This allows for more granular control over the checking process. ```python from archunitpython import ( project_files, assert_passes, CheckOptions, LoggingOptions, ) options = CheckOptions( allow_empty_tests=True, clear_cache=True, ignore_type_checking_imports=True, logging=LoggingOptions( enabled=True, level="debug", log_file=True, ), ) rule = project_files("src/").should().have_no_cycles() assert_passes(rule, options) ``` -------------------------------- ### Create FileConditionBuilder Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/project_files.md Initializes the file-level architecture rule builder. Specify the project path to analyze; defaults to the current working directory. ```python def project_files(project_path: str | None = None) -> FileConditionBuilder: pass ``` -------------------------------- ### Handle UserError Exception Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/errors.md Example of catching a UserError when ArchUnitPython detects incorrect API usage, like calling check() without should(). ```python from archunitpython import project_files, UserError try: rule = project_files("src/") # Calling check() without should()/should_not() would be an error except UserError as e: print(f"API usage error: {e}") # Fix the API call ``` -------------------------------- ### Handle TechnicalError Exception Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/errors.md Example of catching a TechnicalError when ArchUnitPython encounters issues like file system errors or invalid Python syntax. ```python from archunitpython import project_files, TechnicalError try: rule = project_files("/nonexistent/path").should().have_no_cycles() rule.check() except TechnicalError as e: print(f"Technical error: {e}") # Handle file system issues ``` -------------------------------- ### Create a New Metrics Rule Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/metrics.md Initializes the `MetricsBuilder` for applying filters and selecting metric types. Specify the project path to analyze. ```python from archunitpython import metrics, assert_passes # Lines of code metric rule = metrics("src/").count().lines_of_code().should_be_below(500) assert_passes(rule) # Cohesion metric rule = metrics("src/").lcom().lcom96b().should_be_below(0.5) assert_passes(rule) # Distance metrics rule = metrics("src/").distance().instability().should_be_below(0.8) assert_passes(rule) ``` -------------------------------- ### Extract Dependency Graph with Default Excludes Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/configuration.md Use `extract_graph` to get the dependency graph. By default, it excludes common directories like `__pycache__` and `.venv`. ```python from archunitpython import extract_graph, CheckOptions # Default excludes graph = extract_graph("src/") ``` -------------------------------- ### Check Dependency Directions Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/project_files.md Applies file-level rules to assert dependency directions between specified folders. Ensure ArchUnit is imported and `assert_passes` is available. ```python from archunitpython import project_files, assert_passes # Check dependency directions rule = ( project_files("src/") .in_folder("**/presentation/**") .should_not() .depend_on_files() .in_folder("**/database/**") ) assert_passes(rule) ``` -------------------------------- ### Ensure Class Cohesion Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/INDEX.md Enforce class cohesion using `metrics` and the LCOM96b metric. This example requires the LCOM96b value to be below 0.5. Ensure `archunitpython` is imported. ```python from archunitpython import metrics, assert_passes # Ensure class cohesion rule = metrics("src/").lcom().lcom96b().should_be_below(0.5) assert_passes(rule) ``` -------------------------------- ### metrics(project_path: str | None = None) Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/configuration.md Calculates project metrics. This function behaves identically to `project_files()`. ```APIDOC ## metrics(project_path: str | None = None) ### Description Calculates project metrics. This function behaves identically to `project_files()`. ### Parameters #### Path Parameters - **project_path** (str | None) - Optional - Root directory to analyze. Defaults to the current working directory. ### Request Example ```python from archunitpython import metrics # Explicit path metrics_result = metrics("/home/user/myproject/src") # Default to cwd metrics_result = metrics() ``` ``` -------------------------------- ### Configure Project Path for Analysis Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/configuration.md Use `project_files` to specify the root directory for analysis. It accepts absolute, relative, or `None` for the current working directory. ```python from archunitpython import project_files # Explicit path rule = project_files("/home/user/myproject/src").should().have_no_cycles() # Relative path rule = project_files("src/").should().have_no_cycles() # Default to cwd rule = project_files(None).should().have_no_cycles() rule = project_files().should().have_no_cycles() # Same as above ``` -------------------------------- ### Build and Cache Graph Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/graph-extraction.md Builds the graph and caches it for future use. Reusing the cache is significantly faster than rebuilding. ```python from archunitpython import extract_graph, CheckOptions # Build and cache (slow) graph1 = extract_graph("src/") # Reuse cache (fast) graph2 = extract_graph("src/") # Force rebuild graph3 = extract_graph("src/", options=CheckOptions(clear_cache=True)) ``` -------------------------------- ### Define File Dependency Rule Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/project_files.md Use this to define rules that check if certain files depend on other files, optionally within specific folders. This example shows a negated rule. ```python rule = ( project_files("src/") .in_folder("**/presentation/**") .should_not() .depend_on_files() .in_folder("**/database/**") ) violations = rule.check() ``` -------------------------------- ### Define and Check Custom Metric: Method-Field Ratio Source: https://github.com/lukasniessen/archunitpython/blob/main/README.md Shows how to define a custom metric to calculate the ratio of methods to fields for classes and enforce a rule on it. Requires `metrics` and `assert_passes`. ```python def test_method_field_ratio(): rule = ( metrics("src/") .custom_metric( "methodFieldRatio", "Ratio of methods to fields", lambda ci: len(ci.methods) / max(len(ci.fields), 1), ) .should_be_below(10) ) assert_passes(rule) ``` -------------------------------- ### Adjusting Metric Thresholds Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/errors.md If metric rules are too strict and result in numerous violations, adjust the threshold to a more appropriate value. This example shows how to change the `lines_of_code` threshold from 500 to 1000. ```python from archunitpython import metrics, FileCountViolation rule = metrics("src/").count().lines_of_code().should_be_below(500) violations = rule.check() # Analyze violations large_files = [v for v in violations if isinstance(v, FileCountViolation)] print(f"{len(large_files)} files exceed limit") # Adjust threshold rule2 = metrics("src/").count().lines_of_code().should_be_below(1000) violations2 = rule2.check() ``` -------------------------------- ### Check File Dependencies Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/project_files.md Use the `check` method to execute a file dependency rule and retrieve any violations. Returns an empty list if the rule passes. ```python def check(self, options: CheckOptions | None = None) -> list[Violation] ``` -------------------------------- ### Use assert_passes for Clearer Error Messages Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/errors.md Utilize the assert_passes function to get well-formatted and informative error messages when architecture rules are violated. This is an alternative to directly catching AssertionErrors. ```python from archunitpython import project_files, assert_passes, format_violations rule = project_files("src/").should().have_no_cycles() try: assert_passes(rule) except AssertionError as e: print(e) # Well-formatted error message # Output: # Found 2 architecture violation(s): # 1. Circular dependency violation # src/a.py → src/b.py → src/a.py ``` -------------------------------- ### Analysis Scope: Full Project vs. Single Module Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/graph-extraction.md Compares the performance of analyzing the entire project versus a single module. Analyzing a single module is faster. ```python from archunitpython import extract_graph # Slow: entire project graph_all = extract_graph(".") # Fast: single module graph_api = extract_graph("src/api") ``` -------------------------------- ### Custom Architecture Report Generation Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/testing.md Demonstrates how to generate a custom architecture report by writing formatted violations to a file. This is useful for detailed analysis when architecture rules are broken. ```python from archunitpython import ( project_files, format_violations, CheckOptions, LoggingOptions, ) def test_with_custom_report(): options = CheckOptions( logging=LoggingOptions( enabled=True, level="debug", log_file=True, ), ) rule = project_files("src/").should().have_no_cycles() violations = rule.check(options) if violations: # Write custom report with open("architecture_report.txt", "w") as f: f.write(format_violations(violations)) # Fail test raise AssertionError(f"Architecture violations found") else: print("✓ All architecture rules passed") ``` -------------------------------- ### LCOM Metric Analysis Source: https://github.com/lukasniessen/archunitpython/blob/main/README.md Analyze the Lack of Cohesion of Methods (LCOM) for classes. Lower LCOM values indicate better cohesion. This example shows how to check LCOM96a and LCOM96b against a threshold. ```python # LCOM96a (Henderson et al.) metrics("src/").lcom().lcom96a().should_be_below(0.8) # LCOM96b (Henderson et al.) - most commonly used metrics("src/").lcom().lcom96b().should_be_below(0.7) ``` -------------------------------- ### Configure ArchUnitPython Checks Source: https://github.com/lukasniessen/archunitpython/blob/main/README.md Both assert_passes() and .check() accept configuration options via CheckOptions. This allows customization of the checking process, such as allowing empty tests or clearing the cache. ```python from archunitpython import CheckOptions options = CheckOptions( allow_empty_tests=True, # Don't fail when no files match clear_cache=True, # Clear the graph cache ) violations = rule.check(options) ``` -------------------------------- ### Filter files by filename pattern Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/project_files.md Use `with_name` to filter files based on a glob pattern or compiled regex matching only the filename, excluding the directory path. For example, `*_service.py` will match `user_service.py`. ```python rule = project_files("src/").with_name("*_service.py").should().have_no_cycles() ``` -------------------------------- ### Excluding Files from Metric Analysis Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/errors.md To refine metric analysis, you can exclude specific files or patterns from the calculation. This example demonstrates applying a `lines_of_code` threshold only to files matching the `test_*.py` pattern. ```python # Exclude specific files rule3 = ( metrics("src/") .with_name("test_*.py") .count() .lines_of_code() .should_be_below(500) ) ``` -------------------------------- ### Configure Check Options Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/INDEX.md Set up CheckOptions to control rule execution, including allowing empty tests, clearing cache, ignoring type checking imports, and configuring debug logging. ```python from archunitpython import CheckOptions, LoggingOptions options = CheckOptions( allow_empty_tests=False, # Pass if no files match clear_cache=False, # Rebuild graph ignore_type_checking_imports=False, # Skip TYPE_CHECKING imports logging=LoggingOptions( enabled=False, level="info", log_file=False, ), ) rule.check(options) ``` -------------------------------- ### Filtering Rules to Isolate Issues Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/errors.md When dealing with too many violations, isolate the problem by applying more specific rules. This example demonstrates filtering violations by targeting a specific folder path within the project. ```python # Filter to specific folders to isolate issues rule2 = ( project_files("src/") .in_folder("**/services/**") .should() .have_no_cycles() ) violations2 = rule2.check() ``` -------------------------------- ### Core Functions Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/README.md These are the primary entry points for using the ArchUnit CLI. They allow you to analyze project files, measure code quality, define architectural slices, extract dependency graphs, assert rules, format violations, and manage the graph cache. ```APIDOC ## Core Functions (Entry Points) ### `project_files(path)` **Description**: Analyzes project files at the given path. ### `metrics(path)` **Description**: Measures code quality metrics for the project at the given path. ### `project_slices(path)` **Description**: Defines architectural slices for the project at the given path. ### `extract_graph(path)` **Description**: Extracts the dependency graph for the project at the given path. ### `assert_passes(rule, options)` **Description**: Asserts that the given rule passes with the specified options. ### `format_violations(violations)` **Description**: Formats a list of violations into a human-readable string. ### `clear_graph_cache()` **Description**: Clears the cached dependency graph. ``` -------------------------------- ### Define Slices by Path Pattern Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/project_slices.md Use this method to define slices based on a path pattern with a wildcard. The `(**)` placeholder captures the slice identifier. For example, `src/(**)/**` groups files by the first directory level under `src/`. ```python def defined_by(self, pattern: str) -> SliceConditionBuilder: pass ``` ```python rule = project_slices("src/").defined_by("src/(**)/**").should().have_no_cycles() ``` -------------------------------- ### metrics() Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/metrics.md Creates a new metrics rule builder. This is the main entry point for defining code quality metrics. You can optionally specify the project path to analyze. ```APIDOC ## metrics(project_path: str | None = None) ### Description Create a new metrics rule. ### Method Signature ```python def metrics(project_path: str | None = None) -> MetricsBuilder ``` ### Parameters #### Path Parameters - **project_path** (str | None) - Optional - Root directory of the project. Defaults to current working directory. ### Returns - **MetricsBuilder** - Initial builder for applying filters and selecting metric types. ### Example ```python from archunitpython import metrics, assert_passes # Lines of code metric rule = metrics("src/").count().lines_of_code().should_be_below(500) assert_passes(rule) # Cohesion metric rule = metrics("src/").lcom().lcom96b().should_be_below(0.5) assert_passes(rule) # Distance metrics rule = metrics("src/").distance().instability().should_be_below(0.8) assert_passes(rule) ``` ``` -------------------------------- ### Enable Logging for Import Resolution Debugging Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/graph-extraction.md Enables detailed logging for import resolution, outputting debug information to a log file. Check logs/archunit-*.log for details. ```python from archunitpython import extract_graph, CheckOptions, LoggingOptions options = CheckOptions( logging=LoggingOptions( enabled=True, level="debug", log_file=True, ), ) graph = extract_graph("src/", options=options) # Check logs/archunit-*.log for import resolution details ``` -------------------------------- ### Custom Metric Definition and Analysis Source: https://github.com/lukasniessen/archunitpython/blob/main/README.md Define and analyze custom metrics using lambda functions. This allows for flexible and specific code quality checks tailored to project needs. The example defines a complexity ratio metric. ```python metrics("src/").custom_metric( "complexityRatio", "Ratio of methods to fields", lambda ci: len(ci.methods) / max(len(ci.fields), 1), ).should_be_below(3.0) ``` -------------------------------- ### CI Pipeline Integration for Architecture Tests (GitHub Actions) Source: https://github.com/lukasniessen/archunitpython/blob/main/README.md This YAML snippet shows how to integrate ArchUnitPython architecture tests into a GitHub Actions workflow. It includes steps for running tests and uploading logs. ```yaml # GitHub Actions - name: Run Architecture Tests run: pytest tests/test_architecture.py -v - name: Upload Test Logs if: always() uses: actions/upload-artifact@v3 with: name: architecture-test-logs path: logs/ ``` -------------------------------- ### Enforce Layered Architecture Rules Source: https://github.com/lukasniessen/archunitpython/blob/main/README.md Define rules to prevent specific layers from depending on others. This example uses pytest and ArchUnitPython to ensure presentation and business layers do not depend on the database layer. Adjust folder paths as needed. ```python from archunitpython import project_files, assert_passes def test_presentation_should_not_depend_on_database(): rule = ( project_files("src/") .in_folder("**/presentation/**") .should_not() .depend_on_files() .in_folder("**/database/**") ) assert_passes(rule) def test_business_should_not_depend_on_database(): rule = ( project_files("src/") .in_folder("**/business/**") .should_not() .depend_on_files() .in_folder("**/database/**") ) assert_passes(rule) ``` -------------------------------- ### Graph Analysis for Dependencies Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/common.md Shows how to extract a dependency graph from project files and analyze it. This includes finding files that depend on a specific module and identifying external dependencies. ```python from archunitpython import extract_graph graph = extract_graph("src/") # Find all files importing a specific module for edge in graph: if "database" in edge.target: print(f"{edge.source} depends on {edge.target}") # Check for external dependencies external_deps = {edge.target for edge in graph if edge.external} print(f"External dependencies: {external_deps}") ``` -------------------------------- ### Configuring Logging for Debugging Architecture Tests Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/testing.md Shows how to enable and configure logging for ArchUnit-Python to aid in debugging architecture tests. This includes setting the log level and directing output to a file. ```python def test_with_logging(): options = CheckOptions( logging=LoggingOptions( enabled=True, level="debug", log_file=True, ), ) rule = project_files("src/").should().have_no_cycles() assert_passes(rule, options) # Check logs/archunit-*.log for details ``` -------------------------------- ### Test Code Metrics - Lines of Code Source: https://github.com/lukasniessen/archunitpython/blob/main/README.md Enforce a maximum number of lines of code for files within a specified directory. This example uses ArchUnitPython's metrics feature and pytest to ensure files in 'src/' are below 1000 lines. Customize the line count threshold as required. ```python from archunitpython import metrics, assert_passes def test_no_large_files(): rule = metrics("src/").count().lines_of_code().should_be_below(1000) assert_passes(rule) ``` -------------------------------- ### Test Code Metrics - High Cohesion (LCOM) Source: https://github.com/lukasniessen/archunitpython/blob/main/README.md Ensure high code cohesion by setting a threshold for the Lack of Cohesion of Methods (LCOM) metric. This example uses ArchUnitPython and pytest to check if LCOM is below 0.3 for files in 'src/'. Lower LCOM values indicate higher cohesion. ```python from archunitpython import metrics, assert_passes def test_high_cohesion(): # LCOM metric (lack of cohesion of methods), low = high cohesion rule = metrics("src/").lcom().lcom96b().should_be_below(0.3) assert_passes(rule) ``` -------------------------------- ### Main Configuration Objects Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/README.md These objects are used to configure the behavior of ArchUnit, such as test options and logging preferences. ```APIDOC ## Main Configuration Objects ### `CheckOptions` **Description**: Configuration options for checking rules. - **`allow_empty_tests`** (bool) - Optional - If true, allows empty test suites. - **`logging`** (LoggingOptions | None) - Optional - Configures logging behavior. - **`clear_cache`** (bool) - Optional - If true, clears the cache before checking. - **`ignore_type_checking_imports`** (bool) - Optional - If true, ignores imports related to type checking. ### `LoggingOptions` **Description**: Configuration options for logging. - **`enabled`** (bool) - Optional - If true, enables logging. - **`level`** (LogLevel) - Optional - The logging level (e.g., "info"). Defaults to "info". - **`log_file`** (bool) - Optional - If true, logs to a file. - **`append_to_log_file`** (bool) - Optional - If true, appends to the log file instead of overwriting. ``` -------------------------------- ### Pattern Matching with String Glob Support Source: https://github.com/lukasniessen/archunitpython/blob/main/README.md Use string patterns with glob support for case-sensitive matching against filenames, folder paths, or full relative paths. This is the recommended approach for most use cases. ```python # String patterns with glob support (case sensitive) .with_name("*_service.py") # All files ending with _service.py .in_folder("**/services") # All files in any services folder .in_path("src/api/**/*.py") # All Python files under src/api ``` -------------------------------- ### CheckOptions Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/common.md Configuration for rule execution. Allows customization of test behavior, cache clearing, and import handling. ```APIDOC ## CheckOptions ### Description Configuration for rule execution. Allows customization of test behavior, cache clearing, and import handling. ### Fields - **allow_empty_tests** (bool) - Default: False - If True, rules that match no files pass. If False, raises `EmptyTestViolation`. - **logging** (LoggingOptions | None) - Default: None - Configure debug logging output. - **clear_cache** (bool) - Default: False - If True, clears the cached dependency graph before this check. - **ignore_type_checking_imports** (bool) - Default: False - If True, ignores imports inside `if TYPE_CHECKING:` blocks. ### Example ```python from archunitpython import CheckOptions, LoggingOptions options = CheckOptions( allow_empty_tests=True, clear_cache=True, logging=LoggingOptions( enabled=True, level="debug", log_file=True, ), ) violations = rule.check(options) ``` ``` -------------------------------- ### Define and Apply a Custom Metric Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/metrics.md Create a custom metric using `custom_metric` by providing a name, description, and a calculation function. This allows for flexible and specific code quality checks beyond built-in metrics. ```python rule = metrics("src/").custom_metric( "methodFieldRatio", "Ratio of methods to fields", lambda ci: len(ci.methods) / max(len(ci.fields), 1), ).should_be_below(3.0) ``` -------------------------------- ### Documenting Expected Architecture Violations Source: https://github.com/lukasniessen/archunitpython/blob/main/_autodocs/api-reference/testing.md Demonstrates how to document and handle expected architecture violations within tests. This is useful for tracking known issues that will be addressed in future refactoring efforts. ```python def test_known_exceptions(): # TODO: Fix these circular imports in a refactor rule = project_files("src/").should().have_no_cycles() violations = rule.check( CheckOptions(allow_empty_tests=True) ) # Expected cycles (documented for future refactoring) known_cycles = {"module_a", "module_b"} for v in violations: # Handle expected violations differently pass ```