chtypes 2.0.0.dev9__tar.gz → 2.0.0.dev10__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-2.0.0.dev9 → chtypes-2.0.0.dev10}/CHANGELOG.md +5 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/PKG-INFO +1 -1
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/pyproject.toml +1 -1
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/__init__.py +8 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/__main__.py +31 -2
- chtypes-2.0.0.dev10/src/chtypes/_advisory.py +38 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_codes.py +2 -1
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_ocifetch/__init__.py +5 -1
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_ocifetch/_channel.py +6 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_ocifetch/_constants.py +6 -1
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_ocifetch/_ensure.py +289 -89
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_ocifetch/_errors.py +23 -1
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_ocifetch/_http.py +96 -15
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_ocifetch/_referrers.py +123 -2
- chtypes-2.0.0.dev10/src/chtypes/_ocifetch/_retired.py +182 -0
- chtypes-2.0.0.dev10/src/chtypes/_ocifetch/_revocation.py +642 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/errors.py +26 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/registry.py +45 -3
- chtypes-2.0.0.dev10/tests/cli/test_revocation_cli.py +62 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/test_conformance.py +47 -5
- chtypes-2.0.0.dev10/tests/ocifetch/test_revocation.py +334 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/uv.lock +1 -1
- chtypes-2.0.0.dev9/src/chtypes/_ocifetch/_retired.py +0 -77
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/.gitignore +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/LICENSE +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/README.md +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_abi2/__init__.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_abi2/_decls.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_abi2/_errmap.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_abi2/_errors.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_abi2/_loader.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_abi2/_vocab.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_decode.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_ed25519.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_guard.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_input.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_ocifetch/_dsse.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_ocifetch/_faults.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_ocifetch/_goldens.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_ocifetch/_hold.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_ocifetch/_layout.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_ocifetch/_lock.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_ocifetch/_oci.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_ocifetch/_prune.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_ocifetch/_resolve.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_ocifetch/_unpack.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_setup.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/library.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/py.typed +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/results.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/abi2/__init__.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/abi2/_stub_tree.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/abi2/conftest.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/abi2/test_conformance.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/abi2/test_dev_fingerprint.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/abi2/test_goldens_runner_stub.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/abi2/test_loader.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/abi2/test_public_library.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/abi2/test_public_open.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/abi2/test_public_registry.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/abi2/test_public_server.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/abi2/test_public_setup_cases.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/abi2/test_reader_rules.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/abi2/test_registry_fetch.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/abi2/test_registry_flight.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/api/__init__.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/api/test_decode.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/api/test_input.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/api/test_trusted_keys.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/api/test_vocab.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/cli/__init__.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/cli/conftest.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/cli/test_devchannel.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/cli/test_main.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/cli/test_resolve_prune.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/conftest.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/goldens_v1/__init__.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/goldens_v1/data/stub-goldens.json +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/goldens_v1/test_runner.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/__init__.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/_bundle_support.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/_registry_support.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/_sign_support.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/conftest.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/test_cache_roots.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/test_cache_strict.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/test_devchannel.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/test_dsse.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/test_ensure.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/test_goldens.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/test_http.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/test_layout.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/test_lock.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/test_oci.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/test_prodv2_smoke.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/test_prodv2channel.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/test_retired.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/test_seam_gate.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/test_unpack.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/test_abi_identity.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/test_cache_roots_api.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/test_devchannel_registry.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/test_ed25519.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/test_examples_tour.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/test_fetch_options.py +0 -0
- {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/test_support_v2_gen.py +0 -0
|
@@ -20,6 +20,8 @@ The four bindings in this repository are released together and give one answer,
|
|
|
20
20
|
- The v2 spec's decline catalog has 49 entries, and it publishes the settings values a server refuses outright (`spec/abi-v2/where-settings/documented-pathological.tsv`).
|
|
21
21
|
- The v2 spec's decline catalog follows the producer's batch 11: its `server-profile-setting` entry now covers every settings layer the schema compiles under (the id is unchanged), the evaluation-time setting entries name only the filter's own settings, and its `stack-depth` entry lists `chs_schema_describe`.
|
|
22
22
|
- The v2 spec publishes the documented platform differences (`spec/abi-v2/documented-platform/`), and its decline catalog adds `setting-format-pair`, `string-reading` and `equals-fold` (52 reasons).
|
|
23
|
+
- The v2 spec's decline catalog adds `server-dependent-function` (a column's DEFAULT, MATERIALIZED, ALIAS or EPHEMERAL expression, or a CHECK, that calls a function whose resolver needs a server is declined at `chs_schema_create`), `function-name-as-lambda` (a bare function name written where a higher-order function takes a lambda, such as `arrayExists(isNull, arr)`, is declined at `chs_filter_create` on 26.7, 26.8 and 26.9) and `skipped-key-in-squashed-block` (a batch whose answer depends on how the INSERT's squashing step groups its chunks is unsupported), and `call-setting-session-timezone` names only a per-call `session_timezone` the call cannot attach (55 reasons).
|
|
24
|
+
- Build-level revocation for generation 2 (#625; `docs/guides/fetch-v1.md`, "Build-level revocation"). A build its signed revocation refuses is the new code `CHTYPES_ARTIFACT_REVOKED`, exit status 11 (`CODE_ARTIFACT_REVOKED`, `ArtifactRevokedError`), whose message names the advisory and whose `.revocation` carries it for programs (`Revocation`, `Advisory`; an advisory's `class` is `class_`). An install reads a revocation from the trust listing it already makes and refuses before the layer; `--frozen` makes one listing, warm or cold, so a warm frozen fetch makes 1 request instead of 0 (`--frozen --offline` stays at 0); a registry open, `preload` and a fetch that finds the build installed check it at most once per build per process, one attempt per base, no backoff and one 5 s deadline (`check_installed`); `--lock` refuses a revoked build on every platform it pins. A verified revocation is stored in the build's cache entry (`revocation.json`) and refuses it from then on with no request, offline too; `chtypes verify` reports it and exits 11. A revocation that does not verify is ignored with one warning; a status that cannot be read is one warning, or with `FetchOptions(require_revocation_check=True)` (`--require-revocation-check`) the transport's error. `FetchOptions(allow_revoked=True)` (`--allow-revoked`) uses a revoked build for one call, with a warning, never for a lock or an update. A `410` whose body's `errors[0].detail.revoked` is an object is `CHTYPES_ARTIFACT_REVOKED`; every other `410` stays `CHTYPES_SOURCE_RETIRED`. The v1 contract is unchanged.
|
|
23
25
|
- A `410 Gone` is a retired repository, and permanent (#571). Any request the fetch layer makes (a tag, an alias, a digest, a blob, `tags/list`, the referrers API or its fallback tag, after a redirect too) that answers `410` is now the new code `CHTYPES_SOURCE_RETIRED` (exit status 10), never retried and never sent to the next base, nor from the dev channel's alias to its tag. It was `CHTYPES_SOURCE_UNREACHABLE` (exit 3) with the registry's message dropped, and Go, Python and Rust moved on to the next base, so a mirror's 404 could hide the retirement. The error names the URL that answered and carries the registry's own message, with control and bidirectional-formatting characters removed and cut to 256 code points: Go `CodeSourceRetired` (`errors.Is(err, ErrSourceRetired)`), Python and TypeScript `SourceRetiredError`, Rust `Error::SourceRetired`. The rule is `docs/guides/fetch-v1.md` section 2, "A retired repository", and the `retired-410-*` and `list-tags-retired-410` conformance cases and one shared message table (`tests/fixtures/retired-message/cases.json`) hold all four bindings to it.
|
|
24
26
|
- A 2.0.0-dev SDK prefers the newest build of its own fingerprint (#511). A version request resolves `<tag>--fp-<fingerprint>` first, the alias the staging dev registry serves beside every dev tag, with the binding's own generated ABI fingerprint, and the tag itself only when every base answers the alias 404; any other failure of the alias request is that failure, with no fallback, and an alias whose index lacks the platform is `CHTYPES_ARTIFACT_UNPUBLISHED`. The dev channel's cache lookups (the installed-build lookup a registry open makes first, the offline answer, and the monotonic rule of a fetch) now see only records whose signed `abi_fingerprint` is the SDK's own, so a newer build of another fingerprint, installed by another dev SDK in the shared cache, is never returned or kept over this one's own, and is never removed or rewritten. So a dev build of a newer fingerprint no longer strands an older dev SDK until its consumers upgrade. Trust is unchanged (the signed predicate and the load checks decide), a listing never shows an alias, nothing records one, `list` and `where` are unchanged, and there is no option: both steps are the dev channel's alone, and the v1 contract and every production channel never take them. The rule is `docs/guides/fetch-v1.md` section 3, and the `dev-alias-*`, `resolve-installed-dev-alias-*` and `list-tags-dev-alias` conformance cases hold all four bindings to it.
|
|
25
27
|
|
|
@@ -65,6 +67,7 @@ The four bindings in this repository are released together and give one answer,
|
|
|
65
67
|
- **Enforcement gate lifted** for `26.7` on `darwin-arm64`.
|
|
66
68
|
- **Enforcement gate lifted** for `26.8` on `darwin-arm64`.
|
|
67
69
|
- **Enforcement gate lifted** for `26.9` on `darwin-arm64`.
|
|
70
|
+
- `docs/limitations.md` is re-checked against artifact build `20261012.142240` (v2-dev.13), the library half measured on that build: functions that need a server, in a column expression, and a body the server may read serially or in parallel under a non-zero error budget are listed under "Declined by design"; the known divergence for a column expression that calls a function needing a server is retired, since that build declines those schemas (on 26.8 and 26.9 the `AI*` and `ai*` functions compile, as the page says); and the `lossy` raw-NUL TSV gap is marked fixed on that build and still present in the 1.0 libraries. `docs/guides/filters.md` gains a section on subcolumn filters and a JSON value typed by its batch.
|
|
68
71
|
|
|
69
72
|
### Removed
|
|
70
73
|
|
|
@@ -72,6 +75,8 @@ The four bindings in this repository are released together and give one answer,
|
|
|
72
75
|
|
|
73
76
|
### Fixed
|
|
74
77
|
|
|
78
|
+
- A response body cut off part way (shorter than its Content-Length, a reset, a TLS close) is retried like a 5xx and, once the retry table is spent, is `CHTYPES_SOURCE_UNREACHABLE`; only an oversize body is `CHTYPES_ARTIFACT_CORRUPT`, and it is never retried (#641). Python took a body that ended before its Content-Length as the whole body and failed its digest check as corrupt without a retry; now a short body, or `http.client.IncompleteRead`, is retried and ends as unreachable. The rule is `docs/guides/fetch-v1.md` section 7, and the new `body-cut-off-then-ok` and `body-cut-off-exhausted` conformance cases hold every binding to it.
|
|
79
|
+
- A chunked response body cut off before its terminating zero-length chunk is held to the same rule (#644): the new `body-cut-off-chunked-then-ok` and `body-cut-off-chunked-exhausted` conformance cases cut one on the layer blob GET, and this binding already retried it and ended as `CHTYPES_SOURCE_UNREACHABLE` once the retry table was spent. No behavior change; the case now holds it.
|
|
75
80
|
- A settings or query-parameter key or value that is not valid UTF-8 is refused as a usage error; it was silently replaced with U+FFFD, so the library saw a different value. A string holding a lone surrogate (what `surrogateescape` makes of undecodable bytes) was sent as a `\udXXX` escape; the same inputs are refused now, for the per-call zone, the setup defaults and the server profile too. Valid UTF-8 serializes as before.
|
|
76
81
|
- A declined export's payload is absent, as documented: it was an empty payload when the library returned a zero-length buffer for a declined export. `BatchResult.payload` and `spans` are `None` for a declined export. Whether an export was declined is decided from the document's `export_declined`, never from the buffer's length.
|
|
77
82
|
- The layout's `index.json` never gains a second entry for one manifest (#487): concurrent installs of one build each appended an entry (7 or 8 entries for the same digest after 8 concurrent installs, measured on 1.0.2). The merge now deduplicates by digest, as Go and TypeScript do.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: chtypes
|
|
3
|
-
Version: 2.0.0.
|
|
3
|
+
Version: 2.0.0.dev10
|
|
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
|
|
@@ -69,6 +69,7 @@ from .errors import (
|
|
|
69
69
|
CODE_ARTIFACT_INCOMPATIBLE,
|
|
70
70
|
CODE_ARTIFACT_MISSING,
|
|
71
71
|
CODE_ARTIFACT_PINNED,
|
|
72
|
+
CODE_ARTIFACT_REVOKED,
|
|
72
73
|
CODE_ARTIFACT_UNPUBLISHED,
|
|
73
74
|
CODE_ARTIFACT_UNTRUSTED,
|
|
74
75
|
CODE_CACHE_UNUSABLE,
|
|
@@ -77,17 +78,20 @@ from .errors import (
|
|
|
77
78
|
CODE_SOURCE_RETIRED,
|
|
78
79
|
CODE_SOURCE_UNAUTHORIZED,
|
|
79
80
|
CODE_SOURCE_UNREACHABLE,
|
|
81
|
+
Advisory,
|
|
80
82
|
ArtifactCorruptError,
|
|
81
83
|
ArtifactError,
|
|
82
84
|
ArtifactIncompatibleError,
|
|
83
85
|
ArtifactMissingError,
|
|
84
86
|
ArtifactPinnedError,
|
|
87
|
+
ArtifactRevokedError,
|
|
85
88
|
ArtifactUnpublishedError,
|
|
86
89
|
ArtifactUntrustedError,
|
|
87
90
|
CacheUnusableError,
|
|
88
91
|
CallError,
|
|
89
92
|
ChtypesError,
|
|
90
93
|
InternalError,
|
|
94
|
+
Revocation,
|
|
91
95
|
SchemaError,
|
|
92
96
|
SourceForbiddenError,
|
|
93
97
|
SourceIncompatibleError,
|
|
@@ -130,6 +134,7 @@ __all__ = [
|
|
|
130
134
|
"CODE_ARTIFACT_INCOMPATIBLE",
|
|
131
135
|
"CODE_ARTIFACT_MISSING",
|
|
132
136
|
"CODE_ARTIFACT_PINNED",
|
|
137
|
+
"CODE_ARTIFACT_REVOKED",
|
|
133
138
|
"CODE_ARTIFACT_UNPUBLISHED",
|
|
134
139
|
"CODE_ARTIFACT_UNTRUSTED",
|
|
135
140
|
"CODE_CACHE_UNUSABLE",
|
|
@@ -139,11 +144,13 @@ __all__ = [
|
|
|
139
144
|
"CODE_SOURCE_UNAUTHORIZED",
|
|
140
145
|
"CODE_SOURCE_UNREACHABLE",
|
|
141
146
|
"EXPORT_NONE",
|
|
147
|
+
"Advisory",
|
|
142
148
|
"ArtifactCorruptError",
|
|
143
149
|
"ArtifactError",
|
|
144
150
|
"ArtifactIncompatibleError",
|
|
145
151
|
"ArtifactMissingError",
|
|
146
152
|
"ArtifactPinnedError",
|
|
153
|
+
"ArtifactRevokedError",
|
|
147
154
|
"ArtifactUnpublishedError",
|
|
148
155
|
"ArtifactUntrustedError",
|
|
149
156
|
"AtMergeEntry",
|
|
@@ -183,6 +190,7 @@ __all__ = [
|
|
|
183
190
|
"Reason",
|
|
184
191
|
"Registry",
|
|
185
192
|
"Resolved",
|
|
193
|
+
"Revocation",
|
|
186
194
|
"RowResult",
|
|
187
195
|
"Schema",
|
|
188
196
|
"SchemaDescription",
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
chtypes fetch <spelling>... | --all [--platform <os-arch>] [--cache <dir>] [--lock <file>]
|
|
5
5
|
[--frozen] [--offline] [--update] [--strict]
|
|
6
|
+
[--allow-revoked] [--require-revocation-check]
|
|
6
7
|
chtypes verify [--cache <dir>] [--strict] re-verify every installed build
|
|
7
8
|
chtypes list [--cache <dir>] [--offline] [--strict]
|
|
8
9
|
chtypes where [--cache <dir>] [--strict] [--all] the cache root; --all: every search dir
|
|
@@ -37,7 +38,7 @@ from collections.abc import Sequence
|
|
|
37
38
|
|
|
38
39
|
from . import __version__ as chtypes_version
|
|
39
40
|
from . import _ocifetch as fetch_layer
|
|
40
|
-
from ._codes import CODE_ARTIFACT_CORRUPT
|
|
41
|
+
from ._codes import CODE_ARTIFACT_CORRUPT, CODE_ARTIFACT_REVOKED
|
|
41
42
|
from ._ocifetch import _channel
|
|
42
43
|
from ._ocifetch import _constants as C
|
|
43
44
|
from ._ocifetch._ensure import _translate_transport_error, detect_host_platform, missing_notes
|
|
@@ -148,6 +149,19 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
148
149
|
"(or set CHTYPES_OFFLINE=1)"
|
|
149
150
|
),
|
|
150
151
|
)
|
|
152
|
+
fetch.add_argument(
|
|
153
|
+
"--allow-revoked",
|
|
154
|
+
action="store_true",
|
|
155
|
+
help=(
|
|
156
|
+
"use a build a verified revocation refuses, with one warning "
|
|
157
|
+
"(this call only; never --lock or --update)"
|
|
158
|
+
),
|
|
159
|
+
)
|
|
160
|
+
fetch.add_argument(
|
|
161
|
+
"--require-revocation-check",
|
|
162
|
+
action="store_true",
|
|
163
|
+
help="a revocation status that cannot be read is the transport's error, not a warning",
|
|
164
|
+
)
|
|
151
165
|
|
|
152
166
|
verify = sub.add_parser(
|
|
153
167
|
"verify",
|
|
@@ -249,6 +263,8 @@ def _options(args: argparse.Namespace, *, platform: str | None = None):
|
|
|
249
263
|
lock_path=lock_path,
|
|
250
264
|
lock_write=lock_write,
|
|
251
265
|
update=getattr(args, "update", False),
|
|
266
|
+
allow_revoked=getattr(args, "allow_revoked", False),
|
|
267
|
+
require_revocation_check=getattr(args, "require_revocation_check", False),
|
|
252
268
|
)
|
|
253
269
|
return fetch._to_options(platform)
|
|
254
270
|
|
|
@@ -348,10 +364,23 @@ def _cmd_verify(args: argparse.Namespace) -> int:
|
|
|
348
364
|
return EXIT_OK
|
|
349
365
|
_say_notes(options)
|
|
350
366
|
failed = 0
|
|
367
|
+
revoked = 0
|
|
351
368
|
for r in results:
|
|
352
|
-
if not
|
|
369
|
+
if r.revocation is not None:
|
|
370
|
+
# A build a stored revocation refuses (docs/guides/fetch-v1.md,
|
|
371
|
+
# "Build-level revocation"); it is reported whatever its bytes say.
|
|
372
|
+
revoked += 1
|
|
373
|
+
_say(f"REVOKED {r.version} {r.platform} {r.dir}: {r.detail}")
|
|
374
|
+
elif not r.ok:
|
|
353
375
|
failed += 1
|
|
354
376
|
_say(f"chtypes: FAILED {r.version} {r.platform} {r.dir}: {r.detail}")
|
|
377
|
+
if revoked:
|
|
378
|
+
# Exit 11 wins over a mismatch's 1: a revocation is the more specific finding.
|
|
379
|
+
_say(
|
|
380
|
+
f"chtypes: {revoked} of {len(results)} installed build(s) are revoked "
|
|
381
|
+
f"[{CODE_ARTIFACT_REVOKED}]"
|
|
382
|
+
)
|
|
383
|
+
return C.ERROR_EXIT_CODES[CODE_ARTIFACT_REVOKED]
|
|
355
384
|
if failed:
|
|
356
385
|
_say(f"chtypes: {failed} of {len(results)} installed build(s) FAILED verification")
|
|
357
386
|
return C.ERROR_EXIT_CODES[CODE_ARTIFACT_CORRUPT]
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""What a `CHTYPES_ARTIFACT_REVOKED` error carries for programs
|
|
2
|
+
(docs/guides/fetch-v1.md, "Build-level revocation"; public issue #625).
|
|
3
|
+
|
|
4
|
+
A leaf module, so the fetch layer's errors and the public ones share the two
|
|
5
|
+
types without importing each other."""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
|
|
11
|
+
__all__ = ["Advisory", "Revocation"]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass(frozen=True)
|
|
15
|
+
class Advisory:
|
|
16
|
+
"""One advisory a revocation names, every string made safe to print. A
|
|
17
|
+
signed revocation's carries its `revoked_at`; a 410's carries the `build` it
|
|
18
|
+
names, newest first. A field the source did not send is "". `class_` is the
|
|
19
|
+
advisory's `class` (a keyword in Python)."""
|
|
20
|
+
|
|
21
|
+
id: str = ""
|
|
22
|
+
url: str = ""
|
|
23
|
+
class_: str = ""
|
|
24
|
+
summary: str = ""
|
|
25
|
+
build: str = ""
|
|
26
|
+
superseded_by: str = ""
|
|
27
|
+
revoked_at: str = ""
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(frozen=True)
|
|
31
|
+
class Revocation:
|
|
32
|
+
"""`signed` is true for a verified, signed revocation of `manifest`, whose
|
|
33
|
+
one advisory it is, and false for a 410's body, which is unsigned, names no
|
|
34
|
+
manifest (`manifest` is "") and lists one advisory per revoked build."""
|
|
35
|
+
|
|
36
|
+
signed: bool = False
|
|
37
|
+
manifest: str = ""
|
|
38
|
+
advisories: tuple[Advisory, ...] = ()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Code generated by scripts/fetch-v1/gen-constants.py from
|
|
2
2
|
# spec/fetch-v1/constants.json. DO NOT EDIT.
|
|
3
|
-
# Source sha256:
|
|
3
|
+
# Source sha256: 5aa3d14a5d8b60399040a8242f003135fa5f6c2c402de7d4e2db4c768a3366ae
|
|
4
4
|
"""The shared error codes (docs/guides/fetch-v1.md §8), one per entry of the
|
|
5
5
|
fetch constants' `errors`, in their order. `chtypes.errors` re-exports them
|
|
6
6
|
(docs/reference/bindings-v1.md §4, the codes)."""
|
|
@@ -21,3 +21,4 @@ CODE_SOURCE_INCOMPATIBLE: Final[str] = "CHTYPES_SOURCE_INCOMPATIBLE"
|
|
|
21
21
|
CODE_ARTIFACT_INCOMPATIBLE: Final[str] = "CHTYPES_ARTIFACT_INCOMPATIBLE"
|
|
22
22
|
CODE_CACHE_UNUSABLE: Final[str] = "CHTYPES_CACHE_UNUSABLE"
|
|
23
23
|
CODE_SOURCE_RETIRED: Final[str] = "CHTYPES_SOURCE_RETIRED"
|
|
24
|
+
CODE_ARTIFACT_REVOKED: Final[str] = "CHTYPES_ARTIFACT_REVOKED"
|
|
@@ -4,7 +4,7 @@ Not part of this package's public API: every name here is subject to change
|
|
|
4
4
|
without notice until the v1 switch. `_constants` is generated; the fetch
|
|
5
5
|
logic lives in sibling modules. The seam (docs/guides/fetch-v1.md "The seam")
|
|
6
6
|
is the only surface anything outside this package may use: `ensure`,
|
|
7
|
-
`resolve_installed`, `list_installed`, `verify_installed` and
|
|
7
|
+
`resolve_installed`, `check_installed`, `list_installed`, `verify_installed` and
|
|
8
8
|
`fetch_signed`, plus the `Request`/`Options`/`Resolved`/
|
|
9
9
|
`VerifyResult` shapes they take and return; and beside it the CLI's `resolve`
|
|
10
10
|
and `prune` and the registry's in-use `hold` (§1, "In use"). Every other module here
|
|
@@ -25,9 +25,11 @@ from chtypes._ocifetch._ensure import (
|
|
|
25
25
|
Request,
|
|
26
26
|
Resolved,
|
|
27
27
|
VerifyResult,
|
|
28
|
+
check_installed,
|
|
28
29
|
ensure,
|
|
29
30
|
fetch_signed,
|
|
30
31
|
list_installed,
|
|
32
|
+
reset_revocation_memo_for_tests,
|
|
31
33
|
resolve_installed,
|
|
32
34
|
verify_installed,
|
|
33
35
|
)
|
|
@@ -43,11 +45,13 @@ __all__ = [
|
|
|
43
45
|
"Resolved",
|
|
44
46
|
"Superseded",
|
|
45
47
|
"VerifyResult",
|
|
48
|
+
"check_installed",
|
|
46
49
|
"ensure",
|
|
47
50
|
"fetch_signed",
|
|
48
51
|
"hold",
|
|
49
52
|
"list_installed",
|
|
50
53
|
"prune",
|
|
54
|
+
"reset_revocation_memo_for_tests",
|
|
51
55
|
"resolve",
|
|
52
56
|
"resolve_each",
|
|
53
57
|
"resolve_installed",
|
|
@@ -133,6 +133,10 @@ class Channel:
|
|
|
133
133
|
# lookups see only records signed with it. "" does neither (the v1 contract
|
|
134
134
|
# and every production channel).
|
|
135
135
|
own_fingerprint: str = ""
|
|
136
|
+
# Build-level revocation (_revocation.py; docs/guides/fetch-v1.md, "Build-level
|
|
137
|
+
# revocation"): generation 2, the dev channel and the production one. The v1
|
|
138
|
+
# contract has none of it.
|
|
139
|
+
revocation: bool = False
|
|
136
140
|
|
|
137
141
|
|
|
138
142
|
# What a 2.0.0-dev SDK speaks, and what every process speaks unless a test
|
|
@@ -149,6 +153,7 @@ DEV_CHANNEL: Final = Channel(
|
|
|
149
153
|
overridable=False,
|
|
150
154
|
pinnable=False,
|
|
151
155
|
own_fingerprint=CHS_ABI_FINGERPRINT.removeprefix("sha256:"),
|
|
156
|
+
revocation=True,
|
|
152
157
|
)
|
|
153
158
|
|
|
154
159
|
# The v1 contract the fetch-v1 conformance cases specify, from the generated
|
|
@@ -183,6 +188,7 @@ PROD_V2_CHANNEL: Final = Channel(
|
|
|
183
188
|
keys=C.RELEASE_KEYS,
|
|
184
189
|
overridable=True,
|
|
185
190
|
pinnable=True,
|
|
191
|
+
revocation=True,
|
|
186
192
|
)
|
|
187
193
|
|
|
188
194
|
_lock = threading.Lock()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Code generated by scripts/fetch-v1/gen-constants.py from
|
|
2
2
|
# spec/fetch-v1/constants.json. DO NOT EDIT.
|
|
3
|
-
# Source sha256:
|
|
3
|
+
# Source sha256: 5aa3d14a5d8b60399040a8242f003135fa5f6c2c402de7d4e2db4c768a3366ae
|
|
4
4
|
"""The v1 fetch constants, generated: see docs/guides/fetch-v1.md."""
|
|
5
5
|
|
|
6
6
|
from __future__ import annotations
|
|
@@ -46,6 +46,7 @@ ARTIFACT_TYPE: Final[str] = "application/vnd.wavehouse.chtypes.artifact.v1"
|
|
|
46
46
|
GOLDENS_ARTIFACT_TYPE: Final[str] = "application/vnd.wavehouse.chtypes.goldens.v1"
|
|
47
47
|
FIXTURES_ARTIFACT_TYPE: Final[str] = "application/vnd.wavehouse.chtypes.sdk-fetch-fixtures.v1"
|
|
48
48
|
CHANNEL_ARTIFACT_TYPE: Final[str] = "application/vnd.wavehouse.chtypes.channel.v1"
|
|
49
|
+
REVOCATION_ARTIFACT_TYPE: Final[str] = "application/vnd.wavehouse.chtypes.revocation.v1"
|
|
49
50
|
|
|
50
51
|
DSSE_PAYLOAD_TYPE: Final[str] = "application/vnd.in-toto+json"
|
|
51
52
|
DSSE_MAX_SIGNATURES: Final[int] = 4
|
|
@@ -56,6 +57,7 @@ PREDICATE_TYPE_ARTIFACT: Final[str] = "https://artifacts.wavehouse.dev/spec/arti
|
|
|
56
57
|
PREDICATE_TYPE_GOLDENS: Final[str] = "https://artifacts.wavehouse.dev/spec/goldens/v1"
|
|
57
58
|
PREDICATE_TYPE_FIXTURES: Final[str] = "https://artifacts.wavehouse.dev/spec/sdk-fetch-fixtures/v1"
|
|
58
59
|
PREDICATE_TYPE_CHANNEL: Final[str] = "https://artifacts.wavehouse.dev/spec/channel/v1"
|
|
60
|
+
PREDICATE_TYPE_REVOCATION: Final[str] = "https://artifacts.wavehouse.dev/spec/revocation/v1"
|
|
59
61
|
|
|
60
62
|
KEYID_ALGORITHM: Final[str] = "sha256-first16hex"
|
|
61
63
|
RELEASE_KEYS: Final[tuple[dict[str, str], ...]] = (
|
|
@@ -87,6 +89,7 @@ RETIRED_STATUSES: Final[tuple[int, ...]] = (410,)
|
|
|
87
89
|
|
|
88
90
|
CONNECT_TIMEOUT_S: Final[float] = 30.0
|
|
89
91
|
IDLE_READ_TIMEOUT_S: Final[float] = 60.0
|
|
92
|
+
REVOCATION_CHECK_S: Final[float] = 5.0
|
|
90
93
|
|
|
91
94
|
MANIFEST_MAX_BYTES: Final[int] = 4194304
|
|
92
95
|
BUNDLE_MAX_BYTES: Final[int] = 1048576
|
|
@@ -109,6 +112,7 @@ PROD_V2_BASES: Final[tuple[str, ...]] = ("https://registry.wavehouse.dev/chtypes
|
|
|
109
112
|
PROD_V2_SYSTEM_DIRS: Final[tuple[str, ...]] = ("/usr/local/share/chtypes/v2", "/opt/chtypes/v2")
|
|
110
113
|
CACHE_UNPACKED_DIR: Final[str] = "unpacked/sha256"
|
|
111
114
|
CACHE_VERIFIED_RECORD: Final[str] = "verified.json"
|
|
115
|
+
CACHE_REVOCATION_RECORD: Final[str] = "revocation.json"
|
|
112
116
|
CACHE_ANNOTATION_PREFIX: Final[str] = "dev.wavehouse.chtypes."
|
|
113
117
|
|
|
114
118
|
LOCK_SCHEMA: Final[int] = 3
|
|
@@ -132,4 +136,5 @@ ERROR_EXIT_CODES: Final[dict[str, int]] = {
|
|
|
132
136
|
"CHTYPES_ARTIFACT_INCOMPATIBLE": 8,
|
|
133
137
|
"CHTYPES_CACHE_UNUSABLE": 9,
|
|
134
138
|
"CHTYPES_SOURCE_RETIRED": 10,
|
|
139
|
+
"CHTYPES_ARTIFACT_REVOKED": 11,
|
|
135
140
|
}
|