preen 0.7.0__tar.gz → 0.7.2__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.
- {preen-0.7.0 → preen-0.7.2}/PKG-INFO +1 -1
- {preen-0.7.0 → preen-0.7.2}/pyproject.toml +4 -2
- {preen-0.7.0 → preen-0.7.2}/pyproject.toml.orig +5 -2
- {preen-0.7.0 → preen-0.7.2}/src/preen/adopt.py +19 -3
- {preen-0.7.0 → preen-0.7.2}/src/preen/checks/deptree.py +31 -1
- {preen-0.7.0 → preen-0.7.2}/src/preen/checks/metadata.py +3 -2
- {preen-0.7.0 → preen-0.7.2}/src/preen/commands/release.py +2 -2
- {preen-0.7.0 → preen-0.7.2}/LICENSE +0 -0
- {preen-0.7.0 → preen-0.7.2}/README.md +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/__init__.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/__main__.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/checks/__init__.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/checks/audit.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/checks/base.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/checks/changelog.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/checks/ci_matrix.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/checks/citation.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/checks/codespell.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/checks/depgroups.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/checks/deps.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/checks/dropped_args.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/checks/duplicates.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/checks/examples.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/checks/files.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/checks/license.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/checks/links.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/checks/precommit.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/checks/pydoclint.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/checks/pyright.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/checks/pytest_config.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/checks/python_floor.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/checks/ruff.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/checks/runner.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/checks/runtime_assets.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/checks/structure.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/checks/template.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/checks/tests.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/checks/version.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/checks/workflows.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/cli.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/commands/__init__.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/commands/adopt.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/commands/fix.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/commands/new.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/commands/update.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/config.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/interactive.py +0 -0
- {preen-0.7.0 → preen-0.7.2}/src/preen/py.typed +0 -0
|
@@ -5,7 +5,7 @@ readme = "README.md"
|
|
|
5
5
|
license = "MIT"
|
|
6
6
|
license-files = ["LICENSE"]
|
|
7
7
|
requires-python = ">=3.12"
|
|
8
|
-
version = "0.7.
|
|
8
|
+
version = "0.7.2"
|
|
9
9
|
keywords = [
|
|
10
10
|
"packaging",
|
|
11
11
|
"release",
|
|
@@ -51,7 +51,7 @@ Repository = "https://github.com/gojiplus/preen"
|
|
|
51
51
|
preen = "preen.cli:run"
|
|
52
52
|
|
|
53
53
|
[build-system]
|
|
54
|
-
requires = ["uv_build>=0.12.5,<0.
|
|
54
|
+
requires = ["uv_build>=0.12.5,<0.14"]
|
|
55
55
|
build-backend = "uv_build"
|
|
56
56
|
|
|
57
57
|
[tool.uv.build-backend]
|
|
@@ -140,6 +140,8 @@ allow-init-docstring = true
|
|
|
140
140
|
exclude = '\.venv|tests|docs'
|
|
141
141
|
|
|
142
142
|
[tool.codespell]
|
|
143
|
+
builtin = "clear,rare,en-GB_to_en-US"
|
|
144
|
+
ignore-regex = '''\bLICENCE\b|["']licence["']'''
|
|
143
145
|
ignore-words-list = "leary,sav"
|
|
144
146
|
|
|
145
147
|
[tool.pytest.ini_options]
|
|
@@ -6,7 +6,7 @@ license = "MIT"
|
|
|
6
6
|
license-files = ["LICENSE"]
|
|
7
7
|
authors = [{ name = "Gaurav Sood", email = "contact@gsood.com" }]
|
|
8
8
|
requires-python = ">=3.12"
|
|
9
|
-
version = "0.7.
|
|
9
|
+
version = "0.7.2"
|
|
10
10
|
keywords = ["packaging", "release", "conformance", "copier", "cli", "python"]
|
|
11
11
|
classifiers = [
|
|
12
12
|
"Intended Audience :: Developers",
|
|
@@ -52,7 +52,7 @@ Repository = "https://github.com/gojiplus/preen"
|
|
|
52
52
|
preen = "preen.cli:run"
|
|
53
53
|
|
|
54
54
|
[build-system]
|
|
55
|
-
requires = ["uv_build>=0.12.5,<0.
|
|
55
|
+
requires = ["uv_build>=0.12.5,<0.14"]
|
|
56
56
|
build-backend = "uv_build"
|
|
57
57
|
|
|
58
58
|
[tool.uv.build-backend]
|
|
@@ -114,6 +114,9 @@ allow-init-docstring = true
|
|
|
114
114
|
exclude = '\.venv|tests|docs'
|
|
115
115
|
|
|
116
116
|
[tool.codespell]
|
|
117
|
+
builtin = "clear,rare,en-GB_to_en-US"
|
|
118
|
+
# Preserve supported license filenames.
|
|
119
|
+
ignore-regex = '''\bLICENCE\b|["']licence["']''' # codespell:ignore licence
|
|
117
120
|
# "Leary" is a surname in the worked example of why auto-fixing spelling in
|
|
118
121
|
# data files is unsafe; "sav" is a serialized-model filename extension checked
|
|
119
122
|
# by runtime-assets.
|
|
@@ -21,7 +21,7 @@ from packaging.version import InvalidVersion, Version
|
|
|
21
21
|
from tomlkit.items import Table
|
|
22
22
|
|
|
23
23
|
CANON_TEMPLATE = "gh:gojiplus/py-canon"
|
|
24
|
-
UV_BUILD_REQUIREMENT = "uv_build>=0.12.5,<0.
|
|
24
|
+
UV_BUILD_REQUIREMENT = "uv_build>=0.12.5,<0.14"
|
|
25
25
|
|
|
26
26
|
# [tool.*] sections merged onto the repo's own (see `_merge_canon`).
|
|
27
27
|
CANON_TOOL_TOML = """
|
|
@@ -60,6 +60,9 @@ check-yield-types = false
|
|
|
60
60
|
check-class-attributes = false
|
|
61
61
|
allow-init-docstring = true
|
|
62
62
|
exclude = '\\.venv|tests|docs'
|
|
63
|
+
|
|
64
|
+
[tool.codespell]
|
|
65
|
+
builtin = "clear,rare,en-GB_to_en-US"
|
|
63
66
|
"""
|
|
64
67
|
|
|
65
68
|
LEGACY_TOOL_SECTIONS = ("black", "isort", "flake8", "mypy")
|
|
@@ -800,7 +803,7 @@ def _merge_canon(
|
|
|
800
803
|
"""Merge a repo's table onto the canon table for the same path, in place.
|
|
801
804
|
|
|
802
805
|
Canon wins on every key it defines, except array settings on the union
|
|
803
|
-
allowlist, where
|
|
806
|
+
allowlist and codespell dictionaries, where extra entries are appended. Keys and
|
|
804
807
|
subtables canon says nothing about — a repo's ``exclude`` list, its
|
|
805
808
|
``flake8-bugbear`` settings, its extra per-file-ignore patterns — are
|
|
806
809
|
preserved verbatim, so adoption never silently drops deliberate config.
|
|
@@ -820,6 +823,19 @@ def _merge_canon(
|
|
|
820
823
|
preserved.append(_describe(sub_path, repo_value))
|
|
821
824
|
continue
|
|
822
825
|
canon_value = canon[key]
|
|
826
|
+
if sub_path == ("codespell", "builtin"):
|
|
827
|
+
entries = str(canon_value).split(",")
|
|
828
|
+
selected = (
|
|
829
|
+
repo_value.split(",") if isinstance(repo_value, str) else repo_value
|
|
830
|
+
)
|
|
831
|
+
extra = sorted({str(entry).strip() for entry in selected} - set(entries))
|
|
832
|
+
extra = [entry for entry in extra if entry]
|
|
833
|
+
if extra:
|
|
834
|
+
canon[key] = ",".join([*entries, *extra])
|
|
835
|
+
preserved.append(
|
|
836
|
+
f"{_describe(sub_path, repo_value)} entries: " + ", ".join(extra)
|
|
837
|
+
)
|
|
838
|
+
continue
|
|
823
839
|
if isinstance(canon_value, dict) and isinstance(repo_value, dict):
|
|
824
840
|
_merge_canon(canon_value, repo_value, sub_path, preserved)
|
|
825
841
|
elif (
|
|
@@ -866,7 +882,7 @@ def rewrite_pyproject(
|
|
|
866
882
|
preserved: list[str] = []
|
|
867
883
|
|
|
868
884
|
tool = _ensure_table(doc, "tool")
|
|
869
|
-
for section in ("ruff", "pyright", "pydoclint"):
|
|
885
|
+
for section in ("ruff", "pyright", "pydoclint", "codespell"):
|
|
870
886
|
canon_section = canon["tool"][section] # type: ignore[index]
|
|
871
887
|
if section in tool:
|
|
872
888
|
changes.append(f"merged canon into [tool.{section}]")
|
|
@@ -1,11 +1,41 @@
|
|
|
1
1
|
"""Circular dependency check using a simple Python implementation."""
|
|
2
2
|
|
|
3
3
|
import ast
|
|
4
|
+
from collections.abc import Iterable, Iterator
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
|
|
6
7
|
from .base import Check, CheckResult, Impact, Issue, Severity
|
|
7
8
|
|
|
8
9
|
|
|
10
|
+
def _is_type_checking(test: ast.expr) -> bool:
|
|
11
|
+
"""Return whether ``test`` is ``TYPE_CHECKING`` or ``<module>.TYPE_CHECKING``."""
|
|
12
|
+
if isinstance(test, ast.Name):
|
|
13
|
+
return test.id == "TYPE_CHECKING"
|
|
14
|
+
return isinstance(test, ast.Attribute) and test.attr == "TYPE_CHECKING"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _load_time_nodes(node: ast.AST) -> Iterator[ast.AST]:
|
|
18
|
+
"""Yield the nodes under ``node`` that execute when the module is imported.
|
|
19
|
+
|
|
20
|
+
A circular import only fails through imports that run while a module loads.
|
|
21
|
+
Deferring an import into a function body, or guarding it with
|
|
22
|
+
``if TYPE_CHECKING:``, is the standard way to break a cycle, so neither is
|
|
23
|
+
an edge. Class bodies and the ``else`` branch of a TYPE_CHECKING test do run.
|
|
24
|
+
"""
|
|
25
|
+
yield from _scan(ast.iter_child_nodes(node))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _scan(nodes: Iterable[ast.AST]) -> Iterator[ast.AST]:
|
|
29
|
+
for node in nodes:
|
|
30
|
+
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.Lambda)):
|
|
31
|
+
continue
|
|
32
|
+
if isinstance(node, ast.If) and _is_type_checking(node.test):
|
|
33
|
+
yield from _scan(node.orelse)
|
|
34
|
+
continue
|
|
35
|
+
yield node
|
|
36
|
+
yield from _scan(ast.iter_child_nodes(node))
|
|
37
|
+
|
|
38
|
+
|
|
9
39
|
class DeptreeCheck(Check):
|
|
10
40
|
"""Check for circular dependencies in Python code."""
|
|
11
41
|
|
|
@@ -36,7 +66,7 @@ class DeptreeCheck(Check):
|
|
|
36
66
|
|
|
37
67
|
tree = ast.parse(content, filename=str(file_path))
|
|
38
68
|
|
|
39
|
-
for node in
|
|
69
|
+
for node in _load_time_nodes(tree):
|
|
40
70
|
if isinstance(node, ast.Import):
|
|
41
71
|
for alias in node.names:
|
|
42
72
|
imports.add(alias.name)
|
|
@@ -53,7 +53,7 @@ def _compatible_requirement(declared: Any, expected: str) -> bool:
|
|
|
53
53
|
if len(found.specifier) != 2 or set(bounds) != {">=", "<"}:
|
|
54
54
|
return False
|
|
55
55
|
minimum, ceiling = Version(bounds[">="]), Version(bounds["<"])
|
|
56
|
-
return standard[">="] <= minimum < ceiling
|
|
56
|
+
return standard[">="] <= minimum < ceiling <= standard["<"]
|
|
57
57
|
|
|
58
58
|
|
|
59
59
|
class MetadataCheck(Check):
|
|
@@ -166,7 +166,8 @@ class MetadataCheck(Check):
|
|
|
166
166
|
impact=Impact.IMPORTANT,
|
|
167
167
|
explanation=(
|
|
168
168
|
"The minimum may increase within the approved compatibility "
|
|
169
|
-
"range;
|
|
169
|
+
"range; the exclusive upper bound must not exceed the approved "
|
|
170
|
+
"ceiling. Run preen adopt "
|
|
170
171
|
"--release-migration to migrate an older backend."
|
|
171
172
|
),
|
|
172
173
|
)
|
|
@@ -6,7 +6,7 @@ publishing, GitHub Release). This command runs the checks, verifies that the tag
|
|
|
6
6
|
and project metadata agree, asks for informed consent, then tags and pushes.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
-
import datetime
|
|
9
|
+
import datetime as dt
|
|
10
10
|
import json
|
|
11
11
|
import re
|
|
12
12
|
import subprocess
|
|
@@ -498,7 +498,7 @@ def release_package(
|
|
|
498
498
|
)
|
|
499
499
|
# Local date, explicitly zoned: a changelog heading should carry the date
|
|
500
500
|
# the releaser sees, not UTC's.
|
|
501
|
-
today =
|
|
501
|
+
today = dt.datetime.now().astimezone().date().isoformat()
|
|
502
502
|
rename_unreleased = False
|
|
503
503
|
if not has_version_entry(changelog_text, version):
|
|
504
504
|
unreleased = unreleased_section_text(changelog_text)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|