Date: 2026-06-12 Baseline: v1.0.11 (PyPI) + large uncommitted working tree (~262 changed paths)
This plan is based on a full repository audit covering architecture and features, code quality, testing, CI/CD, documentation, and release/product quality. Items are grouped by theme and ordered by priority within each theme. Suggested phasing is at the end.
Strengths
- 107 format classes behind a unified
open_iterable()/BaseIterableAPI; 10 compression codecs; 7 DB read engines; mature convert, pipeline, and ops layers. - 147 test files with a 75% coverage gate; multi-Python (3.10–3.12) and cross-OS CI; weekly security scans; Dependabot.
- Minimal core dependencies (
chardet,tqdm) with 30+ optional extras;py.typedshipped; published on PyPI asiterabledata. - Mature OpenSpec process (38 specs, 0 active proposals), Cursor skills, AGENTS.md, large Docusaurus docs site, 1,300-line README.
- Well-designed plugin system (entry points for formats, codecs, drivers, rules, engines).
Key weaknesses
- Format knowledge is scattered across 5 places (
DATATYPE_REGISTRY,TEXT_DATA_TYPES,FLAT_TYPES,READ_ONLY_FORMATS,datatypes/__init__.py) andREAD_ONLY_FORMATSis stale (missing fasta, bam, sam, trig, graphml, gexf, ...). - Heavy copy-paste: identical
read_bulkloops in ~90 files,_graph_to_recordsduplicated in 3 graph formats, RDF helpers duplicated in 3 modules, codec stream-wrapping duplicated, 6 near-identical ingest modules. - Quality gates not enforced: mypy, pydocstyle, vulture, radon, security scans, and even some ruff format checks run with
|| truein CI. - 10 formats with no test coverage (kafka, pulsar, flink, beam, lance, recordio, sequencefile, tfrecord, flexbuffers, zipped); 26 formats without docs pages.
- Broken contracts:
pcap.pyreturnsNoneinstead of raisingStopIteration, lacksid()/read_bulk();zipped.py/zipxml.pybypassBaseFileIterable;async_base.pyswallows all exceptions asStopAsyncIteration. - Docs site deployment is broken (configured URL returns 404; org/repo mismatch between
iterabledata.github.ioconfig anddatenoio/iterabledatarepo). - Release hygiene: two overlapping CI workflows, two PyPI publish paths with different secrets, release tests use legacy
requirements.txt, and a large feature batch (RDF/bio/graph/geo formats, examples) is uncommitted and unreleased. - SQL injection surface in ingest modules (table/column names interpolated into SQL strings).
These unblock everything else and carry release/security risk.
- Review, commit, and push the ~262-path working tree (new formats: fasta, fastq, bam, sam, cdf, dot, gexf, graphml, gpx, kmz, n3, trig, trix, xlsb; new examples; OpenSpec archives).
- Cut release
1.0.12(or1.1.0given the feature volume): move the large[Unreleased]CHANGELOG section into a release entry, bumpiterable/__init__.py, tag, and publish.
iterable/ingest/postgresql.py,mysql.py,sqlite.py, etc. interpolate table/column names directly into SQL.- Add identifier validation/quoting (e.g.
psycopg.sql.Identifier, backtick/bracket quoting per driver, plus an allowlist regex for identifiers). - Status: Done.
iterable/ingest/identifiers.pyvalidates and quotes table/column names; used by postgresql/mysql/sqlite/duckdb ingest and_sql_base.py. Covered bytests/test_ingest_identifiers.py.
iterable/datatypes/pcap.py: raiseStopIterationinstead of returningNone; addid(),read_bulk(), properreset(); switch to relative imports.iterable/async_base.py__anext__: only convertStopIterationtoStopAsyncIteration; let parse/I/O errors propagate. Align async default bulk size (1000) with syncDEFAULT_BULK_NUMBER(100) or document the difference.- Status: Done.
pcap.pyrefactored:read()/__iter__yield row dicts and raiseStopIterationat EOF;reset()clears reader state; contract tests intest_pcap.py.async_base.pyusesDEFAULT_BULK_NUMBER(100); error propagation tests intest_async_support.py.
- Resolve the org/repo mismatch:
docs/docusaurus.config.jstargetsiterabledata.github.iowhile the repo isdatenoio/iterabledata; both URLs currently 404. - Point config at the real Pages target, verify
deploy-docs.ymlpublishes successfully, and update README/pyproject URLs to the working docs site. - Status: Done. Docusaurus config, README, and
pyproject.tomlpoint athttps://datenoio.github.io/iterabledata/.deploy-docs.ymluses GitHub Pages OIDC. Removeddocs/docs/formats/TEMPLATE.md(invalid YAML brokenpm run build). CIdocsjob runs blocking Docusaurus build. Seedocs/GITHUB_PAGES_SETUP.md.
- Replace the parallel lists in
iterable/helpers/detect.py(DATATYPE_REGISTRY~160 keys,TEXT_DATA_TYPES,FLAT_TYPES,READ_ONLY_FORMATS) and the 521-line conditional-importiterable/datatypes/__init__.pywith declarative per-format descriptors: id, aliases, module/class, text vs binary, flat, streaming, read/write capability, optional extra name, magic bytes. - Drive
detect_file_type_from_content()(currently a long if/elif chain),capabilities.py, and docs generation from the same descriptors. - This also fixes the stale
READ_ONLY_FORMATSlist automatically. - Status: Implemented (core) via OpenSpec change
add-format-metadata-registry. Newiterable/helpers/format_registry.pyholds 108FormatDescriptorentries;detect.pyderivesDATATYPE_REGISTRY,READ_ONLY_FORMATS,TEXT_DATA_TYPES, andFLAT_TYPESfrom them. Magic-byte detection usesmatch_magic_prefix().capabilities.pyusesget_descriptor()for read-only checks. Covered bytests/test_format_registry.py. Full suite verified (pytest -m "not stress": 1959 passed). Follow-up: optionaldatatypes/__init__.pygeneration from descriptors.
- Add a default
read_bulk()loop toBaseIterable(the exact loop copy-pasted in ~90 files); keep optimized overrides (parquetiter_batches, csv inline error handling). - Create shared helpers:
_graph_to_records(gexf/graphml/dot), RDF_term_to_strand load-then-iterate pattern (trig/n3/trix/turtle), optional-dependency import guard. - Extract a generic SQL ingest base (batch loop, table creation, upsert,
IngestionResult) sopostgresql.py/mysql.py/sqlite.pyshrink to driver-specific deltas. Same idea for the_original_fileobjstream-wrapping pattern shared by gzip/bz2/lzma/zstd codecs. - Status: Partially done. Default
read_bulk()inBaseIterable(17 optimized overrides remain). Shared helpers initerable/datatypes/_shared.py(graph_to_records,rdf_term_to_str). SQL ingest batch loop + table creation extracted toiterable/ingest/_sql_base.py(used by postgresql/mysql/sqlite). Codec stream-wrapping deduped initerable/codecs/_stream.py(gzip, bz2, zstd).
- Migrate
zipped.py/zipxml.pyfrom rawBaseIterabletoBaseFileIterable(or implement the abstract contract properly); registerzipxmlinDATATYPE_REGISTRYso it works throughopen_iterable(). - Decide the fate of placeholders:
flatbuffers.py("This is a placeholder"),hudi.py("Placeholder"), the disabled7zcodec.py_(wire it in or delete it; README currently advertises 7z). - Remove the no-op DuckDB path in
iterable/ops/stats.py::compute()or implement it. - Status: Done. ZIP wrappers on
BaseFileIterable; stats no-op removed. Placeholders kept registered with module docstrings documenting partial/schema-dependent behavior. Stale7zcodec.py_removed (szipcodec.pyis canonical);7zadded toCODEC_REGISTRY.
iterable/helpers/detect.py(1,196 lines) → registry, content detection, andopen_iterableorchestration modules.iterable/base.py(955 lines) → split codec base and DataFrame adapters (to_pandas/to_polars/to_daskshare near-identical chunking) out of the iterable hierarchy.iterable/exceptions.py(716 lines) → move the ~230 lines of guidance strings into a separateguidancemodule.- Status: Done. Splits:
content_detection.py,open_iterable.py,format_registry.py,guidance.py,dataframe_adapters.py,codec_base.py.exceptions.py~451 lines;base.py~866 lines;detect.py~440 lines (lazy re-exports).
- Replace legacy
filename: str = Nonewithstr | None = None(~90 files); addfrom __future__ import annotationsand return types onreset()in older modules. - Make mypy blocking in CI for core modules first (
base.py,types.py,helpers/detect.py,exceptions.py), then enabledisallow_untyped_defspackage by package. - Standardize the new-format template: module docstring, typed
__init__, explicit streaming behavior,_handle_error/on_errorintegration (currently only ~15 formats use it), read-only declared in the registry rather than implied by base-class default. - Stop swallowing plugin discovery errors silently in
detect.py(_ensure_plugins_discovered); log at warning level with the failing entry point. - Status: In progress. Strict mypy on 12 core modules (
base,codec_base,guidance,exceptions,types,dataframe_adapters,async_base,helpers/utils,helpers/detect,helpers/open_iterable,helpers/content_detection,helpers/format_registry). Legacyfilename: str = Noneupdated tostr | Noneacross format modules.install_extra_hint()drives per-formatpip install iterabledata[<extra>]messages in_load_symbol. CI lint job runs blocking mypy on all 12.
- Add tests for the 10 uncovered formats:
kafka,pulsar,flink,beam(mock/integration-marker based),lance,recordio,sequencefile,tfrecord,flexbuffers,zipped. - For broker-backed formats (kafka, pulsar) use unit tests with mocked clients plus optional
@pytest.mark.integrationtests behind docker-compose. - Status: Implemented (baseline) in
tests/test_uncovered_formats.py— registry/contract tests for all 10, write/read round-trips for tfrecord/recordio/kafka/pulsar/flink/beam, SequenceFile read contract, zipped wrapper tests; flexbuffers/lance round-trips skip when optional deps are missing.
- Add a parametrized conformance suite that iterates the format registry and asserts the base contract for every registered format with an available fixture:
read()raisesStopIterationat EOF,read_bulk(n)length semantics,reset()re-yields identical rows,id()is a static string, write round-trip wheresupports_writeis true. This would have caught thepcap.pybug. - Extend
dev/scripts/find_missing_fixtures.pyto binary formats and wire it into CI as a report. - Status: Done (baseline).
tests/test_format_conformance.pyuses auto-discovery viatests/conformance_fixtures.py(24 golden formats). Write round-trip tests for writable fixture-backed formats.dev/scripts/find_missing_fixtures.pyreports text+codec gaps and missing binary golden fixtures; CI runs it as an advisory step.
- Merge the duplicate fixture roots (
tests/fixtures/,tests/testdata/, repo-roottestdata/) into one documented layout. - Status: Done. All committed fixtures live under
tests/fixtures/.tests/testdatais a symlink tofixtures/for legacy paths. Repo-roottestdata/removed (files merged intotests/fixtures/). Seetests/fixtures/README.mdandfixture_path()intests/conftest.py.
- Once gaps are closed, raise
fail_underfrom 75 toward 85; track per-package coverage sodatatypes/weak spots are visible. - Status: Started.
dev/scripts/coverage_by_package.pyprints per-package breakdown with advisory floors (--checkfor gradual hardening). Latest local baseline (~62% global): datatypes 54%, helpers 82%, codecs 65%, engines 58%, ingest 48%, convert 79%, db 69%, ops 81%, pipeline 81%. CI runs the script (advisory) on Ubuntu 3.11 after pytest. Globalfail_underremains 75 until coverage gaps close.
- Merge
ci.ymlandtest.yml(they overlap on pytest + ruff for the same branches) into one matrix workflow; keeplint.ymlchecks inside it. - Make checks blocking in stages: ruff format check first, then mypy on core modules, then pydocstyle on new code. Keep vulture/radon advisory.
- Make
bandit/pip-auditfailures at high severity blocking (currently all|| true/continue-on-error). - Status: Done (baseline). Single
ci.ymlwith lint, docs build, matrix test, and security jobs. Blocking: ruff, format, mypy (12 core modules), Docusaurus build, bandit high-severity, pip-audit. Advisory: pydocstyle, vulture, radon, fixture/coverage reports. Weeklysecurity.ymlretained for scheduled scans.
- Pick one publish path:
release.yml(tag-triggered,PYPI_API_TOKEN) vspython-publish.yml(release-triggered,PYPI_ITERABLE_API_TOKEN). Prefer one workflow using PyPI Trusted Publishing (OIDC, no token secret). - Release workflow should install via
pip install -e ".[dev]"like CI, not the legacyrequirements.txt. - Add
check-wheel-contentsandtwine checkto the release job (deps already present). - Status: Done. Tag workflow uses
pip install -e ".[dev]",twine check, andcheck-wheel-contents. PyPI publish is OIDC-only viapython-publish.ymlon release published; no token-based publish path remains in workflows.
- Bump pinned ruff (v0.1.6 is old) and align hook versions with CI; consider adding mypy on changed files.
- Status: Done (baseline). Ruff pre-commit hook bumped to v0.14.9 (matches local/CI ruff).
pre-commit-hooksv5.0.0, bandit 1.8.3, vulture v2.14.CONTRIBUTING.mddocumentspre-commit install. Follow-up: optional mypy hook on changed core modules.
- 27 formats are read-only. Prioritize per the existing
write-support-roadmap.md: avro, xlsx, xml, ods first (common ETL targets); then scientific (hdf5, netcdf) and lakehouse (delta — write viadeltalakepackage is feasible; iceberg viapyiceberg).
aopen_iterable()is a thread-pool wrapper. Implement native async I/O for the highest-traffic line-oriented formats first (csv, jsonl, json) usingaiofiles, and exportaopen_iterablefrom the top-leveliterablepackage.
- Add ClickHouse and MSSQL ingest backends to match the 7 read drivers (8 vs 6 today).
- Extend connection pooling beyond PostgreSQL (at minimum MySQL).
- Write-side validation hooks exist in only 8 of ~80 writable formats. Once
read_bulk/write plumbing is centralized in the base class (3.2), hooks come for free — verify with the conformance suite.
- Publish one reference plugin package (e.g. a niche format) to prove the entry-point path end to end, and document plugin authoring in the docs site.
- A CLI is already provided through the project's distribution metadata, not from this package. Do not add a CLI module (
iterable/cli.py) or a[project.scripts]entry point to this repo. See the note inAGENTS.md.
- Add docs pages for the 26 undocumented formats (bam, cdf, fasta, fastq, gexf, gpx, graphml, kmz, n3, sam, topojson, trig, trix, xlsb, dot, arff, bsonf, dxf, feed, libsvm, mvt, netcdf, numpy, pcap, picklef, zipped). Consider generating stubs from the format metadata registry (3.1). Status: Done (stubs).
dev/scripts/generate_format_doc_stubs.pygenerates registry stubs; 23 pages added underdocs/docs/formats/and linked fromindex.md. Expand stubs with full examples over time. - Add
CONTRIBUTING.md(can largely link to AGENTS.md), GitHub issue templates, and a PR template. Status: Done —CONTRIBUTING.md,.github/ISSUE_TEMPLATE/, and.github/pull_request_template.md. - Fix the
[Unreleased]CHANGELOG discipline after the 1.0.12 release: keep entries small and release more frequently. - Fix the
iterable/iterabledata.code-workspacefile accidentally placed inside the package directory (untracked; should not ship in the wheel). - Clean up the misleading
ImportErrorhint indetect.py::_load_symbolthat suggestspip install iterabledata[dev]for missing format extras — point to the correct extra per format (also enabled by 3.1). Status: Done —install_extra_hint()informat_registry.pydrives per-formatpip install iterabledata[<extra>]messages.
Phase 1 — Stabilize (1–2 weeks)
- 2.1 commit + release 1.0.12, 2.2 SQL injection fix, 2.3 contract bugs (pcap, async), 2.4 docs deployment, 5.2 unify release pipeline.
Phase 2 — Consolidate (2–4 weeks)
- 3.1 format metadata registry, 3.2 dedupe (
read_bulkdefault, shared helpers, ingest base), 3.3 outliers/placeholders, 4.2 conformance suite, 4.1 missing format tests, 5.1 CI consolidation with staged blocking checks.
Phase 3 — Harden (2–4 weeks)
- 3.4 split large modules, 3.5 typing rollout (blocking mypy on core), 4.3 fixture consolidation, 4.4 coverage to 85%, 7 docs/DX items.
Phase 4 — Grow (ongoing)
- 6.1 write support roadmap, 6.2 async Phase 2, 6.3 DB parity, 6.4 validation hooks everywhere, 6.5 reference plugin, 6.6 CLI.
Each Phase 2–4 item that changes public behavior should go through the existing OpenSpec proposal workflow (openspec/AGENTS.md).
- Commit the in-flight feature batch and release 1.0.12.
- Parameterize/quote identifiers in
iterable/ingest/*(security). - Fix
pcap.pyiterator contract andasync_base.pyexception swallowing. - Repair docs site deployment and URL mismatch.
- Build a declarative format metadata registry; delete the 4 parallel lists.
- Move the duplicated
read_bulkloop intoBaseIterable. - Add a registry-driven format conformance test suite.
- Add tests for the 10 uncovered formats.
- Merge duplicate CI workflows; make mypy/format/security checks blocking in stages.
- Unify on a single PyPI publish workflow with Trusted Publishing.