From afb69150fa70d66f354de42a3e9c69ed75b5f29c Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Fri, 4 Oct 2024 17:25:51 -0400 Subject: [PATCH] chore: add more checks and formatters Signed-off-by: Henry Schreiner --- .github/workflows/tests.yml | 23 ++++---- .pre-commit-config.yaml | 98 ++++++++++++++++++++++++++------- README.md | 52 +++++++++-------- docs/changelog.md | 2 +- docs/index.md | 1 - pyproject.toml | 5 ++ pyproject_metadata/__init__.py | 6 +- pyproject_metadata/errors.py | 2 +- tests/test_rfc822.py | 2 +- tests/test_standard_metadata.py | 4 +- 10 files changed, 132 insertions(+), 63 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9ce3887..235aee1 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -18,21 +18,21 @@ jobs: fail-fast: false matrix: os: - - 'windows-latest' - - 'ubuntu-latest' + - "windows-latest" + - "ubuntu-latest" python: - - '3.7' - - '3.8' - - '3.9' - - '3.10' - - '3.11' - - '3.12' - - '3.13' + - "3.7" + - "3.8" + - "3.9" + - "3.10" + - "3.11" + - "3.12" + - "3.13" include: - os: macos-13 - python: '3.7' + python: "3.7" - os: macos-14 - python: '3.12' + python: "3.12" steps: - name: Checkout @@ -56,7 +56,6 @@ jobs: env_vars: PYTHON name: ${{ matrix.python }} - # https://github.com/marketplace/actions/alls-green#why required-checks-pass: # This job does nothing and is only used for the branch protection if: always() diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6712811..2888eb6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,24 +1,82 @@ ci: autofix_prs: false - autoupdate_commit_msg: 'pre-commit: bump repositories' + autoupdate_commit_msg: "pre-commit: bump repositories" repos: -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 - hooks: - - id: check-ast - - id: check-builtin-literals - - id: check-docstring-first - - id: check-merge-conflict - - id: check-yaml - - id: check-toml - - id: debug-statements - - id: end-of-file-fixer - - id: trailing-whitespace - -- repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.6.8" - hooks: - - id: ruff - args: ["--fix", "--show-fixes"] - - id: ruff-format + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + - id: check-ast + - id: check-builtin-literals + - id: check-docstring-first + - id: check-merge-conflict + - id: check-yaml + - id: check-toml + - id: debug-statements + - id: end-of-file-fixer + - id: trailing-whitespace + + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: "v0.6.8" + hooks: + - id: ruff + args: ["--fix", "--show-fixes"] + - id: ruff-format + + - repo: https://github.com/pre-commit/pygrep-hooks + rev: v1.10.0 + hooks: + - id: rst-backticks + - id: rst-directive-colons + - id: rst-inline-touching-normal + + - repo: https://github.com/adamchainz/blacken-docs + rev: 1.18.0 + hooks: + - id: blacken-docs + additional_dependencies: [black==24.*] + + - repo: https://github.com/rbubley/mirrors-prettier + rev: "v3.3.3" + hooks: + - id: prettier + types_or: [yaml, markdown, html, css, scss, javascript, json] + args: [--prose-wrap=always] + + - repo: https://github.com/henryiii/check-sdist + rev: "v1.0.0" + hooks: + - id: check-sdist + args: [--inject-junk] + additional_dependencies: + - flit-core + + - repo: https://github.com/codespell-project/codespell + rev: v2.3.0 + hooks: + - id: codespell + exclude: ^(LICENSE$|src/scikit_build_core/resources/find_python|tests/test_skbuild_settings.py$) + + - repo: https://github.com/shellcheck-py/shellcheck-py + rev: v0.10.0.1 + hooks: + - id: shellcheck + + - repo: https://github.com/henryiii/validate-pyproject-schema-store + rev: 2024.09.23 + hooks: + - id: validate-pyproject + + - repo: https://github.com/python-jsonschema/check-jsonschema + rev: 0.29.3 + hooks: + - id: check-dependabot + - id: check-github-workflows + - id: check-readthedocs + - id: check-metaschema + files: \.schema\.json + + - repo: https://github.com/scientific-python/cookie + rev: 2024.08.19 + hooks: + - id: sp-repo-review diff --git a/README.md b/README.md index 9afc3a9..522fff7 100644 --- a/README.md +++ b/README.md @@ -7,31 +7,30 @@ [![Documentation Status][rtd-badge]][rtd-link] [![PyPI version][pypi-version]][pypi-link] - > Dataclass for PEP 621 metadata with support for [core metadata] generation -This project does not implement the parsing of `pyproject.toml` -containing PEP 621 metadata. +This project does not implement the parsing of `pyproject.toml` containing PEP +621 metadata. Instead, given a Python data structure representing PEP 621 metadata (already parsed), it will validate this input and generate a PEP 643-compliant metadata file (e.g. `PKG-INFO`). - ## Usage -After [installing `pyproject-metadata`](https://pypi.org/project/pyproject-metadata/), +After +[installing `pyproject-metadata`](https://pypi.org/project/pyproject-metadata/), you can use it as a library in your scripts and programs: ```python from pyproject_metadata import StandardMetadata -parsed_pyproject = { ... } # you can use parsers like `tomli` to obtain this dict -metadata = StandardMetadata.from_pyproject(parsed_pyproject, allow_extra_keys = False) +parsed_pyproject = {...} # you can use parsers like `tomli` to obtain this dict +metadata = StandardMetadata.from_pyproject(parsed_pyproject, allow_extra_keys=False) print(metadata.entrypoints) # same fields as defined in PEP 621 pkg_info = metadata.as_rfc822() -print(str(pkg_info))) # core metadata +print(str(pkg_info)) # core metadata ``` ## SPDX licenses (METADATA 2.4+) @@ -50,7 +49,6 @@ A backend is also expected to copy entries from `project.licence_files`, which are paths relative to the project directory, into the `dist-info/licenses` folder, preserving the original source structure. - ## Modifying metadata By default, `StandardMetadata` metadata fields are immutable unless a field is @@ -58,16 +56,18 @@ listed in `dynaimc` (not to be confused with `dynamic_metadata`). If you want to modify fields that are not dynamic, you can use the `dataclasses.replace` / `copy.replace` (Python 3.13+) function. - ## Dynamic Metadata (METADATA 2.2+) -Pyproject-metadata supports dynamic metadata. To use it, specify your METADATA fields in `dynamic_metadata`. If you want to convert `pyproject.toml` field names to METADATA field(s), use `pyproject_metadata.pyproject_to_metadata("field-name")`, which will return a frozenset of metadata names that are touched by that field. - +Pyproject-metadata supports dynamic metadata. To use it, specify your METADATA +fields in `dynamic_metadata`. If you want to convert `pyproject.toml` field +names to METADATA field(s), use +`pyproject_metadata.pyproject_to_metadata("field-name")`, which will return a +frozenset of metadata names that are touched by that field. ## Adding extra fields -You can add extra fields to the Message returned by `to_rfc822()`, as long as they are valid metadata entries. - +You can add extra fields to the Message returned by `to_rfc822()`, as long as +they are valid metadata entries. ## Collecting multiple errors @@ -76,7 +76,6 @@ the metadata parse at once, instead of raising an exception on the first one. The exception type will be `pyproject_metadata.errors.ExceptionGroup` (which is just `ExceptionGroup` on Python 3.11+). - ## Validating extra fields By default, a warning (`pyproject_metadata.errors.ExtraKeyWarning`) will be @@ -85,27 +84,35 @@ to either avoid the check (`True`) or hard error (`False`). If you want to detect extra keys, you can get them with `pyproject_metadata.extra_top_level` and `pyproject_metadata.extra_build_sytem`. - ## Validating classifiers -If you want to validate classifiers, then install the `trove_classifiers` library (the canonical source for classifiers), and run: +If you want to validate classifiers, then install the `trove_classifiers` +library (the canonical source for classifiers), and run: ```python import trove_classifiers -metadata_classifieres = {c for c in metadata.classifiers if not c.startswith("Private ::")} +metadata_classifieres = { + c for c in metadata.classifiers if not c.startswith("Private ::") +} invalid_classifiers = set(metadata.classifiers) - trove_classifiers.classifiers # Also the deprecated dict if you want it dep_names = set(metadata.classifiers) & set(trove_classifiers.deprecated_classifiers) -deprecated_classifiers = {k: trove_classifiers.deprecated_classifiers[k] for k in dep_names} +deprecated_classifiers = { + k: trove_classifiers.deprecated_classifiers[k] for k in dep_names +} ``` -If you are writing a build backend, you should not validate classifiers with a `Private ::` prefix; these are only restricted for upload to PyPI (such as `Private :: Do Not Upload`). - -Since classifiers are a moving target, it is probably best for build backends (which may be shipped by third party distributors like Debian or Fedora) to either ignore or have optional classifier validation. +If you are writing a build backend, you should not validate classifiers with a +`Private ::` prefix; these are only restricted for upload to PyPI (such as +`Private :: Do Not Upload`). +Since classifiers are a moving target, it is probably best for build backends +(which may be shipped by third party distributors like Debian or Fedora) to +either ignore or have optional classifier validation. + [core metadata]: https://packaging.python.org/specifications/core-metadata/ [gha-checks-link]: https://github.com/pypa/pyproject-metadata/actions/workflows/checks.yml [gha-checks-badge]: https://github.com/pypa/pyproject-metadata/actions/workflows/checks.yml/badge.svg @@ -119,3 +126,4 @@ Since classifiers are a moving target, it is probably best for build backends (w [pypi-version]: https://badge.fury.io/py/pyproject-metadata.svg [rtd-link]: https://pep621.readthedocs.io/en/latest/?badge=latest [rtd-badge]: https://readthedocs.org/projects/pep621/badge/?version=latest + diff --git a/docs/changelog.md b/docs/changelog.md index b00b9cf..6bac64d 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -11,7 +11,6 @@ Docs: - Better API section - ## 0.9.0 (WIP) This release adds PEP 639 support (METADATA 2.4), refactors the RFC messages, @@ -69,6 +68,7 @@ Internal and CI: - Require 100% coverage Docs: + - Include extra badge in readme - Rework docs, include README and more classes - Changelog is now in markdown diff --git a/docs/index.md b/docs/index.md index 66845a8..637e659 100644 --- a/docs/index.md +++ b/docs/index.md @@ -2,7 +2,6 @@ ``` - ```{toctree} :caption: Contents: :maxdepth: 2 diff --git a/pyproject.toml b/pyproject.toml index 5fb6e1d..2b659a8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -62,6 +62,8 @@ testpaths = ["tests"] [tool.mypy] strict = true +warn_unreachable = false +enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"] [tool.ruff.lint] @@ -103,3 +105,6 @@ html.show_contexts = true report.exclude_also = [ "if typing.TYPE_CHECKING:", ] + +[tool.repo-review] +ignore = ["PC140"] diff --git a/pyproject_metadata/__init__.py b/pyproject_metadata/__init__.py index 905c2a0..39ae6e4 100644 --- a/pyproject_metadata/__init__.py +++ b/pyproject_metadata/__init__.py @@ -242,11 +242,11 @@ class StandardMetadata: dynamic_metadata: list[str] = dataclasses.field(default_factory=list) """ - This is a list of METADATA fields that can change inbetween SDist and wheel. Requires metadata_version 2.2+. + This is a list of METADATA fields that can change in between SDist and wheel. Requires metadata_version 2.2+. """ metadata_version: str | None = None """ - Thi is the target metadata version. If None, it will be computed as a minimum based on the fields set. + This is the target metadata version. If None, it will be computed as a minimum based on the fields set. """ all_errors: bool = False """ @@ -254,7 +254,7 @@ class StandardMetadata: """ _locked_metadata: bool = False """ - Interal flag to prevent setting non-dynamic fields after initialization. + Internal flag to prevent setting non-dynamic fields after initialization. """ def __post_init__(self) -> None: diff --git a/pyproject_metadata/errors.py b/pyproject_metadata/errors.py index 262bd26..4fe90c2 100644 --- a/pyproject_metadata/errors.py +++ b/pyproject_metadata/errors.py @@ -2,7 +2,7 @@ """ This module defines exceptions and error handling utilities. It is the -recommened path to access ``ConfiguraitonError``, ``ConfigurationWarning``, and +recommend path to access ``ConfiguratonError``, ``ConfigurationWarning``, and ``ExceptionGroup``. For backward compatibility, ``ConfigurationError`` is re-exported in the top-level package. """ diff --git a/tests/test_rfc822.py b/tests/test_rfc822.py index cee0ec5..488acec 100644 --- a/tests/test_rfc822.py +++ b/tests/test_rfc822.py @@ -119,7 +119,7 @@ ], ) def test_headers( - items: list[tuple[str, str]], data: str, monkeypatch: pytest.MonkeyPatch + items: list[tuple[str, None | str]], data: str, monkeypatch: pytest.MonkeyPatch ) -> None: message = pyproject_metadata.RFC822Message() smart_message = pyproject_metadata._SmartMessageSetter(message) diff --git a/tests/test_standard_metadata.py b/tests/test_standard_metadata.py index 681f3a4..0c21cdd 100644 --- a/tests/test_standard_metadata.py +++ b/tests/test_standard_metadata.py @@ -749,7 +749,7 @@ def all_errors(request: pytest.FixtureRequest, monkeypatch: pytest.MonkeyPatch) license-files = ['/LICENSE'] """, "'/LICENSE' is an invalid \"project.license-files\" glob: the pattern must match files within the project directory", - id="Aboslute license-files glob", + id="Absolute license-files glob", ), pytest.param( """ @@ -960,7 +960,7 @@ def test_load_with_metadata_version( """, "'License ::' classifiers are deprecated for metadata >= 2.4, use a SPDX license expression for \"project.license\" instead", "2.4", - id="License trove classfiers with metadata 2.4", + id="License trove classifiers with metadata 2.4", ), ], )