vgi-python 0.32.0__py3-none-any.whl → 0.33.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.
@@ -44,6 +44,14 @@ BAD_PROTOCOL_VERSION = "99.0.0"
44
44
  class BadProtocol(VgiProtocol):
45
45
  """VgiProtocol surface with a deliberately incompatible version."""
46
46
 
47
+ # The *only* thing this fixture varies is the version. As of vgi-rpc
48
+ # 0.46.0 the protocol name is the dispatch routing key, and it defaults to
49
+ # the class name -- so without this line subclassing alone renames the
50
+ # protocol to ``BadProtocol``, the client's request for ``VgiProtocol``
51
+ # finds no such protocol hosted, and the caller gets
52
+ # ``ProtocolNotSupportedError`` before the version gate it came to test
53
+ # ever runs.
54
+ protocol_name: ClassVar[str] = "vgi.v2"
47
55
  protocol_version: ClassVar[str] = BAD_PROTOCOL_VERSION
48
56
 
49
57
 
vgi/auth.py CHANGED
@@ -9,7 +9,7 @@ HTTP auth factories (require ``vgi[http]``):
9
9
  bearer_authenticate, bearer_authenticate_static, chain_authenticate,
10
10
  OAuthResourceMetadata, AuthUnavailableError
11
11
 
12
- Token introspection (requires ``vgi[http]``):
12
+ Token introspection (always available):
13
13
  TokenIdentity, TokenResolver
14
14
 
15
15
  JWT auth (requires ``vgi[oauth]``):
@@ -19,12 +19,28 @@ JWT auth (requires ``vgi[oauth]``):
19
19
  from __future__ import annotations
20
20
 
21
21
  import contextlib
22
+ from collections.abc import Callable
22
23
 
23
24
  from vgi_rpc.rpc import AuthContext, CallContext
24
25
 
26
+ # ``vgi_rpc.Identity.v1`` lives at the RPC layer as of vgi-rpc 0.46.0 -- it was
27
+ # an HTTP JSON route (``POST {prefix}/__introspect_token__``) through 0.45.x,
28
+ # which meant it existed on exactly one transport. So this is no longer gated
29
+ # on the ``http`` extra. Still not re-exported by ``vgi_rpc.rpc`` itself, so
30
+ # the private module remains the only import path; re-exported here so a worker
31
+ # that implements ``resolve_token`` never has to name one.
32
+ from vgi_rpc.rpc._token_identity import TokenIdentity
33
+
34
+ #: What :meth:`vgi.worker.Worker.resolve_token` is. Upstream dropped its own
35
+ #: alias when identity moved to the RPC layer; it is spelled out here so the
36
+ #: name ``vgi.auth.TokenResolver`` keeps working.
37
+ TokenResolver = Callable[[str], "TokenIdentity | None"]
38
+
25
39
  __all__ = [
26
40
  "AuthContext",
27
41
  "CallContext",
42
+ "TokenIdentity",
43
+ "TokenResolver",
28
44
  ]
29
45
 
30
46
  # HTTP auth helpers — available when vgi[http] is installed.
@@ -41,16 +57,9 @@ with contextlib.suppress(ImportError):
41
57
  parse_device_code_client_secret,
42
58
  )
43
59
 
44
- # Not re-exported by ``vgi_rpc.http`` itself, so the private module is the
45
- # only import path. Re-exported here so a worker that implements
46
- # ``resolve_token`` never has to name a private module.
47
- from vgi_rpc.http.server._introspect import TokenIdentity, TokenResolver # noqa: F401
48
-
49
60
  __all__ += [
50
61
  "AuthUnavailableError",
51
62
  "OAuthResourceMetadata",
52
- "TokenIdentity",
53
- "TokenResolver",
54
63
  "bearer_authenticate",
55
64
  "bearer_authenticate_static",
56
65
  "chain_authenticate",
vgi/protocol.py CHANGED
@@ -3077,10 +3077,26 @@ class VgiProtocol(Protocol):
3077
3077
  a dedicated generator so this version doesn't pollute the byte-key constants).
3078
3078
 
3079
3079
  Attributes:
3080
+ protocol_name: Wire name used as the ``vgi_rpc.protocol`` routing key
3081
+ and as the ``{protocol}`` HTTP path segment. Carries the major
3082
+ version, so an incompatible major is a different name and therefore
3083
+ a 404 rather than a confusing dispatch.
3080
3084
  protocol_version: The VGI protocol version integer, declared as the
3081
3085
  canonical semver (MAJOR.MINOR.PATCH) of the method-and-schema contract.
3082
3086
  """
3083
3087
 
3088
+ # The wire name carries the major version, so an incompatible major is a
3089
+ # *different* protocol and therefore a 404 -- an answer every proxy, WAF and
3090
+ # load balancer understands without an Arrow parser, and one that lets
3091
+ # ``vgi.v2`` and a future ``vgi.v3`` be served side by side while clients
3092
+ # migrate. That matters here specifically: the DuckDB extension ships to
3093
+ # users and cannot be flag-dayed.
3094
+ #
3095
+ # Declared rather than derived. Without it the name defaults to the class
3096
+ # name, which left the six implementations disagreeing four ways -- Python
3097
+ # ``VgiProtocol``, Java and C# ``VgiService``, Go the framework default
3098
+ # ``Service``, TypeScript ``vgi`` -- so no client could address them all.
3099
+ protocol_name: ClassVar[str] = "vgi.v2"
3084
3100
  protocol_version: ClassVar[str] = "2.0.0"
3085
3101
 
3086
3102
  def bind(self, request: BindRequest) -> BindResponse:
vgi/secret_protocol.py CHANGED
@@ -126,10 +126,17 @@ class VgiSecretProtocol(Protocol):
126
126
  backwards-incompatible change, minor for additive, patch for worker-side fixes.
127
127
 
128
128
  Attributes:
129
+ protocol_name: Wire name used as the ``vgi_rpc.protocol`` routing key.
130
+ Independent of :class:`vgi.protocol.VgiProtocol`'s, so this service
131
+ can be co-hosted beside it or deployed on its own.
129
132
  protocol_version: Canonical semver (MAJOR.MINOR.PATCH) of this contract,
130
133
  enforced as an exact major+minor match at the dispatch boundary.
131
134
  """
132
135
 
136
+ # Its own name and its own major, independent of ``vgi.v2``: this contract
137
+ # versions on a separate cadence, which is why ``protocol_version_override``
138
+ # exists on the client side at all.
139
+ protocol_name: ClassVar[str] = "vgi.secret.v1"
133
140
  protocol_version: ClassVar[str] = "1.0.0"
134
141
 
135
142
  def secret_lookup(self, path: str, type: str) -> SecretLookupResponse: # noqa: A002
vgi/serve.py CHANGED
@@ -43,6 +43,7 @@ if TYPE_CHECKING:
43
43
  import falcon
44
44
  from vgi_rpc.otel import OtelConfig
45
45
  from vgi_rpc.rpc import AuthContext, PeerAuthenticationPolicy, PeerIdentityProvider
46
+ from vgi_rpc.rpc._token_identity import IdentityImpl
46
47
 
47
48
  from vgi.worker import Worker
48
49
 
@@ -275,8 +276,8 @@ def create_app(
275
276
  escape, because bytes already uploaded cannot be un-uploaded.
276
277
  ``None`` (the default) means no cap.
277
278
  introspect_principals: Principals permitted to call
278
- ``__introspect_token__``. Only consulted when the worker class
279
- overrides ``resolve_token``. ``None`` reads
279
+ ``vgi_rpc.Identity.v1``'s ``introspect_token``. Only consulted
280
+ when the worker class overrides ``resolve_token``. ``None`` reads
280
281
  ``VGI_INTROSPECT_PRINCIPALS``.
281
282
  introspect_rate_limit: Introspection requests allowed per caller per
282
283
  second. ``None`` reads ``VGI_INTROSPECT_RATE_LIMIT``, defaulting
@@ -321,19 +322,13 @@ def create_app(
321
322
  worker._signing_key = signing_key
322
323
  from vgi.worker import _get_vgi_version
323
324
 
324
- server = RpcServer(VgiProtocol, worker, enable_describe=describe, server_version=_get_vgi_version())
325
-
326
- # Absent unless the worker class actually implements the lookup. Passing
327
- # ``None`` leaves the route unrouted rather than routed-and-refusing, which
328
- # is what keeps a dependency upgrade from growing a credential oracle.
329
- introspect_resolver = worker_cls._introspect_resolver()
330
- introspect_kwargs: dict[str, Any] = {}
331
- if introspect_resolver is not None:
332
- introspect_kwargs = {
333
- "introspect_resolver": introspect_resolver,
334
- "introspect_principals": _resolve_introspect_principals(introspect_principals),
335
- "introspect_rate_limit": _resolve_introspect_rate_limit(introspect_rate_limit),
336
- }
325
+ server = RpcServer(
326
+ VgiProtocol,
327
+ worker,
328
+ enable_describe=describe,
329
+ server_version=_get_vgi_version(),
330
+ identity=_build_identity(worker_cls, introspect_principals, introspect_rate_limit),
331
+ )
337
332
 
338
333
  effective_peer_identity_providers = tuple(peer_identity_providers)
339
334
  effective_peer_authentication_policy = peer_authentication_policy
@@ -370,7 +365,6 @@ def create_app(
370
365
  max_stream_response_bytes=max_stream_response_bytes,
371
366
  max_externalized_response_bytes=max_externalized_response_bytes,
372
367
  enable_landing_page=False,
373
- **introspect_kwargs,
374
368
  )
375
369
 
376
370
  # Frontend: either redirect to external CDN or serve pre-rendered worker page
@@ -500,7 +494,7 @@ def main() -> None:
500
494
  None,
501
495
  "--introspect-principals",
502
496
  help=(
503
- "Comma-separated principals permitted to call __introspect_token__. "
497
+ "Comma-separated principals permitted to call introspect_token. "
504
498
  "Only used when the worker implements resolve_token(); required in "
505
499
  "that case, with no permissive default. Env: VGI_INTROSPECT_PRINCIPALS."
506
500
  ),
@@ -805,6 +799,53 @@ def _resolve_authenticate() -> Callable[..., Any] | None:
805
799
  return require_all(gate, inner)
806
800
 
807
801
 
802
+ def _build_identity(
803
+ worker_cls: type[Worker],
804
+ introspect_principals: Iterable[str] | None,
805
+ introspect_rate_limit: int | None,
806
+ ) -> IdentityImpl | None:
807
+ """Build the ``vgi_rpc.Identity.v1`` implementation, or ``None``.
808
+
809
+ ``None`` unless the worker class actually implements the lookup, and that
810
+ is the point: ``RpcServer`` then does not host the protocol at all, rather
811
+ than hosting it and refusing every call. Absent beats routed-and-refusing —
812
+ it is what keeps a dependency upgrade from growing a
813
+ credential-to-identity oracle on every existing worker.
814
+
815
+ As of vgi-rpc 0.46.0 identity is an RPC-layer protocol hosted on the
816
+ server, not the HTTP JSON route (``POST {prefix}/__introspect_token__``) it
817
+ was through 0.45.x. It therefore reaches every transport rather than only
818
+ HTTP, and a client discovers it through ordinary reflection rather than by
819
+ calling and reading an error.
820
+
821
+ Args:
822
+ worker_cls: The worker class, consulted for a ``resolve_token``
823
+ override.
824
+ introspect_principals: Principals permitted to introspect, or ``None``
825
+ to read the environment.
826
+ introspect_rate_limit: Per-caller, per-second ceiling, or ``None`` to
827
+ read the environment.
828
+
829
+ Returns:
830
+ The implementation, or ``None`` when this worker does not resolve
831
+ credentials.
832
+
833
+ """
834
+ resolver = worker_cls._introspect_resolver()
835
+ if resolver is None:
836
+ return None
837
+
838
+ # Not re-exported by ``vgi_rpc.rpc``, so the private module is the only
839
+ # import path for the ``vgi_rpc.Identity.v1`` implementation helper.
840
+ from vgi_rpc.rpc._token_identity import IdentityImpl
841
+
842
+ return IdentityImpl(
843
+ resolve_token=resolver,
844
+ introspect_principals=_resolve_introspect_principals(introspect_principals),
845
+ introspect_rate_limit=_resolve_introspect_rate_limit(introspect_rate_limit),
846
+ )
847
+
848
+
808
849
  def _resolve_introspect_principals(explicit: Iterable[str] | None) -> list[str]:
809
850
  """Resolve the introspector allowlist, or exit with an actionable message.
810
851
 
@@ -835,16 +876,16 @@ def _resolve_introspect_principals(explicit: Iterable[str] | None) -> list[str]:
835
876
 
836
877
  if not principals:
837
878
  sys.stderr.write(
838
- "Error: this worker implements resolve_token(), which enables the\n"
839
- " POST /__introspect_token__ endpoint, but no introspector allowlist\n"
840
- " was configured. Set VGI_INTROSPECT_PRINCIPALS (comma-separated) or\n"
879
+ "Error: this worker implements resolve_token(), which hosts the\n"
880
+ " vgi_rpc.Identity.v1 protocol, but no introspector allowlist was\n"
881
+ " configured. Set VGI_INTROSPECT_PRINCIPALS (comma-separated) or\n"
841
882
  " pass --introspect-principals.\n"
842
883
  "\n"
843
884
  " There is no permissive default on purpose: introspection is a\n"
844
885
  " separate capability from authentication, and allowing every\n"
845
886
  " authenticated caller lets any user resolve any other user's\n"
846
- " credential to its owner. Remove resolve_token() to disable the\n"
847
- " endpoint entirely.\n"
887
+ " credential to its owner. Remove resolve_token() to leave the\n"
888
+ " protocol unhosted entirely.\n"
848
889
  )
849
890
  sys.exit(1)
850
891
  return principals
vgi/worker.py CHANGED
@@ -172,8 +172,8 @@ if TYPE_CHECKING:
172
172
  from collections.abc import Callable
173
173
  from ssl import SSLContext
174
174
 
175
- from vgi_rpc.http.server._introspect import TokenIdentity
176
175
  from vgi_rpc.rpc import PeerAuthenticationPolicy, PeerIdentityProvider
176
+ from vgi_rpc.rpc._token_identity import TokenIdentity
177
177
 
178
178
  from vgi.catalog.descriptors import Catalog
179
179
  from vgi.protocol import (
@@ -1420,13 +1420,17 @@ class Worker:
1420
1420
  def resolve_token(cls, token: str) -> TokenIdentity | None:
1421
1421
  """Resolve an opaque bearer credential to the identity it authenticates as.
1422
1422
 
1423
- Override to enable ``POST {prefix}/__introspect_token__``, which a
1424
- reverse proxy calls when it terminates the only public listener and
1425
- must know *which principal* a credential is before it can authorize
1426
- anything. Until it is overridden the route does not exist at all —
1427
- not "exists and refuses", absent — so no worker grows a
1423
+ Override to host ``vgi_rpc.Identity.v1``'s ``introspect_token``,
1424
+ which a reverse proxy calls when it terminates the only public
1425
+ listener and must know *which principal* a credential is before it can
1426
+ authorize anything. Until it is overridden the protocol is not hosted
1427
+ at all — not "hosted and refusing", absent — so no worker grows a
1428
1428
  credential-to-identity oracle by upgrading a dependency.
1429
1429
 
1430
+ Through vgi-rpc 0.45.x this was an HTTP JSON route, ``POST
1431
+ {prefix}/__introspect_token__``. As of 0.46.0 identity lives at the RPC
1432
+ layer, so it reaches every transport rather than only HTTP.
1433
+
1430
1434
  Enabling it also requires an allowlist of principals permitted to ask
1431
1435
  (``--introspect-principals`` / ``VGI_INTROSPECT_PRINCIPALS``). There is
1432
1436
  no permissive default: authenticating and introspecting are different
@@ -1437,7 +1441,7 @@ class Worker:
1437
1441
 
1438
1442
  This is deliberately *not* "run the credential back through the
1439
1443
  worker's own authenticate chain" — see
1440
- ``vgi_rpc.http.server._introspect`` for the four ways that breaks.
1444
+ ``vgi_rpc.rpc._token_identity`` for the four ways that breaks.
1441
1445
  Write a narrow lookup against whatever store issued the credential.
1442
1446
 
1443
1447
  Args:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: vgi-python
3
- Version: 0.32.0
3
+ Version: 0.33.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.9.1
162
162
  Requires-Dist: platformdirs
163
163
  Requires-Dist: pyarrow
164
164
  Requires-Dist: typer>=0.9
165
- Requires-Dist: vgi-rpc>=0.45.1
165
+ Requires-Dist: vgi-rpc>=0.46.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'
@@ -5,7 +5,7 @@ vgi/aggregate_function.py,sha256=v03wBSy2Rrl9F2pMYvu8GnGrTCQrAVm01HXIFNdjnm4,268
5
5
  vgi/argument_spec.py,sha256=_aO3r6LrRrM7ziOQ27i_ouaofXD_2XMHiwhihNVfj0o,32374
6
6
  vgi/arguments.py,sha256=ncnC1CS7KN-2rDjSw9vdf1uHqV5ozebet_eZtcq_ujw,72300
7
7
  vgi/attach_header.py,sha256=sxpzCOVa5fl_-kQSCZgeUxS5I9Kuv8VCUjAOF5s1pr0,3782
8
- vgi/auth.py,sha256=bZq6yWdCI4tP1XglzO-p6t3Dt0b93Am-d57s1aGB_38,1993
8
+ vgi/auth.py,sha256=TlD5bhdyXnFuAoCnoHMU9ffjewVEnpwO5auIvp9GsCA,2486
9
9
  vgi/cache_control.py,sha256=IyBp9gZTCc4Y-4V7Rs05_o5lSEjbR2jt6cddXPU0dKw,7812
10
10
  vgi/copy_from_function.py,sha256=oroQe5qYzZTJ-ppzQTQ-OruEF1ZlwTBNLBoXYfN-P2o,7133
11
11
  vgi/copy_to_function.py,sha256=8l0v_DyEF1EnFwinStrZDQ-yNFfGJsv3Mr5o2jG30f0,9279
@@ -22,26 +22,26 @@ vgi/meta_worker.py,sha256=WggIKvDAlahSrBPaVM7ZqVWk3Xc3JNE70EgeoXdy-3w,31725
22
22
  vgi/metadata.py,sha256=M8KZObX1wwCr2lPjgDL0-79gPbeM_NSOIqYCIAAck5M,74719
23
23
  vgi/otel.py,sha256=JlUDnk2c0UXSo_j1PJdS67Y--t55sXPg054ADuvHANc,15452
24
24
  vgi/profiling.py,sha256=rK7vij3oHhXC3JHI2BsMySsXh7_hrXi0aXpCW8iFSIc,3671
25
- vgi/protocol.py,sha256=3ln4b0a20is6nPLyZW-Goiexqg6PpeeuzNfKcWoCJHU,163944
25
+ vgi/protocol.py,sha256=m9W00W-c9jv2kqISp_wb8E8Rtity_60_12nzE3_d9Xg,165016
26
26
  vgi/protocol_version.txt,sha256=wo_MpTY3vIjhJK8XJd8Ty5jGne3v1i-zzb4c22t2BiQ,6
27
27
  vgi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
28
  vgi/scalar_function.py,sha256=zj6U1e5mXm4hk_ZB_CAjYmeO1LZBJcEMzNDzhwpXPmQ,53415
29
29
  vgi/schema_path.py,sha256=GVlkZlCUj7GbVhgiocSMC6Ff5iTJhKqiYE62hMIEKiI,2403
30
30
  vgi/schema_utils.py,sha256=zQCliUgIpO2u77Mhtgg3OFvOEDNgJopu6kj2fw8YP1I,7945
31
- vgi/secret_protocol.py,sha256=53L01b2KeKGxslniDBOYo299GQmAHm7JaDAC3RNLl6Q,6678
31
+ vgi/secret_protocol.py,sha256=iCDl7nF7Nb4cJV4vpfbldunTDqDJWzCzS7uCmOACf7U,7151
32
32
  vgi/secret_service.py,sha256=r7qCzfnOMirf9wSCUe-mgFe3z6v9_cDRzixuSAZG_bs,8973
33
- vgi/serve.py,sha256=xfiGIlTaLvalFxh233u-BKGdWOBHgza3yFtVGKb5Qjk,64719
33
+ vgi/serve.py,sha256=dyLHzMFdu1AUBtte2mwwYjtQeSF0fOLha1v9MBZbuD0,66163
34
34
  vgi/split_token.py,sha256=tutcksj-hgWrLY7UW3sSqkyoaD-cMqNTOeff40MGl_g,10850
35
35
  vgi/table_buffering_function.py,sha256=O5TcIdqk8-YViCQCkb8bOevO-zhe3IoyzLgwXDO3Cjc,19531
36
36
  vgi/table_filter_pushdown.py,sha256=2wJK09OkZZm-WYFsB-um4lbIMZV6gU1fcx8EsHglnA4,61861
37
37
  vgi/table_function.py,sha256=lAxWnjBcZBpLoR87Pe-QTK7c8WK_JQh1r_OJ9amI4kk,72746
38
38
  vgi/table_in_out_function.py,sha256=UGEeXsGkcx0BLt9RyKP6jQEbK6eFTUG6QIGEhxo_VxA,18865
39
- vgi/worker.py,sha256=NdwS5EqVtZ4ZdeDpEr7kinCJ_AX8jMM8SjJ8nd6JwCU,249336
39
+ vgi/worker.py,sha256=MGMQmy6PPKCqo5q_9fBS3z3zf4y6-UKdJAJiHGfnk50,249553
40
40
  vgi/write_results.py,sha256=-OrUcz6E9ahhC_pnKYAwIMR2LpapwQafLC4IOnPTmec,2851
41
41
  vgi/_test_fixtures/__init__.py,sha256=xQq-QQLk8USQ6TZHsPiPcZldNNUdcJ2gToXMPGzScLI,444
42
42
  vgi/_test_fixtures/attach_options.py,sha256=ZNgNTNF1RymSfnU-uVIDTLvCpc283h6O8tZ1UuEa3Lc,12516
43
43
  vgi/_test_fixtures/bad_enum.py,sha256=b8l-afmXgGR6iqgIUiVeqHu0C0F8Of8ahEmdQGtqd1g,2916
44
- vgi/_test_fixtures/bad_protocol.py,sha256=L8kxafWZ-4Sd73F6EpCRo1kWVC-09qzof6OYRXazS_8,2495
44
+ vgi/_test_fixtures/bad_protocol.py,sha256=fPU7rpXEgBxRtPHfuCa0O2AHNRRbZDllLfydHsAKPEg,2992
45
45
  vgi/_test_fixtures/cancellable.py,sha256=hetTRLyT4kkazPNcxAdfzj0pYzEmegMksamJbR0w3Ho,12151
46
46
  vgi/_test_fixtures/catalog.py,sha256=zjBKBgg4TKoDQ9SYIFj7sx6bOL3_aXHMJHSzTgk1fdA,28526
47
47
  vgi/_test_fixtures/companion.py,sha256=Ya_rg2J4tXOYC7zBFOjTTh-4AYuRhJN5BT4tVZULQ-Q,6037
@@ -152,8 +152,8 @@ vgi/transactor/_duckdb_compat.py,sha256=sXVZ9JLKAQyGR1BjWczSwdQEavtr-TcZPoVZZnTr
152
152
  vgi/transactor/client.py,sha256=7DTeMksogsw6ANjQjGOPpKYrV76rg4_kGjktMJf54jg,4486
153
153
  vgi/transactor/protocol.py,sha256=v59IvrKnuvwOXvn_HEcBAbCzDVcx0akgKN0R1mChXg0,5034
154
154
  vgi/transactor/server.py,sha256=nzsZQxJZdgappdYX8okrFIrjFtUbdM_Hhskl1fZ2nDY,32553
155
- vgi_python-0.32.0.dist-info/METADATA,sha256=5w0sPJG--crdBQDtvcjDQdPDCr4f89IOXqdPh_PKj5c,25617
156
- vgi_python-0.32.0.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
157
- vgi_python-0.32.0.dist-info/entry_points.txt,sha256=3Kz1vgodw3pOL_xjtSyDB55-ZRy-U2X-X_Bdr582x0Q,165
158
- vgi_python-0.32.0.dist-info/licenses/LICENSE,sha256=pbJb4zZasP6n5ifEV81wFu017TarjydaYVmGbHcehtY,6103
159
- vgi_python-0.32.0.dist-info/RECORD,,
155
+ vgi_python-0.33.0.dist-info/METADATA,sha256=RsTar3agbo8sxMrJEFtAFx4Qc-nICjnLZdUMLK83JcA,25617
156
+ vgi_python-0.33.0.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
157
+ vgi_python-0.33.0.dist-info/entry_points.txt,sha256=3Kz1vgodw3pOL_xjtSyDB55-ZRy-U2X-X_Bdr582x0Q,165
158
+ vgi_python-0.33.0.dist-info/licenses/LICENSE,sha256=pbJb4zZasP6n5ifEV81wFu017TarjydaYVmGbHcehtY,6103
159
+ vgi_python-0.33.0.dist-info/RECORD,,