Usage¶
ArrowRecordContainer is designed for synchronous ingestion of Python mapping
records. It does not perform HTTP requests itself; callers provide records from
their own client, parser, stream, or ETL layer.
Append records¶
import pyarrow as pa
from http_to_arrow import ArrowRecordContainer
container = ArrowRecordContainer(
schema=pa.schema([pa.field("id", pa.int64())]),
batch_size=2,
)
container.append({"id": 1})
container.extend([{"id": 2}, {"id": 3}])
assert container.total_rows == 3
batch_size controls when the in-memory accumulator is converted to a pending
Arrow RecordBatch. It does not force materialization into the cached table;
use to_table() or incremental_flush() for that.
Materialize Arrow and Polars outputs¶
table = container.to_table()
arrow_again = container.to_arrow()
polars_frame = container.to_polars_frame()
polars_again = container.to_polars()
assert table is arrow_again
assert polars_frame.shape == polars_again.shape
to_arrow() and to_polars() are compatibility aliases for to_table() and
to_polars_frame().
Flush incrementally¶
For long streams, call incremental_flush() periodically to merge pending
batches into the cached table and clear the batch list.
Tip
Use incremental_flush() inside the loop that reads your upstream records.
This keeps pending Arrow batches bounded without forcing every append to
rebuild the cached table.
import pyarrow as pa
from http_to_arrow import ArrowRecordContainer
container = ArrowRecordContainer(
schema=pa.schema([pa.field("id", pa.int64())]),
batch_size=2,
)
container.extend([{"id": 1}, {"id": 2}, {"id": 3}])
assert container.incremental_flush(threshold=2) is True
assert container.batch_total_rows == 0
assert container.total_rows == 3
incremental_flush(threshold=N) returns True only when pending batch rows are
greater than N.
Drain completed batches¶
Use the batch-draining helpers when a consumer can process Arrow
RecordBatch objects directly and should not retain every completed batch in
the container.
import pyarrow as pa
from http_to_arrow import ArrowRecordContainer
container = ArrowRecordContainer(
schema=pa.schema([pa.field("id", pa.int64())]),
batch_size=2,
)
container.extend([{"id": 1}, {"id": 2}, {"id": 3}])
batches = list(container.iter_batches())
assert [batch.column("id").to_pylist() for batch in batches] == [[1, 2]]
assert container.batch_total_rows == 1
trailing_batch = container.flush_partial()
assert trailing_batch is not None
assert trailing_batch.column("id").to_pylist() == [3]
assert container.batch_total_rows == 0
iter_batches() yields completed batches in FIFO order and removes each batch
from the container as it is yielded. It does not flush the in-flight
accumulator, so call flush_partial() when you need to emit a trailing short
batch.
drain_batches() is the list-returning form for completed pending batches. It
does not flush the accumulator or touch the cached table.
Returned or drained batches are no longer owned by the container. A later
to_table() call will not materialize them again.
Inspect container state¶
Use batch_total_rows and total_rows to understand where rows currently live.
batch_total_rowscounts rows in completed pending batches plus rows still in the in-flight accumulator.total_rowscounts the cached materialized table, completed pending batches, and the in-flight accumulator.
incremental_flush() can move pending batches into the cached table, which
reduces batch_total_rows while preserving total_rows. iter_batches(),
drain_batches(), and flush_partial() transfer batch ownership to the caller,
so those returned rows no longer contribute to either count.
Reset state¶
container.reset()
assert container.total_rows == 0
container.clear()
assert container.batch_total_rows == 0
clear() is a compatibility alias for reset().
Normalize records before ingestion¶
Subclass ArrowRecordContainer to normalize incoming records before schema
matching and coercion.
from typing import Any, Mapping
import pyarrow as pa
from http_to_arrow import ArrowRecordContainer
class LowerKeyContainer(ArrowRecordContainer):
def normalize_record(self, record: Mapping[str, Any]) -> dict[str, Any]:
return {key.lower(): value for key, value in record.items()}
container = LowerKeyContainer(schema=pa.schema([pa.field("id", pa.int64())]))
container.append({"ID": 7})
assert container.to_table().column("id").to_pylist() == [7]
Concurrency model¶
to_table() and incremental_flush() synchronize their materialization paths.
Direct append() and direct flush() calls are not synchronized, so coordinate
multi-threaded appenders outside the container.
Warning
Do not share one container across multiple append threads unless your
application provides the synchronization around append() and flush().
Not provided by this package¶
http-to-arrow has no retry, pagination, authentication, HTTP client, or async
API layer. Compose it with the networking or streaming library that owns those
concerns in your application.