dataframely 2.12.0__tar.gz → 2.13.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.
- {dataframely-2.12.0 → dataframely-2.13.1}/PKG-INFO +1 -1
- dataframely-2.13.1/dataframely/_deprecation.py +58 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/collection/collection.py +100 -6
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/filter_result.py +11 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/schema.py +57 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/guides/features/serialization.md +22 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/guides/migration/index.md +5 -6
- {dataframely-2.12.0 → dataframely-2.13.1}/pyproject.toml +6 -3
- dataframely-2.13.1/tests/collection/test_skip_member_validation.py +108 -0
- dataframely-2.13.1/tests/test_deprecation.py +149 -0
- dataframely-2.12.0/dataframely/_deprecation.py +0 -27
- dataframely-2.12.0/tests/test_deprecation.py +0 -24
- {dataframely-2.12.0 → dataframely-2.13.1}/.claude/CLAUDE.md +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/.copier-answers.yml +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/.envrc +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/.gitattributes +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/.github/CODEOWNERS +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/.github/copilot-instructions.md +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/.github/dependabot.yml +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/.github/instructions/tests.instructions.md +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/.github/release-drafter.yml +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/.github/workflows/build.yml +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/.github/workflows/chore.yml +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/.github/workflows/ci.yml +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/.github/workflows/copilot-setup-steps.yml +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/.github/workflows/nightly.yml +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/.github/workflows/release-drafter.yml +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/.github/workflows/scorecard.yml +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/.gitignore +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/.lefthook.yaml +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/.prettierignore +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/.prettierrc +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/.readthedocs.yml +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/AGENTS.md +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/Cargo.lock +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/Cargo.toml +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/LICENSE +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/README.md +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/SECURITY.md +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/__init__.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/_base_schema.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/_compat.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/_filter.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/_match_to_schema.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/_native.pyi +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/_plugin.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/_polars.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/_pydantic.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/_rule.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/_serialization.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/_storage/__init__.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/_storage/_base.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/_storage/_exc.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/_storage/_fsspec.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/_storage/constants.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/_storage/delta.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/_storage/parquet.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/_typing.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/collection/__init__.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/collection/_base.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/collection/filter_result.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/columns/__init__.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/columns/_base.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/columns/_mixins.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/columns/_registry.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/columns/_utils.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/columns/any.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/columns/array.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/columns/binary.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/columns/bool.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/columns/categorical.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/columns/datetime.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/columns/decimal.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/columns/enum.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/columns/float.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/columns/integer.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/columns/list.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/columns/object.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/columns/string.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/columns/struct.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/config.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/exc.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/experimental/__init__.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/experimental/infer_schema.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/functional.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/py.typed +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/random.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/testing/__init__.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/testing/const.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/testing/factory.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/testing/mask.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/testing/rules.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/dataframely/testing/storage.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docker-compose.yml +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/_static/custom.css +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/_static/favicon.ico +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/_templates/autosummary/class.rst +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/_templates/autosummary/method.rst +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/_templates/classes/column.rst +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/_templates/classes/error.rst +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/_templates/classes/filter_result.rst +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/api/collection/generation.rst +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/api/collection/index.rst +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/api/collection/io.rst +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/api/collection/metadata.rst +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/api/collection/operations.rst +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/api/collection/validation.rst +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/api/columns/index.rst +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/api/errors/index.rst +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/api/experimental/index.rst +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/api/filter_result/failure_info.rst +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/api/filter_result/index.rst +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/api/index.rst +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/api/misc/index.rst +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/api/schema/conversion.rst +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/api/schema/generation.rst +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/api/schema/index.rst +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/api/schema/io.rst +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/api/schema/metadata.rst +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/api/schema/validation.rst +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/conf.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/css/custom.css +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/guides/coding-agents.md +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/guides/development.md +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/guides/examples/index.md +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/guides/examples/real-world.ipynb +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/guides/faq.md +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/guides/features/column-metadata.md +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/guides/features/data-generation.md +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/guides/features/index.md +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/guides/features/lazy-validation.md +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/guides/features/primary-keys.md +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/guides/features/sql-generation.md +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/guides/index.md +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/guides/migration/v1-v2.md +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/guides/quickstart.md +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/docs/index.md +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/pixi.lock +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/pixi.toml +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/rust-toolchain.toml +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/skills/SKILL.md +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/src/lib.rs +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/src/polars_plugin/mod.rs +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/src/polars_plugin/rule_failure.rs +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/src/polars_plugin/utils.rs +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/src/polars_plugin/validation_error.rs +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/src/regex/errdefs.rs +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/src/regex/mod.rs +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/src/regex/repr.rs +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/benches/conftest.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/benches/test_collection.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/benches/test_failure.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/benches/test_schema.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/collection/test_base.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/collection/test_cast.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/collection/test_collection_future_annotations.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/collection/test_create_empty.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/collection/test_dataframe_members.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/collection/test_filter_one_to_n.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/collection/test_filter_validate.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/collection/test_ignore_in_filter.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/collection/test_implementation.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/collection/test_join.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/collection/test_matches.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/collection/test_optional_members.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/collection/test_pipe.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/collection/test_propagate_row_failures.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/collection/test_repr.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/collection/test_sample.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/collection/test_serialization.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/collection/test_storage.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/collection/test_validate_input.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/column_types/__init__.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/column_types/test_any.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/column_types/test_array.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/column_types/test_binary.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/column_types/test_datetime.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/column_types/test_decimal.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/column_types/test_enum.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/column_types/test_float.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/column_types/test_integer.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/column_types/test_list.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/column_types/test_object.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/column_types/test_string.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/column_types/test_struct.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/columns/__init__.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/columns/test_alias.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/columns/test_base.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/columns/test_check.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/columns/test_default_dtypes.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/columns/test_description.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/columns/test_matches.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/columns/test_metadata.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/columns/test_polars_schema.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/columns/test_pyarrow.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/columns/test_pydantic.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/columns/test_rules.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/columns/test_sample.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/columns/test_sqlalchemy_columns.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/columns/test_str.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/columns/test_utils.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/conftest.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/core_validation/__init__.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/core_validation/test_match_to_schema.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/core_validation/test_rule_evaluation.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/experimental/test_infer_schema.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/failure_info/test_storage.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/functional/test_concat.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/functional/test_relationships.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/schema/test_base.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/schema/test_cast.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/schema/test_create_empty.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/schema/test_create_empty_if_none.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/schema/test_filter.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/schema/test_inheritance.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/schema/test_matches.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/schema/test_pydantic_model.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/schema/test_read_write_parquet.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/schema/test_repr.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/schema/test_rule_implementation.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/schema/test_sample.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/schema/test_serialization.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/schema/test_storage.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/schema/test_validate.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/storage/test_delta.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/storage/test_fsspec.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/test_compat.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/test_config.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/test_factory.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/test_native_regex.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/test_pydantic.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/test_random.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/test_serialization.py +0 -0
- {dataframely-2.12.0 → dataframely-2.13.1}/tests/test_typing.py +0 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Copyright (c) QuantCo 2025-2026
|
|
2
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import sys
|
|
7
|
+
import warnings
|
|
8
|
+
from functools import wraps
|
|
9
|
+
from typing import TYPE_CHECKING, TypeVar
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from collections.abc import Callable
|
|
13
|
+
from typing import ParamSpec
|
|
14
|
+
|
|
15
|
+
P = ParamSpec("P")
|
|
16
|
+
T = TypeVar("T")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def issue_deprecation_warning(message: str, *, version: str = "") -> None:
|
|
20
|
+
"""Issue a deprecation warning pointing at the caller of the deprecated method.
|
|
21
|
+
|
|
22
|
+
This must be called directly from the body of the deprecated (public) method so
|
|
23
|
+
that the warning points at the user's code rather than at dataframely internals.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
message: The message associated with the warning.
|
|
27
|
+
version: The dataframely version in which the deprecation occurred (if not
|
|
28
|
+
already part of ``message``).
|
|
29
|
+
"""
|
|
30
|
+
if version:
|
|
31
|
+
message = f"{message.strip()}\n(Deprecated in dataframely {version})"
|
|
32
|
+
# `stacklevel=2` blames the caller of the deprecated method (one frame up from this
|
|
33
|
+
# function). All call sites invoke this directly from the deprecated method body.
|
|
34
|
+
warnings.warn(message, DeprecationWarning, stacklevel=2)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
if sys.version_info >= (3, 13):
|
|
38
|
+
from warnings import deprecated
|
|
39
|
+
else:
|
|
40
|
+
try:
|
|
41
|
+
from typing_extensions import deprecated
|
|
42
|
+
except ImportError: # pragma: no cover
|
|
43
|
+
|
|
44
|
+
def deprecated( # type: ignore[no-redef]
|
|
45
|
+
message: str,
|
|
46
|
+
) -> Callable[[Callable[P, T]], Callable[P, T]]:
|
|
47
|
+
"""Fallback for :func:`warnings.deprecated` without :pep:`702` support."""
|
|
48
|
+
|
|
49
|
+
def decorate(function: Callable[P, T]) -> Callable[P, T]:
|
|
50
|
+
@wraps(function)
|
|
51
|
+
def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
|
|
52
|
+
issue_deprecation_warning(message)
|
|
53
|
+
return function(*args, **kwargs)
|
|
54
|
+
|
|
55
|
+
wrapper.__deprecated__ = message # type: ignore[attr-defined]
|
|
56
|
+
return wrapper
|
|
57
|
+
|
|
58
|
+
return decorate
|
|
@@ -28,6 +28,7 @@ import polars as pl
|
|
|
28
28
|
import polars.exceptions as plexc
|
|
29
29
|
|
|
30
30
|
from dataframely._compat import deltalake
|
|
31
|
+
from dataframely._deprecation import deprecated, issue_deprecation_warning
|
|
31
32
|
from dataframely._filter import Filter
|
|
32
33
|
from dataframely._native import format_rule_failures
|
|
33
34
|
from dataframely._plugin import all_rules_required
|
|
@@ -63,6 +64,15 @@ else:
|
|
|
63
64
|
|
|
64
65
|
_FILTER_COLUMN_PREFIX = "__DATAFRAMELY_FILTER_COLUMN__"
|
|
65
66
|
|
|
67
|
+
#: Deprecation message emitted when reading a collection with implicit validation, i.e.
|
|
68
|
+
#: with any ``validation`` other than ``"skip"`` (see #367).
|
|
69
|
+
_IMPLICIT_VALIDATION_DEPRECATION = (
|
|
70
|
+
"Reading a collection with `validation != 'skip'` is deprecated. Starting with "
|
|
71
|
+
"dataframely v3, data is read without inspecting schema metadata and without "
|
|
72
|
+
"running validation. Pass `validation='skip'` to opt into the future behavior, or "
|
|
73
|
+
"call `validate` explicitly if you require validation."
|
|
74
|
+
)
|
|
75
|
+
|
|
66
76
|
P = ParamSpec("P")
|
|
67
77
|
T = TypeVar("T")
|
|
68
78
|
|
|
@@ -386,6 +396,7 @@ class Collection(BaseCollection, ABC):
|
|
|
386
396
|
*,
|
|
387
397
|
cast: bool = False,
|
|
388
398
|
eager: bool = True,
|
|
399
|
+
skip_member_validation: bool = False,
|
|
389
400
|
**kwargs: Any,
|
|
390
401
|
) -> Self:
|
|
391
402
|
"""Validate that a set of data frames satisfy the collection's invariants.
|
|
@@ -406,6 +417,11 @@ class Collection(BaseCollection, ABC):
|
|
|
406
417
|
:meth:`~polars.LazyFrame.collect` on the individual member or
|
|
407
418
|
:meth:`collect_all` on the collection. Note that, in the latter case,
|
|
408
419
|
information from error messages is limited.
|
|
420
|
+
skip_member_validation: Whether to skip validating individual members and only
|
|
421
|
+
apply the collection filters. **Use this option with caution** as it
|
|
422
|
+
requires the caller to ensure that the individual members have been
|
|
423
|
+
validated. This option is particularly useful in performance-critical
|
|
424
|
+
scenarios where the members are known to be valid.
|
|
409
425
|
kwargs: Keyword arguments passed directly to :meth:`polars.collect_all` and
|
|
410
426
|
:meth:`polars.LazyFrame.collect` when `eager=True`.
|
|
411
427
|
|
|
@@ -429,7 +445,13 @@ class Collection(BaseCollection, ABC):
|
|
|
429
445
|
if eager:
|
|
430
446
|
# If we perform the validation eagerly, we call filter and check the failure
|
|
431
447
|
# information to properly construct a useful error message.
|
|
432
|
-
filtered, failures = cls.filter(
|
|
448
|
+
filtered, failures = cls.filter(
|
|
449
|
+
data,
|
|
450
|
+
cast=cast,
|
|
451
|
+
eager=True,
|
|
452
|
+
skip_member_validation=skip_member_validation,
|
|
453
|
+
**kwargs,
|
|
454
|
+
)
|
|
433
455
|
if any(len(failure) > 0 for failure in failures.values()):
|
|
434
456
|
errors: dict[str, str] = {}
|
|
435
457
|
for member, failure in failures.items():
|
|
@@ -460,7 +482,17 @@ class Collection(BaseCollection, ABC):
|
|
|
460
482
|
# efficiently as we cannot easily propagate error messages from different
|
|
461
483
|
# members anyways.
|
|
462
484
|
members: dict[str, pl.LazyFrame] = {
|
|
463
|
-
name:
|
|
485
|
+
name: (
|
|
486
|
+
(
|
|
487
|
+
member.schema.cast(data[name].lazy())
|
|
488
|
+
if cast
|
|
489
|
+
else data[name].lazy()
|
|
490
|
+
)
|
|
491
|
+
if skip_member_validation
|
|
492
|
+
else member.schema.validate(
|
|
493
|
+
data[name].lazy(), cast=cast, eager=False
|
|
494
|
+
)
|
|
495
|
+
)
|
|
464
496
|
for name, member in cls.members().items()
|
|
465
497
|
if name in data
|
|
466
498
|
}
|
|
@@ -556,6 +588,7 @@ class Collection(BaseCollection, ABC):
|
|
|
556
588
|
*,
|
|
557
589
|
cast: bool = False,
|
|
558
590
|
eager: bool = True,
|
|
591
|
+
skip_member_validation: bool = False,
|
|
559
592
|
**kwargs: Any,
|
|
560
593
|
) -> CollectionFilterResult[Self]:
|
|
561
594
|
"""Filter the members data frame by their schemas and the collection's filters.
|
|
@@ -572,6 +605,11 @@ class Collection(BaseCollection, ABC):
|
|
|
572
605
|
eager: Whether the filter operation should be performed eagerly.
|
|
573
606
|
Note that until https://github.com/pola-rs/polars/pull/24129 is
|
|
574
607
|
released, eagerly filtering can provide significant speedups.
|
|
608
|
+
skip_member_validation: Whether to skip filtering individual members and only
|
|
609
|
+
apply the collection filters. **Use this option with caution** as it
|
|
610
|
+
requires the caller to ensure that the individual members have been
|
|
611
|
+
validated. This option is particularly useful in performance-critical
|
|
612
|
+
scenarios where the members are known to already be valid.
|
|
575
613
|
kwargs: Keyword arguments passed directly to :meth:`polars.collect_all` and
|
|
576
614
|
:meth:`polars.LazyFrame.collect` when `eager=True`.
|
|
577
615
|
|
|
@@ -615,10 +653,20 @@ class Collection(BaseCollection, ABC):
|
|
|
615
653
|
if member.is_optional and member_name not in data:
|
|
616
654
|
continue
|
|
617
655
|
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
656
|
+
if skip_member_validation:
|
|
657
|
+
results[member_name] = (
|
|
658
|
+
member.schema.cast(data[member_name].lazy())
|
|
659
|
+
if cast
|
|
660
|
+
else data[member_name].lazy()
|
|
661
|
+
)
|
|
662
|
+
failures[member_name] = FailureInfo._create_empty(
|
|
663
|
+
member.schema, with_casting_rules=cast
|
|
664
|
+
)
|
|
665
|
+
else:
|
|
666
|
+
member_result, failures[member_name] = member.schema.filter(
|
|
667
|
+
data[member_name].lazy(), cast=cast, eager=eager, **kwargs
|
|
668
|
+
)
|
|
669
|
+
results[member_name] = member_result.lazy()
|
|
622
670
|
|
|
623
671
|
# Once we've done that, we can apply the filters on this collection. To this end,
|
|
624
672
|
# we iterate over all filters and store the filter results.
|
|
@@ -1023,7 +1071,16 @@ class Collection(BaseCollection, ABC):
|
|
|
1023
1071
|
Attention:
|
|
1024
1072
|
Be aware that this method suffers from the same limitations as
|
|
1025
1073
|
:meth:`serialize`.
|
|
1074
|
+
|
|
1075
|
+
.. deprecated:: 3.0.0
|
|
1076
|
+
Reading with `validation != "skip"` is deprecated. Starting with
|
|
1077
|
+
dataframely v3, this method reads the data without inspecting any schema
|
|
1078
|
+
metadata and without running validation. Pass `validation="skip"` to opt
|
|
1079
|
+
into this behavior, or call :meth:`validate` explicitly if you require
|
|
1080
|
+
validation.
|
|
1026
1081
|
"""
|
|
1082
|
+
if validation != "skip":
|
|
1083
|
+
issue_deprecation_warning(_IMPLICIT_VALIDATION_DEPRECATION)
|
|
1027
1084
|
return cls._read(
|
|
1028
1085
|
backend=ParquetStorageBackend(),
|
|
1029
1086
|
validation=validation,
|
|
@@ -1080,7 +1137,16 @@ class Collection(BaseCollection, ABC):
|
|
|
1080
1137
|
Attention:
|
|
1081
1138
|
Be aware that this method suffers from the same limitations as
|
|
1082
1139
|
:meth:`serialize`.
|
|
1140
|
+
|
|
1141
|
+
.. deprecated:: 3.0.0
|
|
1142
|
+
Reading with `validation != "skip"` is deprecated. Starting with
|
|
1143
|
+
dataframely v3, this method reads the data without inspecting any schema
|
|
1144
|
+
metadata and without running validation. Pass `validation="skip"` to opt
|
|
1145
|
+
into this behavior, or call :meth:`validate` explicitly if you require
|
|
1146
|
+
validation.
|
|
1083
1147
|
"""
|
|
1148
|
+
if validation != "skip":
|
|
1149
|
+
issue_deprecation_warning(_IMPLICIT_VALIDATION_DEPRECATION)
|
|
1084
1150
|
return cls._read(
|
|
1085
1151
|
backend=ParquetStorageBackend(),
|
|
1086
1152
|
validation=validation,
|
|
@@ -1089,6 +1155,10 @@ class Collection(BaseCollection, ABC):
|
|
|
1089
1155
|
**kwargs,
|
|
1090
1156
|
)
|
|
1091
1157
|
|
|
1158
|
+
@deprecated(
|
|
1159
|
+
"`Collection.write_delta` is deprecated and will be removed in dataframely v3. "
|
|
1160
|
+
"Write the individual members with `polars.DataFrame.write_delta` instead."
|
|
1161
|
+
)
|
|
1092
1162
|
def write_delta(
|
|
1093
1163
|
self, target: str | Path | deltalake.DeltaTable, **kwargs: Any
|
|
1094
1164
|
) -> None:
|
|
@@ -1115,6 +1185,10 @@ class Collection(BaseCollection, ABC):
|
|
|
1115
1185
|
break your schema.
|
|
1116
1186
|
|
|
1117
1187
|
This method suffers from the same limitations as :meth:`~dataframely.Schema.serialize`.
|
|
1188
|
+
|
|
1189
|
+
.. deprecated:: 3.0.0
|
|
1190
|
+
This method is deprecated and will be removed in dataframely v3. Write the
|
|
1191
|
+
individual members with :meth:`polars.DataFrame.write_delta` instead.
|
|
1118
1192
|
"""
|
|
1119
1193
|
self._write(
|
|
1120
1194
|
backend=DeltaStorageBackend(),
|
|
@@ -1123,6 +1197,11 @@ class Collection(BaseCollection, ABC):
|
|
|
1123
1197
|
)
|
|
1124
1198
|
|
|
1125
1199
|
@classmethod
|
|
1200
|
+
@deprecated(
|
|
1201
|
+
"`Collection.scan_delta` is deprecated and will be removed in dataframely v3. "
|
|
1202
|
+
"Read the individual members with `polars.scan_delta` and call `validate` "
|
|
1203
|
+
"explicitly instead."
|
|
1204
|
+
)
|
|
1126
1205
|
def scan_delta(
|
|
1127
1206
|
cls,
|
|
1128
1207
|
source: str | Path | deltalake.DeltaTable,
|
|
@@ -1176,6 +1255,11 @@ class Collection(BaseCollection, ABC):
|
|
|
1176
1255
|
break your schema.
|
|
1177
1256
|
|
|
1178
1257
|
Be aware that this method suffers from the same limitations as :meth:`serialize`.
|
|
1258
|
+
|
|
1259
|
+
.. deprecated:: 3.0.0
|
|
1260
|
+
This method is deprecated and will be removed in dataframely v3. Read the
|
|
1261
|
+
individual members with :meth:`polars.scan_delta` and call :meth:`validate`
|
|
1262
|
+
explicitly instead.
|
|
1179
1263
|
"""
|
|
1180
1264
|
return cls._read(
|
|
1181
1265
|
backend=DeltaStorageBackend(),
|
|
@@ -1185,6 +1269,11 @@ class Collection(BaseCollection, ABC):
|
|
|
1185
1269
|
)
|
|
1186
1270
|
|
|
1187
1271
|
@classmethod
|
|
1272
|
+
@deprecated(
|
|
1273
|
+
"`Collection.read_delta` is deprecated and will be removed in dataframely v3. "
|
|
1274
|
+
"Read the individual members with `polars.read_delta` and call `validate` "
|
|
1275
|
+
"explicitly instead."
|
|
1276
|
+
)
|
|
1188
1277
|
def read_delta(
|
|
1189
1278
|
cls,
|
|
1190
1279
|
source: str | Path | deltalake.DeltaTable,
|
|
@@ -1237,6 +1326,11 @@ class Collection(BaseCollection, ABC):
|
|
|
1237
1326
|
break your schema.
|
|
1238
1327
|
|
|
1239
1328
|
Be aware that this method suffers from the same limitations as :meth:`serialize`.
|
|
1329
|
+
|
|
1330
|
+
.. deprecated:: 3.0.0
|
|
1331
|
+
This method is deprecated and will be removed in dataframely v3. Read the
|
|
1332
|
+
individual members with :meth:`polars.read_delta` and call :meth:`validate`
|
|
1333
|
+
explicitly instead.
|
|
1240
1334
|
"""
|
|
1241
1335
|
return cls._read(
|
|
1242
1336
|
backend=DeltaStorageBackend(),
|
|
@@ -106,6 +106,17 @@ class FailureInfo(Generic[S]):
|
|
|
106
106
|
self._rule_columns = rule_columns
|
|
107
107
|
self.schema = schema
|
|
108
108
|
|
|
109
|
+
@classmethod
|
|
110
|
+
def _create_empty(cls, schema: type[S], with_casting_rules: bool) -> FailureInfo[S]:
|
|
111
|
+
rules = schema._validation_rules(with_cast=with_casting_rules)
|
|
112
|
+
lf = pl.LazyFrame(
|
|
113
|
+
schema={
|
|
114
|
+
**schema.to_polars_schema(), # type: ignore
|
|
115
|
+
**{rule: pl.Boolean for rule in rules},
|
|
116
|
+
}
|
|
117
|
+
)
|
|
118
|
+
return cls(lf=lf, rule_columns=list(rules.keys()), schema=schema)
|
|
119
|
+
|
|
109
120
|
@cached_property
|
|
110
121
|
def _df(self) -> pl.DataFrame:
|
|
111
122
|
return self._lf.collect()
|
|
@@ -18,6 +18,7 @@ from polars._typing import FileSource
|
|
|
18
18
|
|
|
19
19
|
from ._base_schema import ORIGINAL_COLUMN_PREFIX, BaseSchema
|
|
20
20
|
from ._compat import PartitionSchemeOrSinkDirectory, deltalake, pa, pydantic, sa
|
|
21
|
+
from ._deprecation import deprecated
|
|
21
22
|
from ._match_to_schema import match_to_schema
|
|
22
23
|
from ._native import format_rule_failures
|
|
23
24
|
from ._plugin import all_rules, all_rules_horizontal, all_rules_required
|
|
@@ -897,6 +898,10 @@ class Schema(BaseSchema, ABC):
|
|
|
897
898
|
# ------------------------------------ PARQUET ----------------------------------- #
|
|
898
899
|
|
|
899
900
|
@classmethod
|
|
901
|
+
@deprecated(
|
|
902
|
+
"`Schema.write_parquet` is deprecated and will be removed in dataframely v3. "
|
|
903
|
+
"Use `polars.DataFrame.write_parquet` directly instead."
|
|
904
|
+
)
|
|
900
905
|
def write_parquet(
|
|
901
906
|
cls, df: DataFrame[Self], /, file: str | Path | IO[bytes], **kwargs: Any
|
|
902
907
|
) -> None:
|
|
@@ -919,10 +924,18 @@ class Schema(BaseSchema, ABC):
|
|
|
919
924
|
Attention:
|
|
920
925
|
Be aware that this method suffers from the same limitations as
|
|
921
926
|
:meth:`serialize`.
|
|
927
|
+
|
|
928
|
+
.. deprecated:: 3.0.0
|
|
929
|
+
This method is deprecated and will be removed in dataframely v3. Use
|
|
930
|
+
:meth:`polars.DataFrame.write_parquet` directly instead.
|
|
922
931
|
"""
|
|
923
932
|
cls._write(df=df, backend=ParquetStorageBackend(), file=file, **kwargs)
|
|
924
933
|
|
|
925
934
|
@classmethod
|
|
935
|
+
@deprecated(
|
|
936
|
+
"`Schema.sink_parquet` is deprecated and will be removed in dataframely v3. "
|
|
937
|
+
"Use `polars.LazyFrame.sink_parquet` directly instead."
|
|
938
|
+
)
|
|
926
939
|
def sink_parquet(
|
|
927
940
|
cls,
|
|
928
941
|
lf: LazyFrame[Self],
|
|
@@ -947,10 +960,18 @@ class Schema(BaseSchema, ABC):
|
|
|
947
960
|
Attention:
|
|
948
961
|
Be aware that this method suffers from the same limitations as
|
|
949
962
|
:meth:`serialize`.
|
|
963
|
+
|
|
964
|
+
.. deprecated:: 3.0.0
|
|
965
|
+
This method is deprecated and will be removed in dataframely v3. Use
|
|
966
|
+
:meth:`polars.LazyFrame.sink_parquet` directly instead.
|
|
950
967
|
"""
|
|
951
968
|
cls._sink(lf=lf, backend=ParquetStorageBackend(), file=file, **kwargs)
|
|
952
969
|
|
|
953
970
|
@classmethod
|
|
971
|
+
@deprecated(
|
|
972
|
+
"`Schema.read_parquet` is deprecated and will be removed in dataframely v3. "
|
|
973
|
+
"Use `polars.read_parquet` and call `validate` explicitly instead."
|
|
974
|
+
)
|
|
954
975
|
def read_parquet(
|
|
955
976
|
cls,
|
|
956
977
|
source: FileSource,
|
|
@@ -997,6 +1018,10 @@ class Schema(BaseSchema, ABC):
|
|
|
997
1018
|
Attention:
|
|
998
1019
|
Be aware that this method suffers from the same limitations as
|
|
999
1020
|
:meth:`serialize`.
|
|
1021
|
+
|
|
1022
|
+
.. deprecated:: 3.0.0
|
|
1023
|
+
This method is deprecated and will be removed in dataframely v3. Use
|
|
1024
|
+
:meth:`polars.read_parquet` and call :meth:`validate` explicitly instead.
|
|
1000
1025
|
"""
|
|
1001
1026
|
return cls._read(
|
|
1002
1027
|
ParquetStorageBackend(),
|
|
@@ -1007,6 +1032,10 @@ class Schema(BaseSchema, ABC):
|
|
|
1007
1032
|
)
|
|
1008
1033
|
|
|
1009
1034
|
@classmethod
|
|
1035
|
+
@deprecated(
|
|
1036
|
+
"`Schema.scan_parquet` is deprecated and will be removed in dataframely v3. "
|
|
1037
|
+
"Use `polars.scan_parquet` and call `validate` explicitly instead."
|
|
1038
|
+
)
|
|
1010
1039
|
def scan_parquet(
|
|
1011
1040
|
cls,
|
|
1012
1041
|
source: FileSource,
|
|
@@ -1053,6 +1082,10 @@ class Schema(BaseSchema, ABC):
|
|
|
1053
1082
|
Attention:
|
|
1054
1083
|
Be aware that this method suffers from the same limitations as
|
|
1055
1084
|
:meth:`serialize`.
|
|
1085
|
+
|
|
1086
|
+
.. deprecated:: 3.0.0
|
|
1087
|
+
This method is deprecated and will be removed in dataframely v3. Use
|
|
1088
|
+
:meth:`polars.scan_parquet` and call :meth:`validate` explicitly instead.
|
|
1056
1089
|
"""
|
|
1057
1090
|
return cls._read(
|
|
1058
1091
|
ParquetStorageBackend(),
|
|
@@ -1099,6 +1132,10 @@ class Schema(BaseSchema, ABC):
|
|
|
1099
1132
|
|
|
1100
1133
|
# --------------------------------- Delta -----------------------------------------#
|
|
1101
1134
|
@classmethod
|
|
1135
|
+
@deprecated(
|
|
1136
|
+
"`Schema.write_delta` is deprecated and will be removed in dataframely v3. "
|
|
1137
|
+
"Use `polars.DataFrame.write_delta` directly instead."
|
|
1138
|
+
)
|
|
1102
1139
|
def write_delta(
|
|
1103
1140
|
cls,
|
|
1104
1141
|
df: DataFrame[Self],
|
|
@@ -1127,6 +1164,10 @@ class Schema(BaseSchema, ABC):
|
|
|
1127
1164
|
in violation of group constraints that dataframely cannot catch
|
|
1128
1165
|
without re-validating. Only use appends if you are certain that they do not
|
|
1129
1166
|
break your schema.
|
|
1167
|
+
|
|
1168
|
+
.. deprecated:: 3.0.0
|
|
1169
|
+
This method is deprecated and will be removed in dataframely v3. Use
|
|
1170
|
+
:meth:`polars.DataFrame.write_delta` directly instead.
|
|
1130
1171
|
"""
|
|
1131
1172
|
DeltaStorageBackend().write_frame(
|
|
1132
1173
|
df=df,
|
|
@@ -1135,6 +1176,10 @@ class Schema(BaseSchema, ABC):
|
|
|
1135
1176
|
)
|
|
1136
1177
|
|
|
1137
1178
|
@classmethod
|
|
1179
|
+
@deprecated(
|
|
1180
|
+
"`Schema.scan_delta` is deprecated and will be removed in dataframely v3. "
|
|
1181
|
+
"Use `polars.scan_delta` and call `validate` explicitly instead."
|
|
1182
|
+
)
|
|
1138
1183
|
def scan_delta(
|
|
1139
1184
|
cls,
|
|
1140
1185
|
source: str | Path | deltalake.DeltaTable,
|
|
@@ -1182,6 +1227,10 @@ class Schema(BaseSchema, ABC):
|
|
|
1182
1227
|
that are not through dataframely will result in losing the metadata.
|
|
1183
1228
|
|
|
1184
1229
|
This method suffers from the same limitations as :meth:`serialize`.
|
|
1230
|
+
|
|
1231
|
+
.. deprecated:: 3.0.0
|
|
1232
|
+
This method is deprecated and will be removed in dataframely v3. Use
|
|
1233
|
+
:meth:`polars.scan_delta` and call :meth:`validate` explicitly instead.
|
|
1185
1234
|
"""
|
|
1186
1235
|
return cls._read(
|
|
1187
1236
|
DeltaStorageBackend(),
|
|
@@ -1192,6 +1241,10 @@ class Schema(BaseSchema, ABC):
|
|
|
1192
1241
|
)
|
|
1193
1242
|
|
|
1194
1243
|
@classmethod
|
|
1244
|
+
@deprecated(
|
|
1245
|
+
"`Schema.read_delta` is deprecated and will be removed in dataframely v3. "
|
|
1246
|
+
"Use `polars.read_delta` and call `validate` explicitly instead."
|
|
1247
|
+
)
|
|
1195
1248
|
def read_delta(
|
|
1196
1249
|
cls,
|
|
1197
1250
|
source: str | Path | deltalake.DeltaTable,
|
|
@@ -1244,6 +1297,10 @@ class Schema(BaseSchema, ABC):
|
|
|
1244
1297
|
break your schema.
|
|
1245
1298
|
|
|
1246
1299
|
This method suffers from the same limitations as :meth:`serialize`.
|
|
1300
|
+
|
|
1301
|
+
.. deprecated:: 3.0.0
|
|
1302
|
+
This method is deprecated and will be removed in dataframely v3. Use
|
|
1303
|
+
:meth:`polars.read_delta` and call :meth:`validate` explicitly instead.
|
|
1247
1304
|
"""
|
|
1248
1305
|
return cls._read(
|
|
1249
1306
|
DeltaStorageBackend(),
|
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Serialization
|
|
2
2
|
|
|
3
|
+
```{warning}
|
|
4
|
+
Most of the I/O functionality described on this page is **deprecated** and will be
|
|
5
|
+
removed in dataframely v3 (see [#367](https://github.com/Quantco/dataframely/issues/367)).
|
|
6
|
+
Calling any of these methods now emits a {class}`DeprecationWarning`. Specifically:
|
|
7
|
+
|
|
8
|
+
- All I/O methods on {class}`~dataframely.Schema`
|
|
9
|
+
({meth}`~dataframely.Schema.write_parquet`, {meth}`~dataframely.Schema.sink_parquet`,
|
|
10
|
+
{meth}`~dataframely.Schema.read_parquet`, {meth}`~dataframely.Schema.scan_parquet`,
|
|
11
|
+
{meth}`~dataframely.Schema.write_delta`, {meth}`~dataframely.Schema.read_delta`,
|
|
12
|
+
{meth}`~dataframely.Schema.scan_delta`) are deprecated. Use the corresponding
|
|
13
|
+
`polars` functions directly and call {meth}`~dataframely.Schema.validate` explicitly
|
|
14
|
+
where validation is required.
|
|
15
|
+
- The `deltalake` I/O methods on {class}`~dataframely.Collection`
|
|
16
|
+
({meth}`~dataframely.Collection.write_delta`, {meth}`~dataframely.Collection.read_delta`,
|
|
17
|
+
{meth}`~dataframely.Collection.scan_delta`) are deprecated.
|
|
18
|
+
- {meth}`~dataframely.Collection.read_parquet` and
|
|
19
|
+
{meth}`~dataframely.Collection.scan_parquet` continue to exist, but reading with
|
|
20
|
+
`validation != "skip"` is deprecated: metadata will no longer be inspected and
|
|
21
|
+
validation will no longer run implicitly. Pass `validation="skip"` to opt into the
|
|
22
|
+
future behavior, or call {meth}`~dataframely.Collection.validate` explicitly.
|
|
23
|
+
```
|
|
24
|
+
|
|
3
25
|
`dataframely` provides support for easily storing and reading validated data.
|
|
4
26
|
`polars` already provides native support for serializing data frames into different storage
|
|
5
27
|
backends. For the storage of the data itself, `dataframely` usually dispatches to polars-native
|
|
@@ -26,17 +26,16 @@ As always, automated testing is useful here, but we also recommend checking the
|
|
|
26
26
|
[published on GitHub](https://github.com/Quantco/dataframely/releases).
|
|
27
27
|
|
|
28
28
|
In order to give users a heads-up before breaking changes are released, we introduce
|
|
29
|
-
[
|
|
29
|
+
[DeprecationWarnings](https://docs.python.org/3/library/exceptions.html#DeprecationWarning).
|
|
30
30
|
Warnings are the most direct and effective tool at our disposal for reaching users directly.
|
|
31
31
|
We therefore generally recommend that users do not silence such warnings explicitly, but instead migrate their code
|
|
32
32
|
proactively, whenever possible.
|
|
33
33
|
However, we also understand that the need for migration may catch users at an inconvenient time, and a temporary band
|
|
34
34
|
aid solution might be required.
|
|
35
|
-
Users can disable `
|
|
36
|
-
[python builtins](https://docs.python.org/3/library/warnings.html#warnings.filterwarnings)
|
|
37
|
-
builtins from tools
|
|
38
|
-
like [pytest](https://docs.pytest.org/en/stable/how-to/capture-warnings.html#controlling-warnings)
|
|
39
|
-
or by setting the `DATAFRAMELY_NO_FUTURE_WARNINGS` environment variable to `true` or `1`.
|
|
35
|
+
Users can disable `DeprecationWarnings` either through
|
|
36
|
+
[python builtins](https://docs.python.org/3/library/warnings.html#warnings.filterwarnings)
|
|
37
|
+
or through builtins from tools
|
|
38
|
+
like [pytest](https://docs.pytest.org/en/stable/how-to/capture-warnings.html#controlling-warnings).
|
|
40
39
|
|
|
41
40
|
## Experimental features
|
|
42
41
|
|
|
@@ -27,7 +27,7 @@ description = "A declarative, polars-native data frame validation library"
|
|
|
27
27
|
name = "dataframely"
|
|
28
28
|
readme = "README.md"
|
|
29
29
|
requires-python = ">=3.10"
|
|
30
|
-
version = "2.
|
|
30
|
+
version = "2.13.1"
|
|
31
31
|
|
|
32
32
|
[project.optional-dependencies]
|
|
33
33
|
deltalake = ["deltalake"]
|
|
@@ -107,10 +107,13 @@ addopts = "--import-mode=importlib --benchmark-skip -m 'not s3'"
|
|
|
107
107
|
filterwarnings = [
|
|
108
108
|
# By default, all warnings should yield errors
|
|
109
109
|
"error",
|
|
110
|
-
# Almost all tests are oblivious to the value of `nullable`. Let's ignore the warning as long as it exists.
|
|
111
|
-
"ignore:The 'nullable' argument was not explicitly set:FutureWarning",
|
|
112
110
|
# boto3 still uses .utcnow()
|
|
113
111
|
"ignore::DeprecationWarning:botocore.*:",
|
|
112
|
+
# I/O deprecation warnings (see #367) are asserted in dedicated tests via
|
|
113
|
+
# `pytest.warns`; silence them elsewhere so existing tests keep exercising the
|
|
114
|
+
# still-present functionality.
|
|
115
|
+
"ignore:.*Reading a collection with .validation.*:DeprecationWarning",
|
|
116
|
+
"ignore:.*deprecated and will be removed in dataframely v3.*:DeprecationWarning",
|
|
114
117
|
]
|
|
115
118
|
markers = [
|
|
116
119
|
"s3: tests that run against and S3 backend",
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Copyright (c) QuantCo 2025-2026
|
|
2
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
3
|
+
|
|
4
|
+
import polars as pl
|
|
5
|
+
import polars.exceptions as plexc
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
import dataframely as dy
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class FirstSchema(dy.Schema):
|
|
12
|
+
a = dy.Float64(min=5)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class SecondSchema(dy.Schema):
|
|
16
|
+
a = dy.String()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Collection(dy.Collection):
|
|
20
|
+
first: dy.LazyFrame[FirstSchema]
|
|
21
|
+
second: dy.LazyFrame[SecondSchema]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@pytest.mark.parametrize("df_type", [pl.DataFrame, pl.LazyFrame])
|
|
25
|
+
def test_validate_skip_member_validation_eager(
|
|
26
|
+
df_type: type[pl.DataFrame] | type[pl.LazyFrame],
|
|
27
|
+
) -> None:
|
|
28
|
+
first = df_type({"a": [3, 4, 5]}) # NOTE: first two rows are violations
|
|
29
|
+
second = df_type({"a": ["1", "2", "3"]})
|
|
30
|
+
|
|
31
|
+
with pytest.raises(dy.exc.ValidationError):
|
|
32
|
+
Collection.validate({"first": first, "second": second}, cast=True) # type: ignore
|
|
33
|
+
|
|
34
|
+
Collection.validate(
|
|
35
|
+
{"first": first, "second": second}, # type: ignore
|
|
36
|
+
cast=True,
|
|
37
|
+
skip_member_validation=True,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@pytest.mark.parametrize("df_type", [pl.DataFrame, pl.LazyFrame])
|
|
42
|
+
def test_validate_skip_member_validation_lazy(
|
|
43
|
+
df_type: type[pl.DataFrame] | type[pl.LazyFrame],
|
|
44
|
+
) -> None:
|
|
45
|
+
first = df_type({"a": [3, 4, 5]}) # NOTE: first two rows are violations
|
|
46
|
+
second = df_type({"a": ["1", "2", "3"]})
|
|
47
|
+
|
|
48
|
+
with pytest.raises(plexc.ComputeError):
|
|
49
|
+
Collection.validate(
|
|
50
|
+
{"first": first, "second": second}, # type: ignore
|
|
51
|
+
cast=True,
|
|
52
|
+
eager=False,
|
|
53
|
+
).collect_all()
|
|
54
|
+
|
|
55
|
+
Collection.validate(
|
|
56
|
+
{"first": first, "second": second}, # type: ignore
|
|
57
|
+
cast=True,
|
|
58
|
+
skip_member_validation=True,
|
|
59
|
+
eager=False,
|
|
60
|
+
).collect_all()
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@pytest.mark.parametrize("df_type", [pl.DataFrame, pl.LazyFrame])
|
|
64
|
+
def test_filter_skip_member_validation_eager(
|
|
65
|
+
df_type: type[pl.DataFrame] | type[pl.LazyFrame],
|
|
66
|
+
) -> None:
|
|
67
|
+
first = df_type({"a": [3, 4, 5]}) # NOTE: first two rows are violations
|
|
68
|
+
second = df_type({"a": ["1", "2", "3"]})
|
|
69
|
+
|
|
70
|
+
_, failure_info = Collection.filter(
|
|
71
|
+
{"first": first, "second": second}, # type: ignore
|
|
72
|
+
cast=True,
|
|
73
|
+
)
|
|
74
|
+
assert failure_info["first"].counts() == {"a|min": 2}
|
|
75
|
+
assert failure_info["second"].counts() == {}
|
|
76
|
+
|
|
77
|
+
_, failure_info = Collection.filter(
|
|
78
|
+
{"first": first, "second": second}, # type: ignore
|
|
79
|
+
cast=True,
|
|
80
|
+
skip_member_validation=True,
|
|
81
|
+
)
|
|
82
|
+
assert failure_info["first"].counts() == {}
|
|
83
|
+
assert failure_info["second"].counts() == {}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@pytest.mark.parametrize("df_type", [pl.DataFrame, pl.LazyFrame])
|
|
87
|
+
def test_filter_skip_member_validation_lazy(
|
|
88
|
+
df_type: type[pl.DataFrame] | type[pl.LazyFrame],
|
|
89
|
+
) -> None:
|
|
90
|
+
first = df_type({"a": [3, 4, 5]}) # NOTE: first two rows are violations
|
|
91
|
+
second = df_type({"a": ["1", "2", "3"]})
|
|
92
|
+
|
|
93
|
+
_, failure_info = Collection.filter(
|
|
94
|
+
{"first": first, "second": second}, # type: ignore
|
|
95
|
+
cast=True,
|
|
96
|
+
eager=False,
|
|
97
|
+
)
|
|
98
|
+
assert failure_info["first"].counts() == {"a|min": 2}
|
|
99
|
+
assert failure_info["second"].counts() == {}
|
|
100
|
+
|
|
101
|
+
_, failure_info = Collection.filter(
|
|
102
|
+
{"first": first, "second": second}, # type: ignore
|
|
103
|
+
cast=True,
|
|
104
|
+
skip_member_validation=True,
|
|
105
|
+
eager=False,
|
|
106
|
+
)
|
|
107
|
+
assert failure_info["first"].counts() == {}
|
|
108
|
+
assert failure_info["second"].counts() == {}
|