contractme 2.4.1__tar.gz → 2.4.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 (72) hide show
  1. {contractme-2.4.1 → contractme-2.4.2}/.gitlab-ci.yml +26 -0
  2. {contractme-2.4.1 → contractme-2.4.2}/CHANGELOG.md +11 -0
  3. {contractme-2.4.1 → contractme-2.4.2}/CLAUDE.md +4 -0
  4. {contractme-2.4.1 → contractme-2.4.2}/PKG-INFO +4 -2
  5. {contractme-2.4.1 → contractme-2.4.2}/README.md +3 -1
  6. {contractme-2.4.1 → contractme-2.4.2}/justfile +5 -0
  7. {contractme-2.4.1 → contractme-2.4.2}/pyproject.toml +6 -2
  8. contractme-2.4.2/release_notes.md +8 -0
  9. {contractme-2.4.1 → contractme-2.4.2}/src/contractme/contracting.py +13 -7
  10. {contractme-2.4.1 → contractme-2.4.2}/tests/test_contracting.py +22 -1
  11. contractme-2.4.2/tox.ini +21 -0
  12. {contractme-2.4.1 → contractme-2.4.2}/uv.lock +186 -13
  13. contractme-2.4.1/release_notes.md +0 -12
  14. {contractme-2.4.1 → contractme-2.4.2}/.flake8 +0 -0
  15. {contractme-2.4.1 → contractme-2.4.2}/.gitignore +0 -0
  16. {contractme-2.4.1 → contractme-2.4.2}/.pre-commit-config.yaml +0 -0
  17. {contractme-2.4.1 → contractme-2.4.2}/.python-version +0 -0
  18. {contractme-2.4.1 → contractme-2.4.2}/.vscode/settings.json +0 -0
  19. {contractme-2.4.1 → contractme-2.4.2}/LICENSE +0 -0
  20. {contractme-2.4.1 → contractme-2.4.2}/context7.json +0 -0
  21. {contractme-2.4.1 → contractme-2.4.2}/docs/comparison-facts.yaml +0 -0
  22. {contractme-2.4.1 → contractme-2.4.2}/docs/comparison.md +0 -0
  23. {contractme-2.4.1 → contractme-2.4.2}/docs/types.md +0 -0
  24. {contractme-2.4.1 → contractme-2.4.2}/release/prerelease.py +0 -0
  25. {contractme-2.4.1 → contractme-2.4.2}/release/pypi_mint_token.py +0 -0
  26. {contractme-2.4.1 → contractme-2.4.2}/release/release_notes.py +0 -0
  27. {contractme-2.4.1 → contractme-2.4.2}/src/contractme/__init__.py +0 -0
  28. {contractme-2.4.1 → contractme-2.4.2}/src/contractme/annotations.py +0 -0
  29. {contractme-2.4.1 → contractme-2.4.2}/src/contractme/config.py +0 -0
  30. {contractme-2.4.1 → contractme-2.4.2}/src/contractme/policy.py +0 -0
  31. {contractme-2.4.1 → contractme-2.4.2}/src/contractme/probing.py +0 -0
  32. {contractme-2.4.1 → contractme-2.4.2}/src/contractme/py.typed +0 -0
  33. {contractme-2.4.1 → contractme-2.4.2}/src/contractme/records.py +0 -0
  34. {contractme-2.4.1 → contractme-2.4.2}/src/contractme/result.py +0 -0
  35. {contractme-2.4.1 → contractme-2.4.2}/src/contractme/skills/contractme/SKILL.md +0 -0
  36. {contractme-2.4.1 → contractme-2.4.2}/src/contractme/testing.py +0 -0
  37. {contractme-2.4.1 → contractme-2.4.2}/src/contractme/typecheck.py +0 -0
  38. {contractme-2.4.1 → contractme-2.4.2}/src/contractme/types/__init__.py +0 -0
  39. {contractme-2.4.1 → contractme-2.4.2}/src/contractme/types/containers.py +0 -0
  40. {contractme-2.4.1 → contractme-2.4.2}/src/contractme/types/data.py +0 -0
  41. {contractme-2.4.1 → contractme-2.4.2}/src/contractme/types/identifiers.py +0 -0
  42. {contractme-2.4.1 → contractme-2.4.2}/src/contractme/types/network.py +0 -0
  43. {contractme-2.4.1 → contractme-2.4.2}/src/contractme/types/numeric.py +0 -0
  44. {contractme-2.4.1 → contractme-2.4.2}/src/contractme/types/paths.py +0 -0
  45. {contractme-2.4.1 → contractme-2.4.2}/src/contractme/types/predicates.py +0 -0
  46. {contractme-2.4.1 → contractme-2.4.2}/src/contractme/types/system.py +0 -0
  47. {contractme-2.4.1 → contractme-2.4.2}/src/contractme/types/temporal.py +0 -0
  48. {contractme-2.4.1 → contractme-2.4.2}/src/contractme/types/text.py +0 -0
  49. {contractme-2.4.1 → contractme-2.4.2}/src/contractme/typeshape.py +0 -0
  50. {contractme-2.4.1 → contractme-2.4.2}/tests/conftest.py +0 -0
  51. {contractme-2.4.1 → contractme-2.4.2}/tests/test_annotations.py +0 -0
  52. {contractme-2.4.1 → contractme-2.4.2}/tests/test_annotations_future.py +0 -0
  53. {contractme-2.4.1 → contractme-2.4.2}/tests/test_async.py +0 -0
  54. {contractme-2.4.1 → contractme-2.4.2}/tests/test_canaries.py +0 -0
  55. {contractme-2.4.1 → contractme-2.4.2}/tests/test_contracting_optimized_python.py +0 -0
  56. {contractme-2.4.1 → contractme-2.4.2}/tests/test_evaluation_order.py +0 -0
  57. {contractme-2.4.1 → contractme-2.4.2}/tests/test_hypothesis.py +0 -0
  58. {contractme-2.4.1 → contractme-2.4.2}/tests/test_hypothesis_async.py +0 -0
  59. {contractme-2.4.1 → contractme-2.4.2}/tests/test_layering.py +0 -0
  60. {contractme-2.4.1 → contractme-2.4.2}/tests/test_literal.py +0 -0
  61. {contractme-2.4.1 → contractme-2.4.2}/tests/test_optimized_policy.py +0 -0
  62. {contractme-2.4.1 → contractme-2.4.2}/tests/test_policy.py +0 -0
  63. {contractme-2.4.1 → contractme-2.4.2}/tests/test_predicate_source.py +0 -0
  64. {contractme-2.4.1 → contractme-2.4.2}/tests/test_probing.py +0 -0
  65. {contractme-2.4.1 → contractme-2.4.2}/tests/test_records.py +0 -0
  66. {contractme-2.4.1 → contractme-2.4.2}/tests/test_reentrancy.py +0 -0
  67. {contractme-2.4.1 → contractme-2.4.2}/tests/test_show_source.py +0 -0
  68. {contractme-2.4.1 → contractme-2.4.2}/tests/test_typechecks.py +0 -0
  69. {contractme-2.4.1 → contractme-2.4.2}/tests/test_types.py +0 -0
  70. {contractme-2.4.1 → contractme-2.4.2}/tests/test_types_advanced.py +0 -0
  71. {contractme-2.4.1 → contractme-2.4.2}/tests/test_types_extra.py +0 -0
  72. {contractme-2.4.1 → contractme-2.4.2}/tests/test_typeshape.py +0 -0
@@ -44,6 +44,7 @@ stages:
44
44
  - pyproject.toml
45
45
  - uv.lock
46
46
  - justfile
47
+ - tox.ini
47
48
  - .flake8
48
49
  - .gitlab-ci.yml
49
50
  when: always
@@ -128,6 +129,31 @@ checks-optimized:
128
129
  reports:
129
130
  junit: obj/tests_report_optimized.xml
130
131
 
132
+ # Python 3.14: one extra job runs both version-sensitive gates
133
+ # (coverage + the -O run) through tox; lint and types stay on 3.13 in `checks`.
134
+ # UV_PYTHON makes the outer `uv run` use the image's interpreter instead of
135
+ # downloading .python-version's 3.13. A draft MR tolerates failures here, like
136
+ # the lax mode of `checks`.
137
+ checks-py314:
138
+ extends: .uv
139
+ stage: test
140
+ interruptible: true
141
+ variables:
142
+ PYTHON_VERSION: "3.14"
143
+ UV_PYTHON: "3.14"
144
+ rules:
145
+ - if: $CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TITLE =~ /^(draft|wip):/i
146
+ changes: *code-paths
147
+ allow_failure: true
148
+ - !reference [.on-code-change, rules]
149
+ script:
150
+ - uv run just tox -e py314
151
+ artifacts:
152
+ reports:
153
+ junit:
154
+ - obj/py314/tests_report.xml
155
+ - obj/py314/tests_report_optimized.xml
156
+
131
157
  # Validate the tag against pyproject + CHANGELOG, extract the release notes,
132
158
  # then build dists.
133
159
  build:
@@ -7,6 +7,17 @@ section whose heading matches the tag via
7
7
  and GitLab release description — so every `## vX.Y.Z` heading must match its
8
8
  tag exactly.
9
9
 
