Skip to content

Commit 54b2ed6

Browse files
rustyconoverclaude
andcommitted
release: 0.26.0 — httpx2, a clean mypy tests/, and CI green again
**httpx2 replaces httpx.** httpx's last release was 0.28.1 in December 2024; Pydantic has picked up stewardship as httpx2 -- same author, same design, BSD-3, actively maintained. Every `import httpx` becomes `import httpx2` and the pin moves to `httpx2>=2.9.1`. Explicit imports rather than httpx2's `alias_httpx()`, which resolves `import httpx` process-wide: that is an import hook, and a library others embed has no business installing one in its callers' interpreters. vgi-rpc 0.40.0 is required for this, not merely compatible with it. That release makes the same move, and it owns the HTTP transport client -- had this package migrated first, httpx would have stayed installed through `vgi-rpc[http]` and we would have shipped both clients. httpx and httpcore are now absent from the lock entirely. **`mypy tests/` is clean and gated.** It carried 174 errors across 37 files because CI only ever checked `vgi/`. Fixed properly rather than suppressed: four narrow `type: ignore`s remain, all commented, and ten stale ones were removed along the way. Nothing under `vgi/` changed -- where a test disagreed with a `vgi/` annotation the test was wrong, most instructively in test_aggregate_function.py, whose window fixture passed `filter_mask=None` where the worker in fact always builds an all-true mask. The fixture now matches the payload production actually sends. Two mypy config additions: `jsonschema.*` gets `ignore_missing_imports` (it ships no py.typed), and `mypy_path = "examples"` lets the example workers resolve -- which surfaced four genuine type errors in test_examples_workers.py, now fixed. The new CI step gates `tests/` from here on. It went unchecked long enough to accumulate 174 errors; the point is that it cannot accumulate a second batch. **ty is removed.** It was already non-blocking, and at 0.0.66 it stopped terminating on this tree -- `ty check vgi/` ran past ten minutes locally and, on CI, hung until the step was cancelled. A cancelled step is not a failed one, so `continue-on-error` did not rescue it and the Lint job went red on 0.25.1 with every other check passing. A signal nothing gates on, that also fails the build, is worse than no signal. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 494161c commit 54b2ed6

46 files changed

Lines changed: 500 additions & 306 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/ci.yml‎

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,15 +62,18 @@ jobs:
6262
# Docstring consistency (pydoclint) runs inside the pytest suite via
6363
# tests/test_docstrings.py, so it's not a separate CI lint step.
6464

65+
# ty was removed here in 0.25.2. It was already non-blocking, and at
66+
# 0.0.66 it stopped terminating on this tree at all -- `ty check vgi/`
67+
# ran past ten minutes and had to be killed. A signal nothing gates on,
68+
# that also burns a CI job's wall clock, is worse than no signal.
6569
- name: Type check (mypy)
6670
run: uv run mypy vgi/
6771

68-
# ty is pre-1.0 and its inference drifts release-to-release; keep it
69-
# visible as a signal but don't gate CI on it (mypy is the blocking
70-
# type gate).
71-
- name: Type check (ty, non-blocking)
72-
run: uv run ty check vgi/
73-
continue-on-error: true
72+
# tests/ became clean in 0.26.0 and is gated from here on. It was
73+
# unchecked for long enough to accumulate 174 errors; the point of the
74+
# gate is that it never gets to accumulate a second batch.
75+
- name: Type check tests (mypy)
76+
run: uv run mypy tests/
7477

7578
docs:
7679
name: Docs (build + examples + prose)

‎docs/how-to/state-storage.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ VGI_WORKER_SHARED_STORAGE=cloudflare-do vgi-serve my_worker.py --http
5757
|---|---|---|---|
5858
| SQLite | `sqlite` (default) | local / subprocess | none (stdlib) |
5959
| Azure SQL | `azure-sql` | Azure deployments | `vgi-python[azure]` |
60-
| Cloudflare DO | `cloudflare-do` | edge / multi-cloud | none extra — uses `httpx`, which ships with `vgi-python`; needs a Worker endpoint + token |
60+
| Cloudflare DO | `cloudflare-do` | edge / multi-cloud | none extra — uses `httpx2`, which ships with `vgi-python`; needs a Worker endpoint + token |
6161

6262
The per-backend setup (connection strings, credentials, table provisioning) is documented in the
6363
[Shared Storage reference](../shared-storage.md).

