dataframely 2.11.0__tar.gz → 2.12.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 (232) hide show
  1. {dataframely-2.11.0 → dataframely-2.12.0}/PKG-INFO +1 -1
  2. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/_polars.py +14 -5
  3. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/collection/collection.py +99 -32
  4. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/functional.py +2 -2
  5. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/schema.py +21 -13
  6. {dataframely-2.11.0 → dataframely-2.12.0}/docs/api/collection/operations.rst +1 -0
  7. {dataframely-2.11.0 → dataframely-2.12.0}/pyproject.toml +1 -1
  8. {dataframely-2.11.0 → dataframely-2.12.0}/tests/benches/test_collection.py +12 -6
  9. dataframely-2.12.0/tests/collection/test_pipe.py +55 -0
  10. {dataframely-2.11.0 → dataframely-2.12.0}/.claude/CLAUDE.md +0 -0
  11. {dataframely-2.11.0 → dataframely-2.12.0}/.copier-answers.yml +0 -0
  12. {dataframely-2.11.0 → dataframely-2.12.0}/.envrc +0 -0
  13. {dataframely-2.11.0 → dataframely-2.12.0}/.gitattributes +0 -0
  14. {dataframely-2.11.0 → dataframely-2.12.0}/.github/CODEOWNERS +0 -0
  15. {dataframely-2.11.0 → dataframely-2.12.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  16. {dataframely-2.11.0 → dataframely-2.12.0}/.github/copilot-instructions.md +0 -0
  17. {dataframely-2.11.0 → dataframely-2.12.0}/.github/dependabot.yml +0 -0
  18. {dataframely-2.11.0 → dataframely-2.12.0}/.github/instructions/tests.instructions.md +0 -0
  19. {dataframely-2.11.0 → dataframely-2.12.0}/.github/release-drafter.yml +0 -0
  20. {dataframely-2.11.0 → dataframely-2.12.0}/.github/workflows/build.yml +0 -0
  21. {dataframely-2.11.0 → dataframely-2.12.0}/.github/workflows/chore.yml +0 -0
  22. {dataframely-2.11.0 → dataframely-2.12.0}/.github/workflows/ci.yml +0 -0
  23. {dataframely-2.11.0 → dataframely-2.12.0}/.github/workflows/copilot-setup-steps.yml +0 -0
  24. {dataframely-2.11.0 → dataframely-2.12.0}/.github/workflows/nightly.yml +0 -0
  25. {dataframely-2.11.0 → dataframely-2.12.0}/.github/workflows/release-drafter.yml +0 -0
  26. {dataframely-2.11.0 → dataframely-2.12.0}/.github/workflows/scorecard.yml +0 -0
  27. {dataframely-2.11.0 → dataframely-2.12.0}/.gitignore +0 -0
  28. {dataframely-2.11.0 → dataframely-2.12.0}/.lefthook.yaml +0 -0
  29. {dataframely-2.11.0 → dataframely-2.12.0}/.prettierignore +0 -0
  30. {dataframely-2.11.0 → dataframely-2.12.0}/.prettierrc +0 -0
  31. {dataframely-2.11.0 → dataframely-2.12.0}/.readthedocs.yml +0 -0
  32. {dataframely-2.11.0 → dataframely-2.12.0}/AGENTS.md +0 -0
  33. {dataframely-2.11.0 → dataframely-2.12.0}/Cargo.lock +0 -0
  34. {dataframely-2.11.0 → dataframely-2.12.0}/Cargo.toml +0 -0
  35. {dataframely-2.11.0 → dataframely-2.12.0}/LICENSE +0 -0
  36. {dataframely-2.11.0 → dataframely-2.12.0}/README.md +0 -0
  37. {dataframely-2.11.0 → dataframely-2.12.0}/SECURITY.md +0 -0
  38. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/__init__.py +0 -0
  39. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/_base_schema.py +0 -0
  40. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/_compat.py +0 -0
  41. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/_deprecation.py +0 -0
  42. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/_filter.py +0 -0
  43. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/_match_to_schema.py +0 -0
  44. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/_native.pyi +0 -0
  45. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/_plugin.py +0 -0
  46. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/_pydantic.py +0 -0
  47. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/_rule.py +0 -0
  48. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/_serialization.py +0 -0
  49. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/_storage/__init__.py +0 -0
  50. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/_storage/_base.py +0 -0
  51. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/_storage/_exc.py +0 -0
  52. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/_storage/_fsspec.py +0 -0
  53. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/_storage/constants.py +0 -0
  54. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/_storage/delta.py +0 -0
  55. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/_storage/parquet.py +0 -0
  56. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/_typing.py +0 -0
  57. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/collection/__init__.py +0 -0
  58. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/collection/_base.py +0 -0
  59. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/collection/filter_result.py +0 -0
  60. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/columns/__init__.py +0 -0
  61. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/columns/_base.py +0 -0
  62. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/columns/_mixins.py +0 -0
  63. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/columns/_registry.py +0 -0
  64. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/columns/_utils.py +0 -0
  65. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/columns/any.py +0 -0
  66. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/columns/array.py +0 -0
  67. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/columns/binary.py +0 -0
  68. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/columns/bool.py +0 -0
  69. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/columns/categorical.py +0 -0
  70. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/columns/datetime.py +0 -0
  71. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/columns/decimal.py +0 -0
  72. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/columns/enum.py +0 -0
  73. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/columns/float.py +0 -0
  74. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/columns/integer.py +0 -0
  75. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/columns/list.py +0 -0
  76. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/columns/object.py +0 -0
  77. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/columns/string.py +0 -0
  78. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/columns/struct.py +0 -0
  79. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/config.py +0 -0
  80. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/exc.py +0 -0
  81. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/experimental/__init__.py +0 -0
  82. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/experimental/infer_schema.py +0 -0
  83. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/filter_result.py +0 -0
  84. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/py.typed +0 -0
  85. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/random.py +0 -0
  86. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/testing/__init__.py +0 -0
  87. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/testing/const.py +0 -0
  88. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/testing/factory.py +0 -0
  89. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/testing/mask.py +0 -0
  90. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/testing/rules.py +0 -0
  91. {dataframely-2.11.0 → dataframely-2.12.0}/dataframely/testing/storage.py +0 -0
  92. {dataframely-2.11.0 → dataframely-2.12.0}/docker-compose.yml +0 -0
  93. {dataframely-2.11.0 → dataframely-2.12.0}/docs/_static/custom.css +0 -0
  94. {dataframely-2.11.0 → dataframely-2.12.0}/docs/_static/favicon.ico +0 -0
  95. {dataframely-2.11.0 → dataframely-2.12.0}/docs/_templates/autosummary/class.rst +0 -0
  96. {dataframely-2.11.0 → dataframely-2.12.0}/docs/_templates/autosummary/method.rst +0 -0
  97. {dataframely-2.11.0 → dataframely-2.12.0}/docs/_templates/classes/column.rst +0 -0
  98. {dataframely-2.11.0 → dataframely-2.12.0}/docs/_templates/classes/error.rst +0 -0
  99. {dataframely-2.11.0 → dataframely-2.12.0}/docs/_templates/classes/filter_result.rst +0 -0
  100. {dataframely-2.11.0 → dataframely-2.12.0}/docs/api/collection/generation.rst +0 -0
  101. {dataframely-2.11.0 → dataframely-2.12.0}/docs/api/collection/index.rst +0 -0
  102. {dataframely-2.11.0 → dataframely-2.12.0}/docs/api/collection/io.rst +0 -0
  103. {dataframely-2.11.0 → dataframely-2.12.0}/docs/api/collection/metadata.rst +0 -0
  104. {dataframely-2.11.0 → dataframely-2.12.0}/docs/api/collection/validation.rst +0 -0
  105. {dataframely-2.11.0 → dataframely-2.12.0}/docs/api/columns/index.rst +0 -0
  106. {dataframely-2.11.0 → dataframely-2.12.0}/docs/api/errors/index.rst +0 -0
  107. {dataframely-2.11.0 → dataframely-2.12.0}/docs/api/experimental/index.rst +0 -0
  108. {dataframely-2.11.0 → dataframely-2.12.0}/docs/api/filter_result/failure_info.rst +0 -0
  109. {dataframely-2.11.0 → dataframely-2.12.0}/docs/api/filter_result/index.rst +0 -0
  110. {dataframely-2.11.0 → dataframely-2.12.0}/docs/api/index.rst +0 -0
  111. {dataframely-2.11.0 → dataframely-2.12.0}/docs/api/misc/index.rst +0 -0
  112. {dataframely-2.11.0 → dataframely-2.12.0}/docs/api/schema/conversion.rst +0 -0
  113. {dataframely-2.11.0 → dataframely-2.12.0}/docs/api/schema/generation.rst +0 -0
  114. {dataframely-2.11.0 → dataframely-2.12.0}/docs/api/schema/index.rst +0 -0
  115. {dataframely-2.11.0 → dataframely-2.12.0}/docs/api/schema/io.rst +0 -0
  116. {dataframely-2.11.0 → dataframely-2.12.0}/docs/api/schema/metadata.rst +0 -0
  117. {dataframely-2.11.0 → dataframely-2.12.0}/docs/api/schema/validation.rst +0 -0
  118. {dataframely-2.11.0 → dataframely-2.12.0}/docs/conf.py +0 -0
  119. {dataframely-2.11.0 → dataframely-2.12.0}/docs/css/custom.css +0 -0
  120. {dataframely-2.11.0 → dataframely-2.12.0}/docs/guides/coding-agents.md +0 -0
  121. {dataframely-2.11.0 → dataframely-2.12.0}/docs/guides/development.md +0 -0
  122. {dataframely-2.11.0 → dataframely-2.12.0}/docs/guides/examples/index.md +0 -0
  123. {dataframely-2.11.0 → dataframely-2.12.0}/docs/guides/examples/real-world.ipynb +0 -0
  124. {dataframely-2.11.0 → dataframely-2.12.0}/docs/guides/faq.md +0 -0
  125. {dataframely-2.11.0 → dataframely-2.12.0}/docs/guides/features/column-metadata.md +0 -0
  126. {dataframely-2.11.0 → dataframely-2.12.0}/docs/guides/features/data-generation.md +0 -0
  127. {dataframely-2.11.0 → dataframely-2.12.0}/docs/guides/features/index.md +0 -0
  128. {dataframely-2.11.0 → dataframely-2.12.0}/docs/guides/features/lazy-validation.md +0 -0
  129. {dataframely-2.11.0 → dataframely-2.12.0}/docs/guides/features/primary-keys.md +0 -0
  130. {dataframely-2.11.0 → dataframely-2.12.0}/docs/guides/features/serialization.md +0 -0
  131. {dataframely-2.11.0 → dataframely-2.12.0}/docs/guides/features/sql-generation.md +0 -0
  132. {dataframely-2.11.0 → dataframely-2.12.0}/docs/guides/index.md +0 -0
  133. {dataframely-2.11.0 → dataframely-2.12.0}/docs/guides/migration/index.md +0 -0
  134. {dataframely-2.11.0 → dataframely-2.12.0}/docs/guides/migration/v1-v2.md +0 -0
  135. {dataframely-2.11.0 → dataframely-2.12.0}/docs/guides/quickstart.md +0 -0
  136. {dataframely-2.11.0 → dataframely-2.12.0}/docs/index.md +0 -0
  137. {dataframely-2.11.0 → dataframely-2.12.0}/pixi.lock +0 -0
  138. {dataframely-2.11.0 → dataframely-2.12.0}/pixi.toml +0 -0
  139. {dataframely-2.11.0 → dataframely-2.12.0}/rust-toolchain.toml +0 -0
  140. {dataframely-2.11.0 → dataframely-2.12.0}/skills/SKILL.md +0 -0
  141. {dataframely-2.11.0 → dataframely-2.12.0}/src/lib.rs +0 -0
  142. {dataframely-2.11.0 → dataframely-2.12.0}/src/polars_plugin/mod.rs +0 -0
  143. {dataframely-2.11.0 → dataframely-2.12.0}/src/polars_plugin/rule_failure.rs +0 -0
  144. {dataframely-2.11.0 → dataframely-2.12.0}/src/polars_plugin/utils.rs +0 -0
  145. {dataframely-2.11.0 → dataframely-2.12.0}/src/polars_plugin/validation_error.rs +0 -0
  146. {dataframely-2.11.0 → dataframely-2.12.0}/src/regex/errdefs.rs +0 -0
  147. {dataframely-2.11.0 → dataframely-2.12.0}/src/regex/mod.rs +0 -0
  148. {dataframely-2.11.0 → dataframely-2.12.0}/src/regex/repr.rs +0 -0
  149. {dataframely-2.11.0 → dataframely-2.12.0}/tests/benches/conftest.py +0 -0
  150. {dataframely-2.11.0 → dataframely-2.12.0}/tests/benches/test_failure.py +0 -0
  151. {dataframely-2.11.0 → dataframely-2.12.0}/tests/benches/test_schema.py +0 -0
  152. {dataframely-2.11.0 → dataframely-2.12.0}/tests/collection/test_base.py +0 -0
  153. {dataframely-2.11.0 → dataframely-2.12.0}/tests/collection/test_cast.py +0 -0
  154. {dataframely-2.11.0 → dataframely-2.12.0}/tests/collection/test_collection_future_annotations.py +0 -0
  155. {dataframely-2.11.0 → dataframely-2.12.0}/tests/collection/test_create_empty.py +0 -0
  156. {dataframely-2.11.0 → dataframely-2.12.0}/tests/collection/test_dataframe_members.py +0 -0
  157. {dataframely-2.11.0 → dataframely-2.12.0}/tests/collection/test_filter_one_to_n.py +0 -0
  158. {dataframely-2.11.0 → dataframely-2.12.0}/tests/collection/test_filter_validate.py +0 -0
  159. {dataframely-2.11.0 → dataframely-2.12.0}/tests/collection/test_ignore_in_filter.py +0 -0
  160. {dataframely-2.11.0 → dataframely-2.12.0}/tests/collection/test_implementation.py +0 -0
  161. {dataframely-2.11.0 → dataframely-2.12.0}/tests/collection/test_join.py +0 -0
  162. {dataframely-2.11.0 → dataframely-2.12.0}/tests/collection/test_matches.py +0 -0
  163. {dataframely-2.11.0 → dataframely-2.12.0}/tests/collection/test_optional_members.py +0 -0
  164. {dataframely-2.11.0 → dataframely-2.12.0}/tests/collection/test_propagate_row_failures.py +0 -0
  165. {dataframely-2.11.0 → dataframely-2.12.0}/tests/collection/test_repr.py +0 -0
  166. {dataframely-2.11.0 → dataframely-2.12.0}/tests/collection/test_sample.py +0 -0
  167. {dataframely-2.11.0 → dataframely-2.12.0}/tests/collection/test_serialization.py +0 -0
  168. {dataframely-2.11.0 → dataframely-2.12.0}/tests/collection/test_storage.py +0 -0
  169. {dataframely-2.11.0 → dataframely-2.12.0}/tests/collection/test_validate_input.py +0 -0
  170. {dataframely-2.11.0 → dataframely-2.12.0}/tests/column_types/__init__.py +0 -0
  171. {dataframely-2.11.0 → dataframely-2.12.0}/tests/column_types/test_any.py +0 -0
  172. {dataframely-2.11.0 → dataframely-2.12.0}/tests/column_types/test_array.py +0 -0
  173. {dataframely-2.11.0 → dataframely-2.12.0}/tests/column_types/test_binary.py +0 -0
  174. {dataframely-2.11.0 → dataframely-2.12.0}/tests/column_types/test_datetime.py +0 -0
  175. {dataframely-2.11.0 → dataframely-2.12.0}/tests/column_types/test_decimal.py +0 -0
  176. {dataframely-2.11.0 → dataframely-2.12.0}/tests/column_types/test_enum.py +0 -0
  177. {dataframely-2.11.0 → dataframely-2.12.0}/tests/column_types/test_float.py +0 -0
  178. {dataframely-2.11.0 → dataframely-2.12.0}/tests/column_types/test_integer.py +0 -0
  179. {dataframely-2.11.0 → dataframely-2.12.0}/tests/column_types/test_list.py +0 -0
  180. {dataframely-2.11.0 → dataframely-2.12.0}/tests/column_types/test_object.py +0 -0
  181. {dataframely-2.11.0 → dataframely-2.12.0}/tests/column_types/test_string.py +0 -0
  182. {dataframely-2.11.0 → dataframely-2.12.0}/tests/column_types/test_struct.py +0 -0
  183. {dataframely-2.11.0 → dataframely-2.12.0}/tests/columns/__init__.py +0 -0
  184. {dataframely-2.11.0 → dataframely-2.12.0}/tests/columns/test_alias.py +0 -0
  185. {dataframely-2.11.0 → dataframely-2.12.0}/tests/columns/test_base.py +0 -0
  186. {dataframely-2.11.0 → dataframely-2.12.0}/tests/columns/test_check.py +0 -0
  187. {dataframely-2.11.0 → dataframely-2.12.0}/tests/columns/test_default_dtypes.py +0 -0
  188. {dataframely-2.11.0 → dataframely-2.12.0}/tests/columns/test_description.py +0 -0
  189. {dataframely-2.11.0 → dataframely-2.12.0}/tests/columns/test_matches.py +0 -0
  190. {dataframely-2.11.0 → dataframely-2.12.0}/tests/columns/test_metadata.py +0 -0
  191. {dataframely-2.11.0 → dataframely-2.12.0}/tests/columns/test_polars_schema.py +0 -0
  192. {dataframely-2.11.0 → dataframely-2.12.0}/tests/columns/test_pyarrow.py +0 -0
  193. {dataframely-2.11.0 → dataframely-2.12.0}/tests/columns/test_pydantic.py +0 -0
  194. {dataframely-2.11.0 → dataframely-2.12.0}/tests/columns/test_rules.py +0 -0
  195. {dataframely-2.11.0 → dataframely-2.12.0}/tests/columns/test_sample.py +0 -0
  196. {dataframely-2.11.0 → dataframely-2.12.0}/tests/columns/test_sqlalchemy_columns.py +0 -0
  197. {dataframely-2.11.0 → dataframely-2.12.0}/tests/columns/test_str.py +0 -0
  198. {dataframely-2.11.0 → dataframely-2.12.0}/tests/columns/test_utils.py +0 -0
  199. {dataframely-2.11.0 → dataframely-2.12.0}/tests/conftest.py +0 -0
  200. {dataframely-2.11.0 → dataframely-2.12.0}/tests/core_validation/__init__.py +0 -0
  201. {dataframely-2.11.0 → dataframely-2.12.0}/tests/core_validation/test_match_to_schema.py +0 -0
  202. {dataframely-2.11.0 → dataframely-2.12.0}/tests/core_validation/test_rule_evaluation.py +0 -0
  203. {dataframely-2.11.0 → dataframely-2.12.0}/tests/experimental/test_infer_schema.py +0 -0
  204. {dataframely-2.11.0 → dataframely-2.12.0}/tests/failure_info/test_storage.py +0 -0
  205. {dataframely-2.11.0 → dataframely-2.12.0}/tests/functional/test_concat.py +0 -0
  206. {dataframely-2.11.0 → dataframely-2.12.0}/tests/functional/test_relationships.py +0 -0
  207. {dataframely-2.11.0 → dataframely-2.12.0}/tests/schema/test_base.py +0 -0
  208. {dataframely-2.11.0 → dataframely-2.12.0}/tests/schema/test_cast.py +0 -0
  209. {dataframely-2.11.0 → dataframely-2.12.0}/tests/schema/test_create_empty.py +0 -0
  210. {dataframely-2.11.0 → dataframely-2.12.0}/tests/schema/test_create_empty_if_none.py +0 -0
  211. {dataframely-2.11.0 → dataframely-2.12.0}/tests/schema/test_filter.py +0 -0
  212. {dataframely-2.11.0 → dataframely-2.12.0}/tests/schema/test_inheritance.py +0 -0
  213. {dataframely-2.11.0 → dataframely-2.12.0}/tests/schema/test_matches.py +0 -0
  214. {dataframely-2.11.0 → dataframely-2.12.0}/tests/schema/test_pydantic_model.py +0 -0
  215. {dataframely-2.11.0 → dataframely-2.12.0}/tests/schema/test_read_write_parquet.py +0 -0
  216. {dataframely-2.11.0 → dataframely-2.12.0}/tests/schema/test_repr.py +0 -0
  217. {dataframely-2.11.0 → dataframely-2.12.0}/tests/schema/test_rule_implementation.py +0 -0
  218. {dataframely-2.11.0 → dataframely-2.12.0}/tests/schema/test_sample.py +0 -0
  219. {dataframely-2.11.0 → dataframely-2.12.0}/tests/schema/test_serialization.py +0 -0
  220. {dataframely-2.11.0 → dataframely-2.12.0}/tests/schema/test_storage.py +0 -0
  221. {dataframely-2.11.0 → dataframely-2.12.0}/tests/schema/test_validate.py +0 -0
  222. {dataframely-2.11.0 → dataframely-2.12.0}/tests/storage/test_delta.py +0 -0
  223. {dataframely-2.11.0 → dataframely-2.12.0}/tests/storage/test_fsspec.py +0 -0
  224. {dataframely-2.11.0 → dataframely-2.12.0}/tests/test_compat.py +0 -0
  225. {dataframely-2.11.0 → dataframely-2.12.0}/tests/test_config.py +0 -0
  226. {dataframely-2.11.0 → dataframely-2.12.0}/tests/test_deprecation.py +0 -0
  227. {dataframely-2.11.0 → dataframely-2.12.0}/tests/test_factory.py +0 -0
  228. {dataframely-2.11.0 → dataframely-2.12.0}/tests/test_native_regex.py +0 -0
  229. {dataframely-2.11.0 → dataframely-2.12.0}/tests/test_pydantic.py +0 -0
  230. {dataframely-2.11.0 → dataframely-2.12.0}/tests/test_random.py +0 -0
  231. {dataframely-2.11.0 → dataframely-2.12.0}/tests/test_serialization.py +0 -0
  232. {dataframely-2.11.0 → dataframely-2.12.0}/tests/test_typing.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dataframely
3
- Version: 2.11.0
3
+ Version: 2.12.0
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: 3.10
6
6
  Classifier: Programming Language :: Python :: 3.11
@@ -2,7 +2,7 @@
2
2
  # SPDX-License-Identifier: BSD-3-Clause
3
3
 
4
4
  import datetime as dt
5
- from typing import TypeVar
5
+ from typing import Any, TypeVar
6
6
 
7
7
  import polars as pl
8
8
  from polars.datatypes import DataTypeClass
@@ -41,9 +41,18 @@ def timedelta_matches_resolution(d: dt.timedelta, resolution: str) -> bool:
41
41
  return datetime_matches_resolution(EPOCH_DATETIME + d, resolution)
42
42
 
43
43
 
44
- def collect_if(lf: pl.LazyFrame, condition: bool) -> pl.DataFrame | pl.LazyFrame:
45
- """Collect a lazy frame if the original was eager, otherwise return the lazy
46
- frame."""
44
+ def collect_if(lf: pl.LazyFrame, condition: bool, **kwargs: Any) -> pl.LazyFrame:
45
+ """Collect a lazy frame based on `condition`."""
47
46
  if condition:
48
- return lf.collect()
47
+ return lf.collect(**kwargs).lazy()
49
48
  return lf
49
+
50
+
51
+ def collect_all_if(
52
+ lfs: dict[str, pl.LazyFrame], condition: bool, **kwargs: Any
53
+ ) -> dict[str, pl.LazyFrame]:
54
+ """Collect the lazy frames in the dictionary based on `condition`."""
55
+ if condition:
56
+ dfs = pl.collect_all(lfs.values(), **kwargs)
57
+ return {k: v.lazy() for k, v in zip(lfs.keys(), dfs)}
58
+ return lfs
@@ -8,11 +8,21 @@ import sys
8
8
  import textwrap
9
9
  import warnings
10
10
  from abc import ABC
11
- from collections.abc import Iterable, Mapping, Sequence
11
+ from collections.abc import Callable, Iterable, Mapping, Sequence
12
12
  from dataclasses import asdict
13
13
  from json import JSONDecodeError
14
14
  from pathlib import Path
15
- from typing import IO, Annotated, Any, Literal, cast, overload
15
+ from typing import (
16
+ IO,
17
+ Annotated,
18
+ Any,
19
+ Concatenate,
20
+ Literal,
21
+ ParamSpec,
22
+ TypeVar,
23
+ cast,
24
+ overload,
25
+ )
16
26
 
17
27
  import polars as pl
18
28
  import polars.exceptions as plexc
@@ -21,7 +31,7 @@ from dataframely._compat import deltalake
21
31
  from dataframely._filter import Filter
22
32
  from dataframely._native import format_rule_failures
23
33
  from dataframely._plugin import all_rules_required
24
- from dataframely._polars import FrameType, collect_if
34
+ from dataframely._polars import FrameType, collect_all_if
25
35
  from dataframely._serialization import (
26
36
  SERIALIZATION_FORMAT_VERSION,
27
37
  SchemaJSONDecoder,
@@ -53,6 +63,9 @@ else:
53
63
 
54
64
  _FILTER_COLUMN_PREFIX = "__DATAFRAMELY_FILTER_COLUMN__"
55
65
 
66
+ P = ParamSpec("P")
67
+ T = TypeVar("T")
68
+
56
69
 
57
70
  class Collection(BaseCollection, ABC):
58
71
  """Base class for all collections of data frames with a predefined schema.
@@ -367,7 +380,13 @@ class Collection(BaseCollection, ABC):
367
380
 
368
381
  @classmethod
369
382
  def validate(
370
- cls, data: Mapping[str, FrameType], /, *, cast: bool = False, eager: bool = True
383
+ cls,
384
+ data: Mapping[str, FrameType],
385
+ /,
386
+ *,
387
+ cast: bool = False,
388
+ eager: bool = True,
389
+ **kwargs: Any,
371
390
  ) -> Self:
372
391
  """Validate that a set of data frames satisfy the collection's invariants.
373
392
 
@@ -387,6 +406,8 @@ class Collection(BaseCollection, ABC):
387
406
  :meth:`~polars.LazyFrame.collect` on the individual member or
388
407
  :meth:`collect_all` on the collection. Note that, in the latter case,
389
408
  information from error messages is limited.
409
+ kwargs: Keyword arguments passed directly to :meth:`polars.collect_all` and
410
+ :meth:`polars.LazyFrame.collect` when `eager=True`.
390
411
 
391
412
  Raises:
392
413
  ValueError: If an insufficient set of input data frames is provided, i.e. if
@@ -408,7 +429,7 @@ class Collection(BaseCollection, ABC):
408
429
  if eager:
409
430
  # If we perform the validation eagerly, we call filter and check the failure
410
431
  # information to properly construct a useful error message.
411
- filtered, failures = cls.filter(data, cast=cast, eager=True)
432
+ filtered, failures = cls.filter(data, cast=cast, eager=True, **kwargs)
412
433
  if any(len(failure) > 0 for failure in failures.values()):
413
434
  errors: dict[str, str] = {}
414
435
  for member, failure in failures.items():
@@ -476,7 +497,9 @@ class Collection(BaseCollection, ABC):
476
497
  return cls._init(members)
477
498
 
478
499
  @classmethod
479
- def is_valid(cls, data: Mapping[str, FrameType], /, *, cast: bool = False) -> bool:
500
+ def is_valid(
501
+ cls, data: Mapping[str, FrameType], /, *, cast: bool = False, **kwargs: Any
502
+ ) -> bool:
480
503
  """Utility method to check whether :meth:`validate` raises an exception.
481
504
 
482
505
  Args:
@@ -485,6 +508,8 @@ class Collection(BaseCollection, ABC):
485
508
  the member as key.
486
509
  cast: Whether columns with a wrong data type in the member data frame are
487
510
  cast to their schemas' defined data types if possible.
511
+ kwargs: Keyword arguments passed directly to :meth:`polars.collect_all` and
512
+ :meth:`polars.LazyFrame.collect`.
488
513
 
489
514
  Returns:
490
515
  Whether the provided members satisfy the invariants of the collection.
@@ -499,7 +524,7 @@ class Collection(BaseCollection, ABC):
499
524
  members: dict[str, pl.LazyFrame] = {}
500
525
  for member, schema in cls.member_schemas().items():
501
526
  if member in data:
502
- if not schema.is_valid(data[member], cast=cast):
527
+ if not schema.is_valid(data[member], cast=cast, **kwargs):
503
528
  return False
504
529
  members[member] = data[member].lazy()
505
530
 
@@ -510,9 +535,12 @@ class Collection(BaseCollection, ABC):
510
535
  keep = [filter.logic(result_cls).select(primary_key) for filter in filters]
511
536
  joined = _join_all(*keep, on=primary_key, how="inner")
512
537
  removed_rows = pl.collect_all(
513
- data[member].lazy().join(joined, on=primary_key, how="anti")
514
- for member in cls.members()
515
- if member in data
538
+ (
539
+ data[member].lazy().join(joined, on=primary_key, how="anti")
540
+ for member in cls.members()
541
+ if member in data
542
+ ),
543
+ **kwargs,
516
544
  )
517
545
  return all(df.is_empty() for df in removed_rows)
518
546
 
@@ -522,7 +550,13 @@ class Collection(BaseCollection, ABC):
522
550
 
523
551
  @classmethod
524
552
  def filter(
525
- cls, data: Mapping[str, FrameType], /, *, cast: bool = False, eager: bool = True
553
+ cls,
554
+ data: Mapping[str, FrameType],
555
+ /,
556
+ *,
557
+ cast: bool = False,
558
+ eager: bool = True,
559
+ **kwargs: Any,
526
560
  ) -> CollectionFilterResult[Self]:
527
561
  """Filter the members data frame by their schemas and the collection's filters.
528
562
 
@@ -538,6 +572,8 @@ class Collection(BaseCollection, ABC):
538
572
  eager: Whether the filter operation should be performed eagerly.
539
573
  Note that until https://github.com/pola-rs/polars/pull/24129 is
540
574
  released, eagerly filtering can provide significant speedups.
575
+ kwargs: Keyword arguments passed directly to :meth:`polars.collect_all` and
576
+ :meth:`polars.LazyFrame.collect` when `eager=True`.
541
577
 
542
578
  Returns:
543
579
  A named tuple with fields `result` and `failure`. The `result` field
@@ -580,7 +616,7 @@ class Collection(BaseCollection, ABC):
580
616
  continue
581
617
 
582
618
  member_result, failures[member_name] = member.schema.filter(
583
- data[member_name].lazy(), cast=cast, eager=eager
619
+ data[member_name].lazy(), cast=cast, eager=eager, **kwargs
584
620
  )
585
621
  results[member_name] = member_result.lazy()
586
622
 
@@ -592,28 +628,25 @@ class Collection(BaseCollection, ABC):
592
628
  result_cls = cls._init(results)
593
629
  primary_key = cls.common_primary_key()
594
630
 
595
- keep: dict[str, pl.LazyFrame] = {}
596
- for name, filter in filters.items():
597
- keep[name] = (
598
- filter.logic(result_cls)
599
- .select(primary_key)
600
- .pipe(collect_if, eager)
601
- .lazy()
602
- )
631
+ keep = {
632
+ name: filter.logic(result_cls).select(primary_key)
633
+ for name, filter in filters.items()
634
+ }
635
+ keep = collect_all_if(keep, eager, **kwargs)
603
636
 
604
- drop: dict[str, pl.LazyFrame] = {}
605
- for failure_propagating_member in failure_propagating_members:
606
- annotation_column = f"{failure_propagating_member}|failure_propagation"
607
- drop[annotation_column] = (
637
+ drop: dict[str, pl.LazyFrame] = {
638
+ f"{failure_propagating_member}|failure_propagation": (
608
639
  failures[failure_propagating_member]
609
640
  ._lf.select(primary_key)
610
641
  .unique()
611
- .pipe(collect_if, eager)
612
- .lazy()
613
642
  )
643
+ for failure_propagating_member in failure_propagating_members
644
+ }
645
+ drop = collect_all_if(drop, eager, **kwargs)
614
646
 
615
647
  # Now we can iterate over the results and left-join onto each individual
616
- # filter to obtain independent boolean indicators of whether to keep the row
648
+ # filter to obtain independent boolean indicators of whether to keep the row.
649
+ lfs_with_eval: dict[str, pl.LazyFrame] = {}
617
650
  for member_name, filtered in results.items():
618
651
  member_info = cls.members()[member_name]
619
652
  if member_info.ignored_in_filters:
@@ -635,7 +668,11 @@ class Collection(BaseCollection, ABC):
635
668
  maintain_order="left",
636
669
  ).with_columns(pl.col(name).fill_null(True))
637
670
 
638
- lf_with_eval = lf_with_eval.pipe(collect_if, eager).lazy()
671
+ lfs_with_eval[member_name] = lf_with_eval
672
+
673
+ lfs_with_eval = collect_all_if(lfs_with_eval, eager, **kwargs)
674
+ for member_name, lf_with_eval in lfs_with_eval.items():
675
+ member_info = cls.members()[member_name]
639
676
 
640
677
  # Filtering `lf_with_eval` by the rows for which all joins
641
678
  # "succeeded", we can identify the rows that pass all the filters. We
@@ -700,7 +737,7 @@ class Collection(BaseCollection, ABC):
700
737
 
701
738
  result = CollectionFilterResult(cls._init(results), failures)
702
739
  if eager:
703
- return result.collect_all()
740
+ return result.collect_all(**kwargs)
704
741
  return result
705
742
 
706
743
  def join(
@@ -795,22 +832,52 @@ class Collection(BaseCollection, ABC):
795
832
 
796
833
  # ---------------------------------- COLLECTION ---------------------------------- #
797
834
 
798
- def collect_all(self) -> Self:
835
+ def collect_all(self, **kwargs: Any) -> Self:
799
836
  """Collect all members of the collection.
800
837
 
801
838
  This method collects all members in parallel for maximum efficiency. It is
802
839
  particularly useful when :meth:`filter` is called with lazy frame inputs.
803
840
 
841
+ Args:
842
+ kwargs: Keyword arguments passed directly to :meth:`polars.collect_all`.
843
+
804
844
  Returns:
805
845
  The same collection with all members collected once. Members annotated
806
846
  with :class:`~dataframely.DataFrame` are returned as DataFrames, while
807
847
  members annotated with :class:`~dataframely.LazyFrame` are returned as
808
- "shallow-lazy" frames (obtained by calling ``.collect().lazy()``).
848
+ "shallow-lazy" frames (obtained by calling `.collect().lazy()`).
809
849
  """
810
850
  lazy_dict = self.to_dict()
811
- dfs = pl.collect_all(lazy_dict.values())
851
+ dfs = pl.collect_all(lazy_dict.values(), **kwargs)
812
852
  return self._init(dict(zip(lazy_dict, dfs)))
813
853
 
854
+ def pipe(
855
+ self,
856
+ function: Callable[Concatenate[Self, P], T],
857
+ *args: P.args,
858
+ **kwargs: P.kwargs,
859
+ ) -> T:
860
+ """Apply a function to this collection.
861
+
862
+ This method allows chaining operations on a collection in a fluent style,
863
+ analogously to :meth:`polars.LazyFrame.pipe`.
864
+
865
+ Args:
866
+ function: The callable to apply. It receives this collection as its first
867
+ argument, followed by any additional ``args`` and ``kwargs``.
868
+ args: Additional positional arguments to pass to ``function``.
869
+ kwargs: Additional keyword arguments to pass to ``function``.
870
+
871
+ Returns:
872
+ The return value of ``function`` when called as described.
873
+
874
+ Example:
875
+ >>> def add_prefix(collection: MyCollection, prefix: str) -> MyCollection:
876
+ ... ...
877
+ >>> result = my_collection.pipe(add_prefix, prefix="foo")
878
+ """
879
+ return function(self, *args, **kwargs)
880
+
814
881
  # --------------------------------- SERIALIZATION -------------------------------- #
815
882
 
816
883
  @classmethod
@@ -84,9 +84,9 @@ def require_relationship_one_to_at_least_one(
84
84
  columns, filtered to ensure a 1:{1,N} relationship.
85
85
  """
86
86
  if drop_duplicates:
87
- return lhs.unique(on, keep="none").join(rhs.unique(on), on=on)
87
+ return lhs.unique(on, keep="none").join(rhs, on=on, how="semi")
88
88
 
89
- return lhs.join(rhs.unique(on), on=on)
89
+ return lhs.join(rhs, on=on, how="semi")
90
90
 
91
91
 
92
92
  # ------------------------------------------------------------------------------------ #
@@ -501,6 +501,7 @@ class Schema(BaseSchema, ABC):
501
501
  *,
502
502
  cast: bool = False,
503
503
  eager: Literal[True] = True,
504
+ **kwargs: Any,
504
505
  ) -> DataFrame[Self]: ...
505
506
 
506
507
  @overload
@@ -512,6 +513,7 @@ class Schema(BaseSchema, ABC):
512
513
  *,
513
514
  cast: bool = False,
514
515
  eager: Literal[False],
516
+ **kwargs: Any,
515
517
  ) -> LazyFrame[Self]: ...
516
518
 
517
519
  @overload
@@ -523,6 +525,7 @@ class Schema(BaseSchema, ABC):
523
525
  *,
524
526
  cast: bool = False,
525
527
  eager: bool,
528
+ **kwargs: Any,
526
529
  ) -> DataFrame[Self] | LazyFrame[Self]: ...
527
530
 
528
531
  @classmethod
@@ -533,6 +536,7 @@ class Schema(BaseSchema, ABC):
533
536
  *,
534
537
  cast: bool = False,
535
538
  eager: bool = True,
539
+ **kwargs: Any,
536
540
  ) -> DataFrame[Self] | LazyFrame[Self]:
537
541
  """Validate that a data frame satisfies the schema.
538
542
 
@@ -554,6 +558,8 @@ class Schema(BaseSchema, ABC):
554
558
  not surface *all* validation issues as the validation is aborted
555
559
  once the first failure is encountered. Likewise, the reported
556
560
  validation failure can be non-deterministic.
561
+ kwargs: Keyword arguments passed directly to :meth:`polars.LazyFrame.collect`
562
+ when `eager=True`.
557
563
 
558
564
  Returns:
559
565
  The input eager or lazy frame, wrapped in a generic version of the
@@ -574,7 +580,7 @@ class Schema(BaseSchema, ABC):
574
580
  `eager=False`.
575
581
  """
576
582
  if eager:
577
- out, failure = cls.filter(df, cast=cast, eager=True)
583
+ out, failure = cls.filter(df, cast=cast, eager=True, **kwargs)
578
584
  if len(failure) > 0:
579
585
  counts = failure.counts()
580
586
  raise ValidationError(
@@ -608,7 +614,7 @@ class Schema(BaseSchema, ABC):
608
614
 
609
615
  @classmethod
610
616
  def is_valid(
611
- cls, df: pl.DataFrame | pl.LazyFrame, /, *, cast: bool = False
617
+ cls, df: pl.DataFrame | pl.LazyFrame, /, *, cast: bool = False, **kwargs: Any
612
618
  ) -> bool:
613
619
  """Check whether a data frame satisfies the schema.
614
620
 
@@ -627,6 +633,7 @@ class Schema(BaseSchema, ABC):
627
633
  cast to the schema's defined data type before running validation. If set
628
634
  to `False`, a wrong data type will result in a return value of
629
635
  `False`.
636
+ kwargs: Keyword arguments passed directly to :meth:`polars.LazyFrame.collect`.
630
637
 
631
638
  Returns:
632
639
  Whether the provided dataframe can be validated with this schema.
@@ -646,12 +653,12 @@ class Schema(BaseSchema, ABC):
646
653
  return (
647
654
  lf.pipe(with_evaluation_rules, rules)
648
655
  .select(all_rules(rules.keys()))
649
- .collect()
656
+ .collect(**kwargs)
650
657
  .item()
651
658
  )
652
659
  # NOTE: We cannot simply return `True` here as, otherwise, we wouldn't
653
660
  # validate the schema.
654
- return lf.select(pl.lit(True)).collect().item()
661
+ return lf.select(pl.lit(True)).collect(**kwargs).item()
655
662
  except SchemaError:
656
663
  # If we encounter a schema error, we gracefully handle this as 'invalid'
657
664
  return False
@@ -699,6 +706,7 @@ class Schema(BaseSchema, ABC):
699
706
  *,
700
707
  cast: bool = False,
701
708
  eager: bool = True,
709
+ **kwargs: Any,
702
710
  ) -> FilterResult[Self] | LazyFilterResult[Self]:
703
711
  """Filter the data frame by the rules of this schema, returning `(valid,
704
712
  failures)`.
@@ -709,16 +717,16 @@ class Schema(BaseSchema, ABC):
709
717
  succeeds.
710
718
 
711
719
  Args:
712
- df: The data frame to filter for valid rows.
713
- The data frame is collected within this method, regardless of whether
714
- a :class:`~polars.DataFrame` or :class:`~polars.LazyFrame` is passed.
715
- cast:
716
- Whether columns with a wrong data type in the input data frame are
720
+ df: The data frame to filter for valid rows. The data frame is collected
721
+ within this method, regardless of whether a :class:`~polars.DataFrame`
722
+ or :class:`~polars.LazyFrame` is passed.
723
+ cast: Whether columns with a wrong data type in the input data frame are
717
724
  cast to the schema's defined data type if possible. Rows for which the
718
725
  cast fails for any column are filtered out.
719
726
  eager: Whether the filter operation should be performed eagerly. If `False`, the
720
- returned lazy frame will
721
- fail to collect if the validation does not pass.
727
+ returned lazy frame will fail to collect if the validation does not pass.
728
+ kwargs: Keyword arguments passed directly to :meth:`polars.LazyFrame.collect`
729
+ when `eager=True`.
722
730
 
723
731
  Returns:
724
732
  A tuple of the validated rows in the input data frame (potentially
@@ -754,8 +762,8 @@ class Schema(BaseSchema, ABC):
754
762
  match_to_schema, cls, casting=("lenient" if cast else "none")
755
763
  )
756
764
  if rules := cls._validation_rules(with_cast=cast):
757
- evaluated = (
758
- lf.pipe(cls._with_evaluated_rules, rules).pipe(collect_if, eager).lazy()
765
+ evaluated = lf.pipe(cls._with_evaluated_rules, rules).pipe(
766
+ collect_if, eager, **kwargs
759
767
  )
760
768
  filtered = evaluated.filter(pl.col(_COLUMN_VALID)).select(
761
769
  cls.column_names()
@@ -8,4 +8,5 @@ Operations
8
8
 
9
9
  Collection.collect_all
10
10
  Collection.join
11
+ Collection.pipe
11
12
  concat_collection_members
@@ -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.11.0"
30
+ version = "2.12.0"
31
31
 
32
32
  [project.optional-dependencies]
33
33
  deltalake = ["deltalake"]
@@ -15,13 +15,13 @@ def partitioned_dataset(dataset: pl.DataFrame) -> dict[str, pl.DataFrame]:
15
15
  "elevation",
16
16
  "aspect",
17
17
  "slope",
18
- idx=pl.int_range(pl.len(), dtype=pl.UInt32),
18
+ idx=pl.int_range(pl.len(), dtype=pl.UInt32).shuffle(),
19
19
  ),
20
20
  "second": dataset.select(
21
21
  "horizontal_distance_to_hydrology",
22
22
  "vertical_distance_to_hydrology",
23
23
  "horizontal_distance_to_roadways",
24
- idx=pl.int_range(pl.len(), dtype=pl.UInt32),
24
+ idx=pl.int_range(pl.len(), dtype=pl.UInt32).shuffle(),
25
25
  ),
26
26
  }
27
27
 
@@ -95,18 +95,24 @@ class MultiFilterCollection(dy.Collection):
95
95
 
96
96
 
97
97
  @pytest.mark.benchmark(group="collection-filter-multi")
98
+ @pytest.mark.parametrize("engine", ["in-memory", "streaming"])
98
99
  def test_multi_filter_validate(
99
- benchmark: BenchmarkFixture, partitioned_dataset: dict[str, pl.DataFrame]
100
+ benchmark: BenchmarkFixture,
101
+ partitioned_dataset: dict[str, pl.DataFrame],
102
+ engine: str,
100
103
  ) -> None:
101
- benchmark(MultiFilterCollection.validate, partitioned_dataset)
104
+ benchmark(MultiFilterCollection.validate, partitioned_dataset, engine=engine)
102
105
 
103
106
 
104
107
  @pytest.mark.benchmark(group="collection-filter-multi")
108
+ @pytest.mark.parametrize("engine", ["in-memory", "streaming"])
105
109
  def test_multi_filter_filter(
106
- benchmark: BenchmarkFixture, partitioned_dataset: dict[str, pl.DataFrame]
110
+ benchmark: BenchmarkFixture,
111
+ partitioned_dataset: dict[str, pl.DataFrame],
112
+ engine: str,
107
113
  ) -> None:
108
114
  def benchmark_fn() -> None:
109
- _, failure = MultiFilterCollection.filter(partitioned_dataset)
115
+ _, failure = MultiFilterCollection.filter(partitioned_dataset, engine=engine)
110
116
  _ = [len(f) for f in failure.values()]
111
117
 
112
118
  benchmark(benchmark_fn)
@@ -0,0 +1,55 @@
1
+ # Copyright (c) QuantCo 2025-2026
2
+ # SPDX-License-Identifier: BSD-3-Clause
3
+
4
+ import dataframely as dy
5
+
6
+
7
+ class SchemaOne(dy.Schema):
8
+ id = dy.Int64(primary_key=True)
9
+ name = dy.String(nullable=False)
10
+
11
+
12
+ class SchemaTwo(dy.Schema):
13
+ id = dy.Int64(primary_key=True)
14
+ name = dy.String(nullable=False)
15
+
16
+
17
+ class MyCollection(dy.Collection):
18
+ member_one: dy.LazyFrame[SchemaOne]
19
+ member_two: dy.LazyFrame[SchemaTwo]
20
+
21
+
22
+ def test_pipe_passes_self() -> None:
23
+ # Arrange
24
+ collection = MyCollection.sample(overrides=[{"id": 1}, {"id": 2}])
25
+
26
+ # Act
27
+ result = collection.pipe(lambda c: c)
28
+
29
+ # Assert
30
+ assert result is collection
31
+
32
+
33
+ def test_pipe_forwards_args_and_kwargs() -> None:
34
+ # Arrange
35
+ collection = MyCollection.sample(overrides=[{"id": 1}, {"id": 2}])
36
+
37
+ def combine(c: MyCollection, prefix: str, *, suffix: str) -> str:
38
+ return f"{prefix}{type(c).__name__}{suffix}"
39
+
40
+ # Act
41
+ result = collection.pipe(combine, "pre-", suffix="-post")
42
+
43
+ # Assert
44
+ assert result == "pre-MyCollection-post"
45
+
46
+
47
+ def test_pipe_returns_arbitrary_type() -> None:
48
+ # Arrange
49
+ collection = MyCollection.sample(overrides=[{"id": 1}, {"id": 2}, {"id": 3}])
50
+
51
+ # Act
52
+ result = collection.pipe(lambda c: c.member_one.collect().height)
53
+
54
+ # Assert
55
+ assert result == 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