dataframely 1.4.0__tar.gz → 1.5.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 (168) hide show
  1. {dataframely-1.4.0 → dataframely-1.5.0}/.copier-answers.yml +1 -1
  2. dataframely-1.5.0/.gitattributes +1 -0
  3. {dataframely-1.4.0 → dataframely-1.5.0}/.github/workflows/build.yml +2 -0
  4. {dataframely-1.4.0 → dataframely-1.5.0}/.github/workflows/chore.yml +4 -4
  5. {dataframely-1.4.0 → dataframely-1.5.0}/.github/workflows/ci.yml +5 -2
  6. dataframely-1.5.0/.github/workflows/scorecard.yml +79 -0
  7. {dataframely-1.4.0 → dataframely-1.5.0}/PKG-INFO +2 -2
  8. dataframely-1.5.0/SECURITY.md +7 -0
  9. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/collection.py +8 -15
  10. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/columns/datetime.py +7 -2
  11. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/failure.py +20 -12
  12. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/random.py +6 -2
  13. {dataframely-1.4.0 → dataframely-1.5.0}/pixi.lock +3591 -3676
  14. {dataframely-1.4.0 → dataframely-1.5.0}/pixi.toml +1 -1
  15. {dataframely-1.4.0 → dataframely-1.5.0}/pyproject.toml +2 -2
  16. {dataframely-1.4.0 → dataframely-1.5.0}/tests/column_types/test_datetime.py +7 -0
  17. {dataframely-1.4.0 → dataframely-1.5.0}/tests/columns/test_pyarrow.py +7 -0
  18. {dataframely-1.4.0 → dataframely-1.5.0}/tests/test_failure_info.py +33 -0
  19. {dataframely-1.4.0 → dataframely-1.5.0}/tests/test_typing.py +1 -1
  20. dataframely-1.4.0/.gitattributes +0 -1
  21. {dataframely-1.4.0 → dataframely-1.5.0}/.envrc +0 -0
  22. {dataframely-1.4.0 → dataframely-1.5.0}/.github/CODEOWNERS +0 -0
  23. {dataframely-1.4.0 → dataframely-1.5.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  24. {dataframely-1.4.0 → dataframely-1.5.0}/.github/dependabot.yml +0 -0
  25. {dataframely-1.4.0 → dataframely-1.5.0}/.github/release-drafter.yml +0 -0
  26. {dataframely-1.4.0 → dataframely-1.5.0}/.gitignore +0 -0
  27. {dataframely-1.4.0 → dataframely-1.5.0}/.pre-commit-config.yaml +0 -0
  28. {dataframely-1.4.0 → dataframely-1.5.0}/.prettierignore +0 -0
  29. {dataframely-1.4.0 → dataframely-1.5.0}/.prettierrc +0 -0
  30. {dataframely-1.4.0 → dataframely-1.5.0}/.readthedocs.yml +0 -0
  31. {dataframely-1.4.0 → dataframely-1.5.0}/Cargo.lock +0 -0
  32. {dataframely-1.4.0 → dataframely-1.5.0}/Cargo.toml +0 -0
  33. {dataframely-1.4.0 → dataframely-1.5.0}/LICENSE +0 -0
  34. {dataframely-1.4.0 → dataframely-1.5.0}/README.md +0 -0
  35. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/__init__.py +0 -0
  36. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/_base_collection.py +0 -0
  37. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/_base_schema.py +0 -0
  38. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/_compat.py +0 -0
  39. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/_deprecation.py +0 -0
  40. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/_extre.pyi +0 -0
  41. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/_filter.py +0 -0
  42. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/_polars.py +0 -0
  43. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/_rule.py +0 -0
  44. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/_typing.py +0 -0
  45. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/_validation.py +0 -0
  46. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/columns/__init__.py +0 -0
  47. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/columns/_base.py +0 -0
  48. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/columns/_mixins.py +0 -0
  49. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/columns/_utils.py +0 -0
  50. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/columns/any.py +0 -0
  51. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/columns/array.py +0 -0
  52. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/columns/bool.py +0 -0
  53. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/columns/decimal.py +0 -0
  54. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/columns/enum.py +0 -0
  55. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/columns/float.py +0 -0
  56. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/columns/integer.py +0 -0
  57. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/columns/list.py +0 -0
  58. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/columns/object.py +0 -0
  59. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/columns/string.py +0 -0
  60. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/columns/struct.py +0 -0
  61. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/config.py +0 -0
  62. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/exc.py +0 -0
  63. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/functional.py +0 -0
  64. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/mypy.py +0 -0
  65. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/py.typed +0 -0
  66. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/schema.py +0 -0
  67. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/testing/__init__.py +0 -0
  68. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/testing/const.py +0 -0
  69. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/testing/factory.py +0 -0
  70. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/testing/mask.py +0 -0
  71. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/testing/rules.py +0 -0
  72. {dataframely-1.4.0 → dataframely-1.5.0}/dataframely/testing/typing.py +0 -0
  73. {dataframely-1.4.0 → dataframely-1.5.0}/docker-compose.yml +0 -0
  74. {dataframely-1.4.0 → dataframely-1.5.0}/docs/Makefile +0 -0
  75. {dataframely-1.4.0 → dataframely-1.5.0}/docs/_api/dataframely.collection.rst +0 -0
  76. {dataframely-1.4.0 → dataframely-1.5.0}/docs/_api/dataframely.columns.any.rst +0 -0
  77. {dataframely-1.4.0 → dataframely-1.5.0}/docs/_api/dataframely.columns.bool.rst +0 -0
  78. {dataframely-1.4.0 → dataframely-1.5.0}/docs/_api/dataframely.columns.datetime.rst +0 -0
  79. {dataframely-1.4.0 → dataframely-1.5.0}/docs/_api/dataframely.columns.decimal.rst +0 -0
  80. {dataframely-1.4.0 → dataframely-1.5.0}/docs/_api/dataframely.columns.enum.rst +0 -0
  81. {dataframely-1.4.0 → dataframely-1.5.0}/docs/_api/dataframely.columns.float.rst +0 -0
  82. {dataframely-1.4.0 → dataframely-1.5.0}/docs/_api/dataframely.columns.integer.rst +0 -0
  83. {dataframely-1.4.0 → dataframely-1.5.0}/docs/_api/dataframely.columns.list.rst +0 -0
  84. {dataframely-1.4.0 → dataframely-1.5.0}/docs/_api/dataframely.columns.rst +0 -0
  85. {dataframely-1.4.0 → dataframely-1.5.0}/docs/_api/dataframely.columns.string.rst +0 -0
  86. {dataframely-1.4.0 → dataframely-1.5.0}/docs/_api/dataframely.columns.struct.rst +0 -0
  87. {dataframely-1.4.0 → dataframely-1.5.0}/docs/_api/dataframely.config.rst +0 -0
  88. {dataframely-1.4.0 → dataframely-1.5.0}/docs/_api/dataframely.exc.rst +0 -0
  89. {dataframely-1.4.0 → dataframely-1.5.0}/docs/_api/dataframely.failure.rst +0 -0
  90. {dataframely-1.4.0 → dataframely-1.5.0}/docs/_api/dataframely.functional.rst +0 -0
  91. {dataframely-1.4.0 → dataframely-1.5.0}/docs/_api/dataframely.mypy.rst +0 -0
  92. {dataframely-1.4.0 → dataframely-1.5.0}/docs/_api/dataframely.random.rst +0 -0
  93. {dataframely-1.4.0 → dataframely-1.5.0}/docs/_api/dataframely.rst +0 -0
  94. {dataframely-1.4.0 → dataframely-1.5.0}/docs/_api/dataframely.schema.rst +0 -0
  95. {dataframely-1.4.0 → dataframely-1.5.0}/docs/_api/dataframely.testing.const.rst +0 -0
  96. {dataframely-1.4.0 → dataframely-1.5.0}/docs/_api/dataframely.testing.factory.rst +0 -0
  97. {dataframely-1.4.0 → dataframely-1.5.0}/docs/_api/dataframely.testing.mask.rst +0 -0
  98. {dataframely-1.4.0 → dataframely-1.5.0}/docs/_api/dataframely.testing.rst +0 -0
  99. {dataframely-1.4.0 → dataframely-1.5.0}/docs/_api/dataframely.testing.rules.rst +0 -0
  100. {dataframely-1.4.0 → dataframely-1.5.0}/docs/_api/dataframely.testing.typing.rst +0 -0
  101. {dataframely-1.4.0 → dataframely-1.5.0}/docs/_api/modules.rst +0 -0
  102. {dataframely-1.4.0 → dataframely-1.5.0}/docs/_static/custom.css +0 -0
  103. {dataframely-1.4.0 → dataframely-1.5.0}/docs/_static/favicon.ico +0 -0
  104. {dataframely-1.4.0 → dataframely-1.5.0}/docs/conf.py +0 -0
  105. {dataframely-1.4.0 → dataframely-1.5.0}/docs/index.rst +0 -0
  106. {dataframely-1.4.0 → dataframely-1.5.0}/docs/make.bat +0 -0
  107. {dataframely-1.4.0 → dataframely-1.5.0}/docs/sites/development.rst +0 -0
  108. {dataframely-1.4.0 → dataframely-1.5.0}/docs/sites/examples/real-world.ipynb +0 -0
  109. {dataframely-1.4.0 → dataframely-1.5.0}/docs/sites/faq.rst +0 -0
  110. {dataframely-1.4.0 → dataframely-1.5.0}/docs/sites/installation.rst +0 -0
  111. {dataframely-1.4.0 → dataframely-1.5.0}/docs/sites/quickstart.rst +0 -0
  112. {dataframely-1.4.0 → dataframely-1.5.0}/docs/sites/versioning.rst +0 -0
  113. {dataframely-1.4.0 → dataframely-1.5.0}/src/errdefs.rs +0 -0
  114. {dataframely-1.4.0 → dataframely-1.5.0}/src/lib.rs +0 -0
  115. {dataframely-1.4.0 → dataframely-1.5.0}/src/regex_repr.rs +0 -0
  116. {dataframely-1.4.0 → dataframely-1.5.0}/tests/collection/test_base.py +0 -0
  117. {dataframely-1.4.0 → dataframely-1.5.0}/tests/collection/test_cast.py +0 -0
  118. {dataframely-1.4.0 → dataframely-1.5.0}/tests/collection/test_create_empty.py +0 -0
  119. {dataframely-1.4.0 → dataframely-1.5.0}/tests/collection/test_filter_one_to_n.py +0 -0
  120. {dataframely-1.4.0 → dataframely-1.5.0}/tests/collection/test_filter_validate.py +0 -0
  121. {dataframely-1.4.0 → dataframely-1.5.0}/tests/collection/test_ignore_in_filter.py +0 -0
  122. {dataframely-1.4.0 → dataframely-1.5.0}/tests/collection/test_implementation.py +0 -0
  123. {dataframely-1.4.0 → dataframely-1.5.0}/tests/collection/test_optional_members.py +0 -0
  124. {dataframely-1.4.0 → dataframely-1.5.0}/tests/collection/test_sample.py +0 -0
  125. {dataframely-1.4.0 → dataframely-1.5.0}/tests/collection/test_validate_input.py +0 -0
  126. {dataframely-1.4.0 → dataframely-1.5.0}/tests/column_types/__init__.py +0 -0
  127. {dataframely-1.4.0 → dataframely-1.5.0}/tests/column_types/test_any.py +0 -0
  128. {dataframely-1.4.0 → dataframely-1.5.0}/tests/column_types/test_array.py +0 -0
  129. {dataframely-1.4.0 → dataframely-1.5.0}/tests/column_types/test_decimal.py +0 -0
  130. {dataframely-1.4.0 → dataframely-1.5.0}/tests/column_types/test_enum.py +0 -0
  131. {dataframely-1.4.0 → dataframely-1.5.0}/tests/column_types/test_float.py +0 -0
  132. {dataframely-1.4.0 → dataframely-1.5.0}/tests/column_types/test_integer.py +0 -0
  133. {dataframely-1.4.0 → dataframely-1.5.0}/tests/column_types/test_list.py +0 -0
  134. {dataframely-1.4.0 → dataframely-1.5.0}/tests/column_types/test_object.py +0 -0
  135. {dataframely-1.4.0 → dataframely-1.5.0}/tests/column_types/test_string.py +0 -0
  136. {dataframely-1.4.0 → dataframely-1.5.0}/tests/column_types/test_struct.py +0 -0
  137. {dataframely-1.4.0 → dataframely-1.5.0}/tests/columns/__init__.py +0 -0
  138. {dataframely-1.4.0 → dataframely-1.5.0}/tests/columns/test_alias.py +0 -0
  139. {dataframely-1.4.0 → dataframely-1.5.0}/tests/columns/test_check.py +0 -0
  140. {dataframely-1.4.0 → dataframely-1.5.0}/tests/columns/test_default_dtypes.py +0 -0
  141. {dataframely-1.4.0 → dataframely-1.5.0}/tests/columns/test_metadata.py +0 -0
  142. {dataframely-1.4.0 → dataframely-1.5.0}/tests/columns/test_polars_schema.py +0 -0
  143. {dataframely-1.4.0 → dataframely-1.5.0}/tests/columns/test_rules.py +0 -0
  144. {dataframely-1.4.0 → dataframely-1.5.0}/tests/columns/test_sample.py +0 -0
  145. {dataframely-1.4.0 → dataframely-1.5.0}/tests/columns/test_sql_schema.py +0 -0
  146. {dataframely-1.4.0 → dataframely-1.5.0}/tests/columns/test_str.py +0 -0
  147. {dataframely-1.4.0 → dataframely-1.5.0}/tests/columns/test_utils.py +0 -0
  148. {dataframely-1.4.0 → dataframely-1.5.0}/tests/core_validation/__init__.py +0 -0
  149. {dataframely-1.4.0 → dataframely-1.5.0}/tests/core_validation/test_column_validation.py +0 -0
  150. {dataframely-1.4.0 → dataframely-1.5.0}/tests/core_validation/test_dtype_validation.py +0 -0
  151. {dataframely-1.4.0 → dataframely-1.5.0}/tests/core_validation/test_rule_evaluation.py +0 -0
  152. {dataframely-1.4.0 → dataframely-1.5.0}/tests/functional/test_concat.py +0 -0
  153. {dataframely-1.4.0 → dataframely-1.5.0}/tests/functional/test_relationships.py +0 -0
  154. {dataframely-1.4.0 → dataframely-1.5.0}/tests/schema/test_base.py +0 -0
  155. {dataframely-1.4.0 → dataframely-1.5.0}/tests/schema/test_cast.py +0 -0
  156. {dataframely-1.4.0 → dataframely-1.5.0}/tests/schema/test_create_empty.py +0 -0
  157. {dataframely-1.4.0 → dataframely-1.5.0}/tests/schema/test_create_empty_if_none.py +0 -0
  158. {dataframely-1.4.0 → dataframely-1.5.0}/tests/schema/test_filter.py +0 -0
  159. {dataframely-1.4.0 → dataframely-1.5.0}/tests/schema/test_inheritance.py +0 -0
  160. {dataframely-1.4.0 → dataframely-1.5.0}/tests/schema/test_rule_implementation.py +0 -0
  161. {dataframely-1.4.0 → dataframely-1.5.0}/tests/schema/test_sample.py +0 -0
  162. {dataframely-1.4.0 → dataframely-1.5.0}/tests/schema/test_validate.py +0 -0
  163. {dataframely-1.4.0 → dataframely-1.5.0}/tests/test_compat.py +0 -0
  164. {dataframely-1.4.0 → dataframely-1.5.0}/tests/test_config.py +0 -0
  165. {dataframely-1.4.0 → dataframely-1.5.0}/tests/test_deprecation.py +0 -0
  166. {dataframely-1.4.0 → dataframely-1.5.0}/tests/test_exc.py +0 -0
  167. {dataframely-1.4.0 → dataframely-1.5.0}/tests/test_extre.py +0 -0
  168. {dataframely-1.4.0 → dataframely-1.5.0}/tests/test_random.py +0 -0
@@ -1,5 +1,5 @@
1
1
  # This file is managed by Copier; DO NOT EDIT OR REMOVE.
2
- _commit: v0.3.0
2
+ _commit: v0.4.1
3
3
  _src_path: https://github.com/quantco/copier-template-python-open-source
4
4
  add_autobump_workflow: false
5
5
  author_email: oliver.borchert@quantco.com
@@ -0,0 +1 @@
1
+ pixi.lock merge=binary linguist-language=YAML linguist-generated=true
@@ -10,6 +10,8 @@ jobs:
10
10
  build-sdist:
11
11
  name: Build Sdist
12
12
  runs-on: ubuntu-latest
13
+ permissions:
14
+ contents: read
13
15
  steps:
14
16
  - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
15
17
  with:
@@ -21,14 +21,14 @@ jobs:
21
21
  steps:
22
22
  - name: Check valid conventional commit message
23
23
  id: lint
24
- uses: amannn/action-semantic-pull-request@v5
24
+ uses: amannn/action-semantic-pull-request@0723387faaf9b38adef4775cd42cfd5155ed6017 # v5.5.3
25
25
  with:
26
26
  subjectPattern: ^[A-Z].+[^. ]$ # subject must start with uppercase letter and may not end with a dot/space
27
27
  env:
28
28
  GITHUB_TOKEN: ${{ github.token }}
29
29
  - name: Post comment about invalid PR title
30
30
  if: failure()
31
- uses: marocchino/sticky-pull-request-comment@v2
31
+ uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 # v2.9.1
32
32
  with:
33
33
  header: conventional-commit-pr-title
34
34
  message: |
@@ -45,7 +45,7 @@ jobs:
45
45
  </details>
46
46
  - name: Delete comment about invalid PR title
47
47
  if: success()
48
- uses: marocchino/sticky-pull-request-comment@v2
48
+ uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 # v2.9.1
49
49
  with:
50
50
  header: conventional-commit-pr-title
51
51
  delete: true
@@ -58,7 +58,7 @@ jobs:
58
58
  pull-requests: write
59
59
  steps:
60
60
  - name: ${{ github.event_name == 'pull_request_target' && 'Assign labels' || 'Update release draft' }}
61
- uses: release-drafter/release-drafter@v6
61
+ uses: release-drafter/release-drafter@b1476f6e6eb133afa41ed8589daba6dc69b4d3f5 # v6.1.0
62
62
  with:
63
63
  disable-releaser: ${{ github.event_name == 'pull_request_target' }}
64
64
  disable-autolabeler: ${{ github.event_name == 'push' }}
@@ -9,6 +9,9 @@ concurrency:
9
9
  group: ${{ github.workflow }}-${{ github.ref }}
10
10
  cancel-in-progress: true
11
11
 
12
+ permissions:
13
+ contents: read
14
+
12
15
  jobs:
13
16
  pre-commit-checks:
14
17
  name: Pre-commit Checks
@@ -40,7 +43,7 @@ jobs:
40
43
  environment: [py311, py312, py313]
41
44
  steps:
42
45
  - name: Checkout branch
43
- uses: actions/checkout@v4
46
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
44
47
  - name: Set up pixi
45
48
  uses: prefix-dev/setup-pixi@19eac09b398e3d0c747adc7921926a6d802df4da # v0.8.8
46
49
  with:
@@ -50,7 +53,7 @@ jobs:
50
53
  - name: Run pytest
51
54
  run: pixi run -e ${{ matrix.environment }} test-coverage --color=yes
52
55
  - name: Upload codecov
53
- uses: codecov/codecov-action@v5
56
+ uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3
54
57
  with:
55
58
  files: ./coverage.xml
56
59
  token: ${{ secrets.CODECOV_TOKEN }}
@@ -0,0 +1,79 @@
1
+ # This workflow uses actions that are not certified by GitHub. They are provided
2
+ # by a third-party and are governed by separate terms of service, privacy
3
+ # policy, and support documentation.
4
+
5
+ name: Scorecard supply-chain security
6
+ on:
7
+ # For Branch-Protection check. Only the default branch is supported. See
8
+ # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
9
+ branch_protection_rule:
10
+ # To guarantee Maintained check is occasionally updated. See
11
+ # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
12
+ schedule:
13
+ - cron: "34 5 * * 0"
14
+ workflow_dispatch:
15
+ push:
16
+ branches: ["main"]
17
+
18
+ # Declare default permissions as read only.
19
+ permissions: read-all
20
+
21
+ jobs:
22
+ analysis:
23
+ name: Scorecard analysis
24
+ runs-on: ubuntu-latest
25
+ # `publish_results: true` only works when run from the default branch. conditional can be removed if disabled.
26
+ if: (github.event.repository.default_branch == github.ref_name || github.event_name == 'pull_request') && github.repository == 'quantco/dataframely'
27
+ permissions:
28
+ # Needed to upload the results to code-scanning dashboard.
29
+ security-events: write
30
+ # Needed to publish results and get a badge (see publish_results below).
31
+ id-token: write
32
+ # Uncomment the permissions below if installing in a private repository.
33
+ # contents: read
34
+ # actions: read
35
+
36
+ steps:
37
+ - name: "Checkout code"
38
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
39
+ with:
40
+ persist-credentials: false
41
+
42
+ - name: "Run analysis"
43
+ uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2
44
+ with:
45
+ results_file: results.sarif
46
+ results_format: sarif
47
+ # (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
48
+ # - you want to enable the Branch-Protection check on a *public* repository, or
49
+ # - you are installing Scorecard on a *private* repository
50
+ # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action?tab=readme-ov-file#authentication-with-fine-grained-pat-optional.
51
+ # repo_token: ${{ secrets.SCORECARD_TOKEN }}
52
+
53
+ # Public repositories:
54
+ # - Publish results to OpenSSF REST API for easy access by consumers
55
+ # - Allows the repository to include the Scorecard badge.
56
+ # - See https://github.com/ossf/scorecard-action#publishing-results.
57
+ # For private repositories:
58
+ # - `publish_results` will always be set to `false`, regardless
59
+ # of the value entered here.
60
+ publish_results: true
61
+
62
+ # (Optional) Uncomment file_mode if you have a .gitattributes with files marked export-ignore
63
+ # file_mode: git
64
+
65
+ # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
66
+ # format to the repository Actions tab.
67
+ - name: "Upload artifact"
68
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
69
+ with:
70
+ name: SARIF file
71
+ path: results.sarif
72
+ retention-days: 5
73
+
74
+ # Upload the results to GitHub's code scanning dashboard (optional).
75
+ # Commenting out will disable upload of results to your repo's Code Scanning dashboard
76
+ - name: "Upload to code-scanning"
77
+ uses: github/codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
78
+ with:
79
+ sarif_file: results.sarif
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dataframely
3
- Version: 1.4.0
3
+ Version: 1.5.0
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: 3.11
6
6
  Classifier: Programming Language :: Python :: 3.12
7
7
  Classifier: Programming Language :: Python :: 3.13
8
8
  Requires-Dist: numpy
9
- Requires-Dist: polars>=1.12
9
+ Requires-Dist: polars>=1.30
10
10
  License-File: LICENSE
11
11
  Summary: A declarative, polars-native data frame validation library
12
12
  Author-email: Andreas Albert <andreas.albert@quantco.com>, Daniel Elsner <daniel.elsner@quantco.com>, Oliver Borchert <oliver.borchert@quantco.com>
@@ -0,0 +1,7 @@
1
+ # Reporting Security Issues
2
+
3
+ We take security bugs in our projects seriously. We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions.
4
+
5
+ To report a security issue, please use the GitHub Security Advisory ["Report a Vulnerability"](https://github.com/quantco/dataframely/security/advisories/new) tab.
6
+
7
+ We will send a response indicating the next steps in handling your report. After the initial reply to your report, the security team will keep you informed of the progress towards a fix and full announcement, and may ask for additional information or guidance.
@@ -1,12 +1,11 @@
1
1
  # Copyright (c) QuantCo 2025-2025
2
2
  # SPDX-License-Identifier: BSD-3-Clause
3
3
 
4
- import sys
5
4
  import warnings
6
5
  from abc import ABC
7
6
  from collections.abc import Mapping, Sequence
8
7
  from pathlib import Path
9
- from typing import Any, Generic, Self, TypeVar, cast
8
+ from typing import Any, Self, cast
10
9
 
11
10
  import polars as pl
12
11
  import polars.exceptions as plexc
@@ -18,15 +17,8 @@ from ._polars import FrameType, join_all_inner, join_all_outer
18
17
  from .failure import FailureInfo
19
18
  from .random import Generator
20
19
 
21
- if sys.version_info >= (3, 13):
22
- SamplingType = TypeVar(
23
- "SamplingType", bound=Mapping[str, Any], default=Mapping[str, Any]
24
- )
25
- else: # pragma: no cover
26
- SamplingType = TypeVar("SamplingType", bound=Mapping[str, Any])
27
20
 
28
-
29
- class Collection(BaseCollection, ABC, Generic[SamplingType]):
21
+ class Collection(BaseCollection, ABC):
30
22
  """Base class for all collections of data frames with a predefined schema.
31
23
 
32
24
  A collection is comprised of a set of *members* which are collectively "consistent",
@@ -86,7 +78,7 @@ class Collection(BaseCollection, ABC, Generic[SamplingType]):
86
78
  cls,
87
79
  num_rows: int | None = None,
88
80
  *,
89
- overrides: Sequence[SamplingType] | None = None,
81
+ overrides: Sequence[Mapping[str, Any]] | None = None,
90
82
  generator: Generator | None = None,
91
83
  ) -> Self:
92
84
  """Create a random sample from the members of this collection.
@@ -162,10 +154,11 @@ class Collection(BaseCollection, ABC, Generic[SamplingType]):
162
154
  samples = (
163
155
  overrides
164
156
  if overrides is not None
165
- else [cast(SamplingType, {}) for _ in range(cast(int, num_rows))]
157
+ else [{} for _ in range(cast(int, num_rows))]
166
158
  )
167
159
  processed_samples = [
168
- cls._preprocess_sample(sample, i, g) for i, sample in enumerate(samples)
160
+ cls._preprocess_sample(dict(sample.items()), i, g)
161
+ for i, sample in enumerate(samples)
169
162
  ]
170
163
 
171
164
  # 2) Ensure that all samples have primary keys assigned to ensure that we
@@ -234,8 +227,8 @@ class Collection(BaseCollection, ABC, Generic[SamplingType]):
234
227
 
235
228
  @classmethod
236
229
  def _preprocess_sample(
237
- cls, sample: SamplingType, index: int, generator: Generator
238
- ) -> SamplingType:
230
+ cls, sample: dict[str, Any], index: int, generator: Generator
231
+ ) -> dict[str, Any]:
239
232
  """Overridable method to preprocess a sample passed to :meth:`sample`.
240
233
 
241
234
  The purpose of this method is to (1) set the primary key columns to enable
@@ -8,6 +8,7 @@ from collections.abc import Callable
8
8
  from typing import Any, cast
9
9
 
10
10
  import polars as pl
11
+ from polars._typing import TimeUnit
11
12
 
12
13
  from dataframely._compat import pa, sa, sa_mssql, sa_TypeEngine
13
14
  from dataframely._polars import (
@@ -300,6 +301,7 @@ class Datetime(OrdinalMixin[dt.datetime], Column):
300
301
  max_exclusive: dt.datetime | None = None,
301
302
  resolution: str | None = None,
302
303
  time_zone: str | dt.tzinfo | None = None,
304
+ time_unit: TimeUnit = "us",
303
305
  check: (
304
306
  Callable[[pl.Expr], pl.Expr]
305
307
  | list[Callable[[pl.Expr], pl.Expr]]
@@ -330,6 +332,7 @@ class Datetime(OrdinalMixin[dt.datetime], Column):
330
332
  time_zone: The time zone that datetimes in the column must have. The time
331
333
  zone must use a valid IANA time zone name identifier e.x. ``Etc/UTC`` or
332
334
  ``America/New_York``.
335
+ time_unit: Unit of time. Defaults to ``us`` (microseconds).
333
336
  check: A custom rule or multiple rules to run for this column. This can be:
334
337
  - A single callable that returns a non-aggregated boolean expression.
335
338
  The name of the rule is derived from the callable name, or defaults to
@@ -373,10 +376,11 @@ class Datetime(OrdinalMixin[dt.datetime], Column):
373
376
  )
374
377
  self.resolution = resolution
375
378
  self.time_zone = time_zone
379
+ self.time_unit = time_unit
376
380
 
377
381
  @property
378
382
  def dtype(self) -> pl.DataType:
379
- return pl.Datetime(time_zone=self.time_zone)
383
+ return pl.Datetime(time_zone=self.time_zone, time_unit=self.time_unit)
380
384
 
381
385
  def validation_rules(self, expr: pl.Expr) -> dict[str, pl.Expr]:
382
386
  result = super().validation_rules(expr)
@@ -400,7 +404,7 @@ class Datetime(OrdinalMixin[dt.datetime], Column):
400
404
  if isinstance(self.time_zone, dt.tzinfo)
401
405
  else self.time_zone
402
406
  )
403
- return pa.timestamp("us", time_zone)
407
+ return pa.timestamp(self.time_unit, time_zone)
404
408
 
405
409
  def _sample_unchecked(self, generator: Generator, n: int) -> pl.Series:
406
410
  return generator.sample_datetime(
@@ -416,6 +420,7 @@ class Datetime(OrdinalMixin[dt.datetime], Column):
416
420
  allow_null_response=True,
417
421
  ),
418
422
  resolution=self.resolution,
423
+ time_unit=self.time_unit,
419
424
  time_zone=self.time_zone,
420
425
  null_probability=self._null_probability,
421
426
  )
@@ -79,17 +79,13 @@ class FailureInfo(Generic[S]):
79
79
  Args:
80
80
  file: The file path or writable file-like object to write to.
81
81
  """
82
- # NOTE: We add a dummy column with metadata in the column name to allow writing
83
- # the rule columns and schema to the same file.
84
82
  metadata_json = json.dumps(
85
83
  {
86
84
  "rule_columns": self._rule_columns,
87
85
  "schema": f"{self.schema.__module__}.{self.schema.__name__}",
88
86
  }
89
87
  )
90
- self._df.with_columns(
91
- pl.lit(None).alias(metadata_json),
92
- ).write_parquet(file)
88
+ self._df.write_parquet(file, metadata={"dataframely": metadata_json})
93
89
 
94
90
  @classmethod
95
91
  def scan_parquet(cls, source: str | Path | IO[bytes]) -> Self:
@@ -102,15 +98,27 @@ class FailureInfo(Generic[S]):
102
98
  The failure info object.
103
99
  """
104
100
  lf = pl.scan_parquet(source)
105
- # NOTE: In `write_parquet`, the rule columns are added as the name of the last
106
- # column.
107
- last_column = lf.collect_schema().names()[-1]
108
- metadata = json.loads(last_column)
109
- rule_columns = metadata["rule_columns"]
110
- *schema_module_parts, schema_name = metadata["schema"].split(".")
101
+
102
+ # We can read the rule columns either from the metadata of the Parquet file
103
+ # or, to remain backwards-compatible, from the last column of the lazy frame if
104
+ # the parquet file is missing metadata.
105
+ rule_columns: list[str]
106
+ schema_name: str
107
+ if (meta := pl.read_parquet_metadata(source).get("dataframely")) is not None:
108
+ metadata = json.loads(meta)
109
+ rule_columns = metadata["rule_columns"]
110
+ schema_name = metadata["schema"]
111
+ else:
112
+ last_column = lf.collect_schema().names()[-1]
113
+ metadata = json.loads(last_column)
114
+ rule_columns = metadata["rule_columns"]
115
+ schema_name = metadata["schema"]
116
+ lf = lf.drop(last_column)
117
+
118
+ *schema_module_parts, schema_name = schema_name.split(".")
111
119
  module = importlib.import_module(".".join(schema_module_parts))
112
120
  schema = cast(type[S], getattr(module, schema_name))
113
- return cls(lf.drop(last_column), rule_columns, schema=schema)
121
+ return cls(lf, rule_columns, schema=schema)
114
122
 
115
123
 
116
124
  # ------------------------------------ COMPUTATION ----------------------------------- #
@@ -7,6 +7,7 @@ from typing import TypeVar
7
7
 
8
8
  import numpy as np
9
9
  import polars as pl
10
+ from polars._typing import TimeUnit
10
11
 
11
12
  from ._extre import sample as extre_sample
12
13
  from ._polars import (
@@ -294,6 +295,7 @@ class Generator:
294
295
  max: dt.datetime | None,
295
296
  resolution: str | None = None,
296
297
  time_zone: str | dt.tzinfo | None = None,
298
+ time_unit: TimeUnit = "us",
297
299
  null_probability: float = 0.0,
298
300
  ) -> pl.Series:
299
301
  """Sample a list of datetimes in the provided range.
@@ -303,7 +305,9 @@ class Generator:
303
305
  min: The minimum datetime to sample (inclusive).
304
306
  max: The maximum datetime to sample (exclusive). '10000-01-01' when ``None``.
305
307
  resolution: The resolution that datetimes in the column must have. This uses
306
- the formatting language used by :mod:`polars` datetime ``round`` method.
308
+ the formatting language used by :mod:`polars` datetime ``round``
309
+ method.
310
+ time_unit: The time unit of the datetime column. Defaults to ``us`` (microseconds).
307
311
  time_zone: The time zone that datetimes in the column must have. The time
308
312
  zone must use a valid IANA time zone name identifier e.x. ``Etc/UTC`` or
309
313
  ``America/New_York``.
@@ -333,7 +337,7 @@ class Generator:
333
337
  )
334
338
  # NOTE: polars tracks datetimes relative to epoch
335
339
  - _datetime_to_microseconds(EPOCH_DATETIME)
336
- ).cast(pl.Datetime(time_zone=time_zone))
340
+ ).cast(pl.Datetime(time_unit=time_unit, time_zone=time_zone))
337
341
 
338
342
  if resolution is not None:
339
343
  return result.dt.truncate(resolution)