Skip to main content

Python package quality checker inspired by R CMD check

Project description

pycmdcheck

A Python package quality checker inspired by R's R CMD check.

Installation

Note: pycmdcheck is not yet published to PyPI. Install it directly from GitHub for now. Once it is released, pip install pycmdcheck will work too.

pip install git+https://github.com/coatless-py-pkg/pycmdcheck

Or with uv:

uv pip install git+https://github.com/coatless-py-pkg/pycmdcheck

To pin a specific branch, tag, or commit, append @<ref> to the URL — for example git+https://github.com/coatless-py-pkg/pycmdcheck@main.

Optional dependencies

pycmdcheck can use external tools for linting and type checking. Request the matching extra in the install command. For a GitHub install, the extra goes in the PEP 508 pycmdcheck[extra] @ <url> form:

# For mypy type checking
pip install "pycmdcheck[typing-mypy] @ git+https://github.com/coatless-py-pkg/pycmdcheck"

# For pyright type checking
pip install "pycmdcheck[typing-pyright] @ git+https://github.com/coatless-py-pkg/pycmdcheck"

# For ruff linting
pip install "pycmdcheck[linting] @ git+https://github.com/coatless-py-pkg/pycmdcheck"

# All optional dependencies
pip install "pycmdcheck[all] @ git+https://github.com/coatless-py-pkg/pycmdcheck"

Usage

Command Line

# Check current directory
pycmdcheck

# Check a specific directory
pycmdcheck /path/to/package

# Run only specific checks
pycmdcheck -c metadata -c tests

# Skip specific checks
pycmdcheck -s typing -s linting

# Fail on warnings too
pycmdcheck --fail-on error --fail-on warning

# List available checks
pycmdcheck --list

# JSON output
pycmdcheck --json

# Stop after first error (fail fast)
pycmdcheck -x
pycmdcheck --fail-fast

# Verbose output with details
pycmdcheck -v

# Use a check profile
pycmdcheck --profile pyopensci
pycmdcheck --profile minimal
pycmdcheck --profile strict

# List available profiles
pycmdcheck --list-profiles

With uv

uv run pycmdcheck
uv run pycmdcheck -c tests -c linting

Python API

from pycmdcheck import check, CheckStatus

# Check current directory
report = check(".")

# Check specific directory
report = check("/path/to/package")

# Run only specific checks
report = check(".", checks=["metadata", "tests"])

# Skip specific checks
report = check(".", skip=["linting", "typing"])

# Check results
if report.passed:
    print("All checks passed!")
else:
    for result in report.results:
        print(f"{result.status}: {result.name} - {result.message}")

# Get counts by status
counts = report.count_by_status()
print(f"OK: {counts[CheckStatus.OK]}, Errors: {counts[CheckStatus.ERROR]}")

# JSON output
import json
print(json.dumps(report.to_dict(), indent=2))

Built-in Checks

Core checks

Check Description
build Verifies the package builds successfully (wheel/sdist)
dependencies Audits declared vs. actual imports, warns on unbounded versions
docs Checks for README (content sections, word count) and docstrings
formatting Checks code formatting (ruff format/black)
imports Validates all imports can be resolved
license Checks for LICENSE file and OSI-approved license validation
linting Runs ruff, flake8, or pylint
metadata Validates pyproject.toml fields (required, recommended, extended)
py_typed Checks for PEP 561 py.typed marker
structure Checks package directory structure (src or flat layout)
tests Runs pytest or unittest and reports results
typing Runs mypy or pyright type checker
version Verifies version consistency between pyproject.toml and code

pyOpenSci / community checks

Check Description
changelog Checks for CHANGELOG, NEWS, or HISTORY file
ci Checks for CI configuration (GitHub Actions, CircleCI, etc.)
citation Checks for CITATION.cff or CITATION.bib file
community Checks for CONTRIBUTING.md and CODE_OF_CONDUCT.md
doctests Runs doctests via pytest --doctest-modules
python_versions Checks requires-python excludes EOL Python versions
urls Validates project URLs in metadata are reachable

Profiles

Profiles bundle checks for common use cases:

Profile Description Checks
minimal Quick sanity checks metadata, structure, license
triage Static checks only — no install, external tools, or network (for CI triage of arbitrary repos) All static checks (excludes tests, imports, dependencies, build, linting, typing, formatting, doctests, urls)
default Standard quality checks All 13 core checks
pyopensci pyOpenSci onboarding All 20 checks with stricter docs settings
strict Maximum strictness All 20 checks with strict typing
# Run pyOpenSci onboarding checks
pycmdcheck --profile pyopensci

