Configuration Reference¶
Every configurable key in .pyscn.toml (or [tool.pyscn.*] in pyproject.toml). Run pyscn init to generate a commented starter file.
[output]¶
Controls how results are reported.
| Key | Type | Default | Description |
|---|---|---|---|
format |
string | "text" |
Default for configuration-backed analyzer requests. pyscn analyze uses its format flags and defaults to HTML. |
directory |
string | "" |
Output directory. Empty = .pyscn/reports/ under CWD. |
show_details |
bool | false |
Include per-finding detail in the summary. |
sort_by |
string | "complexity" |
name, complexity, or risk. |
min_complexity |
int | 1 |
Filter out execution scopes below this complexity. Overrides [complexity].min_complexity when set. |
[complexity]¶
Cyclomatic complexity analysis.
| Key | Type | Default | Description |
|---|---|---|---|
enabled |
bool | true |
Run the analyzer. |
low_threshold |
int | 9 |
Upper bound for "low risk" (inclusive). |
medium_threshold |
int | 19 |
Upper bound for "medium risk". |
max_complexity |
int | 0 |
CI failure threshold. 0 = no limit. |
min_complexity |
int | 1 |
Don't report execution scopes below this. |
report_unchanged |
bool | true |
Include functions with complexity = 1. |
See high-cyclomatic-complexity for thresholds guidance.
[dead_code]¶
Dead code detection.
| Key | Type | Default | Description |
|---|---|---|---|
enabled |
bool | true |
Run the analyzer. |
min_severity |
string | "warning" |
info, warning, or critical. |
show_context |
bool | false |
Include surrounding source lines. |
context_lines |
int | 3 |
Lines of context (0–20). |
sort_by |
string | "severity" |
severity, line, file, or function. |
detect_after_return |
bool | true |
Flag statements after return. |
detect_after_break |
bool | true |
Flag statements after break. |
detect_after_continue |
bool | true |
Flag statements after continue. |
detect_after_raise |
bool | true |
Flag statements after raise. |
detect_unreachable_branches |
bool | true |
Flag branches that can never be taken. |
ignore_patterns |
string[] | [] |
Regex patterns for lines to ignore. |
[clones]¶
Clone detection (the most configurable analyzer).
Fragment selection¶
| Key | Type | Default | Description |
|---|---|---|---|
min_lines |
int | 8 |
Minimum SLOC to consider a fragment (comments, blank lines and docstrings excluded). |
min_nodes |
int | 20 |
Minimum AST nodes. |
skip_docstrings |
bool | true |
Skip docstrings when hashing. |
min_lines and min_nodes have one exception: a function identical to another
function is always reported, however short it is, as long as its body has more
than one statement. Copy-pasted overrides across sibling classes are usually
shorter than the threshold, and dropping them would hide real duplication.
One-statement bodies (pass, raise NotImplementedError, a single return)
stay out, since those repeat across unrelated classes by design.
Identity here is the Type-1 comparison, so comments and whitespace do not break
the match. Line breaks do still count toward min_lines, so the same body can
clear the threshold in one place and fall short in another depending on how its
statements are wrapped; either copy vindicates the other.
The reverse also holds: a fragment that clears min_lines only because one
statement's arguments or literal span many lines is reported only when it is
identical to another. This covers a fragment with one statement, or two where
the second fits on one line (docstrings and nested def/class lines do not
count). Deprecation shims, template-tag wrappers and dispatch guards share a
shape across a codebase without sharing logic.
Type thresholds (0.0–1.0)¶
| Key | Default | Clone type |
|---|---|---|
type1_threshold |
0.85 |
Identical (whitespace/comments only). |
type2_threshold |
0.75 |
Renamed identifiers/literals. |
type3_threshold |
0.70 |
Structurally similar with modifications. |
type4_threshold |
0.65 |
Semantic equivalence. |
similarity_threshold |
0.65 |
Global minimum for any clone. |
A Type-4 pair is dropped when both of its fragments have a Type-1 to Type-3 match elsewhere that is at least 0.15 more similar. Each fragment's real counterpart is then already reported, and the weak Type-4 match only links two code blocks that share a generic skeleton, such as the same decode-and-flush loop.
An if/elif chain whose every branch is one one-line statement, such as a
dispatch that returns a different encoder per argument, is never reported as
Type-4. Two such chains of the same length share a control flow graph whatever
they dispatch on, so only a Type-1 to Type-3 match between them counts.
Algorithm¶
| Key | Type | Default | Description |
|---|---|---|---|
cost_model_type |
string | "python" |
default, python, or weighted. |
ignore_literals |
bool | false |
Treat different literals as equivalent. |
ignore_identifiers |
bool | false |
Treat different variable names as equivalent. |
max_edit_distance |
float | 50.0 |
Cap on tree edit distance. |
enable_dfa |
bool | true |
Data-flow analysis for Type-4. |
enabled_clone_types |
string[] | all | Subset of type1, type2, type3, type4. |
LSH acceleration¶
| Key | Type | Default | Description |
|---|---|---|---|
lsh_enabled |
true\|false\|"auto" |
"auto" |
Enable LSH (auto = based on fragment count or estimated pair count). |
lsh_auto_threshold |
int | 500 |
Fragment count threshold for auto-enable; auto also enables above 10,000 estimated pairs. |
lsh_similarity_threshold |
float | 0.50 |
Minimum AST feature Jaccard for a clone pair. Applies with and without LSH. |
lsh_bands |
int | 42 |
LSH bands. |
lsh_rows |
int | 3 |
Rows per band. |
lsh_hashes |
int | 128 |
Hash function count. |
Grouping¶
| Key | Type | Default | Description |
|---|---|---|---|
grouping_mode |
string | "complete_linkage" |
connected, star, complete_linkage, k_core, centroid. connected chains clones transitively and can merge unrelated fragments into one group. |
grouping_threshold |
float | 0.65 |
Minimum similarity for grouping. |
k_core_k |
int | 2 |
k parameter for k_core mode. |
Every mode except k_core keeps a fragment in at most one group, so a pair that
no group covers is reported as a group of its own rather than dropped. Such a
group is not reported when a stronger group already contains both of its
windows, so a nested window is counted once instead of twice.
Performance¶
| Key | Type | Default | Description |
|---|---|---|---|
max_memory_mb |
int | 100 |
Memory cap (MB). 0 = no limit. |
batch_size |
int | 100 |
Files per batch. |
enable_batching |
bool | true |
Process in batches. |
max_goroutines |
int | 4 |
Concurrent workers. |
timeout_seconds |
int | 300 |
Per-analysis timeout. |
Output filtering¶
| Key | Type | Default | Description |
|---|---|---|---|
min_similarity |
float | 0.0 |
Filter out pairs below this. |
max_similarity |
float | 1.0 |
Filter out pairs above this. |
max_results |
int | 10000 |
Maximum pairs to report. 0 = no limit. |
show_details |
bool | false |
Verbose output. |
show_content |
bool | false |
Include source in the report. |
sort_by |
string | "similarity" |
similarity, size, location, type. |
group_clones |
bool | true |
Group related clones. |
These keys only shape the report. They never change the duplication score or the health grade.
[cbo]¶
Coupling Between Objects (class coupling).
| Key | Type | Default | Description |
|---|---|---|---|
enabled |
bool | true |
Run the analyzer. |
low_threshold |
int | 3 |
Upper bound for "low risk". |
medium_threshold |
int | 7 |
Upper bound for "medium risk". |
min_cbo |
int | 0 |
Filter out classes below this CBO. |
max_cbo |
int | 0 |
Filter out classes above this. 0 = no limit. |
show_zeros |
bool | false |
Include classes with CBO = 0. |
include_builtins |
bool | false |
Count list/dict/str as dependencies. |
include_imports |
bool | true |
Count imported module references. |
[lcom]¶
Lack of Cohesion of Methods (LCOM4).
| Key | Type | Default | Description |
|---|---|---|---|
low_threshold |
int | 2 |
Upper bound for "low risk" (good cohesion). |
medium_threshold |
int | 5 |
Upper bound for "medium risk". |
[analysis]¶
File discovery rules.
| Key | Type | Default | Description |
|---|---|---|---|
recursive |
bool | true |
Descend into subdirectories. |
follow_symlinks |
bool | false |
Follow symbolic links. |
include_patterns |
string[] | ["**/*.py"] |
Glob patterns to include. |
exclude_patterns |
string[] | see below | Glob patterns to exclude. |
Patterns without a directory separator match the filename at any depth. For example, main*.py matches both main.py and pkg/main_utils.py. Include a directory segment, such as pkg/main*.py, when the rule should apply only within that path.
Default exclude_patterns:
[
"test_*.py", "*_test.py",
"**/__pycache__/*", "**/*.pyc",
"**/.pytest_cache/", ".tox/",
"venv/", "env/", ".venv/", ".env/",
]
[architecture]¶
Layer validation. All keys optional — if you don't define layers, architecture analysis runs in permissive mode.
| Key | Type | Default | Description |
|---|---|---|---|
enabled |
bool | true |
Run layer validation. |
style |
string | "" |
Apply a built-in preset of layers + rules: layered, hexagonal, clean, mvc. Explicit layers/rules below override the preset. With no style, layers, or rules at all, pyscn auto-detects the architecture instead. |
validate_layers |
bool | true |
Check layer-to-layer rules. |
validate_cohesion |
bool | true |
Check package cohesion. |
validate_responsibility |
bool | true |
Check module responsibility count. |
strict_mode |
bool | true |
Strict validation. |
fail_on_violations |
bool | false |
Non-zero exit on violation. |
min_cohesion |
float | 0.5 |
Minimum package cohesion. |
max_coupling |
int | 10 |
Max inter-layer coupling. |
max_responsibilities |
int | 3 |
Max concerns per module. |
neutral_prefixes |
string[] | [] |
Top-level module segments to strip before matching layer packages. Useful when every module starts with the same project prefix (e.g. app, src). |
Style presets¶
Set style to apply a ready-made bundle of layer definitions and rules instead of writing them by hand. The preset is the starting point; any [[architecture.layers]] or [[architecture.rules]] you add override it (user rules win per from layer). layered reproduces the layers/rules of the generated default config; with no style, layers, or rules set at all, pyscn auto-detects the architecture rather than applying layered.
| Preset | Enforces | Notable rule |
|---|---|---|
layered |
Classic presentation → application → domain → infrastructure (the generated default config's baseline). |
domain may still reach infrastructure (lenient DIP). |
hexagonal |
Ports & Adapters / Onion: domain depends on nothing outward. |
domain → ports/adapters denied. |
clean |
Clean Architecture: dependencies point only inward across entities → use_cases → interface_adapters → frameworks. |
Any outward dependency denied. |
mvc |
MVC / MVT: model / view / controller. |
A direct view → model is discouraged (warning), not denied. |
Rules use three lists, checked in order: deny (error), warn (allowed but reported as a warning — used by mvc for view → model), then allow (when a non-empty allow list is present, anything not listed becomes an error). A warning lowers the architecture compliance score less than an error does.
Layer definitions¶
[[architecture.layers]]
name = "presentation"
packages = ["router", "routers", "handler", "handlers", "controller", "api"]
[[architecture.layers]]
name = "application"
packages = ["service", "services", "usecase", "usecases"]
[[architecture.layers]]
name = "domain"
packages = ["model", "models", "entity", "entities"]
[[architecture.layers]]
name = "infrastructure"
packages = ["repository", "repositories", "db", "database"]
Layer rules¶
[[architecture.rules]]
from = "presentation"
allow = ["application", "domain"]
deny = ["infrastructure"]
[[architecture.rules]]
from = "application"
allow = ["domain"]
Neutral prefixes¶
If every module in the project starts with the same root segment (app., src., ...), layer matching can fail because the project prefix shadows the layer name. List those segments under neutral_prefixes and pyscn will strip them before resolving a module to a layer:
With this set, app.routers.user_router is matched as routers.user_router and resolves to the presentation layer.
[dependencies]¶
Module dependency analysis. Opt-in for pyscn check; always on for pyscn analyze unless skipped.
| Key | Type | Default | Description |
|---|---|---|---|
enabled |
bool | false |
Run the analyzer (analyze always runs it regardless). |
include_stdlib |
bool | false |
Include standard-library imports. |
include_third_party |
bool | true |
Include third-party imports. |
follow_relative |
bool | true |
Follow relative imports. |
detect_cycles |
bool | true |
Find circular imports. |
calculate_metrics |
bool | true |
Compute Ca/Ce/I/A/D. |
find_long_chains |
bool | true |
Report longest dependency chains. |
cycle_reporting |
string | "summary" |
all, critical, summary. |
max_cycles_to_show |
int | 10 |
Cap on reported cycles. |
sort_by |
string | "name" |
name, coupling, instability, distance, risk. |
show_matrix |
bool | false |
Include dependency matrix. |
generate_dot_graph |
bool | false |
Emit Graphviz DOT output. |
[communities]¶
Module community detection over the import dependency graph. It runs in default pyscn analyze; set enabled = false to disable it for default analyze runs.
| Key | Type | Default | Description |
|---|---|---|---|
enabled |
bool | true for default analyze |
Set to false to disable community detection in default pyscn analyze. |
algorithm |
string | "leiden" |
Community detection algorithm (currently only leiden). |
scope |
string | "module" |
Graph scope. Module-level only in Phase 1. |
min_community_size |
int | 2 |
Minimum community size; smaller partitions stay as singletons. |
include_lazy_edges |
bool | true |
Include lazy / TYPE_CHECKING imports as graph edges. |
report_bridge_modules |
bool | true |
Emit bridge_modules cross-community coupling list. |
resolution |
float | 1.0 |
Leiden resolution parameter (higher tends to produce more communities). |
See Module Community Detection for interpretation, determinism guarantees, and JSON field reference.
[mock_data]¶
Mock/placeholder data detection. Opt-in.
| Key | Type | Default | Description |
|---|---|---|---|
enabled |
bool | false |
Run the analyzer. |
min_severity |
string | "warning" |
info, warning, error. |
ignore_tests |
bool | true |
Skip test files. |
keywords |
string[] | built-in | Words flagged as mock indicators. |
domains |
string[] | built-in | Domains flagged (example.com, test.com, etc.). |
ignore_patterns |
string[] | [] |
Files/regex patterns to skip. |
[di]¶
Dependency Injection anti-pattern detection. Opt-in.
| Key | Type | Default | Description |
|---|---|---|---|
enabled |
bool | false |
Run the analyzer. |
min_severity |
string | "warning" |
info, warning, error. |
constructor_param_threshold |
int | 5 |
Flag __init__ with more params. |
CLI flag → config key map¶
Flags that don't map directly to a config key (--select, --skip-*, --no-open, and output-format flags) work on top of whatever config you have loaded.
| CLI flag | Config key |
|---|---|
--config <path> |
— (overrides discovery) |
--json/--yaml/--csv/--html/--text |
— (selects the analyze report format) |
--min-complexity |
[complexity] min_complexity |
--max-complexity |
[complexity] max_complexity |
--min-severity |
[dead_code] min_severity |
--clone-threshold |
[clones] similarity_threshold |
--min-cbo |
[cbo] min_cbo |
--select communities |
explicit per-run selection |
--skip-communities |
disables communities for the run |
--max-cycles |
— (check command only) |
See also¶
- Config File Format — discovery and priority.
- Examples — ready-to-use configurations.