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.
Files changed (48) hide show
  1. {preen-0.7.0 → preen-0.7.2}/PKG-INFO +1 -1
  2. {preen-0.7.0 → preen-0.7.2}/pyproject.toml +4 -2
  3. {preen-0.7.0 → preen-0.7.2}/pyproject.toml.orig +5 -2
  4. {preen-0.7.0 → preen-0.7.2}/src/preen/adopt.py +19 -3
  5. {preen-0.7.0 → preen-0.7.2}/src/preen/checks/deptree.py +31 -1
  6. {preen-0.7.0 → preen-0.7.2}/src/preen/checks/metadata.py +3 -2
  7. {preen-0.7.0 → preen-0.7.2}/src/preen/commands/release.py +2 -2
  8. {preen-0.7.0 → preen-0.7.2}/LICENSE +0 -0
  9. {preen-0.7.0 → preen-0.7.2}/README.md +0 -0
  10. {preen-0.7.0 → preen-0.7.2}/src/preen/__init__.py +0 -0
  11. {preen-0.7.0 → preen-0.7.2}/src/preen/__main__.py +0 -0
  12. {preen-0.7.0 → preen-0.7.2}/src/preen/checks/__init__.py +0 -0
  13. {preen-0.7.0 → preen-0.7.2}/src/preen/checks/audit.py +0 -0
  14. {preen-0.7.0 → preen-0.7.2}/src/preen/checks/base.py +0 -0
  15. {preen-0.7.0 → preen-0.7.2}/src/preen/checks/changelog.py +0 -0
  16. {preen-0.7.0 → preen-0.7.2}/src/preen/checks/ci_matrix.py +0 -0
  17. {preen-0.7.0 → preen-0.7.2}/src/preen/checks/citation.py +0 -0
  18. {preen-0.7.0 → preen-0.7.2}/src/preen/checks/codespell.py +0 -0
  19. {preen-0.7.0 → preen-0.7.2}/src/preen/checks/depgroups.py +0 -0
  20. {preen-0.7.0 → preen-0.7.2}/src/preen/checks/deps.py +0 -0
  21. {preen-0.7.0 → preen-0.7.2}/src/preen/checks/dropped_args.py +0 -0
  22. {preen-0.7.0 → preen-0.7.2}/src/preen/checks/duplicates.py +0 -0
  23. {preen-0.7.0 → preen-0.7.2}/src/preen/checks/examples.py +0 -0
  24. {preen-0.7.0 → preen-0.7.2}/src/preen/checks/files.py +0 -0
  25. {preen-0.7.0 → preen-0.7.2}/src/preen/checks/license.py +0 -0
  26. {preen-0.7.0 → preen-0.7.2}/src/preen/checks/links.py +0 -0
  27. {preen-0.7.0 → preen-0.7.2}/src/preen/checks/precommit.py +0 -0
  28. {preen-0.7.0 → preen-0.7.2}/src/preen/checks/pydoclint.py +0 -0
  29. {preen-0.7.0 → preen-0.7.2}/src/preen/checks/pyright.py +0 -0
  30. {preen-0.7.0 → preen-0.7.2}/src/preen/checks/pytest_config.py +0 -0
  31. {preen-0.7.0 → preen-0.7.2}/src/preen/checks/python_floor.py +0 -0
  32. {preen-0.7.0 → preen-0.7.2}/src/preen/checks/ruff.py +0 -0
  33. {preen-0.7.0 → preen-0.7.2}/src/preen/checks/runner.py +0 -0
  34. {preen-0.7.0 → preen-0.7.2}/src/preen/checks/runtime_assets.py +0 -0
  35. {preen-0.7.0 → preen-0.7.2}/src/preen/checks/structure.py +0 -0
  36. {preen-0.7.0 → preen-0.7.2}/src/preen/checks/template.py +0 -0
  37. {preen-0.7.0 → preen-0.7.2}/src/preen/checks/tests.py +0 -0
  38. {preen-0.7.0 → preen-0.7.2}/src/preen/checks/version.py +0 -0
  39. {preen-0.7.0 → preen-0.7.2}/src/preen/checks/workflows.py +0 -0
  40. {preen-0.7.0 → preen-0.7.2}/src/preen/cli.py +0 -0
  41. {preen-0.7.0 → preen-0.7.2}/src/preen/commands/__init__.py +0 -0
  42. {preen-0.7.0 → preen-0.7.2}/src/preen/commands/adopt.py +0 -0
  43. {preen-0.7.0 → preen-0.7.2}/src/preen/commands/fix.py +0 -0
  44. {preen-0.7.0 → preen-0.7.2}/src/preen/commands/new.py +0 -0
  45. {preen-0.7.0 → preen-0.7.2}/src/preen/commands/update.py +0 -0
  46. {preen-0.7.0 → preen-0.7.2}/src/preen/config.py +0 -0
  47. {preen-0.7.0 → preen-0.7.2}/src/preen/interactive.py +0 -0
  48. {preen-0.7.0 → preen-0.7.2}/src/preen/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: preen
3
- Version: 0.7.0
3
+ Version: 0.7.2
4
4
  Summary: Conformance and adoption CLI for the py-canon fleet standard
5
5
  Keywords: packaging,release,conformance,copier,cli,python
6
6
  Author: Gaurav Sood
@@ -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.0"
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.13"]
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.0"
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.13"]
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.13"
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 the repo's extra entries are appended. Keys and
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 ast.walk(tree):
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 == standard["<"]
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; keep its exclusive upper bound. Run preen adopt "
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 = datetime.datetime.now().astimezone().date().isoformat()
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