v0.9.0 Public API Review¶
Baseline and outcome¶
Reviewed the dev baseline at commit
869a5b1ef62d66cdfdeff236db3cd5355d9f0e82 against the
v1.0 feature scope. This record describes the corrections in
this working change; it does not imply that they have been merged or released.
The baseline passed 693 tests with 100% configured branch coverage on Linux /
CPython 3.12.14. Its strict documentation build failed because the API reference
still requested the removed Reader class.
No new source, sink, processor, storage backend, configuration schema version, or CLI command is required by this review.
Review outcomes¶
| Area | Outcome | Decision and evidence |
|---|---|---|
| Public names, imports, constructors, and exports | Corrected | Use TabularReader/TabularWriter; update current examples, diagrams, source docstrings, and generated API targets. Preserve existing constructor arguments. See core/__init__.py, package __init__.py files, and the API reference. |
Batch and output schema |
Accepted | Batch(table: pa.Table) retains the supplied Arrow table. Pipeline output compatibility requires matching names, order, types, and nullability; schema/field metadata is ignored. The first processed output batch, including a zero-row batch, establishes the run schema. See tests/unit/core/test_batch.py and tests/unit/pipeline/test_pipeline.py. |
| Processors and predicates | Corrected | Retain existing signatures and the six comparison operators. Normalize ambiguous selection to ValueError; enforce unique final rename names even for duplicate input fields. Existing and new tests are in tests/unit/processors/. |
| File composition and storage | Accepted | FileSource(path, storage, reader) and FileSink(path, storage, writer) own opened streams, borrow component instances, and propagate errors. Local writes truncate and may leave partial output. See unit source/sink/storage tests and file round-trip integration tests. |
| Tabular streams and document parsing | Corrected | Keep separate batch and mapping contracts. Document YamlDocumentError as the expected root-shape failure and verify a non-YAML KeyValueReader with the real configuration validator. See reader/writer unit tests and tests/unit/config/test_loader.py. |
| SQL queries and parameters | Accepted | Retain SqlSource(engine, query, *, parameters=None); bind query values with SQLAlchemy. SQL text and identifiers remain trusted application configuration, not an authorization boundary. Results are materialized into one batch; empty results produce no batches. See unit/integration SQL source tests. |
| SQL writes and transactions | Accepted | Retain SqlSink(engine, table_name, *, schema=None). Append to existing tables; skip zero-row batches before reflection/validation; use one transaction per nonempty batch. Earlier commits survive later failures. See unit/integration SQL sink tests and SQL round-trip tests. |
| SQL resource documentation | Corrected | Replace the copied file-sink note in SqlSource.read() with result/connection ownership, eager materialization, and Arrow conversion behavior. |
| YAML schema and registration | Accepted | Retain schema version 1, required root fields, strict built-in properties, explicit registrations, aggregated configuration errors, and builder preflight before runtime factories. Direct builders/factories require validated configuration. See config unit tests, default-component integration tests, and configuration-preflight integration tests. |
| CLI commands, help, and validation | Corrected | Preserve commands, help, logging controls, and exit codes. Correct README claims: validate parses and validates without constructing runtime components or resolving SQL environment values. Regression coverage uses an unset SQL reference and forbids builder construction. |
| CLI document errors | Corrected | Empty YAML, non-mapping roots, and non-string root keys return code 3 for both validate and run, without document contents or a traceback. Previously they returned code 1. See tests/unit/test_cli_validate.py. |
| Exceptions, logging, and failure propagation | Accepted with documented correction | Preserve native library exceptions and existing pipeline execution/cleanup precedence. Add only the specific YamlDocumentError subtype required to identify expected document failures at the CLI boundary. Existing CLI, pipeline, and SQL engine factory tests cover safe diagnostics and cleanup precedence. |
| Additional formats, backends, write modes, plugins, and conversion APIs | Explicitly deferred | No change to the frozen feature boundary. KeyValueWriter and generic record conversion remain deferred. |
Public imports and compatibility¶
The supported convenience imports are the responsibility-level packages:
| Package | Public names |
|---|---|
etlrelay.core |
Batch, Source, Processor, Sink, TabularReader, TabularWriter, KeyValueReader, WriterError |
etlrelay.pipeline |
Pipeline |
etlrelay.processors |
SelectColumns, RenameColumns, FilterRows |
etlrelay.readers |
CsvReader, ParquetReader, YamlReader, YamlDocumentError |
etlrelay.writers |
CsvWriter, ParquetWriter |
etlrelay.sources |
FileSource, SqlSource |
etlrelay.sinks |
FileSink, SqlSink |
etlrelay.storage |
FileSystem, FileSystemError, LocalFileSystem |
etlrelay.security |
PathValidator, PathValidationError |
The root etlrelay package does not provide an umbrella re-export API.
Configuration services and registries use the documented implementation-module
imports, as shown by the generated API reference.
The old Reader and Writer class names are removed without aliases. Their
modules remain etlrelay.core.reader and etlrelay.core.writer to avoid an
additional import-path break. KeyValueReader remains in the reader module.
This explicitly revises the earlier module-renaming plan and corrects the
changelog's inaccurate claim that those modules were removed.
ReaderError remains importable from etlrelay.core.reader; it is not a
promise that every reader normalizes failures to that exception. WriterError
is used by the Parquet writer for empty iterables and incompatible schemas.
Protocols use structural typing. Runtime isinstance checks on protocols only
check member availability; they do not prove return types, stream ownership,
or behavioral substitutability. Both reader protocols contain read, so they
must not be distinguished solely through runtime protocol checks.
Contract details retained for v1.0¶
- Type annotations define expected Python inputs. This review does not add blanket runtime type validation or widen constructor signatures.
Batchcan contain duplicate Arrow field names. Selection requires requested names to be unambiguous, filtering requires its target to be unambiguous, renaming requires unique final names, and SQL boundaries reject duplicates.- Processors preserve the relevant field types and nullability.
FilterRowsuses Arrow comparisons without custom casting and excludes false/null results. YAML filter values remain restricted to strings, integers, floats, and booleans; SQL bound parameters additionally accept null. Direct Python values follow Arrow's comparison support and are not constrained by the YAML schema. - Output schema enforcement belongs to
Pipeline; direct writers and sinks retain their own format/database checks. There is no implicit reconciliation. - Tabular readers/writers borrow streams. Callers keep streams open while consuming lazy input. File components own and close streams they open.
- An entirely empty pipeline source leaves its sink untouched. Direct calls to a file sink still open/truncate its destination, even with no batches.
- CSV writing of no batches writes no bytes. Parquet writing requires at least one batch to establish its schema. A zero-row batch with a schema is valid.
- SQL result/connection lifetimes end before Arrow conversion. Sources materialize all result rows, so batch-oriented execution does not guarantee bounded memory.
- Direct SQL components borrow caller-owned engines. Configuration-built engines belong to the shared engine factory and are released through builder cleanup.
- File writes are not atomic. SQL batch transactions do not provide pipeline-wide rollback or automatic retry/idempotency.
validatereads the configuration file and checks document/schema rules.runadditionally resolves component registrations, preflights SQL environment references, builds components, executes, and closes builder resources.- CLI exit codes remain 0 success, 1 unexpected internal error, 2 usage error, 3 configuration/build failure, and 4 execution/cleanup failure.
- Library errors may include native third-party details. CLI execution diagnostics omit underlying messages; controlled logs do not establish universal redaction.
Exception boundary correction¶
YamlDocumentError subclasses ValueError, preserving existing library catches
for YAML root-shape errors. Syntax, duplicate-key, and multiple-document errors
remain yaml.YAMLError. ConfigLoader continues to propagate reader errors.
The CLI catches the specific document error and emits a controlled shape
message. It does not classify every ValueError as user configuration failure;
unrelated internal errors retain exit code 1.
Follow-up and release blockers¶
The defects identified in this review are addressed in this change. There is no additional unresolved runtime API blocker identified by the reviewed evidence. These corrections must be integrated and required checks must pass before this ticket is closed.
The following release-readiness gates remain owned by later tickets:
| Ticket | Required follow-up |
|---|---|
| 0.9.0-04 | Audit representative supported pipeline combinations and interface boundaries. |
| 0.9.0-05 | Complete the failure/cleanup matrix, including borrowed-stream failure paths and inaccessible resources. |
| 0.9.0-06 | Verify wheel and source-distribution installation and the declared Python/platform matrix. Local Linux/3.12 results do not replace those checks. |
| 0.9.0-07 | Complete examples and user documentation, versioned release links, contribution instructions, and release notes. |
| 0.9.0-08 | Review actual issue-tracker defects and complete beta release sign-off. This source review does not establish that the tracker has no blockers. |
Validation of this change¶
Verified locally on Linux / CPython 3.12.14 using the locked dependencies:
- Full unit, integration, and functional suite: 708 passed.
- Configured statement/branch coverage: 100%.
- Ruff lint and formatting checks: passed.
- Pyright: zero errors and warnings.
- Strict MkDocs build: passed.
git diff --check: passed.
The 15 additional cases include invalid document shapes through both CLI
commands, preservation of unrelated internal ValueError classification,
duplicate-column behavior, non-YAML reader substitution, and validation without
SQL preflight. Twelve new regression cases were demonstrated failing before
implementation corrections.
These results do not certify other Python/platform combinations or complete later compatibility, failure-matrix, documentation, or beta-sign-off tickets.