Skip to content

Commit 4193bb0

Browse files
rustyconoverclaude
andcommitted
Move zero-column exchange test to conformance suite and bump version to 0.1.11
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 54a2aa7 commit 4193bb0

8 files changed

Lines changed: 40 additions & 37 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "vgi-rpc"
3-
version = "0.1.10"
3+
version = "0.1.11"
44
description = "Vector Gateway Interface - RPC framework based on Apache Arrow"
55
readme = "README.md"
66
requires-python = ">=3.13"

tests/test_conformance.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -811,6 +811,17 @@ def test_empty_exchange_session(self, conformance_conn: ConnFactory) -> None:
811811
# Verify transport is still usable
812812
assert proxy.echo_int(value=42) == 42
813813

814+
def test_zero_column_exchange(self, conformance_conn: ConnFactory) -> None:
815+
"""Exchange stream with zero-column batches works over 100 iterations."""
816+
empty_schema = pa.schema([])
817+
empty_input = AnnotatedBatch(batch=pa.record_batch([], schema=empty_schema))
818+
with conformance_conn() as proxy, proxy.exchange_zero_columns() as session:
819+
for _ in range(100):
820+
output = session.exchange(empty_input)
821+
assert output.batch.schema == empty_schema
822+
assert output.batch.num_rows == 0
823+
assert output.batch.num_columns == 0
824+
814825
def test_zero_row_input(self, conformance_conn: ConnFactory) -> None:
815826
"""Send zero-row batch to exchange."""
816827
with conformance_conn() as proxy, proxy.exchange_scale(factor=2.0) as session:
@@ -919,7 +930,7 @@ def test_run_describe_conformance(self, service_description: ServiceDescription)
919930

920931
def test_describe_via_rpc(self, service_description: ServiceDescription) -> None:
921932
"""Smoke test: basic transport-level describe call works."""
922-
assert len(service_description.methods) == 46
933+
assert len(service_description.methods) == 47
923934
assert service_description.protocol_name == "ConformanceService"
924935
echo_str = service_description.methods["echo_string"]
925936
assert echo_str.method_type == MethodType.UNARY

tests/test_rpc.py

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -222,18 +222,6 @@ def process(self, input: AnnotatedBatch, out: OutputCollector, ctx: CallContext)
222222
out.finish()
223223

224224

225-
@dataclass
226-
class ZeroColumnExchangeState(ExchangeState):
227-
"""State for an exchange stream with zero-column batches."""
228-
229-
call_count: int = 0
230-
231-
def exchange(self, input: AnnotatedBatch, out: OutputCollector, ctx: CallContext) -> None:
232-
"""Accept a zero-column batch and emit a zero-column batch."""
233-
self.call_count += 1
234-
out.emit(pa.record_batch([], schema=pa.schema([])))
235-
236-
237225
# ---------------------------------------------------------------------------
238226
# Test fixtures: Protocol + Implementation
239227
# ---------------------------------------------------------------------------
@@ -349,10 +337,6 @@ def transform_with_header(self, factor: float) -> Stream[ExchangeState, StreamHe
349337
"""Exchange stream with a stream header."""
350338
...
351339

352-
def zero_column_exchange(self) -> Stream[ExchangeState]:
353-
"""Exchange stream with zero-column input and output."""
354-
...
355-
356340
def fail_stream_init_with_header(self) -> Stream[ProducerState, StreamHeader]:
357341
"""Stream that raises during init (with header declared)."""
358342
...
@@ -466,11 +450,6 @@ def transform_with_header(self, factor: float) -> Stream[TransformWithHeaderStat
466450
output_schema=schema, state=TransformWithHeaderState(factor=factor), input_schema=schema, header=header
467451
)
468452

469-
def zero_column_exchange(self) -> Stream[ZeroColumnExchangeState]:
470-
"""Exchange stream with zero-column input and output."""
471-
empty = pa.schema([])
472-
return Stream(output_schema=empty, state=ZeroColumnExchangeState(), input_schema=empty)
473-
474453
def fail_stream_init_with_header(self) -> Stream[GenerateWithHeaderState, StreamHeader]:
475454
"""Stream that raises during init (with header declared)."""
476455
raise ValueError("init boom with header")
@@ -742,17 +721,6 @@ def test_bidi_context_manager(self, make_conn: ConnFactory) -> None:
742721
output = session.exchange(AnnotatedBatch(batch=pa.RecordBatch.from_pydict({"value": [5.0]})))
743722
assert output.batch.column("value").to_pylist() == [10.0]
744723

745-
def test_zero_column_exchange_100_batches(self, make_conn: ConnFactory) -> None:
746-
"""Exchange stream with zero-column batches works over 100 iterations."""
747-
empty_schema = pa.schema([])
748-
empty_input = AnnotatedBatch(batch=pa.record_batch([], schema=empty_schema))
749-
with make_conn() as proxy, proxy.zero_column_exchange() as session:
750-
for _ in range(100):
751-
output = session.exchange(empty_input)
752-
assert output.batch.schema == empty_schema
753-
assert output.batch.num_rows == 0
754-
assert output.batch.num_columns == 0
755-
756724

757725
# ---------------------------------------------------------------------------
758726
# Tests: RpcConnection (context manager + proxy)

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vgi_rpc/conformance/_impl.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
ScaleExchangeState,
4040
SingleProducerState,
4141
Status,
42+
ZeroColumnExchangeState,
4243
build_dynamic_schema,
4344
build_rich_header,
4445
)
@@ -293,6 +294,11 @@ def exchange_with_logs(self) -> Stream[LoggingExchangeState]:
293294
input_schema=_LOGGING_EXCHANGE_INPUT,
294295
)
295296

