cfinterface 1.9.1__tar.gz → 1.10.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cfinterface-1.9.1 → cfinterface-1.10.1}/.gitignore +3 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/CHANGELOG.md +16 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/PKG-INFO +1 -1
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/__init__.py +1 -1
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/components/floatfield.py +14 -1
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/reading/registerreading.py +1 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/components/test_floatfield.py +105 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/files/test_registerfile.py +42 -0
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/.implementation-state.json +0 -172
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/00-master-plan.md +0 -143
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/README.md +0 -55
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-01-remove-pandas-dependency/00-epic-overview.md +0 -49
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-01-remove-pandas-dependency/learnings.md +0 -91
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-01-remove-pandas-dependency/ticket-001-create-null-checking-utility.md +0 -176
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-01-remove-pandas-dependency/ticket-002-replace-pd-isnull-in-fields.md +0 -177
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-01-remove-pandas-dependency/ticket-003-lazy-pandas-import-in-registerfile.md +0 -143
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-01-remove-pandas-dependency/ticket-004-move-pandas-to-optional-dependency.md +0 -153
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-02-compile-regex-and-storage-enum/00-epic-overview.md +0 -60
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-02-compile-regex-and-storage-enum/learnings.md +0 -76
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-02-compile-regex-and-storage-enum/ticket-005-compile-regex-patterns.md +0 -183
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-02-compile-regex-and-storage-enum/ticket-006-create-storagetype-enum.md +0 -180
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-02-compile-regex-and-storage-enum/ticket-007-update-factory-functions.md +0 -194
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-02-compile-regex-and-storage-enum/ticket-008-migrate-internal-storage-usage.md +0 -243
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-02-compile-regex-and-storage-enum/ticket-009-add-deprecation-warnings.md +0 -193
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-03-optimize-float-field-write/00-epic-overview.md +0 -25
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-03-optimize-float-field-write/learnings.md +0 -69
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-03-optimize-float-field-write/ticket-010-optimize-floatfield-write.md +0 -242
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-03-optimize-float-field-write/ticket-011-add-floatfield-benchmarks.md +0 -224
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-04-array-backed-data-containers/00-epic-overview.md +0 -33
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-04-array-backed-data-containers/learnings.md +0 -96
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-04-array-backed-data-containers/ticket-012-list-backed-registerdata.md +0 -160
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-04-array-backed-data-containers/ticket-013-list-backed-blockdata.md +0 -158
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-04-array-backed-data-containers/ticket-014-list-backed-sectiondata.md +0 -156
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-04-array-backed-data-containers/ticket-015-computed-prev-next-properties.md +0 -266
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-04-array-backed-data-containers/ticket-016-type-indexed-lookup.md +0 -199
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-05-type-safe-text-binary-dispatch/00-epic-overview.md +0 -29
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-05-type-safe-text-binary-dispatch/learnings.md +0 -209
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-05-type-safe-text-binary-dispatch/ticket-017-typed-field-protocol.md +0 -175
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-05-type-safe-text-binary-dispatch/ticket-018-typed-repository-signatures.md +0 -210
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-05-type-safe-text-binary-dispatch/ticket-019-typed-line-dispatch.md +0 -159
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-05-type-safe-text-binary-dispatch/ticket-020-typed-component-io.md +0 -249
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-06-generic-tabular-parser/00-epic-overview.md +0 -27
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-06-generic-tabular-parser/ticket-021-tabular-parser-engine.md +0 -198
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-06-generic-tabular-parser/ticket-022-tabular-section-base.md +0 -220
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-06-generic-tabular-parser/ticket-023-delimited-tabular-parsing.md +0 -198
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-07-schema-versioning-and-batch-ops/00-epic-overview.md +0 -32
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-07-schema-versioning-and-batch-ops/learnings.md +0 -62
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-07-schema-versioning-and-batch-ops/ticket-024-schema-version-descriptor.md +0 -133
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-07-schema-versioning-and-batch-ops/ticket-025-instance-level-version-binding.md +0 -162
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-07-schema-versioning-and-batch-ops/ticket-026-batch-read-api.md +0 -164
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/epic-07-schema-versioning-and-batch-ops/ticket-027-version-validation.md +0 -214
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/learnings/epic-01-summary.md +0 -41
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/learnings/epic-02-summary.md +0 -64
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/learnings/epic-03-summary.md +0 -80
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/learnings/epic-04-summary.md +0 -79
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/learnings/epic-05-summary.md +0 -96
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/learnings/epic-06-summary.md +0 -130
- cfinterface-1.9.1/plans/cfinterface-architecture-improvement/learnings/epic-07-summary.md +0 -125
- cfinterface-1.9.1/plans/cfinterface-quality-and-ci/.implementation-state.json +0 -449
- cfinterface-1.9.1/plans/cfinterface-quality-and-ci/00-master-plan.md +0 -119
- cfinterface-1.9.1/plans/cfinterface-quality-and-ci/README.md +0 -69
- cfinterface-1.9.1/plans/cfinterface-quality-and-ci/epic-01-pyproject-and-tool-config/00-epic-overview.md +0 -44
- cfinterface-1.9.1/plans/cfinterface-quality-and-ci/epic-01-pyproject-and-tool-config/ticket-001-update-classifiers-and-metadata.md +0 -117
- cfinterface-1.9.1/plans/cfinterface-quality-and-ci/epic-01-pyproject-and-tool-config/ticket-002-add-pytest-configuration.md +0 -118
- cfinterface-1.9.1/plans/cfinterface-quality-and-ci/epic-01-pyproject-and-tool-config/ticket-003-add-mypy-configuration.md +0 -157
- cfinterface-1.9.1/plans/cfinterface-quality-and-ci/epic-01-pyproject-and-tool-config/ticket-004-fix-mypy-strict-errors.md +0 -141
- cfinterface-1.9.1/plans/cfinterface-quality-and-ci/epic-01-pyproject-and-tool-config/ticket-005-add-coverage-and-ruff-config.md +0 -131
- cfinterface-1.9.1/plans/cfinterface-quality-and-ci/epic-01-pyproject-and-tool-config/ticket-006-add-py-typed-and-dev-deps.md +0 -138
- cfinterface-1.9.1/plans/cfinterface-quality-and-ci/epic-02-ci-overhaul/00-epic-overview.md +0 -41
- cfinterface-1.9.1/plans/cfinterface-quality-and-ci/epic-02-ci-overhaul/ticket-007-expand-test-matrix.md +0 -166
- cfinterface-1.9.1/plans/cfinterface-quality-and-ci/epic-02-ci-overhaul/ticket-008-extract-lint-job.md +0 -138
- cfinterface-1.9.1/plans/cfinterface-quality-and-ci/epic-02-ci-overhaul/ticket-009-create-benchmark-workflow.md +0 -135
- cfinterface-1.9.1/plans/cfinterface-quality-and-ci/epic-02-ci-overhaul/ticket-010-update-docs-and-publish-workflows.md +0 -189
- cfinterface-1.9.1/plans/cfinterface-quality-and-ci/epic-03-test-infrastructure/00-epic-overview.md +0 -40
- cfinterface-1.9.1/plans/cfinterface-quality-and-ci/epic-03-test-infrastructure/ticket-011-create-conftest-and-fixtures.md +0 -195
- cfinterface-1.9.1/plans/cfinterface-quality-and-ci/epic-03-test-infrastructure/ticket-012-add-hypothesis-field-tests.md +0 -205
- cfinterface-1.9.1/plans/cfinterface-quality-and-ci/epic-03-test-infrastructure/ticket-013-add-hypothesis-tabular-tests.md +0 -238
- cfinterface-1.9.1/plans/cfinterface-quality-and-ci/epic-03-test-infrastructure/ticket-014-migrate-to-pytest-benchmark.md +0 -179
- cfinterface-1.9.1/plans/cfinterface-quality-and-ci/epic-04-sphinx-docs/00-epic-overview.md +0 -41
- cfinterface-1.9.1/plans/cfinterface-quality-and-ci/epic-04-sphinx-docs/ticket-015-add-tabular-reference-pages.md +0 -155
- cfinterface-1.9.1/plans/cfinterface-quality-and-ci/epic-04-sphinx-docs/ticket-016-add-versioning-reference-page.md +0 -151
- cfinterface-1.9.1/plans/cfinterface-quality-and-ci/epic-04-sphinx-docs/ticket-017-add-storage-and-adapter-pages.md +0 -181
- cfinterface-1.9.1/plans/cfinterface-quality-and-ci/epic-04-sphinx-docs/ticket-018-fix-docstrings-and-index.md +0 -157
- cfinterface-1.9.1/plans/cfinterface-quality-and-ci/epic-05-changelog-and-release-prep/00-epic-overview.md +0 -33
- cfinterface-1.9.1/plans/cfinterface-quality-and-ci/epic-05-changelog-and-release-prep/ticket-019-create-changelog.md +0 -169
- cfinterface-1.9.1/plans/cfinterface-quality-and-ci/epic-05-changelog-and-release-prep/ticket-020-release-prep-review.md +0 -124
- cfinterface-1.9.1/plans/infra-docs-overhaul/.implementation-state.json +0 -337
- cfinterface-1.9.1/plans/infra-docs-overhaul/00-master-plan.md +0 -107
- cfinterface-1.9.1/plans/infra-docs-overhaul/README.md +0 -81
- cfinterface-1.9.1/plans/infra-docs-overhaul/epic-01-ci-and-developer-tooling/00-epic-overview.md +0 -26
- cfinterface-1.9.1/plans/infra-docs-overhaul/epic-01-ci-and-developer-tooling/ticket-001-add-pre-commit-configuration.md +0 -124
- cfinterface-1.9.1/plans/infra-docs-overhaul/epic-01-ci-and-developer-tooling/ticket-002-upgrade-github-actions-versions.md +0 -103
- cfinterface-1.9.1/plans/infra-docs-overhaul/epic-01-ci-and-developer-tooling/ticket-003-migrate-docs-deployment.md +0 -149
- cfinterface-1.9.1/plans/infra-docs-overhaul/epic-02-sphinx-modernization/00-epic-overview.md +0 -27
- cfinterface-1.9.1/plans/infra-docs-overhaul/epic-02-sphinx-modernization/ticket-004-migrate-sphinx-theme-to-furo.md +0 -119
- cfinterface-1.9.1/plans/infra-docs-overhaul/epic-02-sphinx-modernization/ticket-005-add-sphinx-gallery-examples.md +0 -119
- cfinterface-1.9.1/plans/infra-docs-overhaul/epic-02-sphinx-modernization/ticket-006-update-conf-language-intersphinx.md +0 -113
- cfinterface-1.9.1/plans/infra-docs-overhaul/epic-03-documentation-content/00-epic-overview.md +0 -30
- cfinterface-1.9.1/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-007-create-architecture-overview.md +0 -132
- cfinterface-1.9.1/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-008-create-faq-page.md +0 -129
- cfinterface-1.9.1/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-009-create-migration-guide.md +0 -138
- cfinterface-1.9.1/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-010-create-performance-tips-page.md +0 -131
- cfinterface-1.9.1/plans/infra-docs-overhaul/epic-03-documentation-content/ticket-011-update-contributing-rst.md +0 -130
- cfinterface-1.9.1/plans/infra-docs-overhaul/epic-04-repository-polish/00-epic-overview.md +0 -23
- cfinterface-1.9.1/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-012-create-root-contributing-md.md +0 -106
- cfinterface-1.9.1/plans/infra-docs-overhaul/epic-04-repository-polish/ticket-013-restructure-index-rst.md +0 -142
- cfinterface-1.9.1/plans/infra-docs-overhaul/learnings/epic-01-summary.md +0 -29
- cfinterface-1.9.1/plans/infra-docs-overhaul/learnings/epic-02-summary.md +0 -31
- cfinterface-1.9.1/plans/infra-docs-overhaul/learnings/epic-03-summary.md +0 -28
- {cfinterface-1.9.1 → cfinterface-1.10.1}/.github/workflows/benchmark.yml +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/.github/workflows/docs.yml +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/.github/workflows/main.yml +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/.github/workflows/publish.yml +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/.pre-commit-config.yaml +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/CONTRIBUTING.md +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/LICENSE +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/README.md +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/benchmarks/__init__.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/benchmarks/bench_floatfield_write.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/_utils/__init__.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/adapters/__init__.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/adapters/components/__init__.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/adapters/components/line/__init__.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/adapters/components/line/repository.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/adapters/components/repository.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/adapters/reading/__init__.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/adapters/reading/repository.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/adapters/writing/__init__.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/adapters/writing/repository.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/components/__init__.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/components/block.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/components/datetimefield.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/components/defaultblock.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/components/defaultregister.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/components/defaultsection.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/components/field.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/components/integerfield.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/components/line.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/components/literalfield.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/components/register.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/components/section.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/components/tabular.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/data/__init__.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/data/blockdata.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/data/registerdata.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/data/sectiondata.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/files/__init__.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/files/blockfile.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/files/registerfile.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/files/sectionfile.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/py.typed +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/reading/__init__.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/reading/blockreading.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/reading/sectionreading.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/storage.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/versioning.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/writing/__init__.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/writing/blockwriting.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/writing/registerwriting.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/cfinterface/writing/sectionwriting.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/_static/logo_cfinterface.svg +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/conf.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/getting_started/contributing.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/getting_started/tutorial.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/guides/architecture.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/guides/faq.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/guides/migration-v1.9.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/guides/performance.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/index.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/install/install.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/reference/adapters/files/components.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/reference/adapters/files/line.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/reference/adapters/files/reading.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/reference/adapters/files/writing.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/reference/adapters/index.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/reference/blocks/files/block.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/reference/blocks/files/blockdata.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/reference/blocks/files/blockfile.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/reference/blocks/index.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/reference/fields/components/datetimefield.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/reference/fields/components/field.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/reference/fields/components/floatfield.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/reference/fields/components/integerfield.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/reference/fields/components/literalfield.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/reference/fields/index.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/reference/line/index.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/reference/registers/files/register.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/reference/registers/files/registerdata.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/reference/registers/files/registerfile.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/reference/registers/index.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/reference/sections/files/section.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/reference/sections/files/sectiondata.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/reference/sections/files/sectionfile.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/reference/sections/index.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/reference/storage/files/storagetype.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/reference/storage/index.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/reference/tabular/files/tabularparser.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/reference/tabular/files/tabularsection.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/reference/tabular/index.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/reference/versioning/files/versioning.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/docs/source/reference/versioning/index.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/examples/README.rst +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/examples/__init__.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/examples/plot_block_file.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/examples/plot_register_file.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/examples/plot_section_file.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/examples/plot_tabular_parsing.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/examples/plot_versioned_file.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/pyproject.toml +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/__init__.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/_utils/__init__.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/_utils/test_is_null.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/adapters/__init__.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/adapters/components/__init__.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/adapters/components/test_blockrepository.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/adapters/components/test_factory.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/adapters/components/test_linerepository.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/adapters/components/test_pattern_cache.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/adapters/components/test_registerrepository.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/adapters/components/test_sectionrepository.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/adapters/reading/__init__.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/adapters/reading/test_readingrepository.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/adapters/writing/__init__.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/adapters/writing/test_writingrepository.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/components/__init__.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/components/conftest.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/components/test_block.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/components/test_datetimefield.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/components/test_defaultblock.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/components/test_defaultregister.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/components/test_defaultsection.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/components/test_field.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/components/test_field_hypothesis.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/components/test_floatfield_benchmark.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/components/test_integerfield.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/components/test_line.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/components/test_literalfield.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/components/test_register.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/components/test_section.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/components/test_storage_integration.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/components/test_tabular.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/components/test_tabular_hypothesis.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/conftest.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/data/__init__.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/data/test_blockdata.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/data/test_registerdata.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/data/test_sectiondata.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/files/__init__.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/files/test_blockfile.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/files/test_sectionfile.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/mocks/__init__.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/mocks/mock_open.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/reading/__init__.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/reading/test_blockreading.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/reading/test_registerreading.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/reading/test_sectionreading.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/test_storage.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/test_storage_deprecation.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/test_versioning.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/writing/__init__.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/writing/test_blockwriting.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/writing/test_registerwriting.py +0 -0
- {cfinterface-1.9.1 → cfinterface-1.10.1}/tests/writing/test_sectionwriting.py +0 -0
|
@@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.10.1] - 2026-04-13
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Resolve mypy `arg-type` error in `FloatField._apply_compact` by passing the numeric value as a parameter instead of accessing `self._value` directly
|
|
15
|
+
|
|
16
|
+
## [1.10.0] - 2026-04-13
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- `compact` parameter on `FloatField` (`compact=False` by default) that omits the leading zero for values where `0 < |value| < 1`, matching Fortran fixed-width file conventions and preserving precision in narrow fields
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- `RegisterReading.read()` now resets its internal data before each call, preventing register duplication when `RegisterFile.read()` retries with fallback encodings after a mid-file `UnicodeDecodeError`
|
|
25
|
+
|
|
10
26
|
## [1.9.1] - 2026-03-08
|
|
11
27
|
|
|
12
28
|
### Fixed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cfinterface
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.10.1
|
|
4
4
|
Summary: Interface for handling custom formatted files
|
|
5
5
|
Project-URL: Documentation, https://rjmalves.github.io/cfinterface/
|
|
6
6
|
Project-URL: Repository, https://github.com/rjmalves/cfinterface/
|
|
@@ -13,7 +13,7 @@ class FloatField(Field):
|
|
|
13
13
|
by 'F' for fixed point notation and 'E' or 'D' for scientific notation.
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
|
-
__slots__ = ["__decimal_digits", "__format", "__sep", "__type"]
|
|
16
|
+
__slots__ = ["__compact", "__decimal_digits", "__format", "__sep", "__type"]
|
|
17
17
|
|
|
18
18
|
TYPES = {
|
|
19
19
|
2: np.float16,
|
|
@@ -29,12 +29,14 @@ class FloatField(Field):
|
|
|
29
29
|
format: str = "F",
|
|
30
30
|
sep: str = ".",
|
|
31
31
|
value: float | None = None,
|
|
32
|
+
compact: bool = False,
|
|
32
33
|
) -> None:
|
|
33
34
|
super().__init__(
|
|
34
35
|
size,
|
|
35
36
|
starting_position,
|
|
36
37
|
value,
|
|
37
38
|
)
|
|
39
|
+
self.__compact = compact
|
|
38
40
|
self.__decimal_digits = decimal_digits
|
|
39
41
|
self.__format = format
|
|
40
42
|
self.__sep = sep
|
|
@@ -63,6 +65,14 @@ class FloatField(Field):
|
|
|
63
65
|
else:
|
|
64
66
|
return np.array([self._value], dtype=self.__type).tobytes()
|
|
65
67
|
|
|
68
|
+
def _apply_compact(self, value: str, numeric: float) -> str:
|
|
69
|
+
if self.__compact and 0 < abs(numeric) < 1:
|
|
70
|
+
if value.startswith("0."):
|
|
71
|
+
return value[1:]
|
|
72
|
+
if value.startswith("-0."):
|
|
73
|
+
return "-" + value[2:]
|
|
74
|
+
return value
|
|
75
|
+
|
|
66
76
|
def _textual_write(self) -> str:
|
|
67
77
|
value = ""
|
|
68
78
|
if self.value is not None and not _is_null(self.value):
|
|
@@ -97,6 +107,7 @@ class FloatField(Field):
|
|
|
97
107
|
d=self.__decimal_digits,
|
|
98
108
|
format=formatting_format,
|
|
99
109
|
).replace("E", self.__format)
|
|
110
|
+
value = self._apply_compact(value, self.value)
|
|
100
111
|
if len(value) > self._size:
|
|
101
112
|
excess = len(value) - self._size
|
|
102
113
|
new_d = self.__decimal_digits - excess
|
|
@@ -107,6 +118,7 @@ class FloatField(Field):
|
|
|
107
118
|
d=new_d,
|
|
108
119
|
format=formatting_format,
|
|
109
120
|
).replace("E", self.__format)
|
|
121
|
+
value = self._apply_compact(value, self.value)
|
|
110
122
|
if len(value) > self._size:
|
|
111
123
|
new_d = max(0, new_d - 1)
|
|
112
124
|
value = "{:.{d}{format}}".format(
|
|
@@ -114,6 +126,7 @@ class FloatField(Field):
|
|
|
114
126
|
d=new_d,
|
|
115
127
|
format=formatting_format,
|
|
116
128
|
).replace("E", self.__format)
|
|
129
|
+
value = self._apply_compact(value, self.value)
|
|
117
130
|
return value.rjust(self.size)
|
|
118
131
|
|
|
119
132
|
@property
|
|
@@ -82,6 +82,7 @@ class RegisterReading:
|
|
|
82
82
|
:return: The data from the registers found in the file
|
|
83
83
|
:rtype: RegisterData
|
|
84
84
|
"""
|
|
85
|
+
self.__data = RegisterData(DefaultRegister(data=""))
|
|
85
86
|
self.__repository = factory(self.__storage)(
|
|
86
87
|
content, not isfile(content), encoding
|
|
87
88
|
)
|
|
@@ -196,3 +196,108 @@ def test_floatfield_write_fuzz_equivalence():
|
|
|
196
196
|
f"Mismatch: fmt={fmt} size={size} dec={dec} val={val} "
|
|
197
197
|
f"got={result!r} expected={expected!r}"
|
|
198
198
|
)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def test_floatfield_compact_positive_small():
|
|
202
|
+
f = FloatField(5, 0, 4, format="F", value=0.0563, compact=True)
|
|
203
|
+
assert f._textual_write() == ".0563"
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def test_floatfield_compact_negative_small():
|
|
207
|
+
f = FloatField(6, 0, 4, format="F", value=-0.0563, compact=True)
|
|
208
|
+
assert f._textual_write() == "-.0563"
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def test_floatfield_compact_no_effect_above_one():
|
|
212
|
+
f = FloatField(5, 0, 2, format="F", value=1.50, compact=True)
|
|
213
|
+
assert f._textual_write() == " 1.50"
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def test_floatfield_compact_no_effect_zero():
|
|
217
|
+
f = FloatField(6, 0, 3, format="F", value=0.0, compact=True)
|
|
218
|
+
assert f._textual_write() == " 0.000"
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def test_floatfield_compact_default_false():
|
|
222
|
+
f = FloatField(5, 0, 3, format="F", value=0.0563)
|
|
223
|
+
assert f._textual_write() == "0.056"
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def test_floatfield_compact_preserves_precision():
|
|
227
|
+
"""Concrete example from issue: size=5, decimal_digits=4.
|
|
228
|
+
|
|
229
|
+
Without compact: "0.0563" (6 chars) overflows → reduced to "0.056" (0.53% error).
|
|
230
|
+
With compact: ".0563" fits in 5 chars → no precision loss.
|
|
231
|
+
"""
|
|
232
|
+
f_default = FloatField(5, 0, 4, format="F", value=0.0563)
|
|
233
|
+
f_compact = FloatField(5, 0, 4, format="F", value=0.0563, compact=True)
|
|
234
|
+
assert f_default._textual_write() == "0.056"
|
|
235
|
+
assert f_compact._textual_write() == ".0563"
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def test_floatfield_compact_read_roundtrip():
|
|
239
|
+
"""Reading a compact-formatted value works because float('.0563') parses."""
|
|
240
|
+
f_write = FloatField(5, 0, 4, format="F", value=0.0563, compact=True)
|
|
241
|
+
written = f_write._textual_write()
|
|
242
|
+
assert written == ".0563"
|
|
243
|
+
line = written + "-rest"
|
|
244
|
+
f_read = FloatField(5, 0, 4, format="F")
|
|
245
|
+
f_read.read(line)
|
|
246
|
+
assert f_read.value == 0.0563
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def test_floatfield_compact_negative_read_roundtrip():
|
|
250
|
+
f_write = FloatField(6, 0, 4, format="F", value=-0.0563, compact=True)
|
|
251
|
+
written = f_write._textual_write()
|
|
252
|
+
assert written == "-.0563"
|
|
253
|
+
line = written + "-rest"
|
|
254
|
+
f_read = FloatField(6, 0, 4, format="F")
|
|
255
|
+
f_read.read(line)
|
|
256
|
+
assert f_read.value == -0.0563
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def test_floatfield_compact_no_effect_negative_zero():
|
|
260
|
+
f = FloatField(8, 0, 4, format="F", value=-0.0, compact=True)
|
|
261
|
+
assert f._textual_write() == " -0.0000"
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def test_floatfield_compact_overflow_still_reduces():
|
|
265
|
+
"""When compact alone doesn't prevent overflow, precision is still reduced."""
|
|
266
|
+
f = FloatField(4, 0, 4, format="F", value=0.0563, compact=True)
|
|
267
|
+
result = f._textual_write()
|
|
268
|
+
assert len(result) == 4
|
|
269
|
+
assert result == ".056"
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def test_floatfield_compact_fuzz_properties():
|
|
273
|
+
"""Fuzz: compact output fits field, parses correctly, is at least as precise."""
|
|
274
|
+
import random
|
|
275
|
+
|
|
276
|
+
random.seed(99)
|
|
277
|
+
for _ in range(50000):
|
|
278
|
+
size = random.randint(3, 20)
|
|
279
|
+
dec = random.randint(0, min(10, size - 1))
|
|
280
|
+
val = random.uniform(-0.999, 0.999)
|
|
281
|
+
if abs(val) < 1e-15:
|
|
282
|
+
continue
|
|
283
|
+
f_default = FloatField(size, 0, dec, format="F", value=val)
|
|
284
|
+
f_compact = FloatField(
|
|
285
|
+
size, 0, dec, format="F", value=val, compact=True
|
|
286
|
+
)
|
|
287
|
+
default_result = f_default._textual_write()
|
|
288
|
+
compact_result = f_compact._textual_write()
|
|
289
|
+
assert len(compact_result) == size, (
|
|
290
|
+
f"Wrong length: size={size} dec={dec} val={val} "
|
|
291
|
+
f"compact={compact_result!r} len={len(compact_result)}"
|
|
292
|
+
)
|
|
293
|
+
default_float = (
|
|
294
|
+
float(default_result.strip()) if default_result.strip() else 0.0
|
|
295
|
+
)
|
|
296
|
+
compact_float = (
|
|
297
|
+
float(compact_result.strip()) if compact_result.strip() else 0.0
|
|
298
|
+
)
|
|
299
|
+
# Compact must be at least as precise as default
|
|
300
|
+
assert abs(compact_float - val) <= abs(default_float - val) + 1e-15, (
|
|
301
|
+
f"Precision loss: size={size} dec={dec} val={val} "
|
|
302
|
+
f"default={default_result!r} compact={compact_result!r}"
|
|
303
|
+
)
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import tempfile
|
|
1
3
|
import warnings
|
|
2
4
|
from io import StringIO
|
|
3
5
|
from unittest.mock import MagicMock, patch
|
|
@@ -10,6 +12,8 @@ from cfinterface.components.literalfield import LiteralField
|
|
|
10
12
|
from cfinterface.components.register import Register
|
|
11
13
|
from cfinterface.data.registerdata import RegisterData
|
|
12
14
|
from cfinterface.files.registerfile import RegisterFile
|
|
15
|
+
from cfinterface.reading.registerreading import RegisterReading
|
|
16
|
+
from cfinterface.storage import StorageType
|
|
13
17
|
from tests.mocks.mock_open import mock_open
|
|
14
18
|
|
|
15
19
|
|
|
@@ -259,3 +263,41 @@ def test_registerfile_validate_unknown_version():
|
|
|
259
263
|
f = VersionedRegisterFile.read(filedata)
|
|
260
264
|
result = f.validate(version="v0")
|
|
261
265
|
assert result.matched is False
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def test_registerreading_data_reset_between_reads():
|
|
269
|
+
"""Verify that RegisterReading.__data is reset on each read() call."""
|
|
270
|
+
data = "Hello, world!"
|
|
271
|
+
filedata = DummyRegister.IDENTIFIER + " " + data + "\n"
|
|
272
|
+
reader = RegisterReading([DummyRegister], StorageType.TEXT)
|
|
273
|
+
result1 = reader.read(filedata, "utf-8")
|
|
274
|
+
assert len(result1) == 2 # root DefaultRegister + 1 DummyRegister
|
|
275
|
+
result2 = reader.read(filedata, "utf-8")
|
|
276
|
+
assert len(result2) == 2 # must be 2 again, not 4
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def test_registerfile_read_encoding_fallback_no_duplication():
|
|
280
|
+
"""Verify no data duplication when first encoding fails mid-file.
|
|
281
|
+
|
|
282
|
+
A Latin-1 file with a non-UTF-8 byte causes UTF-8 decoding to fail
|
|
283
|
+
partway through. The fallback to Latin-1 must not carry over partially
|
|
284
|
+
read data from the failed UTF-8 attempt.
|
|
285
|
+
"""
|
|
286
|
+
line1 = DummyRegister.IDENTIFIER + " " + "Hello, world!" + "\n"
|
|
287
|
+
line2 = DummyRegister.IDENTIFIER + " " + "with acc\u00eant! " + "\n"
|
|
288
|
+
|
|
289
|
+
class FallbackFile(RegisterFile):
|
|
290
|
+
REGISTERS = [DummyRegister]
|
|
291
|
+
ENCODING = ["utf-8", "latin-1"]
|
|
292
|
+
|
|
293
|
+
with tempfile.NamedTemporaryFile(
|
|
294
|
+
mode="wb", suffix=".txt", delete=False
|
|
295
|
+
) as f:
|
|
296
|
+
f.write((line1 + line2).encode("latin-1"))
|
|
297
|
+
tmppath = f.name
|
|
298
|
+
try:
|
|
299
|
+
result = FallbackFile.read(tmppath)
|
|
300
|
+
# root DefaultRegister + 2 DummyRegisters = 3
|
|
301
|
+
assert len(result.data) == 3
|
|
302
|
+
finally:
|
|
303
|
+
os.unlink(tmppath)
|
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"plan": "cfinterface-architecture-improvement",
|
|
3
|
-
"last_updated": "2026-02-23T23:30:00.000000+00:00",
|
|
4
|
-
"progressive": true,
|
|
5
|
-
"tickets": {
|
|
6
|
-
"ticket-001-create-null-checking-utility.md": {
|
|
7
|
-
"status": "completed",
|
|
8
|
-
"detail_level": "detailed",
|
|
9
|
-
"agent": "python-task-automation-developer"
|
|
10
|
-
},
|
|
11
|
-
"ticket-002-replace-pd-isnull-in-fields.md": {
|
|
12
|
-
"status": "completed",
|
|
13
|
-
"detail_level": "detailed",
|
|
14
|
-
"agent": "python-task-automation-developer"
|
|
15
|
-
},
|
|
16
|
-
"ticket-003-lazy-pandas-import-in-registerfile.md": {
|
|
17
|
-
"status": "completed",
|
|
18
|
-
"detail_level": "detailed",
|
|
19
|
-
"agent": "python-task-automation-developer"
|
|
20
|
-
},
|
|
21
|
-
"ticket-004-move-pandas-to-optional-dependency.md": {
|
|
22
|
-
"status": "completed",
|
|
23
|
-
"detail_level": "detailed",
|
|
24
|
-
"agent": "python-task-automation-developer"
|
|
25
|
-
},
|
|
26
|
-
"ticket-005-compile-regex-patterns.md": {
|
|
27
|
-
"status": "completed",
|
|
28
|
-
"detail_level": "detailed",
|
|
29
|
-
"agent": "python-task-automation-developer"
|
|
30
|
-
},
|
|
31
|
-
"ticket-006-create-storagetype-enum.md": {
|
|
32
|
-
"status": "completed",
|
|
33
|
-
"detail_level": "detailed",
|
|
34
|
-
"agent": "python-task-automation-developer"
|
|
35
|
-
},
|
|
36
|
-
"ticket-007-update-factory-functions.md": {
|
|
37
|
-
"status": "completed",
|
|
38
|
-
"detail_level": "detailed",
|
|
39
|
-
"agent": "python-task-automation-developer"
|
|
40
|
-
},
|
|
41
|
-
"ticket-008-migrate-internal-storage-usage.md": {
|
|
42
|
-
"status": "completed",
|
|
43
|
-
"detail_level": "detailed",
|
|
44
|
-
"agent": "python-task-automation-developer"
|
|
45
|
-
},
|
|
46
|
-
"ticket-009-add-deprecation-warnings.md": {
|
|
47
|
-
"status": "completed",
|
|
48
|
-
"detail_level": "detailed",
|
|
49
|
-
"agent": "python-task-automation-developer"
|
|
50
|
-
},
|
|
51
|
-
"ticket-010-optimize-floatfield-write.md": {
|
|
52
|
-
"status": "completed",
|
|
53
|
-
"detail_level": "refined",
|
|
54
|
-
"agent": "python-task-automation-developer"
|
|
55
|
-
},
|
|
56
|
-
"ticket-011-add-floatfield-benchmarks.md": {
|
|
57
|
-
"status": "completed",
|
|
58
|
-
"detail_level": "refined",
|
|
59
|
-
"agent": "python-task-automation-developer"
|
|
60
|
-
},
|
|
61
|
-
"ticket-012-list-backed-registerdata.md": {
|
|
62
|
-
"status": "completed",
|
|
63
|
-
"detail_level": "refined",
|
|
64
|
-
"agent": "python-task-automation-developer"
|
|
65
|
-
},
|
|
66
|
-
"ticket-013-list-backed-blockdata.md": {
|
|
67
|
-
"status": "completed",
|
|
68
|
-
"detail_level": "refined",
|
|
69
|
-
"agent": "python-task-automation-developer"
|
|
70
|
-
},
|
|
71
|
-
"ticket-014-list-backed-sectiondata.md": {
|
|
72
|
-
"status": "completed",
|
|
73
|
-
"detail_level": "refined",
|
|
74
|
-
"agent": "python-task-automation-developer"
|
|
75
|
-
},
|
|
76
|
-
"ticket-015-computed-prev-next-properties.md": {
|
|
77
|
-
"status": "completed",
|
|
78
|
-
"detail_level": "refined",
|
|
79
|
-
"agent": "python-task-automation-developer"
|
|
80
|
-
},
|
|
81
|
-
"ticket-016-type-indexed-lookup.md": {
|
|
82
|
-
"status": "completed",
|
|
83
|
-
"detail_level": "refined",
|
|
84
|
-
"agent": "python-task-automation-developer"
|
|
85
|
-
},
|
|
86
|
-
"ticket-017-typed-field-protocol.md": {
|
|
87
|
-
"status": "completed",
|
|
88
|
-
"detail_level": "refined",
|
|
89
|
-
"agent": "python-task-automation-developer"
|
|
90
|
-
},
|
|
91
|
-
"ticket-018-typed-repository-signatures.md": {
|
|
92
|
-
"status": "completed",
|
|
93
|
-
"detail_level": "refined",
|
|
94
|
-
"agent": "python-task-automation-developer"
|
|
95
|
-
},
|
|
96
|
-
"ticket-019-typed-line-dispatch.md": {
|
|
97
|
-
"status": "completed",
|
|
98
|
-
"detail_level": "refined",
|
|
99
|
-
"agent": "python-task-automation-developer"
|
|
100
|
-
},
|
|
101
|
-
"ticket-020-typed-component-io.md": {
|
|
102
|
-
"status": "completed",
|
|
103
|
-
"detail_level": "refined",
|
|
104
|
-
"agent": "python-task-automation-developer"
|
|
105
|
-
},
|
|
106
|
-
"ticket-021-tabular-parser-engine.md": {
|
|
107
|
-
"status": "completed",
|
|
108
|
-
"detail_level": "refined",
|
|
109
|
-
"agent": "python-task-automation-developer"
|
|
110
|
-
},
|
|
111
|
-
"ticket-022-tabular-section-base.md": {
|
|
112
|
-
"status": "completed",
|
|
113
|
-
"detail_level": "refined",
|
|
114
|
-
"agent": "python-task-automation-developer"
|
|
115
|
-
},
|
|
116
|
-
"ticket-023-delimited-tabular-parsing.md": {
|
|
117
|
-
"status": "completed",
|
|
118
|
-
"detail_level": "refined",
|
|
119
|
-
"agent": "python-task-automation-developer"
|
|
120
|
-
},
|
|
121
|
-
"ticket-024-schema-version-descriptor.md": {
|
|
122
|
-
"status": "completed",
|
|
123
|
-
"detail_level": "refined",
|
|
124
|
-
"agent": "python-task-automation-developer"
|
|
125
|
-
},
|
|
126
|
-
"ticket-025-instance-level-version-binding.md": {
|
|
127
|
-
"status": "completed",
|
|
128
|
-
"detail_level": "refined",
|
|
129
|
-
"agent": "python-task-automation-developer"
|
|
130
|
-
},
|
|
131
|
-
"ticket-026-batch-read-api.md": {
|
|
132
|
-
"status": "completed",
|
|
133
|
-
"detail_level": "refined",
|
|
134
|
-
"agent": "python-task-automation-developer"
|
|
135
|
-
},
|
|
136
|
-
"ticket-027-version-validation.md": {
|
|
137
|
-
"status": "completed",
|
|
138
|
-
"detail_level": "refined",
|
|
139
|
-
"agent": "python-task-automation-developer"
|
|
140
|
-
}
|
|
141
|
-
},
|
|
142
|
-
"epics": {
|
|
143
|
-
"epic-01-remove-pandas-dependency": {
|
|
144
|
-
"phase": "completed",
|
|
145
|
-
"learning_extracted": true
|
|
146
|
-
},
|
|
147
|
-
"epic-02-compile-regex-and-storage-enum": {
|
|
148
|
-
"phase": "completed",
|
|
149
|
-
"learning_extracted": true
|
|
150
|
-
},
|
|
151
|
-
"epic-03-optimize-float-field-write": {
|
|
152
|
-
"phase": "completed",
|
|
153
|
-
"learning_extracted": true
|
|
154
|
-
},
|
|
155
|
-
"epic-04-array-backed-data-containers": {
|
|
156
|
-
"phase": "completed",
|
|
157
|
-
"learning_extracted": true
|
|
158
|
-
},
|
|
159
|
-
"epic-05-type-safe-text-binary-dispatch": {
|
|
160
|
-
"phase": "completed",
|
|
161
|
-
"learning_extracted": true
|
|
162
|
-
},
|
|
163
|
-
"epic-06-generic-tabular-parser": {
|
|
164
|
-
"phase": "completed",
|
|
165
|
-
"learning_extracted": true
|
|
166
|
-
},
|
|
167
|
-
"epic-07-schema-versioning-and-batch-ops": {
|
|
168
|
-
"phase": "completed",
|
|
169
|
-
"learning_extracted": true
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
# Master Plan: cfinterface Architecture and Performance Improvement
|
|
2
|
-
|
|
3
|
-
## Executive Summary
|
|
4
|
-
|
|
5
|
-
This plan addresses architectural debt and performance bottlenecks in cfinterface, a Python file schema modeling framework used to parse/write dozens of legacy HPC file formats. The improvements are ordered by impact and risk: foundational dependency removal and performance wins first (pandas removal, regex compilation, storage enum), followed by structural improvements (data containers, type-safe dispatch), and finally consumer-facing features (generic tabular parser, schema versioning). All changes maintain backward compatibility with 254+ downstream file type implementations in inewave and sintetizador-newave.
|
|
6
|
-
|
|
7
|
-
## Goals & Non-Goals
|
|
8
|
-
|
|
9
|
-
### Goals
|
|
10
|
-
|
|
11
|
-
1. Remove the unnecessary pandas runtime dependency from cfinterface core (pandas is only used for `pd.isnull()`)
|
|
12
|
-
2. Pre-compile and cache regex patterns used for identifier/block pattern matching
|
|
13
|
-
3. Replace magic string `"TEXT"`/`"BINARY"` storage parameters with a type-safe enum
|
|
14
|
-
4. Optimize `FloatField._textual_write()` to eliminate the O(decimal_digits) precision loop
|
|
15
|
-
5. Replace linked list data containers with array-backed containers for O(1) `len()` and indexed access
|
|
16
|
-
6. Introduce type-safe text/binary dispatch eliminating `Union[str, bytes]` threading
|
|
17
|
-
7. Provide a generic tabular parser to reduce per-file custom DataFrame parsing in consumers
|
|
18
|
-
8. Add schema versioning support and batch operations API
|
|
19
|
-
|
|
20
|
-
### Non-Goals
|
|
21
|
-
|
|
22
|
-
- Modifying inewave or sintetizador-newave directly (cfinterface remains independent)
|
|
23
|
-
- Breaking backward compatibility without a migration path
|
|
24
|
-
- Adding streaming/chunked read support (deferred to future work after array-backed containers)
|
|
25
|
-
- Property accessor generation for consumer wrappers (separate future initiative)
|
|
26
|
-
- Typed `Section.data` generic parameter (separate future initiative, depends on Python version constraints)
|
|
27
|
-
|
|
28
|
-
## Architecture Overview
|
|
29
|
-
|
|
30
|
-
### Current State
|
|
31
|
-
|
|
32
|
-
```
|
|
33
|
-
cfinterface v1.8.3
|
|
34
|
-
Field (base) --> FloatField, IntegerField, LiteralField, DatetimeField
|
|
35
|
-
- All import pandas for pd.isnull() null detection
|
|
36
|
-
- FloatField has O(n) textual write loop
|
|
37
|
-
Line --> composed of Fields
|
|
38
|
-
- Takes storage: str = "" parameter
|
|
39
|
-
Register/Block/Section --> contain linked-list prev/next pointers
|
|
40
|
-
- re.search() called per line without pre-compilation
|
|
41
|
-
- "TEXT"/"BINARY" magic strings threaded through all APIs
|
|
42
|
-
RegisterData/BlockData/SectionData --> linked list containers
|
|
43
|
-
- O(n) __len__(), O(n) type filtering, no indexing
|
|
44
|
-
RegisterFile/BlockFile/SectionFile --> high-level APIs
|
|
45
|
-
- RegisterFile imports pandas for _as_df() helper
|
|
46
|
-
adapters/ --> Repository pattern with factory("TEXT"/"BINARY") dispatch
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
### Target State
|
|
50
|
-
|
|
51
|
-
```
|
|
52
|
-
cfinterface v2.0.0
|
|
53
|
-
Field (base) --> FloatField, IntegerField, LiteralField, DatetimeField
|
|
54
|
-
- Native _is_null() helper using math.isnan/numpy, zero pandas dependency
|
|
55
|
-
- FloatField uses direct format string, no precision loop
|
|
56
|
-
StorageType enum --> TEXT, BINARY (replaces magic strings)
|
|
57
|
-
Line --> composed of Fields, uses StorageType
|
|
58
|
-
Register/Block/Section --> contain index into parent container (no linked-list pointers)
|
|
59
|
-
- Compiled regex patterns cached at class level
|
|
60
|
-
RegisterData/BlockData/SectionData --> list-backed containers
|
|
61
|
-
- O(1) len(), indexed access, type-indexed lookups
|
|
62
|
-
RegisterFile/BlockFile/SectionFile --> high-level APIs
|
|
63
|
-
- pandas import deferred to _as_df() method only (optional dependency)
|
|
64
|
-
- GenericTabularParser mixin for DataFrame construction
|
|
65
|
-
- Schema versioning with explicit contracts
|
|
66
|
-
adapters/ --> Repository pattern with StorageType enum dispatch
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
### Key Design Decisions
|
|
70
|
-
|
|
71
|
-
1. **pandas becomes optional**: Move pandas to optional dependency. Use `math.isnan()` + type checking for null detection in fields. The `_as_df()` method in RegisterFile keeps pandas but imports lazily.
|
|
72
|
-
2. **StorageType enum is additive**: The enum is introduced alongside the existing string API. Factory functions accept both `str` and `StorageType` during migration. The string API is deprecated but not removed.
|
|
73
|
-
3. **Array-backed containers preserve API**: `RegisterData`, `BlockData`, `SectionData` switch from linked lists to `list` internally. The `previous`/`next` properties on Register/Block/Section become computed from the parent container rather than stored pointers. The public API (`append`, `preppend`, `of_type`, `get_*_of_type`) is preserved.
|
|
74
|
-
4. **Regex compilation is transparent**: Patterns are compiled lazily at first use and cached as class attributes. No API change required.
|
|
75
|
-
|
|
76
|
-
## Technical Approach
|
|
77
|
-
|
|
78
|
-
### Tech Stack
|
|
79
|
-
|
|
80
|
-
- Python >=3.10, numpy >=2.0.0
|
|
81
|
-
- pandas >=2.2.3 (moved to optional dependency)
|
|
82
|
-
- pytest, ruff, mypy (dev)
|
|
83
|
-
|
|
84
|
-
### Component/Module Breakdown
|
|
85
|
-
|
|
86
|
-
| Epic | Component | Files Modified | Risk |
|
|
87
|
-
| ---- | ----------------------------------------------------------- | -------------- | ------ |
|
|
88
|
-
| 1 | Field subclasses, pyproject.toml | 6 files | Low |
|
|
89
|
-
| 2 | adapters/components/repository.py, all storage string sites | 15 files | Low |
|
|
90
|
-
| 3 | FloatField.\_textual_write() | 1 file | Low |
|
|
91
|
-
| 4 | data/\*.py, Register/Block/Section prev/next | 9 files | Medium |
|
|
92
|
-
| 5 | adapters/_, components/_, type annotations | 15 files | Medium |
|
|
93
|
-
| 6 | New module: cfinterface/parsers/ | 3-5 new files | Medium |
|
|
94
|
-
| 7 | files/\*.py, new module: cfinterface/versioning/ | 5-8 files | Medium |
|
|
95
|
-
|
|
96
|
-
### Data Flow
|
|
97
|
-
|
|
98
|
-
```
|
|
99
|
-
File on disk
|
|
100
|
-
--> ReadingRepository (opens file, manages IO)
|
|
101
|
-
--> Reading class (dispatches lines to Register/Block/Section)
|
|
102
|
-
--> Component.read(file, storage) (reads fields from line)
|
|
103
|
-
--> Field.read(line) (extracts typed value from line slice)
|
|
104
|
-
--> Data container stores components
|
|
105
|
-
--> File class exposes data to consumer
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
### Testing Strategy
|
|
109
|
-
|
|
110
|
-
- Every ticket includes unit tests for the specific change
|
|
111
|
-
- Regression tests: run full existing test suite after each ticket
|
|
112
|
-
- Performance benchmarks: add pytest-benchmark fixtures for regex matching, field write, data container operations
|
|
113
|
-
- Integration validation: verify inewave test suite still passes (manual step at epic boundaries)
|
|
114
|
-
|
|
115
|
-
## Phases & Milestones
|
|
116
|
-
|
|
117
|
-
| Phase | Epic | Duration | Milestone |
|
|
118
|
-
| ----- | ---------------------------------------- | --------- | -------------------------------------------------------------------------------- |
|
|
119
|
-
| 1 | Epic 1: Remove pandas dependency | 1 week | pandas is optional, all field tests pass without pandas installed |
|
|
120
|
-
| 1 | Epic 2: Compile regex + StorageType enum | 1-2 weeks | All re.search() calls use compiled patterns, StorageType enum in place |
|
|
121
|
-
| 2 | Epic 3: Optimize FloatField write | 3-5 days | FloatField.\_textual_write() uses direct formatting, benchmarks show improvement |
|
|
122
|
-
| 2 | Epic 4: Array-backed data containers | 2-3 weeks | LinkedList replaced with list, O(1) len(), existing API preserved |
|
|
123
|
-
| 3 | Epic 5: Type-safe text/binary dispatch | 2-3 weeks | Union[str, bytes] eliminated from core APIs, adapters use StorageType |
|
|
124
|
-
| 4 | Epic 6: Generic tabular parser | 2-3 weeks | Reusable tabular parsing for consumers, reduces boilerplate |
|
|
125
|
-
| 4 | Epic 7: Schema versioning + batch ops | 2-3 weeks | Version contracts, bulk read API |
|
|
126
|
-
|
|
127
|
-
## Risk Analysis
|
|
128
|
-
|
|
129
|
-
| Risk | Impact | Mitigation |
|
|
130
|
-
| --------------------------------------------------------- | ------ | --------------------------------------------------------------------------------------------------------- |
|
|
131
|
-
| Breaking 254+ downstream file types | High | Backward-compatible changes only; deprecation before removal; string API preserved alongside enum |
|
|
132
|
-
| pandas removal breaks consumers that pass NaN values | Medium | The \_is_null() function handles numpy NaN, float NaN, and None; comprehensive test coverage |
|
|
133
|
-
| Linked list to array migration changes iteration behavior | Medium | Preserve exact API surface; comprehensive tests for edge cases (concurrent modification during iteration) |
|
|
134
|
-
| Regex compilation overhead at import time | Low | Lazy compilation on first use, not at class definition time |
|
|
135
|
-
|
|
136
|
-
## Success Metrics
|
|
137
|
-
|
|
138
|
-
1. `pytest` passes with 100% of existing tests after each epic
|
|
139
|
-
2. pandas is no longer in `dependencies` (moved to `[project.optional-dependencies]`)
|
|
140
|
-
3. No `re.search()` calls with raw string patterns remain
|
|
141
|
-
4. `len(RegisterData)` is O(1) instead of O(n)
|
|
142
|
-
5. FloatField write benchmark shows measurable improvement
|
|
143
|
-
6. All `"TEXT"`/`"BINARY"` string literals replaced with `StorageType.TEXT`/`StorageType.BINARY`
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
# cfinterface Architecture and Performance Improvement
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
Comprehensive architectural and performance improvement for the cfinterface Python package -- a file schema modeling framework used to parse/write dozens of legacy HPC file formats. Changes are ordered by impact and risk, starting with foundational improvements and progressing to consumer-facing features.
|
|
6
|
-
|
|
7
|
-
## Tech Stack
|
|
8
|
-
|
|
9
|
-
- Python >=3.10, numpy >=2.0.0
|
|
10
|
-
- pandas >=2.2.3 (moved to optional dependency in Epic 01)
|
|
11
|
-
- pytest, ruff, mypy (dev tools)
|
|
12
|
-
|
|
13
|
-
## Epics
|
|
14
|
-
|
|
15
|
-
| Epic | Name | Tickets | Detail Level | Phase |
|
|
16
|
-
| ---- | -------------------------------- | ------- | ------------ | --------- |
|
|
17
|
-
| 01 | Remove pandas dependency | 4 | Detailed | Completed |
|
|
18
|
-
| 02 | Compile regex + StorageType enum | 5 | Detailed | Completed |
|
|
19
|
-
| 03 | Optimize FloatField write | 2 | Refined | Completed |
|
|
20
|
-
| 04 | Array-backed data containers | 5 | Refined | Completed |
|
|
21
|
-
| 05 | Type-safe text/binary dispatch | 4 | Refined | Completed |
|
|
22
|
-
| 06 | Generic tabular parser | 3 | Refined | Completed |
|
|
23
|
-
| 07 | Schema versioning + batch ops | 4 | Refined | Completed |
|
|
24
|
-
|
|
25
|
-
## Progress
|
|
26
|
-
|
|
27
|
-
| Ticket | Title | Epic | Status | Detail Level |
|
|
28
|
-
| ---------- | --------------------------------------------------------------- | ------- | --------- | ------------ |
|
|
29
|
-
| ticket-001 | Create native null-checking utility function | epic-01 | completed | Detailed |
|
|
30
|
-
| ticket-002 | Replace pd.isnull() in all Field subclasses | epic-01 | completed | Detailed |
|
|
31
|
-
| ticket-003 | Convert RegisterFile.\_as_df() to lazy pandas import | epic-01 | completed | Detailed |
|
|
32
|
-
| ticket-004 | Move pandas to optional dependency in pyproject.toml | epic-01 | completed | Detailed |
|
|
33
|
-
| ticket-005 | Add regex pattern compilation and caching | epic-02 | completed | Detailed |
|
|
34
|
-
| ticket-006 | Create StorageType enum module | epic-02 | completed | Detailed |
|
|
35
|
-
| ticket-007 | Update all factory functions to accept StorageType | epic-02 | completed | Detailed |
|
|
36
|
-
| ticket-008 | Migrate internal storage parameter usage to StorageType | epic-02 | completed | Detailed |
|
|
37
|
-
| ticket-009 | Add deprecation warnings for string-based storage | epic-02 | completed | Detailed |
|
|
38
|
-
| ticket-010 | Optimize FloatField fixed-point textual write | epic-03 | completed | Refined |
|
|
39
|
-
| ticket-011 | Add FloatField write benchmark tests | epic-03 | completed | Refined |
|
|
40
|
-
| ticket-012 | Implement list-backed RegisterData container | epic-04 | completed | Refined |
|
|
41
|
-
| ticket-013 | Implement list-backed BlockData container | epic-04 | completed | Refined |
|
|
42
|
-
| ticket-014 | Implement list-backed SectionData container | epic-04 | completed | Refined |
|
|
43
|
-
| ticket-015 | Migrate Register/Block/Section prev/next to computed properties | epic-04 | completed | Refined |
|
|
44
|
-
| ticket-016 | Add type-indexed lookup optimization | epic-04 | completed | Refined |
|
|
45
|
-
| ticket-017 | Design typed Field read/write protocol | epic-05 | completed | Refined |
|
|
46
|
-
| ticket-018 | Separate TextualRepository and BinaryRepository type signatures | epic-05 | completed | Refined |
|
|
47
|
-
| ticket-019 | Update Line class for typed dispatch | epic-05 | completed | Refined |
|
|
48
|
-
| ticket-020 | Update Register/Block/Section for typed IO paths | epic-05 | completed | Refined |
|
|
49
|
-
| ticket-021 | Implement TabularParser core engine | epic-06 | completed | Refined |
|
|
50
|
-
| ticket-022 | Create TabularSection convenience base class | epic-06 | completed | Refined |
|
|
51
|
-
| ticket-023 | Add delimited tabular parsing support | epic-06 | completed | Refined |
|
|
52
|
-
| ticket-024 | Design SchemaVersion descriptor and version resolution utility | epic-07 | completed | Refined |
|
|
53
|
-
| ticket-025 | Implement instance-level version binding for file classes | epic-07 | completed | Refined |
|
|
54
|
-
| ticket-026 | Add batch read_many() API to file classes | epic-07 | completed | Refined |
|
|
55
|
-
| ticket-027 | Add version validation and mismatch detection | epic-07 | completed | Refined |
|