chtypes 1.1.0__tar.gz → 2.0.0.dev1__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 (88) hide show
  1. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/PKG-INFO +6 -2
  2. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/README.md +5 -1
  3. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/pyproject.toml +1 -1
  4. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/__init__.py +9 -1
  5. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/__main__.py +20 -0
  6. {chtypes-1.1.0/src/chtypes/_abi1 → chtypes-2.0.0.dev1/src/chtypes/_abi2}/__init__.py +5 -3
  7. {chtypes-1.1.0/src/chtypes/_abi1 → chtypes-2.0.0.dev1/src/chtypes/_abi2}/_decls.py +79 -20
  8. {chtypes-1.1.0/src/chtypes/_abi1 → chtypes-2.0.0.dev1/src/chtypes/_abi2}/_errmap.py +3 -3
  9. {chtypes-1.1.0/src/chtypes/_abi1 → chtypes-2.0.0.dev1/src/chtypes/_abi2}/_loader.py +51 -21
  10. chtypes-2.0.0.dev1/src/chtypes/_abi2/_vocab.py +416 -0
  11. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/_decode.py +13 -15
  12. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/_ocifetch/__init__.py +6 -0
  13. chtypes-2.0.0.dev1/src/chtypes/_ocifetch/_channel.py +325 -0
  14. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/_ocifetch/_ensure.py +36 -9
  15. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/_ocifetch/_layout.py +25 -14
  16. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/_ocifetch/_lock.py +5 -3
  17. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/errors.py +1 -1
  18. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/library.py +5 -3
  19. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/registry.py +31 -4
  20. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/results.py +1 -1
  21. {chtypes-1.1.0/tests/abi1 → chtypes-2.0.0.dev1/tests/abi2}/__init__.py +1 -1
  22. {chtypes-1.1.0/tests/abi1 → chtypes-2.0.0.dev1/tests/abi2}/conftest.py +28 -11
  23. {chtypes-1.1.0/tests/abi1 → chtypes-2.0.0.dev1/tests/abi2}/test_conformance.py +6 -6
  24. chtypes-2.0.0.dev1/tests/abi2/test_dev_fingerprint.py +80 -0
  25. {chtypes-1.1.0/tests/abi1 → chtypes-2.0.0.dev1/tests/abi2}/test_goldens_runner_stub.py +22 -7
  26. {chtypes-1.1.0/tests/abi1 → chtypes-2.0.0.dev1/tests/abi2}/test_loader.py +3 -3
  27. {chtypes-1.1.0/tests/abi1 → chtypes-2.0.0.dev1/tests/abi2}/test_public_library.py +13 -6
  28. {chtypes-1.1.0/tests/abi1 → chtypes-2.0.0.dev1/tests/abi2}/test_public_open.py +1 -1
  29. {chtypes-1.1.0/tests/abi1 → chtypes-2.0.0.dev1/tests/abi2}/test_public_registry.py +16 -1
  30. {chtypes-1.1.0/tests/abi1 → chtypes-2.0.0.dev1/tests/abi2}/test_public_setup_cases.py +3 -3
  31. chtypes-2.0.0.dev1/tests/abi2/test_reader_rules.py +243 -0
  32. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/api/test_decode.py +37 -13
  33. chtypes-2.0.0.dev1/tests/api/test_vocab.py +124 -0
  34. chtypes-2.0.0.dev1/tests/cli/conftest.py +26 -0
  35. chtypes-2.0.0.dev1/tests/cli/test_devchannel.py +126 -0
  36. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/cli/test_main.py +4 -2
  37. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/conftest.py +1 -1
  38. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/goldens_v1/test_runner.py +26 -6
  39. chtypes-2.0.0.dev1/tests/ocifetch/conftest.py +28 -0
  40. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/ocifetch/test_conformance.py +12 -0
  41. chtypes-2.0.0.dev1/tests/ocifetch/test_devchannel.py +268 -0
  42. chtypes-2.0.0.dev1/tests/ocifetch/test_seam_gate.py +57 -0
  43. chtypes-2.0.0.dev1/tests/test_abi_identity.py +24 -0
  44. chtypes-2.0.0.dev1/tests/test_devchannel_registry.py +98 -0
  45. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/test_fetch_options.py +9 -4
  46. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/uv.lock +2 -2
  47. chtypes-1.1.0/src/chtypes/_abi1/_vocab.py +0 -254
  48. chtypes-1.1.0/tests/api/test_vocab.py +0 -90
  49. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/.gitignore +0 -0
  50. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/CHANGELOG.md +0 -0
  51. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/LICENSE +0 -0
  52. {chtypes-1.1.0/src/chtypes/_abi1 → chtypes-2.0.0.dev1/src/chtypes/_abi2}/_errors.py +0 -0
  53. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/_ed25519.py +0 -0
  54. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/_guard.py +0 -0
  55. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/_input.py +0 -0
  56. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/_ocifetch/_constants.py +0 -0
  57. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/_ocifetch/_dsse.py +0 -0
  58. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/_ocifetch/_errors.py +0 -0
  59. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/_ocifetch/_faults.py +0 -0
  60. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/_ocifetch/_goldens.py +0 -0
  61. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/_ocifetch/_http.py +0 -0
  62. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/_ocifetch/_oci.py +0 -0
  63. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/_ocifetch/_referrers.py +0 -0
  64. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/_ocifetch/_unpack.py +0 -0
  65. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/_setup.py +0 -0
  66. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/py.typed +0 -0
  67. {chtypes-1.1.0/tests/abi1 → chtypes-2.0.0.dev1/tests/abi2}/_stub_tree.py +0 -0
  68. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/api/__init__.py +0 -0
  69. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/api/test_input.py +0 -0
  70. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/cli/__init__.py +0 -0
  71. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/goldens_v1/__init__.py +0 -0
  72. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/goldens_v1/data/stub-goldens.json +0 -0
  73. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/ocifetch/__init__.py +0 -0
  74. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/ocifetch/_bundle_support.py +0 -0
  75. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/ocifetch/_registry_support.py +0 -0
  76. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/ocifetch/_sign_support.py +0 -0
  77. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/ocifetch/test_cache_roots.py +0 -0
  78. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/ocifetch/test_cache_strict.py +0 -0
  79. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/ocifetch/test_dsse.py +0 -0
  80. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/ocifetch/test_ensure.py +0 -0
  81. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/ocifetch/test_goldens.py +0 -0
  82. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/ocifetch/test_http.py +0 -0
  83. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/ocifetch/test_layout.py +0 -0
  84. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/ocifetch/test_lock.py +0 -0
  85. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/ocifetch/test_oci.py +0 -0
  86. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/ocifetch/test_unpack.py +0 -0
  87. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/test_ed25519.py +0 -0
  88. {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/test_examples_tour.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: chtypes
3
- Version: 1.1.0
3
+ Version: 2.0.0.dev1
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
@@ -19,6 +19,8 @@ Description-Content-Type: text/markdown
19
19
 
20
20
  # chtypes — Python SDK
21
21
 
22
+ > **2.0.0-dev: UNSTABLE, staging only, not for production.** This is the ABI v2 development binding (public issue #511). It speaks ABI v2's unstable description, pins its dev fingerprint and refuses a library with any other ("update your dev SDK"). It fetches only from the staging dev channel (`https://registry-staging.wavehouse.dev/chtypes/v2-dev`) and trusts only the staging key; `CHTYPES_ARTIFACTS_URL`, `CHTYPES_TRUSTED_KEYS` and `CHTYPES_ALLOW_UNSIGNED`, and the `bases`, `trusted_keys` and `allow_unsigned` fetch options, are ignored, each with one warning; `--lock`, `--frozen` and `--update`, and the `frozen`, `lock_path`, `lock_write` and `update` options, are refused, because a dev build is replaceable and a superseded one expires after 14 days. Its cache is `${XDG_CACHE_HOME:-~/.cache}/chtypes/v2-dev`, or `<CHTYPES_CACHE>/v2-dev` under an explicit cache, which no 1.x SDK reads. Every vocabulary keeps a value it does not list as its `unknown(n)` member (`Outcome("x")`, whose `known` is `False`) instead of failing the document. The rules are r1 to r6 of [`docs/reference/abi-v2.md`](../docs/reference/abi-v2.md). For production, use the 1.x package: `pip install "chtypes<2"`.
23
+
22
24
  **If this row were inserted into this table on this ClickHouse version, what would happen?** chtypes answers with ClickHouse's own code: the real C++ type machinery, vendored per release into a native library behind the frozen `chs_*` C ABI and reached here through stdlib `ctypes`. Nothing semantic is reimplemented, so _"what does ClickHouse do with `256` into a `UInt8`?"_ is answered by ClickHouse rather than by a model of it. One peer binding among `{go, python, ts, rust}` — no language is privileged, and all four give one answer.
23
25
 
24
26
  Pure ctypes: no compiler, no build step. The only dependency is a zstd decompressor for the artifact layer, which is the standard library's own from Python 3.14 and the `backports.zstd` package before that.
@@ -28,9 +30,11 @@ Pure ctypes: no compiler, no build step. The only dependency is a zstd decompres
28
30
  Two things: this package, and at least one **artifact** — the per-version native library it `dlopen`s at runtime. The registry fetches the artifact on first use when autofetch is on (`Registry(autofetch=True)` or `CHTYPES_AUTOFETCH=1`), after verifying its signature and every byte against the signed statement; with autofetch off it answers only from what is already installed.
29
31
 
30
32
  ```sh
31
- uv add chtypes # or: pip install chtypes
33
+ uv add "chtypes>=2.0.0.dev0" # or: pip install "chtypes>=2.0.0.dev0"
32
34
  ```
33
35
 
36
+ `uv add chtypes` and `pip install chtypes` never select a pre-release on their own: the specifier names a dev version, which is what allows one.
37
+
34
38
  ## Quickstart
35
39
 
36
40
  ```python
@@ -1,5 +1,7 @@
1
1
  # chtypes — Python SDK
2
2
 
3
+ > **2.0.0-dev: UNSTABLE, staging only, not for production.** This is the ABI v2 development binding (public issue #511). It speaks ABI v2's unstable description, pins its dev fingerprint and refuses a library with any other ("update your dev SDK"). It fetches only from the staging dev channel (`https://registry-staging.wavehouse.dev/chtypes/v2-dev`) and trusts only the staging key; `CHTYPES_ARTIFACTS_URL`, `CHTYPES_TRUSTED_KEYS` and `CHTYPES_ALLOW_UNSIGNED`, and the `bases`, `trusted_keys` and `allow_unsigned` fetch options, are ignored, each with one warning; `--lock`, `--frozen` and `--update`, and the `frozen`, `lock_path`, `lock_write` and `update` options, are refused, because a dev build is replaceable and a superseded one expires after 14 days. Its cache is `${XDG_CACHE_HOME:-~/.cache}/chtypes/v2-dev`, or `<CHTYPES_CACHE>/v2-dev` under an explicit cache, which no 1.x SDK reads. Every vocabulary keeps a value it does not list as its `unknown(n)` member (`Outcome("x")`, whose `known` is `False`) instead of failing the document. The rules are r1 to r6 of [`docs/reference/abi-v2.md`](../docs/reference/abi-v2.md). For production, use the 1.x package: `pip install "chtypes<2"`.
4
+
3
5
  **If this row were inserted into this table on this ClickHouse version, what would happen?** chtypes answers with ClickHouse's own code: the real C++ type machinery, vendored per release into a native library behind the frozen `chs_*` C ABI and reached here through stdlib `ctypes`. Nothing semantic is reimplemented, so _"what does ClickHouse do with `256` into a `UInt8`?"_ is answered by ClickHouse rather than by a model of it. One peer binding among `{go, python, ts, rust}` — no language is privileged, and all four give one answer.
4
6
 
5
7
  Pure ctypes: no compiler, no build step. The only dependency is a zstd decompressor for the artifact layer, which is the standard library's own from Python 3.14 and the `backports.zstd` package before that.
@@ -9,9 +11,11 @@ Pure ctypes: no compiler, no build step. The only dependency is a zstd decompres
9
11
  Two things: this package, and at least one **artifact** — the per-version native library it `dlopen`s at runtime. The registry fetches the artifact on first use when autofetch is on (`Registry(autofetch=True)` or `CHTYPES_AUTOFETCH=1`), after verifying its signature and every byte against the signed statement; with autofetch off it answers only from what is already installed.
10
12
 
11
13
  ```sh
12
- uv add chtypes # or: pip install chtypes
14
+ uv add "chtypes>=2.0.0.dev0" # or: pip install "chtypes>=2.0.0.dev0"
13
15
  ```
14
16
 
17
+ `uv add chtypes` and `pip install chtypes` never select a pre-release on their own: the specifier names a dev version, which is what allows one.
18
+
15
19
  ## Quickstart
16
20
 
17
21
  ```python
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "chtypes"
3
- version = "1.1.0"
3
+ version = "2.0.0.dev1"
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"
@@ -1,5 +1,11 @@
1
1
  """chtypes: ClickHouse's own type system, per version, from Python.
2
2
 
3
+ 2.0.0-dev: UNSTABLE, staging only, not for production. This pre-release speaks
4
+ the ABI v2 dev description (spec/abi-v2/docs.md): it fetches only from the
5
+ staging dev channel and trusts only its key, refuses locks and frozen fetches,
6
+ caches under its own `v2-dev` root, and refuses a library built from any other
7
+ dev fingerprint (rules r5 and r6).
8
+
3
9
  One question, exactly: *if this row were inserted into this ClickHouse table on
4
10
  this ClickHouse version, what would happen?* The answer comes from ClickHouse's
5
11
  real C++ machinery, vendored per release into a shared library behind the frozen
@@ -40,9 +46,10 @@ from __future__ import annotations
40
46
  from importlib.metadata import PackageNotFoundError as _PackageNotFound
41
47
  from importlib.metadata import version as _package_version
42
48
 
43
- from ._abi1._vocab import (
49
+ from ._abi2._vocab import (
44
50
  EXPORT_NONE,
45
51
  DefaultKind,
52
+ DiscoverQueryParam,
46
53
  DocFlags,
47
54
  FilterOutcome,
48
55
  Format,
@@ -140,6 +147,7 @@ __all__ = [
140
147
  "Column",
141
148
  "Computed",
142
149
  "DefaultKind",
150
+ "DiscoverQueryParam",
143
151
  "DiscoveredColumn",
144
152
  "Discovery",
145
153
  "EngineCell",
@@ -12,6 +12,15 @@ A spelling is `26.8`, `26.8.15` or `26.8.15.10`. Exit statuses come from the
12
12
  as `ERROR_EXIT_CODES`); a usage error exits 2 and success exits 0. Progress and
13
13
  diagnostics go to stderr; `fetch` prints one installed library path per line on
14
14
  stdout.
15
+
16
+ 2.0.0-dev: UNSTABLE, staging only, not for production. This CLI speaks the ABI
17
+ v2 dev channel (spec/abi-v2/docs.md, rules r5 and r6): it fetches only from
18
+ https://registry-staging.wavehouse.dev/chtypes/v2-dev and trusts only the
19
+ staging key; CHTYPES_ARTIFACTS_URL, CHTYPES_TRUSTED_KEYS and
20
+ CHTYPES_ALLOW_UNSIGNED are ignored, each with one warning; --lock, --frozen and
21
+ --update are refused before any network call; its default cache is
22
+ ${XDG_CACHE_HOME:-~/.cache}/chtypes/v2-dev, and an explicit one (--cache,
23
+ CHTYPES_CACHE) is used through its v2-dev subroot.
15
24
  """
16
25
 
17
26
  from __future__ import annotations
@@ -24,6 +33,7 @@ from collections.abc import Sequence
24
33
 
25
34
  from . import __version__ as chtypes_version
26
35
  from . import _ocifetch as fetch_layer
36
+ from ._ocifetch import _channel
27
37
  from ._ocifetch import _constants as C
28
38
  from ._ocifetch._ensure import _translate_transport_error, detect_host_platform, missing_notes
29
39
  from ._ocifetch._errors import ArtifactCorruptError, ArtifactMissingError, FetchError
@@ -67,6 +77,12 @@ def build_parser() -> argparse.ArgumentParser:
67
77
  parser = _Parser(
68
78
  prog="chtypes",
69
79
  description="Fetch, verify and locate chtypes artifacts (docs/guides/fetch-v1.md).",
80
+ epilog=(
81
+ "2.0.0-dev: UNSTABLE, staging only, not for production. Fetches only from the staging "
82
+ "dev channel and trusts only its key; --lock, --frozen and --update are refused; "
83
+ "CHTYPES_ARTIFACTS_URL, CHTYPES_TRUSTED_KEYS and CHTYPES_ALLOW_UNSIGNED are ignored "
84
+ "(spec/abi-v2/docs.md, rule r6)."
85
+ ),
70
86
  )
71
87
  parser.add_argument("--version", action="version", version=f"chtypes {_display_version()}")
72
88
  sub = parser.add_subparsers(dest="command", metavar="<command>")
@@ -196,6 +212,10 @@ def _published_tags(options) -> list[str]:
196
212
 
197
213
 
198
214
  def _cmd_fetch(args: argparse.Namespace) -> int:
215
+ # A dev SDK pins nothing: the refusal comes before anything else, the
216
+ # network above all (rule r6).
217
+ if (args.lock or args.frozen or args.update) and not _channel.active().pinnable:
218
+ raise ValueError(f"chtypes: {_channel.PINNING_REFUSED}")
199
219
  if args.all and args.spellings:
200
220
  raise ValueError("chtypes: give spellings or --all, not both")
201
221
  if not args.all and not args.spellings and not args.update:
@@ -1,4 +1,4 @@
1
- """The ABI v1 loader and generated declaration layer (plan
1
+ """The ABI v2 loader and generated declaration layer (plan
2
2
  PLAN-sdk-v1-ffi-2026-10-01.md), private to this binding. Not part of `chtypes`'s
3
3
  public surface: the public API (`chtypes.library`, `chtypes.registry`) is built
4
4
  over it, and re-exports the vocabularies and errors it defines.
@@ -7,11 +7,13 @@ over it, and re-exports the vocabularies and errors it defines.
7
7
  handle classes, the typed call wrappers on `Api`, and the test-only
8
8
  `invoke_by_name()` dispatcher.
9
9
  - `_vocab`: GENERATED -- `Format`, `Status`, `Outcome`, `Verdict`, `Reason`,
10
- `Source`, `DefaultKind`, `DocFlags` and the facts the description attaches.
10
+ `Source`, `DefaultKind`, `DiscoverQueryParam`, `DocFlags` and the facts the
11
+ description attaches, each vocabulary with its unknown(n) member (rule r3).
11
12
  - `_errors`: the public `chtypes.errors` classes, re-exported under the names
12
13
  `_errmap` uses.
13
14
  - `_errmap`: GENERATED -- the sdk.json status/reason -> `_errors` class maps.
14
- - `_loader`: hand-written -- `open()`/`open_unverified()`, the loader steps.
15
+ - `_loader`: hand-written -- `open()`/`open_unverified()`, the loader steps,
16
+ and rule r6's exact refusal of another fingerprint.
15
17
  """
16
18
 
17
19
  from __future__ import annotations
@@ -1,9 +1,9 @@
1
- # GENERATED by scripts/abi-v1/gen.py from spec/abi-v1/abi.json (CHS_ABI_FINGERPRINT sha256:856e5cb7543db291b4db716662bf6e487b2258973b959bbf5d58b4bd46d73285) — DO NOT EDIT # noqa: E501
2
- """The Python binding's generated ABI v1 layer: ctypes signatures, the handle
1
+ # GENERATED by scripts/abi-v1/gen.py --major 2 from spec/abi-v2/abi.json (CHS_ABI_FINGERPRINT sha256:d87e91be2bbe75b985c38224268c2f7dc5d8236dab0a4b4d61357c6dfdc1a232) — DO NOT EDIT # noqa: E501
2
+ """The Python binding's generated ABI v2 layer: ctypes signatures, the handle
3
3
  classes, the two-phase resolve functions and invoke_by_name(), the test-only
4
- dispatcher the conformance suite drives from tests/fixtures/abi-v1/cases.json.
4
+ dispatcher the conformance suite drives from tests/fixtures/abi-v2/cases.json.
5
5
  See scripts/abi-v1/emit/python.py's module docstring for the design this file
6
- implements and why. This is the ONLY python/src/chtypes/_abi1 file that reads
6
+ implements and why. This is the ONLY python/src/chtypes/_abi2 file that reads
7
7
  a chs_* attribute (scripts/abi-v1/check-no-hand-decls.py exempts it by its
8
8
  banner, above); every other file in this package calls the non-chs_-prefixed
9
9
  names this module exposes instead.
@@ -18,8 +18,14 @@ from dataclasses import dataclass, field
18
18
 
19
19
  from . import _errmap
20
20
 
21
- CHS_ABI_VERSION = 1
22
- CHS_ABI_FINGERPRINT = "sha256:856e5cb7543db291b4db716662bf6e487b2258973b959bbf5d58b4bd46d73285"
21
+ CHS_ABI_VERSION = 2
22
+ CHS_ABI_FINGERPRINT = "sha256:d87e91be2bbe75b985c38224268c2f7dc5d8236dab0a4b4d61357c6dfdc1a232"
23
+
24
+ # The description's stability: "unstable" while this generation is being
25
+ # designed, so CHS_ABI_FINGERPRINT moves with every change, and "locked" after
26
+ # (spec/abi-v2/docs.md, rule r6). A dev SDK refuses any other fingerprint with
27
+ # the dev message (_loader.py).
28
+ CHS_ABI_STABILITY = "unstable"
23
29
 
24
30
  # sdk.json's loader.cross_check: (build_info field, predicate field, how to
25
31
  # compare), read by _loader.py's step 5 without it ever touching sdk.json
@@ -55,6 +61,7 @@ _STATUS_OK = 0
55
61
  HANDLE_FREE: dict[str, str] = {
56
62
  "chs_buf": "chs_buf_free",
57
63
  "chs_error": "chs_error_free",
64
+ "chs_server": "chs_server_free",
58
65
  "chs_schema": "chs_schema_free",
59
66
  "chs_filter": "chs_filter_free",
60
67
  "chs_block": "chs_block_free",
@@ -96,6 +103,8 @@ ALL_SYMBOLS: tuple[str, ...] = (
96
103
  "chs_schema_create",
97
104
  "chs_schema_describe",
98
105
  "chs_schema_free",
106
+ "chs_server_create",
107
+ "chs_server_free",
99
108
  "chs_set_defaults",
100
109
  "chs_shutdown",
101
110
  "chs_type_validate",
@@ -168,6 +177,10 @@ class ErrorHandle(Handle):
168
177
  """A `chs_error` the library owns the free function for."""
169
178
 
170
179
 
180
+ class ServerHandle(Handle):
181
+ """A `chs_server` the library owns the free function for."""
182
+
183
+
171
184
  class SchemaHandle(Handle):
172
185
  """A `chs_schema` the library owns the free function for."""
173
186
 
@@ -183,6 +196,7 @@ class BlockHandle(Handle):
183
196
  _HANDLE_CLASS: dict[str, type[Handle]] = {
184
197
  "chs_buf": BufHandle,
185
198
  "chs_error": ErrorHandle,
199
+ "chs_server": ServerHandle,
186
200
  "chs_schema": SchemaHandle,
187
201
  "chs_filter": FilterHandle,
188
202
  "chs_block": BlockHandle,
@@ -318,12 +332,24 @@ _add(
318
332
  _P("out", "out_handle", "chs_buf"),
319
333
  _P("err", "out_error", "chs_error", nullable=True),
320
334
  )
335
+ _add(
336
+ "chs_server_create",
337
+ "status",
338
+ "chs_status",
339
+ _P("profile", "bytes_in", None),
340
+ _P("options", "bytes_in", None),
341
+ _P("out", "out_handle", "chs_server"),
342
+ _P("err", "out_error", "chs_error", nullable=True),
343
+ )
344
+ _add("chs_server_free", "void", None, _P("server", "handle", "chs_server", nullable=True))
321
345
  _add(
322
346
  "chs_schema_create",
323
347
  "status",
324
348
  "chs_status",
349
+ _P("server", "handle", "chs_server", nullable=True),
325
350
  _P("create_table", "bytes_in", None),
326
351
  _P("settings", "bytes_in", None),
352
+ _P("options", "bytes_in", None),
327
353
  _P("out", "out_handle", "chs_schema"),
328
354
  _P("err", "out_error", "chs_error", nullable=True),
329
355
  )
@@ -480,6 +506,18 @@ def resolve_abi_version(lib: ctypes.CDLL) -> Callable[..., object]:
480
506
  return fn
481
507
 
482
508
 
509
+ def resolve_build_info(lib: ctypes.CDLL) -> Callable[..., object]:
510
+ """Loader step 4's dlsym: `chs_build_info` ALONE, so the fingerprint can be
511
+ compared before step 6's full sweep (rule r6 beats a missing symbol,
512
+ public issue #537). Absent is MissingSymbol, the step 4 refusal."""
513
+ try:
514
+ fn = lib.chs_build_info
515
+ except AttributeError:
516
+ raise MissingSymbol("chs_build_info") from None
517
+ _set_signature(fn, FUNCTIONS["chs_build_info"])
518
+ return fn
519
+
520
+
483
521
  class Api:
484
522
  """Every described symbol, resolved and signature-typed, step 6
485
523
  (plan section 3.2). Constructed ONLY by resolve_all(): the constructor
@@ -516,7 +554,7 @@ class Api:
516
554
  cls = _errmap.call_error_class(name)
517
555
  message = info.message if info else b""
518
556
  if name not in STATUS_BY_NAME:
519
- message = f"status {status} is outside the closed chs_status set".encode() + (
557
+ message = f"call status unknown({status}) is outside the closed set".encode() + (
520
558
  b": " + message if message else b""
521
559
  )
522
560
  raise cls(
@@ -697,19 +735,42 @@ class Api:
697
735
  self._check(status, err)
698
736
  return _read_buf(self, out.value)
699
737
 
738
+ def server_create(
739
+ self,
740
+ profile: bytes | None,
741
+ options: bytes | None,
742
+ ) -> ServerHandle:
743
+ """The generated call wrapper for chs_server_create (thread class any).
744
+
745
+ Raises the class the status maps to on a non-OK status."""
746
+ out = ctypes.c_void_p()
747
+ err = ctypes.c_void_p()
748
+ status = self._raw["chs_server_create"](
749
+ *_bytes_in(profile),
750
+ *_bytes_in(options),
751
+ ctypes.byref(out),
752
+ ctypes.byref(err),
753
+ )
754
+ self._check(status, err)
755
+ return ServerHandle(out.value, self._raw["chs_server_free"])
756
+
700
757
  def schema_create(
701
758
  self,
759
+ server: ServerHandle | None,
702
760
  create_table: bytes | None,
703
761
  settings: bytes | None,
762
+ options: bytes | None,
704
763
  ) -> SchemaHandle:
705
- """The generated call wrapper for chs_schema_create (thread class any).
764
+ """The generated call wrapper for chs_schema_create (thread class shared).
706
765
 
707
766
  Raises the class the status maps to on a non-OK status."""
708
767
  out = ctypes.c_void_p()
709
768
  err = ctypes.c_void_p()
710
769
  status = self._raw["chs_schema_create"](
770
+ None if server is None else server.value,
711
771
  *_bytes_in(create_table),
712
772
  *_bytes_in(settings),
773
+ *_bytes_in(options),
713
774
  ctypes.byref(out),
714
775
  ctypes.byref(err),
715
776
  )
@@ -919,17 +980,15 @@ _API_KEY = object()
919
980
 
920
981
 
921
982
  def resolve_all(lib: ctypes.CDLL) -> Api:
922
- """Every described symbol, in sorted, deterministic order -- chs_abi_version
923
- INCLUDED, even though _loader.py's step 3 (resolve_abi_version) already
924
- resolved it before this runs: the test-only invoke_by_name() dispatcher
925
- below needs every symbol reachable through ONE Api, handshake class
926
- included, and re-resolving an already-present symbol is harmless (by the
927
- time step 6 runs, step 3 has already proven it exists). The first symbol
928
- ctypes cannot dlsym raises MissingSymbol(name); _loader.py's step 6 turns
929
- that into "missing_symbol:<name>", matching every missing-<sym> stub
930
- variant (tests/fixtures/abi-v1/cases.json), chs_build_info and
931
- chs_clickhouse_version included -- loader step 4 needs chs_build_info
932
- already resolved, which is exactly why this sweep must run before it."""
983
+ """Every described symbol, in sorted, deterministic order -- the handshake
984
+ symbols INCLUDED, even though _loader.py's steps 3 and 4 (resolve_abi_version,
985
+ resolve_build_info) already resolved theirs before this runs: the test-only
986
+ invoke_by_name() dispatcher below needs every symbol reachable through ONE
987
+ Api, and re-resolving an already-present symbol is harmless. The first
988
+ symbol ctypes cannot dlsym raises MissingSymbol(name); _loader.py's step 6
989
+ turns that into "missing_symbol:<name>", matching every missing-<sym> stub
990
+ variant. Step 6 runs AFTER step 4's fingerprint comparison, so a library
991
+ with another fingerprint is refused with rule r6's message first."""
933
992
  raw: dict = {}
934
993
  for name in ALL_SYMBOLS:
935
994
  try:
@@ -1028,7 +1087,7 @@ def invoke_by_name(api: Api, name: str, args: Iterable[object] = ()) -> InvokeRe
1028
1087
  (or None, where nullable) for handle. Output parameters are never
1029
1088
  supplied by the caller: this dispatcher allocates them, and decodes them
1030
1089
  into the returned InvokeResult. Test-only: every chs_ spelling below is
1031
- why this file, alone in python/src/chtypes/_abi1, carries the generated
1090
+ why this file, alone in python/src/chtypes/_abi2, carries the generated
1032
1091
  banner check-no-hand-decls.py exempts."""
1033
1092
  spec = FUNCTIONS[name]
1034
1093
  fn = api._raw[name]
@@ -1,7 +1,7 @@
1
- # GENERATED by scripts/abi-v1/gen.py from spec/abi-v1/abi.json (CHS_ABI_FINGERPRINT sha256:856e5cb7543db291b4db716662bf6e487b2258973b959bbf5d58b4bd46d73285) — DO NOT EDIT # noqa: E501
2
- """spec/abi-v1/sdk.json's error-mapping tables, as references to the
1
+ # GENERATED by scripts/abi-v1/gen.py --major 2 from spec/abi-v2/abi.json (CHS_ABI_FINGERPRINT sha256:d87e91be2bbe75b985c38224268c2f7dc5d8236dab0a4b4d61357c6dfdc1a232) — DO NOT EDIT # noqa: E501
2
+ """spec/abi-v2/sdk.json's error-mapping tables, as references to the
3
3
  public classes (python/src/chtypes/errors.py, re-exported by
4
- python/src/chtypes/_abi1/_errors.py). Only the MAPPING is generated: the
4
+ python/src/chtypes/_abi2/_errors.py). Only the MAPPING is generated: the
5
5
  classes themselves are stable across a regeneration (plan section 3.4), so a
6
6
  status renumbering or a new loader refusal reason changes this file, never
7
7
  the classes.
@@ -1,4 +1,4 @@
1
- """The ABI v1 loader, Python half (plan PLAN-sdk-v1-ffi section 3.2/3.3).
1
+ """The ABI v2 loader, Python half (plan PLAN-sdk-v1-ffi section 3.2/3.3).
2
2
 
3
3
  Decoupled from the fetch PRs on purpose (plan section 3.1): `open()` takes a
4
4
  small, loader-owned `predicate` mapping rather than the fetch module's
@@ -8,16 +8,21 @@ wired up; `predicate` is passed through exactly as that seam promises.
8
8
 
9
9
  Every `chs_*` spelling in this module is forbidden by
10
10
  scripts/abi-v1/check-no-hand-decls.py (it is not a generated file), so this
11
- calls ONLY python/src/chtypes/_abi1/_decls.py's non-`chs_`-prefixed surface:
12
- `resolve_abi_version()`, `resolve_all()`, and the `Api` object they return.
13
-
14
- Steps 1-6 run in the order plan section 3.2 gives (with step 6's full
15
- resolve-all sweep run right after step 3, BEFORE step 4's
16
- `chs_build_info()` call -- step 4 needs that symbol already resolved, and
17
- `tests/fixtures/abi-v1/cases.json`'s `missing-chs_build_info` and
18
- `missing-chs_clickhouse_version` stub variants both expect the step 6
19
- `missing_symbol:<name>` reason, not a step 4 one, which is only possible if
20
- the full symbol sweep happens before chs_build_info is ever called). Step 7
11
+ calls ONLY python/src/chtypes/_abi2/_decls.py's non-`chs_`-prefixed surface:
12
+ `resolve_abi_version()`, `resolve_build_info()`, `resolve_all()`, and the `Api` object they return.
13
+
14
+ Steps 1-7 run in the documented order, the order Go, TypeScript and Rust
15
+ run: glibc (1), dlopen (2), `chs_abi_version` alone (3), `chs_build_info`
16
+ alone, parsed strictly and its fingerprint compared (4: rule r6's exact
17
+ message wins here), the signed-statement cross-check (5), and only then the
18
+ resolve-every-declared-symbol sweep (6). A library with another fingerprint
19
+ that also lacks a symbol this SDK declares (every fingerprint move that adds
20
+ an export meets one) is therefore refused with r6's message, never as
21
+ `missing_symbol:<name>` (public issue #537; the stub variant
22
+ `fingerprint-other-missing-symbol`). A symbol missing from step 4's own
23
+ `chs_build_info` is still `missing_symbol:chs_build_info`, and
24
+ `tests/fixtures/abi-v2/cases.json`'s other `missing-<sym>` variants still
25
+ expect `missing_symbol:<name>` from step 6. Step 7
21
26
  calls the image's once-per-image zone setup (empty zone = UTC unless the
22
27
  caller supplies one) and then its default settings.
23
28
  """
@@ -54,6 +59,25 @@ def _refuse(reason: str, path: str, want: object = None, got: object = None) ->
54
59
  raise cls(reason=reason, path=path, want=want, got=got)
55
60
 
56
61
 
62
+ def dev_fingerprint_message(sdk: str, library: str) -> str:
63
+ """Rule r6's exact refusal of a library whose fingerprint is not this dev
64
+ SDK's (spec/abi-v2/docs.md): `sdk` is the SDK's own fingerprint and
65
+ `library` the library's, both full "sha256:" spellings."""
66
+ return f"this SDK speaks dev fingerprint {sdk}; the library has {library} — update your dev SDK"
67
+
68
+
69
+ def _refuse_fingerprint(path: str, got: object) -> None:
70
+ """Loader step 4's fingerprint refusal: CHTYPES_ARTIFACT_INCOMPATIBLE.
71
+ While this generation's description is unstable (a 2.0.0-dev SDK), its
72
+ message is exactly rule r6's; after the lock it is the ordinary refusal."""
73
+ want = _decls.CHS_ABI_FINGERPRINT
74
+ cls = _errmap.loader_error_class("fingerprint")
75
+ if _decls.CHS_ABI_STABILITY == "unstable":
76
+ message = dev_fingerprint_message(want, str(got))
77
+ raise cls(message, reason="fingerprint", path=path, want=want, got=got)
78
+ raise cls(reason="fingerprint", path=path, want=want, got=got)
79
+
80
+
57
81
  def _glibc_version() -> tuple[int, int] | None:
58
82
  """Resolve `gnu_get_libc_version` dynamically from the running process:
59
83
  `ctypes.CDLL(None)` is the process image's own global symbols (glibc's,
@@ -179,27 +203,33 @@ def _open(
179
203
  if version != _decls.CHS_ABI_VERSION:
180
204
  _refuse("abi_version", path, want=_decls.CHS_ABI_VERSION, got=version)
181
205
 
206
+ # Step 4: chs_build_info ALONE, parsed strictly, then the fingerprint
207
+ # compared. Rule r6 is decided here, before the step 6 sweep can name a
208
+ # missing symbol instead (#537).
182
209
  try:
183
- api = _decls.resolve_all(lib)
210
+ build_info_fn = _decls.resolve_build_info(lib)
184
211
  except _decls.MissingSymbol as e:
185
212
  _refuse(f"missing_symbol:{e.name}", path, want=e.name, got=None)
186
- # _refuse always raises; the following line only satisfies a type
187
- # checker that cannot see that. (Never reached.)
188
- raise
189
-
190
- raw = api.build_info()
213
+ raise # never reached: _refuse always raises
214
+ raw = build_info_fn()
191
215
  if raw is None:
192
216
  _refuse("build_info_malformed", path, got="chs_build_info() returned NULL")
193
217
  info = _parse_build_info(raw, path)
194
218
 
195
219
  if info["abi_fingerprint"] != _decls.CHS_ABI_FINGERPRINT:
196
- _refuse(
197
- "fingerprint", path, want=_decls.CHS_ABI_FINGERPRINT, got=info.get("abi_fingerprint")
198
- )
220
+ _refuse_fingerprint(path, info.get("abi_fingerprint"))
199
221
 
222
+ # Step 5: the signed predicate against build_info.
200
223
  if not skip_step5:
201
224
  _cross_check(info, predicate, path)
202
225
 
226
+ # Step 6: resolve every declared symbol.
227
+ try:
228
+ api = _decls.resolve_all(lib)
229
+ except _decls.MissingSymbol as e:
230
+ _refuse(f"missing_symbol:{e.name}", path, want=e.name, got=None)
231
+ raise # never reached: _refuse always raises
232
+
203
233
  # Step 7: the once-per-image setup (docs/reference/bindings-v1.md section
204
234
  # 6): the image zone (empty = UTC), then the default settings when there
205
235
  # are any. After every handshake check and before any other call. A
@@ -231,7 +261,7 @@ def open(
231
261
  timezone: bytes = b"",
232
262
  defaults: bytes = b"",
233
263
  ) -> LoadResult:
234
- """Load and verify a v1 artifact at `path` against its (already verified
264
+ """Load and verify an ABI v2 artifact at `path` against its (already verified
235
265
  elsewhere) signed `predicate`, then set the image zone (`timezone`, empty
236
266
  = UTC) and the default settings (`defaults`, a JSON object of string
237
267
  values, empty for none): step 7. Raises an artifact error naming the exact