pyscn Documentation

repository·main·Indexed 21 days ago

https://github.com/ludo-technologies/pyscn

A high-performance code quality analyzer for Python designed for AI agents. pyscn performs structural analysis to detect dead code, duplicates, complexity, architectural violations, and class design issues. It includes a CLI for analysis and quality gating, as well as a Model Context Protocol (MCP) server (pyscn-mcp) that provides tools like analyze_code, check_complexity, detect_clones, and get_health_score for integration with AI assistants such as Claude Code and Cursor.

Tokens
102K
Snippets
278
Records
441
Agent score
75%

What's inside pyscn

  1. Browse the pyscn rule catalog

    main

    pyscn includes 33 rules organized into 7 functional categories. Each rule detects specific code smells, architectural issues, or security risks.

    Rule Categories

    • Unreachable Code: Detects dead code that can never execute (e.g., unreachable-after-return, unreachable-after-raise).
    • Duplicate Code: Identifies copy-paste or near-copy-paste fragments (e.g., duplicate-code-identical, duplicate-code-semantic).
    • Complexity: Flags functions that are too branchy to test reliably (e.g., high-cyclomatic-complexity).
    • Class Design: Detects classes with too many dependencies or unrelated responsibilities (e.g., high-class-coupling, low-class-cohesion).
    • Dependency Injection: Identifies patterns that hurt testability (e.g., too-many-constructor-parameters, global-state-dependency, service-locator-pattern).
    • Module Structure: Analyzes import graphs for cycles, deep chains, or layer violations (e.g., circular-import, layer-violation).
    • Mock Data: Detects placeholder or test data accidentally left in code (e.g., mock-email-address, test-credential-in-code).
  2. What is the CBO (Coupling Between Objects) algorithm?

    main

    CBO (Coupling Between Objects) is an object-oriented design metric that measures the number of distinct classes a given class depends on. It provides a quantitative assessment of inter-class coupling.

    A higher CBO value indicates greater dependency on external classes, which increases risks such as:

    • Change propagation: Changes to dependent classes may ripple into this class.
    • Testing difficulty: More mocks and stubs are required for unit testing.
    • Reduced reusability: The class becomes harder to reuse in different contexts.
    • Comprehension overhead: Understanding the class requires knowledge of many other classes.
  3. What is the pyscn MCP server and its tools?

    main

    pyscn-mcp is a Model Context Protocol (MCP) server that exposes pyscn's static analysis capabilities as tools for AI agents and MCP clients (such as Claude Code, Cursor, or ChatGPT desktop).

    All tools accept path arguments and optional threshold overrides, returning results in structured JSON format.

    Available Tools:

    • analyze_code: Equivalent to pyscn analyze.
    • check_complexity: Complexity analyzer.
    • detect_clones: Clone detector.
    • check_coupling: CBO analyzer.
    • find_dead_code: Dead code analyzer.
    • get_health_score: Summary score.
    | Tool | Equivalent CLI |
    | --- | --- |
    | `analyze_code` | `pyscn analyze` |
    | `check_complexity` | Complexity analyzer |
    | `detect_clones` | Clone detector |
    | `check_coupling` | CBO analyzer |
    | `find_dead_code` | Dead code analyzer |
    | `get_health_score` | Summary score |
  4. Type-1: Textual Clone Detection with FNV Hash

    main

    Type-1 detection identifies code fragments that are textually identical after normalization. This analysis is opt-in via the EnableTextualAnalysis flag because it requires storing raw source content, which increases memory usage.

    Algorithm Steps:

    1. Normalization: Removes Python comments (single-line # and multi-line '''/""") and collapses whitespace, maintaining a single separator between word-token boundaries.
    2. FNV-64a Hash Comparison: Computes a 64-bit FNV hash of the normalized string. If hashes match, similarity is 1.0.
    3. Levenshtein Fallback: If hashes differ, it calculates the Levenshtein edit distance using a space-optimized O(min(m,n)) implementation. Similarity is calculated as: 1.0 - distance / max(len(s1), len(s2)).
  5. Understand the four types of code clones

    main

    pyscn detects four distinct types of code duplication, ranging from literal copy-pasting to semantic equivalence:

    1. Type-1 (Identical): Literal copy-paste where only whitespace or comments differ.
    2. Type-2 (Renamed Identifiers): The structure is identical, but variable or function names have changed.
    3. Type-3 (Modified Copies): A copy of a function that has had statements added or removed (detected via tree edit distance).
    4. Type-4 (Same Behavior, Different Implementation): The code is written differently but performs the same logic (detected via control-flow comparison).

    While tools like pylint or jscpd typically only catch Type-1 or Type-2, pyscn is designed to detect all four types.

  6. Understand module communities and the context map

    main

    The communities analysis groups modules into clusters based on their import structure. This is specifically designed to help AI agents understand which files belong together.

    To retrieve the full context map, you must:

    1. Include "communities" in the analyses array.
    2. Set output_mode to "full".

    The output includes:

    • bundles[]: Clusters of modules to review together. Each bundle contains modules, packages, risk_level, and a suggested_review_scope (a path prefix).
    • bridge_modules[]: Modules that connect two or more communities. These should be reviewed before changing cluster boundaries.

    Example Prompt: Map the module architecture of /home/user/project and tell me which files to review together

    {
      "community_analysis": {
        "total_communities": 2,
        "community_risk_score": 65,
        "community_context_map": {
          "version": 1,
          "bundles": [
            {
              "community_id": "community_1",
              "modules": ["app.orders.service", "app.orders.repository"],
              "module_count": 2,
              "packages": ["app.orders"],
              "risk_level": "low",
              "bridge_modules": [],
              "suggested_review_scope": "app/orders/",
              "summary": "2 modules; 1 package; risk low; 0 cross-community edges."
            }
          ],
          "bridge_modules": [
            {
              "module": "app.core.hub",
              "connects": ["community_1", "community_3"],
              "reason": "3 cross-community import edges"
            }
          ]
        }
      }
    }
  7. Interpret Community Detection results

    main

    The community analysis provides several metrics to evaluate the architectural health of a codebase:

    Communities

    • Cohesive Subsystems: Indicated by high internal_edges and low external_edges.
    • Coupling Hotspots: Indicated by a high external_dependency_ratio (the cluster depends heavily on other communities).
    • Cross-cutting Areas: Indicated by a large size spanning many packages.

    Bridge Modules

    Bridge modules belong to one community but have import edges into others. They are identified in the bridge_modules list and are primary targets for refactoring to reduce coupling.

    Modularity

    A quality score for the partition (range: $\approx -0.5$ to $1.0$).

    • $\approx 0$: Weak community structure.
    • $0.3 - 0.7$: Typical for repositories with identifiable subsystems.
    • Very high: Strong separation (verify the graph isn't trivially disconnected).

    Package and Layer Mismatch

    • package_alignment_score (0–1): Measures how well communities respect declared package boundaries. $1.0$ means every package is contained within exactly one community.
    • split_packages: Lists packages whose modules are split across multiple communities.
    • layer_alignment_score (0–1): Measures how well communities respect configured architecture layers.
    • cross_layer_communities: Communities that span multiple configured layers.
  8. Detect layer violations in architecture

    main

    The layer-violation rule flags import statements that violate your defined architectural boundaries. It ensures that modules in one layer do not depend on modules in a forbidden layer, preventing hidden coupling and maintaining testability.

    To use this rule, you must define your layers using [[architecture.layers]] (mapping package name fragments to layer names) and then define dependency constraints using [[architecture.rules]].

    This rule is triggered by running:

    • pyscn analyze
    • pyscn check --select deps
    pyscn analyze
    # or
    pyscn check --select deps
  9. Detect placeholder comments with the placeholder-comment rule

    main

    The placeholder-comment rule (categorized under mockdata) flags comments containing unfinished-work markers such as TODO, FIXME, XXX, HACK, BUG, or NOTE. This rule helps surface hidden scope and unmanaged technical debt within a codebase.

    You can trigger this specific rule using the pyscn check command with the --select mockdata flag.

    pyscn check --select mockdata
  10. Analyze dependency depth

    main

    Dependency depth measures the length of the longest chain of load-time dependencies.

    Calculation Logic

    1. The analyzer condenses all strongly connected components (SCCs) into single nodes to create a Directed Acyclic Graph (DAG).
    2. It calculates the longest path through this DAG using a topological pass.
    3. Note: Lazy function and method imports are excluded from depth/topology calculations to avoid including non-load-time dependencies.

    Benchmarking Depth

    For a project with $N$ modules, the expected maximum depth for a well-structured project is:

    expected = max(3, ceil(log2(N + 1)) + 1)

    Exceeding this threshold suggests poor layering or excessive transitive coupling.

  11. Understand the Module Dependency Data Model

    main

    pyscn represents a Python project as a directed graph where Nodes are Python modules (one per .py file) and Edges are import relationships.

    ModuleNode Fields

    Each module in the graph tracks the following metadata:

    • Name: Dotted module name (e.g., mypackage.submodule)
    • FilePath: Absolute path to the .py file
    • Package: Top-level package extracted from the name
    • IsPackage: true if the file is __init__.py
    • InDegree: Fan-in (number of modules importing this module)
    • OutDegree: Fan-out (number of modules this module imports)
    • Dependencies: Set of outgoing module names
    • Dependents: Set of incoming module names

    DependencyEdgeTypes

    Edges are categorized by the Python syntax used:

    • import: import module
    • from_import: from module import name
    • relative: from .module import name
    • implicit: Indirect dependencies
  12. Detect placeholder phone numbers

    main

    The placeholder-phone-number rule flags phone numbers in string literals that follow obviously fake patterns, such as all zeros (000-0000-0000), sequential digits (123-456-7890, 012-345-6789), or long runs of repeated digits. This rule is part of the mockdata category and is triggered using the pyscn check command.

    To avoid this warning, instead of using a fake string, you should leave the field empty, require it from the caller, or pull it from configuration.

    # Bad: uses a placeholder pattern
    default_phone = "000-0000-0000"
    
    # Good: uses None instead of a fake value
    default_phone: str | None = None