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.
Files changed (106) hide show
  1. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/CHANGELOG.md +5 -0
  2. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/PKG-INFO +1 -1
  3. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/pyproject.toml +1 -1
  4. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/__init__.py +8 -0
  5. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/__main__.py +31 -2
  6. chtypes-2.0.0.dev10/src/chtypes/_advisory.py +38 -0
  7. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_codes.py +2 -1
  8. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_ocifetch/__init__.py +5 -1
  9. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_ocifetch/_channel.py +6 -0
  10. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_ocifetch/_constants.py +6 -1
  11. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_ocifetch/_ensure.py +289 -89
  12. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_ocifetch/_errors.py +23 -1
  13. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_ocifetch/_http.py +96 -15
  14. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_ocifetch/_referrers.py +123 -2
  15. chtypes-2.0.0.dev10/src/chtypes/_ocifetch/_retired.py +182 -0
  16. chtypes-2.0.0.dev10/src/chtypes/_ocifetch/_revocation.py +642 -0
  17. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/errors.py +26 -0
  18. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/registry.py +45 -3
  19. chtypes-2.0.0.dev10/tests/cli/test_revocation_cli.py +62 -0
  20. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/test_conformance.py +47 -5
  21. chtypes-2.0.0.dev10/tests/ocifetch/test_revocation.py +334 -0
  22. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/uv.lock +1 -1
  23. chtypes-2.0.0.dev9/src/chtypes/_ocifetch/_retired.py +0 -77
  24. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/.gitignore +0 -0
  25. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/LICENSE +0 -0
  26. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/README.md +0 -0
  27. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_abi2/__init__.py +0 -0
  28. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_abi2/_decls.py +0 -0
  29. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_abi2/_errmap.py +0 -0
  30. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_abi2/_errors.py +0 -0
  31. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_abi2/_loader.py +0 -0
  32. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_abi2/_vocab.py +0 -0
  33. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_decode.py +0 -0
  34. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_ed25519.py +0 -0
  35. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_guard.py +0 -0
  36. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_input.py +0 -0
  37. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_ocifetch/_dsse.py +0 -0
  38. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_ocifetch/_faults.py +0 -0
  39. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_ocifetch/_goldens.py +0 -0
  40. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_ocifetch/_hold.py +0 -0
  41. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_ocifetch/_layout.py +0 -0
  42. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_ocifetch/_lock.py +0 -0
  43. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_ocifetch/_oci.py +0 -0
  44. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_ocifetch/_prune.py +0 -0
  45. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_ocifetch/_resolve.py +0 -0
  46. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_ocifetch/_unpack.py +0 -0
  47. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/_setup.py +0 -0
  48. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/library.py +0 -0
  49. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/py.typed +0 -0
  50. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/src/chtypes/results.py +0 -0
  51. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/abi2/__init__.py +0 -0
  52. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/abi2/_stub_tree.py +0 -0
  53. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/abi2/conftest.py +0 -0
  54. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/abi2/test_conformance.py +0 -0
  55. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/abi2/test_dev_fingerprint.py +0 -0
  56. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/abi2/test_goldens_runner_stub.py +0 -0
  57. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/abi2/test_loader.py +0 -0
  58. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/abi2/test_public_library.py +0 -0
  59. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/abi2/test_public_open.py +0 -0
  60. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/abi2/test_public_registry.py +0 -0
  61. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/abi2/test_public_server.py +0 -0
  62. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/abi2/test_public_setup_cases.py +0 -0
  63. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/abi2/test_reader_rules.py +0 -0
  64. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/abi2/test_registry_fetch.py +0 -0
  65. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/abi2/test_registry_flight.py +0 -0
  66. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/api/__init__.py +0 -0
  67. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/api/test_decode.py +0 -0
  68. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/api/test_input.py +0 -0
  69. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/api/test_trusted_keys.py +0 -0
  70. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/api/test_vocab.py +0 -0
  71. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/cli/__init__.py +0 -0
  72. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/cli/conftest.py +0 -0
  73. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/cli/test_devchannel.py +0 -0
  74. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/cli/test_main.py +0 -0
  75. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/cli/test_resolve_prune.py +0 -0
  76. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/conftest.py +0 -0
  77. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/goldens_v1/__init__.py +0 -0
  78. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/goldens_v1/data/stub-goldens.json +0 -0
  79. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/goldens_v1/test_runner.py +0 -0
  80. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/__init__.py +0 -0
  81. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/_bundle_support.py +0 -0
  82. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/_registry_support.py +0 -0
  83. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/_sign_support.py +0 -0
  84. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/conftest.py +0 -0
  85. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/test_cache_roots.py +0 -0
  86. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/test_cache_strict.py +0 -0
  87. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/test_devchannel.py +0 -0
  88. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/test_dsse.py +0 -0
  89. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/test_ensure.py +0 -0
  90. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/test_goldens.py +0 -0
  91. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/test_http.py +0 -0
  92. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/test_layout.py +0 -0
  93. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/test_lock.py +0 -0
  94. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/test_oci.py +0 -0
  95. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/test_prodv2_smoke.py +0 -0
  96. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/test_prodv2channel.py +0 -0
  97. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/test_retired.py +0 -0
  98. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/test_seam_gate.py +0 -0
  99. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/ocifetch/test_unpack.py +0 -0
  100. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/test_abi_identity.py +0 -0
  101. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/test_cache_roots_api.py +0 -0
  102. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/test_devchannel_registry.py +0 -0
  103. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/test_ed25519.py +0 -0
  104. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/test_examples_tour.py +0 -0
  105. {chtypes-2.0.0.dev9 → chtypes-2.0.0.dev10}/tests/test_fetch_options.py +0 -0
  106. {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.dev9
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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "chtypes"
3
- version = "2.0.0.dev9"
3
+ version = "2.0.0.dev10"
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"
@@ -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 r.ok:
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: 82afa8d6cba96c10b5945c6f7c1c8360a504cc8ac6231e74017cc6c20b908df6
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: 82afa8d6cba96c10b5945c6f7c1c8360a504cc8ac6231e74017cc6c20b908df6
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
  }