chtypes 0.1.2__tar.gz → 0.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.
- {chtypes-0.1.2 → chtypes-0.2.0}/CHANGELOG.md +20 -1
- {chtypes-0.1.2 → chtypes-0.2.0}/PKG-INFO +1 -1
- {chtypes-0.1.2 → chtypes-0.2.0}/pyproject.toml +3 -3
- {chtypes-0.1.2 → chtypes-0.2.0}/src/chtypes/__init__.py +2 -0
- {chtypes-0.1.2 → chtypes-0.2.0}/src/chtypes/__main__.py +3 -1
- {chtypes-0.1.2 → chtypes-0.2.0}/src/chtypes/_manifest.py +4 -1
- {chtypes-0.1.2 → chtypes-0.2.0}/src/chtypes/_native.py +2 -1
- {chtypes-0.1.2 → chtypes-0.2.0}/src/chtypes/errors.py +7 -1
- {chtypes-0.1.2 → chtypes-0.2.0}/src/chtypes/fetch.py +3 -2
- {chtypes-0.1.2 → chtypes-0.2.0}/src/chtypes/registry.py +3 -2
- {chtypes-0.1.2 → chtypes-0.2.0}/tests/conftest.py +1 -1
- {chtypes-0.1.2 → chtypes-0.2.0}/tests/test_cli.py +4 -1
- {chtypes-0.1.2 → chtypes-0.2.0}/tests/test_fetch.py +3 -3
- {chtypes-0.1.2 → chtypes-0.2.0}/tests/test_parity.py +22 -12
- {chtypes-0.1.2 → chtypes-0.2.0}/tests/test_registry.py +23 -0
- chtypes-0.2.0/uv.lock +108 -0
- chtypes-0.1.2/uv.lock +0 -108
- {chtypes-0.1.2 → chtypes-0.2.0}/.gitignore +0 -0
- {chtypes-0.1.2 → chtypes-0.2.0}/LICENSE +0 -0
- {chtypes-0.1.2 → chtypes-0.2.0}/README.md +0 -0
- {chtypes-0.1.2 → chtypes-0.2.0}/src/chtypes/_document.py +0 -0
- {chtypes-0.1.2 → chtypes-0.2.0}/src/chtypes/_ed25519.py +0 -0
- {chtypes-0.1.2 → chtypes-0.2.0}/src/chtypes/_rawjson.py +0 -0
- {chtypes-0.1.2 → chtypes-0.2.0}/src/chtypes/discover.py +0 -0
- {chtypes-0.1.2 → chtypes-0.2.0}/src/chtypes/py.typed +0 -0
- {chtypes-0.1.2 → chtypes-0.2.0}/src/chtypes/results.py +0 -0
- {chtypes-0.1.2 → chtypes-0.2.0}/src/chtypes/transform.py +0 -0
- {chtypes-0.1.2 → chtypes-0.2.0}/tests/test_ed25519.py +0 -0
- {chtypes-0.1.2 → chtypes-0.2.0}/tests/test_errors.py +0 -0
- {chtypes-0.1.2 → chtypes-0.2.0}/tests/test_golden.py +0 -0
- {chtypes-0.1.2 → chtypes-0.2.0}/tests/test_rawjson.py +0 -0
- {chtypes-0.1.2 → chtypes-0.2.0}/tests/test_transform.py +0 -0
|
@@ -4,6 +4,25 @@ All notable changes to the python binding. The format is [Keep a Changelog](http
|
|
|
4
4
|
|
|
5
5
|
The four bindings in this repository are released together and give one answer, so an entry here has a counterpart in the other three.
|
|
6
6
|
|
|
7
|
+
## [0.2.0] — 2026-09-15
|
|
8
|
+
|
|
9
|
+
### Changed — BREAKING
|
|
10
|
+
|
|
11
|
+
- **The TypeScript filter verdicts are the wire characters.** `Verdict.True` / `False` / `Error` / `Decline` are now `'t'` / `'f'` / `'e'` / `'d'`, which Go, Python and Rust have always rendered. TypeScript alone spelled them `'true'` / `'false'` / `'error'` / `'decline'`, so two SDKs could not share a log line, a fixture or a test. The four states and the fail-closed rule are unchanged: `'e'` and `'d'` are NOT answers, and a caller enforcing visibility must hide the row or fail the request on both — collapsing either into `false` inverts fail-closed into fail-open, the measured leak class. Only the rendered characters moved. **The parity suites now ENFORCE the shared value in all four bindings**; until this release two of the four merely declared it.
|
|
12
|
+
|
|
13
|
+
- **TypeScript `FetchError` now extends `ArtifactError` (and so `RegistryError`), where it previously extended `ChtypesError` directly.** A `try`/`catch` chain that tests `RegistryError` before `FetchError` now takes the `RegistryError` arm for the five fetch verdicts, which it did not before. Ordering a catch chain most-specific-first is unaffected.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- **Load-time checksum verification in Go and Rust**, matching what Python and TypeScript have always offered: re-hash the library against `manifest.json`'s `library_sha256` **before `dlopen`**, and refuse the load on a mismatch. Off by default in all four. Whether an artifact was re-hashed before being mapped used to depend on which binding a consumer picked, which is a security posture and not an API spelling. A manifest carrying no `library_sha256` is refused by Go, TypeScript and Rust — verification asked for and not possible is not verification — while Python's returns silently, which is what remains of issue #13's item A2.
|
|
18
|
+
- **One catchable artifact error in TypeScript.** `ArtifactError` is a new exported base: all six artifact conditions now extend it, where `ArtifactMissingError` previously sat under `RegistryError` and the other five under `FetchError`, so a caller had to catch two unrelated types. Every existing `code` value and message is unchanged, and `ArtifactMissingError instanceof RegistryError` still holds.
|
|
19
|
+
- **Eight constants that three bindings exported and the fourth did not**, so a caller no longer retypes a literal the other SDKs hand them: `Registry.SearchPath()` (go), `parse_signature_file` and `FETCH_COMMAND` (python), `RELEASE_KEY_ID` and `DEFAULT_LOCK_FILE` (ts), `fetch::LOCK_SCHEMA` (rust), and `CHTYPES_REGISTRY` / `CHTYPES_AUTOFETCH` as `EnvRegistry` / `EnvAutoFetch` (go) and `ENV_REGISTRY` / `ENV_AUTOFETCH` (ts). Each new name replaces the inline literal in its own binding's code path, so there is one definition rather than two.
|
|
20
|
+
|
|
21
|
+
### Notes
|
|
22
|
+
|
|
23
|
+
- Speaks **ABI revision 4**, unchanged since 0.1.0, so no artifact needs relinking. The golden set this release was tested against is the one core serves, generated on the ClickHouse lines 24.8, 25.3, 25.8, 25.10, 26.2, 26.3, 26.4, 26.5, 26.6, 26.7 and 26.8.
|
|
24
|
+
- `Library` still has no scope-based release in Python or TypeScript, while `Registry`, `Schema`, `Filter` and `Block` do. That asymmetry is deliberate — a `with library:` or `using library` closes at the end of a block, which is the mid-lifecycle teardown measured to segfault on the next open — and it is now recorded in the parity manifest so no future audit reopens it as a gap.
|
|
25
|
+
|
|
7
26
|
## [0.1.2] — 2026-09-11
|
|
8
27
|
|
|
9
28
|
### Changed — BREAKING
|
|
@@ -14,7 +33,7 @@ The four bindings in this repository are released together and give one answer,
|
|
|
14
33
|
### Changed
|
|
15
34
|
|
|
16
35
|
- **The per-language README is now an orientation page, not a manual.** It carries install, a quickstart that runs, the three outcomes and the error model, and then points at `docs/` for the rest — roughly 450 lines of reference prose moved to `docs/guides/` and `docs/reference/` rather than being repeated four times and drifting four ways.
|
|
17
|
-
- **The published packages no longer cite a repository you cannot open.** 148 source comments — in every binding and in `include/chtypes.h`, all of which ship inside the crate, the sdist and the module — attributed the contract they implement to a private repository's specification. They now cite **the C ABI contract**, with `include/chtypes.h` as the public authority for it. Alongside those, 14 dead paths into that repository, the examples' instruction to build an artifact there, and two descriptions of the signing infrastructure are gone. What remains
|
|
36
|
+
- **The published packages no longer cite a repository you cannot open.** 148 source comments — in every binding and in `include/chtypes.h`, all of which ship inside the crate, the sdist and the module — attributed the contract they implement to a private repository's specification. They now cite **the C ABI contract**, with `include/chtypes.h` as the public authority for it. Alongside those, 14 dead paths into that repository, the examples' instruction to build an artifact there, and two descriptions of the signing infrastructure are gone. What remains is every place where the two-repository split is itself the subject — describing it in prose is honest and stays legal; naming something a reader cannot open is not, and `scripts/lint-public.sh` is what enforces the difference. (An earlier draft of this entry enumerated three files. That count was never checked and was wrong by eighteen, which is the same mistake the entry above is about: the rule is checkable, a file list is not.)
|
|
18
37
|
- `scripts/lint-public.sh` is new and blocking in CI, because the sweep this replaces reported itself complete while 148 of its targets survived in a different shape. It refuses both the private repository's name and that exact prose pointer, and its `--selftest` proves each rule fires rather than assuming it.
|
|
19
38
|
|
|
20
39
|
## [0.1.1] — 2026-09-11
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: chtypes
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Python binding for the chtypes artifacts: ClickHouse's own type system, per version, behind the frozen chs_* C ABI
|
|
5
5
|
Project-URL: Homepage, https://github.com/wave-rf/chtypes
|
|
6
6
|
Project-URL: Repository, https://github.com/wave-rf/chtypes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "chtypes"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.2.0"
|
|
4
4
|
description = "Python binding for the chtypes artifacts: ClickHouse's own type system, per version, behind the frozen chs_* C ABI"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "Apache-2.0"
|
|
@@ -54,7 +54,7 @@ src = ["src", "tests"]
|
|
|
54
54
|
# the classes that have actually cost time here — unused imports and names (F),
|
|
55
55
|
# style errors that hide real ones (E), import order drift across 20 files (I),
|
|
56
56
|
# syntax this project's floor already allows (UP), and the mutable-default /
|
|
57
|
-
# loop-variable-capture family (B). `just lint` and
|
|
58
|
-
# both run this, so a finding is a gate failure, not advice.
|
|
57
|
+
# loop-variable-capture family (B). `just lint` and the core repository's
|
|
58
|
+
# static checks both run this, so a finding is a gate failure, not advice.
|
|
59
59
|
[tool.ruff.lint]
|
|
60
60
|
select = ["E", "F", "I", "UP", "B"]
|
|
@@ -64,6 +64,7 @@ from .errors import (
|
|
|
64
64
|
CODE_ARTIFACT_UNTRUSTED,
|
|
65
65
|
CODE_SOURCE_UNREACHABLE,
|
|
66
66
|
CODE_UNSUPPORTED,
|
|
67
|
+
FETCH_COMMAND,
|
|
67
68
|
ArtifactCorruptError,
|
|
68
69
|
ArtifactError,
|
|
69
70
|
ArtifactMissingError,
|
|
@@ -142,6 +143,7 @@ __all__ = [
|
|
|
142
143
|
"DOC_VALUES",
|
|
143
144
|
"ENV_AUTOFETCH",
|
|
144
145
|
"ENV_REGISTRY",
|
|
146
|
+
"FETCH_COMMAND",
|
|
145
147
|
"default_registry_dir",
|
|
146
148
|
"EXPORT_NONE",
|
|
147
149
|
"RELEASE_KEY_ID",
|
|
@@ -76,7 +76,9 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
76
76
|
fetch = sub.add_parser(
|
|
77
77
|
"fetch",
|
|
78
78
|
help="install one or more ClickHouse lines, verified",
|
|
79
|
-
description=
|
|
79
|
+
description=(
|
|
80
|
+
"Install ClickHouse lines through the verification chain (docs/guides/fetch.md §3)."
|
|
81
|
+
),
|
|
80
82
|
)
|
|
81
83
|
fetch.add_argument(
|
|
82
84
|
"lines",
|
|
@@ -111,7 +111,10 @@ def verify_library(version_dir: str | os.PathLike[str]) -> None:
|
|
|
111
111
|
with path.open("rb") as handle:
|
|
112
112
|
for chunk in iter(lambda: handle.read(1 << 20), b""):
|
|
113
113
|
digest.update(chunk)
|
|
114
|
-
|
|
114
|
+
# Case-insensitive: a hex digest is the same digest in either case, and Go
|
|
115
|
+
# and Rust already lower-case before comparing. Comparing raw here made an
|
|
116
|
+
# uppercase manifest digest pass in two bindings and fail in two.
|
|
117
|
+
if digest.hexdigest() != manifest.library_sha256.lower():
|
|
115
118
|
raise RegistryError(
|
|
116
119
|
f"chtypes: {path} sha256 {digest.hexdigest()} != manifest {manifest.library_sha256}"
|
|
117
120
|
)
|
|
@@ -179,7 +179,8 @@ class _RWLock:
|
|
|
179
179
|
|
|
180
180
|
**Readers** are every call that reaches `chs_row`, `chs_rows`,
|
|
181
181
|
`chs_schema_compile`, `chs_schema_engine`, `chs_schema_ttl`,
|
|
182
|
-
`chs_validate_type` and the column accessors. The C ABI contract
|
|
182
|
+
`chs_validate_type` and the column accessors. The C ABI contract
|
|
183
|
+
§Thread-safety declares those safe together **on distinct handles**, so
|
|
183
184
|
per-handle serialization is `Schema`'s own lock and not this one.
|
|
184
185
|
|
|
185
186
|
**The writer** is the pair that mutates per-library PROCESS state —
|
|
@@ -14,6 +14,7 @@ __all__ = [
|
|
|
14
14
|
"CODE_ARTIFACT_UNTRUSTED",
|
|
15
15
|
"CODE_SOURCE_UNREACHABLE",
|
|
16
16
|
"CODE_UNSUPPORTED",
|
|
17
|
+
"FETCH_COMMAND",
|
|
17
18
|
"ArtifactCorruptError",
|
|
18
19
|
"ArtifactError",
|
|
19
20
|
"ArtifactMissingError",
|
|
@@ -70,6 +71,11 @@ CODE_ARTIFACT_PINNED: Final = "CHTYPES_ARTIFACT_PINNED"
|
|
|
70
71
|
CODE_ARTIFACT_UNPUBLISHED: Final = "CHTYPES_ARTIFACT_UNPUBLISHED"
|
|
71
72
|
CODE_SOURCE_UNREACHABLE: Final = "CHTYPES_SOURCE_UNREACHABLE"
|
|
72
73
|
|
|
74
|
+
# This SDK's own fetch command, as the docs/guides/fetch.md §7 "Install it" message
|
|
75
|
+
# spells it. The VALUE is deliberately per-language — it is the command a
|
|
76
|
+
# caller of THIS SDK runs — go/ts/rust each carry their own equivalent.
|
|
77
|
+
FETCH_COMMAND: Final = "python -m chtypes fetch"
|
|
78
|
+
|
|
73
79
|
|
|
74
80
|
class ArtifactError(RegistryError):
|
|
75
81
|
"""An artifact is missing, could not be obtained, or failed verification.
|
|
@@ -123,7 +129,7 @@ class ArtifactMissingError(ArtifactError):
|
|
|
123
129
|
super().__init__(
|
|
124
130
|
f"chtypes: no artifact for ClickHouse {line} ({platform}). "
|
|
125
131
|
f"Looked in: {', '.join(self.looked_in)}.\n"
|
|
126
|
-
f"Install it:
|
|
132
|
+
f"Install it: {FETCH_COMMAND} {line}\n"
|
|
127
133
|
f"or set CHTYPES_AUTOFETCH=1 to fetch on first use."
|
|
128
134
|
)
|
|
129
135
|
|
|
@@ -79,6 +79,7 @@ __all__ = [
|
|
|
79
79
|
"fetch_lines",
|
|
80
80
|
"fetch_destination",
|
|
81
81
|
"installed_lines",
|
|
82
|
+
"parse_signature_file",
|
|
82
83
|
"read_lock",
|
|
83
84
|
"registry_search_path",
|
|
84
85
|
"trusted_keys",
|
|
@@ -258,7 +259,7 @@ def key_id(raw_public_key: bytes) -> str:
|
|
|
258
259
|
return hashlib.sha256(raw_public_key).hexdigest()[:16]
|
|
259
260
|
|
|
260
261
|
|
|
261
|
-
def
|
|
262
|
+
def parse_signature_file(sig: bytes) -> tuple[str, bytes]:
|
|
262
263
|
"""``SHA256SUMS.sig`` -> (the comment line, the 64 signature bytes)."""
|
|
263
264
|
comment = ""
|
|
264
265
|
body: list[str] = []
|
|
@@ -285,7 +286,7 @@ def verify_sums_signature(sums: bytes, sig: bytes, keys: Sequence[bytes], source
|
|
|
285
286
|
"""Step 0 of the chain: the signature over the EXACT bytes of ``SHA256SUMS``
|
|
286
287
|
with one of the trusted keys. Returns the id of the key that verified;
|
|
287
288
|
raises `ArtifactUntrustedError` otherwise."""
|
|
288
|
-
comment, signature =
|
|
289
|
+
comment, signature = parse_signature_file(sig)
|
|
289
290
|
for raw in keys:
|
|
290
291
|
if _ed25519_verify(raw, sums, signature):
|
|
291
292
|
return key_id(raw)
|
|
@@ -370,8 +370,9 @@ class Schema:
|
|
|
370
370
|
`BatchResult.rows` as `Outcome.SKIPPED`, carrying the server's own
|
|
371
371
|
caught error verbatim (2026-08-27).
|
|
372
372
|
|
|
373
|
-
**The revision-3 export channel** (the C ABI contract §Rows
|
|
374
|
-
|
|
373
|
+
**The revision-3 export channel** (the C ABI contract §Rows, for
|
|
374
|
+
which `include/chtypes.h` is the public authority) — still ONE
|
|
375
|
+
`chs_rows` call, never a
|
|
375
376
|
second, never re-parsing:
|
|
376
377
|
|
|
377
378
|
`export` is None (no bytes — today's path, byte-identical documents)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
not run at all — it skips, loudly, by name. The fetch, CLI and pure-Python
|
|
3
3
|
tests need none and always run; that is what this repository's CI proves on
|
|
4
4
|
hosted runners, and the artifact-backed proof is the core repository's
|
|
5
|
-
|
|
5
|
+
server-truth suites against this same tree.
|
|
6
6
|
|
|
7
7
|
The registry comes from the search path (docs/guides/fetch.md §1): `$CHTYPES_REGISTRY`,
|
|
8
8
|
else the per-user artifact cache for this host (`chtypes.default_registry_dir()`:
|
|
@@ -23,7 +23,10 @@ PLATFORM = "linux-arm64"
|
|
|
23
23
|
|
|
24
24
|
needs_fixtures = pytest.mark.skipif(
|
|
25
25
|
not EXPECTED_FILE.is_file(),
|
|
26
|
-
reason=
|
|
26
|
+
reason=(
|
|
27
|
+
f"no fetch fixtures at {FIXTURES} "
|
|
28
|
+
"(generated in the core repository, docs/guides/fetch.md §9)"
|
|
29
|
+
),
|
|
27
30
|
)
|
|
28
31
|
|
|
29
32
|
|
|
@@ -504,7 +504,7 @@ def test_trusted_keys_policy(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
|
504
504
|
|
|
505
505
|
def test_signature_file_shape() -> None:
|
|
506
506
|
sig = (FIXTURES / "signed" / "SHA256SUMS.sig").read_bytes()
|
|
507
|
-
comment, raw = fetch_module.
|
|
507
|
+
comment, raw = fetch_module.parse_signature_file(sig)
|
|
508
508
|
assert comment == f"chtypes artifacts, ed25519 key {_expected()['key_id']}"
|
|
509
509
|
assert len(raw) == 64
|
|
510
510
|
sums = (FIXTURES / "signed" / "SHA256SUMS").read_bytes()
|
|
@@ -517,9 +517,9 @@ def test_signature_file_shape() -> None:
|
|
|
517
517
|
sums + b"\n", sig, fetch_module.trusted_keys(_test_keys()), "x"
|
|
518
518
|
)
|
|
519
519
|
with pytest.raises(chtypes.ArtifactUntrustedError, match="base64"):
|
|
520
|
-
fetch_module.
|
|
520
|
+
fetch_module.parse_signature_file(b"untrusted comment: x\n!!!\n")
|
|
521
521
|
with pytest.raises(chtypes.ArtifactUntrustedError, match="64"):
|
|
522
|
-
fetch_module.
|
|
522
|
+
fetch_module.parse_signature_file(b"untrusted comment: x\nAAAA\n")
|
|
523
523
|
|
|
524
524
|
|
|
525
525
|
# ------------------------------------------------------------- tar safety
|
|
@@ -127,7 +127,9 @@ def test_every_capability_is_fully_declared() -> None:
|
|
|
127
127
|
problems.append(f"{cid}: duplicate id")
|
|
128
128
|
seen.add(cid)
|
|
129
129
|
if not cap.get("what", "").strip():
|
|
130
|
-
problems.append(
|
|
130
|
+
problems.append(
|
|
131
|
+
f"{cid}: no `what` — a capability with no description is a name, not a contract"
|
|
132
|
+
)
|
|
131
133
|
if cap.get("group") not in MANIFEST_DOC["groups"]:
|
|
132
134
|
problems.append(f"{cid}: unknown group {cap.get('group')!r}")
|
|
133
135
|
|
|
@@ -135,7 +137,9 @@ def test_every_capability_is_fully_declared() -> None:
|
|
|
135
137
|
for lang in MANIFEST_DOC["languages"]:
|
|
136
138
|
col = column(cap, lang)
|
|
137
139
|
if not col:
|
|
138
|
-
problems.append(
|
|
140
|
+
problems.append(
|
|
141
|
+
f"{cid}: no {lang} column at all — declare a spelling or an absence"
|
|
142
|
+
)
|
|
139
143
|
continue
|
|
140
144
|
if "absent" in col:
|
|
141
145
|
absent += 1
|
|
@@ -148,7 +152,9 @@ def test_every_capability_is_fully_declared() -> None:
|
|
|
148
152
|
problems.append(f"{cid}: {lang} column has neither `symbol` nor `absent`")
|
|
149
153
|
if absent == len(MANIFEST_DOC["languages"]):
|
|
150
154
|
problems.append(f"{cid}: absent in every binding — that is a note, not a contract")
|
|
151
|
-
assert not problems, "the parity manifest is not internally consistent:\n " + "\n ".join(
|
|
155
|
+
assert not problems, "the parity manifest is not internally consistent:\n " + "\n ".join(
|
|
156
|
+
problems
|
|
157
|
+
)
|
|
152
158
|
|
|
153
159
|
|
|
154
160
|
def test_the_go_copy_of_the_manifest_is_byte_identical() -> None:
|
|
@@ -162,7 +168,8 @@ def test_the_go_copy_of_the_manifest_is_byte_identical() -> None:
|
|
|
162
168
|
if GO_COPY.read_bytes() != MANIFEST.read_bytes():
|
|
163
169
|
pytest.fail(
|
|
164
170
|
f"{GO_COPY} has drifted from {MANIFEST}. The manifest is one file; this is its "
|
|
165
|
-
f"embedded copy. Re-sync it:\n\n
|
|
171
|
+
f"embedded copy. Re-sync it:\n\n "
|
|
172
|
+
f"cp tests/parity/manifest.json go/chtypes/testdata/parity.json\n"
|
|
166
173
|
)
|
|
167
174
|
|
|
168
175
|
|
|
@@ -285,7 +292,8 @@ def test_the_cli_offers_every_contract_subcommand() -> None:
|
|
|
285
292
|
"""`chtypes fetch|verify|list|where` is a surface too, and it drifted once."""
|
|
286
293
|
source = (SRC / "__main__.py").read_text(encoding="utf-8")
|
|
287
294
|
missing = [
|
|
288
|
-
f"{cap['id']}: the python CLI has no `{cap['value']}` subcommand,
|
|
295
|
+
f"{cap['id']}: the python CLI has no `{cap['value']}` subcommand, "
|
|
296
|
+
f"which {also_in(cap)} offer"
|
|
289
297
|
for cap in CAPABILITIES
|
|
290
298
|
if cap["kind"] == "cli" and spelling(cap, LANG) and f'"{cap["value"]}"' not in source
|
|
291
299
|
]
|
|
@@ -334,8 +342,8 @@ def test_no_public_name_escapes_the_contract() -> None:
|
|
|
334
342
|
assert not undeclared, (
|
|
335
343
|
f"python exports {len(undeclared)} public name(s) the parity contract has never heard "
|
|
336
344
|
f"of:\n " + "\n ".join(undeclared) + "\n\nEither give each one a capability in "
|
|
337
|
-
|
|
338
|
-
|
|
345
|
+
"tests/parity/manifest.json (if the other bindings should have it too) or list it under "
|
|
346
|
+
"`unlisted.python` with a reason (if it is deliberately ours alone)."
|
|
339
347
|
)
|
|
340
348
|
|
|
341
349
|
|
|
@@ -397,8 +405,9 @@ def test_the_manifest_and_the_spec_table_agree() -> None:
|
|
|
397
405
|
# assertion is about which COLUMNS exist, not about their alignment.
|
|
398
406
|
squashed = re.sub(r"[ \t]+", " ", full)
|
|
399
407
|
assert "| Concept | Go | Python | TypeScript | Rust |" in squashed, (
|
|
400
|
-
"docs/reference/bindings.md's object-model table has lost a language
|
|
401
|
-
"SDK over the same C ABI and the
|
|
408
|
+
"docs/reference/bindings.md's object-model table has lost a language "
|
|
409
|
+
"column. Every language is a peer SDK over the same C ABI and the "
|
|
410
|
+
"document says so in its own second paragraph; a table with "
|
|
402
411
|
"three of the four columns is how Rust came to be described only in a Notes cell."
|
|
403
412
|
)
|
|
404
413
|
|
|
@@ -420,10 +429,11 @@ def test_the_manifest_and_the_spec_table_agree() -> None:
|
|
|
420
429
|
undocumented.append(f"{cap['id']}: {lang} `{spelled}` — {cap['what']}")
|
|
421
430
|
assert checked > 0, "no object-model spelling was compared against docs/reference/bindings.md"
|
|
422
431
|
assert not undocumented, (
|
|
423
|
-
f"the parity manifest requires {len(undocumented)} spelling(s) that
|
|
432
|
+
f"the parity manifest requires {len(undocumented)} spelling(s) that "
|
|
433
|
+
f"docs/reference/bindings.md never "
|
|
424
434
|
f"names:\n " + "\n ".join(undocumented) + "\n\nEither document them there, or — if the "
|
|
425
|
-
|
|
426
|
-
|
|
435
|
+
"capability should not be required at all — remove it from the manifest. The two are one "
|
|
436
|
+
"contract stated twice."
|
|
427
437
|
)
|
|
428
438
|
|
|
429
439
|
|
|
@@ -178,6 +178,29 @@ def test_manifest_ignores_unknown_fields(tmp_path: Path) -> None:
|
|
|
178
178
|
assert manifest.library == "libchtypes.so"
|
|
179
179
|
|
|
180
180
|
|
|
181
|
+
def test_verify_library_accepts_an_uppercase_digest(tmp_path: Path) -> None:
|
|
182
|
+
"""A hex digest is the same digest in either case.
|
|
183
|
+
|
|
184
|
+
Go lower-cases (`strings.ToLower`) and Rust lower-cases
|
|
185
|
+
(`.to_ascii_lowercase()`) before comparing; Python and TypeScript once
|
|
186
|
+
compared raw, so an uppercase `library_sha256` was accepted by two
|
|
187
|
+
bindings and refused by two. docs/reference/artifact.md asserts all four
|
|
188
|
+
behave alike, so this pins it.
|
|
189
|
+
"""
|
|
190
|
+
payload = b"not really a library"
|
|
191
|
+
(tmp_path / "fake.so").write_bytes(payload)
|
|
192
|
+
(tmp_path / "manifest.json").write_text(
|
|
193
|
+
json.dumps(
|
|
194
|
+
{
|
|
195
|
+
"library": "fake.so",
|
|
196
|
+
"library_bytes": len(payload),
|
|
197
|
+
"library_sha256": hashlib.sha256(payload).hexdigest().upper(),
|
|
198
|
+
}
|
|
199
|
+
)
|
|
200
|
+
)
|
|
201
|
+
chtypes.verify_library(tmp_path) # must NOT raise
|
|
202
|
+
|
|
203
|
+
|
|
181
204
|
def test_verify_library_checks_the_bytes(tmp_path: Path) -> None:
|
|
182
205
|
payload = b"not really a library"
|
|
183
206
|
(tmp_path / "fake.so").write_bytes(payload)
|
chtypes-0.2.0/uv.lock
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
version = 1
|
|
2
|
+
revision = 3
|
|
3
|
+
requires-python = ">=3.11"
|
|
4
|
+
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "chtypes"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
source = { editable = "." }
|
|
9
|
+
|
|
10
|
+
[package.dev-dependencies]
|
|
11
|
+
dev = [
|
|
12
|
+
{ name = "pytest" },
|
|
13
|
+
{ name = "ruff" },
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
[package.metadata]
|
|
17
|
+
|
|
18
|
+
[package.metadata.requires-dev]
|
|
19
|
+
dev = [
|
|
20
|
+
{ name = "pytest", specifier = ">=8.0" },
|
|
21
|
+
{ name = "ruff", specifier = ">=0.14" },
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[[package]]
|
|
25
|
+
name = "colorama"
|
|
26
|
+
version = "0.4.6"
|
|
27
|
+
source = { registry = "https://pypi.org/simple" }
|
|
28
|
+
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
|
|
29
|
+
wheels = [
|
|
30
|
+
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[[package]]
|
|
34
|
+
name = "iniconfig"
|
|
35
|
+
version = "2.3.0"
|
|
36
|
+
source = { registry = "https://pypi.org/simple" }
|
|
37
|
+
sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
|
|
38
|
+
wheels = [
|
|
39
|
+
{ url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[[package]]
|
|
43
|
+
name = "packaging"
|
|
44
|
+
version = "26.3"
|
|
45
|
+
source = { registry = "https://pypi.org/simple" }
|
|
46
|
+
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" }
|
|
47
|
+
wheels = [
|
|
48
|
+
{ 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" },
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
[[package]]
|
|
52
|
+
name = "pluggy"
|
|
53
|
+
version = "1.6.0"
|
|
54
|
+
source = { registry = "https://pypi.org/simple" }
|
|
55
|
+
sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
|
|
56
|
+
wheels = [
|
|
57
|
+
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
[[package]]
|
|
61
|
+
name = "pygments"
|
|
62
|
+
version = "2.21.0"
|
|
63
|
+
source = { registry = "https://pypi.org/simple" }
|
|
64
|
+
sdist = { url = "https://files.pythonhosted.org/packages/49/2e/ced460408999b33da6b31b0021b0f37d329e202d4169aeb164493778f25b/pygments-2.21.0.tar.gz", hash = "sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c", size = 5005329, upload-time = "2026-08-17T08:02:48.824Z" }
|
|
65
|
+
wheels = [
|
|
66
|
+
{ url = "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl", hash = "sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", size = 1250147, upload-time = "2026-08-17T08:02:44.912Z" },
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
[[package]]
|
|
70
|
+
name = "pytest"
|
|
71
|
+
version = "9.1.1"
|
|
72
|
+
source = { registry = "https://pypi.org/simple" }
|
|
73
|
+
dependencies = [
|
|
74
|
+
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
|
75
|
+
{ name = "iniconfig" },
|
|
76
|
+
{ name = "packaging" },
|
|
77
|
+
{ name = "pluggy" },
|
|
78
|
+
{ name = "pygments" },
|
|
79
|
+
]
|
|
80
|
+
sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" }
|
|
81
|
+
wheels = [
|
|
82
|
+
{ url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" },
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
[[package]]
|
|
86
|
+
name = "ruff"
|
|
87
|
+
version = "0.16.7"
|
|
88
|
+
source = { registry = "https://pypi.org/simple" }
|
|
89
|
+
sdist = { url = "https://files.pythonhosted.org/packages/82/bb/5a449b9162e49b139d72f61672bd3ac1d790221f796d3304e2241fff4c58/ruff-0.16.7.tar.gz", hash = "sha256:5f71d004ac1263b22fa39462ac5ae618a4b77d58981af2cc79bf79a29c12b1a6", size = 4924184, upload-time = "2026-09-10T18:04:06.336Z" }
|
|
90
|
+
wheels = [
|
|
91
|
+
{ url = "https://files.pythonhosted.org/packages/e3/b2/c80aeeb7f9e469c0d63a85d2f1ab6e1ebfbe10ea7a8d2438b7e09e3ff09e/ruff-0.16.7-py3-none-linux_armv6l.whl", hash = "sha256:727307773e7c7f9181d3ed3a2484186e56c1fa1874255911c74585eb2c7c19f9", size = 10048917, upload-time = "2026-09-10T18:03:30.28Z" },
|
|
92
|
+
{ url = "https://files.pythonhosted.org/packages/7b/96/20bb7bcae008004df52afcb7ac83432d4a467f2c17b672fe46d26be231c5/ruff-0.16.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9d61c258deabf58f34c67bd4bb4d939c7f2e6b5f0e59c1cdd1cf771b11cde929", size = 10242929, upload-time = "2026-09-10T18:03:32.706Z" },
|
|
93
|
+
{ url = "https://files.pythonhosted.org/packages/90/b2/f184b0d5abec02db69cfd7e49b688ae0237554528ca777136c613bf36bee/ruff-0.16.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7ab81118df8945e0193d0240712aa4496573595b75185c3636ed825592a0f728", size = 9847245, upload-time = "2026-09-10T18:03:34.509Z" },
|
|
94
|
+
{ url = "https://files.pythonhosted.org/packages/eb/2d/db1633a641866ed801e34cc6b60ef236c5e16f9b2124ab1d49cc24a5fe4f/ruff-0.16.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c196c968874fc8019da8e7163de7a1a370f111e2309b4b7dfea0fce950198d0", size = 9961780, upload-time = "2026-09-10T18:03:36.618Z" },
|
|
95
|
+
{ url = "https://files.pythonhosted.org/packages/4d/98/edea21e1a3e38dbbc3bf6bb068b863b3b06184cf8533a4c7dbbe208a89d5/ruff-0.16.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac8c3bd0a7e10ad31e6ce51e7a99f3cb772e69aecdd6b9ea7e99b362f62a62c0", size = 9866337, upload-time = "2026-09-10T18:03:38.805Z" },
|
|
96
|
+
{ url = "https://files.pythonhosted.org/packages/0b/11/a15e60d4c87b214646f116ca9d204475bf993ee1047459bc9a360fd4d6d1/ruff-0.16.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:398d3988edde000b5c75dc1b3f584708da9bc990de069c18909142580fec1af9", size = 10562512, upload-time = "2026-09-10T18:03:40.71Z" },
|
|
97
|
+
{ url = "https://files.pythonhosted.org/packages/29/42/eaff4c9b6d0c7cdf56df313a17e89ae854f5bbc0b0c8f9cce19be0ab7a8f/ruff-0.16.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce05b62b770a8217c4646a9c4139fca00efe8fe5d71f87df2b243ff20d4584d1", size = 11302938, upload-time = "2026-09-10T18:03:42.607Z" },
|
|
98
|
+
{ url = "https://files.pythonhosted.org/packages/5d/43/c75aa59a4ec181fe2ec06cab30e198c1c6d107229a9f008ae3a7c16cabd8/ruff-0.16.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af1b576fddb9d9ef2ececfb5fadcd6a624b25070ed85e3cfcfe449fc3ff6a7b9", size = 10840857, upload-time = "2026-09-10T18:03:44.604Z" },
|
|
99
|
+
{ url = "https://files.pythonhosted.org/packages/21/33/81f3da371942ea031105ba679d8d6e28ec1660ccd690a45f42d381161356/ruff-0.16.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ce7f8f22df67c93ed96c717f9128eadb797144ac2bad475cf536f31d6100c55", size = 10370001, upload-time = "2026-09-10T18:03:46.706Z" },
|
|
100
|
+
{ url = "https://files.pythonhosted.org/packages/fa/0b/6345fb4dbf6dd0ed1cfe5d18391dc9c3f59cc81622a7b0a65b84b3e730ba/ruff-0.16.7-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:06d0e93d04f392996435ebd600c153f65b47d73fbec2415aa99c5ee5756b3a5f", size = 10548735, upload-time = "2026-09-10T18:03:48.658Z" },
|
|
101
|
+
{ url = "https://files.pythonhosted.org/packages/3f/4d/c5576adf511f92a328e5569dda190ecdd430da51f1a649f3a4a2fd73e21e/ruff-0.16.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:142151a5e7b93c1b11111337142f89dd2fbfee92161225c99a97222f22e32656", size = 10108496, upload-time = "2026-09-10T18:03:50.563Z" },
|
|
102
|
+
{ url = "https://files.pythonhosted.org/packages/ff/8c/667d83c16199a17a56adc6b0bd4c3beb5b767a2babcd16a56f76f9be7fd6/ruff-0.16.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:e6651f97a342d8b35d54d8991544ca22169b86dc54111cb604666940c431b750", size = 9860136, upload-time = "2026-09-10T18:03:52.621Z" },
|
|
103
|
+
{ url = "https://files.pythonhosted.org/packages/99/75/78d401106731999a1dd20cc5a6961e37e1eb9397a3b589f73f3a5ce146a3/ruff-0.16.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ef140c6eb935fa9a84c9c607dfb2cb1b85843c192e79265b0c54f35f557ea8e5", size = 10286290, upload-time = "2026-09-10T18:03:55.207Z" },
|
|
104
|
+
{ url = "https://files.pythonhosted.org/packages/68/49/56f9c3a8b755df93a0ad318b2147bf4ef5dae9a7e5ec61c460109c67957f/ruff-0.16.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:53e39506a730fadeee0d998ed5946f30671f0db240c6c7c73bdabbe33604bb6f", size = 10745048, upload-time = "2026-09-10T18:03:57.299Z" },
|
|
105
|
+
{ url = "https://files.pythonhosted.org/packages/5f/ea/7f9b938a63ece4bec677ad7f9f7fa02df3383db1949ed93a382441c09a87/ruff-0.16.7-py3-none-win32.whl", hash = "sha256:2ea3470fcebcbc5df2fb0c6f3b90333fa9084c534e0111c038fa4a6ab9f1c4b7", size = 10059082, upload-time = "2026-09-10T18:03:59.632Z" },
|
|
106
|
+
{ url = "https://files.pythonhosted.org/packages/39/11/480a6973a927aa653e1cead6a6416008640e03a99d05b34c0434b8c6c366/ruff-0.16.7-py3-none-win_amd64.whl", hash = "sha256:7ac26aca826e9e21d0f1cb25b54ac660760a9fdd094d3e4df9848232be98cfc6", size = 10593368, upload-time = "2026-09-10T18:04:01.999Z" },
|
|
107
|
+
{ url = "https://files.pythonhosted.org/packages/8b/4b/51327018d056f0dad2c2238f26d1fb0f53707a9d91b75dea6d1b3039f136/ruff-0.16.7-py3-none-win_arm64.whl", hash = "sha256:aab7f39e2c9df6c596216070f98eef1207b94f8516cca20c808826974971855b", size = 10412401, upload-time = "2026-09-10T18:04:04.098Z" },
|
|
108
|
+
]
|
chtypes-0.1.2/uv.lock
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
version = 1
|
|
2
|
-
revision = 3
|
|
3
|
-
requires-python = ">=3.11"
|
|
4
|
-
|
|
5
|
-
[[package]]
|
|
6
|
-
name = "chtypes"
|
|
7
|
-
version = "0.1.2"
|
|
8
|
-
source = { editable = "." }
|
|
9
|
-
|
|
10
|
-
[package.dev-dependencies]
|
|
11
|
-
dev = [
|
|
12
|
-
{ name = "pytest" },
|
|
13
|
-
{ name = "ruff" },
|
|
14
|
-
]
|
|
15
|
-
|
|
16
|
-
[package.metadata]
|
|
17
|
-
|
|
18
|
-
[package.metadata.requires-dev]
|
|
19
|
-
dev = [
|
|
20
|
-
{ name = "pytest", specifier = ">=8.0" },
|
|
21
|
-
{ name = "ruff", specifier = ">=0.14" },
|
|
22
|
-
]
|
|
23
|
-
|
|
24
|
-
[[package]]
|
|
25
|
-
name = "colorama"
|
|
26
|
-
version = "0.4.6"
|
|
27
|
-
source = { registry = "https://pypi.org/simple" }
|
|
28
|
-
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
|
|
29
|
-
wheels = [
|
|
30
|
-
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
|
31
|
-
]
|
|
32
|
-
|
|
33
|
-
[[package]]
|
|
34
|
-
name = "iniconfig"
|
|
35
|
-
version = "2.3.0"
|
|
36
|
-
source = { registry = "https://pypi.org/simple" }
|
|
37
|
-
sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
|
|
38
|
-
wheels = [
|
|
39
|
-
{ url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
|
|
40
|
-
]
|
|
41
|
-
|
|
42
|
-
[[package]]
|
|
43
|
-
name = "packaging"
|
|
44
|
-
version = "26.3"
|
|
45
|
-
source = { registry = "https://pypi.org/simple" }
|
|
46
|
-
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" }
|
|
47
|
-
wheels = [
|
|
48
|
-
{ 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" },
|
|
49
|
-
]
|
|
50
|
-
|
|
51
|
-
[[package]]
|
|
52
|
-
name = "pluggy"
|
|
53
|
-
version = "1.6.0"
|
|
54
|
-
source = { registry = "https://pypi.org/simple" }
|
|
55
|
-
sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
|
|
56
|
-
wheels = [
|
|
57
|
-
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
|
|
58
|
-
]
|
|
59
|
-
|
|
60
|
-
[[package]]
|
|
61
|
-
name = "pygments"
|
|
62
|
-
version = "2.21.0"
|
|
63
|
-
source = { registry = "https://pypi.org/simple" }
|
|
64
|
-
sdist = { url = "https://files.pythonhosted.org/packages/49/2e/ced460408999b33da6b31b0021b0f37d329e202d4169aeb164493778f25b/pygments-2.21.0.tar.gz", hash = "sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c", size = 5005329, upload-time = "2026-08-17T08:02:48.824Z" }
|
|
65
|
-
wheels = [
|
|
66
|
-
{ url = "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl", hash = "sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", size = 1250147, upload-time = "2026-08-17T08:02:44.912Z" },
|
|
67
|
-
]
|
|
68
|
-
|
|
69
|
-
[[package]]
|
|
70
|
-
name = "pytest"
|
|
71
|
-
version = "9.1.1"
|
|
72
|
-
source = { registry = "https://pypi.org/simple" }
|
|
73
|
-
dependencies = [
|
|
74
|
-
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
|
75
|
-
{ name = "iniconfig" },
|
|
76
|
-
{ name = "packaging" },
|
|
77
|
-
{ name = "pluggy" },
|
|
78
|
-
{ name = "pygments" },
|
|
79
|
-
]
|
|
80
|
-
sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" }
|
|
81
|
-
wheels = [
|
|
82
|
-
{ url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" },
|
|
83
|
-
]
|
|
84
|
-
|
|
85
|
-
[[package]]
|
|
86
|
-
name = "ruff"
|
|
87
|
-
version = "0.16.6"
|
|
88
|
-
source = { registry = "https://pypi.org/simple" }
|
|
89
|
-
sdist = { url = "https://files.pythonhosted.org/packages/a4/7c/6adb35d70e7c027e308274557901c7e00fb3407750faf3620c184ae058cb/ruff-0.16.6.tar.gz", hash = "sha256:dcf8a73d2ff77e99dde91244b4da16feba7f14e6beeb4015dee7c5a909e99050", size = 4921251, upload-time = "2026-09-03T16:57:29.037Z" }
|
|
90
|
-
wheels = [
|
|
91
|
-
{ url = "https://files.pythonhosted.org/packages/a4/28/9cc1b79639e284ec103f43c88c644db4eb58cbd0ea1ca11f1193435369ac/ruff-0.16.6-py3-none-linux_armv6l.whl", hash = "sha256:61c368c26bf8e973e5ab14a2772de587bc068ea3f9a277f673380749b4898fb8", size = 10015638, upload-time = "2026-09-03T16:56:40.986Z" },
|
|
92
|
-
{ url = "https://files.pythonhosted.org/packages/71/11/627d342ef727ea7794edf74fe23d60a074b02c3acc2e9436684e782286ca/ruff-0.16.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ecf4f068e2e123e43a26e9db4e19524cc56563912404e83bbfca375757e45a32", size = 10220762, upload-time = "2026-09-03T16:56:44.681Z" },
|
|
93
|
-
{ url = "https://files.pythonhosted.org/packages/43/d9/b75668ce41e4c8d073d18d6d08672ba6906ce45d5c06ea4fdb2e84ce3853/ruff-0.16.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:99b62ea33baf130f50368798d841f0d95527b6d817bf31817b65dd058f1d314c", size = 9835082, upload-time = "2026-09-03T16:56:47.142Z" },
|
|
94
|
-
{ url = "https://files.pythonhosted.org/packages/99/97/123ab10b05cde889c107c20f5a9774955104b5552796a2a8584b089ae8eb/ruff-0.16.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7fbf89013f2bb3f6835a6038ff658dc8a1b38c98dc8e724b964168ad4e881876", size = 9949304, upload-time = "2026-09-03T16:56:49.813Z" },
|
|
95
|
-
{ url = "https://files.pythonhosted.org/packages/3e/58/a4a2c59dd2e5b85929c912d9cac3056eb9ee8c7e75e9b9fe3e109174966b/ruff-0.16.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:56a67065e22efa6bc4d498299d3bb06c0c90aace8fac2068b5a12f9dc4d8d51d", size = 9840612, upload-time = "2026-09-03T16:56:52.368Z" },
|
|
96
|
-
{ url = "https://files.pythonhosted.org/packages/61/6a/ff8c8626a786c4f49d48ced4a752dadbca65f5263005f9c2416578194694/ruff-0.16.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e25cc89174874b176a157e4428d66761c2c0c006654419bf384f967f361ff1b1", size = 10543465, upload-time = "2026-09-03T16:56:55.089Z" },
|
|
97
|
-
{ url = "https://files.pythonhosted.org/packages/ad/bb/c47535923365f337b82e28192e4e9eef2176511007cfd99a62fc22df5dad/ruff-0.16.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0700580ed5303723cb3c11c2f1d2a8913ce77b7ea86646dddb887f5417a9ba70", size = 11267576, upload-time = "2026-09-03T16:56:57.791Z" },
|
|
98
|
-
{ url = "https://files.pythonhosted.org/packages/ba/50/e5119a5212b5cd63b51e1f4b25e7bd636a6668fc069a3160b108ad7e3c16/ruff-0.16.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15f1d0b6e165a6e56567befb6629f8209271311d990bae0f37e6d065035ef5f3", size = 10781993, upload-time = "2026-09-03T16:57:00.666Z" },
|
|
99
|
-
{ url = "https://files.pythonhosted.org/packages/8b/98/083d8b4ef3c51a0d19db84367791cbe9f44e4b53343d19dfa83556e1cd9a/ruff-0.16.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d72c591a96986ee4268860e2b7235082129ca5e4cb9cbba653a4b57c11893757", size = 10317748, upload-time = "2026-09-03T16:57:03.428Z" },
|
|
100
|
-
{ url = "https://files.pythonhosted.org/packages/9a/29/68f7ff2c5ad95f19f00627ac2de95644e25fe47371ea60b2db1fd952315e/ruff-0.16.6-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:65a006baa18f33324325814c864daef03541d51564b98c517610ea756ab7003e", size = 10540096, upload-time = "2026-09-03T16:57:06.182Z" },
|
|
101
|
-
{ url = "https://files.pythonhosted.org/packages/c4/f9/79a8f6de85968641d68a7863aeec577551924ef066a990a48ff93167beab/ruff-0.16.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:cd02a7bf1a21a8735228a3e8c95a9dc5cf86bd2a52194f4aaae2a5755b4de0f4", size = 10100494, upload-time = "2026-09-03T16:57:09.194Z" },
|
|
102
|
-
{ url = "https://files.pythonhosted.org/packages/d9/e8/b81a22d9b90c00b892ccf2fa2ac36fa95de4c13ab85aea3e73795cfe4651/ruff-0.16.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:31b36f1e5ad85e0737f09d2be4e512e2e283583c14015da3b9dc07359ac0fc88", size = 9843663, upload-time = "2026-09-03T16:57:12.168Z" },
|
|
103
|
-
{ url = "https://files.pythonhosted.org/packages/39/aa/54f516ec5e5a11c4afdceb1c454ebb054ffb96e4f4a1705580b4346abd35/ruff-0.16.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:61029b4ab4aa723fd3064fab96b1d814492596bf0c792679fffcbde1e1679953", size = 10282461, upload-time = "2026-09-03T16:57:15.077Z" },
|
|
104
|
-
{ url = "https://files.pythonhosted.org/packages/52/0b/38d0aa8aa32372b96dc44f97b22e576c4147808271aab7b2cb1e353d4445/ruff-0.16.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:9ac8998457832c2061709d900856b7ad271dace0cb41f346588d540162bfa718", size = 10728808, upload-time = "2026-09-03T16:57:17.797Z" },
|
|
105
|
-
{ url = "https://files.pythonhosted.org/packages/5e/e5/9e274e24eeb027640ffc7442f21239f16d17f47acec15ae34f32e03a5c79/ruff-0.16.6-py3-none-win32.whl", hash = "sha256:0b87d9d16fcb63e8018423ca1d50b7260f15cb2da33e30db4baad4183a948c25", size = 10049212, upload-time = "2026-09-03T16:57:20.55Z" },
|
|
106
|
-
{ url = "https://files.pythonhosted.org/packages/22/31/72472449414223ed1a2da236b992adbb1a2ae59e34794574810f60ce068e/ruff-0.16.6-py3-none-win_amd64.whl", hash = "sha256:10d21c51c3495d8eaea7b703a16592117ea6eb1d649e36335aa965ff1173eb39", size = 10556402, upload-time = "2026-09-03T16:57:23.501Z" },
|
|
107
|
-
{ url = "https://files.pythonhosted.org/packages/fc/07/d781f8f8e1ac24bef9f3269cf62ffb1407ca24c3a8f12e5e22874f90528c/ruff-0.16.6-py3-none-win_arm64.whl", hash = "sha256:7a976c79b958f94e50a022a19f0f8c87387448020935ec14fc74331bd0a7f2c5", size = 10412850, upload-time = "2026-09-03T16:57:26.416Z" },
|
|
108
|
-
]
|
|
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
|