chtypes 0.1.0__py3-none-any.whl
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/__init__.py +202 -0
- chtypes/__main__.py +268 -0
- chtypes/_document.py +310 -0
- chtypes/_ed25519.py +115 -0
- chtypes/_manifest.py +125 -0
- chtypes/_native.py +689 -0
- chtypes/_rawjson.py +292 -0
- chtypes/discover.py +273 -0
- chtypes/errors.py +273 -0
- chtypes/fetch.py +1096 -0
- chtypes/py.typed +0 -0
- chtypes/registry.py +1184 -0
- chtypes/results.py +534 -0
- chtypes/transform.py +542 -0
- chtypes-0.1.0.dist-info/METADATA +447 -0
- chtypes-0.1.0.dist-info/RECORD +19 -0
- chtypes-0.1.0.dist-info/WHEEL +4 -0
- chtypes-0.1.0.dist-info/entry_points.txt +2 -0
- chtypes-0.1.0.dist-info/licenses/LICENSE +202 -0
chtypes/__init__.py
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"""chtypes — ClickHouse's own type system, per version, from Python.
|
|
2
|
+
|
|
3
|
+
One question, exactly: *if this row were inserted into this ClickHouse table on
|
|
4
|
+
this ClickHouse version, what would happen?* The answer comes from ClickHouse's
|
|
5
|
+
real C++ machinery (`DataTypeFactory`, `ISerialization`, `ReadHelpers`,
|
|
6
|
+
`evaluateMissingDefaults`, the TTL algorithms, `MergeTreeDataWriter::mergeBlock`)
|
|
7
|
+
vendored per release into a shared library behind the frozen `chs_*` C ABI.
|
|
8
|
+
Nothing here reimplements a coercion rule, which is why the answers are exact by
|
|
9
|
+
construction rather than approximately right.
|
|
10
|
+
|
|
11
|
+
from chtypes import Format, Registry
|
|
12
|
+
|
|
13
|
+
registry = Registry() # the search path: $CHTYPES_REGISTRY, the cache, …
|
|
14
|
+
library = registry.for_version("25.8") # a minor line or an exact patch
|
|
15
|
+
with library.compile_ddl("ts DateTime, seq UInt8") as schema:
|
|
16
|
+
batch = schema.rows(Format.JSON_EACH_ROW, b'{"ts":"2026-01-15 10:30:00","seq":256}\\n')
|
|
17
|
+
|
|
18
|
+
batch.outcome # Outcome.ACCEPTED — the insert would succeed
|
|
19
|
+
batch.rows[0].value("seq") # Value(text='0', ...) — and store 0
|
|
20
|
+
batch.transformed # Transform(column='seq', input='256', stored='0',
|
|
21
|
+
# reason='overflow_wrap', row=0)
|
|
22
|
+
|
|
23
|
+
Three things a caller must not skip, each of which cost this repository
|
|
24
|
+
something to learn:
|
|
25
|
+
|
|
26
|
+
* **`transformed` is the product.** ClickHouse returns success for every one of
|
|
27
|
+
those changes. Reading `outcome` alone tells a tenant their row was accepted
|
|
28
|
+
and nothing about the value the table will hold.
|
|
29
|
+
* **A row accepted per row may not be stored per batch.** `BatchResult.transformed`
|
|
30
|
+
folds in the storage layer's verdicts (`ttl_expired`, `ttl_column_expired`),
|
|
31
|
+
and `engine_rows` — when present — is the stored truth, not `rows`.
|
|
32
|
+
* **`Outcome.UNSUPPORTED` is not a rejection.** It means a real server might well
|
|
33
|
+
have accepted this and this build declines to guess. Treating it as either a
|
|
34
|
+
rejection or an acceptance manufactures a wrong answer the product never gave.
|
|
35
|
+
|
|
36
|
+
The specification in `spec/` is normative; `go/chtypes` (Go) is the reference
|
|
37
|
+
implementation. Where this binding and that package disagree, the package is
|
|
38
|
+
right.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
from __future__ import annotations
|
|
42
|
+
|
|
43
|
+
from ._native import ABI_REVISION
|
|
44
|
+
from ._rawjson import RawNumber, quote_bare_denormals
|
|
45
|
+
from .discover import (
|
|
46
|
+
QUERY_CHANGED_SETTINGS,
|
|
47
|
+
QUERY_SERVER_VERSION,
|
|
48
|
+
QUERY_TABLE_COLUMNS,
|
|
49
|
+
DiscoveredColumn,
|
|
50
|
+
ServerProfile,
|
|
51
|
+
parse_changed_settings_result,
|
|
52
|
+
parse_columns_result,
|
|
53
|
+
parse_version_result,
|
|
54
|
+
reconstruct_ddl,
|
|
55
|
+
)
|
|
56
|
+
from .errors import (
|
|
57
|
+
CODE_ARTIFACT_CORRUPT,
|
|
58
|
+
CODE_ARTIFACT_MISSING,
|
|
59
|
+
CODE_ARTIFACT_PINNED,
|
|
60
|
+
CODE_ARTIFACT_UNPUBLISHED,
|
|
61
|
+
CODE_ARTIFACT_UNTRUSTED,
|
|
62
|
+
CODE_SOURCE_UNREACHABLE,
|
|
63
|
+
CODE_UNSUPPORTED,
|
|
64
|
+
ArtifactCorruptError,
|
|
65
|
+
ArtifactError,
|
|
66
|
+
ArtifactMissingError,
|
|
67
|
+
ArtifactPinnedError,
|
|
68
|
+
ArtifactUnpublishedError,
|
|
69
|
+
ArtifactUntrustedError,
|
|
70
|
+
ChtypesError,
|
|
71
|
+
RegistryError,
|
|
72
|
+
SchemaError,
|
|
73
|
+
SourceUnreachableError,
|
|
74
|
+
UnsignedArtifactWarning,
|
|
75
|
+
UnsupportedError,
|
|
76
|
+
)
|
|
77
|
+
from .fetch import (
|
|
78
|
+
ENV_AUTOFETCH,
|
|
79
|
+
RELEASE_KEY_ID,
|
|
80
|
+
RELEASE_PUBLIC_KEY,
|
|
81
|
+
ensure,
|
|
82
|
+
fetch_destination,
|
|
83
|
+
fetch_lines,
|
|
84
|
+
registry_search_path,
|
|
85
|
+
)
|
|
86
|
+
from .registry import (
|
|
87
|
+
ENV_REGISTRY,
|
|
88
|
+
Block,
|
|
89
|
+
Filter,
|
|
90
|
+
Library,
|
|
91
|
+
Manifest,
|
|
92
|
+
Registry,
|
|
93
|
+
Schema,
|
|
94
|
+
default_registry_dir,
|
|
95
|
+
host_platform,
|
|
96
|
+
minor_of,
|
|
97
|
+
read_manifest,
|
|
98
|
+
verify_library,
|
|
99
|
+
)
|
|
100
|
+
from .results import (
|
|
101
|
+
COMPILE_DECLARED,
|
|
102
|
+
DOC_ALL,
|
|
103
|
+
DOC_DEFAULTS,
|
|
104
|
+
DOC_TRANSFORMS,
|
|
105
|
+
DOC_VALUES,
|
|
106
|
+
EXPORT_NONE,
|
|
107
|
+
LOSSLESS_REASONS,
|
|
108
|
+
BatchResult,
|
|
109
|
+
Column,
|
|
110
|
+
Computed,
|
|
111
|
+
DefaultKind,
|
|
112
|
+
FilterOutcome,
|
|
113
|
+
FilterResult,
|
|
114
|
+
FilterRowError,
|
|
115
|
+
Format,
|
|
116
|
+
Outcome,
|
|
117
|
+
Reason,
|
|
118
|
+
RowResult,
|
|
119
|
+
Span,
|
|
120
|
+
Substitution,
|
|
121
|
+
Transform,
|
|
122
|
+
Value,
|
|
123
|
+
Verdict,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
__all__ = [
|
|
127
|
+
"ABI_REVISION",
|
|
128
|
+
"CODE_ARTIFACT_CORRUPT",
|
|
129
|
+
"CODE_ARTIFACT_MISSING",
|
|
130
|
+
"CODE_ARTIFACT_PINNED",
|
|
131
|
+
"CODE_ARTIFACT_UNPUBLISHED",
|
|
132
|
+
"CODE_ARTIFACT_UNTRUSTED",
|
|
133
|
+
"CODE_SOURCE_UNREACHABLE",
|
|
134
|
+
"CODE_UNSUPPORTED",
|
|
135
|
+
"COMPILE_DECLARED",
|
|
136
|
+
"DOC_ALL",
|
|
137
|
+
"DOC_DEFAULTS",
|
|
138
|
+
"DOC_TRANSFORMS",
|
|
139
|
+
"DOC_VALUES",
|
|
140
|
+
"ENV_AUTOFETCH",
|
|
141
|
+
"ENV_REGISTRY",
|
|
142
|
+
"default_registry_dir",
|
|
143
|
+
"EXPORT_NONE",
|
|
144
|
+
"RELEASE_KEY_ID",
|
|
145
|
+
"RELEASE_PUBLIC_KEY",
|
|
146
|
+
"LOSSLESS_REASONS",
|
|
147
|
+
"QUERY_CHANGED_SETTINGS",
|
|
148
|
+
"QUERY_SERVER_VERSION",
|
|
149
|
+
"QUERY_TABLE_COLUMNS",
|
|
150
|
+
"ArtifactCorruptError",
|
|
151
|
+
"ArtifactError",
|
|
152
|
+
"ArtifactMissingError",
|
|
153
|
+
"ArtifactPinnedError",
|
|
154
|
+
"ArtifactUnpublishedError",
|
|
155
|
+
"ArtifactUntrustedError",
|
|
156
|
+
"BatchResult",
|
|
157
|
+
"Block",
|
|
158
|
+
"ChtypesError",
|
|
159
|
+
"Column",
|
|
160
|
+
"Computed",
|
|
161
|
+
"DefaultKind",
|
|
162
|
+
"DiscoveredColumn",
|
|
163
|
+
"Filter",
|
|
164
|
+
"FilterOutcome",
|
|
165
|
+
"FilterResult",
|
|
166
|
+
"FilterRowError",
|
|
167
|
+
"Format",
|
|
168
|
+
"Library",
|
|
169
|
+
"Manifest",
|
|
170
|
+
"Outcome",
|
|
171
|
+
"RawNumber",
|
|
172
|
+
"Reason",
|
|
173
|
+
"Registry",
|
|
174
|
+
"RegistryError",
|
|
175
|
+
"RowResult",
|
|
176
|
+
"Schema",
|
|
177
|
+
"SchemaError",
|
|
178
|
+
"ServerProfile",
|
|
179
|
+
"SourceUnreachableError",
|
|
180
|
+
"Span",
|
|
181
|
+
"Substitution",
|
|
182
|
+
"Transform",
|
|
183
|
+
"UnsignedArtifactWarning",
|
|
184
|
+
"UnsupportedError",
|
|
185
|
+
"Value",
|
|
186
|
+
"Verdict",
|
|
187
|
+
"ensure",
|
|
188
|
+
"fetch_destination",
|
|
189
|
+
"fetch_lines",
|
|
190
|
+
"host_platform",
|
|
191
|
+
"minor_of",
|
|
192
|
+
"parse_changed_settings_result",
|
|
193
|
+
"parse_columns_result",
|
|
194
|
+
"parse_version_result",
|
|
195
|
+
"quote_bare_denormals",
|
|
196
|
+
"read_manifest",
|
|
197
|
+
"reconstruct_ddl",
|
|
198
|
+
"registry_search_path",
|
|
199
|
+
"verify_library",
|
|
200
|
+
]
|
|
201
|
+
|
|
202
|
+
__version__ = "0.1.0"
|
chtypes/__main__.py
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
"""``python -m chtypes`` and the ``chtypes`` console script — docs/fetch.md §6.
|
|
2
|
+
|
|
3
|
+
chtypes fetch <line>... [--all] [--platform <os-arch>] [--dest <dir>]
|
|
4
|
+
[--tag <t> | --url <base>] [--lock <file>] [--frozen]
|
|
5
|
+
[--force] [--offline]
|
|
6
|
+
chtypes verify [--dest <dir>] re-hash every installed line against its manifest
|
|
7
|
+
chtypes list [--dest <dir>] what is installed, and what the release offers
|
|
8
|
+
chtypes where the registry directory fetch would write to
|
|
9
|
+
|
|
10
|
+
Exit codes: 0 ok · 1 verification failed · 2 usage · 3 source unreachable ·
|
|
11
|
+
4 not published for this platform/line. Progress goes to stderr; ``fetch``
|
|
12
|
+
prints the installed directory alone on stdout, one per line.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import argparse
|
|
18
|
+
import sys
|
|
19
|
+
import warnings
|
|
20
|
+
from collections.abc import Sequence
|
|
21
|
+
|
|
22
|
+
from ._manifest import Manifest
|
|
23
|
+
from .errors import (
|
|
24
|
+
CODE_ARTIFACT_UNPUBLISHED,
|
|
25
|
+
CODE_SOURCE_UNREACHABLE,
|
|
26
|
+
ArtifactError,
|
|
27
|
+
ChtypesError,
|
|
28
|
+
UnsignedArtifactWarning,
|
|
29
|
+
)
|
|
30
|
+
from .fetch import DEFAULT_ARTIFACTS_URL, DEFAULT_TAG, PLATFORMS, ReleaseEntry
|
|
31
|
+
|
|
32
|
+
__all__ = ["main"]
|
|
33
|
+
|
|
34
|
+
EXIT_OK = 0
|
|
35
|
+
EXIT_VERIFY_FAILED = 1
|
|
36
|
+
EXIT_USAGE = 2
|
|
37
|
+
EXIT_UNREACHABLE = 3
|
|
38
|
+
EXIT_UNPUBLISHED = 4
|
|
39
|
+
|
|
40
|
+
_EXIT_FOR_CODE = {
|
|
41
|
+
CODE_SOURCE_UNREACHABLE: EXIT_UNREACHABLE,
|
|
42
|
+
CODE_ARTIFACT_UNPUBLISHED: EXIT_UNPUBLISHED,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _say(line: str) -> None:
|
|
47
|
+
sys.stderr.write(line + "\n")
|
|
48
|
+
sys.stderr.flush()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class _Parser(argparse.ArgumentParser):
|
|
52
|
+
"""argparse exits 2 on usage errors already; this keeps the message shape ours."""
|
|
53
|
+
|
|
54
|
+
def error(self, message: str) -> None: # type: ignore[override]
|
|
55
|
+
self.print_usage(sys.stderr)
|
|
56
|
+
_say(f"chtypes: {message}")
|
|
57
|
+
raise SystemExit(EXIT_USAGE)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
61
|
+
parser = _Parser(
|
|
62
|
+
prog="chtypes",
|
|
63
|
+
description="Fetch, verify and locate chtypes artifacts (docs/fetch.md).",
|
|
64
|
+
)
|
|
65
|
+
sub = parser.add_subparsers(dest="command", metavar="<command>")
|
|
66
|
+
sub.required = True
|
|
67
|
+
|
|
68
|
+
def dest_option(p: argparse.ArgumentParser) -> None:
|
|
69
|
+
p.add_argument(
|
|
70
|
+
"--dest",
|
|
71
|
+
metavar="<dir>",
|
|
72
|
+
help="the registry directory (default: $CHTYPES_REGISTRY, else the per-user cache)",
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
fetch = sub.add_parser(
|
|
76
|
+
"fetch",
|
|
77
|
+
help="install one or more ClickHouse lines, verified",
|
|
78
|
+
description="Install ClickHouse lines through the verification chain (docs/fetch.md §3).",
|
|
79
|
+
)
|
|
80
|
+
fetch.add_argument(
|
|
81
|
+
"lines",
|
|
82
|
+
nargs="*",
|
|
83
|
+
metavar="<line>",
|
|
84
|
+
help="a minor line (25.8) or an exact patch (25.8.28.1-lts, a hard requirement)",
|
|
85
|
+
)
|
|
86
|
+
fetch.add_argument("--all", action="store_true", help="every line the release publishes")
|
|
87
|
+
fetch.add_argument(
|
|
88
|
+
"--platform",
|
|
89
|
+
metavar="<os-arch>",
|
|
90
|
+
choices=PLATFORMS,
|
|
91
|
+
help=f"one of {', '.join(PLATFORMS)} (default: this host)",
|
|
92
|
+
)
|
|
93
|
+
dest_option(fetch)
|
|
94
|
+
where_from = fetch.add_mutually_exclusive_group()
|
|
95
|
+
where_from.add_argument(
|
|
96
|
+
"--tag",
|
|
97
|
+
metavar="<t>",
|
|
98
|
+
help=f"a release tag on the artifacts host (default: the rolling '{DEFAULT_TAG}')",
|
|
99
|
+
)
|
|
100
|
+
where_from.add_argument(
|
|
101
|
+
"--url",
|
|
102
|
+
metavar="<base>",
|
|
103
|
+
help=f"any base: an http(s) URL, a file:// URL or a directory "
|
|
104
|
+
f"(default: $CHTYPES_ARTIFACTS_URL or {DEFAULT_ARTIFACTS_URL}, plus the tag)",
|
|
105
|
+
)
|
|
106
|
+
fetch.add_argument(
|
|
107
|
+
"--lock", metavar="<file>", help="record what was installed in this lock file (§5)"
|
|
108
|
+
)
|
|
109
|
+
fetch.add_argument(
|
|
110
|
+
"--frozen",
|
|
111
|
+
action="store_true",
|
|
112
|
+
help="refuse anything the lock file does not pin (default lock: chtypes.lock)",
|
|
113
|
+
)
|
|
114
|
+
fetch.add_argument("--force", action="store_true", help="re-download an installed line")
|
|
115
|
+
fetch.add_argument(
|
|
116
|
+
"--offline",
|
|
117
|
+
action="store_true",
|
|
118
|
+
help="never touch the source: installed and verified, or CHTYPES_SOURCE_UNREACHABLE",
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
verify = sub.add_parser(
|
|
122
|
+
"verify",
|
|
123
|
+
help="re-hash every installed line against its manifest",
|
|
124
|
+
description="Re-hash every installed line's library against its own manifest.json.",
|
|
125
|
+
)
|
|
126
|
+
dest_option(verify)
|
|
127
|
+
|
|
128
|
+
listing = sub.add_parser(
|
|
129
|
+
"list",
|
|
130
|
+
help="what is installed, and what the release offers",
|
|
131
|
+
description="What is installed in the registry, and what the release offers for it.",
|
|
132
|
+
)
|
|
133
|
+
dest_option(listing)
|
|
134
|
+
listing.add_argument("--platform", metavar="<os-arch>", choices=PLATFORMS)
|
|
135
|
+
lfrom = listing.add_mutually_exclusive_group()
|
|
136
|
+
lfrom.add_argument("--tag", metavar="<t>")
|
|
137
|
+
lfrom.add_argument("--url", metavar="<base>")
|
|
138
|
+
|
|
139
|
+
where = sub.add_parser(
|
|
140
|
+
"where",
|
|
141
|
+
help="the registry directory fetch would write to",
|
|
142
|
+
description="Print the registry directory fetch would write to (docs/fetch.md §1).",
|
|
143
|
+
)
|
|
144
|
+
where.add_argument("--platform", metavar="<os-arch>", choices=PLATFORMS)
|
|
145
|
+
return parser
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _cmd_fetch(args: argparse.Namespace) -> int:
|
|
149
|
+
from .fetch import fetch_lines
|
|
150
|
+
|
|
151
|
+
installed = fetch_lines(
|
|
152
|
+
args.lines,
|
|
153
|
+
all_lines=args.all,
|
|
154
|
+
dest=args.dest,
|
|
155
|
+
platform=args.platform,
|
|
156
|
+
url=args.url,
|
|
157
|
+
tag=args.tag,
|
|
158
|
+
lock=args.lock,
|
|
159
|
+
frozen=args.frozen,
|
|
160
|
+
force=args.force,
|
|
161
|
+
offline=args.offline,
|
|
162
|
+
progress=_say,
|
|
163
|
+
)
|
|
164
|
+
for directory in installed:
|
|
165
|
+
sys.stdout.write(f"{directory}\n")
|
|
166
|
+
sys.stdout.flush()
|
|
167
|
+
return EXIT_OK
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _cmd_verify(args: argparse.Namespace) -> int:
|
|
171
|
+
from .fetch import fetch_destination, verify_registry
|
|
172
|
+
|
|
173
|
+
registry = fetch_destination(args.dest)
|
|
174
|
+
report = verify_registry(registry)
|
|
175
|
+
if not report:
|
|
176
|
+
_say(f"chtypes: nothing installed under {registry}")
|
|
177
|
+
return EXIT_OK
|
|
178
|
+
failed = 0
|
|
179
|
+
for minor, directory, error in report:
|
|
180
|
+
if error is None:
|
|
181
|
+
sys.stdout.write(f"{minor:<8} ok {directory}\n")
|
|
182
|
+
else:
|
|
183
|
+
failed += 1
|
|
184
|
+
sys.stdout.write(f"{minor:<8} FAILED {directory}\n")
|
|
185
|
+
_say(f" {error}")
|
|
186
|
+
sys.stdout.flush()
|
|
187
|
+
if failed:
|
|
188
|
+
_say(f"chtypes: {failed} of {len(report)} installed line(s) FAILED verification")
|
|
189
|
+
return EXIT_VERIFY_FAILED
|
|
190
|
+
_say(f"chtypes: {len(report)} installed line(s) verified under {registry}")
|
|
191
|
+
return EXIT_OK
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _cmd_list(args: argparse.Namespace) -> int:
|
|
195
|
+
from .fetch import Fetcher, fetch_destination, host_platform, installed_lines
|
|
196
|
+
|
|
197
|
+
platform = args.platform or host_platform()
|
|
198
|
+
registry = fetch_destination(args.dest, platform=platform)
|
|
199
|
+
have = installed_lines(registry)
|
|
200
|
+
sys.stdout.write(f"installed ({registry}):\n")
|
|
201
|
+
if not have:
|
|
202
|
+
sys.stdout.write(" (nothing)\n")
|
|
203
|
+
for minor, (directory, manifest) in have.items():
|
|
204
|
+
sys.stdout.write(f" {minor:<8} {manifest.clickhouse_version:<18} {directory}\n")
|
|
205
|
+
sys.stdout.flush()
|
|
206
|
+
|
|
207
|
+
fetcher = Fetcher(dest=registry, platform=platform, url=args.url, tag=args.tag, progress=_say)
|
|
208
|
+
release = fetcher.release()
|
|
209
|
+
offered = release.offered(platform)
|
|
210
|
+
signed = f"signed by key {release.signed_by}" if release.signed_by else "UNSIGNED"
|
|
211
|
+
sys.stdout.write(f"release ({release.source}, {signed}) offers for {platform}:\n")
|
|
212
|
+
if not offered:
|
|
213
|
+
sys.stdout.write(" (nothing)\n")
|
|
214
|
+
for entry in offered:
|
|
215
|
+
state = "installed" if _installed(have, entry) else "not installed"
|
|
216
|
+
sys.stdout.write(
|
|
217
|
+
f" {entry.minor:<8} {entry.clickhouse_version:<18} {entry.file} [{state}]\n"
|
|
218
|
+
)
|
|
219
|
+
sys.stdout.flush()
|
|
220
|
+
return EXIT_OK
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def _installed(have: dict[str, tuple[object, Manifest]], entry: ReleaseEntry) -> bool:
|
|
224
|
+
got = have.get(entry.minor)
|
|
225
|
+
return got is not None and got[1].clickhouse_version == entry.clickhouse_version
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def _cmd_where(args: argparse.Namespace) -> int:
|
|
229
|
+
from .fetch import fetch_destination
|
|
230
|
+
|
|
231
|
+
sys.stdout.write(f"{fetch_destination(platform=args.platform)}\n")
|
|
232
|
+
return EXIT_OK
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
_COMMANDS = {
|
|
236
|
+
"fetch": _cmd_fetch,
|
|
237
|
+
"verify": _cmd_verify,
|
|
238
|
+
"list": _cmd_list,
|
|
239
|
+
"where": _cmd_where,
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def main(argv: Sequence[str] | None = None) -> int:
|
|
244
|
+
"""Run the CLI; returns the exit code (docs/fetch.md §6)."""
|
|
245
|
+
parser = build_parser()
|
|
246
|
+
args = parser.parse_args(list(sys.argv[1:] if argv is None else argv))
|
|
247
|
+
# The library's loud warning reaches stderr through the progress channel
|
|
248
|
+
# here; the `warnings` copy would print it a second time.
|
|
249
|
+
warnings.simplefilter("ignore", UnsignedArtifactWarning)
|
|
250
|
+
try:
|
|
251
|
+
return _COMMANDS[args.command](args)
|
|
252
|
+
except ValueError as exc: # a bad option combination or spelling: usage
|
|
253
|
+
_say(str(exc))
|
|
254
|
+
return EXIT_USAGE
|
|
255
|
+
except ArtifactError as exc:
|
|
256
|
+
_say(str(exc))
|
|
257
|
+
_say(f"chtypes: {exc.code}")
|
|
258
|
+
return _EXIT_FOR_CODE.get(exc.code, EXIT_VERIFY_FAILED)
|
|
259
|
+
except ChtypesError as exc:
|
|
260
|
+
_say(str(exc))
|
|
261
|
+
return EXIT_VERIFY_FAILED
|
|
262
|
+
except KeyboardInterrupt:
|
|
263
|
+
_say("chtypes: interrupted")
|
|
264
|
+
return 130
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
if __name__ == "__main__":
|
|
268
|
+
sys.exit(main())
|