297+
def exchange_zero_columns(self) -> Stream[ZeroColumnExchangeState]:
298+
"""Exchange stream with zero-column input and output."""
299+
empty = pa.schema([])
300+
return Stream(output_schema=empty, state=ZeroColumnExchangeState(), input_schema=empty)
301+
296302
def exchange_error_on_nth(self, fail_on: int) -> Stream[FailOnExchangeNState]:
297303
"""Raise on Nth exchange."""
298304
return Stream(

vgi_rpc/conformance/_protocol.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
"""ConformanceService Protocol definition.
55
6-
Defines ~43 RPC methods covering every framework capability:
6+
Defines ~44 RPC methods covering every framework capability:
77
scalar echo, void, complex types, optionals, dataclass round-trip,
88
annotated types, multi-param, errors, logging, producer streams,
99
exchange streams, headers, and introspection.
@@ -249,6 +249,10 @@ def exchange_error_on_nth(self, fail_on: int) -> Stream[StreamState]:
249249
"""Raise on the Nth exchange (1-indexed)."""
250250
...
251251

252+
def exchange_zero_columns(self) -> Stream[StreamState]:
253+
"""Exchange stream with zero-column input and output."""
254+
...
255+
252256
def exchange_error_on_init(self) -> Stream[StreamState]:
253257
"""Raise during exchange stream initialization."""
254258
...

vgi_rpc/conformance/_runner.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1080,6 +1080,7 @@ def decorator(
10801080
"exchange_with_header",
10811081
"exchange_with_logs",
10821082
"exchange_with_rich_header",
1083+
"exchange_zero_columns",
10831084
"inspect_point",
10841085
"produce_dynamic_schema",
10851086
"produce_empty",
@@ -1143,6 +1144,7 @@ def decorator(
11431144
"exchange_with_header",
11441145
"exchange_with_logs",
11451146
"exchange_with_rich_header",
1147+
"exchange_zero_columns",
11461148
"produce_dynamic_schema",
11471149
"produce_empty",
11481150
"produce_error_mid_stream",
@@ -1201,7 +1203,7 @@ def _test_desc_describe_version(desc: ServiceDescription) -> None:
12011203

12021204
@_describe_test(category="describe_service", name="method_count")
12031205
def _test_desc_method_count(desc: ServiceDescription) -> None:
1204-
assert len(desc.methods) == 46
1206+
assert len(desc.methods) == 47
12051207

12061208

12071209
# ---------------------------------------------------------------------------

vgi_rpc/conformance/_types.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,18 @@ def exchange(self, input: AnnotatedBatch, out: OutputCollector, ctx: CallContext
264264
out.emit(input.batch)
265265

266266

267+
@dataclass
268+
class ZeroColumnExchangeState(ExchangeState):
269+
"""Accept and emit zero-column batches."""
270+
271+
call_count: int = 0
272+
273+
def exchange(self, input: AnnotatedBatch, out: OutputCollector, ctx: CallContext) -> None:
274+
"""Accept a zero-column batch and emit a zero-column batch."""
275+
self.call_count += 1
276+
out.emit(pa.record_batch([], schema=pa.schema([])))
277+
278+
267279
@dataclass
268280
class FailOnExchangeNState(ExchangeState):
269281
"""Raise on the Nth exchange (1-indexed)."""

0 commit comments

Comments
 (0)