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.
Files changed (218) hide show
  1. {dataframely-3.0.0 → dataframely-3.1.0}/.github/workflows/build.yml +3 -4
  2. {dataframely-3.0.0 → dataframely-3.1.0}/.github/workflows/chore.yml +1 -1
  3. {dataframely-3.0.0 → dataframely-3.1.0}/.github/workflows/ci.yml +5 -5
  4. {dataframely-3.0.0 → dataframely-3.1.0}/.github/workflows/copilot-setup-steps.yml +2 -2
  5. {dataframely-3.0.0 → dataframely-3.1.0}/.github/workflows/nightly.yml +1 -1
  6. {dataframely-3.0.0 → dataframely-3.1.0}/.github/workflows/release-drafter.yml +1 -1
  7. {dataframely-3.0.0 → dataframely-3.1.0}/.github/workflows/scorecard.yml +2 -2
  8. {dataframely-3.0.0 → dataframely-3.1.0}/.lefthook.yaml +1 -1
  9. {dataframely-3.0.0 → dataframely-3.1.0}/Cargo.lock +14 -14
  10. {dataframely-3.0.0 → dataframely-3.1.0}/PKG-INFO +10 -8
  11. {dataframely-3.0.0 → dataframely-3.1.0}/README.md +9 -7
  12. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/_base_schema.py +2 -4
  13. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/columns/_base.py +5 -0
  14. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/columns/array.py +6 -0
  15. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/columns/categorical.py +3 -0
  16. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/columns/list.py +6 -0
  17. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/columns/struct.py +7 -0
  18. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/filter_result.py +24 -4
  19. {dataframely-3.0.0 → dataframely-3.1.0}/docs/guides/coding-agents.md +6 -6
  20. {dataframely-3.0.0 → dataframely-3.1.0}/docs/guides/features/column-metadata.md +6 -4
  21. {dataframely-3.0.0 → dataframely-3.1.0}/docs/guides/features/data-generation.md +32 -14
  22. {dataframely-3.0.0 → dataframely-3.1.0}/docs/guides/features/serialization.md +11 -0
  23. {dataframely-3.0.0 → dataframely-3.1.0}/docs/guides/features/sql-generation.md +1 -5
  24. {dataframely-3.0.0 → dataframely-3.1.0}/docs/guides/migration/v1-v2.md +2 -4
  25. {dataframely-3.0.0 → dataframely-3.1.0}/docs/guides/migration/v2-v3.md +3 -0
  26. {dataframely-3.0.0 → dataframely-3.1.0}/docs/guides/quickstart.md +18 -16
  27. {dataframely-3.0.0 → dataframely-3.1.0}/pixi.lock +15596 -14331
  28. {dataframely-3.0.0 → dataframely-3.1.0}/pixi.toml +1 -0
  29. {dataframely-3.0.0 → dataframely-3.1.0}/pyproject.toml +1 -1
  30. {dataframely-3.0.0 → dataframely-3.1.0}/skills/SKILL.md +13 -9
  31. {dataframely-3.0.0 → dataframely-3.1.0}/tests/column_types/test_categorical.py +30 -8
  32. dataframely-3.1.0/tests/failure_info/test_parquet.py +199 -0
  33. {dataframely-3.0.0 → dataframely-3.1.0}/tests/schema/test_validate.py +5 -2
  34. dataframely-3.0.0/tests/failure_info/test_parquet.py +0 -82
  35. {dataframely-3.0.0 → dataframely-3.1.0}/.claude/CLAUDE.md +0 -0
  36. {dataframely-3.0.0 → dataframely-3.1.0}/.copier-answers.yml +0 -0
  37. {dataframely-3.0.0 → dataframely-3.1.0}/.envrc +0 -0
  38. {dataframely-3.0.0 → dataframely-3.1.0}/.gitattributes +0 -0
  39. {dataframely-3.0.0 → dataframely-3.1.0}/.github/CODEOWNERS +0 -0
  40. {dataframely-3.0.0 → dataframely-3.1.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  41. {dataframely-3.0.0 → dataframely-3.1.0}/.github/copilot-instructions.md +0 -0
  42. {dataframely-3.0.0 → dataframely-3.1.0}/.github/dependabot.yml +0 -0
  43. {dataframely-3.0.0 → dataframely-3.1.0}/.github/instructions/tests.instructions.md +0 -0
  44. {dataframely-3.0.0 → dataframely-3.1.0}/.github/release-drafter.yml +0 -0
  45. {dataframely-3.0.0 → dataframely-3.1.0}/.gitignore +0 -0
  46. {dataframely-3.0.0 → dataframely-3.1.0}/.prettierignore +0 -0
  47. {dataframely-3.0.0 → dataframely-3.1.0}/.prettierrc +0 -0
  48. {dataframely-3.0.0 → dataframely-3.1.0}/.readthedocs.yml +0 -0
  49. {dataframely-3.0.0 → dataframely-3.1.0}/AGENTS.md +0 -0
  50. {dataframely-3.0.0 → dataframely-3.1.0}/Cargo.toml +0 -0
  51. {dataframely-3.0.0 → dataframely-3.1.0}/LICENSE +0 -0
  52. {dataframely-3.0.0 → dataframely-3.1.0}/SECURITY.md +0 -0
  53. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/__init__.py +0 -0
  54. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/_compat.py +0 -0
  55. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/_deprecation.py +0 -0
  56. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/_filter.py +0 -0
  57. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/_match_to_schema.py +0 -0
  58. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/_native.pyi +0 -0
  59. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/_plugin.py +0 -0
  60. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/_polars.py +0 -0
  61. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/_pydantic.py +0 -0
  62. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/_rule.py +0 -0
  63. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/_typing.py +0 -0
  64. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/collection/__init__.py +0 -0
  65. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/collection/_base.py +0 -0
  66. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/collection/collection.py +0 -0
  67. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/collection/filter_result.py +0 -0
  68. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/columns/__init__.py +0 -0
  69. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/columns/_mixins.py +0 -0
  70. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/columns/_utils.py +0 -0
  71. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/columns/any.py +0 -0
  72. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/columns/binary.py +0 -0
  73. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/columns/bool.py +0 -0
  74. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/columns/datetime.py +0 -0
  75. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/columns/decimal.py +0 -0
  76. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/columns/enum.py +0 -0
  77. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/columns/float.py +0 -0
  78. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/columns/integer.py +0 -0
  79. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/columns/object.py +0 -0
  80. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/columns/string.py +0 -0
  81. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/config.py +0 -0
  82. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/exc.py +0 -0
  83. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/experimental/__init__.py +0 -0
  84. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/experimental/infer_schema.py +0 -0
  85. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/functional.py +0 -0
  86. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/py.typed +0 -0
  87. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/random.py +0 -0
  88. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/schema.py +0 -0
  89. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/testing/__init__.py +0 -0
  90. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/testing/const.py +0 -0
  91. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/testing/factory.py +0 -0
  92. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/testing/mask.py +0 -0
  93. {dataframely-3.0.0 → dataframely-3.1.0}/dataframely/testing/rules.py +0 -0
  94. {dataframely-3.0.0 → dataframely-3.1.0}/docker-compose.yml +0 -0
  95. {dataframely-3.0.0 → dataframely-3.1.0}/docs/_static/custom.css +0 -0
  96. {dataframely-3.0.0 → dataframely-3.1.0}/docs/_static/favicon.ico +0 -0
  97. {dataframely-3.0.0 → dataframely-3.1.0}/docs/_templates/autosummary/class.rst +0 -0
  98. {dataframely-3.0.0 → dataframely-3.1.0}/docs/_templates/autosummary/method.rst +0 -0
  99. {dataframely-3.0.0 → dataframely-3.1.0}/docs/_templates/classes/column.rst +0 -0
  100. {dataframely-3.0.0 → dataframely-3.1.0}/docs/_templates/classes/error.rst +0 -0
  101. {dataframely-3.0.0 → dataframely-3.1.0}/docs/_templates/classes/filter_result.rst +0 -0
  102. {dataframely-3.0.0 → dataframely-3.1.0}/docs/api/collection/generation.rst +0 -0
  103. {dataframely-3.0.0 → dataframely-3.1.0}/docs/api/collection/index.rst +0 -0
  104. {dataframely-3.0.0 → dataframely-3.1.0}/docs/api/collection/io.rst +0 -0
  105. {dataframely-3.0.0 → dataframely-3.1.0}/docs/api/collection/metadata.rst +0 -0
  106. {dataframely-3.0.0 → dataframely-3.1.0}/docs/api/collection/operations.rst +0 -0
  107. {dataframely-3.0.0 → dataframely-3.1.0}/docs/api/collection/validation.rst +0 -0
  108. {dataframely-3.0.0 → dataframely-3.1.0}/docs/api/columns/index.rst +0 -0
  109. {dataframely-3.0.0 → dataframely-3.1.0}/docs/api/errors/index.rst +0 -0
  110. {dataframely-3.0.0 → dataframely-3.1.0}/docs/api/experimental/index.rst +0 -0
  111. {dataframely-3.0.0 → dataframely-3.1.0}/docs/api/filter_result/failure_info.rst +0 -0
  112. {dataframely-3.0.0 → dataframely-3.1.0}/docs/api/filter_result/index.rst +0 -0
  113. {dataframely-3.0.0 → dataframely-3.1.0}/docs/api/index.rst +0 -0
  114. {dataframely-3.0.0 → dataframely-3.1.0}/docs/api/misc/index.rst +0 -0
  115. {dataframely-3.0.0 → dataframely-3.1.0}/docs/api/schema/conversion.rst +0 -0
  116. {dataframely-3.0.0 → dataframely-3.1.0}/docs/api/schema/generation.rst +0 -0
  117. {dataframely-3.0.0 → dataframely-3.1.0}/docs/api/schema/index.rst +0 -0
  118. {dataframely-3.0.0 → dataframely-3.1.0}/docs/api/schema/metadata.rst +0 -0
  119. {dataframely-3.0.0 → dataframely-3.1.0}/docs/api/schema/validation.rst +0 -0
  120. {dataframely-3.0.0 → dataframely-3.1.0}/docs/conf.py +0 -0
  121. {dataframely-3.0.0 → dataframely-3.1.0}/docs/css/custom.css +0 -0
  122. {dataframely-3.0.0 → dataframely-3.1.0}/docs/guides/development.md +0 -0
  123. {dataframely-3.0.0 → dataframely-3.1.0}/docs/guides/examples/index.md +0 -0
  124. {dataframely-3.0.0 → dataframely-3.1.0}/docs/guides/examples/real-world.ipynb +0 -0
  125. {dataframely-3.0.0 → dataframely-3.1.0}/docs/guides/faq.md +0 -0
  126. {dataframely-3.0.0 → dataframely-3.1.0}/docs/guides/features/index.md +0 -0
  127. {dataframely-3.0.0 → dataframely-3.1.0}/docs/guides/features/lazy-validation.md +0 -0
  128. {dataframely-3.0.0 → dataframely-3.1.0}/docs/guides/features/primary-keys.md +0 -0
  129. {dataframely-3.0.0 → dataframely-3.1.0}/docs/guides/index.md +0 -0
  130. {dataframely-3.0.0 → dataframely-3.1.0}/docs/guides/migration/index.md +0 -0
  131. {dataframely-3.0.0 → dataframely-3.1.0}/docs/index.md +0 -0
  132. {dataframely-3.0.0 → dataframely-3.1.0}/rust-toolchain.toml +0 -0
  133. {dataframely-3.0.0 → dataframely-3.1.0}/src/arrow.rs +0 -0
  134. {dataframely-3.0.0 → dataframely-3.1.0}/src/lib.rs +0 -0
  135. {dataframely-3.0.0 → dataframely-3.1.0}/src/polars_plugin/mod.rs +0 -0
  136. {dataframely-3.0.0 → dataframely-3.1.0}/src/polars_plugin/rule_failure.rs +0 -0
  137. {dataframely-3.0.0 → dataframely-3.1.0}/src/polars_plugin/utils.rs +0 -0
  138. {dataframely-3.0.0 → dataframely-3.1.0}/src/polars_plugin/validation_error.rs +0 -0
  139. {dataframely-3.0.0 → dataframely-3.1.0}/src/regex/errdefs.rs +0 -0
  140. {dataframely-3.0.0 → dataframely-3.1.0}/src/regex/mod.rs +0 -0
  141. {dataframely-3.0.0 → dataframely-3.1.0}/src/regex/repr.rs +0 -0
  142. {dataframely-3.0.0 → dataframely-3.1.0}/tests/benches/conftest.py +0 -0
  143. {dataframely-3.0.0 → dataframely-3.1.0}/tests/benches/test_collection.py +0 -0
  144. {dataframely-3.0.0 → dataframely-3.1.0}/tests/benches/test_failure.py +0 -0
  145. {dataframely-3.0.0 → dataframely-3.1.0}/tests/benches/test_schema.py +0 -0
  146. {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_base.py +0 -0
  147. {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_cast.py +0 -0
  148. {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_collection_future_annotations.py +0 -0
  149. {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_create_empty.py +0 -0
  150. {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_dataframe_members.py +0 -0
  151. {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_filter_one_to_n.py +0 -0
  152. {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_filter_validate.py +0 -0
  153. {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_ignore_in_filter.py +0 -0
  154. {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_implementation.py +0 -0
  155. {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_join.py +0 -0
  156. {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_matches.py +0 -0
  157. {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_optional_members.py +0 -0
  158. {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_parquet.py +0 -0
  159. {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_pipe.py +0 -0
  160. {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_propagate_row_failures.py +0 -0
  161. {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_repr.py +0 -0
  162. {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_sample.py +0 -0
  163. {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_skip_member_validation.py +0 -0
  164. {dataframely-3.0.0 → dataframely-3.1.0}/tests/collection/test_validate_input.py +0 -0
  165. {dataframely-3.0.0 → dataframely-3.1.0}/tests/column_types/__init__.py +0 -0
  166. {dataframely-3.0.0 → dataframely-3.1.0}/tests/column_types/test_any.py +0 -0
  167. {dataframely-3.0.0 → dataframely-3.1.0}/tests/column_types/test_array.py +0 -0
  168. {dataframely-3.0.0 → dataframely-3.1.0}/tests/column_types/test_binary.py +0 -0
  169. {dataframely-3.0.0 → dataframely-3.1.0}/tests/column_types/test_datetime.py +0 -0
  170. {dataframely-3.0.0 → dataframely-3.1.0}/tests/column_types/test_decimal.py +0 -0
  171. {dataframely-3.0.0 → dataframely-3.1.0}/tests/column_types/test_enum.py +0 -0
  172. {dataframely-3.0.0 → dataframely-3.1.0}/tests/column_types/test_float.py +0 -0
  173. {dataframely-3.0.0 → dataframely-3.1.0}/tests/column_types/test_integer.py +0 -0
  174. {dataframely-3.0.0 → dataframely-3.1.0}/tests/column_types/test_list.py +0 -0
  175. {dataframely-3.0.0 → dataframely-3.1.0}/tests/column_types/test_object.py +0 -0
  176. {dataframely-3.0.0 → dataframely-3.1.0}/tests/column_types/test_string.py +0 -0
  177. {dataframely-3.0.0 → dataframely-3.1.0}/tests/column_types/test_struct.py +0 -0
  178. {dataframely-3.0.0 → dataframely-3.1.0}/tests/columns/__init__.py +0 -0
  179. {dataframely-3.0.0 → dataframely-3.1.0}/tests/columns/test_alias.py +0 -0
  180. {dataframely-3.0.0 → dataframely-3.1.0}/tests/columns/test_arrow_pycapsule.py +0 -0
  181. {dataframely-3.0.0 → dataframely-3.1.0}/tests/columns/test_base.py +0 -0
  182. {dataframely-3.0.0 → dataframely-3.1.0}/tests/columns/test_check.py +0 -0
  183. {dataframely-3.0.0 → dataframely-3.1.0}/tests/columns/test_default_dtypes.py +0 -0
  184. {dataframely-3.0.0 → dataframely-3.1.0}/tests/columns/test_description.py +0 -0
  185. {dataframely-3.0.0 → dataframely-3.1.0}/tests/columns/test_matches.py +0 -0
  186. {dataframely-3.0.0 → dataframely-3.1.0}/tests/columns/test_metadata.py +0 -0
  187. {dataframely-3.0.0 → dataframely-3.1.0}/tests/columns/test_polars_schema.py +0 -0
  188. {dataframely-3.0.0 → dataframely-3.1.0}/tests/columns/test_pydantic.py +0 -0
  189. {dataframely-3.0.0 → dataframely-3.1.0}/tests/columns/test_rules.py +0 -0
  190. {dataframely-3.0.0 → dataframely-3.1.0}/tests/columns/test_sample.py +0 -0
  191. {dataframely-3.0.0 → dataframely-3.1.0}/tests/columns/test_sqlalchemy_columns.py +0 -0
  192. {dataframely-3.0.0 → dataframely-3.1.0}/tests/columns/test_str.py +0 -0
  193. {dataframely-3.0.0 → dataframely-3.1.0}/tests/columns/test_utils.py +0 -0
  194. {dataframely-3.0.0 → dataframely-3.1.0}/tests/core_validation/__init__.py +0 -0
  195. {dataframely-3.0.0 → dataframely-3.1.0}/tests/core_validation/test_match_to_schema.py +0 -0
  196. {dataframely-3.0.0 → dataframely-3.1.0}/tests/core_validation/test_rule_evaluation.py +0 -0
  197. {dataframely-3.0.0 → dataframely-3.1.0}/tests/experimental/test_infer_schema.py +0 -0
  198. {dataframely-3.0.0 → dataframely-3.1.0}/tests/functional/test_concat.py +0 -0
  199. {dataframely-3.0.0 → dataframely-3.1.0}/tests/functional/test_relationships.py +0 -0
  200. {dataframely-3.0.0 → dataframely-3.1.0}/tests/schema/test_base.py +0 -0
  201. {dataframely-3.0.0 → dataframely-3.1.0}/tests/schema/test_cast.py +0 -0
  202. {dataframely-3.0.0 → dataframely-3.1.0}/tests/schema/test_create_empty.py +0 -0
  203. {dataframely-3.0.0 → dataframely-3.1.0}/tests/schema/test_create_empty_if_none.py +0 -0
  204. {dataframely-3.0.0 → dataframely-3.1.0}/tests/schema/test_filter.py +0 -0
  205. {dataframely-3.0.0 → dataframely-3.1.0}/tests/schema/test_inheritance.py +0 -0
  206. {dataframely-3.0.0 → dataframely-3.1.0}/tests/schema/test_matches.py +0 -0
  207. {dataframely-3.0.0 → dataframely-3.1.0}/tests/schema/test_pydantic_model.py +0 -0
  208. {dataframely-3.0.0 → dataframely-3.1.0}/tests/schema/test_repr.py +0 -0
  209. {dataframely-3.0.0 → dataframely-3.1.0}/tests/schema/test_rule_implementation.py +0 -0
  210. {dataframely-3.0.0 → dataframely-3.1.0}/tests/schema/test_sample.py +0 -0
  211. {dataframely-3.0.0 → dataframely-3.1.0}/tests/test_compat.py +0 -0
  212. {dataframely-3.0.0 → dataframely-3.1.0}/tests/test_config.py +0 -0
  213. {dataframely-3.0.0 → dataframely-3.1.0}/tests/test_deprecation.py +0 -0
  214. {dataframely-3.0.0 → dataframely-3.1.0}/tests/test_factory.py +0 -0
  215. {dataframely-3.0.0 → dataframely-3.1.0}/tests/test_native_regex.py +0 -0
  216. {dataframely-3.0.0 → dataframely-3.1.0}/tests/test_pydantic.py +0 -0
  217. {dataframely-3.0.0 → dataframely-3.1.0}/tests/test_random.py +0 -0
  218. {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@a09b6247153796b190642a2b53fac4241043cf6f # v0.10.0
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@a09b6247153796b190642a2b53fac4241043cf6f # v0.10.0
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@ba38be9e461d3875417946c167d0b5f3d385a247 # v1.14.1
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@eada3c96a64734dd381cfbda23511034e328ddb0 # v7.6.0
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@a09b6247153796b190642a2b53fac4241043cf6f # v0.10.0
27
+ uses: prefix-dev/setup-pixi@f00437f565399d418b0acc85936d12c1fb668347 # v0.10.1
28
28
  with:
29
- environments: default lint polars-minimal
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@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
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@a09b6247153796b190642a2b53fac4241043cf6f # v0.10.0
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@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
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@a09b6247153796b190642a2b53fac4241043cf6f # v0.10.0
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@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
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@a09b6247153796b190642a2b53fac4241043cf6f # v0.10.0
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@eada3c96a64734dd381cfbda23511034e328ddb0 # v7.6.0
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@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
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@e4fba868fa4b1b91e1fdab776edc8cfbe6e9fb81 # v4.37.3
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.0"
2281
+ version = "0.29.2"
2282
2282
  source = "registry+https://github.com/rust-lang/crates.io-index"
2283
- checksum = "cd274650b21d4bfc26a0a47587962c1edb425f69287324355cd040c3ea66071c"
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.0"
2295
+ version = "0.29.2"
2296
2296
  source = "registry+https://github.com/rust-lang/crates.io-index"
2297
- checksum = "c5e2a7d2f0d013342f295c048ad19237add5154a55b1c5a254c0ec93d4109078"
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.0"
2304
+ version = "0.29.2"
2305
2305
  source = "registry+https://github.com/rust-lang/crates.io-index"
2306
- checksum = "ca85c467da1bbc8d866eea5deff9cf29ea5f7785054a17da36e65bda9c05845b"
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.0"
2314
+ version = "0.29.2"
2315
2315
  source = "registry+https://github.com/rust-lang/crates.io-index"
2316
- checksum = "9ac53762fd065daa3194dd09337a38bd793a188100fd1a9304c4ab312d901771"
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.0"
2326
+ version = "0.29.2"
2327
2327
  source = "registry+https://github.com/rust-lang/crates.io-index"
2328
- checksum = "4ca3a1557399783172dc5bf39cfca835157732532cba56b71d2292161e53b362"
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.19"
3218
+ version = "2.0.20"
3219
3219
  source = "registry+https://github.com/rust-lang/crates.io-index"
3220
- checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9"
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.19"
3227
+ version = "2.0.20"
3228
3228
  source = "registry+https://github.com/rust-lang/crates.io-index"
3229
- checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd"
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.0.0
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
- "zip_code": ["01234", "01234", "1", "213", "123", "213"],
96
- "num_bedrooms": [2, 2, 1, None, None, 2],
97
- "num_bathrooms": [1, 2, 1, 1, 0, 8],
98
- "price": [100_000, 110_000, 50_000, 80_000, 60_000, 160_000]
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
- "zip_code": ["01234", "01234", "1", "213", "123", "213"],
72
- "num_bedrooms": [2, 2, 1, None, None, 2],
73
- "num_bathrooms": [1, 2, 1, 1, 0, 8],
74
- "price": [100_000, 110_000, 50_000, 80_000, 60_000, 160_000]
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._schema = f"{cls.__module__}:{cls.__name__}"
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]._schema = f"{cls.__module__}:{cls.__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(self, file: str | Path | IO[bytes], **kwargs: Any) -> None:
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.write_parquet(
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(self._rule_columns)},
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 -o .claude/skills/dataframely/SKILL.md https://raw.githubusercontent.com/Quantco/dataframely/refs/heads/main/skills/SKILL.md
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(raw: dy.LazyFrame[MyRawSchema]) -> dy.DataFrame[MyPreprocessedSchema]:
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(metadata={
12
- "pseudonymized": True,
13
- "comment": "Pseudonymized using cryptographic hash function"
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(overrides={
83
- # Use either <schema>.<column>.name or just the column name as a string.
84
- InvoiceSchema.invoice_id.name: ["1234567890", "2345678901", "3456789012"],
85
- # Dataframely will automatically infer the number of rows based on the longest given
86
- # sequence of values and broadcast all other columns to that shape.
87
- "admission_date": date(2025, 1, 1),
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(overrides=[
97
- {"invoice_id": "1234567890", "admission_date": date(2025, 1, 1)},
98
- {"invoice_id": "2345678901", "admission_date": date(2025, 1, 1)},
99
- {"invoice_id": "3456789012", "admission_date": date(2025, 1, 1)},
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(pl.col("a"), pl.col("b")).rank(method="ordinal")
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(cls, sample: dict[str, Any], index: int, generator: Generator):
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.