‎pyproject.toml‎

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "vgi-python"
3-
version = "0.25.1"
3+
version = "0.26.0"
44
description = "Vector Gateway Interface - Connect DuckDB to external programs via Apache Arrow"
55
readme = "README.md"
66
keywords = [
@@ -40,8 +40,8 @@ dependencies = [
4040
"pyarrow",
4141
"typer>=0.9",
4242
"platformdirs",
43-
"vgi-rpc>=0.39.1",
44-
"httpx>=0.24",
43+
"vgi-rpc>=0.40.0",
44+
"httpx2>=2.9.1",
4545
]
4646

4747
[project.optional-dependencies]
@@ -186,6 +186,10 @@ python_version = "3.13"
186186
strict = true
187187
warn_return_any = true
188188
warn_unused_ignores = true
189+
# tests/test_examples_workers.py puts examples/ on sys.path and imports the
190+
# doc example workers by bare module name; mirror that for mypy so the imports
191+
# resolve (and the examples themselves get checked).
192+
mypy_path = "examples"
189193

190194
[[tool.mypy.overrides]]
191195
module = "typer.*"
@@ -197,6 +201,13 @@ ignore_missing_imports = true
197201
module = "granian.*"
198202
ignore_missing_imports = true
199203

204+
[[tool.mypy.overrides]]
205+
# jsonschema ships no py.typed marker and its stubs live in the separate
206+
# types-jsonschema distribution, which we don't depend on. Used only by the
207+
# landing-page conformance checker in tests/.
208+
module = "jsonschema.*"
209+
ignore_missing_imports = true
210+
200211
[[tool.mypy.overrides]]
201212
# These files have type: ignore comments for ty that mypy doesn't need
202213
module = ["vgi._test_fixtures.scalar.null_handling", "vgi.table_function"]
@@ -250,7 +261,7 @@ members = ["packages/vgi-fixtures"]
250261
vgi-fixtures = { workspace = true }
251262

252263
[dependency-groups]
253-
dev = ["lxml>=6.0.2", "pytest-timeout>=2.4.0", "ty>=0.0.8", "vgi-fixtures"]
264+
dev = ["lxml>=6.0.2", "pytest-timeout>=2.4.0", "vgi-fixtures"]
254265
docs = [
255266
"mkdocs>=1.6",
256267
"mkdocs-material>=9.5",

‎tests/catalog/test_declarative.py‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1279,9 +1279,11 @@ def test_estimated_object_count_zeros_empty_categories(self) -> None:
12791279
"""
12801280
schema = Schema(name="main", functions=[UsersFunction]) # only table fn
12811281
info = schema.to_schema_info(AttachOpaqueData(b"x"))
1282-
assert info.estimated_object_count["scalar_function"] == 0
1283-
assert info.estimated_object_count["aggregate_function"] == 0
1284-
assert info.estimated_object_count["table_function"] == 1
1282+
counts = info.estimated_object_count
1283+
assert counts is not None
1284+
assert counts["scalar_function"] == 0
1285+
assert counts["aggregate_function"] == 0
1286+
assert counts["table_function"] == 1
12851287

12861288

12871289
class TestLegacyFunctionsListSchemaInfo:
@@ -1320,6 +1322,7 @@ def test_schemas_reports_function_counts(self) -> None:
13201322
schemas = ci.schemas(attach_opaque_data=AttachOpaqueData(b"x"), transaction_opaque_data=None)
13211323
assert len(schemas) == 1
13221324
counts = schemas[0].estimated_object_count
1325+
assert counts is not None
13231326
assert counts["scalar_function"] == 1
13241327
assert counts["table_function"] == 1
13251328

‎tests/catalog/test_required_filters.py‎

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,23 +23,22 @@ class TestRequiredFiltersField:
2323

2424
def _bbox_columns(self) -> pa.Schema:
2525
"""Return a small schema with a ``bbox`` STRUCT plus flat id/ticker."""
26-
return pa.schema(
27-
[
28-
("id", pa.int64()),
29-
("ticker", pa.string()),
30-
(
31-
"bbox",
32-
pa.struct(
33-
[
34-
("xmin", pa.float64()),
35-
("ymin", pa.float64()),
36-
("xmax", pa.float64()),
37-
("ymax", pa.float64()),
38-
]
39-
),
26+
fields: list[tuple[str, pa.DataType]] = [
27+
("id", pa.int64()),
28+
("ticker", pa.string()),
29+
(
30+
"bbox",
31+
pa.struct(
32+
[
33+
("xmin", pa.float64()),
34+
("ymin", pa.float64()),
35+
("xmax", pa.float64()),
36+
("ymax", pa.float64()),
37+
]
4038
),
41-
]
42-
)
39+
),
40+
]
41+
return pa.schema(fields)
4342

4443
def test_default_is_empty_tuple(self) -> None:
4544
"""Default is an empty tuple; TableInfo wire field is an empty list."""

‎tests/catalog/test_scan_branches.py‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def __init__(self, function_name: str = "legacy_scan", required: list[str] | Non
6363

6464
# Only the legacy method is overridden; table_scan_branches_get falls back
6565
# to the CatalogInterface default-impl shim.
66-
def table_scan_function_get( # type: ignore[override]
66+
def table_scan_function_get(
6767
self,
6868
*,
6969
attach_opaque_data: AttachOpaqueData,
@@ -89,7 +89,7 @@ class _MultiBranchCatalog(_StubCatalogBase):
8989
keep them non-overlapping at scan time.
9090
"""
9191

92-
def table_scan_function_get( # type: ignore[override]
92+
def table_scan_function_get(
9393
self,
9494
*,
9595
attach_opaque_data: AttachOpaqueData,
@@ -108,7 +108,7 @@ def table_scan_function_get( # type: ignore[override]
108108
required_extensions=[],
109109
)
110110

111-
def table_scan_branches_get( # type: ignore[override]
111+
def table_scan_branches_get(
112112
self,
113113
*,
114114
attach_opaque_data: AttachOpaqueData,
@@ -184,7 +184,7 @@ def __init__(self) -> None:
184184
super().__init__()
185185
self.last_at: tuple[str | None, str | None] | None = None
186186

187-
def table_scan_function_get( # type: ignore[override]
187+
def table_scan_function_get(
188188
self,
189189
*,
190190
attach_opaque_data: AttachOpaqueData,

0 commit comments

Comments
 (0)