10
+ ## v2.4.2
11
+
12
+ * On Python 3.14, `@annotated` stacked over `@precondition` (or any contracted function
13
+ passed to `typing.get_type_hints`) works again: it raised
14
+ `NameError: name 'ClassVar' is not defined` at decoration since v2.4.0
15
+ ([#104](https://gitlab.com/leogermond/contractme/-/issues/104)).
16
+ * The test suite runs on Python 3.13 and 3.14, locally (`just tox`) and in CI
17
+ ([9b69657](https://gitlab.com/leogermond/contractme/-/commit/9b69657)).
18
+ * `just types` installs pyright with its bundled node, so a broken nodeenv no longer fails CI
19
+ ([51e80c6](https://gitlab.com/leogermond/contractme/-/commit/51e80c6)).
20
+
10
21
  ## v2.4.1
11
22
 
12
23
  * A failed `Predicate` constraint is named by its source when it is a lambda
@@ -24,6 +24,10 @@ just test-optimized # differential run: contracts compiled out via -O, valid-p
24
24
  # behavior must be identical (mirrors the checks-optimized CI
25
25
  # job). Contract-violation tests skip themselves via
26
26
  # tests/conftest.py:contract_violation().
27
+ just tox [args...] # cov + test-optimized on Python 3.13 and 3.14 (tox.ini, tox-uv:
28
+ # each env synced from uv.lock's dev group); mirrors the
29
+ # checks-py314 CI job:
30
+ # just tox -e py314 -- -x
27
31
  just fmt # format code (black)
28
32
  just lint # black --check + flake8
29
33
  just types # type checking (pinned pyright from the dev deps — matches CI)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: contractme
3
- Version: 2.4.1
3
+ Version: 2.4.2
4
4
  Summary: Executable specifications for python. Keep control of the code AI writes for you.
5
5
  Project-URL: Repository, https://gitlab.com/leogermond/contractme
6
6
  Author-email: Leo Germond <leo.germond@gmail.com>
@@ -495,7 +495,9 @@ cost nothing where it matters.
495
495
  `just test`
496
496
 
497
497
  Dev commands are `just` recipes — bare `just` lists them; `just check` runs the
498
- full local gate (tests + coverage, the `-O` differential run, lint, types).
498
+ full local gate (tests + coverage, the `-O` differential run, lint, types); `just tox`
499
+ runs the version-sensitive gates (coverage, the `-O` run) on every supported Python
500
+ (3.13, 3.14).
499
501
 
500
502
  # Deploy new version
501
503
 
@@ -476,7 +476,9 @@ cost nothing where it matters.
476
476
  `just test`
477
477
 
478
478
  Dev commands are `just` recipes — bare `just` lists them; `just check` runs the
479
- full local gate (tests + coverage, the `-O` differential run, lint, types).
479
+ full local gate (tests + coverage, the `-O` differential run, lint, types); `just tox`
480
+ runs the version-sensitive gates (coverage, the `-O` run) on every supported Python
481
+ (3.13, 3.14).
480
482
 
481
483
  # Deploy new version
482
484
 
@@ -24,6 +24,11 @@ cov *args:
24
24
  test-optimized *args:
25
25
  uv run python -O -m pytest -W ignore::pytest.PytestConfigWarning "$@"
26
26
 
27
+ # Envs and args pass through to tox (e.g. `just tox -e py314 -- -x`); see tox.ini.
28
+ [doc('Coverage gate + -O differential run on every supported Python (3.13, 3.14)')]
29
+ tox *args:
30
+ uv run tox "$@"
31
+
27
32
  # Format the code (black, line length 100)
28
33
  fmt:
29
34
  uv run black .
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "contractme"
3
- version = "2.4.1"
3
+ version = "2.4.2"
4
4
  description = "Executable specifications for python. Keep control of the code AI writes for you."
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -30,7 +30,9 @@ build-backend = "hatchling.build"
30
30
  [dependency-groups]
31
31
  dev = [
32
32
  "epycs>=1.4.0",
33
- "pyright==1.1.407",
33
+ # [nodejs] ships node as a wheel: pyright's nodeenv fallback crashes on
34
+ # some node release listings (CI image has no node of its own)
35
+ "pyright[nodejs]==1.1.407",
34
36
  "pytest>=8.3.5",
35
37
  "pytest-cov>=6.0.0",
36
38
  "black>=25.9.0",
@@ -42,6 +44,8 @@ dev = [
42
44
  "croniter>=2.0.0",
43
45
  "pycountry>=23.12.11",
44
46
  "pyyaml>=6.0",
47
+ "tox>=4.67.12",
48
+ "tox-uv>=1.37.5",
45
49
  ]
46
50
 
47
51
  [tool.pytest.ini_options]
@@ -0,0 +1,8 @@
1
+ * On Python 3.14, `@annotated` stacked over `@precondition` (or any contracted function
2
+ passed to `typing.get_type_hints`) works again: it raised
3
+ `NameError: name 'ClassVar' is not defined` at decoration since v2.4.0
4
+ ([#104](https://gitlab.com/leogermond/contractme/-/issues/104)).
5
+ * The test suite runs on Python 3.13 and 3.14, locally (`just tox`) and in CI
6
+ ([9b69657](https://gitlab.com/leogermond/contractme/-/commit/9b69657)).
7
+ * `just types` installs pyright with its bundled node, so a broken nodeenv no longer fails CI
8
+ ([51e80c6](https://gitlab.com/leogermond/contractme/-/commit/51e80c6)).
@@ -34,7 +34,6 @@ from contextvars import ContextVar
34
34
  from itertools import accumulate, islice
35
35
  from typing import (
36
36
  Callable,
37
- ClassVar,
38
37
  NamedTuple,
39
38
  Protocol,
40
39
  Self,
@@ -379,15 +378,19 @@ def get_kw_for_postcondition_after(result, conditional: Callable) -> dict:
379
378
 
380
379
 
381
380
  class _ContractWrapper[**F_Params, F_Ret]:
382
- """The engine's contracted-callable wrapper (the non-``optimized`` path)."""
381
+ """The engine's contracted-callable wrapper (the non-``optimized`` path).
382
+
383
+ Its annotations are the wrapped function's, as for any ``functools.wraps``
384
+ wrapper (issue #104): the class body declares no annotation, since a class
385
+ ``__annotations__`` dict is what an instance attribute lookup finds first —
386
+ on Python 3.14, where ``update_wrapper`` copies ``__annotate__`` instead of
387
+ ``__annotations__``, ``get_type_hints`` would read the class's own hints
388
+ instead. Its attributes are declared in ``__init__``, invisible at runtime."""
383
389
 
384
- # set by `functools.update_wrapper` in `__init__`
385
- __name__: str
386
- __qualname__: str
387
390
  # a class-level mark, out of reach of `functools.wraps` (which copies the
388
391
  # instance `__dict__`): how `config.no_contracts` recognises the engine's
389
- # wrapper without importing it (issue #74)
390
- contractme_engine_wrapper: ClassVar[bool] = True
392
+ # wrapper without importing it (issue #74); unannotated, see the docstring
393
+ contractme_engine_wrapper = True
391
394
 
392
395
  def __init__(
393
396
  self,
@@ -398,6 +401,9 @@ class _ContractWrapper[**F_Params, F_Ret]:
398
401
  accepts: F_Pred,
399
402
  rejects: F_Pred,
400
403
  ):
404
+ # set by `functools.update_wrapper`
405
+ self.__name__: str
406
+ self.__qualname__: str
401
407
  functools.update_wrapper(self, original_call)
402
408
  self.call_and_run_contracts = call_and_run_contracts
403
409
  self.contractme_wrapped = original_call
@@ -3,11 +3,12 @@
3
3
  # These tests exercise the raw pre/postcondition machinery on purpose; don't take them
4
4
  # as the recommended style.
5
5
  import functools
6
+ from typing import get_type_hints
6
7
 
7
8
  import pytest
8
9
  from conftest import contract_violation
9
10
  import contractme
10
- from contractme import precondition, postcondition
11
+ from contractme import annotated, precondition, postcondition
11
12
  from contractme.contracting import ContractedFunction
12
13
  import math
13
14
 
@@ -470,6 +471,26 @@ def test_contracted_function_names_itself_like_a_function():
470
471
  assert isinstance(step, ContractedFunction)
471
472
 
472
473
 
474
+ def test_contracted_function_has_the_wrapped_type_hints():
475
+ # issue #104: on Python 3.14, `get_type_hints` read the wrapper class's own
476
+ # annotations, evaluated in the wrapped function's globals (NameError)
477
+ def step(x: int, label: str = "") -> int:
478
+ return x
479
+
480
+ contracted = precondition(lambda x: x > 0)(step)
481
+ assert get_type_hints(contracted, include_extras=True) == get_type_hints(step)
482
+ # `@annotated` stacked over `@precondition` reads those hints at decoration time
483
+ assert annotated(contracted)(1) == 1
484
+
485
+
486
+ def test_contracted_function_class_declares_no_runtime_annotation():
487
+ # issue #104, on every Python version: a class annotation is what an instance
488
+ # attribute lookup of `__annotations__` finds once `update_wrapper` copies only
489
+ # `__annotate__` (Python 3.14) — the wrapper's hints must be the wrapped function's
490
+ contracted = precondition(lambda x: x > 0)(lambda x: x)
491
+ assert get_type_hints(type(contracted)) == {}
492
+
493
+
473
494
  def test_cannot_have_precondition_of_a_constructor():
474
495
  with pytest.raises(RuntimeError):
475
496
 
@@ -0,0 +1,21 @@
1
+ # The version-sensitive gates, once per supported Python: `just tox` (all envs)
2
+ # or `just tox -e py314`. Lint and types stay single-version (`just check`).
3
+ [tox]
4
+ env_list = py313, py314
5
+ requires = tox-uv>=1.37
6
+ # obj/ is already ignored by git, black and flake8; the dot hides it from pyright
7
+ work_dir = {tox_root}/obj/.tox
8
+
9
+ [testenv]
10
+ # installs uv.lock's `dev` group into the env (uv sync), no duplicated dep list
11
+ runner = uv-venv-lock-runner
12
+ dependency_groups = dev
13
+ pass_env = UV_*
14
+ # the recipes' `uv run` reuses this env as tox synced it (UV_PROJECT_ENVIRONMENT
15
+ # is set by tox-uv); a resync would rebuild it on .python-version's 3.13
16
+ set_env = UV_NO_SYNC = 1
17
+ # no fail-fast, like `just check`: the -O run still reports when cov fails
18
+ ignore_errors = true
19
+ commands =
20
+ just cov --junitxml=obj/{env_name}/tests_report.xml {posargs}
21
+ just test-optimized --junitxml=obj/{env_name}/tests_report_optimized.xml {posargs}
@@ -1,5 +1,5 @@
1
1
  version = 1
2
- revision = 3
2
+ revision = 5
3
3
  requires-python = ">=3.13"
4
4
 
5
5
  [[package]]
@@ -41,6 +41,15 @@ wheels = [
41
41
  { url = "https://files.pythonhosted.org/packages/1b/46/863c90dcd3f9d41b109b7f19032ae0db021f0b2a81482ba0a1e28c84de86/black-25.9.0-py3-none-any.whl", hash = "sha256:474b34c1342cdc157d307b56c4c65bce916480c4a8f6551fdc6bf9b486a7c4ae", size = 203363, upload-time = "2025-09-19T00:27:35.724Z" },
42
42
  ]
43
43
 
44
+ [[package]]
45
+ name = "cachetools"
46
+ version = "7.3.0"
47
+ source = { registry = "https://pypi.org/simple" }
48
+ sdist = { url = "https://files.pythonhosted.org/packages/5f/6b/6c1553e1e8f672f9423344a1915aad1537439ad3d14a3fbc1a02159cfd94/cachetools-7.3.0.tar.gz", hash = "sha256:3f63fc208410f0c3a3b37883c4903ecdacd6f0b259134e6d2e82debd5b2e01de", size = 48563, upload-time = "2026-11-05T20:38:46.434Z" }
49
+ wheels = [
50
+ { url = "https://files.pythonhosted.org/packages/29/aa/e54a49d0927b2fa7b86bc94dbde57125e38e8cfca7a31b6764e8dbf35e9f/cachetools-7.3.0-py3-none-any.whl", hash = "sha256:2105787c94619bde7072624fec0fe7b8a08bb7bf6c757c318f8925573c6f07c9", size = 19575, upload-time = "2026-11-05T20:38:44.928Z" },
51
+ ]
52
+
44
53
  [[package]]
45
54
  name = "click"
46
55
  version = "8.3.0"
@@ -64,7 +73,7 @@ wheels = [
64
73
 
65
74
  [[package]]
66
75
  name = "contractme"
67
- version = "2.4.1"
76
+ version = "2.4.2"
68
77
  source = { editable = "." }
69
78
  dependencies = [
70
79
  { name = "annotated-types" },
@@ -89,11 +98,13 @@ dev = [
89
98
  { name = "epycs" },
90
99
  { name = "flake8" },
91
100
  { name = "pycountry" },
92
- { name = "pyright" },
101
+ { name = "pyright", extra = ["nodejs"] },
93
102
  { name = "pytest" },
94
103
  { name = "pytest-cov" },
95
104
  { name = "pyyaml" },
96
105
  { name = "rust-just" },
106
+ { name = "tox" },
107
+ { name = "tox-uv" },
97
108
  ]
98
109
 
99
110
  [package.metadata]
@@ -113,11 +124,13 @@ dev = [
113
124
  { name = "epycs", specifier = ">=1.4.0" },
114
125
  { name = "flake8", specifier = ">=7.2.0" },
115
126
  { name = "pycountry", specifier = ">=23.12.11" },
116
- { name = "pyright", specifier = "==1.1.407" },
127
+ { name = "pyright", extras = ["nodejs"], specifier = "==1.1.407" },
117
128
  { name = "pytest", specifier = ">=8.3.5" },
118
129
  { name = "pytest-cov", specifier = ">=6.0.0" },
119
130
  { name = "pyyaml", specifier = ">=6.0" },
120
131
  { name = "rust-just", specifier = ">=1.34" },
132
+ { name = "tox", specifier = ">=4.67.12" },
133
+ { name = "tox-uv", specifier = ">=1.37.5" },
121
134
  ]
122
135
 
123
136
  [[package]]
@@ -161,6 +174,15 @@ wheels = [
161
174
  { url = "https://files.pythonhosted.org/packages/d0/39/783980e78cb92c2d7bdb1fc7dbc86e94ccc6d58224d76a7f1f51b6c51e30/croniter-6.2.2-py3-none-any.whl", hash = "sha256:a5d17b1060974d36251ea4faf388233eca8acf0d09cbd92d35f4c4ac8f279960", size = 45422, upload-time = "2026-03-15T08:43:46.626Z" },
162
175
  ]
163
176
 
177
+ [[package]]
178
+ name = "distlib"
179
+ version = "0.4.4"
180
+ source = { registry = "https://pypi.org/simple" }
181
+ sdist = { url = "https://files.pythonhosted.org/packages/68/bc/d36966de7304047a1001a7559fbf266c464bb5d8cc85464536ef8c9bcc32/distlib-0.4.4.tar.gz", hash = "sha256:ca4175b4ac7f71223cc30ed7248c45d2b38c2439d8ee6e498ab67ed63cf3508d", size = 621304, upload-time = "2026-10-16T13:56:34.203Z" }
182
+ wheels = [
183
+ { url = "https://files.pythonhosted.org/packages/c1/2b/238f02a4927fa2e27aa995371d240a468b52e42eae44a678434aff736b38/distlib-0.4.4-py2.py3-none-any.whl", hash = "sha256:cf09d55b1e85a89576b036aeb71550bfd68320f3f21761ca329eadadad918638", size = 472099, upload-time = "2026-10-16T13:56:32.053Z" },
184
+ ]
185
+
164
186
  [[package]]
165
187
  name = "epycs"
166
188
  version = "1.5.0"
@@ -170,6 +192,15 @@ wheels = [
170
192
  { url = "https://files.pythonhosted.org/packages/50/01/aa81bef60e1aa2ce606ba7c86f7119ed6e1e3bf30a9e8b33a507681847c8/epycs-1.5.0-py3-none-any.whl", hash = "sha256:79a99b197eb251f80fd0a42bb54404e170c030e0df3698402f4037ac07ce9878", size = 10452, upload-time = "2025-03-23T14:25:43.476Z" },
171
193
  ]
172
194
 
195
+ [[package]]
196
+ name = "filelock"
197
+ version = "4.2.9"
198
+ source = { registry = "https://pypi.org/simple" }
199
+ sdist = { url = "https://files.pythonhosted.org/packages/a3/1d/d730c664fcb4f1d264a722722a3134cf0881854f73c1fd58abf87572f232/filelock-4.2.9.tar.gz", hash = "sha256:96530b7c538d4ea08493ab067247aab7948c54d8cd622cce711c97cccc5d9896", size = 663116, upload-time = "2026-11-04T18:37:10.014Z" }
200
+ wheels = [
201
+ { url = "https://files.pythonhosted.org/packages/00/05/76184fe08339723d643ab47887a8e4de8f2921ca4e008617ab7490af7857/filelock-4.2.9-py3-none-any.whl", hash = "sha256:a379c50a729e5b68c8519a4d26489f755d9769960a236ecd838190e2a78db2e9", size = 164734, upload-time = "2026-11-04T18:37:08.273Z" },
202
+ ]
203
+
173
204
  [[package]]
174
205
  name = "flake8"
175
206
  version = "7.3.0"
@@ -233,13 +264,29 @@ wheels = [
233
264
  { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" },
234
265
  ]
235
266
 
267
+ [[package]]
268
+ name = "nodejs-wheel-binaries"
269
+ version = "24.19.0"
270
+ source = { registry = "https://pypi.org/simple" }
271
+ sdist = { url = "https://files.pythonhosted.org/packages/c0/76/7e97195e14346598565a0de4ca8bdbd5e634b3fb5b1ba590b7b1b89f8a63/nodejs_wheel_binaries-24.19.0.tar.gz", hash = "sha256:db217eef8cab8551667863379b08db4d9067403f6cbbe87481eb40edceb8aa9b", size = 8058, upload-time = "2026-08-19T21:47:19.671Z" }
272
+ wheels = [
273
+ { url = "https://files.pythonhosted.org/packages/8c/52/0774b52c7be8151ad9d5aff44edc100c3f13d6d8eb3765f63ffa40e69fe8/nodejs_wheel_binaries-24.19.0-py2.py3-none-macosx_13_0_arm64.whl", hash = "sha256:e12cbfd69089504e42fb14194ce734a9dcf3eb38c820ca63dd511d36fb964e9c", size = 56047203, upload-time = "2026-08-19T21:46:43.448Z" },
274
+ { url = "https://files.pythonhosted.org/packages/67/3a/4fdbbfecf2c23d52c0e3f68de7f7c1b3c97a26d328c69c5f6c49c48e340e/nodejs_wheel_binaries-24.19.0-py2.py3-none-macosx_13_0_x86_64.whl", hash = "sha256:1c890adf4b7e6556ccc1ca66c866bb81884b6c9a581dee4e530dc7f78fb9d514", size = 56219459, upload-time = "2026-08-19T21:46:48.45Z" },
275
+ { url = "https://files.pythonhosted.org/packages/5f/a8/0147149415195c59b8a72a594916bfb80d6be4d586f9fbfda313889e0efc/nodejs_wheel_binaries-24.19.0-py2.py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:4e029dadfae1295876063c96b236f673487e8c27379fe146c1e2250283520227", size = 60588256, upload-time = "2026-08-19T21:46:53.298Z" },
276
+ { url = "https://files.pythonhosted.org/packages/f4/89/6631d0982353da1bb7bc00bb1988f702822c62b42634f57999ee53b5c337/nodejs_wheel_binaries-24.19.0-py2.py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:4196a947bcc883f2003ab101762d729f3e99b5e86b75bd09151563403e2eceb8", size = 61123607, upload-time = "2026-08-19T21:46:58.117Z" },
277
+ { url = "https://files.pythonhosted.org/packages/32/a2/fa30f0841e4602995782e124359f9b910c7b481d98decf61ef0b2fc3ebfb/nodejs_wheel_binaries-24.19.0-py2.py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:352e048ab4dd35e7de5f338d1cc4fcbf77a0e93da30bf7336a8217ee246b31d7", size = 62632842, upload-time = "2026-08-19T21:47:03.42Z" },
278
+ { url = "https://files.pythonhosted.org/packages/18/01/22d97ca72213f66cc386ee638db30c2e62757fdf761c6029074bced83d1c/nodejs_wheel_binaries-24.19.0-py2.py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:28d078b2ced9e2069516e652dc4b1380e7a1a7f2d3934eccd1611586d283ba4c", size = 63250653, upload-time = "2026-08-19T21:47:07.938Z" },
279
+ { url = "https://files.pythonhosted.org/packages/88/d1/e3be8fa327a795bcaf7a19cd84299e338a7bce32ff0665fdce9cfa22573c/nodejs_wheel_binaries-24.19.0-py2.py3-none-win_amd64.whl", hash = "sha256:67e3abeb9c3830cae8c8487ae8a2af7cc27dfa75af06145cee5ca7d1857c81bd", size = 42448503, upload-time = "2026-08-19T21:47:12.093Z" },
280
+ { url = "https://files.pythonhosted.org/packages/1d/37/34cf28ba1691a060174948a9927fe61091982d6048b2e403071a9acce443/nodejs_wheel_binaries-24.19.0-py2.py3-none-win_arm64.whl", hash = "sha256:d9074c665ea68b04e183d82482c86dc907d3a9bd15eb6cf85542cb785266bb36", size = 40090155, upload-time = "2026-08-19T21:47:16.032Z" },
281
+ ]
282
+
236
283
  [[package]]
237
284
  name = "packaging"
238
- version = "25.0"
285
+ version = "26.3"
239
286
  source = { registry = "https://pypi.org/simple" }
240
- sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" }
287
+ sdist = { url = "https://files.pythonhosted.org/packages/7d/fa/3944b40b07da9ce895c0e6303a5ab7d53da063554f534556b134a54d6093/packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79", size = 313412, upload-time = "2026-08-04T18:15:28.737Z" }
241
288
  wheels = [
242
- { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" },
289
+ { url = "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z" },
243
290
  ]
244
291
 
245
292
  [[package]]
@@ -253,20 +300,20 @@ wheels = [
253
300
 
254
301
  [[package]]
255
302
  name = "platformdirs"
256
- version = "4.5.0"
303
+ version = "4.14.5"
257
304
  source = { registry = "https://pypi.org/simple" }
258
- sdist = { url = "https://files.pythonhosted.org/packages/61/33/9611380c2bdb1225fdef633e2a9610622310fed35ab11dac9620972ee088/platformdirs-4.5.0.tar.gz", hash = "sha256:70ddccdd7c99fc5942e9fc25636a8b34d04c24b335100223152c2803e4063312", size = 21632, upload-time = "2025-10-08T17:44:48.791Z" }
305
+ sdist = { url = "https://files.pythonhosted.org/packages/d7/9a/8475be631fb33ca1466915e70fd594c42a4e84b512db650eaa14f90fe75d/platformdirs-4.14.5.tar.gz", hash = "sha256:32e9921db7385aa8169bee4fe9501923e5715ba9fb7ea3b9c69b0d8e0639f7a1", size = 71464, upload-time = "2026-11-04T16:54:50.358Z" }
259
306
  wheels = [
260
- { url = "https://files.pythonhosted.org/packages/73/cb/ac7874b3e5d58441674fb70742e6c374b28b0c7cb988d37d991cde47166c/platformdirs-4.5.0-py3-none-any.whl", hash = "sha256:e578a81bb873cbb89a41fcc904c7ef523cc18284b7e3b3ccf06aca1403b7ebd3", size = 18651, upload-time = "2025-10-08T17:44:47.223Z" },
307
+ { url = "https://files.pythonhosted.org/packages/7c/ae/ecbbbfd0e4343a6a521d49b3c8656fb4643dee2a4f6eaf941cf3280391a8/platformdirs-4.14.5-py3-none-any.whl", hash = "sha256:8e778c6fa421516f4d3369449f8df3b0e1b72c0e0005fe88e3ec772d0a14e750", size = 35402, upload-time = "2026-11-04T16:54:48.875Z" },
261
308
  ]
262
309
 
263
310
  [[package]]
264
311
  name = "pluggy"
265
- version = "1.5.0"
312
+ version = "1.7.0"
266
313
  source = { registry = "https://pypi.org/simple" }
267
- sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955, upload-time = "2024-04-20T21:34:42.531Z" }
314
+ sdist = { url = "https://files.pythonhosted.org/packages/bf/db/7fc19e6f2dc92a966727031389fc2e08b558f0f25eb7403c1119ad4713cd/pluggy-1.7.0.tar.gz", hash = "sha256:d1eaa46ebb595891b860ab086b4d09c8588af65ebd4361b8e8f4bb8920b90ba8", size = 123304, upload-time = "2026-10-15T09:50:58.343Z" }
268
315
  wheels = [
269
- { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556, upload-time = "2024-04-20T21:34:40.434Z" },
316
+ { url = "https://files.pythonhosted.org/packages/40/9e/2b38731e0fc536806f16490e1a12d7f0dc2a1235aa8cc07bcc75416a7daa/pluggy-1.7.0-py3-none-any.whl", hash = "sha256:7dd7b0d8832ba3cb632c306926ded123429211b83641b35dc5c41ad2d34f9bec", size = 27082, upload-time = "2026-10-15T09:50:56.808Z" },
270
317
  ]
271
318
 
272
319
  [[package]]
@@ -296,6 +343,18 @@ wheels = [
296
343
  { url = "https://files.pythonhosted.org/packages/c2/2f/81d580a0fb83baeb066698975cb14a618bdbed7720678566f1b046a95fe8/pyflakes-3.4.0-py2.py3-none-any.whl", hash = "sha256:f742a7dbd0d9cb9ea41e9a24a918996e8170c799fa528688d40dd582c8265f4f", size = 63551, upload-time = "2025-06-20T18:45:26.937Z" },
297
344
  ]
298
345
 
346
+ [[package]]
347
+ name = "pyproject-api"
348
+ version = "1.12.4"
349
+ source = { registry = "https://pypi.org/simple" }
350
+ dependencies = [
351
+ { name = "packaging" },
352
+ ]
353
+ sdist = { url = "https://files.pythonhosted.org/packages/7b/10/806fcc3ddb672150199bf769af4c90016654829b02f039c3d58f23a08fb7/pyproject_api-1.12.4.tar.gz", hash = "sha256:79d0584a851fc945150715fa7871b0d72f48000e848b2298193d160cc18b3c89", size = 67794, upload-time = "2026-11-04T07:34:27.978Z" }
354
+ wheels = [
355
+ { url = "https://files.pythonhosted.org/packages/2e/32/06e1a59b084b1cebdbb3ffc2b440396c6d43956820684ccd12e6afbdfd14/pyproject_api-1.12.4-py3-none-any.whl", hash = "sha256:5cb6235adb84998753e3b37d50820391854a6a967914b08051fc8f534a1c12b0", size = 14385, upload-time = "2026-11-04T07:34:26.518Z" },
356
+ ]
357
+
299
358
  [[package]]
300
359
  name = "pyright"
301
360
  version = "1.1.407"
@@ -309,6 +368,11 @@ wheels = [
309
368
  { url = "https://files.pythonhosted.org/packages/dc/93/b69052907d032b00c40cb656d21438ec00b3a471733de137a3f65a49a0a0/pyright-1.1.407-py3-none-any.whl", hash = "sha256:6dd419f54fcc13f03b52285796d65e639786373f433e243f8b94cf93a7444d21", size = 5997008, upload-time = "2025-10-24T23:17:13.159Z" },
310
369
  ]
311
370
 
371
+ [package.optional-dependencies]
372
+ nodejs = [
373
+ { name = "nodejs-wheel-binaries" },
374
+ ]
375
+
312
376
  [[package]]
313
377
  name = "pytest"
314
378
  version = "8.3.5"
@@ -349,6 +413,18 @@ wheels = [
349
413
  { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" },
350
414
  ]
351
415
 
416
+ [[package]]
417
+ name = "python-discovery"
418
+ version = "1.9.4"
419
+ source = { registry = "https://pypi.org/simple" }
420
+ dependencies = [
421
+ { name = "filelock" },
422
+ ]
423
+ sdist = { url = "https://files.pythonhosted.org/packages/0f/d7/1e94df7d1636bf00b9f261bf88a4adc0414f14931ff60511eb0f34c250d1/python_discovery-1.9.4.tar.gz", hash = "sha256:c01b45fe515c1d268b57c57d554bb0e1d320b8e9eb5490bb2f4f577be26e4380", size = 140385, upload-time = "2026-11-04T05:25:52.097Z" }
424
+ wheels = [
425
+ { url = "https://files.pythonhosted.org/packages/c5/44/b248f3b1a23f44d8de172e2767748082c41cbd9353009af72f4b11b00afa/python_discovery-1.9.4-py3-none-any.whl", hash = "sha256:b468f365040b40f4cb39918b0766d3e5a2f340968e07df752fbc96f1bb4641ab", size = 44220, upload-time = "2026-11-04T05:25:50.514Z" },
426
+ ]
427
+
352
428
  [[package]]
353
429
  name = "pytokens"
354
430
  version = "0.1.10"
@@ -436,6 +512,61 @@ wheels = [
436
512
  { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575, upload-time = "2021-05-16T22:03:41.177Z" },
437
513
  ]
438
514
 
515
+ [[package]]
516
+ name = "tomli-w"
517
+ version = "1.2.0"
518
+ source = { registry = "https://pypi.org/simple" }
519
+ sdist = { url = "https://files.pythonhosted.org/packages/19/75/241269d1da26b624c0d5e110e8149093c759b7a286138f4efd61a60e75fe/tomli_w-1.2.0.tar.gz", hash = "sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021", size = 7184, upload-time = "2025-01-15T12:07:24.262Z" }
520
+ wheels = [
521
+ { url = "https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90", size = 6675, upload-time = "2025-01-15T12:07:22.074Z" },
522
+ ]
523
+
524
+ [[package]]
525
+ name = "tox"
526
+ version = "4.67.12"
527
+ source = { registry = "https://pypi.org/simple" }
528
+ dependencies = [
529
+ { name = "cachetools" },
530
+ { name = "colorama" },
531
+ { name = "filelock" },
532
+ { name = "packaging" },
533
+ { name = "platformdirs" },
534
+ { name = "pluggy" },
535
+ { name = "pyproject-api" },
536
+ { name = "python-discovery" },
537
+ { name = "tomli-w" },
538
+ { name = "virtualenv" },
539
+ ]
540
+ sdist = { url = "https://files.pythonhosted.org/packages/8c/41/3e3d9dee587244fadd8f4227958a19cf83eb2f5a4df7318fb4e2b19d12bb/tox-4.67.12.tar.gz", hash = "sha256:d94739a6830f8a8b113cc0f95a505bdfd34289a237c70160c8ba785b7fceef6c", size = 424991, upload-time = "2026-11-05T23:31:33.879Z" }
541
+ wheels = [
542
+ { url = "https://files.pythonhosted.org/packages/6f/8d/20c1fd28ed9b899a518bab1908e519454ea383f588b659627c2f0b5c6c07/tox-4.67.12-py3-none-any.whl", hash = "sha256:d3ba259e8e28d3a2c69219d1aa979acfcc6a031a41d4b5ce099667bb47fccbfb", size = 263266, upload-time = "2026-11-05T23:31:31.905Z" },
543
+ ]
544
+
545
+ [[package]]
546
+ name = "tox-uv"
547
+ version = "1.37.5"
548
+ source = { registry = "https://pypi.org/simple" }
549
+ dependencies = [
550
+ { name = "tox-uv-bare" },
551
+ { name = "uv" },
552
+ ]
553
+ wheels = [
554
+ { url = "https://files.pythonhosted.org/packages/a5/e4/425b97dbf64516e376a9675e800be0042e15537dff2cd97ab6cfbbdc9291/tox_uv-1.37.5-py3-none-any.whl", hash = "sha256:d74a5c1937a6d0f7498284c3767f95e2e9c0e28e8e2d9c3a6343c8c8b5905fae", size = 7823, upload-time = "2026-11-04T05:48:59.608Z" },
555
+ ]
556
+
557
+ [[package]]
558
+ name = "tox-uv-bare"
559
+ version = "1.37.5"
560
+ source = { registry = "https://pypi.org/simple" }
561
+ dependencies = [
562
+ { name = "packaging" },
563
+ { name = "tox" },
564
+ ]
565
+ sdist = { url = "https://files.pythonhosted.org/packages/51/4f/d8909e69db5697e3eb41d662fd240ae95287e842362c2e9d5c5ec076890d/tox_uv_bare-1.37.5.tar.gz", hash = "sha256:65026a7964c610c84f30f8e7e9d27f69109bcd0e303e31693015be4e8db568bf", size = 34955, upload-time = "2026-11-04T05:49:02.217Z" }
566
+ wheels = [
567
+ { url = "https://files.pythonhosted.org/packages/70/a6/6585101c4e435c51fe9ca9321562d26322899d99e559465d111699ffaf6d/tox_uv_bare-1.37.5-py3-none-any.whl", hash = "sha256:38a88874153100baf0d2911779e4f928846689a66b0d45fb65dcdb2f0f28ad9d", size = 22793, upload-time = "2026-11-04T05:49:00.778Z" },
568
+ ]
569
+
439
570
  [[package]]
440
571
  name = "typing-extensions"
441
572
  version = "4.13.2"
@@ -444,3 +575,45 @@ sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e3549295
444
575
  wheels = [
445
576
  { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806, upload-time = "2025-04-10T14:19:03.967Z" },
446
577
  ]
578
+
579
+ [[package]]
580
+ name = "uv"
581
+ version = "0.13.7"
582
+ source = { registry = "https://pypi.org/simple" }
583
+ sdist = { url = "https://files.pythonhosted.org/packages/71/73/0021f6679b1b686f2fdb7cb9914a9d953428644e3057b173edc181d1590c/uv-0.13.7.tar.gz", hash = "sha256:5e766791cb825d7a07e2ef9e622d307b48754fb682a31ba7bc5f272fab9ea395", size = 7550003, upload-time = "2026-11-03T02:42:34.944Z" }
584
+ wheels = [
585
+ { url = "https://files.pythonhosted.org/packages/a6/e8/bda02e68c92b896e2c27581843c461e1007cb68c67c0b357d1e4849e6311/uv-0.13.7-py3-none-linux_armv6l.whl", hash = "sha256:89df450624dec8c48b8b5a7adb654928cc50e95753f869c70cd65ef8333f2197", size = 18121202, upload-time = "2026-11-03T02:41:43.776Z" },
586
+ { url = "https://files.pythonhosted.org/packages/3d/5b/e0379e7b172659420b1ec99a8e8229830aa85ea59684847c80b1c959a056/uv-0.13.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:fd04bf01ba5f2829f4f94ca8f0c24a743f21e73d75045613ed6fc2fd66fc7bda", size = 17194673, upload-time = "2026-11-03T02:41:46.959Z" },
587
+ { url = "https://files.pythonhosted.org/packages/40/a5/8756c38ace102fa13fd4e653e8bc40e1f65924f1c0a0d23350339dac97e5/uv-0.13.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f91a229093163eefca89ea1b220a221d64c3beade05d03519a6584d1797a5a5f", size = 14454941, upload-time = "2026-11-03T02:41:50.208Z" },
588
+ { url = "https://files.pythonhosted.org/packages/96/72/da1119f94034d7173e6c8e2cd77a6299744458f0af3cb8e3a04d5cd419d1/uv-0.13.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:a90f5d95716ab83091f0f73a8e7ec64e557abf2a28537fcd719b2ac88e6f725a", size = 17656749, upload-time = "2026-11-03T02:41:52.729Z" },
589
+ { url = "https://files.pythonhosted.org/packages/f3/a8/d554d757e372d2dc8788e4c07601e440c09ce31535e3107180690233f4a2/uv-0.13.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:952dcae9b1a7742f2fab28e82391fbfabe8807e4e9363f674b0f6be1053b8dd4", size = 17815154, upload-time = "2026-11-03T02:41:55.393Z" },
590
+ { url = "https://files.pythonhosted.org/packages/19/db/70ad6aeef83b9566ec1cbfa345a1a976af13413863086aa85809cba16b53/uv-0.13.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:94f65a8ac5826dff76a93cb3cafbaad15def1788c63da2ce5ce09e0bd1303e7a", size = 17827605, upload-time = "2026-11-03T02:41:58.237Z" },
591
+ { url = "https://files.pythonhosted.org/packages/78/55/fafeedc33a903456e7fd5fa442e7116331451c50e68e6fa985df47c06b3a/uv-0.13.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57ff66891d9a738ec087c23fdde842ac8e5eb63189ffed6ee95caee1b82ceb54", size = 18466549, upload-time = "2026-11-03T02:42:01.031Z" },
592
+ { url = "https://files.pythonhosted.org/packages/d0/5e/50494a1cac0ff7030edc430c8ac86ee91ca43c54134d399b0f7a137930c7/uv-0.13.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:917733990ebc178318af0321d6873823ed5da707e1c4b1e5060ac2065a81f1f3", size = 19781452, upload-time = "2026-11-03T02:42:03.787Z" },
593
+ { url = "https://files.pythonhosted.org/packages/28/b2/7fc68c91580168907f557c8824521973a3f63d1e3629ae3478aaeeebef7f/uv-0.13.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1bc8543d930464bbb22ac01bb1a660a448235e558014914de1e1cd5b993361b8", size = 19493895, upload-time = "2026-11-03T02:42:06.604Z" },
594
+ { url = "https://files.pythonhosted.org/packages/b9/93/0e053a2ca4523ee9825ccbedeb7121470d0a188933f11688b6d90d7f5c08/uv-0.13.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c9bdc4a872b60a93c5f26c0791089ea11585a4f9d3b3c017d78eb8487505e25", size = 17202345, upload-time = "2026-11-03T02:42:09.344Z" },
595
+ { url = "https://files.pythonhosted.org/packages/cf/c8/2d0da4535b8607cf224023724cbf8d4572d1a3e33c2c9629100af6b522bb/uv-0.13.7-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:08f16fd463926786b702b497473511deae4b3e17a2fa6d22180950d6059a120d", size = 16636343, upload-time = "2026-11-03T02:42:12.122Z" },
596
+ { url = "https://files.pythonhosted.org/packages/28/3d/e7db43beff8b219ec392346f11b8aad325bc87bb282eea57e64cbe0aba56/uv-0.13.7-py3-none-manylinux_2_31_riscv64.musllinux_1_1_riscv64.whl", hash = "sha256:d30cafb7070901ba35353ec64cf228ae46caf79ac0f6fae329f95468b1482531", size = 18026164, upload-time = "2026-11-03T02:42:14.745Z" },
597
+ { url = "https://files.pythonhosted.org/packages/7d/85/dc25db684d7e8c8c1c6927d78a29b64c7ef5479e8f17c7a2d1b74af4af21/uv-0.13.7-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:158953b8669bee6f4bc2babdd997e34ff9f0d57170093cfe907f64448292b7c3", size = 17911506, upload-time = "2026-11-03T02:42:17.752Z" },
598
+ { url = "https://files.pythonhosted.org/packages/04/c3/0ca099ff442fb6a01a59b7582d8733f187183d491bc86e6f96c9203c3e1e/uv-0.13.7-py3-none-musllinux_1_1_i686.whl", hash = "sha256:8fc6019740794df995085f7b278d99dc11ccacffad9c63957278d3a6e272280b", size = 17946690, upload-time = "2026-11-03T02:42:20.53Z" },
599
+ { url = "https://files.pythonhosted.org/packages/2a/22/065e097ca6051ae7022d7c5c9d1591dcff97ed5609fb9eb450d8ee22fa17/uv-0.13.7-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:8b8ac028ae4ebb2a3f9d04907c61794f2cf8d7a7002bfbb4019a3dee28351fb2", size = 18949870, upload-time = "2026-11-03T02:42:23.574Z" },
600
+ { url = "https://files.pythonhosted.org/packages/c4/f2/3cfb8543acb8dc07b228d2517a0962b124ab97bd859690617d957248129c/uv-0.13.7-py3-none-win32.whl", hash = "sha256:5f37cc4d4660e4aee8a24d0dc179a0b0ce99506259c6796d37fa69bca9543a03", size = 16094323, upload-time = "2026-11-03T02:42:26.697Z" },
601
+ { url = "https://files.pythonhosted.org/packages/8e/4f/041b59d2a38807a3e31ef41064cf3e45d2752fe0409d89040d52778049ae/uv-0.13.7-py3-none-win_amd64.whl", hash = "sha256:2835e2ab397d0db86323b9975dce75ecd76aa9cfa0d6a2aea84734aae7f8c9b3", size = 15330939, upload-time = "2026-11-03T02:42:29.422Z" },
602
+ { url = "https://files.pythonhosted.org/packages/44/8d/1594dce4465d9699467a639b25aafd335ce1e5917f8bb60c9c0bed9e0859/uv-0.13.7-py3-none-win_arm64.whl", hash = "sha256:55e741a380a98008fc4985cf069855f36bc50242ef438e21a466786f5bc84da8", size = 15633037, upload-time = "2026-11-03T02:42:32.095Z" },
603
+ ]
604
+
605
+ [[package]]
606
+ name = "virtualenv"
607
+ version = "21.15.5"
608
+ source = { registry = "https://pypi.org/simple" }
609
+ dependencies = [
610
+ { name = "distlib" },
611
+ { name = "filelock" },
612
+ { name = "packaging" },
613
+ { name = "platformdirs" },
614
+ { name = "python-discovery" },
615
+ ]
616
+ sdist = { url = "https://files.pythonhosted.org/packages/69/74/745e612b69ad14a534b7bc233c4574b968eb2c21a276ea172e8f0fc13f51/virtualenv-21.15.5.tar.gz", hash = "sha256:fb869ea32930530ccc87549877b53268c2a4d4a7d7f2c384042eca05170350e6", size = 5639868, upload-time = "2026-11-04T05:13:20.929Z" }
617
+ wheels = [
618
+ { url = "https://files.pythonhosted.org/packages/9d/d6/f9ac78f115e32e0d76adda9db72b74425b86495ae274cd7b58c660f5d1c1/virtualenv-21.15.5-py3-none-any.whl", hash = "sha256:bcf98c32139cf3df7f0cc2c8c48ca150645c20e66f8c4fe6c1e5e40bcae73947", size = 5680750, upload-time = "2026-11-04T05:13:19.057Z" },
619
+ ]
@@ -1,12 +0,0 @@
1
- * A failed `Predicate` constraint is named by its source when it is a lambda
2
- (`Predicate(lambda s: s.islower())`), by its qualified name otherwise, in `type_error`,
3
- `@record` and the `@annotated` header, where it used to print `<function <lambda> at 0x…>`;
4
- a lambda with default arguments, or whose source cannot be proven to match, falls back to
5
- its qualified name ([#80](https://gitlab.com/leogermond/contractme/-/issues/80)).
6
- * A passing check and every `accepts`/`rejects` probe build no message, unions included:
7
- the text is written only for the failure that gets reported
8
- ([#80](https://gitlab.com/leogermond/contractme/-/issues/80)).
9
- * The `@annotated` header lists its constraints as `under constraints [...]`, where it used
10
- to show a tuple ([#80](https://gitlab.com/leogermond/contractme/-/issues/80)).
11
- * v2.4.0 never reached PyPI (its CI build failed on the in-project uv cache): v2.4.1 ships
12
- its changes too, see below ([5739ee2](https://gitlab.com/leogermond/contractme/-/commit/5739ee2)).
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes