dataframely 2.2.0__tar.gz → 2.3.0__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.2.0 → dataframely-2.3.0}/PKG-INFO +1 -1
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/collection/_base.py +17 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/collection/collection.py +25 -5
- {dataframely-2.2.0 → dataframely-2.3.0}/pyproject.toml +1 -1
- dataframely-2.3.0/tests/collection/test_propagate_row_failures.py +126 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/.copier-answers.yml +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/.envrc +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/.gitattributes +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/.github/CODEOWNERS +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/.github/copilot-instructions.md +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/.github/dependabot.yml +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/.github/instructions/tests.instructions.md +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/.github/release-drafter.yml +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/.github/workflows/build.yml +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/.github/workflows/chore.yml +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/.github/workflows/ci.yml +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/.github/workflows/copilot-setup-steps.yml +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/.github/workflows/nightly.yml +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/.github/workflows/scorecard.yml +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/.gitignore +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/.pre-commit-config.yaml +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/.prettierignore +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/.prettierrc +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/.readthedocs.yml +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/Cargo.lock +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/Cargo.toml +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/LICENSE +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/README.md +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/SECURITY.md +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/__init__.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/_base_schema.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/_compat.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/_deprecation.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/_filter.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/_match_to_schema.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/_native.pyi +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/_plugin.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/_polars.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/_pydantic.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/_rule.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/_serialization.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/_storage/__init__.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/_storage/_base.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/_storage/_exc.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/_storage/constants.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/_storage/delta.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/_storage/parquet.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/_typing.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/collection/__init__.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/collection/filter_result.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/columns/__init__.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/columns/_base.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/columns/_mixins.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/columns/_registry.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/columns/_utils.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/columns/any.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/columns/array.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/columns/binary.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/columns/bool.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/columns/categorical.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/columns/datetime.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/columns/decimal.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/columns/enum.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/columns/float.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/columns/integer.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/columns/list.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/columns/object.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/columns/string.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/columns/struct.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/config.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/exc.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/filter_result.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/functional.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/py.typed +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/random.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/schema.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/testing/__init__.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/testing/const.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/testing/factory.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/testing/mask.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/testing/rules.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/dataframely/testing/storage.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docker-compose.yml +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/_static/custom.css +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/_static/favicon.ico +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/_templates/autosummary/class.rst +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/_templates/autosummary/method.rst +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/_templates/classes/column.rst +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/_templates/classes/error.rst +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/_templates/classes/filter_result.rst +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/api/collection/generation.rst +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/api/collection/index.rst +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/api/collection/io.rst +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/api/collection/metadata.rst +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/api/collection/operations.rst +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/api/collection/validation.rst +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/api/columns/index.rst +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/api/errors/index.rst +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/api/filter_result/failure_info.rst +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/api/filter_result/index.rst +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/api/index.rst +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/api/misc/index.rst +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/api/schema/conversion.rst +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/api/schema/generation.rst +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/api/schema/index.rst +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/api/schema/io.rst +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/api/schema/metadata.rst +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/api/schema/validation.rst +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/conf.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/css/custom.css +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/guides/development.md +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/guides/examples/index.md +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/guides/examples/real-world.ipynb +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/guides/faq.md +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/guides/features/column-metadata.md +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/guides/features/data-generation.md +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/guides/features/index.md +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/guides/features/lazy-validation.md +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/guides/features/primary-keys.md +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/guides/features/serialization.md +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/guides/features/sql-generation.md +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/guides/index.md +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/guides/migration/index.md +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/guides/migration/v1-v2.md +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/guides/quickstart.md +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/docs/index.md +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/pixi.lock +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/pixi.toml +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/rust-toolchain.toml +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/src/lib.rs +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/src/polars_plugin/mod.rs +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/src/polars_plugin/rule_failure.rs +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/src/polars_plugin/utils.rs +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/src/polars_plugin/validation_error.rs +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/src/regex/errdefs.rs +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/src/regex/mod.rs +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/src/regex/repr.rs +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/benches/conftest.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/benches/test_collection.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/benches/test_failure.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/benches/test_schema.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/collection/test_base.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/collection/test_cast.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/collection/test_collection_future_annotations.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/collection/test_create_empty.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/collection/test_filter_one_to_n.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/collection/test_filter_validate.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/collection/test_ignore_in_filter.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/collection/test_implementation.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/collection/test_join.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/collection/test_matches.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/collection/test_optional_members.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/collection/test_repr.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/collection/test_sample.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/collection/test_serialization.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/collection/test_storage.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/collection/test_validate_input.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/column_types/__init__.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/column_types/test_any.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/column_types/test_array.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/column_types/test_binary.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/column_types/test_datetime.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/column_types/test_decimal.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/column_types/test_enum.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/column_types/test_float.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/column_types/test_integer.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/column_types/test_list.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/column_types/test_object.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/column_types/test_string.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/column_types/test_struct.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/columns/__init__.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/columns/test_alias.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/columns/test_base.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/columns/test_check.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/columns/test_default_dtypes.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/columns/test_matches.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/columns/test_metadata.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/columns/test_polars_schema.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/columns/test_pyarrow.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/columns/test_rules.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/columns/test_sample.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/columns/test_sqlalchemy_columns.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/columns/test_str.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/columns/test_utils.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/conftest.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/core_validation/__init__.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/core_validation/test_match_to_schema.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/core_validation/test_rule_evaluation.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/failure_info/test_storage.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/functional/test_concat.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/functional/test_relationships.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/schema/test_base.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/schema/test_cast.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/schema/test_create_empty.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/schema/test_create_empty_if_none.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/schema/test_filter.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/schema/test_inheritance.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/schema/test_matches.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/schema/test_read_write_parquet.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/schema/test_repr.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/schema/test_rule_implementation.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/schema/test_sample.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/schema/test_serialization.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/schema/test_storage.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/schema/test_validate.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/storage/test_delta.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/test_compat.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/test_config.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/test_deprecation.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/test_factory.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/test_native_regex.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/test_pydantic.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/test_random.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/test_serialization.py +0 -0
- {dataframely-2.2.0 → dataframely-2.3.0}/tests/test_typing.py +0 -0
|
@@ -65,6 +65,11 @@ class CollectionMember:
|
|
|
65
65
|
#: the collection's common primary key. Two members that share common column names
|
|
66
66
|
#: may not both be inlined for sampling.
|
|
67
67
|
inline_for_sampling: bool = False
|
|
68
|
+
#: Whether individual row failures in this member should be propagated to the
|
|
69
|
+
#: collection, i.e., cause the common primary key of the failures to be filtered
|
|
70
|
+
#: out from the entire collection. This setting is ignored if `ignored_in_filters`
|
|
71
|
+
#: is `True`.
|
|
72
|
+
propagate_row_failures: bool = False
|
|
68
73
|
|
|
69
74
|
|
|
70
75
|
# --------------------------------------- UTILS -------------------------------------- #
|
|
@@ -250,6 +255,7 @@ class CollectionMeta(ABCMeta):
|
|
|
250
255
|
is_optional=True,
|
|
251
256
|
ignored_in_filters=collection_member.ignored_in_filters,
|
|
252
257
|
inline_for_sampling=collection_member.inline_for_sampling,
|
|
258
|
+
propagate_row_failures=collection_member.propagate_row_failures,
|
|
253
259
|
)
|
|
254
260
|
elif issubclass(origin, TypedLazyFrame):
|
|
255
261
|
# Happy path: required member
|
|
@@ -258,6 +264,7 @@ class CollectionMeta(ABCMeta):
|
|
|
258
264
|
is_optional=False,
|
|
259
265
|
ignored_in_filters=collection_member.ignored_in_filters,
|
|
260
266
|
inline_for_sampling=collection_member.inline_for_sampling,
|
|
267
|
+
propagate_row_failures=collection_member.propagate_row_failures,
|
|
261
268
|
)
|
|
262
269
|
else:
|
|
263
270
|
# Some other unknown annotation
|
|
@@ -333,6 +340,16 @@ class BaseCollection(metaclass=CollectionMeta):
|
|
|
333
340
|
if not member.ignored_in_filters
|
|
334
341
|
}
|
|
335
342
|
|
|
343
|
+
@classmethod
|
|
344
|
+
def _failure_propagating_members(cls) -> set[str]:
|
|
345
|
+
"""The names of all members of the collection that propagate individual row
|
|
346
|
+
failures to the collection."""
|
|
347
|
+
return {
|
|
348
|
+
name
|
|
349
|
+
for name, member in cls.members().items()
|
|
350
|
+
if member.propagate_row_failures
|
|
351
|
+
}
|
|
352
|
+
|
|
336
353
|
@classmethod
|
|
337
354
|
def common_primary_key(cls) -> list[str]:
|
|
338
355
|
"""The primary keys shared by non ignored members of the collection."""
|
|
@@ -573,7 +573,8 @@ class Collection(BaseCollection, ABC):
|
|
|
573
573
|
# Once we've done that, we can apply the filters on this collection. To this end,
|
|
574
574
|
# we iterate over all filters and store the filter results.
|
|
575
575
|
filters = cls._filters()
|
|
576
|
-
|
|
576
|
+
failure_propagating_members = cls._failure_propagating_members()
|
|
577
|
+
if len(filters) > 0 or len(failure_propagating_members) > 0:
|
|
577
578
|
result_cls = cls._init(results)
|
|
578
579
|
primary_key = cls.common_primary_key()
|
|
579
580
|
|
|
@@ -586,6 +587,17 @@ class Collection(BaseCollection, ABC):
|
|
|
586
587
|
.lazy()
|
|
587
588
|
)
|
|
588
589
|
|
|
590
|
+
drop: dict[str, pl.LazyFrame] = {}
|
|
591
|
+
for failure_propagating_member in failure_propagating_members:
|
|
592
|
+
annotation_column = f"{failure_propagating_member}|failure_propagation"
|
|
593
|
+
drop[annotation_column] = (
|
|
594
|
+
failures[failure_propagating_member]
|
|
595
|
+
._lf.select(primary_key)
|
|
596
|
+
.unique()
|
|
597
|
+
.pipe(collect_if, eager)
|
|
598
|
+
.lazy()
|
|
599
|
+
)
|
|
600
|
+
|
|
589
601
|
# Now we can iterate over the results and left-join onto each individual
|
|
590
602
|
# filter to obtain independent boolean indicators of whether to keep the row
|
|
591
603
|
for member_name, filtered in results.items():
|
|
@@ -601,15 +613,23 @@ class Collection(BaseCollection, ABC):
|
|
|
601
613
|
how="left",
|
|
602
614
|
maintain_order="left",
|
|
603
615
|
).with_columns(pl.col(name).fill_null(False))
|
|
616
|
+
for name, filter_drop in drop.items():
|
|
617
|
+
lf_with_eval = lf_with_eval.join(
|
|
618
|
+
filter_drop.with_columns(pl.lit(False).alias(name)),
|
|
619
|
+
on=primary_key,
|
|
620
|
+
how="left",
|
|
621
|
+
maintain_order="left",
|
|
622
|
+
).with_columns(pl.col(name).fill_null(True))
|
|
604
623
|
|
|
605
624
|
lf_with_eval = lf_with_eval.pipe(collect_if, eager).lazy()
|
|
606
625
|
|
|
607
626
|
# Filtering `lf_with_eval` by the rows for which all joins
|
|
608
627
|
# "succeeded", we can identify the rows that pass all the filters. We
|
|
609
628
|
# keep these rows for the result.
|
|
629
|
+
all_filter_columns = list(keep.keys()) + list(drop.keys())
|
|
610
630
|
results[member_name] = lf_with_eval.filter(
|
|
611
|
-
pl.all_horizontal(
|
|
612
|
-
).drop(
|
|
631
|
+
pl.all_horizontal(all_filter_columns)
|
|
632
|
+
).drop(all_filter_columns)
|
|
613
633
|
|
|
614
634
|
# Filtering `lf_with_eval` with the inverse condition, we find all
|
|
615
635
|
# the problematic rows. We can build a single failure info object by
|
|
@@ -623,7 +643,7 @@ class Collection(BaseCollection, ABC):
|
|
|
623
643
|
#
|
|
624
644
|
failure = failures[member_name]
|
|
625
645
|
filtered_failure = lf_with_eval.filter(
|
|
626
|
-
~pl.all_horizontal(
|
|
646
|
+
~pl.all_horizontal(all_filter_columns)
|
|
627
647
|
).lazy()
|
|
628
648
|
|
|
629
649
|
# If we cast previously, `failure` and `filtered_failure` have different
|
|
@@ -660,7 +680,7 @@ class Collection(BaseCollection, ABC):
|
|
|
660
680
|
|
|
661
681
|
failures[member_name] = FailureInfo(
|
|
662
682
|
lf=failure_lf,
|
|
663
|
-
rule_columns=failure._rule_columns +
|
|
683
|
+
rule_columns=failure._rule_columns + all_filter_columns,
|
|
664
684
|
schema=failure.schema,
|
|
665
685
|
)
|
|
666
686
|
|
|
@@ -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.3.0"
|
|
31
31
|
|
|
32
32
|
[project.optional-dependencies]
|
|
33
33
|
deltalake = ["deltalake"]
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Copyright (c) QuantCo 2025-2025
|
|
2
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
3
|
+
|
|
4
|
+
from typing import Annotated
|
|
5
|
+
|
|
6
|
+
import polars as pl
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
import dataframely as dy
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class MyTestSchema(dy.Schema):
|
|
13
|
+
shared = dy.UInt8(primary_key=True)
|
|
14
|
+
u = dy.UInt8()
|
|
15
|
+
v = dy.UInt8()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class MyTestSchema2(dy.Schema):
|
|
19
|
+
shared = dy.UInt8(primary_key=True)
|
|
20
|
+
id = dy.UInt8(primary_key=True)
|
|
21
|
+
x = dy.UInt8()
|
|
22
|
+
y = dy.UInt8()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class MyTestCollection(dy.Collection):
|
|
26
|
+
a: dy.LazyFrame[MyTestSchema]
|
|
27
|
+
b: Annotated[
|
|
28
|
+
dy.LazyFrame[MyTestSchema2],
|
|
29
|
+
dy.CollectionMember(propagate_row_failures=True),
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
@dy.filter()
|
|
33
|
+
def x_greater_u(self) -> pl.LazyFrame:
|
|
34
|
+
return (
|
|
35
|
+
self.a.join(self.b, on="shared")
|
|
36
|
+
.filter(pl.col("x") > pl.col("u"))
|
|
37
|
+
.unique("shared")
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@pytest.fixture()
|
|
42
|
+
def valid_a() -> pl.LazyFrame:
|
|
43
|
+
return pl.LazyFrame(
|
|
44
|
+
[
|
|
45
|
+
{"shared": 1, "u": 10, "v": 5},
|
|
46
|
+
{"shared": 2, "u": 20, "v": 15},
|
|
47
|
+
{"shared": 3, "u": 30, "v": 25},
|
|
48
|
+
]
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@pytest.fixture()
|
|
53
|
+
def valid_b() -> pl.LazyFrame:
|
|
54
|
+
return pl.LazyFrame(
|
|
55
|
+
[
|
|
56
|
+
{"shared": 1, "id": 1, "x": 15, "y": 50},
|
|
57
|
+
{"shared": 2, "id": 1, "x": 25, "y": 60},
|
|
58
|
+
{"shared": 2, "id": 2, "x": 25, "y": 70},
|
|
59
|
+
{"shared": 3, "id": 1, "x": 5, "y": 70},
|
|
60
|
+
]
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@pytest.fixture()
|
|
65
|
+
def invalid_b() -> pl.LazyFrame:
|
|
66
|
+
return pl.LazyFrame(
|
|
67
|
+
[
|
|
68
|
+
{"shared": 1, "id": 1, "x": 15, "y": 50},
|
|
69
|
+
{"shared": 2, "id": 1, "x": 25, "y": 60},
|
|
70
|
+
{
|
|
71
|
+
"shared": 2,
|
|
72
|
+
"id": 2,
|
|
73
|
+
"x": 25,
|
|
74
|
+
"y": None,
|
|
75
|
+
}, # invalid row, should be propagated
|
|
76
|
+
{"shared": 3, "id": 1, "x": 10, "y": 70}, # filtered out due to the filter
|
|
77
|
+
]
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def test_collection_propagate_row_failures_meta() -> None:
|
|
82
|
+
assert MyTestCollection._failure_propagating_members() == {"b"}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def test_collection_propagate_row_failure_no_propagation(
|
|
86
|
+
valid_a: pl.LazyFrame,
|
|
87
|
+
valid_b: pl.LazyFrame,
|
|
88
|
+
) -> None:
|
|
89
|
+
success, failures = MyTestCollection.filter(
|
|
90
|
+
{
|
|
91
|
+
"a": valid_a,
|
|
92
|
+
"b": valid_b,
|
|
93
|
+
},
|
|
94
|
+
cast=True,
|
|
95
|
+
)
|
|
96
|
+
# Assert that only id 3 is filtered out (caused by the filter)
|
|
97
|
+
assert success.a.select("shared").collect().to_series().to_list() == [1, 2]
|
|
98
|
+
assert success.b.select("shared").collect().to_series().to_list() == [1, 2, 2]
|
|
99
|
+
# Assert that nothing is filtered out due to propagation
|
|
100
|
+
for member_name in MyTestCollection.members().keys():
|
|
101
|
+
assert failures[member_name].counts()["x_greater_u"] == 1
|
|
102
|
+
assert len(failures[member_name].counts()) == 1
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def test_collection_propagate_row_failure_with_propagation(
|
|
106
|
+
valid_a: pl.LazyFrame,
|
|
107
|
+
invalid_b: pl.LazyFrame,
|
|
108
|
+
) -> None:
|
|
109
|
+
success, failures = MyTestCollection.filter(
|
|
110
|
+
{
|
|
111
|
+
"a": valid_a,
|
|
112
|
+
"b": invalid_b,
|
|
113
|
+
},
|
|
114
|
+
cast=True,
|
|
115
|
+
)
|
|
116
|
+
# Assert that id 2 is also filtered out
|
|
117
|
+
assert success.a.select("shared").collect().to_series().to_list() == [1]
|
|
118
|
+
assert success.b.select("shared").collect().to_series().to_list() == [1]
|
|
119
|
+
# Assert that nothing is filtered out due to propagation
|
|
120
|
+
for member_name in MyTestCollection.members().keys():
|
|
121
|
+
assert failures[member_name].counts()["x_greater_u"] == 1
|
|
122
|
+
assert failures[member_name].counts()["b|failure_propagation"] == 1
|
|
123
|
+
assert failures[member_name].counts()["x_greater_u"] == 1
|
|
124
|
+
assert failures[member_name].counts()["b|failure_propagation"] == 1
|
|
125
|
+
assert len(failures["a"].counts()) == 2
|
|
126
|
+
assert len(failures["b"].counts()) == 3
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dataframely-2.2.0 → dataframely-2.3.0}/tests/collection/test_collection_future_annotations.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|