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.
- {constutil-1.1.0 → constutil-1.2.0}/PKG-INFO +40 -14
- {constutil-1.1.0 → constutil-1.2.0}/README.md +39 -13
- {constutil-1.1.0 → constutil-1.2.0}/pyproject.toml +1 -1
- constutil-1.2.0/scripts/bump-version.sh +43 -0
- {constutil-1.1.0 → constutil-1.2.0}/src/constutil/constgroup.py +15 -0
- {constutil-1.1.0 → constutil-1.2.0}/tests/test_constutil.py +24 -0
- {constutil-1.1.0 → constutil-1.2.0}/uv.lock +1 -1
- {constutil-1.1.0 → constutil-1.2.0}/.github/workflows/ci.yml +0 -0
- {constutil-1.1.0 → constutil-1.2.0}/.github/workflows/pages.yml +0 -0
- {constutil-1.1.0 → constutil-1.2.0}/.github/workflows/prepare_release.yml +0 -0
- {constutil-1.1.0 → constutil-1.2.0}/.github/workflows/pypi_publish.yml +0 -0
- {constutil-1.1.0 → constutil-1.2.0}/.gitignore +0 -0
- {constutil-1.1.0 → constutil-1.2.0}/LICENSE +0 -0
- {constutil-1.1.0 → constutil-1.2.0}/examples/README.md +0 -0
- {constutil-1.1.0 → constutil-1.2.0}/examples/days.py +0 -0
- {constutil-1.1.0 → constutil-1.2.0}/examples/saturn_moons.py +0 -0
- {constutil-1.1.0 → constutil-1.2.0}/scripts/build_docs.py +0 -0
- {constutil-1.1.0 → constutil-1.2.0}/scripts/install-skills.sh +0 -0
- {constutil-1.1.0 → constutil-1.2.0}/skills/constutil/SKILL.md +0 -0
- {constutil-1.1.0 → constutil-1.2.0}/skills/constutil/agents/openai.yaml +0 -0
- {constutil-1.1.0 → constutil-1.2.0}/src/constutil/__init__.py +0 -0
- {constutil-1.1.0 → constutil-1.2.0}/src/constutil/constdef.py +0 -0
- {constutil-1.1.0 → constutil-1.2.0}/src/constutil/py.typed +0 -0
- {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.
|
|
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
|
-
|
|
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
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
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
|
-
|
|
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
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
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
|
|
@@ -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)],
|
|
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
|