pycmdcheck 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. pycmdcheck-0.1.0/.devcontainer/Dockerfile +17 -0
  2. pycmdcheck-0.1.0/.devcontainer/devcontainer.json +61 -0
  3. pycmdcheck-0.1.0/.devcontainer/requirements-dev.txt +5 -0
  4. pycmdcheck-0.1.0/.github/workflows/ci.yml +54 -0
  5. pycmdcheck-0.1.0/.github/workflows/release.yml +52 -0
  6. pycmdcheck-0.1.0/.gitignore +22 -0
  7. pycmdcheck-0.1.0/CHANGELOG.md +78 -0
  8. pycmdcheck-0.1.0/LICENSE +21 -0
  9. pycmdcheck-0.1.0/PKG-INFO +346 -0
  10. pycmdcheck-0.1.0/README.md +304 -0
  11. pycmdcheck-0.1.0/docs/.gitignore +2 -0
  12. pycmdcheck-0.1.0/docs/_extensions/machow/interlinks/.gitignore +3 -0
  13. pycmdcheck-0.1.0/docs/_extensions/machow/interlinks/_extension.yml +7 -0
  14. pycmdcheck-0.1.0/docs/_extensions/machow/interlinks/interlinks.lua +411 -0
  15. pycmdcheck-0.1.0/docs/_extensions/machow/interlinks/objects.txt +7 -0
  16. pycmdcheck-0.1.0/docs/_extensions/machow/interlinks/test.qmd +22 -0
  17. pycmdcheck-0.1.0/docs/_inv/python_objects.json +1 -0
  18. pycmdcheck-0.1.0/docs/_quarto.yml +85 -0
  19. pycmdcheck-0.1.0/docs/_site/changelog.html +618 -0
  20. pycmdcheck-0.1.0/docs/_site/cli.html +788 -0
  21. pycmdcheck-0.1.0/docs/_site/configuration.html +900 -0
  22. pycmdcheck-0.1.0/docs/_site/custom-checks.html +882 -0
  23. pycmdcheck-0.1.0/docs/_site/getting-started.html +784 -0
  24. pycmdcheck-0.1.0/docs/_site/index.html +680 -0
  25. pycmdcheck-0.1.0/docs/_site/reference/CheckResult.html +693 -0
  26. pycmdcheck-0.1.0/docs/_site/reference/CheckStatus.html +638 -0
  27. pycmdcheck-0.1.0/docs/_site/reference/Report.html +797 -0
  28. pycmdcheck-0.1.0/docs/_site/reference/check.html +657 -0
  29. pycmdcheck-0.1.0/docs/_site/reference/checks.base.BaseCheck.html +727 -0
  30. pycmdcheck-0.1.0/docs/_site/reference/checks.base.Check.html +684 -0
  31. pycmdcheck-0.1.0/docs/_site/reference/checks.docs.DocsCheck.html +707 -0
  32. pycmdcheck-0.1.0/docs/_site/reference/checks.imports.ImportsCheck.html +707 -0
  33. pycmdcheck-0.1.0/docs/_site/reference/checks.license.LicenseCheck.html +698 -0
  34. pycmdcheck-0.1.0/docs/_site/reference/checks.linting.LintingCheck.html +713 -0
  35. pycmdcheck-0.1.0/docs/_site/reference/checks.metadata.MetadataCheck.html +709 -0
  36. pycmdcheck-0.1.0/docs/_site/reference/checks.structure.StructureCheck.html +701 -0
  37. pycmdcheck-0.1.0/docs/_site/reference/checks.tests.TestsCheck.html +709 -0
  38. pycmdcheck-0.1.0/docs/_site/reference/checks.typing.TypingCheck.html +707 -0
  39. pycmdcheck-0.1.0/docs/_site/reference/config.get_check_config.html +645 -0
  40. pycmdcheck-0.1.0/docs/_site/reference/config.is_check_enabled.html +636 -0
  41. pycmdcheck-0.1.0/docs/_site/reference/config.load_config.html +643 -0
  42. pycmdcheck-0.1.0/docs/_site/reference/discovery.discover_checks.html +636 -0
  43. pycmdcheck-0.1.0/docs/_site/reference/discovery.list_available_checks.html +652 -0
  44. pycmdcheck-0.1.0/docs/_site/reference/index.html +688 -0
  45. pycmdcheck-0.1.0/docs/_site/reference/runner.run_checks.html +661 -0
  46. pycmdcheck-0.1.0/docs/_site/search.json +856 -0
  47. pycmdcheck-0.1.0/docs/_site/site_libs/bootstrap/bootstrap-5b4ad623e5705c0698d39aec6f10cf02.min.css +12 -0
  48. pycmdcheck-0.1.0/docs/_site/site_libs/bootstrap/bootstrap-icons.css +2106 -0
  49. pycmdcheck-0.1.0/docs/_site/site_libs/bootstrap/bootstrap-icons.woff +0 -0
  50. pycmdcheck-0.1.0/docs/_site/site_libs/bootstrap/bootstrap.min.js +7 -0
  51. pycmdcheck-0.1.0/docs/_site/site_libs/clipboard/clipboard.min.js +7 -0
  52. pycmdcheck-0.1.0/docs/_site/site_libs/quarto-html/anchor.min.js +9 -0
  53. pycmdcheck-0.1.0/docs/_site/site_libs/quarto-html/axe/axe-check.js +145 -0
  54. pycmdcheck-0.1.0/docs/_site/site_libs/quarto-html/popper.min.js +6 -0
  55. pycmdcheck-0.1.0/docs/_site/site_libs/quarto-html/quarto-syntax-highlighting-587c61ba64f3a5504c4d52d930310e48.css +236 -0
  56. pycmdcheck-0.1.0/docs/_site/site_libs/quarto-html/quarto.js +847 -0
  57. pycmdcheck-0.1.0/docs/_site/site_libs/quarto-html/tabsets/tabsets.js +95 -0
  58. pycmdcheck-0.1.0/docs/_site/site_libs/quarto-html/tippy.css +1 -0
  59. pycmdcheck-0.1.0/docs/_site/site_libs/quarto-html/tippy.umd.min.js +2 -0
  60. pycmdcheck-0.1.0/docs/_site/site_libs/quarto-nav/headroom.min.js +7 -0
  61. pycmdcheck-0.1.0/docs/_site/site_libs/quarto-nav/quarto-nav.js +325 -0
  62. pycmdcheck-0.1.0/docs/_site/site_libs/quarto-search/autocomplete.umd.js +3 -0
  63. pycmdcheck-0.1.0/docs/_site/site_libs/quarto-search/fuse.min.js +9 -0
  64. pycmdcheck-0.1.0/docs/_site/site_libs/quarto-search/quarto-search.js +1290 -0
  65. pycmdcheck-0.1.0/docs/changelog.qmd +5 -0
  66. pycmdcheck-0.1.0/docs/cli.qmd +249 -0
  67. pycmdcheck-0.1.0/docs/configuration.qmd +299 -0
  68. pycmdcheck-0.1.0/docs/custom-checks.qmd +264 -0
  69. pycmdcheck-0.1.0/docs/getting-started.qmd +146 -0
  70. pycmdcheck-0.1.0/docs/index.qmd +76 -0
  71. pycmdcheck-0.1.0/docs/objects.json +1 -0
  72. pycmdcheck-0.1.0/docs/reference/CheckResult.qmd +91 -0
  73. pycmdcheck-0.1.0/docs/reference/CheckStatus.qmd +44 -0
  74. pycmdcheck-0.1.0/docs/reference/Report.qmd +178 -0
  75. pycmdcheck-0.1.0/docs/reference/_sidebar.yml +36 -0
  76. pycmdcheck-0.1.0/docs/reference/check.qmd +73 -0
  77. pycmdcheck-0.1.0/docs/reference/checks.base.BaseCheck.qmd +116 -0
  78. pycmdcheck-0.1.0/docs/reference/checks.base.Check.qmd +72 -0
  79. pycmdcheck-0.1.0/docs/reference/checks.docs.DocsCheck.qmd +101 -0
  80. pycmdcheck-0.1.0/docs/reference/checks.imports.ImportsCheck.qmd +96 -0
  81. pycmdcheck-0.1.0/docs/reference/checks.license.LicenseCheck.qmd +89 -0
  82. pycmdcheck-0.1.0/docs/reference/checks.linting.LintingCheck.qmd +105 -0
  83. pycmdcheck-0.1.0/docs/reference/checks.metadata.MetadataCheck.qmd +98 -0
  84. pycmdcheck-0.1.0/docs/reference/checks.structure.StructureCheck.qmd +92 -0
  85. pycmdcheck-0.1.0/docs/reference/checks.tests.TestsCheck.qmd +102 -0
  86. pycmdcheck-0.1.0/docs/reference/checks.typing.TypingCheck.qmd +102 -0
  87. pycmdcheck-0.1.0/docs/reference/config.get_check_config.qmd +57 -0
  88. pycmdcheck-0.1.0/docs/reference/config.is_check_enabled.qmd +38 -0
  89. pycmdcheck-0.1.0/docs/reference/config.load_config.qmd +42 -0
  90. pycmdcheck-0.1.0/docs/reference/discovery.discover_checks.qmd +51 -0
  91. pycmdcheck-0.1.0/docs/reference/discovery.list_available_checks.qmd +60 -0
  92. pycmdcheck-0.1.0/docs/reference/index.qmd +56 -0
  93. pycmdcheck-0.1.0/docs/reference/runner.run_checks.qmd +73 -0
  94. pycmdcheck-0.1.0/pyproject.toml +111 -0
  95. pycmdcheck-0.1.0/src/pycmdcheck/__init__.py +104 -0
  96. pycmdcheck-0.1.0/src/pycmdcheck/__main__.py +6 -0
  97. pycmdcheck-0.1.0/src/pycmdcheck/ast_cache.py +45 -0
  98. pycmdcheck-0.1.0/src/pycmdcheck/checks/__init__.py +1 -0
  99. pycmdcheck-0.1.0/src/pycmdcheck/checks/base.py +143 -0
  100. pycmdcheck-0.1.0/src/pycmdcheck/checks/build.py +101 -0
  101. pycmdcheck-0.1.0/src/pycmdcheck/checks/changelog.py +101 -0
  102. pycmdcheck-0.1.0/src/pycmdcheck/checks/ci.py +87 -0
  103. pycmdcheck-0.1.0/src/pycmdcheck/checks/citation.py +102 -0
  104. pycmdcheck-0.1.0/src/pycmdcheck/checks/community.py +108 -0
  105. pycmdcheck-0.1.0/src/pycmdcheck/checks/dependencies.py +309 -0
  106. pycmdcheck-0.1.0/src/pycmdcheck/checks/docs.py +305 -0
  107. pycmdcheck-0.1.0/src/pycmdcheck/checks/doctests.py +129 -0
  108. pycmdcheck-0.1.0/src/pycmdcheck/checks/formatting.py +200 -0
  109. pycmdcheck-0.1.0/src/pycmdcheck/checks/imports.py +239 -0
  110. pycmdcheck-0.1.0/src/pycmdcheck/checks/license.py +304 -0
  111. pycmdcheck-0.1.0/src/pycmdcheck/checks/linting.py +304 -0
  112. pycmdcheck-0.1.0/src/pycmdcheck/checks/metadata.py +233 -0
  113. pycmdcheck-0.1.0/src/pycmdcheck/checks/py_typed.py +207 -0
  114. pycmdcheck-0.1.0/src/pycmdcheck/checks/python_versions.py +188 -0
  115. pycmdcheck-0.1.0/src/pycmdcheck/checks/structure.py +282 -0
  116. pycmdcheck-0.1.0/src/pycmdcheck/checks/tests.py +230 -0
  117. pycmdcheck-0.1.0/src/pycmdcheck/checks/typing.py +257 -0
  118. pycmdcheck-0.1.0/src/pycmdcheck/checks/urls.py +124 -0
  119. pycmdcheck-0.1.0/src/pycmdcheck/checks/version.py +184 -0
  120. pycmdcheck-0.1.0/src/pycmdcheck/cli.py +296 -0
  121. pycmdcheck-0.1.0/src/pycmdcheck/config.py +194 -0
  122. pycmdcheck-0.1.0/src/pycmdcheck/constants.py +29 -0
  123. pycmdcheck-0.1.0/src/pycmdcheck/discovery.py +127 -0
  124. pycmdcheck-0.1.0/src/pycmdcheck/package_layout.py +234 -0
  125. pycmdcheck-0.1.0/src/pycmdcheck/profiles.py +158 -0
  126. pycmdcheck-0.1.0/src/pycmdcheck/py.typed +0 -0
  127. pycmdcheck-0.1.0/src/pycmdcheck/pyproject_reader.py +226 -0
  128. pycmdcheck-0.1.0/src/pycmdcheck/results.py +320 -0
  129. pycmdcheck-0.1.0/src/pycmdcheck/runner.py +236 -0
  130. pycmdcheck-0.1.0/src/pycmdcheck/subprocess_runner.py +166 -0
  131. pycmdcheck-0.1.0/tests/__init__.py +1 -0
  132. pycmdcheck-0.1.0/tests/conftest.py +375 -0
  133. pycmdcheck-0.1.0/tests/test_ast_cache.py +37 -0
  134. pycmdcheck-0.1.0/tests/test_check_build.py +130 -0
  135. pycmdcheck-0.1.0/tests/test_check_changelog.py +49 -0
  136. pycmdcheck-0.1.0/tests/test_check_ci.py +63 -0
  137. pycmdcheck-0.1.0/tests/test_check_citation.py +56 -0
  138. pycmdcheck-0.1.0/tests/test_check_community.py +74 -0
  139. pycmdcheck-0.1.0/tests/test_check_dependencies.py +143 -0
  140. pycmdcheck-0.1.0/tests/test_check_deps_enhanced.py +53 -0
  141. pycmdcheck-0.1.0/tests/test_check_docs.py +120 -0
  142. pycmdcheck-0.1.0/tests/test_check_doctests.py +110 -0
  143. pycmdcheck-0.1.0/tests/test_check_formatting.py +202 -0
  144. pycmdcheck-0.1.0/tests/test_check_license.py +102 -0
  145. pycmdcheck-0.1.0/tests/test_check_metadata.py +131 -0
  146. pycmdcheck-0.1.0/tests/test_check_py_typed.py +40 -0
  147. pycmdcheck-0.1.0/tests/test_check_python_versions.py +90 -0
  148. pycmdcheck-0.1.0/tests/test_check_urls.py +61 -0
  149. pycmdcheck-0.1.0/tests/test_check_version.py +140 -0
  150. pycmdcheck-0.1.0/tests/test_checks.py +592 -0
  151. pycmdcheck-0.1.0/tests/test_cli.py +204 -0
  152. pycmdcheck-0.1.0/tests/test_config.py +86 -0
  153. pycmdcheck-0.1.0/tests/test_config_validation.py +40 -0
  154. pycmdcheck-0.1.0/tests/test_integration.py +45 -0
  155. pycmdcheck-0.1.0/tests/test_package_layout.py +97 -0
  156. pycmdcheck-0.1.0/tests/test_profiles.py +111 -0
  157. pycmdcheck-0.1.0/tests/test_pyproject_reader.py +154 -0
  158. pycmdcheck-0.1.0/tests/test_results.py +173 -0
  159. pycmdcheck-0.1.0/tests/test_runner.py +247 -0
  160. pycmdcheck-0.1.0/tests/test_subprocess_runner.py +762 -0
  161. pycmdcheck-0.1.0/tests/test_tool_checks_mocked.py +204 -0
  162. pycmdcheck-0.1.0/uv.lock +1264 -0
@@ -0,0 +1,17 @@
1
+ # Python version: 3, 3.10, 3.11, 3.12, ...
2
+ # https://hub.docker.com/_/microsoft-devcontainers-python
3
+ ARG VARIANT="3.12"
4
+ FROM mcr.microsoft.com/devcontainers/python:${VARIANT}
5
+
6
+ # Setup devcontainer user
7
+ ARG USERNAME=vscode
8
+ ARG USER_UID=1000
9
+ ARG USER_GID=$USER_UID
10
+
11
+ # Update pip
12
+ RUN pip install --upgrade pip
13
+
14
+ # Install pkg dev dependencies
15
+ COPY requirements-dev.txt /tmp/requirements-dev.txt
16
+ RUN pip install -r /tmp/requirements-dev.txt && \
17
+ rm /tmp/requirements-dev.txt
@@ -0,0 +1,61 @@
1
+ // For format details, see https://aka.ms/devcontainer.json.
2
+ {
3
+ "name": "Python 3",
4
+ "build": {
5
+ // More info: https://containers.dev/guide/dockerfile
6
+ "dockerfile": "Dockerfile",
7
+ // Change VARIANT to pick different Python version: 3, 3.10, 3.11, 3.12, ...
8
+ "args": {
9
+ "VARIANT": "3.12"
10
+ }
11
+ },
12
+ "customizations": {
13
+ "vscode": {
14
+ "extensions": [
15
+ // Python
16
+ "ms-python.python",
17
+ "ms-python.black-formatter",
18
+ "ms-toolsai.jupyter",
19
+ "ms-toolsai.jupyter-renderers",
20
+ "ms-python.vscode-pylance",
21
+ // Docker
22
+ "ms-azuretools.vscode-docker",
23
+ // Documentation
24
+ "njpwerner.autodocstring",
25
+ // Format
26
+ "stkb.rewrap",
27
+ // Lint
28
+ "charliermarsh.ruff",
29
+ // Gen AI
30
+ "GitHub.copilot"
31
+ ],
32
+ "settings": {
33
+ // Editor
34
+ "editor.formatOnSave": true,
35
+ // Files
36
+ "files.insertFinalNewline": true,
37
+ "files.trimTrailingWhitespace": true,
38
+ // Testing
39
+ "python.testing.pytestArgs": [
40
+ "."
41
+ ],
42
+ "python.testing.unittestEnabled": false,
43
+ "python.testing.pytestEnabled": true,
44
+ "python.testing.autoTestDiscoverOnSaveEnabled": true,
45
+ // Formatting
46
+ "python.formatting.provider": "black",
47
+ "python.formatting.blackArgs": [
48
+ "--no-color"
49
+ ],
50
+ // Linting
51
+ "python.linting.mypyEnabled": true,
52
+ "python.linting.enabled": true
53
+ },
54
+ "git.branchProtection": [
55
+ "main",
56
+ "master"
57
+ ]
58
+ }
59
+ },
60
+ "remoteUser": "vscode"
61
+ }
@@ -0,0 +1,5 @@
1
+ cookiecutter>=2.6.0
2
+ pytest>=8.2.0
3
+ ruff>=0.4.4
4
+ black>=24.4.2
5
+ mypy>=1.10
@@ -0,0 +1,54 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ concurrency:
12
+ group: ci-${{ github.ref }}
13
+ cancel-in-progress: true
14
+
15
+ jobs:
16
+ lint:
17
+ name: Lint & type-check
18
+ runs-on: ubuntu-latest
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+ - name: Install uv
22
+ uses: astral-sh/setup-uv@v6
23
+ with:
24
+ # 3.10 matches [tool.mypy] python_version and installs tomli
25
+ # (a conditional dependency) so mypy can resolve the tomllib fallback.
26
+ python-version: "3.10"
27
+ enable-cache: true
28
+ - name: Install dependencies
29
+ run: uv sync --extra dev --extra linting --extra typing-mypy
30
+ - name: Ruff (lint)
31
+ run: uv run ruff check src/ tests/
32
+ - name: Ruff (format)
33
+ run: uv run ruff format --check src/ tests/
34
+ - name: Mypy
35
+ run: uv run mypy src/pycmdcheck
36
+
37
+ test:
38
+ name: Test (py${{ matrix.python-version }})
39
+ runs-on: ubuntu-latest
40
+ strategy:
41
+ fail-fast: false
42
+ matrix:
43
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
44
+ steps:
45
+ - uses: actions/checkout@v4
46
+ - name: Install uv
47
+ uses: astral-sh/setup-uv@v6
48
+ with:
49
+ python-version: ${{ matrix.python-version }}
50
+ enable-cache: true
51
+ - name: Install dependencies
52
+ run: uv sync --extra dev
53
+ - name: Run tests
54
+ run: uv run pytest -q
@@ -0,0 +1,52 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ build:
13
+ name: Test & build distributions
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - name: Install uv
18
+ uses: astral-sh/setup-uv@v6
19
+ with:
20
+ python-version: "3.12"
21
+ enable-cache: true
22
+ - name: Install dependencies
23
+ run: uv sync --extra dev
24
+ - name: Run tests (gate the release on green)
25
+ run: uv run pytest -q
26
+ - name: Build sdist and wheel
27
+ run: uv build
28
+ - name: Upload distributions
29
+ uses: actions/upload-artifact@v4
30
+ with:
31
+ name: dist
32
+ path: dist/
33
+
34
+ publish:
35
+ name: Publish to PyPI
36
+ needs: build
37
+ runs-on: ubuntu-latest
38
+ # The environment can be configured on GitHub to require manual approval
39
+ # before a release is published.
40
+ environment:
41
+ name: pypi
42
+ url: https://pypi.org/p/pycmdcheck
43
+ permissions:
44
+ id-token: write # OIDC: required for PyPI Trusted Publishing (no token)
45
+ steps:
46
+ - name: Download distributions
47
+ uses: actions/download-artifact@v4
48
+ with:
49
+ name: dist
50
+ path: dist/
51
+ - name: Publish to PyPI
52
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,22 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ *.egg
8
+
9
+ # Virtual environments
10
+ .venv/
11
+ venv/
12
+
13
+ # OS
14
+ .DS_Store
15
+
16
+ # IDE
17
+ .idea/
18
+ .vscode/
19
+ *.swp
20
+
21
+ # Plans (keep locally, don't track)
22
+ docs/plans/
@@ -0,0 +1,78 @@
1
+ # Changelog
2
+
3
+ All notable changes to pycmdcheck will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ### Added
11
+
12
+ - 7 additional built-in checks (bringing the total to **20**):
13
+ - `community` - Checks for CONTRIBUTING and CODE_OF_CONDUCT files
14
+ - `ci` - Checks for CI configuration
15
+ - `changelog` - Checks for a CHANGELOG/NEWS/HISTORY file
16
+ - `citation` - Checks for a CITATION file
17
+ - `python_versions` - Checks `requires-python` excludes EOL Python versions
18
+ - `urls` - Validates project URLs are reachable
19
+ - `doctests` - Runs doctests via `pytest --doctest-modules`
20
+ - Profile system with built-in profiles `minimal`, `triage`, `default`,
21
+ `pyopensci`, and `strict`, plus `--profile` and `--list-profiles` CLI options.
22
+ The new `triage` profile runs only the static, no-install/no-network checks —
23
+ intended for CI triage of arbitrary (un-installed) repositories.
24
+ - Support for legacy Poetry projects (`[tool.poetry]` with no PEP 621
25
+ `[project]` table) across the metadata, version, python_versions, and
26
+ dependencies checks.
27
+ - `structure` now recognizes PEP 420 implicit namespace packages, single-file
28
+ modules, and native-extension build backends (maturin/scikit-build/setuptools-rust).
29
+ - GitHub Actions workflows: CI (lint + type-check + pytest matrix on 3.10–3.13)
30
+ and a tag-triggered PyPI release using Trusted Publishing (OIDC).
31
+ - `packaging` added as a runtime dependency (accurate PEP 440 version handling).
32
+
33
+ ### Changed
34
+
35
+ - The `formatting` check now defaults to `tool = "auto"`: it detects ruff vs
36
+ black from `[tool.ruff]`/`ruff.toml`/`[tool.black]` and is **skipped** when no
37
+ formatter is configured (previously hard-coded to ruff).
38
+ - Many checks were made more lenient to eliminate false positives on legitimate
39
+ real-world packages: PEP 621 `dynamic` metadata fields, dual-license/suffixed
40
+ license filenames, RST/plain-text README section detection, community files in
41
+ `.github/`/`docs/`, single-module/re-exported `__version__`, dependency extras,
42
+ `pytest` "no tests collected" (exit 5) in doctests, and ruff/mypy
43
+ tool/config-error handling.
44
+
45
+ ### Fixed
46
+
47
+ - `metadata` no longer reports a false ERROR for packages whose version is
48
+ supplied dynamically (PEP 621 `dynamic = ["version"]`, e.g. setuptools_scm /
49
+ hatch-vcs / uv-dynamic-versioning) or declared under legacy `[tool.poetry]`.
50
+
51
+ ## [0.1.0] - 2024-12-31
52
+
53
+ ### Added
54
+
55
+ - Initial release of pycmdcheck
56
+ - Core `check()` function for programmatic package checking
57
+ - CLI with `pycmdcheck` command
58
+ - 13 built-in checks:
59
+ - `build` - Verifies package builds (wheel/sdist)
60
+ - `dependencies` - Audits declared vs. actual imports
61
+ - `docs` - Checks for README and docstrings
62
+ - `formatting` - Checks code formatting (ruff format/black)
63
+ - `imports` - Validates imports
64
+ - `license` - Checks for LICENSE file
65
+ - `linting` - Runs ruff/flake8/pylint
66
+ - `metadata` - Validates pyproject.toml
67
+ - `py_typed` - Checks for PEP 561 py.typed marker
68
+ - `structure` - Validates package structure
69
+ - `tests` - Runs pytest/unittest
70
+ - `typing` - Runs mypy/pyright
71
+ - `version` - Verifies version consistency
72
+ - Configuration via `[tool.pycmdcheck]` in pyproject.toml
73
+ - Plugin system via entry points for custom checks
74
+ - Parallel check execution
75
+ - Rich terminal output with colors and tables
76
+ - JSON output format for CI integration
77
+ - `CheckResult`, `CheckStatus`, and `Report` classes
78
+ - `BaseCheck` abstract class for custom check development
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 pycmdcheck contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,346 @@
1
+ Metadata-Version: 2.4
2
+ Name: pycmdcheck
3
+ Version: 0.1.0
4
+ Summary: Python package quality checker inspired by R CMD check
5
+ Project-URL: Homepage, https://github.com/pycmdcheck/pycmdcheck
6
+ Project-URL: Repository, https://github.com/pycmdcheck/pycmdcheck
7
+ Project-URL: Issues, https://github.com/pycmdcheck/pycmdcheck/issues
8
+ Author: pycmdcheck contributors
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: check,lint,package,quality,test
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Software Development :: Quality Assurance
20
+ Classifier: Topic :: Software Development :: Testing
21
+ Requires-Python: >=3.10
22
+ Requires-Dist: click>=8.0
23
+ Requires-Dist: packaging>=21.0
24
+ Requires-Dist: rich>=13.0
25
+ Requires-Dist: tomli>=2.0; python_version < '3.11'
26
+ Provides-Extra: all
27
+ Requires-Dist: mypy>=1.0; extra == 'all'
28
+ Requires-Dist: pyright>=1.1; extra == 'all'
29
+ Requires-Dist: ruff>=0.1; extra == 'all'
30
+ Provides-Extra: dev
31
+ Requires-Dist: pytest-cov>=4.0; extra == 'dev'
32
+ Requires-Dist: pytest>=8.0; extra == 'dev'
33
+ Provides-Extra: docs
34
+ Requires-Dist: quartodoc>=0.7; extra == 'docs'
35
+ Provides-Extra: linting
36
+ Requires-Dist: ruff>=0.1; extra == 'linting'
37
+ Provides-Extra: typing-mypy
38
+ Requires-Dist: mypy>=1.0; extra == 'typing-mypy'
39
+ Provides-Extra: typing-pyright
40
+ Requires-Dist: pyright>=1.1; extra == 'typing-pyright'
41
+ Description-Content-Type: text/markdown
42
+
43
+ # pycmdcheck
44
+
45
+ A Python package quality checker inspired by R's `R CMD check`.
46
+
47
+ ## Installation
48
+
49
+ > **Note:** pycmdcheck is not yet published to PyPI. Install it directly from
50
+ > GitHub for now. Once it is released, `pip install pycmdcheck` will work too.
51
+
52
+ ```bash
53
+ pip install git+https://github.com/coatless-py-pkg/pycmdcheck
54
+ ```
55
+
56
+ Or with uv:
57
+
58
+ ```bash
59
+ uv pip install git+https://github.com/coatless-py-pkg/pycmdcheck
60
+ ```
61
+
62
+ To pin a specific branch, tag, or commit, append `@<ref>` to the URL — for
63
+ example `git+https://github.com/coatless-py-pkg/pycmdcheck@main`.
64
+
65
+ ### Optional dependencies
66
+
67
+ pycmdcheck can use external tools for linting and type checking. Request the
68
+ matching extra in the install command. For a GitHub install, the extra goes in
69
+ the PEP 508 `pycmdcheck[extra] @ <url>` form:
70
+
71
+ ```bash
72
+ # For mypy type checking
73
+ pip install "pycmdcheck[typing-mypy] @ git+https://github.com/coatless-py-pkg/pycmdcheck"
74
+
75
+ # For pyright type checking
76
+ pip install "pycmdcheck[typing-pyright] @ git+https://github.com/coatless-py-pkg/pycmdcheck"
77
+
78
+ # For ruff linting
79
+ pip install "pycmdcheck[linting] @ git+https://github.com/coatless-py-pkg/pycmdcheck"
80
+
81
+ # All optional dependencies
82
+ pip install "pycmdcheck[all] @ git+https://github.com/coatless-py-pkg/pycmdcheck"
83
+ ```
84
+
85
+ ## Usage
86
+
87
+ ### Command Line
88
+
89
+ ```bash
90
+ # Check current directory
91
+ pycmdcheck
92
+
93
+ # Check a specific directory
94
+ pycmdcheck /path/to/package
95
+
96
+ # Run only specific checks
97
+ pycmdcheck -c metadata -c tests
98
+
99
+ # Skip specific checks
100
+ pycmdcheck -s typing -s linting
101
+
102
+ # Fail on warnings too
103
+ pycmdcheck --fail-on error --fail-on warning
104
+
105
+ # List available checks
106
+ pycmdcheck --list
107
+
108
+ # JSON output
109
+ pycmdcheck --json
110
+
111
+ # Stop after first error (fail fast)
112
+ pycmdcheck -x
113
+ pycmdcheck --fail-fast
114
+
115
+ # Verbose output with details
116
+ pycmdcheck -v
117
+
118
+ # Use a check profile
119
+ pycmdcheck --profile pyopensci
120
+ pycmdcheck --profile minimal
121
+ pycmdcheck --profile strict
122
+
123
+ # List available profiles
124
+ pycmdcheck --list-profiles
125
+ ```
126
+
127
+ ### With uv
128
+
129
+ ```bash
130
+ uv run pycmdcheck
131
+ uv run pycmdcheck -c tests -c linting
132
+ ```
133
+
134
+ ### Python API
135
+
136
+ ```python
137
+ from pycmdcheck import check, CheckStatus
138
+
139
+ # Check current directory
140
+ report = check(".")
141
+
142
+ # Check specific directory
143
+ report = check("/path/to/package")
144
+
145
+ # Run only specific checks
146
+ report = check(".", checks=["metadata", "tests"])
147
+
148
+ # Skip specific checks
149
+ report = check(".", skip=["linting", "typing"])
150
+
151
+ # Check results
152
+ if report.passed:
153
+ print("All checks passed!")
154
+ else:
155
+ for result in report.results:
156
+ print(f"{result.status}: {result.name} - {result.message}")
157
+
158
+ # Get counts by status
159
+ counts = report.count_by_status()
160
+ print(f"OK: {counts[CheckStatus.OK]}, Errors: {counts[CheckStatus.ERROR]}")
161
+
162
+ # JSON output
163
+ import json
164
+ print(json.dumps(report.to_dict(), indent=2))
165
+ ```
166
+
167
+ ## Built-in Checks
168
+
169
+ ### Core checks
170
+
171
+ | Check | Description |
172
+ |-------|-------------|
173
+ | `build` | Verifies the package builds successfully (wheel/sdist) |
174
+ | `dependencies` | Audits declared vs. actual imports, warns on unbounded versions |
175
+ | `docs` | Checks for README (content sections, word count) and docstrings |
176
+ | `formatting` | Checks code formatting (ruff format/black) |
177
+ | `imports` | Validates all imports can be resolved |
178
+ | `license` | Checks for LICENSE file and OSI-approved license validation |
179
+ | `linting` | Runs ruff, flake8, or pylint |
180
+ | `metadata` | Validates pyproject.toml fields (required, recommended, extended) |
181
+ | `py_typed` | Checks for PEP 561 py.typed marker |
182
+ | `structure` | Checks package directory structure (src or flat layout) |
183
+ | `tests` | Runs pytest or unittest and reports results |
184
+ | `typing` | Runs mypy or pyright type checker |
185
+ | `version` | Verifies version consistency between pyproject.toml and code |
186
+
187
+ ### pyOpenSci / community checks
188
+
189
+ | Check | Description |
190
+ |-------|-------------|
191
+ | `changelog` | Checks for CHANGELOG, NEWS, or HISTORY file |
192
+ | `ci` | Checks for CI configuration (GitHub Actions, CircleCI, etc.) |
193
+ | `citation` | Checks for CITATION.cff or CITATION.bib file |
194
+ | `community` | Checks for CONTRIBUTING.md and CODE_OF_CONDUCT.md |
195
+ | `doctests` | Runs doctests via pytest --doctest-modules |
196
+ | `python_versions` | Checks requires-python excludes EOL Python versions |
197
+ | `urls` | Validates project URLs in metadata are reachable |
198
+
199
+ ## Profiles
200
+
201
+ Profiles bundle checks for common use cases:
202
+
203
+ | Profile | Description | Checks |
204
+ |---------|-------------|--------|
205
+ | `minimal` | Quick sanity checks | metadata, structure, license |
206
+ | `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) |
207
+ | `default` | Standard quality checks | All 13 core checks |
208
+ | `pyopensci` | pyOpenSci onboarding | All 20 checks with stricter docs settings |
209
+ | `strict` | Maximum strictness | All 20 checks with strict typing |
210
+
211
+ ```bash
212
+ # Run pyOpenSci onboarding checks
213
+ pycmdcheck --profile pyopensci
214
+
215
+ # Static-only triage (safe to run on an un-installed repo in CI)
216
+ pycmdcheck --profile triage
217
+
218
+ # Quick check before committing
219
+ pycmdcheck --profile minimal
220
+ ```
221
+
222
+ Profiles can be combined with `--skip` to exclude specific checks:
223
+
224
+ ```bash
225
+ pycmdcheck --profile pyopensci -s urls -s doctests
226
+ ```
227
+
228
+ ## Configuration
229
+
230
+ Configure pycmdcheck in your `pyproject.toml`:
231
+
232
+ ```toml
233
+ [tool.pycmdcheck]
234
+ # Fail on these statuses (default: ["error"])
235
+ fail_on = ["error", "warning"]
236
+
237
+ [tool.pycmdcheck.checks]
238
+ # Enable/disable checks with boolean
239
+ metadata = true
240
+ structure = true
241
+ imports = true
242
+ license = true
243
+
244
+ # Configure checks with options
245
+ tests = { enabled = true, runner = "pytest" }
246
+ linting = { enabled = true, tool = "ruff" }
247
+ typing = { enabled = true, tool = "mypy", strict = false }
248
+ # formatting "tool" defaults to "auto": detects ruff vs black from
249
+ # [tool.ruff]/ruff.toml or [tool.black]; skips if neither is configured.
250
+ formatting = { enabled = true, tool = "auto" }
251
+ docs = { enabled = true, require_readme = true, check_docstrings = false, check_readme_sections = false }
252
+
253
+ # New checks (disabled by default in "default" profile)
254
+ community = true
255
+ ci = true
256
+ changelog = true
257
+ citation = true
258
+ python_versions = true
259
+ urls = { enabled = true, timeout = 10 }
260
+ doctests = true
261
+ ```
262
+
263
+ ### Linting tools
264
+
265
+ Supported linting tools:
266
+ - `ruff` (default, recommended)
267
+ - `flake8`
268
+ - `pylint`
269
+
270
+ ```toml
271
+ [tool.pycmdcheck.checks.linting]
272
+ tool = "ruff"
273
+ args = ["--select=E,F,W"]
274
+ ```
275
+
276
+ ### Build options
277
+
278
+ ```toml
279
+ [tool.pycmdcheck.checks.build]
280
+ timeout = 300 # seconds (default: 120)
281
+ args = ["--wheel"]
282
+ ```
283
+
284
+ ### Type checking tools
285
+
286
+ Supported type checkers:
287
+ - `mypy` (default)
288
+ - `pyright`
289
+
290
+ ```toml
291
+ [tool.pycmdcheck.checks.typing]
292
+ tool = "pyright"
293
+ strict = true
294
+ ```
295
+
296
+ ## Custom Checks
297
+
298
+ Create custom checks by implementing the `Check` protocol:
299
+
300
+ ```python
301
+ from pathlib import Path
302
+ from typing import Any
303
+ from pycmdcheck.checks.base import BaseCheck
304
+ from pycmdcheck.results import CheckResult, CheckStatus
305
+
306
+ class MyCustomCheck(BaseCheck):
307
+ name = "my_custom_check"
308
+ description = "My custom quality check"
309
+
310
+ def run(self, package_path: Path, config: dict[str, Any]) -> CheckResult:
311
+ # Your check logic here
312
+ issues = [] # ... populate with any issues found
313
+ if not issues:
314
+ return CheckResult(
315
+ name=self.name,
316
+ status=CheckStatus.OK,
317
+ message="All good!",
318
+ )
319
+ return CheckResult(
320
+ name=self.name,
321
+ status=CheckStatus.ERROR,
322
+ message="Something is wrong",
323
+ details=issues,
324
+ )
325
+ ```
326
+
327
+ Register your check via entry points in `pyproject.toml`:
328
+
329
+ ```toml
330
+ [project.entry-points."pycmdcheck.checks"]
331
+ my_custom_check = "my_package.checks:MyCustomCheck"
332
+ ```
333
+
334
+ ## Check Statuses
335
+
336
+ | Status | Symbol | Description |
337
+ |--------|--------|-------------|
338
+ | OK | ✓ | Check passed |
339
+ | NOTE | ℹ | Minor observation, not a problem |
340
+ | WARNING | ⚠ | Potential issue, should be reviewed |
341
+ | ERROR | ✗ | Check failed |
342
+ | SKIPPED | ○ | Check was skipped (e.g., tool not installed) |
343
+
344
+ ## License
345
+
346
+ MIT