fix(check): SPDX license string; E501 off in tests (device-types pilot)
The pilot's build warned that setuptools refuses a license table and License classifiers from 2027-02-18 (PEP 639); check now reports both and the template builds with setuptools>=77. Recorded device output in tests keeps its lines, so the template turns E501 off under tests/.
This commit is contained in:
1 parent
db6b2e4ea7
commit
0b21f62553
7 files changed
+45
-8
No files matched your search
@@ -5,6 +5,13 @@ The format follows [Keep a Changelog](https://keepachangelog.com/), versions fol
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- `check` reports a license given as a TOML table or as a `License ::` classifier:
|
||||||
|
setuptools refuses both from 2027-02-18 (PEP 639). The template builds with
|
||||||
|
setuptools>=77.
|
||||||
|
- E501 is off under `tests/`: recorded device output keeps its lines (found in the
|
||||||
|
device-types pilot).
|
||||||
|
|
||||||
## [0.1.0] - 2026-10-22
|
## [0.1.0] - 2026-10-22
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
+5
-2
@@ -101,7 +101,9 @@ A pull request without tests for what it adds or changes is not merged.
|
|||||||
|
|
||||||
## 5. Clean code
|
## 5. Clean code
|
||||||
|
|
||||||
- **Format and lint:** `ruff format` and `ruff check`, line length 100.
|
- **Format and lint:** `ruff format` and `ruff check` with `E, F, I, UP, B`, line length
|
||||||
|
100. Only under `tests/` may a line be longer (E501 off there): recorded device output
|
||||||
|
keeps its lines.
|
||||||
- **Types:** every public function is annotated; `mypy --strict` (§6 for the old code).
|
- **Types:** every public function is annotated; `mypy --strict` (§6 for the old code).
|
||||||
- **Small functions:** under 30 lines recommended, 50 at most; cyclomatic
|
- **Small functions:** under 30 lines recommended, 50 at most; cyclomatic
|
||||||
complexity 10 at most. One function does one thing.
|
complexity 10 at most. One function does one thing.
|
||||||
@@ -165,7 +167,8 @@ release tags:
|
|||||||
someone else gets a descriptive one instead. The `napalm.drivers` entry point
|
someone else gets a descriptive one instead. The `napalm.drivers` entry point
|
||||||
names do not change with the package name.
|
names do not change with the package name.
|
||||||
- **`pyproject.toml`** declares `name`, `version`, `description`, `readme`,
|
- **`pyproject.toml`** declares `name`, `version`, `description`, `readme`,
|
||||||
`license = "Apache-2.0"`, `requires-python = ">=3.10"`, the dependencies, a
|
`license = "Apache-2.0"` (an SPDX string, PEP 639; no table, no `License ::`
|
||||||
|
classifier, `setuptools>=77`), `requires-python = ">=3.10"`, the dependencies, a
|
||||||
`dev` extra, `[project.urls]` with `Repository` (and `Issues`) on
|
`dev` extra, `[project.urls]` with `Repository` (and `Issues`) on
|
||||||
`https://git.netork.io/NAPALM/…`, and the `napalm.drivers` entry points.
|
`https://git.netork.io/NAPALM/…`, and the `napalm.drivers` entry points.
|
||||||
- **Dependencies are ranges with true floors.** A driver is a library: it never pins
|
- **Dependencies are ranges with true floors.** A driver is a library: it never pins
|
||||||
|
|||||||
@@ -77,6 +77,11 @@ def _metadata(repo: Repo) -> Iterator[Finding]:
|
|||||||
for field in REQUIRED_FIELDS:
|
for field in REQUIRED_FIELDS:
|
||||||
if not repo.project.get(field):
|
if not repo.project.get(field):
|
||||||
yield Finding("metadata", f"[project] has no {field}")
|
yield Finding("metadata", f"[project] has no {field}")
|
||||||
|
# PEP 639: setuptools refuses the table form and license classifiers from 2027-02-18.
|
||||||
|
if isinstance(repo.project.get("license"), dict):
|
||||||
|
yield Finding("metadata", 'license must be an SPDX string: license = "Apache-2.0"')
|
||||||
|
if any(c.startswith("License ::") for c in repo.project.get("classifiers", [])):
|
||||||
|
yield Finding("metadata", "drop the License :: classifier; the SPDX license says it")
|
||||||
|
|
||||||
|
|
||||||
def _python(repo: Repo) -> Iterator[Finding]:
|
def _python(repo: Repo) -> Iterator[Finding]:
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
[build-system]
|
[build-system]
|
||||||
requires = ["setuptools>=68"]
|
requires = ["setuptools>=77"]
|
||||||
build-backend = "setuptools.build_meta"
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[build-system]
|
[build-system]
|
||||||
requires = ["setuptools>=68"]
|
requires = ["setuptools>=77"]
|
||||||
build-backend = "setuptools.build_meta"
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
@@ -43,6 +43,10 @@ target-version = "py310"
|
|||||||
[tool.ruff.lint]
|
[tool.ruff.lint]
|
||||||
select = ["E", "F", "I", "UP", "B"]
|
select = ["E", "F", "I", "UP", "B"]
|
||||||
|
|
||||||
|
[tool.ruff.lint.per-file-ignores]
|
||||||
|
# Recorded device output keeps its lines.
|
||||||
|
"tests/**" = ["E501"]
|
||||||
|
|
||||||
[tool.mypy]
|
[tool.mypy]
|
||||||
python_version = "3.10"
|
python_version = "3.10"
|
||||||
strict = true
|
strict = true
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,7 @@ import pytest
|
|||||||
|
|
||||||
PYPROJECT = """\
|
PYPROJECT = """\
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["setuptools>=68"]
|
requires = ["setuptools>=77"]
|
||||||
build-backend = "setuptools.build_meta"
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
|
|||||||
+21
-3
@@ -51,10 +51,28 @@ def test_a_missing_project_field_is_reported(make_repo: RepoFactory, field: str)
|
|||||||
assert field in findings[0].message
|
assert field in findings[0].message
|
||||||
|
|
||||||
|
|
||||||
def test_a_license_table_is_accepted(make_repo: RepoFactory) -> None:
|
@pytest.mark.parametrize(
|
||||||
pyproject = PYPROJECT.replace('license = "Apache-2.0"', 'license = { text = "Apache-2.0" }')
|
"license_line",
|
||||||
|
['license = { text = "Apache-2.0" }', 'license = { file = "LICENSE" }'],
|
||||||
|
)
|
||||||
|
def test_a_license_table_is_reported(make_repo: RepoFactory, license_line: str) -> None:
|
||||||
|
"""setuptools refuses the table form from 2027-02-18 (PEP 639)."""
|
||||||
|
pyproject = PYPROJECT.replace('license = "Apache-2.0"', license_line)
|
||||||
|
|
||||||
assert check_repo(make_repo({"pyproject.toml": pyproject})) == []
|
findings = check_repo(make_repo({"pyproject.toml": pyproject}))
|
||||||
|
|
||||||
|
assert rules(findings) == {"metadata"}
|
||||||
|
assert "SPDX" in findings[0].message
|
||||||
|
|
||||||
|
|
||||||
|
def test_a_license_classifier_is_reported(make_repo: RepoFactory) -> None:
|
||||||
|
pyproject = PYPROJECT.replace(
|
||||||
|
'requires-python = ">=3.10"\n',
|
||||||
|
'requires-python = ">=3.10"\n'
|
||||||
|
'classifiers = ["License :: OSI Approved :: Apache Software License"]\n',
|
||||||
|
)
|
||||||
|
|
||||||
|
assert rules(check_repo(make_repo({"pyproject.toml": pyproject}))) == {"metadata"}
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("spec", [">=3.8", ">=3.9", ""])
|
@pytest.mark.parametrize("spec", [">=3.8", ">=3.9", ""])
|
||||||
|
|||||||
Reference in new issue
Block a user