chtypes 2.0.0.dev11__py3-none-any.whl → 2.0.0.dev12__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 CHANGED
@@ -13,14 +13,15 @@ C ABI. Nothing here reimplements a coercion rule, which is why the answers are
13
13
  exact by construction rather than approximately right.
14
14
 
15
15
  import chtypes
16
- from chtypes import Format, Registry
16
+ from chtypes import DocFlags, Format, Registry
17
17
 
18
18
  chtypes.setup(timezone="UTC") # once, before the first open
19
19
  registry = Registry(autofetch=True)
20
20
  library = registry.for_version("26.8") # a release line or an exact version
21
21
  ddl = b"CREATE TABLE t (ts DateTime, seq UInt8) ENGINE = Memory"
22
22
  with library.compile_table(ddl) as schema:
23
- batch = schema.rows(Format.JSON_EACH_ROW, b'{"ts":"2026-01-15 10:30:00","seq":256}\\n')
23
+ body = b'{"ts":"2026-01-15 10:30:00","seq":256}\\n'
24
+ batch = schema.rows(Format.JSON_EACH_ROW, body, doc_flags=DocFlags.ALL)
24
25
 
25
26
  batch.outcome # Outcome.ACCEPTED: the insert would succeed
26
27
  batch.transformed # the changes ClickHouse makes and reports as success
