vgi-python 0.41.0__py3-none-any.whl → 0.42.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.
vgi/_duckdb.py CHANGED
@@ -7,8 +7,8 @@ engine (expression-filter evaluation, the transactor, the statistics
7
7
  fixtures) resolves it through this module, which prefers Query Farm's
8
8
  ``haybarn`` distribution when installed and falls back to stock ``duckdb``.
9
9
 
10
- Install one via the extras: ``pip install vgi[haybarn]`` (preferred) or
11
- ``pip install vgi[duckdb]``.
10
+ Install one via the extras: ``pip install 'vgi-python[haybarn]'`` (preferred) or
11
+ ``pip install 'vgi-python[duckdb]'``.
12
12
  """
13
13
 
14
14
  from __future__ import annotations
@@ -50,7 +50,7 @@ def engine_module() -> ModuleType:
50
50
  else:
51
51
  raise ImportError(
52
52
  "No DuckDB-compatible engine is installed. Install 'haybarn' (preferred) "
53
- "or 'duckdb', e.g. `pip install vgi[haybarn]` or `pip install vgi[duckdb]`."
53
+ "or 'duckdb', e.g. `pip install 'vgi-python[haybarn]'` or `pip install 'vgi-python[duckdb]'`."
54
54
  )
55
55
  return _engine
56
56
 
@@ -372,7 +372,7 @@ def main() -> None:
372
372
  from vgi._test_fixtures.catalog_contents import CONTENTS_WORKERS
373
373
  from vgi._test_fixtures.narrow_bind.worker import NarrowBindWorker
374
374
  from vgi._test_fixtures.twin_catalogs import TwinAWorker, TwinBWorker
375
- from vgi.rpc_server import build_rpc_server
375
+ from vgi.rpc_server import build_rpc_server, resolve_grant_keys
376
376
 
377
377
  worker_classes: list[type] = [
378
378
  ExampleWorker,
@@ -408,6 +408,7 @@ def main() -> None:
408
408
  describe=describe,
409
409
  external_location=external_location,
410
410
  introspect_principals=introspect_principals,
411
+ grant_keys=resolve_grant_keys(),
411
412
  )
412
413
  wsgi_app = make_wsgi_app(
413
414
  server,
@@ -61,12 +61,18 @@ class IdentityExampleWorker(ExampleWorker):
61
61
  def conformance_authenticate(req: Any) -> AuthContext:
62
62
  """Derive the caller from ``X-Conformance-Principal`` / ``X-Conformance-Auth-Time``.
63
63
 
64
- An absent principal header means *unauthenticated*. The auth-time header is
64
+ An absent principal header means *unauthenticated*, unless an
65
+ ``Authorization`` header is present: that request falls through to the
66
+ sealed-grant and ``resolve_token`` bearers. The auth-time header is
65
67
  placed in the claims verbatim and unparsed, because the guard does the
66
68
  parsing and the fixture only carries the value.
67
69
  """
68
70
  principal = req.get_header(PRINCIPAL_HEADER)
69
71
  if not principal:
72
+ if req.get_header("Authorization"):
73
+ # A bearer is not ours: fall through to the identity bearer
74
+ # authenticators vgi-rpc appends (sealed grants, resolve_token).
75
+ raise ValueError("no conformance principal header")
70
76
  return AuthContext(domain=None, authenticated=False, principal=None)
71
77
  claims: dict[str, object] = {}
72
78
  auth_time = req.get_header(AUTH_TIME_HEADER)
vgi/rpc_server.py CHANGED
@@ -34,13 +34,14 @@ from typing import TYPE_CHECKING, Any, Literal
34
34
 
35
35
  if TYPE_CHECKING:
36
36
  from vgi_rpc.external import ExternalLocationConfig
37
+ from vgi_rpc.grants import GrantKeys
37
38
  from vgi_rpc.rpc import RpcServer
38
39
  from vgi_rpc.rpc._token_identity import IdentityImpl
39
40
 
40
41
  from vgi.meta_worker import MetaWorker
41
42
  from vgi.worker import Worker
42
43
 
43
- __all__ = ["Transport", "build_rpc_server"]
44
+ __all__ = ["Transport", "build_rpc_server", "resolve_grant_keys"]
44
45
 
45
46
  Transport = Literal["pipe", "unix", "tcp", "iroh", "http"]
46
47
  """The transport a server is being built for.
@@ -65,6 +66,7 @@ def build_rpc_server(
65
66
  transport: Transport,
66
67
  describe: bool = True,
67
68
  introspect_principals: Iterable[str] | None = None,
69
+ grant_keys: GrantKeys | None = None,
68
70
  external_location: ExternalLocationConfig | None = None,
69
71
  otel_config: Any = None,
70
72
  ) -> RpcServer:
@@ -83,6 +85,11 @@ def build_rpc_server(
83
85
  introspect_principals: HTTP only. Principals permitted to call
84
86
  ``introspect_token``; ``None`` reads ``VGI_INTROSPECT_PRINCIPALS``.
85
87
  Only consulted when the worker overrides ``resolve_token``.
88
+ grant_keys: HTTP only. Sealed-grant configuration (see
89
+ [`resolve_grant_keys`][vgi.rpc_server.resolve_grant_keys]). When
90
+ given, Identity is hosted even without hooks: the framework mints
91
+ sealed grants unless the worker overrides ``mint_grant``, and the
92
+ HTTP server accepts them back as bearer credentials.
86
93
  external_location: Optional external-storage configuration for large
87
94
  batches (HTTP).
88
95
  otel_config: Optional ``OtelConfig``. When given, the server is
@@ -116,13 +123,18 @@ def build_rpc_server(
116
123
  if transport in _IDENTITY_TRANSPORTS:
117
124
  # A Worker's hooks are classmethods; a MetaWorker answers for the one
118
125
  # child that implements each hook.
119
- identity = _build_identity(type(worker) if isinstance(worker, Worker) else worker, introspect_principals)
126
+ identity = _build_identity(
127
+ type(worker) if isinstance(worker, Worker) else worker, introspect_principals, grant_keys
128
+ )
120
129
 
121
130
  server = RpcServer(
122
131
  primary,
123
132
  worker,
124
133
  extra_protocols=extra,
125
134
  identity=identity,
135
+ # Explicit, never vgi-rpc's "env" default: grants are an HTTP-only
136
+ # identity feature here, and the caller resolved them already.
137
+ grant_keys=grant_keys if transport in _IDENTITY_TRANSPORTS else None,
126
138
  external_location=external_location,
127
139
  enable_describe=describe,
128
140
  server_version=_get_vgi_version(),
@@ -191,6 +203,7 @@ def _validated_hosted_protocols(worker: Worker | MetaWorker, primary: type) -> t
191
203
  def _build_identity(
192
204
  worker_cls: type[Worker] | MetaWorker,
193
205
  introspect_principals: Iterable[str] | None,
206
+ grant_keys: GrantKeys | None = None,
194
207
  ) -> IdentityImpl | None:
195
208
  """Build the ``vgi_rpc.Identity.v1`` implementation, or ``None``.
196
209
 
@@ -217,6 +230,9 @@ def _build_identity(
217
230
  the one child class that overrides each.
218
231
  introspect_principals: Principals permitted to introspect, or ``None``
219
232
  to read the environment.
233
+ grant_keys: Sealed-grant configuration. When given, Identity is built
234
+ even with no hook, and mints sealed grants unless the worker
235
+ overrides ``mint_grant``.
220
236
 
221
237
  Returns:
222
238
  The implementation, or ``None`` when this worker does not resolve
@@ -225,7 +241,7 @@ def _build_identity(
225
241
  """
226
242
  resolver = worker_cls._introspect_resolver()
227
243
  minter = worker_cls._grant_minter()
228
- if resolver is None and minter is None:
244
+ if resolver is None and minter is None and grant_keys is None:
229
245
  return None
230
246
 
231
247
  # Not re-exported by ``vgi_rpc.rpc``, so the private module is the only
@@ -239,6 +255,9 @@ def _build_identity(
239
255
  # validates it only when a resolver is supplied — a worker that mints
240
256
  # grants but resolves nothing is not an oracle and needs no allowlist.
241
257
  introspect_principals=(_resolve_introspect_principals(introspect_principals) if resolver is not None else None),
258
+ # With keys and no mint_grant override, IdentityImpl mints sealed
259
+ # grants itself; the HTTP server then accepts them as bearers.
260
+ grant_keys=grant_keys,
242
261
  )
243
262
 
244
263
 
@@ -285,3 +304,37 @@ def _resolve_introspect_principals(explicit: Iterable[str] | None) -> list[str]:
285
304
  )
286
305
  sys.exit(1)
287
306
  return principals
307
+
308
+
309
+ def resolve_grant_keys(explicit: Sequence[str] | None = None) -> GrantKeys | None:
310
+ """Resolve the sealed-grant configuration, or exit with an actionable message.
311
+
312
+ Grants are opt-in: with no key configured this returns ``None`` and
313
+ nothing changes. Keys come from *explicit* (``--grant-key``, repeatable)
314
+ or else ``VGI_RPC_GRANT_KEYS`` (comma-separated). Either way
315
+ ``VGI_RPC_GRANT_AUDIENCE`` and ``VGI_RPC_GRANT_MAX_TTL_SECONDS`` (default
316
+ 7 days) apply. Each key is standard base64 of exactly 32 bytes; the first
317
+ mints and every key verifies, which is how keys rotate.
318
+
319
+ Args:
320
+ explicit: Keys given on the command line, or ``None`` to read the
321
+ environment.
322
+
323
+ Returns:
324
+ The configuration, or ``None`` when no key is configured.
325
+
326
+ Raises:
327
+ SystemExit: A key or lifetime is malformed. A worker refuses to start
328
+ rather than run with a key it misread.
329
+
330
+ """
331
+ from vgi_rpc.grants import GRANT_KEYS_ENV, GrantKeys
332
+
333
+ env: dict[str, str] = dict(os.environ)
334
+ if explicit:
335
+ env[GRANT_KEYS_ENV] = ",".join(explicit)
336
+ try:
337
+ return GrantKeys.from_env(env)
338
+ except ValueError as exc:
339
+ sys.stderr.write(f"Error: invalid sealed-grant configuration ({GRANT_KEYS_ENV} / --grant-key): {exc}\n")
340
+ sys.exit(1)
vgi/serve.py CHANGED
@@ -41,6 +41,7 @@ from vgi.profiling import maybe_start_profile
41
41
 
42
42
  if TYPE_CHECKING:
43
43
  import falcon
44
+ from vgi_rpc.grants import GrantKeys
44
45
  from vgi_rpc.otel import OtelConfig
45
46
  from vgi_rpc.rpc import AuthContext, PeerAuthenticationPolicy, PeerIdentityProvider
46
47
 
@@ -216,6 +217,7 @@ def create_app(
216
217
  max_stream_response_bytes: int | None = None,
217
218
  max_externalized_response_bytes: int | None = None,
218
219
  introspect_principals: Iterable[str] | None = None,
220
+ grant_keys: GrantKeys | None = None,
219
221
  iroh_bridge_issuer: str | None = None,
220
222
  iroh_trusted_proxy_addresses: Iterable[str] = (),
221
223
  iroh_authenticate: bool = True,
@@ -277,6 +279,16 @@ def create_app(
277
279
  ``vgi_rpc.Identity.v1``'s ``introspect_token``. Only consulted
278
280
  when the worker class overrides ``resolve_token``. ``None`` reads
279
281
  ``VGI_INTROSPECT_PRINCIPALS``.
282
+ grant_keys: Sealed-grant configuration. ``None`` reads
283
+ ``VGI_RPC_GRANT_KEYS`` (see
284
+ [`resolve_grant_keys`][vgi.rpc_server.resolve_grant_keys]); no key
285
+ configured means grants are off. With keys, ``issue_grant`` mints
286
+ sealed grants (unless the worker overrides ``mint_grant``) and
287
+ requests may present them as ``Authorization: Bearer <grant>``.
288
+ Independently, a worker that overrides ``resolve_token`` has it
289
+ consulted for bearer credentials. Both come after *authenticate*
290
+ in the chain, so the deployment's own authenticator answers first
291
+ and must raise ``ValueError`` for a credential it does not know.
280
292
  iroh_bridge_issuer: Operator-controlled identity namespace for an
281
293
  ``iroh-http/2`` bridge. ``None`` disables Iroh header trust.
282
294
  iroh_trusted_proxy_addresses: Exact immediate bridge addresses.
@@ -297,7 +309,7 @@ def create_app(
297
309
  sys.exit(1)
298
310
 
299
311
  from vgi.otel import VgiTracer
300
- from vgi.rpc_server import build_rpc_server
312
+ from vgi.rpc_server import build_rpc_server, resolve_grant_keys
301
313
 
302
314
  # Resolve the signing key once, here, so the worker (which seals catalog
303
315
  # opaque-data envelopes) and the HTTP state-token machinery share the same
@@ -319,7 +331,9 @@ def create_app(
319
331
  transport="http",
320
332
  describe=describe,
321
333
  introspect_principals=introspect_principals,
334
+ grant_keys=grant_keys if grant_keys is not None else resolve_grant_keys(),
322
335
  )
336
+ authenticate, identity_bearer = _compose_identity_bearer(server, authenticate)
323
337
 
324
338
  effective_peer_identity_providers = tuple(peer_identity_providers)
325
339
  effective_peer_authentication_policy = peer_authentication_policy
@@ -356,6 +370,7 @@ def create_app(
356
370
  max_stream_response_bytes=max_stream_response_bytes,
357
371
  max_externalized_response_bytes=max_externalized_response_bytes,
358
372
  enable_landing_page=False,
373
+ identity_bearer=identity_bearer,
359
374
  )
360
375
 
361
376
  # Frontend: either redirect to external CDN or serve pre-rendered worker page
@@ -490,6 +505,16 @@ def main() -> None:
490
505
  "that case, with no permissive default. Env: VGI_INTROSPECT_PRINCIPALS."
491
506
  ),
492
507
  ),
508
+ grant_key: list[str] | None = typer.Option( # noqa: B008
509
+ None,
510
+ "--grant-key",
511
+ help=(
512
+ "Sealed-grant key: standard base64 of 32 bytes; repeat to rotate (the first "
513
+ "mints, all verify). Enables issue_grant without a mint_grant override and "
514
+ "accepts the grants as HTTP bearer credentials. Env: VGI_RPC_GRANT_KEYS "
515
+ "(comma-separated), VGI_RPC_GRANT_AUDIENCE, VGI_RPC_GRANT_MAX_TTL_SECONDS."
516
+ ),
517
+ ),
493
518
  access_log_sample: float | None = typer.Option( # noqa: B008
494
519
  None,
495
520
  "--access-log-sample",
@@ -573,6 +598,7 @@ def main() -> None:
573
598
  max_stream_response_bytes=max_stream_response_bytes,
574
599
  max_externalized_response_bytes=max_externalized_response_bytes,
575
600
  introspect_principals=introspect_principals,
601
+ grant_keys=grant_key,
576
602
  server=server,
577
603
  worker_ref=worker_ref,
578
604
  http_workers=http_workers,
@@ -770,7 +796,44 @@ def _resolve_authenticate() -> Callable[..., Any] | None:
770
796
  # this worker", with user identity handled upstream.
771
797
  from vgi_rpc.http import require_all
772
798
 
773
- return require_all(gate, inner)
799
+ gated = require_all(gate, inner)
800
+ # Kept so create_app can put the identity bearers (sealed grants,
801
+ # resolve_token) inside the gate rather than beside it.
802
+ setattr(gated, _PROXY_GATE_PARTS_ATTR, (gate, inner))
803
+ return gated
804
+
805
+
806
+ #: Attribute on a proxy-gated authenticator holding its ``(gate, inner)``.
807
+ _PROXY_GATE_PARTS_ATTR = "_vgi_proxy_gate_parts"
808
+
809
+
810
+ def _compose_identity_bearer(
811
+ server: Any,
812
+ authenticate: Callable[..., Any] | None,
813
+ ) -> tuple[Callable[..., Any] | None, bool]:
814
+ """Return ``(authenticate, identity_bearer)`` for ``make_wsgi_app``.
815
+
816
+ vgi-rpc appends the identity bearers (sealed grants, then the worker's
817
+ ``resolve_token``) after *authenticate* itself. It refuses when
818
+ *authenticate* depends on proxy-injected evidence, because an OR beside a
819
+ proxy-proof gate would let a grant bypass it. VGI's own gate
820
+ (``VGI_PROXY_PROOF_MODE``) is built by :func:`_resolve_authenticate`, so
821
+ here the bearers go *inside* it: ``require_all(gate, inner, then grants,
822
+ then resolve_token)``. Any other authenticator is left to vgi-rpc.
823
+ """
824
+ identity = getattr(server, "identity", None)
825
+ parts = getattr(authenticate, _PROXY_GATE_PARTS_ATTR, None)
826
+ if identity is None or parts is None:
827
+ return authenticate, True
828
+ if identity.grant_keys is None and identity.resolve_token_hook is None:
829
+ return authenticate, True
830
+ from vgi_rpc.http import compose_identity_authenticate, require_all
831
+
832
+ gate, inner = parts
833
+ composed = compose_identity_authenticate(
834
+ inner, grant_keys=identity.grant_keys, resolve_token=identity.resolve_token_hook
835
+ )
836
+ return require_all(gate, composed), False
774
837
 
775
838
 
776
839
  def _resolve_proxy_proof_gate() -> Any | None:
@@ -1352,6 +1415,7 @@ def _serve_http(
1352
1415
  max_stream_response_bytes: int | None = None,
1353
1416
  max_externalized_response_bytes: int | None = None,
1354
1417
  introspect_principals: str | None = None,
1418
+ grant_keys: Sequence[str] | None = None,
1355
1419
  server: str = "waitress",
1356
1420
  worker_ref: str | None = None,
1357
1421
  http_workers: int | None = None,
@@ -1369,6 +1433,13 @@ def _serve_http(
1369
1433
  # export_serve_config for why the environment is the channel.
1370
1434
  if introspect_principals is not None:
1371
1435
  os.environ["VGI_INTROSPECT_PRINCIPALS"] = introspect_principals
1436
+ if grant_keys:
1437
+ from vgi_rpc.grants import GRANT_KEYS_ENV
1438
+
1439
+ from vgi.rpc_server import resolve_grant_keys
1440
+
1441
+ resolve_grant_keys(grant_keys) # a malformed key stops startup here
1442
+ os.environ[GRANT_KEYS_ENV] = ",".join(grant_keys)
1372
1443
 
1373
1444
  if server == "granian":
1374
1445
  _serve_http_granian(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: vgi-python
3
- Version: 0.41.0
3
+ Version: 0.42.0
4
4
  Summary: Vector Gateway Interface - Connect DuckDB to external programs via Apache Arrow
5
5
  Project-URL: Homepage, https://query.farm
6
6
  Project-URL: Repository, https://github.com/Query-farm/vgi-python
@@ -162,7 +162,7 @@ Requires-Dist: httpx2>=2.12.0
162
162
  Requires-Dist: platformdirs
163
163
  Requires-Dist: pyarrow
164
164
  Requires-Dist: typer>=0.9
165
- Requires-Dist: vgi-rpc>=0.48.0
165
+ Requires-Dist: vgi-rpc>=0.49.0
166
166
  Provides-Extra: azure
167
167
  Requires-Dist: azure-identity>=1.16.0; extra == 'azure'
168
168
  Requires-Dist: pymssql>=2.3.0; extra == 'azure'
@@ -1,5 +1,5 @@
1
1
  vgi/__init__.py,sha256=8uLMNQcNzWjp8VzYtj2u98_Wp8WpB8c_dKlMv67TH0g,5384
2
- vgi/_duckdb.py,sha256=YB5D7N3Bwg_xP6X8a5QlumtlAovSej1A1Go5XlNGVko,2162
2
+ vgi/_duckdb.py,sha256=Tu8Yb4vABRv4yOMo7qul8LxG_wH5ZqZuOCSSjyiMgbY,2198
3
3
  vgi/_storage_profile.py,sha256=VkTsXojuE0tHEzurmteQSAiL1vI3CZSgYkL6D_h8GvE,5061
4
4
  vgi/aggregate_function.py,sha256=NYxudbvQ9ZLkP2w5hRdd4YgsGUZ_0cVfqqXnlmJPfls,27389
5
5
  vgi/argument_spec.py,sha256=SADH97VlFu3asI8CWZ_I7XTuSxwqFciFgdupdAf-tkY,34231
@@ -25,13 +25,13 @@ vgi/profiling.py,sha256=rK7vij3oHhXC3JHI2BsMySsXh7_hrXi0aXpCW8iFSIc,3671
25
25
  vgi/protocol.py,sha256=J9vaxstGvxe4Vmlf_Aij9HkabGbdFx_nEsQI_m-V7ak,175438
26
26
  vgi/protocol_version.txt,sha256=yJuHwmLp1f_SDnOqx0oXcq3Mn1ohaCzDOtz1CG5f71w,6
27
27
  vgi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
- vgi/rpc_server.py,sha256=KLFgOxnpFWRMkj9nR7HnrMBkqn1ahFuIOqgwd6RJ27o,12444
28
+ vgi/rpc_server.py,sha256=Qdqi6rHkX78SYXUZqG5D-a9NR0ZtGoxW22he0J9xS-0,14914
29
29
  vgi/scalar_function.py,sha256=YBy5s4eEBYTMjACGPE-8-82BObyJoM9qt9cnm24b_RQ,53485
30
30
  vgi/schema_path.py,sha256=GVlkZlCUj7GbVhgiocSMC6Ff5iTJhKqiYE62hMIEKiI,2403
31
31
  vgi/schema_utils.py,sha256=zQCliUgIpO2u77Mhtgg3OFvOEDNgJopu6kj2fw8YP1I,7945
32
32
  vgi/secret_protocol.py,sha256=iCDl7nF7Nb4cJV4vpfbldunTDqDJWzCzS7uCmOACf7U,7151
33
33
  vgi/secret_service.py,sha256=r7qCzfnOMirf9wSCUe-mgFe3z6v9_cDRzixuSAZG_bs,8973
34
- vgi/serve.py,sha256=UATWrggJe0TrcjEguC0P-vNN4UOMceEOqJl_3B2qV2Q,59899
34
+ vgi/serve.py,sha256=V56NcO9VzWaMlkUCXumLBsfMEZ8oZeckT_EJ9sumsQc,63442
35
35
  vgi/split_token.py,sha256=tutcksj-hgWrLY7UW3sSqkyoaD-cMqNTOeff40MGl_g,10850
36
36
  vgi/table_buffering_function.py,sha256=SI6yLAcs8WBHqS6TeaZ92s-Q6GXn0ReROyQWDujw2iA,19554
37
37
  vgi/table_filter_pushdown.py,sha256=4Qo4OAOLjTYQ5pinNTOjLCDnb96OHqpcxJlbSJo3cJU,67144
@@ -50,8 +50,8 @@ vgi/_test_fixtures/companion.py,sha256=Ya_rg2J4tXOYC7zBFOjTTh-4AYuRhJN5BT4tVZULQ
50
50
  vgi/_test_fixtures/copy_from.py,sha256=YAWametIhrKE_K_kRwx37QpKMY0yBWAordE5-cNVn4M,6134
51
51
  vgi/_test_fixtures/copy_to.py,sha256=fFHxPYVd0wfAfMEvpeBRijqUasBeoAGREY3kHmm_oK8,9667
52
52
  vgi/_test_fixtures/global_functions.py,sha256=G1h0w5o6bHMULDFLZuNjrZVrBbO6Cre6kTqTnQwTFmE,11251
53
- vgi/_test_fixtures/http_server.py,sha256=M0drWRwy-apDmQcXfTTAnkAoLRpL9gG5O8l0UwhVe1Q,20487
54
- vgi/_test_fixtures/identity_fixture.py,sha256=9JvCzLEm6MNU_V9IJ_HREAbaR_wKy2oFaqfRj3n_QQ8,2928
53
+ vgi/_test_fixtures/http_server.py,sha256=hS6yKEE6I0UzwfIk_q7ORKee4jn4vD2xSCYItj8VkWE,20552
54
+ vgi/_test_fixtures/identity_fixture.py,sha256=wNTON3wpOGwH7HS9oCPb4Iav7GMYW0NlhtlZF1TL7aY,3318
55
55
  vgi/_test_fixtures/nest_tensor.py,sha256=UjjW9UwipYG3ehQpZOm4hdzsafMyvVc0ZKJCrqiZTPU,24471
56
56
  vgi/_test_fixtures/orchard_catalog.py,sha256=8Uv-mKTIgl-OYU9JLgG23owlHGmgniyF2chPoKXGtuI,1643
57
57
  vgi/_test_fixtures/secret_cache.py,sha256=su4a90I-YHTfsmVPoJGyycTkEzT7GItreYZEfbXMp2o,9552
@@ -156,8 +156,8 @@ vgi/transactor/_duckdb_compat.py,sha256=sXVZ9JLKAQyGR1BjWczSwdQEavtr-TcZPoVZZnTr
156
156
  vgi/transactor/client.py,sha256=7DTeMksogsw6ANjQjGOPpKYrV76rg4_kGjktMJf54jg,4486
157
157
  vgi/transactor/protocol.py,sha256=v59IvrKnuvwOXvn_HEcBAbCzDVcx0akgKN0R1mChXg0,5034
158
158
  vgi/transactor/server.py,sha256=nzsZQxJZdgappdYX8okrFIrjFtUbdM_Hhskl1fZ2nDY,32553
159
- vgi_python-0.41.0.dist-info/METADATA,sha256=cYzmCmk6poHPPaMQ9OiIidOEntlJdzlD_Nr-RLbjoP4,25953
160
- vgi_python-0.41.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
161
- vgi_python-0.41.0.dist-info/entry_points.txt,sha256=3Kz1vgodw3pOL_xjtSyDB55-ZRy-U2X-X_Bdr582x0Q,165
162
- vgi_python-0.41.0.dist-info/licenses/LICENSE,sha256=pbJb4zZasP6n5ifEV81wFu017TarjydaYVmGbHcehtY,6103
163
- vgi_python-0.41.0.dist-info/RECORD,,
159
+ vgi_python-0.42.0.dist-info/METADATA,sha256=1c3-T4txCgcbPeUyxS-fOuMogZtSRPvbjNDOeau4pSg,25953
160
+ vgi_python-0.42.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
161
+ vgi_python-0.42.0.dist-info/entry_points.txt,sha256=3Kz1vgodw3pOL_xjtSyDB55-ZRy-U2X-X_Bdr582x0Q,165
162
+ vgi_python-0.42.0.dist-info/licenses/LICENSE,sha256=pbJb4zZasP6n5ifEV81wFu017TarjydaYVmGbHcehtY,6103
163
+ vgi_python-0.42.0.dist-info/RECORD,,