From e06a8d9c38dbe4a33e2744d9a78121b59f33df6c Mon Sep 17 00:00:00 2001 From: Dillon Stadther Date: Thu, 10 Sep 2026 22:01:54 -0400 Subject: [PATCH 1/3] feat(pyscn): integrate pyscn code quality gate into CI Adds pyscn (complexity, dead code, clone, and circular-dependency checks) as a dev dependency, wired into the pre-commit hooks, the make lint / nox lint targets, and the CI lint job. Uses pyscn's built-in "check" defaults (max complexity 10, dead-code and circular-dependency failures, clone warnings) rather than a custom .pyscn.toml. Claude-Session: https://claude.ai/code/session_01EEpPSmBaxSif7DbNLcFXZM Co-Authored-By: Claude Sonnet 5 --- .github/workflows/tests.yml | 3 +++ .gitignore | 1 + .pre-commit-config.yaml | 9 +++++++++ Makefile | 5 ++++- README.md | 1 + pyproject.toml | 1 + uv.lock | 13 +++++++++++++ 7 files changed, 32 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4da3050..8304a8f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -47,6 +47,9 @@ jobs: - name: Lint with Sqlfluff run: make lint-sql + - name: Quality check with pyscn + run: make lint-quality + type-check: runs-on: ubuntu-latest steps: diff --git a/.gitignore b/.gitignore index 3f2cbf1..9437f13 100644 --- a/.gitignore +++ b/.gitignore @@ -53,6 +53,7 @@ coverage.xml .hypothesis/ .pytest_cache/ cover/ +.pyscn/ # Translations *.mo diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 66b218f..924d696 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -41,6 +41,15 @@ repos: - id: interrogate pass_filenames: false # config (incl. exclude paths) lives in pyproject.toml +# pyscn (code quality gate: complexity, dead code, clones, circular deps) +- repo: local + hooks: + - id: pyscn + name: pyscn + entry: uv run pyscn check + language: system + pass_filenames: false + # sqlfluff - repo: https://github.com/sqlfluff/sqlfluff rev: 3.5.0 diff --git a/Makefile b/Makefile index 9b7c924..f81de20 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ lock: format: uv run ruff format --check . -lint: lint-python lint-sql lint-docstrings +lint: lint-python lint-sql lint-docstrings lint-quality lint-python: uv run ruff check --output-format=github . @@ -50,6 +50,9 @@ lint-sql: lint-docstrings: uv run interrogate +lint-quality: + uv run pyscn check . + type: uv run pyrefly check diff --git a/README.md b/README.md index b19b7ed..6b59d08 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,7 @@ The checks contained in this repo include (in the order in which they run): * `pyrefly` is used for static type checking * `sqlfluff` checks and fixes sql formatting and linting * `interrogate` checks docstring coverage +* `pyscn` checks code quality (complexity, dead code, clones, circular deps) * `uv` checks on valid and aligned pyproject.toml and uv.lock files * `commitlint` enforces commit message conforms to [conventional commit](https://www.conventionalcommits.org/en/v1.0.0/) format diff --git a/pyproject.toml b/pyproject.toml index bbe47e0..6b78432 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,6 +29,7 @@ dev = [ "nox >=2024.4.15,<2025", "prek >=0.1.0,<1", "pyrefly >=1.2.0,<2", + "pyscn >=1.31.2,<2", "pytest >=8.3.2,<9", "pytest-cov >=5.0.0,<6", "python-semantic-release >=10.0.0,<11", diff --git a/uv.lock b/uv.lock index 718b662..033c6f3 100644 --- a/uv.lock +++ b/uv.lock @@ -1988,6 +1988,17 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b3/6f/459f56dc345c32f8597acef13675e7e67679fb56bc3fd8f0a280eaeb8c02/pyrefly-1.3.0-py3-none-win_arm64.whl", hash = "sha256:3a3fb7c07dfb9b43d4205d8e85a83aaefc85377d0437dc8469c132836a8a7e5d", size = 14347403, upload-time = "2026-09-11T00:41:35.01Z" }, ] +[[package]] +name = "pyscn" +version = "1.31.2" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/8f/da2c6b5e9c4f0b70ccb48fdfd0c0b96534ac0f5d2097c20f48664cf1c574/pyscn-1.31.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:87d0e0d1813c384345c3f7cc726678f095c975404f59039127f50a62e5708afa", size = 10232689, upload-time = "2026-09-10T09:12:49.765Z" }, + { url = "https://files.pythonhosted.org/packages/76/26/8dafae4879b88453c4db396f73b102773453c21d05a99291ad193ac01451/pyscn-1.31.2-py3-none-manylinux_2_34_aarch64.whl", hash = "sha256:e07658e496fd0cd0ba60640f6e8cf0da316a1258a44a9790fd55535df0131b31", size = 10046867, upload-time = "2026-09-10T09:12:51.893Z" }, + { url = "https://files.pythonhosted.org/packages/8a/f9/d7d125796d0e6db9d8103dc0174255354561f0abf89c004851afaabaddec/pyscn-1.31.2-py3-none-manylinux_2_34_x86_64.whl", hash = "sha256:55c6a22d4b1ee24628a62b06c9967d7e9b2619067097793172414e03c1505621", size = 11162333, upload-time = "2026-09-10T09:12:54.705Z" }, + { url = "https://files.pythonhosted.org/packages/65/74/b70422a79032bf0d39fb8b7d5fd4b08fea03aede6424d288f3f55422742b/pyscn-1.31.2-py3-none-win_amd64.whl", hash = "sha256:c8349f184d40e16841466054738f5adacd8df4cba2e6458a80e7a0d8ec6e383f", size = 11630685, upload-time = "2026-09-10T09:12:57.287Z" }, +] + [[package]] name = "pytest" version = "8.4.2" @@ -2613,6 +2624,7 @@ dev = [ { name = "nox" }, { name = "prek" }, { name = "pyrefly" }, + { name = "pyscn" }, { name = "pytest" }, { name = "pytest-cov" }, { name = "python-semantic-release" }, @@ -2633,6 +2645,7 @@ dev = [ { name = "nox", specifier = ">=2024.4.15,<2025" }, { name = "prek", specifier = ">=0.1.0,<1" }, { name = "pyrefly", specifier = ">=1.2.0,<2" }, + { name = "pyscn", specifier = ">=1.31.2,<2" }, { name = "pytest", specifier = ">=8.3.2,<9" }, { name = "pytest-cov", specifier = ">=5.0.0,<6" }, { name = "python-semantic-release", specifier = ">=10.0.0,<11" }, From 0f017c564ca9bd28f989d47bbbf9f6b1325d6e2d Mon Sep 17 00:00:00 2001 From: Dillon Stadther Date: Fri, 11 Sep 2026 15:09:01 -0400 Subject: [PATCH 2/3] feat(pyscn): add .pyscn.toml config with reasonable defaults Adds a full pyscn config file (generated via `pyscn init`) as a starting point for tuning quality-gate behavior later. Disables the generated example layered-architecture rules by default since this project is a flat single-package library, not a layered app, so they'd otherwise misclassify modules and score false violations. Everything else keeps pyscn's own defaults, which reproduce the same health score as running with no config file at all. Addresses PR review feedback. Claude-Session: https://claude.ai/code/session_01EEpPSmBaxSif7DbNLcFXZM Co-Authored-By: Claude Sonnet 5 --- .pyscn.toml | 214 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 214 insertions(+) create mode 100644 .pyscn.toml diff --git a/.pyscn.toml b/.pyscn.toml new file mode 100644 index 0000000..7a32f1e --- /dev/null +++ b/.pyscn.toml @@ -0,0 +1,214 @@ +# pyscn configuration file +# This file configures all analysis features of pyscn +# Place this file in your project root to customize analysis behavior + +# ============================================================================= +# OUTPUT CONFIGURATION +# ============================================================================= +[output] +format = "text" # Default output format: text, json, yaml, csv, html +show_details = false # Show detailed breakdown by default +sort_by = "complexity" # Default sort: name, complexity, risk +min_complexity = 1 # Minimum complexity to report +directory = "" # Output directory for reports (empty = default .pyscn/reports/, gitignored) + +# ============================================================================= +# COMPLEXITY ANALYSIS +# ============================================================================= +[complexity] +enabled = true # Enable complexity analysis +low_threshold = 9 # Functions with complexity ≤ 9 are low risk +medium_threshold = 19 # Functions with complexity 10-19 are medium risk + # Functions with complexity ≥ 20 are high risk +max_complexity = 0 # Maximum allowed complexity (0 = no limit). Must be 0 or > medium_threshold; + # the CI gate's own limit (10) is set independently via `pyscn check --max-complexity` +report_unchanged = true # Report functions with complexity = 1 + +# ============================================================================= +# DEAD CODE DETECTION +# ============================================================================= +[dead_code] +enabled = true # Enable dead code detection +min_severity = "warning" # Minimum severity to report: critical, warning, info +show_context = false # Show surrounding code context +context_lines = 3 # Number of context lines to show +sort_by = "severity" # Sort by: severity, line, file, function + +# Detection options - configure what types of dead code to detect +detect_after_return = true # Code after return statements +detect_after_break = true # Code after break statements +detect_after_continue = true # Code after continue statements +detect_after_raise = true # Code after raise statements +detect_unreachable_branches = true # Unreachable conditional branches + +# Patterns to ignore (regex patterns) +ignore_patterns = [] + +# ============================================================================= +# CLONE DETECTION +# ============================================================================= +[clones] +# Analysis settings +min_lines = 10 # Minimum lines for clone candidates +min_nodes = 20 # Minimum AST nodes for clone candidates +max_edit_distance = 50 # Maximum edit distance allowed +ignore_literals = false # Ignore differences in literal values +ignore_identifiers = false # Ignore differences in identifier names +cost_model_type = "python" # Cost model: default, python, weighted + +# Threshold settings for clone type classification (0.0 - 1.0) +# These values are sourced from domain/defaults.go +type1_threshold = 0.85 # Type-1: Identical code (except whitespace/comments) +type2_threshold = 0.75 # Type-2: Syntactically identical (different identifiers) +type3_threshold = 0.7 # Type-3: Syntactically similar (small modifications) +type4_threshold = 0.65 # Type-4: Functionally similar (different syntax) +similarity_threshold = 0.65 # General minimum similarity threshold + +# Advanced analysis settings +enable_dfa = true # Enable Data Flow Analysis for enhanced Type-4 detection + +# Filtering settings +min_similarity = 0.0 # Minimum similarity to report +max_similarity = 1.0 # Maximum similarity to report +# enabled_clone_types = ["type1", "type2", "type4"] # Default: uses domain.DefaultEnabledCloneTypes +max_results = 10000 # Maximum results (0 = no limit) + +# Grouping settings +grouping_mode = "connected" # Grouping mode: connected, star, complete_linkage, k_core +grouping_threshold = 0.65 # Threshold for grouping +k_core_k = 2 # K-core parameter + +# LSH acceleration settings +lsh_enabled = "auto" # Enable LSH: true, false, auto (based on fragment or pair count) +lsh_auto_threshold = 500 # Auto-enable LSH at >= 500 fragments, or >10,000 estimated pairs +lsh_similarity_threshold = 0.5 # LSH similarity threshold +lsh_bands = 32 # Number of LSH bands +lsh_rows = 4 # Number of rows per band +lsh_hashes = 128 # Number of hash functions + +# Performance settings +max_memory_mb = 100 # Memory limit in MB (0 = no limit) +batch_size = 100 # Batch size for processing +enable_batching = true # Enable batching for large projects +max_goroutines = 4 # Maximum concurrent goroutines +timeout_seconds = 300 # Timeout for clone analysis + +# Output settings +show_details = false # Show detailed clone information +show_content = false # Include source code content in output +sort_by = "similarity" # Sort by: similarity, size, location, type +group_clones = true # Group related clones together + +# ============================================================================= +# CBO (COUPLING BETWEEN OBJECTS) ANALYSIS +# ============================================================================= +[cbo] +enabled = true # Enable CBO analysis +low_threshold = 3 # CBO values ≤ 3 are low risk (industry standard) +medium_threshold = 7 # CBO values 4-7 are medium risk (industry standard) + # CBO values > 7 are high risk +include_builtins = false # Include built-in type dependencies +include_imports = true # Include imported module dependencies +group_namespace_imports = true # Collapse alias.Member references to one edge per namespace +min_cbo = 0 # Minimum CBO to report +show_zeros = false # Include classes with CBO = 0 + +# ============================================================================= +# ANALYSIS CONFIGURATION +# ============================================================================= +[analysis] +recursive = true # Recursively analyze directories +follow_symlinks = false # Follow symbolic links +include_patterns = ["**/*.py"] # File patterns to include +exclude_patterns = [ # File patterns to exclude + "**/test_*.py", + "**/*_test.py", + "**/__pycache__/*", + "**/*.pyc", + "**/.pytest_cache/", + ".tox/", + "venv/", + "env/", + ".venv/", + ".env/", + "**/migrations/**" # Django/Alembic auto-generated migration files +] +# ============================================================================= +# ARCHITECTURE VALIDATION +# ============================================================================= +# Define architectural layers and validate dependencies. +# Disabled by default: this project is a small flat package (src/sample/*), +# not a layered app, so the generated example layers/rules below don't apply. +# Uncomment and adjust package name patterns if/when the project grows into +# distinct layers (e.g. api/, services/, models/, db/). + +[architecture] +enabled = false +validate_layers = true +strict_mode = true + +# [[architecture.layers]] +# name = "presentation" +# packages = ["router", "routers", "route", "routes", "endpoint", "endpoints", "handler", "handlers", "controller", "controllers", "view", "views", "api", "apis", "ui", "web", "rest", "graphql"] +# +# [[architecture.layers]] +# name = "application" +# packages = ["service", "services", "usecase", "usecases", "use_case", "use_cases", "workflow", "workflows", "command", "commands", "query", "queries", "manager", "managers"] +# +# [[architecture.layers]] +# name = "domain" +# packages = ["model", "models", "entity", "entities", "schema", "schemas", "domain", "domains", "core", "business", "aggregate", "aggregates", "valueobject", "valueobjects"] +# +# [[architecture.layers]] +# name = "infrastructure" +# packages = ["repository", "repositories", "repo", "repos", "db", "database", "adapter", "adapters", "persistence", "storage", "cache", "client", "clients", "external"] +# +# [[architecture.rules]] +# from = "presentation" +# allow = ["presentation", "application", "domain", "infrastructure"] +# +# [[architecture.rules]] +# from = "application" +# allow = ["application", "domain", "infrastructure"] +# +# [[architecture.rules]] +# from = "domain" +# allow = ["domain", "infrastructure"] +# deny = ["presentation", "application"] +# +# [[architecture.rules]] +# from = "infrastructure" +# allow = ["infrastructure", "domain", "application"] + + +# ============================================================================= +# EXAMPLE CONFIGURATIONS +# ============================================================================= + +# Uncomment and modify these sections for common use cases: + +# # Strict mode - fail on any issues +# [complexity] +# max_complexity = 10 +# [dead_code] +# min_severity = "critical" +# +# # Relaxed mode - only catch major issues +# [complexity] +# low_threshold = 15 +# medium_threshold = 25 +# [dead_code] +# min_severity = "warning" +# +# # Clone detection focused on exact matches +# [clones] +# similarity_threshold = 0.95 +# enabled_clone_types = ["type1", "type2"] +# +# # Performance optimized for large codebases +# [clones] +# max_goroutines = 8 +# max_memory_mb = 1024 +# min_lines = 10 +# min_nodes = 20 +# lsh_enabled = true From 45fa4c724f4b0b4a8d5ef4647adc54ba248cc57b Mon Sep 17 00:00:00 2001 From: Dillon Stadther Date: Fri, 11 Sep 2026 16:03:06 -0400 Subject: [PATCH 3/3] refactor(pyscn): move config into pyproject.toml, trim to essentials Replaces the standalone .pyscn.toml with [tool.pyscn.*] sections in pyproject.toml, matching this repo's convention of keeping tool config there (ruff, pytest, pyrefly, interrogate, sqlfluff, etc.) instead of one-off dotfiles. Trims the generated boilerplate down to what's actually meaningful here: full "output" and "complexity" sections (kept per review request), "dead_code" and "cbo" reduced to their enabled toggle (pyscn's only other options there are fine-tuning, not on/off), and "architecture" reduced to enabled=false with a one-line rationale. Drops the commented-out example-configurations block and the generated layered-architecture layers/rules entirely rather than leaving them commented out. "clones" and "analysis" are omitted rather than reduced to an "enabled" toggle, since pyscn's config schema doesn't define such a field for either section (confirmed against https://docs.codescan.dev/configuration/reference/) - they use pyscn's built-in defaults for now. Verified `pyscn analyze .` and `pyscn check .` behave identically before/after (same 88/100 health score) by reading config from pyproject.toml instead of a dedicated .pyscn.toml. Addresses PR review feedback. Claude-Session: https://claude.ai/code/session_01EEpPSmBaxSif7DbNLcFXZM Co-Authored-By: Claude Sonnet 5 --- .pyscn.toml | 214 ------------------------------------------------- pyproject.toml | 33 ++++++++ 2 files changed, 33 insertions(+), 214 deletions(-) delete mode 100644 .pyscn.toml diff --git a/.pyscn.toml b/.pyscn.toml deleted file mode 100644 index 7a32f1e..0000000 --- a/.pyscn.toml +++ /dev/null @@ -1,214 +0,0 @@ -# pyscn configuration file -# This file configures all analysis features of pyscn -# Place this file in your project root to customize analysis behavior - -# ============================================================================= -# OUTPUT CONFIGURATION -# ============================================================================= -[output] -format = "text" # Default output format: text, json, yaml, csv, html -show_details = false # Show detailed breakdown by default -sort_by = "complexity" # Default sort: name, complexity, risk -min_complexity = 1 # Minimum complexity to report -directory = "" # Output directory for reports (empty = default .pyscn/reports/, gitignored) - -# ============================================================================= -# COMPLEXITY ANALYSIS -# ============================================================================= -[complexity] -enabled = true # Enable complexity analysis -low_threshold = 9 # Functions with complexity ≤ 9 are low risk -medium_threshold = 19 # Functions with complexity 10-19 are medium risk - # Functions with complexity ≥ 20 are high risk -max_complexity = 0 # Maximum allowed complexity (0 = no limit). Must be 0 or > medium_threshold; - # the CI gate's own limit (10) is set independently via `pyscn check --max-complexity` -report_unchanged = true # Report functions with complexity = 1 - -# ============================================================================= -# DEAD CODE DETECTION -# ============================================================================= -[dead_code] -enabled = true # Enable dead code detection -min_severity = "warning" # Minimum severity to report: critical, warning, info -show_context = false # Show surrounding code context -context_lines = 3 # Number of context lines to show -sort_by = "severity" # Sort by: severity, line, file, function - -# Detection options - configure what types of dead code to detect -detect_after_return = true # Code after return statements -detect_after_break = true # Code after break statements -detect_after_continue = true # Code after continue statements -detect_after_raise = true # Code after raise statements -detect_unreachable_branches = true # Unreachable conditional branches - -# Patterns to ignore (regex patterns) -ignore_patterns = [] - -# ============================================================================= -# CLONE DETECTION -# ============================================================================= -[clones] -# Analysis settings -min_lines = 10 # Minimum lines for clone candidates -min_nodes = 20 # Minimum AST nodes for clone candidates -max_edit_distance = 50 # Maximum edit distance allowed -ignore_literals = false # Ignore differences in literal values -ignore_identifiers = false # Ignore differences in identifier names -cost_model_type = "python" # Cost model: default, python, weighted - -# Threshold settings for clone type classification (0.0 - 1.0) -# These values are sourced from domain/defaults.go -type1_threshold = 0.85 # Type-1: Identical code (except whitespace/comments) -type2_threshold = 0.75 # Type-2: Syntactically identical (different identifiers) -type3_threshold = 0.7 # Type-3: Syntactically similar (small modifications) -type4_threshold = 0.65 # Type-4: Functionally similar (different syntax) -similarity_threshold = 0.65 # General minimum similarity threshold - -# Advanced analysis settings -enable_dfa = true # Enable Data Flow Analysis for enhanced Type-4 detection - -# Filtering settings -min_similarity = 0.0 # Minimum similarity to report -max_similarity = 1.0 # Maximum similarity to report -# enabled_clone_types = ["type1", "type2", "type4"] # Default: uses domain.DefaultEnabledCloneTypes -max_results = 10000 # Maximum results (0 = no limit) - -# Grouping settings -grouping_mode = "connected" # Grouping mode: connected, star, complete_linkage, k_core -grouping_threshold = 0.65 # Threshold for grouping -k_core_k = 2 # K-core parameter - -# LSH acceleration settings -lsh_enabled = "auto" # Enable LSH: true, false, auto (based on fragment or pair count) -lsh_auto_threshold = 500 # Auto-enable LSH at >= 500 fragments, or >10,000 estimated pairs -lsh_similarity_threshold = 0.5 # LSH similarity threshold -lsh_bands = 32 # Number of LSH bands -lsh_rows = 4 # Number of rows per band -lsh_hashes = 128 # Number of hash functions - -# Performance settings -max_memory_mb = 100 # Memory limit in MB (0 = no limit) -batch_size = 100 # Batch size for processing -enable_batching = true # Enable batching for large projects -max_goroutines = 4 # Maximum concurrent goroutines -timeout_seconds = 300 # Timeout for clone analysis - -# Output settings -show_details = false # Show detailed clone information -show_content = false # Include source code content in output -sort_by = "similarity" # Sort by: similarity, size, location, type -group_clones = true # Group related clones together - -# ============================================================================= -# CBO (COUPLING BETWEEN OBJECTS) ANALYSIS -# ============================================================================= -[cbo] -enabled = true # Enable CBO analysis -low_threshold = 3 # CBO values ≤ 3 are low risk (industry standard) -medium_threshold = 7 # CBO values 4-7 are medium risk (industry standard) - # CBO values > 7 are high risk -include_builtins = false # Include built-in type dependencies -include_imports = true # Include imported module dependencies -group_namespace_imports = true # Collapse alias.Member references to one edge per namespace -min_cbo = 0 # Minimum CBO to report -show_zeros = false # Include classes with CBO = 0 - -# ============================================================================= -# ANALYSIS CONFIGURATION -# ============================================================================= -[analysis] -recursive = true # Recursively analyze directories -follow_symlinks = false # Follow symbolic links -include_patterns = ["**/*.py"] # File patterns to include -exclude_patterns = [ # File patterns to exclude - "**/test_*.py", - "**/*_test.py", - "**/__pycache__/*", - "**/*.pyc", - "**/.pytest_cache/", - ".tox/", - "venv/", - "env/", - ".venv/", - ".env/", - "**/migrations/**" # Django/Alembic auto-generated migration files -] -# ============================================================================= -# ARCHITECTURE VALIDATION -# ============================================================================= -# Define architectural layers and validate dependencies. -# Disabled by default: this project is a small flat package (src/sample/*), -# not a layered app, so the generated example layers/rules below don't apply. -# Uncomment and adjust package name patterns if/when the project grows into -# distinct layers (e.g. api/, services/, models/, db/). - -[architecture] -enabled = false -validate_layers = true -strict_mode = true - -# [[architecture.layers]] -# name = "presentation" -# packages = ["router", "routers", "route", "routes", "endpoint", "endpoints", "handler", "handlers", "controller", "controllers", "view", "views", "api", "apis", "ui", "web", "rest", "graphql"] -# -# [[architecture.layers]] -# name = "application" -# packages = ["service", "services", "usecase", "usecases", "use_case", "use_cases", "workflow", "workflows", "command", "commands", "query", "queries", "manager", "managers"] -# -# [[architecture.layers]] -# name = "domain" -# packages = ["model", "models", "entity", "entities", "schema", "schemas", "domain", "domains", "core", "business", "aggregate", "aggregates", "valueobject", "valueobjects"] -# -# [[architecture.layers]] -# name = "infrastructure" -# packages = ["repository", "repositories", "repo", "repos", "db", "database", "adapter", "adapters", "persistence", "storage", "cache", "client", "clients", "external"] -# -# [[architecture.rules]] -# from = "presentation" -# allow = ["presentation", "application", "domain", "infrastructure"] -# -# [[architecture.rules]] -# from = "application" -# allow = ["application", "domain", "infrastructure"] -# -# [[architecture.rules]] -# from = "domain" -# allow = ["domain", "infrastructure"] -# deny = ["presentation", "application"] -# -# [[architecture.rules]] -# from = "infrastructure" -# allow = ["infrastructure", "domain", "application"] - - -# ============================================================================= -# EXAMPLE CONFIGURATIONS -# ============================================================================= - -# Uncomment and modify these sections for common use cases: - -# # Strict mode - fail on any issues -# [complexity] -# max_complexity = 10 -# [dead_code] -# min_severity = "critical" -# -# # Relaxed mode - only catch major issues -# [complexity] -# low_threshold = 15 -# medium_threshold = 25 -# [dead_code] -# min_severity = "warning" -# -# # Clone detection focused on exact matches -# [clones] -# similarity_threshold = 0.95 -# enabled_clone_types = ["type1", "type2"] -# -# # Performance optimized for large codebases -# [clones] -# max_goroutines = 8 -# max_memory_mb = 1024 -# min_lines = 10 -# min_nodes = 20 -# lsh_enabled = true diff --git a/pyproject.toml b/pyproject.toml index 6b78432..9a7636e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,6 +53,39 @@ verbose = 1 exclude = ["tests", "docs", "noxfile.py"] +####### +# Pyscn +####### +# Code quality gate: complexity, dead code, clones, coupling, architecture. +# This only sets what deviates from pyscn's own defaults (or is left as an +# explicit toggle). Full option reference: https://docs.codescan.dev/configuration/reference/ + +[tool.pyscn.output] +format = "text" # Default output format: text, json, yaml, csv, html +show_details = false # Show detailed breakdown by default +sort_by = "complexity" # Default sort: name, complexity, risk +min_complexity = 1 # Minimum complexity to report +directory = "" # Output directory for reports (empty = default .pyscn/reports/, gitignored) + +[tool.pyscn.complexity] +enabled = true # Enable complexity analysis +low_threshold = 9 # Functions with complexity ≤ 9 are low risk +medium_threshold = 19 # Functions with complexity 10-19 are medium risk + # Functions with complexity ≥ 20 are high risk +max_complexity = 0 # Maximum allowed complexity (0 = no limit). Must be 0 or > medium_threshold; + # the CI gate's own limit (10) is set independently via `pyscn check --max-complexity` +report_unchanged = true # Report functions with complexity = 1 + +[tool.pyscn.dead_code] +enabled = true # Enable dead code detection; see reference link above for tunable options + +[tool.pyscn.cbo] +enabled = true # Enable class coupling (CBO) analysis; see reference link above for tunable options + +[tool.pyscn.architecture] +enabled = false # This project is a flat single-package library, not a layered app + + ########## # Coverage ##########