dataframely 3.0.0__tar.gz → 3.1.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-3.0.0 → dataframely-3.1.0}/.github/workflows/build.yml +3 -4
- {dataframely-3.0.0 → dataframely-3.1.0}/.github/workflows/chore.yml +1 -1
- {dataframely-3.0.0 → dataframely-3.1.0}/.github/workflows/ci.yml +5 -5
- {dataframely-3.0.0 → dataframely-3.1.0}/.github/workflows/copilot-setup-steps.yml +2 -2
- {dataframely-3.0.0 → dataframely-3.1.0}/.github/workflows/nightly.yml +1 -1
- {dataframely-3.0.0 → dataframely-3.1.0}/.github/workflows/release-drafter.yml +1 -1
- {dataframely-3.0.0 → dataframely-3.1.0}/.github/workflows/scorecard.yml +2 -2
- {dataframely-3.0.0 → dataframely-3.1.0}/.lefthook.yaml +1 -1
- {dataframely-3.0.0 → dataframely-3.1.0}/Cargo.lock +14 -14
- {dataframely-3.0.0 → dataframely-3.1.0}/PKG-INFO +10 -8
- {dataframely-3.0.0 → dataframely-3.1.0}/README.md +9 -7
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/_base_schema.py +2 -4
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/columns/_base.py +5 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/columns/array.py +6 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/columns/categorical.py +3 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/columns/list.py +6 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/columns/struct.py +7 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/filter_result.py +24 -4
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/guides/coding-agents.md +6 -6
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/guides/features/column-metadata.md +6 -4
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/guides/features/data-generation.md +32 -14
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/guides/features/serialization.md +11 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/guides/features/sql-generation.md +1 -5
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/guides/migration/v1-v2.md +2 -4
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/guides/migration/v2-v3.md +3 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/guides/quickstart.md +18 -16
- {dataframely-3.0.0 → dataframely-3.1.0}/pixi.lock +15596 -14331
- {dataframely-3.0.0 → dataframely-3.1.0}/pixi.toml +1 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/pyproject.toml +1 -1
- {dataframely-3.0.0 → dataframely-3.1.0}/skills/SKILL.md +13 -9
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/column_types/test_categorical.py +30 -8
- dataframely-3.1.0/tests/failure_info/test_parquet.py +199 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/schema/test_validate.py +5 -2
- dataframely-3.0.0/tests/failure_info/test_parquet.py +0 -82
- {dataframely-3.0.0 → dataframely-3.1.0}/.claude/CLAUDE.md +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/.copier-answers.yml +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/.envrc +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/.gitattributes +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/.github/CODEOWNERS +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/.github/copilot-instructions.md +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/.github/dependabot.yml +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/.github/instructions/tests.instructions.md +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/.github/release-drafter.yml +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/.gitignore +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/.prettierignore +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/.prettierrc +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/.readthedocs.yml +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/AGENTS.md +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/Cargo.toml +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/LICENSE +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/SECURITY.md +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/__init__.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/_compat.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/_deprecation.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/_filter.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/_match_to_schema.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/_native.pyi +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/_plugin.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/_polars.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/_pydantic.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/_rule.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/_typing.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/collection/__init__.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/collection/_base.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/collection/collection.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/collection/filter_result.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/columns/__init__.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/columns/_mixins.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/columns/_utils.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/columns/any.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/columns/binary.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/columns/bool.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/columns/datetime.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/columns/decimal.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/columns/enum.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/columns/float.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/columns/integer.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/columns/object.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/columns/string.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/config.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/exc.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/experimental/__init__.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/experimental/infer_schema.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/functional.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/py.typed +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/random.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/schema.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/testing/__init__.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/testing/const.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/testing/factory.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/testing/mask.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/testing/rules.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docker-compose.yml +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/_static/custom.css +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/_static/favicon.ico +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/_templates/autosummary/class.rst +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/_templates/autosummary/method.rst +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/_templates/classes/column.rst +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/_templates/classes/error.rst +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/_templates/classes/filter_result.rst +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/api/collection/generation.rst +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/api/collection/index.rst +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/api/collection/io.rst +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/api/collection/metadata.rst +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/api/collection/operations.rst +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/api/collection/validation.rst +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/api/columns/index.rst +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/api/errors/index.rst +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/api/experimental/index.rst +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/api/filter_result/failure_info.rst +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/api/filter_result/index.rst +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/api/index.rst +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/api/misc/index.rst +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/api/schema/conversion.rst +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/api/schema/generation.rst +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/api/schema/index.rst +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/api/schema/metadata.rst +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/api/schema/validation.rst +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/conf.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/css/custom.css +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/guides/development.md +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/guides/examples/index.md +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/guides/examples/real-world.ipynb +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/guides/faq.md +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/guides/features/index.md +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/guides/features/lazy-validation.md +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/guides/features/primary-keys.md +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/guides/index.md +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/guides/migration/index.md +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/docs/index.md +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/rust-toolchain.toml +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/src/arrow.rs +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/src/lib.rs +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/src/polars_plugin/mod.rs +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/src/polars_plugin/rule_failure.rs +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/src/polars_plugin/utils.rs +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/src/polars_plugin/validation_error.rs +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/src/regex/errdefs.rs +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/src/regex/mod.rs +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/src/regex/repr.rs +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/benches/conftest.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/benches/test_collection.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/benches/test_failure.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/benches/test_schema.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_base.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_cast.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_collection_future_annotations.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_create_empty.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_dataframe_members.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_filter_one_to_n.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_filter_validate.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_ignore_in_filter.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_implementation.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_join.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_matches.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_optional_members.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_parquet.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_pipe.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_propagate_row_failures.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_repr.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_sample.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_skip_member_validation.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_validate_input.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/column_types/__init__.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/column_types/test_any.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/column_types/test_array.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/column_types/test_binary.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/column_types/test_datetime.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/column_types/test_decimal.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/column_types/test_enum.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/column_types/test_float.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/column_types/test_integer.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/column_types/test_list.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/column_types/test_object.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/column_types/test_string.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/column_types/test_struct.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/columns/__init__.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/columns/test_alias.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/columns/test_arrow_pycapsule.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/columns/test_base.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/columns/test_check.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/columns/test_default_dtypes.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/columns/test_description.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/columns/test_matches.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/columns/test_metadata.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/columns/test_polars_schema.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/columns/test_pydantic.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/columns/test_rules.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/columns/test_sample.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/columns/test_sqlalchemy_columns.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/columns/test_str.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/columns/test_utils.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/core_validation/__init__.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/core_validation/test_match_to_schema.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/core_validation/test_rule_evaluation.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/experimental/test_infer_schema.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/functional/test_concat.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/functional/test_relationships.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/schema/test_base.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/schema/test_cast.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/schema/test_create_empty.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/schema/test_create_empty_if_none.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/schema/test_filter.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/schema/test_inheritance.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/schema/test_matches.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/schema/test_pydantic_model.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/schema/test_repr.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/schema/test_rule_implementation.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/schema/test_sample.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/test_compat.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/test_config.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/test_deprecation.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/test_factory.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/test_native_regex.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/test_pydantic.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/test_random.py +0 -0
- {dataframely-3.0.0 → dataframely-3.1.0}/tests/test_typing.py +0 -0
|
@@ -17,7 +17,7 @@ jobs:
|
|
|
17
17
|
with:
|
|
18
18
|
fetch-depth: 0
|
|
19
19
|
- name: Set up pixi
|
|
20
|
-
uses: prefix-dev/setup-pixi@
|
|
20
|
+
uses: prefix-dev/setup-pixi@f00437f565399d418b0acc85936d12c1fb668347 # v0.10.1
|
|
21
21
|
with:
|
|
22
22
|
environments: build
|
|
23
23
|
- name: Set version
|
|
@@ -52,7 +52,7 @@ jobs:
|
|
|
52
52
|
with:
|
|
53
53
|
fetch-depth: 0
|
|
54
54
|
- name: Set up pixi
|
|
55
|
-
uses: prefix-dev/setup-pixi@
|
|
55
|
+
uses: prefix-dev/setup-pixi@f00437f565399d418b0acc85936d12c1fb668347 # v0.10.1
|
|
56
56
|
with:
|
|
57
57
|
environments: build
|
|
58
58
|
- name: Set version
|
|
@@ -87,7 +87,6 @@ jobs:
|
|
|
87
87
|
command: build
|
|
88
88
|
args: --out dist --release -i python3.10
|
|
89
89
|
manylinux: auto
|
|
90
|
-
sccache: ${{ github.event_name != 'release' }}
|
|
91
90
|
# NOTE: We also need to set up cargo-auditable inside the docker container
|
|
92
91
|
# where the linux build is performed.
|
|
93
92
|
docker-options: ${{ case(github.event_name == 'release', format('-e CARGO={0}', env.CARGO), '') }}
|
|
@@ -115,4 +114,4 @@ jobs:
|
|
|
115
114
|
path: dist
|
|
116
115
|
merge-multiple: true
|
|
117
116
|
- name: Publish package on PyPi
|
|
118
|
-
uses: pypa/gh-action-pypi-publish@
|
|
117
|
+
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
|
@@ -54,6 +54,6 @@ jobs:
|
|
|
54
54
|
pull-requests: write
|
|
55
55
|
steps:
|
|
56
56
|
- name: Assign labels
|
|
57
|
-
uses: release-drafter/release-drafter/autolabeler@
|
|
57
|
+
uses: release-drafter/release-drafter/autolabeler@34d80673e067bdc0c24568d3af899c216adcfaa9 # v7.7.0
|
|
58
58
|
with:
|
|
59
59
|
token: ${{ github.token }}
|
|
@@ -24,13 +24,13 @@ jobs:
|
|
|
24
24
|
# needed for 'pre-commit-mirrors-insert-license'
|
|
25
25
|
fetch-depth: 0
|
|
26
26
|
- name: Set up pixi
|
|
27
|
-
uses: prefix-dev/setup-pixi@
|
|
27
|
+
uses: prefix-dev/setup-pixi@f00437f565399d418b0acc85936d12c1fb668347 # v0.10.1
|
|
28
28
|
with:
|
|
29
|
-
environments: default
|
|
29
|
+
environments: default default-py310
|
|
30
30
|
- name: Install Rust
|
|
31
31
|
run: rustup show
|
|
32
32
|
- name: Cache Rust dependencies
|
|
33
|
-
uses: Swatinem/rust-cache@
|
|
33
|
+
uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
|
34
34
|
- name: Run linting
|
|
35
35
|
run: pixi run lint
|
|
36
36
|
env:
|
|
@@ -65,7 +65,7 @@ jobs:
|
|
|
65
65
|
- name: Checkout branch
|
|
66
66
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
67
67
|
- name: Set up pixi
|
|
68
|
-
uses: prefix-dev/setup-pixi@
|
|
68
|
+
uses: prefix-dev/setup-pixi@f00437f565399d418b0acc85936d12c1fb668347 # v0.10.1
|
|
69
69
|
with:
|
|
70
70
|
environments: ${{ matrix.environment }}
|
|
71
71
|
# FIXME: Remove when `s3_server` fixture does not start a process anymore
|
|
@@ -73,7 +73,7 @@ jobs:
|
|
|
73
73
|
- name: Install Rust
|
|
74
74
|
run: rustup show
|
|
75
75
|
- name: Cache Rust dependencies
|
|
76
|
-
uses: Swatinem/rust-cache@
|
|
76
|
+
uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
|
77
77
|
- name: Install repository
|
|
78
78
|
run: pixi run -e ${{ matrix.environment }} postinstall
|
|
79
79
|
- name: Run pytest
|
|
@@ -15,12 +15,12 @@ jobs:
|
|
|
15
15
|
- name: Checkout branch
|
|
16
16
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
17
17
|
- name: Set up pixi
|
|
18
|
-
uses: prefix-dev/setup-pixi@
|
|
18
|
+
uses: prefix-dev/setup-pixi@f00437f565399d418b0acc85936d12c1fb668347 # v0.10.1
|
|
19
19
|
with:
|
|
20
20
|
environments: default
|
|
21
21
|
- name: Install Rust
|
|
22
22
|
run: rustup show
|
|
23
23
|
- name: Cache Rust dependencies
|
|
24
|
-
uses: Swatinem/rust-cache@
|
|
24
|
+
uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
|
25
25
|
- name: Install repository
|
|
26
26
|
run: pixi run postinstall
|
|
@@ -25,7 +25,7 @@ jobs:
|
|
|
25
25
|
- name: Checkout branch
|
|
26
26
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
27
27
|
- name: Set up pixi
|
|
28
|
-
uses: prefix-dev/setup-pixi@
|
|
28
|
+
uses: prefix-dev/setup-pixi@f00437f565399d418b0acc85936d12c1fb668347 # v0.10.1
|
|
29
29
|
with:
|
|
30
30
|
environments: nightly
|
|
31
31
|
- name: Install polars nightly
|
|
@@ -16,6 +16,6 @@ jobs:
|
|
|
16
16
|
pull-requests: read
|
|
17
17
|
steps:
|
|
18
18
|
- name: Update release draft
|
|
19
|
-
uses: release-drafter/release-drafter@
|
|
19
|
+
uses: release-drafter/release-drafter@34d80673e067bdc0c24568d3af899c216adcfaa9 # v7.7.0
|
|
20
20
|
with:
|
|
21
21
|
token: ${{ github.token }}
|
|
@@ -40,7 +40,7 @@ jobs:
|
|
|
40
40
|
persist-credentials: false
|
|
41
41
|
|
|
42
42
|
- name: "Run analysis"
|
|
43
|
-
uses: ossf/scorecard-action@
|
|
43
|
+
uses: ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc # v2.4.4
|
|
44
44
|
with:
|
|
45
45
|
results_file: results.sarif
|
|
46
46
|
results_format: sarif
|
|
@@ -74,6 +74,6 @@ jobs:
|
|
|
74
74
|
# Upload the results to GitHub's code scanning dashboard (optional).
|
|
75
75
|
# Commenting out will disable upload of results to your repo's Code Scanning dashboard
|
|
76
76
|
- name: "Upload to code-scanning"
|
|
77
|
-
uses: github/codeql-action/upload-sarif@
|
|
77
|
+
uses: github/codeql-action/upload-sarif@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8
|
|
78
78
|
with:
|
|
79
79
|
sarif_file: results.sarif
|
|
@@ -21,7 +21,7 @@ pre-commit:
|
|
|
21
21
|
run: pixi {run} ruff format --force-exclude
|
|
22
22
|
- name: mypy
|
|
23
23
|
glob: "*.py"
|
|
24
|
-
run: pixi {run} mypy {staged_files}
|
|
24
|
+
run: pixi {run} -e default-py310 mypy {staged_files}
|
|
25
25
|
- name: prettier
|
|
26
26
|
glob: "*.{md,yml,yaml}"
|
|
27
27
|
run: pixi {run} prettier --write --no-error-on-unmatched-pattern --list-different --ignore-unknown {staged_files}
|
|
@@ -2278,9 +2278,9 @@ dependencies = [
|
|
|
2278
2278
|
|
|
2279
2279
|
[[package]]
|
|
2280
2280
|
name = "pyo3"
|
|
2281
|
-
version = "0.29.
|
|
2281
|
+
version = "0.29.2"
|
|
2282
2282
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2283
|
-
checksum = "
|
|
2283
|
+
checksum = "4688ddedf473e32662b9b067670129a8afb8c18e351482c70d62ba4a88171e8b"
|
|
2284
2284
|
dependencies = [
|
|
2285
2285
|
"libc",
|
|
2286
2286
|
"once_cell",
|
|
@@ -2292,18 +2292,18 @@ dependencies = [
|
|
|
2292
2292
|
|
|
2293
2293
|
[[package]]
|
|
2294
2294
|
name = "pyo3-build-config"
|
|
2295
|
-
version = "0.29.
|
|
2295
|
+
version = "0.29.2"
|
|
2296
2296
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2297
|
-
checksum = "
|
|
2297
|
+
checksum = "f41027e41b4bd03f6e60f9f417fe24a6341a6bb744edd62b6f709f2a52ea30e9"
|
|
2298
2298
|
dependencies = [
|
|
2299
2299
|
"target-lexicon",
|
|
2300
2300
|
]
|
|
2301
2301
|
|
|
2302
2302
|
[[package]]
|
|
2303
2303
|
name = "pyo3-ffi"
|
|
2304
|
-
version = "0.29.
|
|
2304
|
+
version = "0.29.2"
|
|
2305
2305
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2306
|
-
checksum = "
|
|
2306
|
+
checksum = "e591a95526fead067432c3b3a33fc74770b87b1e04e73671090d9c2055a2b327"
|
|
2307
2307
|
dependencies = [
|
|
2308
2308
|
"libc",
|
|
2309
2309
|
"pyo3-build-config",
|
|
@@ -2311,9 +2311,9 @@ dependencies = [
|
|
|
2311
2311
|
|
|
2312
2312
|
[[package]]
|
|
2313
2313
|
name = "pyo3-macros"
|
|
2314
|
-
version = "0.29.
|
|
2314
|
+
version = "0.29.2"
|
|
2315
2315
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2316
|
-
checksum = "
|
|
2316
|
+
checksum = "73225868fc1cd84eef2c3c230ddb91273bf1de46aeb8a4248da76d32a0924a1c"
|
|
2317
2317
|
dependencies = [
|
|
2318
2318
|
"proc-macro2",
|
|
2319
2319
|
"pyo3-macros-backend",
|
|
@@ -2323,9 +2323,9 @@ dependencies = [
|
|
|
2323
2323
|
|
|
2324
2324
|
[[package]]
|
|
2325
2325
|
name = "pyo3-macros-backend"
|
|
2326
|
-
version = "0.29.
|
|
2326
|
+
version = "0.29.2"
|
|
2327
2327
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
2328
|
-
checksum = "
|
|
2328
|
+
checksum = "571575aa3749fa6216757dd47d2a3e7ef360f329a40f0666a9fbd14889024952"
|
|
2329
2329
|
dependencies = [
|
|
2330
2330
|
"heck",
|
|
2331
2331
|
"proc-macro2",
|
|
@@ -3215,18 +3215,18 @@ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
|
|
|
3215
3215
|
|
|
3216
3216
|
[[package]]
|
|
3217
3217
|
name = "thiserror"
|
|
3218
|
-
version = "2.0.
|
|
3218
|
+
version = "2.0.20"
|
|
3219
3219
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
3220
|
-
checksum = "
|
|
3220
|
+
checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f"
|
|
3221
3221
|
dependencies = [
|
|
3222
3222
|
"thiserror-impl",
|
|
3223
3223
|
]
|
|
3224
3224
|
|
|
3225
3225
|
[[package]]
|
|
3226
3226
|
name = "thiserror-impl"
|
|
3227
|
-
version = "2.0.
|
|
3227
|
+
version = "2.0.20"
|
|
3228
3228
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
3229
|
-
checksum = "
|
|
3229
|
+
checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af"
|
|
3230
3230
|
dependencies = [
|
|
3231
3231
|
"proc-macro2",
|
|
3232
3232
|
"quote",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dataframely
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.1.0
|
|
4
4
|
Classifier: Programming Language :: Python :: 3
|
|
5
5
|
Classifier: Programming Language :: Python :: 3.10
|
|
6
6
|
Classifier: Programming Language :: Python :: 3.11
|
|
@@ -69,6 +69,7 @@ pip install dataframely
|
|
|
69
69
|
import dataframely as dy
|
|
70
70
|
import polars as pl
|
|
71
71
|
|
|
72
|
+
|
|
72
73
|
class HouseSchema(dy.Schema):
|
|
73
74
|
zip_code = dy.String(nullable=False, min_length=3)
|
|
74
75
|
num_bedrooms = dy.UInt8(nullable=False)
|
|
@@ -88,15 +89,16 @@ class HouseSchema(dy.Schema):
|
|
|
88
89
|
### Validating data against schema
|
|
89
90
|
|
|
90
91
|
```python
|
|
91
|
-
|
|
92
92
|
import polars as pl
|
|
93
93
|
|
|
94
|
-
df = pl.DataFrame(
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
94
|
+
df = pl.DataFrame(
|
|
95
|
+
{
|
|
96
|
+
"zip_code": ["01234", "01234", "1", "213", "123", "213"],
|
|
97
|
+
"num_bedrooms": [2, 2, 1, None, None, 2],
|
|
98
|
+
"num_bathrooms": [1, 2, 1, 1, 0, 8],
|
|
99
|
+
"price": [100_000, 110_000, 50_000, 80_000, 60_000, 160_000],
|
|
100
|
+
}
|
|
101
|
+
)
|
|
100
102
|
|
|
101
103
|
# Validate the data and cast columns to expected types
|
|
102
104
|
validated_df: dy.DataFrame[HouseSchema] = HouseSchema.validate(df, cast=True)
|
|
@@ -45,6 +45,7 @@ pip install dataframely
|
|
|
45
45
|
import dataframely as dy
|
|
46
46
|
import polars as pl
|
|
47
47
|
|
|
48
|
+
|
|
48
49
|
class HouseSchema(dy.Schema):
|
|
49
50
|
zip_code = dy.String(nullable=False, min_length=3)
|
|
50
51
|
num_bedrooms = dy.UInt8(nullable=False)
|
|
@@ -64,15 +65,16 @@ class HouseSchema(dy.Schema):
|
|
|
64
65
|
### Validating data against schema
|
|
65
66
|
|
|
66
67
|
```python
|
|
67
|
-
|
|
68
68
|
import polars as pl
|
|
69
69
|
|
|
70
|
-
df = pl.DataFrame(
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
70
|
+
df = pl.DataFrame(
|
|
71
|
+
{
|
|
72
|
+
"zip_code": ["01234", "01234", "1", "213", "123", "213"],
|
|
73
|
+
"num_bedrooms": [2, 2, 1, None, None, 2],
|
|
74
|
+
"num_bathrooms": [1, 2, 1, 1, 0, 8],
|
|
75
|
+
"price": [100_000, 110_000, 50_000, 80_000, 60_000, 160_000],
|
|
76
|
+
}
|
|
77
|
+
)
|
|
76
78
|
|
|
77
79
|
# Validate the data and cast columns to expected types
|
|
78
80
|
validated_df: dy.DataFrame[HouseSchema] = HouseSchema.validate(df, cast=True)
|
|
@@ -190,8 +190,7 @@ class SchemaMeta(ABCMeta):
|
|
|
190
190
|
# Dynamically set the name of the column if it is a `Column` instance.
|
|
191
191
|
# Also, we "register" the name of the schema that set the name.
|
|
192
192
|
if isinstance(val, Column):
|
|
193
|
-
val.
|
|
194
|
-
val._name = val.alias or name
|
|
193
|
+
val._bind(f"{cls.__module__}:{cls.__name__}", val.alias or name)
|
|
195
194
|
return val
|
|
196
195
|
|
|
197
196
|
@staticmethod
|
|
@@ -294,8 +293,7 @@ class BaseSchema(metaclass=SchemaMeta):
|
|
|
294
293
|
columns: dict[str, Column] = getattr(cls, _COLUMN_ATTR)
|
|
295
294
|
for name in columns.keys():
|
|
296
295
|
# Dynamically set the name and source schema of the columns.
|
|
297
|
-
columns[name].
|
|
298
|
-
columns[name]._name = name
|
|
296
|
+
columns[name]._bind(f"{cls.__module__}:{cls.__name__}", name)
|
|
299
297
|
return columns
|
|
300
298
|
|
|
301
299
|
@classmethod
|
|
@@ -276,6 +276,11 @@ class Column(ABC):
|
|
|
276
276
|
|
|
277
277
|
# ------------------------------------ HELPER ------------------------------------ #
|
|
278
278
|
|
|
279
|
+
def _bind(self, schema: str, name: str) -> None:
|
|
280
|
+
"""Set the schema and column path used by schema-scoped data types."""
|
|
281
|
+
self._schema = schema
|
|
282
|
+
self._name = name
|
|
283
|
+
|
|
279
284
|
@property
|
|
280
285
|
def name(self) -> str:
|
|
281
286
|
"""Get the name of the column in a schema."""
|
|
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
|
|
6
6
|
import math
|
|
7
7
|
import warnings
|
|
8
|
+
from copy import copy
|
|
8
9
|
from typing import Any, cast
|
|
9
10
|
|
|
10
11
|
import polars as pl
|
|
@@ -75,6 +76,11 @@ class Array(Column):
|
|
|
75
76
|
self.inner = inner
|
|
76
77
|
self.shape = shape if isinstance(shape, tuple) else (shape,)
|
|
77
78
|
|
|
79
|
+
def _bind(self, schema: str, name: str) -> None:
|
|
80
|
+
super()._bind(schema, name)
|
|
81
|
+
self.inner = copy(self.inner)
|
|
82
|
+
self.inner._bind(schema, f"{name}.inner")
|
|
83
|
+
|
|
78
84
|
@property
|
|
79
85
|
def dtype(self) -> pl.DataType:
|
|
80
86
|
return pl.Array(self.inner.dtype, self.shape)
|
|
@@ -39,6 +39,9 @@ class Categorical(Column):
|
|
|
39
39
|
a data type is provided, name and namespace are synthesized from the
|
|
40
40
|
enclosing schema and column name, automatically creating a column-
|
|
41
41
|
scoped categories dictionary.
|
|
42
|
+
List and array elements append `.inner` to the containing column's
|
|
43
|
+
name; struct fields append their field names with dots (for example,
|
|
44
|
+
`items.inner.kind` for a field in a list of structs).
|
|
42
45
|
nullable: Whether this column may contain null values.
|
|
43
46
|
Explicitly set `nullable=True` if you want your column to be nullable.
|
|
44
47
|
In a future release, `nullable=False` will be the default if `nullable`
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
6
|
+
from copy import copy
|
|
6
7
|
from itertools import chain
|
|
7
8
|
from typing import Any, cast
|
|
8
9
|
|
|
@@ -85,6 +86,11 @@ class List(Column):
|
|
|
85
86
|
self.min_length = min_length
|
|
86
87
|
self.max_length = max_length
|
|
87
88
|
|
|
89
|
+
def _bind(self, schema: str, name: str) -> None:
|
|
90
|
+
super()._bind(schema, name)
|
|
91
|
+
self.inner = copy(self.inner)
|
|
92
|
+
self.inner._bind(schema, f"{name}.inner")
|
|
93
|
+
|
|
88
94
|
@property
|
|
89
95
|
def dtype(self) -> pl.DataType:
|
|
90
96
|
return pl.List(self.inner.dtype)
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
6
|
+
from copy import copy
|
|
6
7
|
from typing import Any, cast
|
|
7
8
|
|
|
8
9
|
import polars as pl
|
|
@@ -76,6 +77,12 @@ class Struct(Column):
|
|
|
76
77
|
)
|
|
77
78
|
self.inner = inner
|
|
78
79
|
|
|
80
|
+
def _bind(self, schema: str, name: str) -> None:
|
|
81
|
+
super()._bind(schema, name)
|
|
82
|
+
self.inner = {field: copy(col) for field, col in self.inner.items()}
|
|
83
|
+
for field, col in self.inner.items():
|
|
84
|
+
col._bind(schema, f"{name}.{field}")
|
|
85
|
+
|
|
79
86
|
@property
|
|
80
87
|
def dtype(self) -> pl.DataType:
|
|
81
88
|
return pl.Struct({name: col.dtype for name, col in self.inner.items()})
|
|
@@ -128,6 +128,9 @@ class FailureInfo:
|
|
|
128
128
|
filters in addition to member-level rules, or when calling :meth:`Schema.filter`
|
|
129
129
|
with `cast=True` and dtype-casting fails for a value.
|
|
130
130
|
"""
|
|
131
|
+
if len(self._rule_columns) == 0:
|
|
132
|
+
return self.invalid()
|
|
133
|
+
|
|
131
134
|
return self._df.select(
|
|
132
135
|
pl.exclude(self._rule_columns),
|
|
133
136
|
pl.col(*self._rule_columns).replace_strict(
|
|
@@ -166,24 +169,41 @@ class FailureInfo:
|
|
|
166
169
|
|
|
167
170
|
# ---------------------------------- PERSISTENCE --------------------------------- #
|
|
168
171
|
|
|
169
|
-
def write_parquet(
|
|
172
|
+
def write_parquet(
|
|
173
|
+
self,
|
|
174
|
+
file: str | Path | IO[bytes],
|
|
175
|
+
*,
|
|
176
|
+
only_failing_rules: bool = False,
|
|
177
|
+
**kwargs: Any,
|
|
178
|
+
) -> None:
|
|
170
179
|
"""Write the failure info to a single parquet file.
|
|
171
180
|
|
|
172
181
|
Writes the invalid rows along with additional boolean rule columns indicating
|
|
173
182
|
which validation rules failed. Unlike :meth:`invalid`, this includes columns
|
|
174
|
-
for each rule, where ``False`` indicates the rule failed for that row.
|
|
183
|
+
for each rule by default, where ``False`` indicates the rule failed for that row.
|
|
175
184
|
|
|
176
185
|
Args:
|
|
177
186
|
file: The file path or writable file-like object to which to write the
|
|
178
187
|
parquet file.
|
|
188
|
+
only_failing_rules: Whether to write only rule columns containing at least
|
|
189
|
+
one validation failure.
|
|
179
190
|
kwargs: Additional keyword arguments passed directly to
|
|
180
191
|
:meth:`polars.write_parquet`. `metadata` may only be provided if it
|
|
181
192
|
is a dictionary.
|
|
182
193
|
"""
|
|
183
194
|
metadata = kwargs.pop("metadata", {}) or {}
|
|
184
|
-
self._df
|
|
195
|
+
df = self._df
|
|
196
|
+
rule_columns = self._rule_columns
|
|
197
|
+
if only_failing_rules:
|
|
198
|
+
counts = _compute_counts(df, self._rule_columns)
|
|
199
|
+
rule_columns = [column for column in self._rule_columns if column in counts]
|
|
200
|
+
df = df.drop(
|
|
201
|
+
column for column in self._rule_columns if column not in counts
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
df.write_parquet(
|
|
185
205
|
file,
|
|
186
|
-
metadata={**metadata, "rule_columns": json.dumps(
|
|
206
|
+
metadata={**metadata, "rule_columns": json.dumps(rule_columns)},
|
|
187
207
|
**kwargs,
|
|
188
208
|
)
|
|
189
209
|
|
|
@@ -9,13 +9,13 @@ Coding agents like [Claude Code](https://code.claude.com/), [Codex](https://open
|
|
|
9
9
|
When writing data processing logic, `dataframely` helps to fulfill these criteria.
|
|
10
10
|
|
|
11
11
|
To help your coding agent write idiomatic `dataframely` code, we provide a `dataframely`
|
|
12
|
-
[skill](https://raw.githubusercontent.com/Quantco/dataframely/refs/heads/main/SKILL.md) following the
|
|
12
|
+
[skill](https://raw.githubusercontent.com/Quantco/dataframely/refs/heads/main/skills/SKILL.md) following the
|
|
13
13
|
[`agentskills.io` spec](https://agentskills.io/specification). You can install it by placing it where your agent can
|
|
14
14
|
find it. For example, if you are using Claude Code:
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
17
|
mkdir -p .claude/skills/dataframely/
|
|
18
|
-
curl -
|
|
18
|
+
curl -fLo .claude/skills/dataframely/SKILL.md https://raw.githubusercontent.com/Quantco/dataframely/refs/heads/main/skills/SKILL.md
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
or if you are using [skills.sh](https://skills.sh/) to manage your skills:
|
|
@@ -36,15 +36,15 @@ right time.
|
|
|
36
36
|
For example:
|
|
37
37
|
|
|
38
38
|
```python
|
|
39
|
-
def preprocess(
|
|
40
|
-
|
|
39
|
+
def preprocess(
|
|
40
|
+
raw: dy.LazyFrame[MyRawSchema],
|
|
41
|
+
) -> dy.DataFrame[MyPreprocessedSchema]: ...
|
|
41
42
|
```
|
|
42
43
|
|
|
43
44
|
gives a coding agent much more information than the schema-less alternative:
|
|
44
45
|
|
|
45
46
|
```python
|
|
46
|
-
def load_data(raw: pl.LazyFrame) -> pl.DataFrame:
|
|
47
|
-
...
|
|
47
|
+
def load_data(raw: pl.LazyFrame) -> pl.DataFrame: ...
|
|
48
48
|
```
|
|
49
49
|
|
|
50
50
|
This convention also makes your code more readable and maintainable for human developers.
|
|
@@ -8,10 +8,12 @@ For instance, one may use the `metadata` parameter to mark a column as pseudonym
|
|
|
8
8
|
class UserSchema(dy.Schema):
|
|
9
9
|
id = dy.String(primary_key=True)
|
|
10
10
|
# Mark last name column as pseudonymized and (non-docstring) comment on it.
|
|
11
|
-
last_name = dy.String(
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
last_name = dy.String(
|
|
12
|
+
metadata={
|
|
13
|
+
"pseudonymized": True,
|
|
14
|
+
"comment": "Pseudonymized using cryptographic hash function",
|
|
15
|
+
}
|
|
16
|
+
)
|
|
15
17
|
# Add information about database column type.
|
|
16
18
|
address = dy.String(metadata={"database-type": "VARCHAR(MAX)"})
|
|
17
19
|
```
|
|
@@ -17,6 +17,7 @@ class InvoiceSchema(dy.Schema):
|
|
|
17
17
|
discharge_date = dy.Date(nullable=False)
|
|
18
18
|
amount = dy.Decimal(nullable=False)
|
|
19
19
|
|
|
20
|
+
|
|
20
21
|
# Get data frame with correct type hint.
|
|
21
22
|
df: dy.DataFrame[InvoiceSchema] = InvoiceSchema.create_empty()
|
|
22
23
|
```
|
|
@@ -34,6 +35,7 @@ class InvoiceSchema(dy.Schema):
|
|
|
34
35
|
discharge_date = dy.Date(nullable=False)
|
|
35
36
|
amount = dy.Decimal(nullable=False)
|
|
36
37
|
|
|
38
|
+
|
|
37
39
|
df: dy.DataFrame[InvoiceSchema] = InvoiceSchema.sample(num_rows=100)
|
|
38
40
|
```
|
|
39
41
|
|
|
@@ -54,6 +56,7 @@ class InvoiceSchema(dy.Schema):
|
|
|
54
56
|
def discharge_after_admission(cls) -> pl.Expr:
|
|
55
57
|
return InvoiceSchema.discharge_date.col >= InvoiceSchema.admission_date.col
|
|
56
58
|
|
|
59
|
+
|
|
57
60
|
# `@dy.rule`s will be respected as well for data generation.
|
|
58
61
|
df: dy.DataFrame[InvoiceSchema] = InvoiceSchema.sample(num_rows=100)
|
|
59
62
|
```
|
|
@@ -79,25 +82,30 @@ The column-wise specification specifies an iterable of values for each specified
|
|
|
79
82
|
from datetime import date
|
|
80
83
|
|
|
81
84
|
# Override values for specific columns.
|
|
82
|
-
df: dy.DataFrame[InvoiceSchema] = InvoiceSchema.sample(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
85
|
+
df: dy.DataFrame[InvoiceSchema] = InvoiceSchema.sample(
|
|
86
|
+
overrides={
|
|
87
|
+
# Use either <schema>.<column>.name or just the column name as a string.
|
|
88
|
+
InvoiceSchema.invoice_id.name: ["1234567890", "2345678901", "3456789012"],
|
|
89
|
+
# Dataframely will automatically infer the number of rows based on the longest given
|
|
90
|
+
# sequence of values and broadcast all other columns to that shape.
|
|
91
|
+
"admission_date": date(2025, 1, 1),
|
|
92
|
+
}
|
|
93
|
+
)
|
|
89
94
|
```
|
|
90
95
|
|
|
91
96
|
The row-wise specification implements an iterable of mappings for the rows that should be sampled. It is particularly helpful if you want to make it easy to understand how values will be combined in specific rows (e.g., when each row represents one object).
|
|
92
97
|
|
|
93
98
|
```python
|
|
94
99
|
from datetime import date
|
|
100
|
+
|
|
95
101
|
# Override values for specific columns.
|
|
96
|
-
df: dy.DataFrame[InvoiceSchema] = InvoiceSchema.sample(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
102
|
+
df: dy.DataFrame[InvoiceSchema] = InvoiceSchema.sample(
|
|
103
|
+
overrides=[
|
|
104
|
+
{"invoice_id": "1234567890", "admission_date": date(2025, 1, 1)},
|
|
105
|
+
{"invoice_id": "2345678901", "admission_date": date(2025, 1, 1)},
|
|
106
|
+
{"invoice_id": "3456789012", "admission_date": date(2025, 1, 1)},
|
|
107
|
+
]
|
|
108
|
+
)
|
|
101
109
|
```
|
|
102
110
|
|
|
103
111
|
### Providing custom column overrides
|
|
@@ -107,6 +115,8 @@ Complex validation rules (such as dependencies between columns or ordering crite
|
|
|
107
115
|
```python
|
|
108
116
|
import polars as pl
|
|
109
117
|
import dataframely as dy
|
|
118
|
+
|
|
119
|
+
|
|
110
120
|
class OrderedSchema(dy.Schema):
|
|
111
121
|
"""A schema that requires `iter` to be ordered with respect to `a` and `b`."""
|
|
112
122
|
|
|
@@ -116,7 +126,9 @@ class OrderedSchema(dy.Schema):
|
|
|
116
126
|
|
|
117
127
|
@dy.rule()
|
|
118
128
|
def iter_order_correct(cls) -> pl.Expr:
|
|
119
|
-
return pl.col("iter").rank(method="ordinal") == pl.struct(
|
|
129
|
+
return pl.col("iter").rank(method="ordinal") == pl.struct(
|
|
130
|
+
pl.col("a"), pl.col("b")
|
|
131
|
+
).rank(method="ordinal")
|
|
120
132
|
|
|
121
133
|
@classmethod
|
|
122
134
|
def _sampling_overrides(cls) -> dict[str, pl.Expr]:
|
|
@@ -124,6 +136,7 @@ class OrderedSchema(dy.Schema):
|
|
|
124
136
|
"iter": pl.struct(pl.col("a"), pl.col("b")).rank(method="ordinal"),
|
|
125
137
|
}
|
|
126
138
|
|
|
139
|
+
|
|
127
140
|
result = OrderedSchema.sample(100)
|
|
128
141
|
```
|
|
129
142
|
|
|
@@ -137,10 +150,12 @@ class DiagnosisSchema(dy.Schema):
|
|
|
137
150
|
invoice_id = dy.String(primary_key=True)
|
|
138
151
|
code = dy.String(nullable=False, regex=r"[A-Z][0-9]{2,4}")
|
|
139
152
|
|
|
153
|
+
|
|
140
154
|
class HospitalInvoiceData(dy.Collection):
|
|
141
155
|
invoice: dy.LazyFrame[InvoiceSchema]
|
|
142
156
|
diagnosis: dy.LazyFrame[DiagnosisSchema]
|
|
143
157
|
|
|
158
|
+
|
|
144
159
|
invoice_data: HospitalInvoiceData = HospitalInvoiceData.sample(num_rows=10)
|
|
145
160
|
```
|
|
146
161
|
|
|
@@ -153,6 +168,7 @@ class DiagnosisSchema(dy.Schema):
|
|
|
153
168
|
invoice_id = dy.String(primary_key=True)
|
|
154
169
|
code = dy.String(primary_key=True, regex=r"[A-Z][0-9]{2,4}")
|
|
155
170
|
|
|
171
|
+
|
|
156
172
|
class HospitalInvoiceData(dy.Collection):
|
|
157
173
|
invoice: dy.LazyFrame[InvoiceSchema]
|
|
158
174
|
diagnosis: dy.LazyFrame[DiagnosisSchema]
|
|
@@ -190,7 +206,9 @@ class HospitalInvoiceData(dy.Collection):
|
|
|
190
206
|
|
|
191
207
|
@classmethod
|
|
192
208
|
@override
|
|
193
|
-
def _preprocess_sample(
|
|
209
|
+
def _preprocess_sample(
|
|
210
|
+
cls, sample: dict[str, Any], index: int, generator: Generator
|
|
211
|
+
):
|
|
194
212
|
# Set common primary key.
|
|
195
213
|
if "invoice_id" not in sample:
|
|
196
214
|
sample["invoice_id"] = str(index)
|
|
@@ -65,3 +65,14 @@ failure.write_parquet("failures.parquet")
|
|
|
65
65
|
# ...and read it back.
|
|
66
66
|
failure = dy.FailureInfo.read_parquet("failures.parquet")
|
|
67
67
|
```
|
|
68
|
+
|
|
69
|
+
By default, all rule-output columns are persisted. To create a narrower parquet file
|
|
70
|
+
for debugging, keep only rule columns that contain at least one validation failure:
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
failure.write_parquet("failures.parquet", only_failing_rules=True)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
All data columns present in the failure information are still written. This reduced
|
|
77
|
+
representation is intentionally lossy: rule columns containing only successful or
|
|
78
|
+
unknown outcomes are omitted and unavailable after reading the file.
|