chtypes 2.0.0.dev11__py3-none-any.whl → 2.0.0.dev13__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
@@ -75,6 +75,7 @@ from chtypes._ocifetch._layout import (
75
75
  from chtypes._ocifetch._lock import Lock, LockPin, load_lock, new_lock, save_lock
76
76
  from chtypes._ocifetch._oci import (
77
77
  Descriptor,
78
+ copy_blob_verified,
78
79
  fetch_blob_bytes,
79
80
  fetch_blob_to_path,
80
81
  fetch_manifest_by_digest,
@@ -86,6 +87,8 @@ from chtypes._ocifetch._oci import (
86
87
  resolve_platform_manifest,
87
88
  spelling_components,
88
89
  validate_spelling,
90
+ verify_body_matches_digest,
91
+ verify_json_descriptor,
89
92
  version_within_request,
90
93
  )
91
94
  from chtypes._ocifetch._referrers import ReferrerListing, discover_referrers, read_listing
@@ -1296,6 +1299,48 @@ def _ensure(
1296
1299
  raise _translate_transport_error(e) from e
1297
1300
 
1298
1301
 
1302
+ def _local_bundle_referrers(
1303
+ blobs_dir: Path, skip: Collection[str], known: dict[str, bytes] | None = None
1304
+ ) -> dict[str, list[tuple[str, dict]]]:
1305
+ """Each subject manifest digest's signature referrer manifests among
1306
+ `blobs_dir`'s blobs, as (blob name, parsed manifest), in listing order.
1307
+ A referrer need not be listed in `index.json`, so every blob that could
1308
+ be one is read, once per lookup rather than once per entry. A blob named
1309
+ in `skip` (the caller's candidate layers) and a blob larger than
1310
+ `limits.manifest_bytes`, which cannot be a manifest, are never read, so
1311
+ no layer is read here, and a blob in `known` (bytes the caller already
1312
+ read, by name) is not read again (public issue #690)."""
1313
+ out: dict[str, list[tuple[str, dict]]] = {}
1314
+ try:
1315
+ blob_paths = list(blobs_dir.iterdir())
1316
+ except OSError:
1317
+ return out
1318
+ for blob_path in blob_paths:
1319
+ if blob_path.name in skip:
1320
+ continue
1321
+ try:
1322
+ body = known.get(blob_path.name) if known is not None else None
1323
+ if body is None:
1324
+ if blob_path.stat().st_size > C.MANIFEST_MAX_BYTES:
1325
+ continue
1326
+ body = blob_path.read_bytes()
1327
+ doc = json.loads(body)
1328
+ except (ValueError, OSError, UnicodeDecodeError):
1329
+ continue
1330
+ if not isinstance(doc, dict) or doc.get("mediaType") != C.MEDIA_TYPE_MANIFEST:
1331
+ continue
1332
+ subject = doc.get("subject") or {}
1333
+ subject_digest = subject.get("digest") if isinstance(subject, dict) else None
1334
+ if not isinstance(subject_digest, str):
1335
+ continue
1336
+ # A signature referrer only: a revocation of the manifest carries the same
1337
+ # bundle media type, and is not the signature of the artifact.
1338
+ if doc.get("artifactType") != C.MEDIA_TYPE_BUNDLE:
1339
+ continue
1340
+ out.setdefault(subject_digest, []).append((blob_path.name, doc))
1341
+ return out
1342
+
1343
+
1299
1344
  def _verify_and_install_from_local_blobs(
1300
1345
  *,
1301
1346
  source_root: Path,
@@ -1303,6 +1348,8 @@ def _verify_and_install_from_local_blobs(
1303
1348
  cache_write_root: Path,
1304
1349
  trusted_keys: tuple[TrustedKey, ...],
1305
1350
  visible_only: bool = False,
1351
+ referrers: dict[str, list[tuple[str, dict]]] | None = None,
1352
+ manifest_size: int | None = None,
1306
1353
  ) -> VerifiedRecord | None:
1307
1354
  """docs/guides/fetch-v1.md §1/§6: "A pre-seeded layout has entries in
1308
1355
  `index.json` with no corresponding `unpacked/` directory yet; the first
@@ -1314,7 +1361,14 @@ def _verify_and_install_from_local_blobs(
1314
1361
  `source_root/blobs/sha256/*`, never the network. Returns `None` — never
1315
1362
  raises — for a missing, unreadable or untrusted entry, the same
1316
1363
  tolerant shape `_find_verified_signature` uses for a network referrer:
1317
- a bad pre-seed is "nothing usable here," not a crash."""
1364
+ a bad pre-seed is "nothing usable here," not a crash.
1365
+
1366
+ The manifest blob is checked against `manifest_digest` (and against
1367
+ `manifest_size`, the index descriptor's size, when the caller has one)
1368
+ before it is parsed, and the layer blob against the manifest's layer
1369
+ descriptor (size and sha256) before it is decompressed: the same
1370
+ checks a download gets (§7.3), so no byte the signature does not cover
1371
+ reaches the install."""
1318
1372
  blobs_dir = source_root / "blobs" / "sha256"
1319
1373
  if not blobs_dir.is_dir():
1320
1374
  return None
@@ -1323,28 +1377,25 @@ def _verify_and_install_from_local_blobs(
1323
1377
  if not manifest_path.is_file():
1324
1378
  return None
1325
1379
  try:
1326
- manifest_doc = json.loads(manifest_path.read_bytes())
1380
+ manifest_bytes = manifest_path.read_bytes()
1381
+ verify_body_matches_digest(manifest_bytes, manifest_digest, what="pre-seeded manifest")
1382
+ if manifest_size is not None:
1383
+ verify_json_descriptor(
1384
+ manifest_bytes,
1385
+ Descriptor(media_type="", digest=manifest_digest, size=manifest_size),
1386
+ what="pre-seeded manifest",
1387
+ )
1388
+ manifest_doc = json.loads(manifest_bytes)
1327
1389
  layer_desc = manifest_layer_descriptor(manifest_doc)
1328
1390
  except (ValueError, OSError, FetchError):
1329
1391
  return None
1330
1392
 
1393
+ if referrers is None:
1394
+ referrers = _local_bundle_referrers(blobs_dir, {parse_digest(layer_desc.digest)})
1331
1395
  verified = None
1332
1396
  bundle_digest = None
1333
1397
  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
1398
+ for referrer_name, doc in referrers.get(manifest_digest, ()):
1348
1399
  try:
1349
1400
  referrer_layer = manifest_single_layer(doc, expected_media_type=C.MEDIA_TYPE_BUNDLE)
1350
1401
  bundle_bytes = (blobs_dir / parse_digest(referrer_layer.digest)).read_bytes()
@@ -1354,7 +1405,7 @@ def _verify_and_install_from_local_blobs(
1354
1405
  if candidate is not None:
1355
1406
  verified = candidate
1356
1407
  bundle_digest = referrer_layer.digest
1357
- bundle_manifest_digest = "sha256:" + blob_path.name
1408
+ bundle_manifest_digest = "sha256:" + referrer_name
1358
1409
  break
1359
1410
  if verified is None:
1360
1411
  return None
@@ -1383,9 +1434,18 @@ def _verify_and_install_from_local_blobs(
1383
1434
  scratch = _scratch_root(cache_write_root)
1384
1435
  tmp_dir = tempfile.mkdtemp(dir=str(scratch))
1385
1436
  try:
1437
+ # Size and sha256 against the layer descriptor BEFORE decompressing
1438
+ # (§7.3), on a copy in scratch, which is then the file unpacked.
1439
+ verified_layer = os.path.join(tmp_dir, "layer")
1440
+ copy_blob_verified(
1441
+ str(layer_path),
1442
+ layer_desc,
1443
+ verified_layer,
1444
+ what=f"pre-seeded layer {layer_desc.digest}",
1445
+ )
1386
1446
  unpacked_dir = os.path.join(tmp_dir, "unpacked")
1387
1447
  unpack_tar_zst(
1388
- str(layer_path),
1448
+ verified_layer,
1389
1449
  unpacked_dir,
1390
1450
  library_name=predicate["library"],
1391
1451
  library_sha256=predicate["library_sha256"],
@@ -1422,7 +1482,9 @@ def _verify_preseeded_entries(roots: Sequence[Path], trusted_keys: tuple[Trusted
1422
1482
  anywhere yet, and verifies+unpacks it from that root's OWN local blobs,
1423
1483
  writing the result into `roots[0]` (the writable cache) regardless of
1424
1484
  which root — including a read-only system directory — the blobs came
1425
- from."""
1485
+ from. An entry that already has a record is passed over without reading
1486
+ a blob, and each root's signature referrers are read once, never by
1487
+ reading a candidate's layer (public issue #690)."""
1426
1488
  cache_write_root = roots[0]
1427
1489
  already_verified = {record.manifest for _dir, record in list_verified_records(roots)}
1428
1490
  for root in roots:
@@ -1431,16 +1493,41 @@ def _verify_preseeded_entries(roots: Sequence[Path], trusted_keys: tuple[Trusted
1431
1493
  index_doc = json.loads(index_path.read_text(encoding="utf-8"))
1432
1494
  except (OSError, ValueError):
1433
1495
  continue
1496
+ blobs_dir = root / "blobs" / "sha256"
1497
+ candidates: list[str] = []
1498
+ candidate_layers: set[str] = set()
1499
+ manifests_read: dict[str, bytes] = {}
1500
+ sizes: dict[str, int] = {}
1434
1501
  for m in index_doc.get("manifests") or []:
1435
1502
  digest = m.get("digest")
1436
1503
  if not digest or digest in already_verified:
1437
1504
  continue
1505
+ candidates.append(digest)
1506
+ size = m.get("size")
1507
+ if isinstance(size, int) and not isinstance(size, bool):
1508
+ sizes[digest] = size
1509
+ try:
1510
+ manifest_hex = parse_digest(digest)
1511
+ manifest_bytes = (blobs_dir / manifest_hex).read_bytes()
1512
+ manifests_read[manifest_hex] = manifest_bytes
1513
+ layer = manifest_layer_descriptor(json.loads(manifest_bytes))
1514
+ candidate_layers.add(parse_digest(layer.digest))
1515
+ except (ValueError, OSError, FetchError):
1516
+ continue
1517
+ if not candidates:
1518
+ continue
1519
+ referrers = _local_bundle_referrers(blobs_dir, candidate_layers, manifests_read)
1520
+ for digest in candidates:
1521
+ if digest in already_verified:
1522
+ continue
1438
1523
  record = _verify_and_install_from_local_blobs(
1439
1524
  source_root=root,
1440
1525
  manifest_digest=digest,
1441
1526
  cache_write_root=cache_write_root,
1442
1527
  trusted_keys=trusted_keys,
1443
1528
  visible_only=True,
1529
+ referrers=referrers,
1530
+ manifest_size=sizes.get(digest),
1444
1531
  )
1445
1532
  if record is not None:
1446
1533
  already_verified.add(digest)
chtypes/_ocifetch/_oci.py CHANGED
@@ -26,6 +26,7 @@ from chtypes._ocifetch._http import (
26
26
 
27
27
  __all__ = [
28
28
  "Descriptor",
29
+ "copy_blob_verified",
29
30
  "fetch_blob_bytes",
30
31
  "fetch_blob_to_path",
31
32
  "fetch_manifest_by_digest",
@@ -143,6 +144,46 @@ def verify_body_matches_digest(body: bytes, digest: str, *, what: str) -> None:
143
144
  )
144
145
 
145
146
 
147
+ def copy_blob_verified(src_path: str, descriptor: Descriptor, dest_path: str, *, what: str) -> None:
148
+ """Copy a LOCAL blob file (a pre-seeded layout's `blobs/sha256/<hex>`)
149
+ to `dest_path`, streaming, and check the copy's size and sha256 against
150
+ `descriptor` BEFORE the caller does anything else with it — the local
151
+ counterpart of `fetch_blob_to_path`, with the same §7.3 rule ("check
152
+ size and sha256 before decompressing"). The caller unpacks the copy it
153
+ verified, never the source file, so the bytes checked are the bytes
154
+ used. Reads at most `descriptor.size + 1` bytes; removes `dest_path` on
155
+ any failure."""
156
+ expected = parse_digest(descriptor.digest)
157
+ hasher = hashlib.sha256()
158
+ size = 0
159
+ try:
160
+ with open(src_path, "rb") as src, open(dest_path, "wb") as dst:
161
+ while size <= descriptor.size:
162
+ chunk = src.read(min(1 << 20, descriptor.size + 1 - size))
163
+ if not chunk:
164
+ break
165
+ hasher.update(chunk)
166
+ dst.write(chunk)
167
+ size += len(chunk)
168
+ if size > descriptor.size:
169
+ raise ArtifactCorruptError(
170
+ f"{what}: larger than the descriptor's size {descriptor.size}"
171
+ )
172
+ if size != descriptor.size:
173
+ raise ArtifactCorruptError(
174
+ f"{what}: size {size} disagreed with the descriptor's {descriptor.size}"
175
+ )
176
+ actual = hasher.hexdigest()
177
+ if actual != expected:
178
+ raise ArtifactCorruptError(f"{what}: sha256 {actual} disagreed with {expected}")
179
+ except BaseException:
180
+ try:
181
+ os.unlink(dest_path)
182
+ except OSError:
183
+ pass
184
+ raise
185
+
186
+
146
187
  def resolve_platform_manifest(index_doc: dict, platform_key: str) -> Descriptor:
147
188
  """Pick the one manifest descriptor in an OCI image index matching
148
189
  `platform_key` (e.g. "linux-arm64"), refusing a duplicate or absent
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.dev13
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=0v-tGzC4P0oLtADN-YBHzAMWaqOc2bYincxwUAhWtyg,81190
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
@@ -30,15 +30,15 @@ chtypes/_ocifetch/_hold.py,sha256=AmXKLFWdPEmZ5fuxKbTKbzYn-Gue9AyMfYTquQ0qvOs,48
30
30
  chtypes/_ocifetch/_http.py,sha256=3D8WZXAObw-IiZqLSh9GEys2EKOEE2i9CVfUqjLh3E0,29013
31
31
  chtypes/_ocifetch/_layout.py,sha256=ezEoVrsSMhvDhQ4U04hsGdu1-_ISZQ4V6zg4gGIChTQ,21300
32
32
  chtypes/_ocifetch/_lock.py,sha256=14_uex9qXdOht-5FSrSvFvd99-gB7DmI3bDuxXs4YU8,5530
33
- chtypes/_ocifetch/_oci.py,sha256=UdA8QGTh1O27DBYRf5UxsevjgI52IecQlEf9IO6Y0A4,13920
33
+ chtypes/_ocifetch/_oci.py,sha256=Xc0JEShGemdTlu2LviOBFSMTcjwCu9uP6jdG0qlKO80,15677
34
34
  chtypes/_ocifetch/_prune.py,sha256=gPToHNkOOVjCWA-lowJHnGpguqOFXklUOQs7PAGfhBQ,10203
35
35
  chtypes/_ocifetch/_referrers.py,sha256=SQ8BMVzzA1085azzfAA0Nw3GrHPjeu98smasKyS609A,10530
36
36
  chtypes/_ocifetch/_resolve.py,sha256=Qv8xFUdOR-Kb_JqmUeDiQ4_Xv2ETuNFpmoDckMp7eV8,11239
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.dev13.dist-info/METADATA,sha256=W3xyv6dfQ9wI53cfWtQWioHnctgRuShzOSDZC5H3Jv4,11365
41
+ chtypes-2.0.0.dev13.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
42
+ chtypes-2.0.0.dev13.dist-info/entry_points.txt,sha256=TdNcluWBR0VOt5mVFROsp-NiVPAdR3EHo0UsHlMC0r0,50
43
+ chtypes-2.0.0.dev13.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
44
+ chtypes-2.0.0.dev13.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