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)flake8pylint
[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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0b6c6dc454ef51fe023b76b2d339e1e2a906b02dc10df01b05cb75ed059268f3
|
|
| MD5 |
8c155052b10ac76c599538a0a48cabf8
|
|
| BLAKE2b-256 |
42c5237f56a4831bb08826e54ed1d080e5508913c294e4bbde4d5691b1923ba0
|
Provenance
The following attestation bundles were made for pycmdcheck-0.1.1.tar.gz:
Publisher:
release.yml on coatless-py-pkg/pycmdcheck
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pycmdcheck-0.1.1.tar.gz -
Subject digest:
0b6c6dc454ef51fe023b76b2d339e1e2a906b02dc10df01b05cb75ed059268f3 - Sigstore transparency entry: 1960145807
- Sigstore integration time:
-
Permalink:
coatless-py-pkg/pycmdcheck@08b9337b3156bb91057c336647ccc3f6cbdeb8d6 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/coatless-py-pkg
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@08b9337b3156bb91057c336647ccc3f6cbdeb8d6 -
Trigger Event:
push
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c19aadfae5b644b568daf9270691a12f12116926a7d69ad53c5dd35dba2e999c
|
|
| MD5 |
6e629694b3ed0b1e4c64441bf037b3b0
|
|
| BLAKE2b-256 |
0a144e8ab931bc822c2e538ca55aa8dfecf82f1ac6ce44ca8eb07da30dc2dcd3
|
Provenance
The following attestation bundles were made for pycmdcheck-0.1.1-py3-none-any.whl:
Publisher:
release.yml on coatless-py-pkg/pycmdcheck
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pycmdcheck-0.1.1-py3-none-any.whl -
Subject digest:
c19aadfae5b644b568daf9270691a12f12116926a7d69ad53c5dd35dba2e999c - Sigstore transparency entry: 1960145832
- Sigstore integration time:
-
Permalink:
coatless-py-pkg/pycmdcheck@08b9337b3156bb91057c336647ccc3f6cbdeb8d6 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/coatless-py-pkg
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@08b9337b3156bb91057c336647ccc3f6cbdeb8d6 -
Trigger Event:
push
-
Statement type: