vgi-python 0.37.2__py3-none-any.whl → 0.38.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/_test_fixtures/attach_options.py +1 -1
- vgi/catalog/_descriptor_spec.py +1 -1
- vgi/catalog/attach_option.py +41 -8
- vgi/http/landing.html +4 -2
- vgi/http/worker_page.py +19 -2
- {vgi_python-0.37.2.dist-info → vgi_python-0.38.0.dist-info}/METADATA +8 -3
- {vgi_python-0.37.2.dist-info → vgi_python-0.38.0.dist-info}/RECORD +10 -10
- {vgi_python-0.37.2.dist-info → vgi_python-0.38.0.dist-info}/WHEEL +0 -0
- {vgi_python-0.37.2.dist-info → vgi_python-0.38.0.dist-info}/entry_points.txt +0 -0
- {vgi_python-0.37.2.dist-info → vgi_python-0.38.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -123,7 +123,7 @@ class RequiredAttachOptions:
|
|
|
123
123
|
|
|
124
124
|
# No class-level assignment: there is no default to fall back on, so the
|
|
125
125
|
# caller has to supply it.
|
|
126
|
-
api_key: Annotated[str, AttachOption(desc="API key", required=True)]
|
|
126
|
+
api_key: Annotated[str, AttachOption(desc="API key", required=True, secret=True)]
|
|
127
127
|
region: Annotated[str, AttachOption(desc="Region")] = "us-east-1"
|
|
128
128
|
|
|
129
129
|
|
vgi/catalog/_descriptor_spec.py
CHANGED
|
@@ -10,7 +10,7 @@ holds the machinery both share; the two public modules subclass it so their
|
|
|
10
10
|
names and behaviour are unchanged.
|
|
11
11
|
|
|
12
12
|
A subclass may widen its wire format with additional columns — `AttachOption`
|
|
13
|
-
adds ``required`` — via :meth:`_SpecBase._extra_row` /
|
|
13
|
+
adds ``required`` and ``secret`` — via :meth:`_SpecBase._extra_row` /
|
|
14
14
|
:meth:`_SpecBase._extra_kwargs` and :meth:`_DescriptorBase.extra_spec_kwargs`.
|
|
15
15
|
Because both directions read by column name, the widening is compatible in both
|
|
16
16
|
directions: a reader ignores columns it doesn't know, and a reader whose peer
|
vgi/catalog/attach_option.py
CHANGED
|
@@ -9,6 +9,14 @@ from session-level Settings resent on every call).
|
|
|
9
9
|
The declaration mirrors ``vgi.catalog.setting.Setting`` — same Arrow IPC spec
|
|
10
10
|
format, same Python type → Arrow mapping, same extractor shape — so both share
|
|
11
11
|
the machinery in ``vgi.catalog._descriptor_spec``.
|
|
12
|
+
|
|
13
|
+
Credential options (API keys, tokens, passwords) MUST be declared
|
|
14
|
+
``secret=True``. Clients and the DuckDB extension then mask the value, keep it
|
|
15
|
+
out of result-cache keys and logs, and can supply it from a ``vgi_attach``
|
|
16
|
+
DuckDB secret instead of the ``ATTACH`` text::
|
|
17
|
+
|
|
18
|
+
CREATE SECRET (TYPE vgi_attach, SCOPE 'https://worker.example.com', api_key '...');
|
|
19
|
+
ATTACH 'mydb' AS mydb (TYPE vgi, LOCATION 'https://worker.example.com');
|
|
12
20
|
"""
|
|
13
21
|
|
|
14
22
|
from dataclasses import dataclass
|
|
@@ -42,18 +50,31 @@ class AttachOptionSpec(_SpecBase):
|
|
|
42
50
|
rather than surfacing a failure that reads like an empty catalog.
|
|
43
51
|
Mutually exclusive with ``default`` — an option that falls back to
|
|
44
52
|
a value is by definition satisfiable without the caller.
|
|
45
|
-
|
|
53
|
+
secret: The option carries a credential (an API key, token or
|
|
54
|
+
password). Clients and the DuckDB extension mask its value, keep it
|
|
55
|
+
out of result-cache keys and logs, and can supply it from a
|
|
56
|
+
``vgi_attach`` DuckDB secret
|
|
57
|
+
(``CREATE SECRET (TYPE vgi_attach, SCOPE '<worker url>', api_key '...')``)
|
|
58
|
+
rather than the ``ATTACH`` text. Credential options MUST set this.
|
|
59
|
+
Compatible with ``required``. Allowed alongside a ``default``, but a
|
|
60
|
+
secret option should normally have none: a default credential is
|
|
61
|
+
advertised in plain text to every client at discovery.
|
|
62
|
+
ARROW_SCHEMA: The shared four columns plus ``required`` and ``secret``.
|
|
46
63
|
|
|
47
64
|
"""
|
|
48
65
|
|
|
49
66
|
required: bool = False
|
|
67
|
+
secret: bool = False
|
|
50
68
|
|
|
51
69
|
ARROW_SCHEMA: ClassVar[pa.Schema] = pa.schema(
|
|
52
70
|
[
|
|
53
71
|
*_SpecBase.ARROW_SCHEMA,
|
|
54
|
-
# Nullable, and appended
|
|
72
|
+
# Nullable, and appended after the shared columns: a peer that predates this column
|
|
55
73
|
# reads the batch by name and simply doesn't see it.
|
|
56
74
|
pa.field("required", pa.bool_(), nullable=True),
|
|
75
|
+
# Same rules as ``required``: nullable, appended after it, and an
|
|
76
|
+
# absent column reads as False.
|
|
77
|
+
pa.field("secret", pa.bool_(), nullable=True),
|
|
57
78
|
]
|
|
58
79
|
)
|
|
59
80
|
|
|
@@ -67,12 +88,16 @@ class AttachOptionSpec(_SpecBase):
|
|
|
67
88
|
)
|
|
68
89
|
|
|
69
90
|
def _extra_row(self) -> dict[str, Any]:
|
|
70
|
-
return {"required": self.required}
|
|
91
|
+
return {"required": self.required, "secret": self.secret}
|
|
71
92
|
|
|
72
93
|
@classmethod
|
|
73
94
|
def _extra_kwargs(cls, row: dict[str, object]) -> dict[str, Any]:
|
|
74
|
-
# Absent column (older peer) and explicit null both mean "not required"
|
|
75
|
-
|
|
95
|
+
# Absent column (older peer) and explicit null both mean "not required"
|
|
96
|
+
# / "not secret".
|
|
97
|
+
return {
|
|
98
|
+
"required": bool(row.get("required") or False),
|
|
99
|
+
"secret": bool(row.get("secret") or False),
|
|
100
|
+
}
|
|
76
101
|
|
|
77
102
|
|
|
78
103
|
@dataclass
|
|
@@ -90,18 +115,26 @@ class AttachOption(_DescriptorBase):
|
|
|
90
115
|
|
|
91
116
|
class AttachOptions:
|
|
92
117
|
region: Annotated[str, AttachOption(desc="AWS region")] = "us-east-1"
|
|
93
|
-
api_key: Annotated[str, AttachOption(desc="API key", required=True)]
|
|
118
|
+
api_key: Annotated[str, AttachOption(desc="API key", required=True, secret=True)]
|
|
119
|
+
|
|
120
|
+
Credential options (API keys, tokens, passwords) MUST be declared
|
|
121
|
+
``secret=True``; see `AttachOptionSpec.secret`.
|
|
94
122
|
|
|
95
123
|
Attributes:
|
|
96
124
|
required: The caller must supply this option at ``ATTACH`` time.
|
|
125
|
+
secret: The option carries a credential. Clients and the DuckDB
|
|
126
|
+
extension mask it, keep it out of cache keys and logs, and can
|
|
127
|
+
supply it from a ``vgi_attach`` DuckDB secret. Should normally have
|
|
128
|
+
no default.
|
|
97
129
|
|
|
98
130
|
"""
|
|
99
131
|
|
|
100
132
|
required: bool = False
|
|
133
|
+
secret: bool = False
|
|
101
134
|
|
|
102
135
|
def extra_spec_kwargs(self) -> dict[str, Any]:
|
|
103
|
-
"""Carry ``required`` through to the `AttachOptionSpec`."""
|
|
104
|
-
return {"required": self.required}
|
|
136
|
+
"""Carry ``required`` and ``secret`` through to the `AttachOptionSpec`."""
|
|
137
|
+
return {"required": self.required, "secret": self.secret}
|
|
105
138
|
|
|
106
139
|
|
|
107
140
|
class MissingAttachOptionsError(ValueError):
|
vgi/http/landing.html
CHANGED
|
@@ -544,7 +544,8 @@ async function buildCatalog(info){
|
|
|
544
544
|
name: s.name, type: typeStr(s.type),
|
|
545
545
|
default: s.default == null ? '' : String(s.default),
|
|
546
546
|
description: s.description || '',
|
|
547
|
-
required: !!s.required
|
|
547
|
+
required: !!s.required,
|
|
548
|
+
secret: !!s.secret
|
|
548
549
|
}));
|
|
549
550
|
} catch (e) { /* keep the card; drop unreadable specs */ }
|
|
550
551
|
|
|
@@ -792,7 +793,8 @@ function attachOptionsHtml(c){
|
|
|
792
793
|
'<table class="opts"><thead><tr><th>Option</th><th>Type</th><th>Default</th><th>Description</th></tr></thead><tbody>'+
|
|
793
794
|
opts.map(o=>'<tr><td class="o-name">'+esc(o.name)+
|
|
794
795
|
(o.required?'<span class="o-req" title="Must be supplied at ATTACH time">required</span>':'')+
|
|
795
|
-
|
|
796
|
+
(o.secret?'<span class="o-req" title="A credential: supply it from a vgi_attach DuckDB secret rather than the ATTACH text">secret</span>':'')+
|
|
797
|
+
'</td><td class="o-type">'+esc(o.type)+'</td><td class="o-def">'+esc(o.secret&&o.default?'hidden':(o.default||'—'))+'</td><td class="o-desc">'+esc(o.description||'')+'</td></tr>').join('')+
|
|
796
798
|
'</tbody></table></details>';
|
|
797
799
|
}
|
|
798
800
|
function renderConnect(){
|
vgi/http/worker_page.py
CHANGED
|
@@ -584,10 +584,24 @@ def _build_attach_options_table(specs: tuple[AttachOptionSpec, ...]) -> str:
|
|
|
584
584
|
"<table><tr><th>Name</th><th>Type</th><th>Default</th><th>Description</th></tr>",
|
|
585
585
|
]
|
|
586
586
|
for spec in specs:
|
|
587
|
-
|
|
587
|
+
if spec.default is None:
|
|
588
|
+
default_str = "—"
|
|
589
|
+
elif spec.secret:
|
|
590
|
+
# A credential's default is never echoed back onto the page.
|
|
591
|
+
default_str = "<em>hidden</em>"
|
|
592
|
+
else:
|
|
593
|
+
default_str = _esc(repr(spec.default))
|
|
588
594
|
desc_str = _esc(spec.desc) if spec.desc else "—"
|
|
595
|
+
flags = ""
|
|
596
|
+
if spec.required:
|
|
597
|
+
flags += ' <span class="o-flag" title="Must be supplied at ATTACH time">required</span>'
|
|
598
|
+
if spec.secret:
|
|
599
|
+
flags += (
|
|
600
|
+
' <span class="o-flag" title="A credential: supply it from a vgi_attach DuckDB secret '
|
|
601
|
+
'rather than the ATTACH text">secret</span>'
|
|
602
|
+
)
|
|
589
603
|
parts.append(
|
|
590
|
-
f"<tr><td><code>{_esc(spec.name)}</code
|
|
604
|
+
f"<tr><td><code>{_esc(spec.name)}</code>{flags}</td>"
|
|
591
605
|
f"<td><code>{_esc(str(spec.type))}</code></td>"
|
|
592
606
|
f"<td>{default_str}</td>"
|
|
593
607
|
f"<td>{desc_str}</td></tr>"
|
|
@@ -1230,6 +1244,9 @@ _PAGE_TEMPLATE = (
|
|
|
1230
1244
|
font-weight: 600; border-bottom: 2px solid #e0dcd0; }}
|
|
1231
1245
|
td {{ padding: 8px 10px; border-bottom: 1px solid #f0ece0; }}
|
|
1232
1246
|
td code {{ font-size: 0.85em; }}
|
|
1247
|
+
.o-flag {{ font-size: 0.7em; font-weight: 600; text-transform: uppercase; margin-left: 4px;
|
|
1248
|
+
padding: 1px 5px; border-radius: 4px; background: #fdf0e3; color: #8a4b08;
|
|
1249
|
+
border: 1px solid #f0d5b4; }}
|
|
1233
1250
|
.no-params {{ color: #6b6b5a; font-style: italic; font-size: 0.9em; }}
|
|
1234
1251
|
.section-label {{ font-size: 0.8em; font-weight: 600; text-transform: uppercase;
|
|
1235
1252
|
letter-spacing: 0.05em; color: #6b6b5a; margin-top: 14px;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: vgi-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.38.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
|
|
@@ -158,11 +158,11 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
158
158
|
Classifier: Typing :: Typed
|
|
159
159
|
Requires-Python: >=3.13
|
|
160
160
|
Requires-Dist: click
|
|
161
|
-
Requires-Dist: httpx2>=2.
|
|
161
|
+
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.47.
|
|
165
|
+
Requires-Dist: vgi-rpc>=0.47.2
|
|
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'
|
|
@@ -235,6 +235,11 @@ Description-Content-Type: text/markdown
|
|
|
235
235
|
<a href="LICENSE"><img src="https://img.shields.io/pypi/l/vgi-python" alt="License"></a>
|
|
236
236
|
</p>
|
|
237
237
|
|
|
238
|
+
<p align="center">
|
|
239
|
+
<a href="https://youtu.be/J2E51PTZn6o"><img src="https://raw.githubusercontent.com/Query-farm/vgi-python/main/docs/assets/video-thumbnail.jpg" alt="Watch: VGI explained in 2½ minutes, on YouTube" width="640"></a><br>
|
|
240
|
+
<sub>▶ <a href="https://youtu.be/J2E51PTZn6o">Watch the 2½-minute overview</a></sub>
|
|
241
|
+
</p>
|
|
242
|
+
|
|
238
243
|
---
|
|
239
244
|
|
|
240
245
|
## See It in Action
|
|
@@ -39,7 +39,7 @@ vgi/table_in_out_function.py,sha256=F2QEtsbGEYHDVyQ4Cf9OCW7HlWXeqekAyx607foB-bs,
|
|
|
39
39
|
vgi/worker.py,sha256=0ez46DE5tc1-78ll29lBP7KRKdDJnkOv89gdr4RjLrE,253453
|
|
40
40
|
vgi/write_results.py,sha256=-OrUcz6E9ahhC_pnKYAwIMR2LpapwQafLC4IOnPTmec,2851
|
|
41
41
|
vgi/_test_fixtures/__init__.py,sha256=xQq-QQLk8USQ6TZHsPiPcZldNNUdcJ2gToXMPGzScLI,444
|
|
42
|
-
vgi/_test_fixtures/attach_options.py,sha256=
|
|
42
|
+
vgi/_test_fixtures/attach_options.py,sha256=FPsXGmhA9sU0tkgtHAlSO7kUQgqHsaDwtC8jYNXDHko,12529
|
|
43
43
|
vgi/_test_fixtures/bad_enum.py,sha256=b8l-afmXgGR6iqgIUiVeqHu0C0F8Of8ahEmdQGtqd1g,2916
|
|
44
44
|
vgi/_test_fixtures/bad_protocol.py,sha256=fPU7rpXEgBxRtPHfuCa0O2AHNRRbZDllLfydHsAKPEg,2992
|
|
45
45
|
vgi/_test_fixtures/cancellable.py,sha256=hetTRLyT4kkazPNcxAdfzj0pYzEmegMksamJbR0w3Ho,12151
|
|
@@ -122,8 +122,8 @@ vgi/_test_fixtures/writable/generic.py,sha256=3UzNUly3DhDPz-Revq7GePi6pxbP3AZ7R0
|
|
|
122
122
|
vgi/_test_fixtures/writable/table.py,sha256=j4hdzxva7XX_9MG68AJIdpLqC2SY9g-A6jy-eLL6Yzg,5735
|
|
123
123
|
vgi/_test_fixtures/writable/worker.py,sha256=ti-YFqVdYTMPIvDC8iwvaZ_CYUFTIf9xod2gqc1ReBk,42956
|
|
124
124
|
vgi/catalog/__init__.py,sha256=edc2wuvloEgY2WnnJNngV3xP0DN_p49SElYQanGzq4A,2629
|
|
125
|
-
vgi/catalog/_descriptor_spec.py,sha256=
|
|
126
|
-
vgi/catalog/attach_option.py,sha256
|
|
125
|
+
vgi/catalog/_descriptor_spec.py,sha256=8F7QGHvs5uumkVqpWxgMZi_QuqobqTJwf2_xgyuPZh4,9788
|
|
126
|
+
vgi/catalog/attach_option.py,sha256=-f8a6MPOthTWGf_6cjOgnNwJtVH4rGdxLMOMduGSyxk,7641
|
|
127
127
|
vgi/catalog/catalog_interface.py,sha256=-JdVTfkTOC2RYickktUiESF5UCuLVnPnLCUBQrTaN_4,158452
|
|
128
128
|
vgi/catalog/descriptors.py,sha256=JvI0mqDy6DGmtnhZw-U2Z9ZROMjz4KejxGrw7XmNnGw,49362
|
|
129
129
|
vgi/catalog/duckdb_statistics.py,sha256=Bzw4UGO-zdKrontaCcRV2palO8G3XLCWh-AhZozvQI8,16894
|
|
@@ -144,17 +144,17 @@ vgi/client/client.py,sha256=cvBQHzM2Z2lamC-fkBAvZD4HuRxB03aUkEKFVo_E9GU,167312
|
|
|
144
144
|
vgi/client/errors.py,sha256=cSN65cyQMk3crwZCz1IdPErK3mtlEkTNpu-nuQUDOQE,1633
|
|
145
145
|
vgi/http/__init__.py,sha256=hlOwOVcoZqmEKVGk7ganOdr5ryRSpEhLBF9sRD7BkYc,608
|
|
146
146
|
vgi/http/demo_storage.py,sha256=830s-H61thozC3eEuqdnwCpYFfvoJRq9vjyuXa5OURo,8769
|
|
147
|
-
vgi/http/landing.html,sha256=
|
|
147
|
+
vgi/http/landing.html,sha256=v7_5QRwjwVIX_WIKCx-dxZb4FoOF6_XN505__DyOuNw,356562
|
|
148
148
|
vgi/http/landing_page.py,sha256=8dPnRWwao-Zn7SirkJ9KZoGVYFOj9G0kGCTEO0Yo2X4,4149
|
|
149
149
|
vgi/http/vgi-client.js,sha256=dxJEzkpFp-XOAj1_z6inAhZyt2hYsjA9dI2US7k6r8I,560268
|
|
150
|
-
vgi/http/worker_page.py,sha256=
|
|
150
|
+
vgi/http/worker_page.py,sha256=hQefuTk_P3O8IfrXY4QzWo8Q_06pjOQLtxjQPxUUCIs,55663
|
|
151
151
|
vgi/transactor/__init__.py,sha256=Y3pHIQc5f6xy771J259oUZlAegklcHlIlTuinL3ENPQ,764
|
|
152
152
|
vgi/transactor/_duckdb_compat.py,sha256=sXVZ9JLKAQyGR1BjWczSwdQEavtr-TcZPoVZZnTr3vQ,953
|
|
153
153
|
vgi/transactor/client.py,sha256=7DTeMksogsw6ANjQjGOPpKYrV76rg4_kGjktMJf54jg,4486
|
|
154
154
|
vgi/transactor/protocol.py,sha256=v59IvrKnuvwOXvn_HEcBAbCzDVcx0akgKN0R1mChXg0,5034
|
|
155
155
|
vgi/transactor/server.py,sha256=nzsZQxJZdgappdYX8okrFIrjFtUbdM_Hhskl1fZ2nDY,32553
|
|
156
|
-
vgi_python-0.
|
|
157
|
-
vgi_python-0.
|
|
158
|
-
vgi_python-0.
|
|
159
|
-
vgi_python-0.
|
|
160
|
-
vgi_python-0.
|
|
156
|
+
vgi_python-0.38.0.dist-info/METADATA,sha256=BdmzjeWg-CTAbWvVahlskbm-aIC6Ky_-wpf-y9izq3w,25953
|
|
157
|
+
vgi_python-0.38.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
158
|
+
vgi_python-0.38.0.dist-info/entry_points.txt,sha256=3Kz1vgodw3pOL_xjtSyDB55-ZRy-U2X-X_Bdr582x0Q,165
|
|
159
|
+
vgi_python-0.38.0.dist-info/licenses/LICENSE,sha256=pbJb4zZasP6n5ifEV81wFu017TarjydaYVmGbHcehtY,6103
|
|
160
|
+
vgi_python-0.38.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|