@@ -1296,6 +1296,48 @@ def _ensure(
1296
1296
  raise _translate_transport_error(e) from e
1297
1297
 
1298
1298
 
1299
+ def _local_bundle_referrers(
1300
+ blobs_dir: Path, skip: Collection[str], known: dict[str, bytes] | None = None
1301
+ ) -> dict[str, list[tuple[str, dict]]]:
1302
+ """Each subject manifest digest's signature referrer manifests among
1303
+ `blobs_dir`'s blobs, as (blob name, parsed manifest), in listing order.
1304
+ A referrer need not be listed in `index.json`, so every blob that could
1305
+ be one is read, once per lookup rather than once per entry. A blob named
1306
+ in `skip` (the caller's candidate layers) and a blob larger than
1307
+ `limits.manifest_bytes`, which cannot be a manifest, are never read, so
1308
+ no layer is read here, and a blob in `known` (bytes the caller already
1309
+ read, by name) is not read again (public issue #690)."""
1310
+ out: dict[str, list[tuple[str, dict]]] = {}
1311
+ try:
1312
+ blob_paths = list(blobs_dir.iterdir())
1313
+ except OSError:
1314
+ return out
1315
+ for blob_path in blob_paths:
1316
+ if blob_path.name in skip:
1317
+ continue
1318
+ try:
1319
+ body = known.get(blob_path.name) if known is not None else None
1320
+ if body is None:
1321
+ if blob_path.stat().st_size > C.MANIFEST_MAX_BYTES:
1322
+ continue
1323
+ body = blob_path.read_bytes()
1324
+ doc = json.loads(body)
1325
+ except (ValueError, OSError, UnicodeDecodeError):
1326
+ continue
1327
+ if not isinstance(doc, dict) or doc.get("mediaType") != C.MEDIA_TYPE_MANIFEST:
1328
+ continue
1329
+ subject = doc.get("subject") or {}
1330
+ subject_digest = subject.get("digest") if isinstance(subject, dict) else None
1331
+ if not isinstance(subject_digest, str):
1332
+ continue
1333
+ # A signature referrer only: a revocation of the manifest carries the same
1334
+ # bundle media type, and is not the signature of the artifact.
1335
+ if doc.get("artifactType") != C.MEDIA_TYPE_BUNDLE:
1336
+ continue
1337
+ out.setdefault(subject_digest, []).append((blob_path.name, doc))
1338
+ return out
1339
+
1340
+
1299
1341
  def _verify_and_install_from_local_blobs(
1300
1342
  *,
1301
1343
  source_root: Path,
@@ -1303,6 +1345,7 @@ def _verify_and_install_from_local_blobs(
1303
1345
  cache_write_root: Path,
1304
1346
  trusted_keys: tuple[TrustedKey, ...],
1305
1347
  visible_only: bool = False,
1348
+ referrers: dict[str, list[tuple[str, dict]]] | None = None,
1306
1349
  ) -> VerifiedRecord | None:
1307
1350
  """docs/guides/fetch-v1.md §1/§6: "A pre-seeded layout has entries in
1308
1351
  `index.json` with no corresponding `unpacked/` directory yet; the first
@@ -1328,23 +1371,12 @@ def _verify_and_install_from_local_blobs(
1328
1371
  except (ValueError, OSError, FetchError):
1329
1372
  return None
1330
1373
 
1374
+ if referrers is None:
1375
+ referrers = _local_bundle_referrers(blobs_dir, {parse_digest(layer_desc.digest)})
1331
1376
  verified = None
1332
1377
  bundle_digest = None
1333
1378
  bundle_manifest_digest = None
1334
- for blob_path in blobs_dir.iterdir():
1335
- try:
1336
- doc = json.loads(blob_path.read_bytes())
1337
- except (ValueError, OSError, UnicodeDecodeError):
1338
- continue
1339
- if not isinstance(doc, dict) or doc.get("mediaType") != C.MEDIA_TYPE_MANIFEST:
1340
- continue
1341
- subject = doc.get("subject") or {}
1342
- if subject.get("digest") != manifest_digest:
1343
- continue
1344
- # A signature referrer only: a revocation of the manifest carries the same
1345
- # bundle media type, and is not the signature of the artifact.
1346
- if doc.get("artifactType") != C.MEDIA_TYPE_BUNDLE:
1347
- continue
1379
+ for referrer_name, doc in referrers.get(manifest_digest, ()):
1348
1380
  try:
1349
1381
  referrer_layer = manifest_single_layer(doc, expected_media_type=C.MEDIA_TYPE_BUNDLE)
1350
1382
  bundle_bytes = (blobs_dir / parse_digest(referrer_layer.digest)).read_bytes()
@@ -1354,7 +1386,7 @@ def _verify_and_install_from_local_blobs(
1354
1386
  if candidate is not None:
1355
1387
  verified = candidate
1356
1388
  bundle_digest = referrer_layer.digest
1357
- bundle_manifest_digest = "sha256:" + blob_path.name
1389
+ bundle_manifest_digest = "sha256:" + referrer_name
1358
1390
  break
1359
1391
  if verified is None:
1360
1392
  return None
@@ -1422,7 +1454,9 @@ def _verify_preseeded_entries(roots: Sequence[Path], trusted_keys: tuple[Trusted
1422
1454
  anywhere yet, and verifies+unpacks it from that root's OWN local blobs,
1423
1455
  writing the result into `roots[0]` (the writable cache) regardless of
1424
1456
  which root — including a read-only system directory — the blobs came
1425
- from."""
1457
+ from. An entry that already has a record is passed over without reading
1458
+ a blob, and each root's signature referrers are read once, never by
1459
+ reading a candidate's layer (public issue #690)."""
1426
1460
  cache_write_root = roots[0]
1427
1461
  already_verified = {record.manifest for _dir, record in list_verified_records(roots)}
1428
1462
  for root in roots:
@@ -1431,16 +1465,36 @@ def _verify_preseeded_entries(roots: Sequence[Path], trusted_keys: tuple[Trusted
1431
1465
  index_doc = json.loads(index_path.read_text(encoding="utf-8"))
1432
1466
  except (OSError, ValueError):
1433
1467
  continue
1468
+ blobs_dir = root / "blobs" / "sha256"
1469
+ candidates: list[str] = []
1470
+ candidate_layers: set[str] = set()
1471
+ manifests_read: dict[str, bytes] = {}
1434
1472
  for m in index_doc.get("manifests") or []:
1435
1473
  digest = m.get("digest")
1436
1474
  if not digest or digest in already_verified:
1437
1475
  continue
1476
+ candidates.append(digest)
1477
+ try:
1478
+ manifest_hex = parse_digest(digest)
1479
+ manifest_bytes = (blobs_dir / manifest_hex).read_bytes()
1480
+ manifests_read[manifest_hex] = manifest_bytes
1481
+ layer = manifest_layer_descriptor(json.loads(manifest_bytes))
1482
+ candidate_layers.add(parse_digest(layer.digest))
1483
+ except (ValueError, OSError, FetchError):
1484
+ continue
1485
+ if not candidates:
1486
+ continue
1487
+ referrers = _local_bundle_referrers(blobs_dir, candidate_layers, manifests_read)
1488
+ for digest in candidates:
1489
+ if digest in already_verified:
1490
+ continue
1438
1491
  record = _verify_and_install_from_local_blobs(
1439
1492
  source_root=root,
1440
1493
  manifest_digest=digest,
1441
1494
  cache_write_root=cache_write_root,
1442
1495
  trusted_keys=trusted_keys,
1443
1496
  visible_only=True,
1497
+ referrers=referrers,
1444
1498
  )
1445
1499
  if record is not None:
1446
1500
  already_verified.add(digest)
chtypes/library.py CHANGED
@@ -54,6 +54,8 @@ __all__ = ["Block", "Filter", "Library", "Schema", "Server", "open_unverified"]
54
54
  # One image per file, process-wide: keyed on the realpath's device and inode, so
55
55
  # two registries, two spellings or a hardlink of one artifact share one image
56
56
  # and one Library. Guarded by the setup lock, which an open holds throughout.
57
+ # The batch preview's default `doc_flags`: 0, the lean result (no per-value documents).
58
+ _LEAN_DOC_FLAGS = DocFlags(0)
57
59
  _IMAGES: dict[tuple[int, int], Library] = {}
58
60
 
59
61
 
@@ -212,7 +214,8 @@ class Filter(_Handle):
212
214
  insert_options_extra: Mapping[str, str] | None = None,
213
215
  ) -> FilterResult:
214
216
  """Evaluate over a body. `settings` and `session_timezone` are the body's
215
- PARSE settings only: the filter's own zone was fixed when it compiled.
217
+ INSERT settings: they govern its parse and the expressions the INSERT
218
+ evaluates, never the WHERE, whose zone was fixed when the filter compiled.
216
219
  `columns` is the INSERT column list and the keywords after it the
217
220
  `insert_options` members, as for `Schema.rows`."""
218
221
  payload = body_bytes(body)
@@ -304,11 +307,14 @@ class Schema(_Handle):
304
307
  insert_options_extra: Mapping[str, str] | None = None,
305
308
  row_filter: Filter | None = None,
306
309
  export: Format | int | None = None,
307
- doc_flags: DocFlags = DocFlags.ALL,
310
+ doc_flags: DocFlags = _LEAN_DOC_FLAGS,
308
311
  ) -> BatchResult:
309
312
  """A whole body. `export` asks for the accepted rows serialized in that
310
313
  format (`payload`, and `spans`); to insert a body whose rows carry a
311
- `default_generated` column, insert `payload`, never the original input."""
314
+ `default_generated` column, insert `payload`, never the original input.
315
+
316
+ `doc_flags` chooses the document groups; absent means 0, the lean result
317
+ (no per-value documents). Pass `DocFlags.ALL` for every group."""
312
318
  payload = body_bytes(body)
313
319
  settings_bytes = settings_json(settings, session_timezone)
314
320
  columns_bytes = columns_json(columns)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: chtypes
3
- Version: 2.0.0.dev11
3
+ Version: 2.0.0.dev12
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
@@ -43,7 +43,7 @@ uv add "chtypes>=2.0.0.dev0" # or: pip install "chtypes>=2.0.0.dev0"
43
43
 
44
44
  ```python
45
45
  import chtypes
46
- from chtypes import Format, Registry
46
+ from chtypes import DocFlags, Format, Registry
47
47
 
48
48
  chtypes.setup(timezone="UTC") # once, before the first open (optional)
49
49
  registry = Registry(autofetch=True)
@@ -51,7 +51,7 @@ library = registry.for_version("26.8") # a release line or an exact version
51
51
 
52
52
  ddl = b"CREATE TABLE t (x UInt8, ts DateTime DEFAULT now()) ENGINE = Memory"
53
53
  with library.compile_table(ddl) as schema:
54
- batch = schema.rows(Format.JSON_EACH_ROW, b'{"x":256}\n')
54
+ batch = schema.rows(Format.JSON_EACH_ROW, b'{"x":256}\n', doc_flags=DocFlags.ALL)
55
55
 
56
56
  row = batch.rows[0]
57
57
  print(batch.outcome) # accepted
@@ -1,4 +1,4 @@
1
- chtypes/__init__.py,sha256=cmfI6SOZBF7nU7awAbwH1l0HriwA3wWj15NOOGvptfc,6682
1
+ chtypes/__init__.py,sha256=8bwCCMfg7k233x9tK6Eg8lITeC8EDwSS5M8VCkxbuPY,6736
2
2
  chtypes/__main__.py,sha256=6qal_3mJBVfNc9LNHsSB0d9Sfurxix6QbNWG5TEa4L4,19392
3
3
  chtypes/_advisory.py,sha256=wTRNdwqLiOGsFKZQ7CYZHsfIhDrKc_arpaxEYZCUxV4,1212
4
4
  chtypes/_codes.py,sha256=mRhjP-7fOU6j15le7r1jHwLJkiClMIIIEtfOkriR7uE,1301
@@ -8,7 +8,7 @@ chtypes/_guard.py,sha256=kVgvxRWu7H-eba3hTqPBmpqjxOCWdQGwiFAHvSnl1OU,1996
8
8
  chtypes/_input.py,sha256=ZAUEs3r26_CsFiIvaXV9P-F_C9jlRRzMNKgG--G4HnQ,15157
9
9
  chtypes/_setup.py,sha256=7mXvPxLCHS9h1FRWNr5uKfCUg-Gk8VjmC-5KNW5HogI,6125
10
10
  chtypes/errors.py,sha256=-sCgdySXZn5HRL8Vkwbj9gSvhj0nvCCjwt3mFn6gKIc,10511
11
- chtypes/library.py,sha256=W7NywZEkogi5bcw8lGCEZZg3iwb6RF3e92BK5Y9yixg,21934
11
+ chtypes/library.py,sha256=MYlk7ksWiq3OP2nJ4YoqRhP2rqLc_xdRktdNQAnwjm0,22290
12
12
  chtypes/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  chtypes/registry.py,sha256=CQHnHr9T-2LyqRsYl5wdWKIVWCOiX9Vx4BLcZL2yCB0,23702
14
14
  chtypes/results.py,sha256=1HUdvhrjeEl9Fm1GOJUXVREtc7HN31s2yrQbm5_ZeKQ,15397
@@ -22,7 +22,7 @@ chtypes/_ocifetch/__init__.py,sha256=Nwmn1_JqkRSy-1q8182RT5m4HJfbdJgr54it7_TKvBg
22
22
  chtypes/_ocifetch/_channel.py,sha256=1MuIj5O8N1d8OdTN4Rx4zf3kc2Nh1hjx4o5BIE6JgmA,20484
23
23
  chtypes/_ocifetch/_constants.py,sha256=Gk-h6UkPg5dvNlhkr8z-UY4Y866unUPOSpE8ZwJvoJw,6287
24
24
  chtypes/_ocifetch/_dsse.py,sha256=nYkjmRefmVTonH1KUADNNUl0J9sh2C3fg-0WEyURjcc,9838
25
- chtypes/_ocifetch/_ensure.py,sha256=er9uP4MnyIfOZ3CJ2cyHolDdIw_rVYbPTZCod3RWmMw,77014
25
+ chtypes/_ocifetch/_ensure.py,sha256=3d00ZmJY7qjBQAsyEL6g3nikiHnaMm7BzoKTxn-EGts,79685
26
26
  chtypes/_ocifetch/_errors.py,sha256=SE0zcTUr1fSvSH7HiLGjAInYvD_im8_SyeZNf-VKzXI,7594
27
27
  chtypes/_ocifetch/_faults.py,sha256=HpoB9tJW7aLRiJdCFnspzkwhpFATZ6f6hGi49Mbfnhs,7609
28
28
  chtypes/_ocifetch/_goldens.py,sha256=8MicgeJSRd4WZkaEsdujRnOa7twQvbT-wfSMPZaD-ms,4191
@@ -37,8 +37,8 @@ chtypes/_ocifetch/_resolve.py,sha256=Qv8xFUdOR-Kb_JqmUeDiQ4_Xv2ETuNFpmoDckMp7eV8
37
37
  chtypes/_ocifetch/_retired.py,sha256=mqnI9RA7UK0A5RthCeLlBeVAu5SHbtwKFwxsSZk5e6A,7018
38
38
  chtypes/_ocifetch/_revocation.py,sha256=DdKRjFp1eFAxVD7DZHK1bj3_sWVLlaEkVEVx6JyV2Zs,23710
39
39
  chtypes/_ocifetch/_unpack.py,sha256=Cn4-up63KL63L8pnkS5zwJ6VbbHPSigpCnmOdcHJ4RI,5609
40
- chtypes-2.0.0.dev11.dist-info/METADATA,sha256=yBHAW6_UeO3y8EAGTPXlNrIOvqgBJ0b8tTV5v73xiyk,11331
41
- chtypes-2.0.0.dev11.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
42
- chtypes-2.0.0.dev11.dist-info/entry_points.txt,sha256=TdNcluWBR0VOt5mVFROsp-NiVPAdR3EHo0UsHlMC0r0,50
43
- chtypes-2.0.0.dev11.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
44
- chtypes-2.0.0.dev11.dist-info/RECORD,,
40
+ chtypes-2.0.0.dev12.dist-info/METADATA,sha256=3hHZvSuwop0hmBO2CrCF4szXxFd26F5plQWz3Jkf5oI,11365
41
+ chtypes-2.0.0.dev12.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
42
+ chtypes-2.0.0.dev12.dist-info/entry_points.txt,sha256=TdNcluWBR0VOt5mVFROsp-NiVPAdR3EHo0UsHlMC0r0,50
43
+ chtypes-2.0.0.dev12.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
44
+ chtypes-2.0.0.dev12.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.32.4
2
+ Generator: hatchling 1.33.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any