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

pip install pycmdcheck

Or with uv:

uv add pycmdcheck

Optional dependencies

# For mypy type checking
pip install pycmdcheck[typing-mypy]

# For pyright type checking
pip install pycmdcheck[typing-pyright]

# For ruff linting
pip install pycmdcheck[linting]

# All optional dependencies
pip install pycmdcheck[all]

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 original core checks
pyopensci pyOpenSci onboarding All checks with stricter docs settings
strict Maximum strictness All 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.1.tar.gz (828.4 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.1-py3-none-any.whl (74.3 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for pycmdcheck-0.1.1.tar.gz
Algorithm Hash digest
SHA256 0b6c6dc454ef51fe023b76b2d339e1e2a906b02dc10df01b05cb75ed059268f3
MD5 8c155052b10ac76c599538a0a48cabf8
BLAKE2b-256 42c5237f56a4831bb08826e54ed1d080e5508913c294e4bbde4d5691b1923ba0

See more details on using hashes here.

Provenance

The following attestation bundles were made for pycmdcheck-0.1.1.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.1-py3-none-any.whl.

File metadata

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

File hashes

Hashes for pycmdcheck-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 c19aadfae5b644b568daf9270691a12f12116926a7d69ad53c5dd35dba2e999c
MD5 6e629694b3ed0b1e4c64441bf037b3b0
BLAKE2b-256 0a144e8ab931bc822c2e538ca55aa8dfecf82f1ac6ce44ca8eb07da30dc2dcd3

See more details on using hashes here.

Provenance

The following attestation bundles were made for pycmdcheck-0.1.1-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