# Static-only triage (safe to run on an un-installed repo in CI)
pycmdcheck --profile triage

# Quick check before committing
pycmdcheck --profile minimal

Profiles can be combined with --skip to exclude specific checks:

pycmdcheck --profile pyopensci -s urls -s doctests

Configuration

Configure pycmdcheck in your pyproject.toml:

[tool.pycmdcheck]
# Fail on these statuses (default: ["error"])
fail_on = ["error", "warning"]

[tool.pycmdcheck.checks]
# Enable/disable checks with boolean
metadata = true
structure = true
imports = true
license = true

# Configure checks with options
tests = { enabled = true, runner = "pytest" }
linting = { enabled = true, tool = "ruff" }
typing = { enabled = true, tool = "mypy", strict = false }
# formatting "tool" defaults to "auto": detects ruff vs black from
# [tool.ruff]/ruff.toml or [tool.black]; skips if neither is configured.
formatting = { enabled = true, tool = "auto" }
docs = { enabled = true, require_readme = true, check_docstrings = false, check_readme_sections = false }

# New checks (disabled by default in "default" profile)
community = true
ci = true
changelog = true
citation = true
python_versions = true
urls = { enabled = true, timeout = 10 }
doctests = true

Linting tools

Supported linting tools:

  • ruff (default, recommended)
  • flake8
  • pylint
[tool.pycmdcheck.checks.linting]
tool = "ruff"
args = ["--select=E,F,W"]

Build options

[tool.pycmdcheck.checks.build]
timeout = 300  # seconds (default: 120)
args = ["--wheel"]

Type checking tools

Supported type checkers:

  • mypy (default)
  • pyright
[tool.pycmdcheck.checks.typing]
tool = "pyright"
strict = true

Custom Checks

Create custom checks by implementing the Check protocol:

from pathlib import Path
from typing import Any
from pycmdcheck.checks.base import BaseCheck
from pycmdcheck.results import CheckResult, CheckStatus

class MyCustomCheck(BaseCheck):
    name = "my_custom_check"
    description = "My custom quality check"

    def run(self, package_path: Path, config: dict[str, Any]) -> CheckResult:
        # Your check logic here
        issues = []  # ... populate with any issues found
        if not issues:
            return CheckResult(
                name=self.name,
                status=CheckStatus.OK,
                message="All good!",
            )
        return CheckResult(
            name=self.name,
            status=CheckStatus.ERROR,
            message="Something is wrong",
            details=issues,
        )

Register your check via entry points in pyproject.toml:

[project.entry-points."pycmdcheck.checks"]
my_custom_check = "my_package.checks:MyCustomCheck"

Check Statuses

Status Symbol Description
OK Check passed
NOTE Minor observation, not a problem
WARNING Potential issue, should be reviewed
ERROR Check failed
SKIPPED Check was skipped (e.g., tool not installed)

License

MIT

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

pycmdcheck-0.1.0.tar.gz (827.6 kB view details)

Uploaded Source

Built Distribution

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

pycmdcheck-0.1.0-py3-none-any.whl (74.0 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for pycmdcheck-0.1.0.tar.gz
Algorithm Hash digest
SHA256 e77f20ca7674c61666fa1e57231800301be3d7980dbc9a4f965cdde5efbaaaba
MD5 555777118ef4e2d4fa018088a93c3859
BLAKE2b-256 0d99e39fa1b18f6d5fdace02ec6dd863ffb1f2a1a9680dfe681be21396b74359

See more details on using hashes here.

Provenance

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

Publisher: release.yml on coatless-py-pkg/pycmdcheck

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

File details

Details for the file pycmdcheck-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: pycmdcheck-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 74.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pycmdcheck-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 71f559c35a35e8831f13aa4ac865483144d3e1fad49d7cbeb4d0cf61b54720c6
MD5 41cc886f67762c983ce07450c6acaae0
BLAKE2b-256 3f4335c8549bcb244a99940bcf2140a5dd246a4dad0a7fc16a23ca0d973d8454

See more details on using hashes here.

Provenance

The following attestation bundles were made for pycmdcheck-0.1.0-py3-none-any.whl:

Publisher: release.yml on coatless-py-pkg/pycmdcheck

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