constutil 1.1.0__tar.gz → 1.2.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 (24) hide show
  1. {constutil-1.1.0 → constutil-1.2.0}/PKG-INFO +40 -14
  2. {constutil-1.1.0 → constutil-1.2.0}/README.md +39 -13
  3. {constutil-1.1.0 → constutil-1.2.0}/pyproject.toml +1 -1
  4. constutil-1.2.0/scripts/bump-version.sh +43 -0
  5. {constutil-1.1.0 → constutil-1.2.0}/src/constutil/constgroup.py +15 -0
  6. {constutil-1.1.0 → constutil-1.2.0}/tests/test_constutil.py +24 -0
  7. {constutil-1.1.0 → constutil-1.2.0}/uv.lock +1 -1
  8. {constutil-1.1.0 → constutil-1.2.0}/.github/workflows/ci.yml +0 -0
  9. {constutil-1.1.0 → constutil-1.2.0}/.github/workflows/pages.yml +0 -0
  10. {constutil-1.1.0 → constutil-1.2.0}/.github/workflows/prepare_release.yml +0 -0
  11. {constutil-1.1.0 → constutil-1.2.0}/.github/workflows/pypi_publish.yml +0 -0
  12. {constutil-1.1.0 → constutil-1.2.0}/.gitignore +0 -0
  13. {constutil-1.1.0 → constutil-1.2.0}/LICENSE +0 -0
  14. {constutil-1.1.0 → constutil-1.2.0}/examples/README.md +0 -0
  15. {constutil-1.1.0 → constutil-1.2.0}/examples/days.py +0 -0
  16. {constutil-1.1.0 → constutil-1.2.0}/examples/saturn_moons.py +0 -0
  17. {constutil-1.1.0 → constutil-1.2.0}/scripts/build_docs.py +0 -0
  18. {constutil-1.1.0 → constutil-1.2.0}/scripts/install-skills.sh +0 -0
  19. {constutil-1.1.0 → constutil-1.2.0}/skills/constutil/SKILL.md +0 -0
  20. {constutil-1.1.0 → constutil-1.2.0}/skills/constutil/agents/openai.yaml +0 -0
  21. {constutil-1.1.0 → constutil-1.2.0}/src/constutil/__init__.py +0 -0
  22. {constutil-1.1.0 → constutil-1.2.0}/src/constutil/constdef.py +0 -0
  23. {constutil-1.1.0 → constutil-1.2.0}/src/constutil/py.typed +0 -0
  24. {constutil-1.1.0 → constutil-1.2.0}/tests/test_install_skills.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: constutil
3
- Version: 1.1.0
3
+ Version: 1.2.0
4
4
  Summary: Typed constant definitions and groups for Python
5
5
  Project-URL: Homepage, https://github.com/patchfork/constutil
6
6
  Project-URL: Documentation, https://constutil.patchfork.dev/
@@ -36,8 +36,7 @@ Requires **Python 3.12 or newer**.
36
36
  pip install constutil
37
37
  ```
38
38
 
39
- This branch prepares version 1.1.0 (Python 3.12+); PyPI currently provides 1.0.0.
40
- To use the unreleased changes, install directly from GitHub:
39
+ To use changes not yet published to PyPI, install directly from GitHub:
41
40
 
42
41
  ```sh
43
42
  pip install git+https://github.com/patchfork/constutil.git
@@ -120,6 +119,12 @@ class Season(StrConstGroup):
120
119
 
121
120
 
122
121
  assert Season.get_value("Spring") == "spring"
122
+ assert Season.get_value_map() == {
123
+ "SPRING": "spring",
124
+ "SUMMER": "summer",
125
+ "AUTUMN": "autumn",
126
+ "WINTER": "winter",
127
+ }
123
128
  assert Season.is_valid_value("SPRING") is False
124
129
  ```
125
130
 
@@ -193,6 +198,7 @@ returned as-is and is not required to belong to the group; absent defaults are
193
198
  | `get_default()` | Configured member or `None` |
194
199
  | `get_all()` | Tuple of members |
195
200
  | `get_all_map()` | Fresh attribute-name → member dictionary |
201
+ | `get_value_map()` | Fresh attribute-name → stored-value dictionary, in declaration order |
196
202
  | `get_all_values()` | Tuple of stored values |
197
203
  | `get_all_names()` | Tuple of display names |
198
204
  | `get_all_constant_names()` | Tuple of Python attribute names |
@@ -394,17 +400,37 @@ One-time setup:
394
400
  if you already own the project): owner `patchfork`, repository `constutil`,
395
401
  workflow filename `pypi_publish.yml`, environment `pypi`.
396
402
 
397
- For each new release:
398
-
399
- 1. Update `project.version` in `pyproject.toml`, run `uv lock`, and push the changes.
400
- 2. Run **Actions → Prepare GitHub release → Run workflow** on that commit's branch,
401
- with the matching tag (for example, `v1.0.1` for version `1.0.1`). The workflow
402
- runs CI, builds and checks the packages, creates the tag at the tested commit,
403
- and creates a **draft** release with both distributions attached.
404
- 3. Review the draft's notes and downloads, then click **Publish release** in GitHub.
405
- That user action triggers `pypi_publish.yml`. The preparation workflow deliberately
406
- leaves publication to the user: events created using `GITHUB_TOKEN` do not
407
- automatically trigger other workflows.
403
+ #### Trigger a release
404
+
405
+ First bump the package version from the repository root:
406
+
407
+ ```sh
408
+ sh scripts/bump-version.sh --dry-run patch # Preview the next patch version
409
+ sh scripts/bump-version.sh patch # Or: minor, major, or an explicit 1.2.0
410
+ ```
411
+
412
+ The script requires `uv` with the `uv version` command and updates
413
+ `project.version` in `pyproject.toml` and the package entry in `uv.lock`, without
414
+ syncing the environment. Review and commit those changes, then push them to `main`.
415
+ It does not create a commit, tag, or release. Historical version references in
416
+ documentation are left unchanged; review release-specific prose when preparing a
417
+ release.
418
+
419
+ Then prepare the release:
420
+
421
+ 1. Open [Prepare GitHub release](https://github.com/patchfork/constutil/actions/workflows/prepare_release.yml).
422
+ 2. Click **Run workflow**, select the `main` branch, enter the package version
423
+ prefixed with `v` as the tag (for example, **`v1.2.0`**), and start the workflow.
424
+ 3. Wait for the workflow to pass CI and create a **draft release** with the wheel
425
+ and source archive attached.
426
+ 4. Open [Releases](https://github.com/patchfork/constutil/releases), review the
427
+ draft's notes and downloads, and click **Publish release**.
428
+ 5. Publishing automatically triggers
429
+ [Publish to PyPI](https://github.com/patchfork/constutil/actions/workflows/pypi_publish.yml)
430
+ to upload those exact packages. Check that workflow for the publishing result.
431
+
432
+ The preparation workflow deliberately leaves publication to the user: events
433
+ created using `GITHUB_TOKEN` do not automatically trigger other workflows.
408
434
 
409
435
  GitHub releases are immutable after publication. Uploads must happen while the
410
436
  release is still a draft; the publish workflow never adds or replaces release
@@ -14,8 +14,7 @@ Requires **Python 3.12 or newer**.
14
14
  pip install constutil
15
15
  ```
16
16
 
17
- This branch prepares version 1.1.0 (Python 3.12+); PyPI currently provides 1.0.0.
18
- To use the unreleased changes, install directly from GitHub:
17
+ To use changes not yet published to PyPI, install directly from GitHub:
19
18
 
20
19
  ```sh
21
20
  pip install git+https://github.com/patchfork/constutil.git
@@ -98,6 +97,12 @@ class Season(StrConstGroup):
98
97
 
99
98
 
100
99
  assert Season.get_value("Spring") == "spring"
100
+ assert Season.get_value_map() == {
101
+ "SPRING": "spring",
102
+ "SUMMER": "summer",
103
+ "AUTUMN": "autumn",
104
+ "WINTER": "winter",
105
+ }
101
106
  assert Season.is_valid_value("SPRING") is False
102
107
  ```
103
108
 
@@ -171,6 +176,7 @@ returned as-is and is not required to belong to the group; absent defaults are
171
176
  | `get_default()` | Configured member or `None` |
172
177
  | `get_all()` | Tuple of members |
173
178
  | `get_all_map()` | Fresh attribute-name → member dictionary |
179
+ | `get_value_map()` | Fresh attribute-name → stored-value dictionary, in declaration order |
174
180
  | `get_all_values()` | Tuple of stored values |
175
181
  | `get_all_names()` | Tuple of display names |
176
182
  | `get_all_constant_names()` | Tuple of Python attribute names |
@@ -372,17 +378,37 @@ One-time setup:
372
378
  if you already own the project): owner `patchfork`, repository `constutil`,
373
379
  workflow filename `pypi_publish.yml`, environment `pypi`.
374
380
 
375
- For each new release:
376
-
377
- 1. Update `project.version` in `pyproject.toml`, run `uv lock`, and push the changes.
378
- 2. Run **Actions → Prepare GitHub release → Run workflow** on that commit's branch,
379
- with the matching tag (for example, `v1.0.1` for version `1.0.1`). The workflow
380
- runs CI, builds and checks the packages, creates the tag at the tested commit,
381
- and creates a **draft** release with both distributions attached.
382
- 3. Review the draft's notes and downloads, then click **Publish release** in GitHub.
383
- That user action triggers `pypi_publish.yml`. The preparation workflow deliberately
384
- leaves publication to the user: events created using `GITHUB_TOKEN` do not
385
- automatically trigger other workflows.
381
+ #### Trigger a release
382
+
383
+ First bump the package version from the repository root:
384
+
385
+ ```sh
386
+ sh scripts/bump-version.sh --dry-run patch # Preview the next patch version
387
+ sh scripts/bump-version.sh patch # Or: minor, major, or an explicit 1.2.0
388
+ ```
389
+
390
+ The script requires `uv` with the `uv version` command and updates
391
+ `project.version` in `pyproject.toml` and the package entry in `uv.lock`, without
392
+ syncing the environment. Review and commit those changes, then push them to `main`.
393
+ It does not create a commit, tag, or release. Historical version references in
394
+ documentation are left unchanged; review release-specific prose when preparing a
395
+ release.
396
+
397
+ Then prepare the release:
398
+
399
+ 1. Open [Prepare GitHub release](https://github.com/patchfork/constutil/actions/workflows/prepare_release.yml).
400
+ 2. Click **Run workflow**, select the `main` branch, enter the package version
401
+ prefixed with `v` as the tag (for example, **`v1.2.0`**), and start the workflow.
402
+ 3. Wait for the workflow to pass CI and create a **draft release** with the wheel
403
+ and source archive attached.
404
+ 4. Open [Releases](https://github.com/patchfork/constutil/releases), review the
405
+ draft's notes and downloads, and click **Publish release**.
406
+ 5. Publishing automatically triggers
407
+ [Publish to PyPI](https://github.com/patchfork/constutil/actions/workflows/pypi_publish.yml)
408
+ to upload those exact packages. Check that workflow for the publishing result.
409
+
410
+ The preparation workflow deliberately leaves publication to the user: events
411
+ created using `GITHUB_TOKEN` do not automatically trigger other workflows.
386
412
 
387
413
  GitHub releases are immutable after publication. Uploads must happen while the
388
414
  release is still a draft; the publish workflow never adds or replaces release
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "constutil"
7
- version = "1.1.0"
7
+ version = "1.2.0"
8
8
  description = "Typed constant definitions and groups for Python"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -0,0 +1,43 @@
1
+ #!/bin/sh
2
+ # Update package metadata and the lockfile without installing or publishing.
3
+ set -eu
4
+
5
+ usage() {
6
+ cat <<'EOF'
7
+ Usage: sh scripts/bump-version.sh [--dry-run] major|minor|patch|VERSION
8
+
9
+ Examples:
10
+ sh scripts/bump-version.sh patch
11
+ sh scripts/bump-version.sh --dry-run minor
12
+ sh scripts/bump-version.sh 1.2.0
13
+
14
+ Requires uv with the `uv version` command. Updates pyproject.toml and uv.lock.
15
+ Does not commit, tag, or publish. --dry-run previews without changing files.
16
+ EOF
17
+ }
18
+
19
+ dry_run=false
20
+ if [ "${1:-}" = "--help" ] || [ "${1:-}" = "-h" ]; then
21
+ usage
22
+ exit 0
23
+ fi
24
+ if [ "${1:-}" = "--dry-run" ]; then
25
+ dry_run=true
26
+ shift
27
+ fi
28
+ if [ "$#" -ne 1 ]; then
29
+ usage >&2
30
+ exit 2
31
+ fi
32
+
33
+ case "$1" in
34
+ major|minor|patch) set -- --bump "$1" ;;
35
+ [0-9]*) ;; # uv validates and normalizes explicit Python package versions.
36
+ *) usage >&2; exit 2 ;;
37
+ esac
38
+ if [ "$dry_run" = true ]; then
39
+ set -- "$@" --dry-run
40
+ fi
41
+
42
+ repo_root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
43
+ exec uv version --project "$repo_root" --no-sync "$@"
@@ -45,6 +45,21 @@ class ConstGroup(Generic[MemberT]):
45
45
  members[key] = cast(MemberT, member)
46
46
  return members
47
47
 
48
+ @classmethod
49
+ def get_value_map(cls) -> dict[str, int | str]:
50
+ """Return a fresh mapping of constant name to value, in class declaration order.
51
+
52
+ Keys are the class attribute names (``"MONDAY"``), the same keys
53
+ ``get_all_map`` uses; values are each member's ``.value``. Lookups by name go
54
+ through ``get_by_constant_name``; this is for handing the whole group to an
55
+ API that wants a plain dict, such as SQLAlchemy's ``naming_convention`` or a
56
+ JSON payload.
57
+
58
+ Returns:
59
+ A new dict each call, so callers may mutate it freely.
60
+ """
61
+ return {key: member.value for key, member in cls.get_all_map().items()}
62
+
48
63
  @classmethod
49
64
  def get_all(cls) -> tuple[MemberT, ...]:
50
65
  return tuple(cls.get_all_map().values())
@@ -44,6 +44,30 @@ def test_enumeration_and_default():
44
44
  assert len(Day.get_all()) == 2
45
45
 
46
46
 
47
+ @pytest.mark.parametrize(
48
+ "group,expected,value_type",
49
+ [
50
+ (Day, [("MONDAY", 1), ("TUESDAY", 2)], int),
51
+ (Moon, [("TITAN", "titan"), ("RHEA", "rhea")], str),
52
+ ],
53
+ )
54
+ def test_value_map_preserves_declaration_order_filters_and_value_types(group, expected, value_type):
55
+ mapping = group.get_value_map()
56
+ assert isinstance(mapping, dict)
57
+ assert list(mapping.items()) == expected
58
+ assert all(type(value) is value_type for value in mapping.values())
59
+
60
+
61
+ @pytest.mark.parametrize("group", [Day, Moon])
62
+ def test_value_map_returns_a_fresh_dict(group):
63
+ original = group.get_value_map()
64
+ mapping = group.get_value_map()
65
+ assert mapping is not original
66
+ mapping.clear()
67
+ mapping["EXTRA"] = "extra"
68
+ assert group.get_value_map() == original
69
+
70
+
47
71
  @pytest.mark.parametrize(
48
72
  "value,expected",
49
73
  [(1, True), ("1", False), (True, True), (1.0, True), (0, False), (None, False)],
@@ -278,7 +278,7 @@ wheels = [
278
278
 
279
279
  [[package]]
280
280
  name = "constutil"
281
- version = "1.1.0"
281
+ version = "1.2.0"
282
282
  source = { editable = "." }
283
283
 
284
284
  [package.dev-dependencies]
File without changes
File without changes
File without changes
File without changes