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.
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/PKG-INFO +6 -2
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/README.md +5 -1
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/pyproject.toml +1 -1
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/__init__.py +9 -1
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/__main__.py +20 -0
- {chtypes-1.1.0/src/chtypes/_abi1 → chtypes-2.0.0.dev1/src/chtypes/_abi2}/__init__.py +5 -3
- {chtypes-1.1.0/src/chtypes/_abi1 → chtypes-2.0.0.dev1/src/chtypes/_abi2}/_decls.py +79 -20
- {chtypes-1.1.0/src/chtypes/_abi1 → chtypes-2.0.0.dev1/src/chtypes/_abi2}/_errmap.py +3 -3
- {chtypes-1.1.0/src/chtypes/_abi1 → chtypes-2.0.0.dev1/src/chtypes/_abi2}/_loader.py +51 -21
- chtypes-2.0.0.dev1/src/chtypes/_abi2/_vocab.py +416 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/_decode.py +13 -15
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/_ocifetch/__init__.py +6 -0
- chtypes-2.0.0.dev1/src/chtypes/_ocifetch/_channel.py +325 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/_ocifetch/_ensure.py +36 -9
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/_ocifetch/_layout.py +25 -14
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/_ocifetch/_lock.py +5 -3
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/errors.py +1 -1
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/library.py +5 -3
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/registry.py +31 -4
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/results.py +1 -1
- {chtypes-1.1.0/tests/abi1 → chtypes-2.0.0.dev1/tests/abi2}/__init__.py +1 -1
- {chtypes-1.1.0/tests/abi1 → chtypes-2.0.0.dev1/tests/abi2}/conftest.py +28 -11
- {chtypes-1.1.0/tests/abi1 → chtypes-2.0.0.dev1/tests/abi2}/test_conformance.py +6 -6
- chtypes-2.0.0.dev1/tests/abi2/test_dev_fingerprint.py +80 -0
- {chtypes-1.1.0/tests/abi1 → chtypes-2.0.0.dev1/tests/abi2}/test_goldens_runner_stub.py +22 -7
- {chtypes-1.1.0/tests/abi1 → chtypes-2.0.0.dev1/tests/abi2}/test_loader.py +3 -3
- {chtypes-1.1.0/tests/abi1 → chtypes-2.0.0.dev1/tests/abi2}/test_public_library.py +13 -6
- {chtypes-1.1.0/tests/abi1 → chtypes-2.0.0.dev1/tests/abi2}/test_public_open.py +1 -1
- {chtypes-1.1.0/tests/abi1 → chtypes-2.0.0.dev1/tests/abi2}/test_public_registry.py +16 -1
- {chtypes-1.1.0/tests/abi1 → chtypes-2.0.0.dev1/tests/abi2}/test_public_setup_cases.py +3 -3
- chtypes-2.0.0.dev1/tests/abi2/test_reader_rules.py +243 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/api/test_decode.py +37 -13
- chtypes-2.0.0.dev1/tests/api/test_vocab.py +124 -0
- chtypes-2.0.0.dev1/tests/cli/conftest.py +26 -0
- chtypes-2.0.0.dev1/tests/cli/test_devchannel.py +126 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/cli/test_main.py +4 -2
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/conftest.py +1 -1
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/goldens_v1/test_runner.py +26 -6
- chtypes-2.0.0.dev1/tests/ocifetch/conftest.py +28 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/ocifetch/test_conformance.py +12 -0
- chtypes-2.0.0.dev1/tests/ocifetch/test_devchannel.py +268 -0
- chtypes-2.0.0.dev1/tests/ocifetch/test_seam_gate.py +57 -0
- chtypes-2.0.0.dev1/tests/test_abi_identity.py +24 -0
- chtypes-2.0.0.dev1/tests/test_devchannel_registry.py +98 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/test_fetch_options.py +9 -4
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/uv.lock +2 -2
- chtypes-1.1.0/src/chtypes/_abi1/_vocab.py +0 -254
- chtypes-1.1.0/tests/api/test_vocab.py +0 -90
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/.gitignore +0 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/CHANGELOG.md +0 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/LICENSE +0 -0
- {chtypes-1.1.0/src/chtypes/_abi1 → chtypes-2.0.0.dev1/src/chtypes/_abi2}/_errors.py +0 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/_ed25519.py +0 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/_guard.py +0 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/_input.py +0 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/_ocifetch/_constants.py +0 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/_ocifetch/_dsse.py +0 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/_ocifetch/_errors.py +0 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/_ocifetch/_faults.py +0 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/_ocifetch/_goldens.py +0 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/_ocifetch/_http.py +0 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/_ocifetch/_oci.py +0 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/_ocifetch/_referrers.py +0 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/_ocifetch/_unpack.py +0 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/_setup.py +0 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/src/chtypes/py.typed +0 -0
- {chtypes-1.1.0/tests/abi1 → chtypes-2.0.0.dev1/tests/abi2}/_stub_tree.py +0 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/api/__init__.py +0 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/api/test_input.py +0 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/cli/__init__.py +0 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/goldens_v1/__init__.py +0 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/goldens_v1/data/stub-goldens.json +0 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/ocifetch/__init__.py +0 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/ocifetch/_bundle_support.py +0 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/ocifetch/_registry_support.py +0 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/ocifetch/_sign_support.py +0 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/ocifetch/test_cache_roots.py +0 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/ocifetch/test_cache_strict.py +0 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/ocifetch/test_dsse.py +0 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/ocifetch/test_ensure.py +0 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/ocifetch/test_goldens.py +0 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/ocifetch/test_http.py +0 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/ocifetch/test_layout.py +0 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/ocifetch/test_lock.py +0 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/ocifetch/test_oci.py +0 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/ocifetch/test_unpack.py +0 -0
- {chtypes-1.1.0 → chtypes-2.0.0.dev1}/tests/test_ed25519.py +0 -0
- {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:
|
|
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,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 .
|
|
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
|
|
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
|
|
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-
|
|
2
|
-
"""The Python binding's generated ABI
|
|
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-
|
|
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/
|
|
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 =
|
|
22
|
-
CHS_ABI_FINGERPRINT = "sha256:
|
|
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
|
|
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
|
|
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 --
|
|
923
|
-
INCLUDED, even though _loader.py's
|
|
924
|
-
resolved
|
|
925
|
-
below needs every symbol reachable through ONE
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
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/
|
|
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-
|
|
2
|
-
"""spec/abi-
|
|
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/
|
|
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
|
|
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/
|
|
12
|
-
`resolve_abi_version()`, `resolve_all()`, and the `Api` object they return.
|
|
13
|
-
|
|
14
|
-
Steps 1-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
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
|
|
187
|
-
|
|
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
|
-
|
|
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
|
|
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
|