dataframely 1.11.0__tar.gz → 1.12.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. {dataframely-1.11.0 → dataframely-1.12.1}/PKG-INFO +1 -1
  2. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/__init__.py +2 -0
  3. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/columns/__init__.py +2 -0
  4. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/columns/_base.py +10 -7
  5. dataframely-1.12.1/dataframely/columns/binary.py +38 -0
  6. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/columns/enum.py +4 -8
  7. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/random.py +25 -0
  8. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/testing/const.py +1 -0
  9. {dataframely-1.11.0 → dataframely-1.12.1}/pyproject.toml +1 -1
  10. dataframely-1.12.1/tests/column_types/test_binary.py +24 -0
  11. {dataframely-1.11.0 → dataframely-1.12.1}/tests/columns/test_check.py +16 -0
  12. {dataframely-1.11.0 → dataframely-1.12.1}/tests/columns/test_matches.py +12 -0
  13. {dataframely-1.11.0 → dataframely-1.12.1}/tests/columns/test_sql_schema.py +2 -0
  14. {dataframely-1.11.0 → dataframely-1.12.1}/tests/schema/test_repr.py +11 -0
  15. {dataframely-1.11.0 → dataframely-1.12.1}/tests/schema/test_serialization.py +1 -0
  16. {dataframely-1.11.0 → dataframely-1.12.1}/tests/test_random.py +14 -0
  17. {dataframely-1.11.0 → dataframely-1.12.1}/.copier-answers.yml +0 -0
  18. {dataframely-1.11.0 → dataframely-1.12.1}/.envrc +0 -0
  19. {dataframely-1.11.0 → dataframely-1.12.1}/.gitattributes +0 -0
  20. {dataframely-1.11.0 → dataframely-1.12.1}/.github/CODEOWNERS +0 -0
  21. {dataframely-1.11.0 → dataframely-1.12.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  22. {dataframely-1.11.0 → dataframely-1.12.1}/.github/dependabot.yml +0 -0
  23. {dataframely-1.11.0 → dataframely-1.12.1}/.github/release-drafter.yml +0 -0
  24. {dataframely-1.11.0 → dataframely-1.12.1}/.github/workflows/build.yml +0 -0
  25. {dataframely-1.11.0 → dataframely-1.12.1}/.github/workflows/chore.yml +0 -0
  26. {dataframely-1.11.0 → dataframely-1.12.1}/.github/workflows/ci.yml +0 -0
  27. {dataframely-1.11.0 → dataframely-1.12.1}/.github/workflows/nightly.yml +0 -0
  28. {dataframely-1.11.0 → dataframely-1.12.1}/.github/workflows/scorecard.yml +0 -0
  29. {dataframely-1.11.0 → dataframely-1.12.1}/.gitignore +0 -0
  30. {dataframely-1.11.0 → dataframely-1.12.1}/.pre-commit-config.yaml +0 -0
  31. {dataframely-1.11.0 → dataframely-1.12.1}/.prettierignore +0 -0
  32. {dataframely-1.11.0 → dataframely-1.12.1}/.prettierrc +0 -0
  33. {dataframely-1.11.0 → dataframely-1.12.1}/.readthedocs.yml +0 -0
  34. {dataframely-1.11.0 → dataframely-1.12.1}/Cargo.lock +0 -0
  35. {dataframely-1.11.0 → dataframely-1.12.1}/Cargo.toml +0 -0
  36. {dataframely-1.11.0 → dataframely-1.12.1}/LICENSE +0 -0
  37. {dataframely-1.11.0 → dataframely-1.12.1}/README.md +0 -0
  38. {dataframely-1.11.0 → dataframely-1.12.1}/SECURITY.md +0 -0
  39. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/_base_collection.py +0 -0
  40. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/_base_schema.py +0 -0
  41. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/_compat.py +0 -0
  42. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/_deprecation.py +0 -0
  43. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/_extre.pyi +0 -0
  44. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/_filter.py +0 -0
  45. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/_polars.py +0 -0
  46. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/_rule.py +0 -0
  47. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/_serialization.py +0 -0
  48. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/_storage/__init__.py +0 -0
  49. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/_storage/_base.py +0 -0
  50. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/_storage/_exc.py +0 -0
  51. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/_storage/constants.py +0 -0
  52. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/_storage/delta.py +0 -0
  53. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/_storage/parquet.py +0 -0
  54. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/_typing.py +0 -0
  55. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/_validation.py +0 -0
  56. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/collection.py +0 -0
  57. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/columns/_mixins.py +0 -0
  58. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/columns/_registry.py +0 -0
  59. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/columns/_utils.py +0 -0
  60. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/columns/any.py +0 -0
  61. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/columns/array.py +0 -0
  62. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/columns/bool.py +0 -0
  63. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/columns/categorical.py +0 -0
  64. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/columns/datetime.py +0 -0
  65. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/columns/decimal.py +0 -0
  66. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/columns/float.py +0 -0
  67. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/columns/integer.py +0 -0
  68. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/columns/list.py +0 -0
  69. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/columns/object.py +0 -0
  70. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/columns/string.py +0 -0
  71. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/columns/struct.py +0 -0
  72. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/config.py +0 -0
  73. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/exc.py +0 -0
  74. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/failure.py +0 -0
  75. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/functional.py +0 -0
  76. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/mypy.py +0 -0
  77. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/py.typed +0 -0
  78. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/schema.py +0 -0
  79. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/testing/__init__.py +0 -0
  80. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/testing/factory.py +0 -0
  81. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/testing/mask.py +0 -0
  82. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/testing/rules.py +0 -0
  83. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/testing/storage.py +0 -0
  84. {dataframely-1.11.0 → dataframely-1.12.1}/dataframely/testing/typing.py +0 -0
  85. {dataframely-1.11.0 → dataframely-1.12.1}/docker-compose.yml +0 -0
  86. {dataframely-1.11.0 → dataframely-1.12.1}/docs/Makefile +0 -0
  87. {dataframely-1.11.0 → dataframely-1.12.1}/docs/_api/dataframely.collection.rst +0 -0
  88. {dataframely-1.11.0 → dataframely-1.12.1}/docs/_api/dataframely.columns.any.rst +0 -0
  89. {dataframely-1.11.0 → dataframely-1.12.1}/docs/_api/dataframely.columns.bool.rst +0 -0
  90. {dataframely-1.11.0 → dataframely-1.12.1}/docs/_api/dataframely.columns.datetime.rst +0 -0
  91. {dataframely-1.11.0 → dataframely-1.12.1}/docs/_api/dataframely.columns.decimal.rst +0 -0
  92. {dataframely-1.11.0 → dataframely-1.12.1}/docs/_api/dataframely.columns.enum.rst +0 -0
  93. {dataframely-1.11.0 → dataframely-1.12.1}/docs/_api/dataframely.columns.float.rst +0 -0
  94. {dataframely-1.11.0 → dataframely-1.12.1}/docs/_api/dataframely.columns.integer.rst +0 -0
  95. {dataframely-1.11.0 → dataframely-1.12.1}/docs/_api/dataframely.columns.list.rst +0 -0
  96. {dataframely-1.11.0 → dataframely-1.12.1}/docs/_api/dataframely.columns.rst +0 -0
  97. {dataframely-1.11.0 → dataframely-1.12.1}/docs/_api/dataframely.columns.string.rst +0 -0
  98. {dataframely-1.11.0 → dataframely-1.12.1}/docs/_api/dataframely.columns.struct.rst +0 -0
  99. {dataframely-1.11.0 → dataframely-1.12.1}/docs/_api/dataframely.config.rst +0 -0
  100. {dataframely-1.11.0 → dataframely-1.12.1}/docs/_api/dataframely.exc.rst +0 -0
  101. {dataframely-1.11.0 → dataframely-1.12.1}/docs/_api/dataframely.failure.rst +0 -0
  102. {dataframely-1.11.0 → dataframely-1.12.1}/docs/_api/dataframely.functional.rst +0 -0
  103. {dataframely-1.11.0 → dataframely-1.12.1}/docs/_api/dataframely.mypy.rst +0 -0
  104. {dataframely-1.11.0 → dataframely-1.12.1}/docs/_api/dataframely.random.rst +0 -0
  105. {dataframely-1.11.0 → dataframely-1.12.1}/docs/_api/dataframely.rst +0 -0
  106. {dataframely-1.11.0 → dataframely-1.12.1}/docs/_api/dataframely.schema.rst +0 -0
  107. {dataframely-1.11.0 → dataframely-1.12.1}/docs/_api/dataframely.testing.const.rst +0 -0
  108. {dataframely-1.11.0 → dataframely-1.12.1}/docs/_api/dataframely.testing.factory.rst +0 -0
  109. {dataframely-1.11.0 → dataframely-1.12.1}/docs/_api/dataframely.testing.mask.rst +0 -0
  110. {dataframely-1.11.0 → dataframely-1.12.1}/docs/_api/dataframely.testing.rst +0 -0
  111. {dataframely-1.11.0 → dataframely-1.12.1}/docs/_api/dataframely.testing.rules.rst +0 -0
  112. {dataframely-1.11.0 → dataframely-1.12.1}/docs/_api/dataframely.testing.typing.rst +0 -0
  113. {dataframely-1.11.0 → dataframely-1.12.1}/docs/_api/modules.rst +0 -0
  114. {dataframely-1.11.0 → dataframely-1.12.1}/docs/_static/custom.css +0 -0
  115. {dataframely-1.11.0 → dataframely-1.12.1}/docs/_static/favicon.ico +0 -0
  116. {dataframely-1.11.0 → dataframely-1.12.1}/docs/conf.py +0 -0
  117. {dataframely-1.11.0 → dataframely-1.12.1}/docs/index.rst +0 -0
  118. {dataframely-1.11.0 → dataframely-1.12.1}/docs/make.bat +0 -0
  119. {dataframely-1.11.0 → dataframely-1.12.1}/docs/sites/development.rst +0 -0
  120. {dataframely-1.11.0 → dataframely-1.12.1}/docs/sites/examples/real-world.ipynb +0 -0
  121. {dataframely-1.11.0 → dataframely-1.12.1}/docs/sites/faq.rst +0 -0
  122. {dataframely-1.11.0 → dataframely-1.12.1}/docs/sites/installation.rst +0 -0
  123. {dataframely-1.11.0 → dataframely-1.12.1}/docs/sites/quickstart.rst +0 -0
  124. {dataframely-1.11.0 → dataframely-1.12.1}/docs/sites/versioning.rst +0 -0
  125. {dataframely-1.11.0 → dataframely-1.12.1}/pixi.lock +0 -0
  126. {dataframely-1.11.0 → dataframely-1.12.1}/pixi.toml +0 -0
  127. {dataframely-1.11.0 → dataframely-1.12.1}/src/errdefs.rs +0 -0
  128. {dataframely-1.11.0 → dataframely-1.12.1}/src/lib.rs +0 -0
  129. {dataframely-1.11.0 → dataframely-1.12.1}/src/regex_repr.rs +0 -0
  130. {dataframely-1.11.0 → dataframely-1.12.1}/tests/benches/conftest.py +0 -0
  131. {dataframely-1.11.0 → dataframely-1.12.1}/tests/benches/test_collection.py +0 -0
  132. {dataframely-1.11.0 → dataframely-1.12.1}/tests/benches/test_failure.py +0 -0
  133. {dataframely-1.11.0 → dataframely-1.12.1}/tests/benches/test_schema.py +0 -0
  134. {dataframely-1.11.0 → dataframely-1.12.1}/tests/collection/test_base.py +0 -0
  135. {dataframely-1.11.0 → dataframely-1.12.1}/tests/collection/test_cast.py +0 -0
  136. {dataframely-1.11.0 → dataframely-1.12.1}/tests/collection/test_collection_future_annotations.py +0 -0
  137. {dataframely-1.11.0 → dataframely-1.12.1}/tests/collection/test_create_empty.py +0 -0
  138. {dataframely-1.11.0 → dataframely-1.12.1}/tests/collection/test_filter_one_to_n.py +0 -0
  139. {dataframely-1.11.0 → dataframely-1.12.1}/tests/collection/test_filter_validate.py +0 -0
  140. {dataframely-1.11.0 → dataframely-1.12.1}/tests/collection/test_ignore_in_filter.py +0 -0
  141. {dataframely-1.11.0 → dataframely-1.12.1}/tests/collection/test_implementation.py +0 -0
  142. {dataframely-1.11.0 → dataframely-1.12.1}/tests/collection/test_join.py +0 -0
  143. {dataframely-1.11.0 → dataframely-1.12.1}/tests/collection/test_matches.py +0 -0
  144. {dataframely-1.11.0 → dataframely-1.12.1}/tests/collection/test_optional_members.py +0 -0
  145. {dataframely-1.11.0 → dataframely-1.12.1}/tests/collection/test_repr.py +0 -0
  146. {dataframely-1.11.0 → dataframely-1.12.1}/tests/collection/test_sample.py +0 -0
  147. {dataframely-1.11.0 → dataframely-1.12.1}/tests/collection/test_serialization.py +0 -0
  148. {dataframely-1.11.0 → dataframely-1.12.1}/tests/collection/test_storage.py +0 -0
  149. {dataframely-1.11.0 → dataframely-1.12.1}/tests/collection/test_validate_input.py +0 -0
  150. {dataframely-1.11.0 → dataframely-1.12.1}/tests/column_types/__init__.py +0 -0
  151. {dataframely-1.11.0 → dataframely-1.12.1}/tests/column_types/test_any.py +0 -0
  152. {dataframely-1.11.0 → dataframely-1.12.1}/tests/column_types/test_array.py +0 -0
  153. {dataframely-1.11.0 → dataframely-1.12.1}/tests/column_types/test_datetime.py +0 -0
  154. {dataframely-1.11.0 → dataframely-1.12.1}/tests/column_types/test_decimal.py +0 -0
  155. {dataframely-1.11.0 → dataframely-1.12.1}/tests/column_types/test_enum.py +0 -0
  156. {dataframely-1.11.0 → dataframely-1.12.1}/tests/column_types/test_float.py +0 -0
  157. {dataframely-1.11.0 → dataframely-1.12.1}/tests/column_types/test_integer.py +0 -0
  158. {dataframely-1.11.0 → dataframely-1.12.1}/tests/column_types/test_list.py +0 -0
  159. {dataframely-1.11.0 → dataframely-1.12.1}/tests/column_types/test_object.py +0 -0
  160. {dataframely-1.11.0 → dataframely-1.12.1}/tests/column_types/test_string.py +0 -0
  161. {dataframely-1.11.0 → dataframely-1.12.1}/tests/column_types/test_struct.py +0 -0
  162. {dataframely-1.11.0 → dataframely-1.12.1}/tests/columns/__init__.py +0 -0
  163. {dataframely-1.11.0 → dataframely-1.12.1}/tests/columns/test_alias.py +0 -0
  164. {dataframely-1.11.0 → dataframely-1.12.1}/tests/columns/test_default_dtypes.py +0 -0
  165. {dataframely-1.11.0 → dataframely-1.12.1}/tests/columns/test_metadata.py +0 -0
  166. {dataframely-1.11.0 → dataframely-1.12.1}/tests/columns/test_polars_schema.py +0 -0
  167. {dataframely-1.11.0 → dataframely-1.12.1}/tests/columns/test_pyarrow.py +0 -0
  168. {dataframely-1.11.0 → dataframely-1.12.1}/tests/columns/test_rules.py +0 -0
  169. {dataframely-1.11.0 → dataframely-1.12.1}/tests/columns/test_sample.py +0 -0
  170. {dataframely-1.11.0 → dataframely-1.12.1}/tests/columns/test_str.py +0 -0
  171. {dataframely-1.11.0 → dataframely-1.12.1}/tests/columns/test_utils.py +0 -0
  172. {dataframely-1.11.0 → dataframely-1.12.1}/tests/core_validation/__init__.py +0 -0
  173. {dataframely-1.11.0 → dataframely-1.12.1}/tests/core_validation/test_column_validation.py +0 -0
  174. {dataframely-1.11.0 → dataframely-1.12.1}/tests/core_validation/test_dtype_validation.py +0 -0
  175. {dataframely-1.11.0 → dataframely-1.12.1}/tests/core_validation/test_rule_evaluation.py +0 -0
  176. {dataframely-1.11.0 → dataframely-1.12.1}/tests/failure_info/test_storage.py +0 -0
  177. {dataframely-1.11.0 → dataframely-1.12.1}/tests/functional/test_concat.py +0 -0
  178. {dataframely-1.11.0 → dataframely-1.12.1}/tests/functional/test_relationships.py +0 -0
  179. {dataframely-1.11.0 → dataframely-1.12.1}/tests/schema/test_base.py +0 -0
  180. {dataframely-1.11.0 → dataframely-1.12.1}/tests/schema/test_cast.py +0 -0
  181. {dataframely-1.11.0 → dataframely-1.12.1}/tests/schema/test_create_empty.py +0 -0
  182. {dataframely-1.11.0 → dataframely-1.12.1}/tests/schema/test_create_empty_if_none.py +0 -0
  183. {dataframely-1.11.0 → dataframely-1.12.1}/tests/schema/test_filter.py +0 -0
  184. {dataframely-1.11.0 → dataframely-1.12.1}/tests/schema/test_inheritance.py +0 -0
  185. {dataframely-1.11.0 → dataframely-1.12.1}/tests/schema/test_matches.py +0 -0
  186. {dataframely-1.11.0 → dataframely-1.12.1}/tests/schema/test_read_write_parquet.py +0 -0
  187. {dataframely-1.11.0 → dataframely-1.12.1}/tests/schema/test_rule_implementation.py +0 -0
  188. {dataframely-1.11.0 → dataframely-1.12.1}/tests/schema/test_sample.py +0 -0
  189. {dataframely-1.11.0 → dataframely-1.12.1}/tests/schema/test_storage.py +0 -0
  190. {dataframely-1.11.0 → dataframely-1.12.1}/tests/schema/test_validate.py +0 -0
  191. {dataframely-1.11.0 → dataframely-1.12.1}/tests/storage/test_delta.py +0 -0
  192. {dataframely-1.11.0 → dataframely-1.12.1}/tests/test_compat.py +0 -0
  193. {dataframely-1.11.0 → dataframely-1.12.1}/tests/test_config.py +0 -0
  194. {dataframely-1.11.0 → dataframely-1.12.1}/tests/test_deprecation.py +0 -0
  195. {dataframely-1.11.0 → dataframely-1.12.1}/tests/test_exc.py +0 -0
  196. {dataframely-1.11.0 → dataframely-1.12.1}/tests/test_extre.py +0 -0
  197. {dataframely-1.11.0 → dataframely-1.12.1}/tests/test_factory.py +0 -0
  198. {dataframely-1.11.0 → dataframely-1.12.1}/tests/test_serialization.py +0 -0
  199. {dataframely-1.11.0 → dataframely-1.12.1}/tests/test_typing.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dataframely
3
- Version: 1.11.0
3
+ Version: 1.12.1
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: 3.10
6
6
  Classifier: Programming Language :: Python :: 3.11
@@ -23,6 +23,7 @@ from .collection import (
23
23
  from .columns import (
24
24
  Any,
25
25
  Array,
26
+ Binary,
26
27
  Bool,
27
28
  Categorical,
28
29
  Column,
@@ -77,6 +78,7 @@ __all__ = [
77
78
  "read_parquet_metadata_schema",
78
79
  "read_parquet_metadata_collection",
79
80
  "Any",
81
+ "Binary",
80
82
  "Bool",
81
83
  "Categorical",
82
84
  "Column",
@@ -5,6 +5,7 @@ from ._base import Column
5
5
  from ._registry import column_from_dict
6
6
  from .any import Any
7
7
  from .array import Array
8
+ from .binary import Binary
8
9
  from .bool import Bool
9
10
  from .categorical import Categorical
10
11
  from .datetime import Date, Datetime, Duration, Time
@@ -22,6 +23,7 @@ __all__ = [
22
23
  "column_from_dict",
23
24
  "Any",
24
25
  "Array",
26
+ "Binary",
25
27
  "Bool",
26
28
  "Categorical",
27
29
  "Date",
@@ -7,7 +7,7 @@ import inspect
7
7
  import sys
8
8
  from abc import ABC, abstractmethod
9
9
  from collections import Counter
10
- from collections.abc import Callable
10
+ from collections.abc import Callable, Mapping, Sequence
11
11
  from typing import Any, TypeAlias, cast
12
12
 
13
13
  import polars as pl
@@ -27,8 +27,8 @@ else:
27
27
 
28
28
  Check: TypeAlias = (
29
29
  Callable[[pl.Expr], pl.Expr]
30
- | list[Callable[[pl.Expr], pl.Expr]]
31
- | dict[str, Callable[[pl.Expr], pl.Expr]]
30
+ | Sequence[Callable[[pl.Expr], pl.Expr]]
31
+ | Mapping[str, Callable[[pl.Expr], pl.Expr]]
32
32
  )
33
33
 
34
34
  # ------------------------------------------------------------------------------------ #
@@ -141,12 +141,14 @@ class Column(ABC):
141
141
  result["nullability"] = expr.is_not_null()
142
142
 
143
143
  if self.check is not None:
144
- if isinstance(self.check, dict):
144
+ if isinstance(self.check, Mapping):
145
145
  for rule_name, rule_callable in self.check.items():
146
146
  result[f"check__{rule_name}"] = rule_callable(expr)
147
147
  else:
148
148
  list_of_rules = (
149
- self.check if isinstance(self.check, list) else [self.check]
149
+ list(self.check)
150
+ if isinstance(self.check, Sequence)
151
+ else [self.check]
150
152
  )
151
153
  # Get unique names for rules from callables
152
154
  rule_names = self._derive_check_rule_names(list_of_rules)
@@ -378,6 +380,7 @@ class Column(ABC):
378
380
  ) -> bool:
379
381
  if name == "check":
380
382
  return _compare_checks(lhs, rhs, column_expr)
383
+
381
384
  return lhs == rhs
382
385
 
383
386
  # -------------------------------- DUNDER METHODS -------------------------------- #
@@ -423,9 +426,9 @@ def _check_to_expr(check: Check | None, expr: pl.Expr) -> Any | None:
423
426
  match check:
424
427
  case None:
425
428
  return None
426
- case list():
429
+ case Sequence():
427
430
  return [c(expr) for c in check]
428
- case dict():
431
+ case Mapping():
429
432
  return {key: c(expr) for key, c in check.items()}
430
433
  case _ if callable(check):
431
434
  return check(expr)
@@ -0,0 +1,38 @@
1
+ # Copyright (c) QuantCo 2025-2025
2
+ # SPDX-License-Identifier: BSD-3-Clause
3
+
4
+ from __future__ import annotations
5
+
6
+ import polars as pl
7
+
8
+ from dataframely._compat import pa, sa, sa_TypeEngine
9
+ from dataframely.random import Generator
10
+
11
+ from ._base import Column
12
+ from ._registry import register
13
+
14
+
15
+ @register
16
+ class Binary(Column):
17
+ """A column of binary values."""
18
+
19
+ @property
20
+ def dtype(self) -> pl.DataType:
21
+ return pl.Binary()
22
+
23
+ def sqlalchemy_dtype(self, dialect: sa.Dialect) -> sa_TypeEngine:
24
+ if dialect.name == "mssql":
25
+ return sa.VARBINARY()
26
+ return sa.LargeBinary()
27
+
28
+ @property
29
+ def pyarrow_dtype(self) -> pa.DataType:
30
+ return pa.large_binary()
31
+
32
+ def _sample_unchecked(self, generator: Generator, n: int) -> pl.Series:
33
+ return generator.sample_binary(
34
+ n,
35
+ min_bytes=0,
36
+ max_bytes=32,
37
+ null_probability=self._null_probability,
38
+ )
@@ -67,12 +67,8 @@ class Enum(Column):
67
67
  metadata=metadata,
68
68
  )
69
69
  if isclass(categories) and issubclass(categories, enum.Enum):
70
- categories = pl.Series(
71
- values=[getattr(v, "value", v) for v in categories.__members__.values()]
72
- )
73
- elif not isinstance(categories, pl.Series):
74
- categories = pl.Series(values=categories)
75
- self.categories = categories
70
+ categories = (item.value for item in categories)
71
+ self.categories = list(categories)
76
72
 
77
73
  @property
78
74
  def dtype(self) -> pl.DataType:
@@ -81,7 +77,7 @@ class Enum(Column):
81
77
  def validate_dtype(self, dtype: PolarsDataType) -> bool:
82
78
  if not isinstance(dtype, pl.Enum):
83
79
  return False
84
- return self.categories.equals(dtype.categories)
80
+ return self.categories == dtype.categories.to_list()
85
81
 
86
82
  def sqlalchemy_dtype(self, dialect: sa.Dialect) -> sa_TypeEngine:
87
83
  category_lengths = [len(c) for c in self.categories]
@@ -102,6 +98,6 @@ class Enum(Column):
102
98
  def _sample_unchecked(self, generator: Generator, n: int) -> pl.Series:
103
99
  return generator.sample_choice(
104
100
  n,
105
- choices=self.categories.to_list(),
101
+ choices=self.categories,
106
102
  null_probability=self._null_probability,
107
103
  ).cast(self.dtype)
@@ -148,6 +148,31 @@ class Generator:
148
148
  pl.Series(samples, dtype=pl.String), null_probability
149
149
  )
150
150
 
151
+ def sample_binary(
152
+ self,
153
+ n: int = 1,
154
+ *,
155
+ min_bytes: int,
156
+ max_bytes: int,
157
+ null_probability: float = 0.0,
158
+ ) -> pl.Series:
159
+ """Sample a list of binary values in the specified length range.
160
+
161
+ Args:
162
+ n: The number of binary values to sample.
163
+ min_bytes: The minimum number of bytes for each value.
164
+ max_bytes: The maximum number of bytes for each value.
165
+ null_probability: The probability of an element being ``null``.
166
+
167
+ Returns:
168
+ A series with ``n`` elements of dtype ``Binary``.
169
+ """
170
+ lengths = self.numpy_generator.integers(min_bytes, max_bytes + 1, size=n)
171
+ samples = [self.numpy_generator.bytes(length) for length in lengths]
172
+ return self._apply_null_mask(
173
+ pl.Series(samples, dtype=pl.Binary), null_probability
174
+ )
175
+
151
176
  def sample_choice(
152
177
  self,
153
178
  n: int = 1,
@@ -22,6 +22,7 @@ COLUMN_TYPES: list[type[dc.Column]] = [
22
22
  dc.UInt64,
23
23
  dc.String,
24
24
  dc.Categorical,
25
+ dc.Binary,
25
26
  ]
26
27
  INTEGER_COLUMN_TYPES: list[type[dc.Column]] = [
27
28
  dc.Integer,
@@ -25,7 +25,7 @@ description = "A declarative, polars-native data frame validation library"
25
25
  name = "dataframely"
26
26
  readme = "README.md"
27
27
  requires-python = ">=3.10"
28
- version = "1.11.0"
28
+ version = "1.12.1"
29
29
 
30
30
  [project.optional-dependencies]
31
31
  deltalake = ["deltalake"]
@@ -0,0 +1,24 @@
1
+ # Copyright (c) QuantCo 2025-2025
2
+ # SPDX-License-Identifier: BSD-3-Clause
3
+
4
+ import polars as pl
5
+ import pytest
6
+
7
+ import dataframely as dy
8
+ from dataframely.columns import Column
9
+
10
+
11
+ class BinarySchema(dy.Schema):
12
+ a = dy.Binary()
13
+
14
+
15
+ @pytest.mark.parametrize(
16
+ ("column", "dtype", "is_valid"),
17
+ [
18
+ (dy.Binary(), pl.Binary(), True),
19
+ (dy.Binary(), pl.String(), False),
20
+ (dy.Binary(), pl.Null(), False),
21
+ ],
22
+ )
23
+ def test_validate_dtype(column: Column, dtype: pl.DataType, is_valid: bool) -> None:
24
+ assert column.validate_dtype(dtype) == is_valid
@@ -12,6 +12,22 @@ class CheckSchema(dy.Schema):
12
12
  b = dy.String(min_length=3, check=lambda col: col.str.contains("x"))
13
13
 
14
14
 
15
+ def test_check_argument_covariant() -> None:
16
+ # The interesting part of this test is mypy accepting it statically,
17
+ # not the runtime behavior.
18
+ def check_all_or_none(expr: pl.Expr) -> pl.Expr:
19
+ return (expr == "all").all() | (expr != "all").all()
20
+
21
+ check_dict = {"all_or_none": check_all_or_none}
22
+
23
+ class CheckDictSchema(dy.Schema):
24
+ column = dy.String(check=check_dict)
25
+
26
+ df = pl.DataFrame({"column": ["all", "all", "all"]})
27
+ _, failures = CheckDictSchema.filter(df)
28
+ assert failures.counts() == {}
29
+
30
+
15
31
  def test_check() -> None:
16
32
  df = pl.DataFrame({"a": [7, 3, 15], "b": ["abc", "xyz", "x"]})
17
33
  _, failures = CheckSchema.filter(df)
@@ -58,7 +58,19 @@ import dataframely as dy
58
58
  dy.Datetime(time_zone=dt.timezone(dt.timedelta(hours=0))),
59
59
  True,
60
60
  ),
61
+ (dy.Enum(["a", "b"]), dy.Enum(["a", "b"]), True),
62
+ (dy.Enum(["a", "b"]), dy.Enum(["a", "b", "c"]), False),
61
63
  ],
62
64
  )
63
65
  def test_matches(lhs: dy.Column, rhs: dy.Column, expected: bool) -> None:
64
66
  assert lhs.matches(rhs, expr=pl.element()) == expected
67
+
68
+
69
+ def test_matches_enum_attribute_type_mismatch() -> None:
70
+ # Comparison should fail if the `other` column has
71
+ # a `category` member, but its dtype is not `pl.Series`
72
+ col1 = dy.Enum(["a", "b"])
73
+ col2 = dy.Enum(["a", "b"])
74
+ col2.categories = "this_is_not_a_series" # type: ignore
75
+
76
+ assert not col1.matches(col2, pl.element())
@@ -15,6 +15,7 @@ pytestmark = pytest.mark.with_optionals
15
15
  ("column", "datatype"),
16
16
  [
17
17
  (dy.Any(), "SQL_VARIANT"),
18
+ (dy.Binary(), "VARBINARY(max)"),
18
19
  (dy.Bool(), "BIT"),
19
20
  (dy.Date(), "DATE"),
20
21
  (dy.Datetime(), "DATETIME2(6)"),
@@ -61,6 +62,7 @@ def test_mssql_datatype(column: Column, datatype: str) -> None:
61
62
  @pytest.mark.parametrize(
62
63
  ("column", "datatype"),
63
64
  [
65
+ (dy.Binary(), "BYTEA"),
64
66
  (dy.Bool(), "BOOLEAN"),
65
67
  (dy.Date(), "DATE"),
66
68
  (dy.Datetime(), "TIMESTAMP WITHOUT TIME ZONE"),
@@ -52,3 +52,14 @@ def test_repr_with_rules() -> None:
52
52
  - "my_rule": [(col("a")) < (dyn int: 100)]
53
53
  - "my_group_rule": [(col("a").sum()) > (dyn int: 50)] grouped by ['a']
54
54
  """)
55
+
56
+
57
+ def test_repr_enum() -> None:
58
+ class SchemaNoRules(dy.Schema):
59
+ a = dy.Enum(["a"])
60
+
61
+ assert repr(SchemaNoRules) == textwrap.dedent("""\
62
+ [Schema "SchemaNoRules"]
63
+ Columns:
64
+ - "a": Enum(categories=['a'], nullable=True)
65
+ """)
@@ -40,6 +40,7 @@ def test_simple_serialization() -> None:
40
40
  create_schema("test", {"a": dy.Int64(check=[lambda expr: expr > 5])}),
41
41
  create_schema("test", {"a": dy.Int64(check={"x": lambda expr: expr > 5})}),
42
42
  create_schema("test", {"a": dy.Int64(alias="foo")}),
43
+ create_schema("test", {"a": dy.Enum(["a"])}),
43
44
  create_schema("test", {"a": dy.Decimal(scale=2, min=Decimal("1.5"))}),
44
45
  create_schema("test", {"a": dy.Date(min=dt.date(2020, 1, 1))}),
45
46
  create_schema("test", {"a": dy.Datetime(min=dt.datetime(2020, 1, 1))}),
@@ -40,6 +40,7 @@ def test_seeding_nonconstant() -> None:
40
40
  lambda generator, n: generator.sample_float(n, min=0, max=5),
41
41
  lambda generator, n: generator.sample_string(n, regex="[abc]"),
42
42
  lambda generator, n: generator.sample_choice(n, choices=[1, 2, 3]),
43
+ lambda generator, n: generator.sample_binary(n, min_bytes=1, max_bytes=10),
43
44
  lambda generator, n: generator.sample_time(n, min=dt.time(0, 0), max=None),
44
45
  lambda generator, n: generator.sample_date(
45
46
  n, min=dt.date(1970, 1, 1), max=None
@@ -75,6 +76,9 @@ def test_sample_correct_n(
75
76
  lambda generator, n, prob: generator.sample_choice(
76
77
  n, choices=[1, 2, 3], null_probability=prob
77
78
  ),
79
+ lambda generator, n, prob: generator.sample_binary(
80
+ n, min_bytes=1, max_bytes=10, null_probability=prob
81
+ ),
78
82
  lambda generator, n, prob: generator.sample_time(
79
83
  n, min=dt.time(0, 0), max=None, null_probability=prob
80
84
  ),
@@ -131,6 +135,16 @@ def test_sample_string(generator: Generator) -> None:
131
135
  assert (samples.str.len_bytes() == 2).all()
132
136
 
133
137
 
138
+ def test_sample_binary(generator: Generator) -> None:
139
+ samples = generator.sample_binary(100, min_bytes=1, max_bytes=10)
140
+ assert (
141
+ samples.to_frame("s").select(pl.col("s").bin.size("b") >= 1).to_series().all()
142
+ )
143
+ assert (
144
+ samples.to_frame("s").select(pl.col("s").bin.size("b") <= 10).to_series().all()
145
+ )
146
+
147
+
134
148
  def test_sample_choice(generator: Generator) -> None:
135
149
  samples = generator.sample_choice(100_000, choices=[1, 2, 3])
136
150
  assert np.allclose(
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes