Skip to main content

A precise Python dead-code analyzer.

Project description

Culler

PyPI Python versions CI Package Check License: MIT

Culler is a fast, high-confidence dead-code analyzer for Python projects.

It is built in Rust, distributed as a single command-line tool, and designed to find dead Python code without turning ordinary public APIs, exports, tests, or dynamic Python edges into noisy findings.

culler check .

Runtime benchmark comparing Culler, Vulture, and deadcode F1 benchmark comparing Culler high-plus-review, Culler, Vulture, and deadcode

Left: median wall-clock runtime, where lower is better. Right: F1 score, the balance between precision and recall, where higher is better.

Benchmark: 15 realistic Python projects, 57,068 lines, 715 expected findings, and comparisons against Vulture and deadcode. Results are corpus-specific; the methodology and reproduction commands are below.

Highlights

  • High-confidence findings by default. Culler reports the findings it can support strongly, and keeps review-confidence findings available separately.
  • Whole-project reachability for applications. Production roots let Culler identify code that is unreachable from known entry points.
  • Conservative library analysis. Exports, public surfaces, tests, and dynamic behavior are handled carefully to avoid noisy reports.
  • Useful without heavy setup. Basic unused-code checks work with little or no configuration, and deeper reachability is enabled with pyproject.toml.
  • Automation-friendly output. JSON output uses stable rule and diagnostic identifiers for editors, dashboards, CI, and benchmark tooling.

Installation

The recommended installation methods for the CLI are uv tool and pipx:

uv tool install culler
pipx install culler

You can also install it into an existing Python environment:

python -m pip install culler

The PyPI package installs the compiled culler binary. It does not expose a Python import API yet.

Quick Start

Run Culler on the current project:

culler check .

Emit machine-readable JSON:

culler check . --format json

Show review-confidence findings as well as high-confidence findings:

culler check . --show-review

Explain a specific candidate or finding:

culler explain <candidate-or-finding-id> .

Exit codes are intentionally small:

Code Meaning
0 Analysis completed without default-visible findings.
1 Analysis completed and default-visible findings were reported.
2 Input, configuration, parse, or completeness error.

Configuration

Culler reads configuration from pyproject.toml.

[tool.culler]
src = "src"
mode = "auto"
target-python = "3.12"

For applications, declare production roots when you want reachability findings:

[tool.culler]
src = "src"
mode = "application"
root_coverage = "complete"
roots = ["my_app.cli:main"]

For libraries, Culler treats exported and externally visible surfaces more conservatively:

[tool.culler]
src = "src"
mode = "library"

Useful fields:

Field Purpose
src Source root or roots to analyze.
mode auto, application, or library.
root_coverage partial or complete when roots are known.
roots Application entry points such as pkg.cli:main.
tests Test paths when they are not discoverable conventionally.
target-python Python syntax/semantic target, currently 3.10 through 3.15.
exclude Glob patterns to exclude from analysis.
allow_partial Permit partial analysis without escalating to exit code 2.

Output and Rules

Text output is optimized for local development and CI logs. JSON output is intended for editors, dashboards, benchmarks, and automation.

culler check . --format json

Rule IDs are stable machine-readable identifiers:

Rule Finding
CULL001 Unreferenced function.
CULL002 Unreferenced class.
CULL003 Root-unreachable function.
CULL004 Root-unreachable class.
CULL005 Unused import binding.
CULL006 Unused local binding.
CULL007 Unreachable statement range.
CULL008 Unused private method.

Diagnostic IDs such as CULL_P0101 describe analysis, parsing, or configuration problems.

Benchmark Methodology

The benchmark compares Culler with Vulture and deadcode on one fixed corpus of artificial but realistic Python projects. The projects are artificial because precision and recall need ground truth; many real repositories contain little confirmed dead code, and exhaustive manual labeling is subjective. The corpus is designed to resemble code developers commonly inherit: services, libraries, CLIs, workers, pipelines, plugin systems, configuration packages, and utility-heavy AI-era codebases.

Scope Value
Projects 15
Python files 374
Python LOC 57,068
Expected findings 715
Clean projects 2
Large or noisy projects 2

The expected findings live under benchmark/expected/ and use comparable categories: unused imports, unused locals, unreachable statements, unused functions, unused classes, and unused private methods.

Tool Scope

Tool Why included
Culler Subject under evaluation.
Vulture Classic Python dead-code detector.
deadcode Newer whole-codebase Python unused-code detector.

Ruff, Pylint, Pyflakes, Flake8, autoflake, pycln, and unimport are not included in the headline comparison. They overlap on some unused-import or unused-local checks, but they are linters or cleanup tools rather than direct whole-project dead-code analyzers.

Scoring

Every expected finding not matched by a tool is a false negative. Every parsed tool finding in a scoreable category that does not match an expected finding is a false positive, including findings in clean projects. Matching is deterministic by category, path, symbol name where relevant, and source span; duplicate reports count once as a true positive and then as false positives.

Culler's headline score uses high-confidence findings. The benchmark also reports a Culler high-plus-review aggregate, which includes review-confidence findings from the same Culler JSON run. It does not represent a separate timed CLI invocation.

Runtime

Runtime includes subprocess startup time. That reflects command-line user experience and avoids special-casing tools written in different languages. By default, each tool gets one warmup run and five measured runs per project; the report uses median wall time. Result JSON records command lines, tool versions, Python version, OS, CPU, memory, and the Culler commit.

Peak RSS is recorded where /usr/bin/time -l exposes it. If unavailable, the result uses null.

Running the Benchmark

Validate the corpus and expected files:

python3 benchmark/run.py --validate-only

Run the benchmark runner self-tests:

python3 benchmark/test_run.py

Build Culler and run the full benchmark:

cargo build --release
python3 benchmark/run.py \
  --culler target/release/culler \
  --tools culler,vulture,deadcode \
  --runs 5 \
  --results benchmark/results/latest.json

Generated reports are written under benchmark/results/ and ignored by default. Raw tool outputs are retained under benchmark/results/raw/.

Development

Prerequisites:

  • Rust 1.82 or newer
  • Python 3.10 or newer
  • uv for packaging and benchmark helper commands

Core checks:

cargo fmt --all --check
cargo check
cargo clippy --all-targets --all-features -- -D warnings
cargo test
python3 benchmark/test_run.py
python3 benchmark/run.py --validate-only

Build the PyPI package locally:

uvx maturin build --release --out dist --sdist
uvx twine check dist/*

Status

Culler is pre-1.0 software. Rule IDs are intended to be stable, but CLI, configuration, and JSON output details may still evolve before 1.0.

Releases use reviewed PRs and changelog updates. See CONTRIBUTING.md for commit and release guidance.

License

Culler is released under the MIT License. See LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

culler-0.1.0.tar.gz (153.2 kB view details)

Uploaded Source

Built Distributions

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

culler-0.1.0-py3-none-win_amd64.whl (2.4 MB view details)

Uploaded Python 3Windows x86-64

culler-0.1.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.4 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ x86-64

culler-0.1.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (2.3 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ ARM64

culler-0.1.0-py3-none-macosx_11_0_arm64.whl (2.2 MB view details)

Uploaded Python 3macOS 11.0+ ARM64

culler-0.1.0-py3-none-macosx_10_12_x86_64.whl (2.4 MB view details)

Uploaded Python 3macOS 10.12+ x86-64

File details

Details for the file culler-0.1.0.tar.gz.

File metadata

  • Download URL: culler-0.1.0.tar.gz
  • Upload date:
  • Size: 153.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for culler-0.1.0.tar.gz
Algorithm Hash digest
SHA256 5068bf58a1a8d2b9f8edf29a5c39455ab404a116198ef53c40c13fabf1331ea9
MD5 a89ee5ece73c69b35162793440373b61
BLAKE2b-256 146a0b6d704a04b47e6b9cb946b210179e21d21bbfe5e4e52b683d38f13ef043

See more details on using hashes here.

Provenance

The following attestation bundles were made for culler-0.1.0.tar.gz:

Publisher: release.yml on beaubhp/culler

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

File details

Details for the file culler-0.1.0-py3-none-win_amd64.whl.

File metadata

  • Download URL: culler-0.1.0-py3-none-win_amd64.whl
  • Upload date:
  • Size: 2.4 MB
  • Tags: Python 3, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for culler-0.1.0-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 4ffdc66fb3d4ecffd5c66b5104b30e0a53c8de4f786beb4aa33b5b85ca953af2
MD5 d754da25250bfc45a20cea9ff76b2f84
BLAKE2b-256 2f8ea9c3316df8ffba6a5c4173b63e1db2b6b179089789ae0e436ae1e438b150

See more details on using hashes here.

Provenance

The following attestation bundles were made for culler-0.1.0-py3-none-win_amd64.whl:

Publisher: release.yml on beaubhp/culler

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

File details

Details for the file culler-0.1.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for culler-0.1.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 be7ebe4f55adbb6a1574edf2c0498b2b0d8fb350f68d1a21bf10b58210a479b3
MD5 cfbcfff6cad80f82ae6e4241795543d7
BLAKE2b-256 94f401fd33ff66b4a18c1afd78aa22b2408d40c26beb0b968545a4b36a03df61

See more details on using hashes here.

Provenance

The following attestation bundles were made for culler-0.1.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on beaubhp/culler

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

File details

Details for the file culler-0.1.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for culler-0.1.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 48a8aada9a130b8f0bacc15cbda3a1c2388c19cb1b00fb5b22379976e6e0adff
MD5 fc6fef0792947dfb447a7b525f26d10f
BLAKE2b-256 fed558aea7a40935002f6ee0375554bbf7ae95c496001465195c87737e42318c

See more details on using hashes here.

Provenance

The following attestation bundles were made for culler-0.1.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release.yml on beaubhp/culler

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

File details

Details for the file culler-0.1.0-py3-none-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for culler-0.1.0-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 bca40c390a68db84809e80a7cc8ed34259d134f717c764894f866d716d8660c5
MD5 c448f8c1077608836bfff9dcaab3a074
BLAKE2b-256 36eba188c186d6292123a2df56b83bea8b227f55e2cc76faf31663a6c868d8c7

See more details on using hashes here.

Provenance

The following attestation bundles were made for culler-0.1.0-py3-none-macosx_11_0_arm64.whl:

Publisher: release.yml on beaubhp/culler

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

File details

Details for the file culler-0.1.0-py3-none-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for culler-0.1.0-py3-none-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 4f6d5f706bbc7ff25f58dd2945085312e74a331bb0de66961065c7733e5fcb70
MD5 787e16376bed5873fb881edd163d18bd
BLAKE2b-256 860b2352e271b3d66e1c2f2c8563e2a08e21517d999c2f36f29d0ea59527f700

See more details on using hashes here.

Provenance

The following attestation bundles were made for culler-0.1.0-py3-none-macosx_10_12_x86_64.whl:

Publisher: release.yml on beaubhp/culler

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

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page