dataframely 1.0.0__tar.gz → 1.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dataframely-1.0.0 → dataframely-1.1.0}/.github/workflows/build.yml +5 -5
- {dataframely-1.0.0 → dataframely-1.1.0}/.github/workflows/chore.yml +5 -5
- {dataframely-1.0.0 → dataframely-1.1.0}/.github/workflows/ci.yml +3 -2
- {dataframely-1.0.0 → dataframely-1.1.0}/.github/workflows/update-lockfiles.yml +2 -2
- {dataframely-1.0.0 → dataframely-1.1.0}/PKG-INFO +3 -2
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/_base_collection.py +33 -2
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/_base_schema.py +2 -2
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/_compat.py +1 -1
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/_filter.py +1 -1
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/_rule.py +2 -2
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/collection.py +11 -4
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/columns/_mixins.py +1 -1
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/columns/decimal.py +3 -1
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/config.py +5 -5
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/exc.py +11 -7
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/failure.py +4 -2
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/mypy.py +7 -6
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/random.py +1 -1
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/conf.py +1 -1
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/sites/quickstart.rst +1 -1
- {dataframely-1.0.0 → dataframely-1.1.0}/pyproject.toml +4 -2
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/collection/test_base.py +13 -11
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/collection/test_cast.py +5 -5
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/collection/test_create_empty.py +1 -1
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/collection/test_filter_one_to_n.py +1 -1
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/collection/test_filter_validate.py +8 -8
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/collection/test_ignore_in_filter.py +3 -3
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/collection/test_implementation.py +14 -14
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/collection/test_optional_members.py +3 -3
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/collection/test_sample.py +71 -7
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/collection/test_validate_input.py +2 -2
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/column_types/test_any.py +1 -1
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/column_types/test_datetime.py +12 -6
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/column_types/test_decimal.py +7 -7
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/column_types/test_enum.py +2 -2
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/column_types/test_float.py +19 -17
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/column_types/test_integer.py +14 -12
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/column_types/test_list.py +11 -11
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/column_types/test_string.py +4 -4
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/column_types/test_struct.py +8 -8
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/columns/test_alias.py +3 -3
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/columns/test_check.py +1 -1
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/columns/test_default_dtypes.py +1 -1
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/columns/test_metadata.py +1 -1
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/columns/test_pyarrow.py +9 -9
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/columns/test_rules.py +4 -4
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/columns/test_sample.py +20 -16
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/columns/test_sql_schema.py +7 -7
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/columns/test_str.py +4 -4
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/columns/test_utils.py +5 -5
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/core_validation/test_column_validation.py +2 -2
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/core_validation/test_dtype_validation.py +4 -4
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/core_validation/test_rule_evaluation.py +7 -7
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/functional/test_concat.py +2 -2
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/functional/test_relationships.py +2 -2
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/schema/test_base.py +8 -8
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/schema/test_cast.py +3 -3
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/schema/test_create_empty.py +2 -2
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/schema/test_create_empty_if_none.py +2 -2
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/schema/test_filter.py +11 -7
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/schema/test_inheritance.py +1 -1
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/schema/test_rule_implementation.py +4 -4
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/schema/test_sample.py +11 -11
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/schema/test_validate.py +9 -7
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/test_compat.py +1 -1
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/test_config.py +4 -4
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/test_exc.py +3 -3
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/test_extre.py +9 -9
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/test_failure_info.py +1 -1
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/test_random.py +14 -12
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/test_typing.py +14 -12
- dataframely-1.0.0/tests/schema/__init__.py +0 -2
- {dataframely-1.0.0 → dataframely-1.1.0}/.copier-answers.yml +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/.envrc +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/.gitattributes +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/.github/CODEOWNERS +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/.github/dependabot.yml +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/.github/release-drafter.yml +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/.gitignore +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/.pre-commit-config.yaml +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/.prettierignore +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/.prettierrc +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/.readthedocs.yml +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/Cargo.lock +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/Cargo.toml +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/LICENSE +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/README.md +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/__init__.py +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/_extre.pyi +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/_polars.py +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/_typing.py +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/_validation.py +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/columns/__init__.py +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/columns/_base.py +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/columns/_utils.py +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/columns/any.py +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/columns/bool.py +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/columns/datetime.py +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/columns/enum.py +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/columns/float.py +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/columns/integer.py +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/columns/list.py +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/columns/string.py +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/columns/struct.py +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/functional.py +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/py.typed +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/schema.py +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/testing/__init__.py +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/testing/const.py +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/testing/factory.py +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/testing/mask.py +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/testing/rules.py +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/dataframely/testing/typing.py +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docker-compose.yml +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/Makefile +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/_api/dataframely.collection.rst +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/_api/dataframely.columns.any.rst +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/_api/dataframely.columns.bool.rst +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/_api/dataframely.columns.datetime.rst +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/_api/dataframely.columns.decimal.rst +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/_api/dataframely.columns.enum.rst +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/_api/dataframely.columns.float.rst +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/_api/dataframely.columns.integer.rst +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/_api/dataframely.columns.list.rst +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/_api/dataframely.columns.rst +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/_api/dataframely.columns.string.rst +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/_api/dataframely.columns.struct.rst +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/_api/dataframely.config.rst +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/_api/dataframely.exc.rst +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/_api/dataframely.failure.rst +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/_api/dataframely.functional.rst +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/_api/dataframely.mypy.rst +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/_api/dataframely.random.rst +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/_api/dataframely.rst +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/_api/dataframely.schema.rst +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/_api/dataframely.testing.const.rst +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/_api/dataframely.testing.factory.rst +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/_api/dataframely.testing.mask.rst +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/_api/dataframely.testing.rst +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/_api/dataframely.testing.rules.rst +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/_api/dataframely.testing.typing.rst +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/_api/modules.rst +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/_static/custom.css +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/_static/favicon.ico +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/index.rst +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/make.bat +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/sites/development.rst +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/sites/examples/real-world.ipynb +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/sites/faq.rst +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/docs/sites/installation.rst +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/pixi.lock +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/pixi.toml +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/src/errdefs.rs +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/src/lib.rs +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/src/regex_repr.rs +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/column_types/__init__.py +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/columns/__init__.py +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/columns/test_polars_schema.py +0 -0
- {dataframely-1.0.0 → dataframely-1.1.0}/tests/core_validation/__init__.py +0 -0
|
@@ -15,7 +15,7 @@ jobs:
|
|
|
15
15
|
with:
|
|
16
16
|
fetch-depth: 0
|
|
17
17
|
- name: Set up pixi
|
|
18
|
-
uses: prefix-dev/setup-pixi@
|
|
18
|
+
uses: prefix-dev/setup-pixi@19eac09b398e3d0c747adc7921926a6d802df4da # v0.8.8
|
|
19
19
|
with:
|
|
20
20
|
environments: build
|
|
21
21
|
- name: Set version
|
|
@@ -23,7 +23,7 @@ jobs:
|
|
|
23
23
|
- name: Build project
|
|
24
24
|
run: pixi run -e build build-sdist
|
|
25
25
|
- name: Upload package
|
|
26
|
-
uses: actions/upload-artifact@
|
|
26
|
+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
|
27
27
|
with:
|
|
28
28
|
name: sdist
|
|
29
29
|
path: dist/*
|
|
@@ -50,7 +50,7 @@ jobs:
|
|
|
50
50
|
with:
|
|
51
51
|
fetch-depth: 0
|
|
52
52
|
- name: Set up pixi
|
|
53
|
-
uses: prefix-dev/setup-pixi@
|
|
53
|
+
uses: prefix-dev/setup-pixi@19eac09b398e3d0c747adc7921926a6d802df4da # v0.8.8
|
|
54
54
|
with:
|
|
55
55
|
environments: build
|
|
56
56
|
- name: Set version
|
|
@@ -64,7 +64,7 @@ jobs:
|
|
|
64
64
|
- name: Check package
|
|
65
65
|
run: pixi run -e build check-wheel
|
|
66
66
|
- name: Upload package
|
|
67
|
-
uses: actions/upload-artifact@
|
|
67
|
+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
|
68
68
|
with:
|
|
69
69
|
name: wheel-${{ matrix.target-platform }}
|
|
70
70
|
path: dist/*
|
|
@@ -78,7 +78,7 @@ jobs:
|
|
|
78
78
|
id-token: write
|
|
79
79
|
environment: pypi
|
|
80
80
|
steps:
|
|
81
|
-
- uses: actions/download-artifact@
|
|
81
|
+
- uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
|
|
82
82
|
with:
|
|
83
83
|
path: dist
|
|
84
84
|
merge-multiple: true
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
name: Chore
|
|
2
2
|
on:
|
|
3
|
-
|
|
3
|
+
pull_request_target:
|
|
4
4
|
branches: [main]
|
|
5
5
|
types: [opened, reopened, edited, synchronize]
|
|
6
6
|
push:
|
|
@@ -13,7 +13,7 @@ concurrency:
|
|
|
13
13
|
jobs:
|
|
14
14
|
check-pr-title:
|
|
15
15
|
name: Check PR Title
|
|
16
|
-
if: github.event_name == '
|
|
16
|
+
if: github.event_name == 'pull_request_target'
|
|
17
17
|
runs-on: ubuntu-latest
|
|
18
18
|
permissions:
|
|
19
19
|
contents: read
|
|
@@ -51,16 +51,16 @@ jobs:
|
|
|
51
51
|
delete: true
|
|
52
52
|
|
|
53
53
|
release-drafter:
|
|
54
|
-
name: ${{ github.event_name == '
|
|
54
|
+
name: ${{ github.event_name == 'pull_request_target' && 'Assign Labels' || 'Draft Release' }}
|
|
55
55
|
runs-on: ubuntu-latest
|
|
56
56
|
permissions:
|
|
57
57
|
contents: write
|
|
58
58
|
pull-requests: write
|
|
59
59
|
steps:
|
|
60
|
-
- name: ${{ github.event_name == '
|
|
60
|
+
- name: ${{ github.event_name == 'pull_request_target' && 'Assign labels' || 'Update release draft' }}
|
|
61
61
|
uses: release-drafter/release-drafter@v6
|
|
62
62
|
with:
|
|
63
|
-
disable-releaser: ${{ github.event_name == '
|
|
63
|
+
disable-releaser: ${{ github.event_name == 'pull_request_target' }}
|
|
64
64
|
disable-autolabeler: ${{ github.event_name == 'push' }}
|
|
65
65
|
env:
|
|
66
66
|
GITHUB_TOKEN: ${{ github.token }}
|
|
@@ -21,7 +21,7 @@ jobs:
|
|
|
21
21
|
# needed for 'pre-commit-mirrors-insert-license'
|
|
22
22
|
fetch-depth: 0
|
|
23
23
|
- name: Set up pixi
|
|
24
|
-
uses: prefix-dev/setup-pixi@
|
|
24
|
+
uses: prefix-dev/setup-pixi@19eac09b398e3d0c747adc7921926a6d802df4da # v0.8.8
|
|
25
25
|
with:
|
|
26
26
|
environments: default lint
|
|
27
27
|
- name: Install repository
|
|
@@ -42,7 +42,7 @@ jobs:
|
|
|
42
42
|
- name: Checkout branch
|
|
43
43
|
uses: actions/checkout@v4
|
|
44
44
|
- name: Set up pixi
|
|
45
|
-
uses: prefix-dev/setup-pixi@
|
|
45
|
+
uses: prefix-dev/setup-pixi@19eac09b398e3d0c747adc7921926a6d802df4da # v0.8.8
|
|
46
46
|
with:
|
|
47
47
|
environments: ${{ matrix.environment }}
|
|
48
48
|
- name: Install repository
|
|
@@ -53,3 +53,4 @@ jobs:
|
|
|
53
53
|
uses: codecov/codecov-action@v5
|
|
54
54
|
with:
|
|
55
55
|
files: ./coverage.xml
|
|
56
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
@@ -15,13 +15,13 @@ jobs:
|
|
|
15
15
|
steps:
|
|
16
16
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
|
17
17
|
- name: Set up pixi
|
|
18
|
-
uses: prefix-dev/setup-pixi@
|
|
18
|
+
uses: prefix-dev/setup-pixi@19eac09b398e3d0c747adc7921926a6d802df4da # v0.8.8
|
|
19
19
|
with:
|
|
20
20
|
run-install: false
|
|
21
21
|
- name: Update lockfiles
|
|
22
22
|
run: pixi update --json --no-install | pixi exec pixi-diff-to-markdown >> diff.md
|
|
23
23
|
- name: Create pull request
|
|
24
|
-
uses: peter-evans/create-pull-request@
|
|
24
|
+
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
|
|
25
25
|
with:
|
|
26
26
|
token: ${{ github.token }}
|
|
27
27
|
commit-message: Update pixi lockfile
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dataframely
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.0
|
|
4
4
|
Classifier: Programming Language :: Python :: 3
|
|
5
5
|
Classifier: Programming Language :: Python :: 3.11
|
|
6
6
|
Classifier: Programming Language :: Python :: 3.12
|
|
@@ -12,7 +12,8 @@ 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>
|
|
13
13
|
Requires-Python: >=3.11
|
|
14
14
|
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
15
|
-
Project-URL:
|
|
15
|
+
Project-URL: Repository, https://github.com/quantco/dataframely
|
|
16
|
+
Project-URL: Documentation, https://dataframely.readthedocs.io/
|
|
16
17
|
|
|
17
18
|
<!-- LOGO -->
|
|
18
19
|
<br />
|
|
@@ -48,6 +48,12 @@ class CollectionMember:
|
|
|
48
48
|
|
|
49
49
|
#: Whether the member should be ignored in the filter method.
|
|
50
50
|
ignored_in_filters: bool = False
|
|
51
|
+
#: Whether the member's non-primary key columns should be inlined for sampling.
|
|
52
|
+
#: This means that value overrides are supplied on the top-level rather than in
|
|
53
|
+
#: a subkey with the member's name. Only valid if the member's primary key matches
|
|
54
|
+
#: the collection's common primary key. Two members that share common column names
|
|
55
|
+
#: may not both be inlined for sampling.
|
|
56
|
+
inline_for_sampling: bool = False
|
|
51
57
|
|
|
52
58
|
|
|
53
59
|
# --------------------------------------- UTILS -------------------------------------- #
|
|
@@ -79,7 +85,7 @@ class Metadata:
|
|
|
79
85
|
members: dict[str, MemberInfo] = field(default_factory=dict)
|
|
80
86
|
filters: dict[str, Filter] = field(default_factory=dict)
|
|
81
87
|
|
|
82
|
-
def update(self, other: Self):
|
|
88
|
+
def update(self, other: Self) -> None:
|
|
83
89
|
self.members.update(other.members)
|
|
84
90
|
self.filters.update(other.filters)
|
|
85
91
|
|
|
@@ -92,7 +98,7 @@ class CollectionMeta(ABCMeta):
|
|
|
92
98
|
namespace: dict[str, Any],
|
|
93
99
|
*args: Any,
|
|
94
100
|
**kwargs: Any,
|
|
95
|
-
):
|
|
101
|
+
) -> CollectionMeta:
|
|
96
102
|
result = Metadata()
|
|
97
103
|
for base in bases:
|
|
98
104
|
result.update(mcs._get_metadata_recursively(base))
|
|
@@ -136,6 +142,30 @@ class CollectionMeta(ABCMeta):
|
|
|
136
142
|
f"{len(intersection)} such filters: {sorted(intersection)}."
|
|
137
143
|
)
|
|
138
144
|
|
|
145
|
+
# 3) Check that inlining for sampling is configured correctly.
|
|
146
|
+
if len(non_ignored_member_schemas) > 0:
|
|
147
|
+
common_primary_keys = _common_primary_keys(non_ignored_member_schemas)
|
|
148
|
+
inlined_columns: set[str] = set()
|
|
149
|
+
for member, info in result.members.items():
|
|
150
|
+
if info.inline_for_sampling:
|
|
151
|
+
if set(info.schema.primary_keys()) != common_primary_keys:
|
|
152
|
+
raise ImplementationError(
|
|
153
|
+
f"Member '{member}' is inlined for sampling but its primary "
|
|
154
|
+
"key is a superset of the common primary key. Such a member "
|
|
155
|
+
"must not be inlined to be able to provide multiple values "
|
|
156
|
+
"for a single combination of the common primary key."
|
|
157
|
+
)
|
|
158
|
+
non_primary_key_columns = (
|
|
159
|
+
set(info.schema.column_names()) - common_primary_keys
|
|
160
|
+
)
|
|
161
|
+
if len(inlined_columns & non_primary_key_columns):
|
|
162
|
+
raise ImplementationError(
|
|
163
|
+
f"At least one column name of member '{member}' clashes "
|
|
164
|
+
"with a column name of another member that is inlined for "
|
|
165
|
+
"sampling."
|
|
166
|
+
)
|
|
167
|
+
inlined_columns.update(non_primary_key_columns)
|
|
168
|
+
|
|
139
169
|
return super().__new__(mcs, name, bases, namespace, *args, **kwargs)
|
|
140
170
|
|
|
141
171
|
@staticmethod
|
|
@@ -201,6 +231,7 @@ class CollectionMeta(ABCMeta):
|
|
|
201
231
|
schema=get_args(kls)[0],
|
|
202
232
|
is_optional=False,
|
|
203
233
|
ignored_in_filters=collection_member.ignored_in_filters,
|
|
234
|
+
inline_for_sampling=collection_member.inline_for_sampling,
|
|
204
235
|
)
|
|
205
236
|
else:
|
|
206
237
|
# Some other unknown annotation
|
|
@@ -58,7 +58,7 @@ class Metadata:
|
|
|
58
58
|
columns: dict[str, Column] = field(default_factory=dict)
|
|
59
59
|
rules: dict[str, Rule] = field(default_factory=dict)
|
|
60
60
|
|
|
61
|
-
def update(self, other: Self):
|
|
61
|
+
def update(self, other: Self) -> None:
|
|
62
62
|
self.columns.update(other.columns)
|
|
63
63
|
self.rules.update(other.rules)
|
|
64
64
|
|
|
@@ -71,7 +71,7 @@ class SchemaMeta(ABCMeta):
|
|
|
71
71
|
namespace: dict[str, Any],
|
|
72
72
|
*args: Any,
|
|
73
73
|
**kwargs: Any,
|
|
74
|
-
):
|
|
74
|
+
) -> SchemaMeta:
|
|
75
75
|
result = Metadata()
|
|
76
76
|
for base in bases:
|
|
77
77
|
result.update(mcs._get_metadata_recursively(base))
|
|
@@ -12,7 +12,7 @@ C = TypeVar("C")
|
|
|
12
12
|
class Filter(Generic[C]):
|
|
13
13
|
"""Internal class representing logic for filtering members of a collection."""
|
|
14
14
|
|
|
15
|
-
def __init__(self, logic: Callable[[C], pl.LazyFrame]):
|
|
15
|
+
def __init__(self, logic: Callable[[C], pl.LazyFrame]) -> None:
|
|
16
16
|
self.logic = logic
|
|
17
17
|
|
|
18
18
|
|
|
@@ -12,14 +12,14 @@ ValidationFunction = Callable[[], pl.Expr]
|
|
|
12
12
|
class Rule:
|
|
13
13
|
"""Internal class representing validation rules."""
|
|
14
14
|
|
|
15
|
-
def __init__(self, expr: pl.Expr):
|
|
15
|
+
def __init__(self, expr: pl.Expr) -> None:
|
|
16
16
|
self.expr = expr
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
class GroupRule(Rule):
|
|
20
20
|
"""Rule that is evaluated on a group of columns."""
|
|
21
21
|
|
|
22
|
-
def __init__(self, expr: pl.Expr, group_columns: list[str]):
|
|
22
|
+
def __init__(self, expr: pl.Expr, group_columns: list[str]) -> None:
|
|
23
23
|
super().__init__(expr)
|
|
24
24
|
self.group_columns = group_columns
|
|
25
25
|
|
|
@@ -123,7 +123,10 @@ class Collection(BaseCollection, ABC, Generic[SamplingType]):
|
|
|
123
123
|
...
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
|
|
126
|
+
*Any* member/value can be left out and will be sampled automatically.
|
|
127
|
+
Note that overrides for columns of members that are annotated with
|
|
128
|
+
``inline_for_sampling=True`` can be supplied on the top-level instead
|
|
129
|
+
of in a nested dictionary.
|
|
127
130
|
generator: The (seeded) generator to use for sampling data. If ``None``, a
|
|
128
131
|
generator with random seed is automatically created.
|
|
129
132
|
|
|
@@ -198,7 +201,11 @@ class Collection(BaseCollection, ABC, Generic[SamplingType]):
|
|
|
198
201
|
else _extract_keys_if_exist(sample, primary_keys)
|
|
199
202
|
),
|
|
200
203
|
**_extract_keys_if_exist(
|
|
201
|
-
|
|
204
|
+
(
|
|
205
|
+
sample
|
|
206
|
+
if member_infos[member].inline_for_sampling
|
|
207
|
+
else (sample[member] if member in sample else {})
|
|
208
|
+
),
|
|
202
209
|
schema.column_names(),
|
|
203
210
|
),
|
|
204
211
|
}
|
|
@@ -498,7 +505,7 @@ class Collection(BaseCollection, ABC, Generic[SamplingType]):
|
|
|
498
505
|
|
|
499
506
|
# ---------------------------------- PERSISTENCE --------------------------------- #
|
|
500
507
|
|
|
501
|
-
def write_parquet(self, directory: Path):
|
|
508
|
+
def write_parquet(self, directory: Path) -> None:
|
|
502
509
|
"""Write the members of this collection to Parquet files in a directory.
|
|
503
510
|
|
|
504
511
|
This method writes one Parquet file per member into the provided directory.
|
|
@@ -590,7 +597,7 @@ class Collection(BaseCollection, ABC, Generic[SamplingType]):
|
|
|
590
597
|
return out
|
|
591
598
|
|
|
592
599
|
@classmethod
|
|
593
|
-
def _validate_input_keys(cls, data: Mapping[str, FrameType], /):
|
|
600
|
+
def _validate_input_keys(cls, data: Mapping[str, FrameType], /) -> None:
|
|
594
601
|
actual = set(data)
|
|
595
602
|
|
|
596
603
|
missing = cls.required_members() - actual
|
|
@@ -83,7 +83,7 @@ U = TypeVar("U")
|
|
|
83
83
|
class IsInMixin(Generic[U], Base):
|
|
84
84
|
"""Mixin to use for types implementing "is in"."""
|
|
85
85
|
|
|
86
|
-
def __init__(self, *, is_in: Sequence[U] | None = None, **kwargs: Any):
|
|
86
|
+
def __init__(self, *, is_in: Sequence[U] | None = None, **kwargs: Any) -> None:
|
|
87
87
|
super().__init__(**kwargs)
|
|
88
88
|
self.is_in = is_in
|
|
89
89
|
|
|
@@ -148,7 +148,9 @@ class Decimal(OrdinalMixin[decimal.Decimal], Column):
|
|
|
148
148
|
# --------------------------------------- UTILS -------------------------------------- #
|
|
149
149
|
|
|
150
150
|
|
|
151
|
-
def _validate(
|
|
151
|
+
def _validate(
|
|
152
|
+
value: decimal.Decimal, precision: int | None, scale: int, name: str
|
|
153
|
+
) -> None:
|
|
152
154
|
exponent = value.as_tuple().exponent
|
|
153
155
|
if not isinstance(exponent, int):
|
|
154
156
|
raise ValueError(f"Encountered 'inf' or 'NaN' for `{name}`.")
|
|
@@ -25,23 +25,23 @@ class Config(contextlib.ContextDecorator):
|
|
|
25
25
|
#: Singleton stack to track where to go back after exiting a context.
|
|
26
26
|
_stack: list[Options] = []
|
|
27
27
|
|
|
28
|
-
def __init__(self, **options: Unpack[Options]):
|
|
28
|
+
def __init__(self, **options: Unpack[Options]) -> None:
|
|
29
29
|
self._local_options: Options = {**default_options(), **options}
|
|
30
30
|
|
|
31
31
|
@staticmethod
|
|
32
|
-
def set_max_sampling_iterations(iterations: int):
|
|
32
|
+
def set_max_sampling_iterations(iterations: int) -> None:
|
|
33
33
|
"""Set the maximum number of sampling iterations to use on
|
|
34
34
|
:meth:`Schema.sample`."""
|
|
35
35
|
Config.options["max_sampling_iterations"] = iterations
|
|
36
36
|
|
|
37
37
|
@staticmethod
|
|
38
|
-
def restore_defaults():
|
|
38
|
+
def restore_defaults() -> None:
|
|
39
39
|
"""Restore the defaults of the configuration."""
|
|
40
40
|
Config.options = default_options()
|
|
41
41
|
|
|
42
42
|
# ------------------------------------ CONTEXT ----------------------------------- #
|
|
43
43
|
|
|
44
|
-
def __enter__(self):
|
|
44
|
+
def __enter__(self) -> None:
|
|
45
45
|
Config._stack.append(Config.options)
|
|
46
46
|
Config.options = self._local_options
|
|
47
47
|
|
|
@@ -50,5 +50,5 @@ class Config(contextlib.ContextDecorator):
|
|
|
50
50
|
exc_type: type[BaseException] | None,
|
|
51
51
|
exc_val: BaseException | None,
|
|
52
52
|
exc_tb: TracebackType | None,
|
|
53
|
-
):
|
|
53
|
+
) -> None:
|
|
54
54
|
Config.options = Config._stack.pop()
|
|
@@ -11,7 +11,7 @@ from ._polars import PolarsDataType
|
|
|
11
11
|
class ValidationError(Exception):
|
|
12
12
|
"""Error raised when :mod:`dataframely` validation encounters an issue."""
|
|
13
13
|
|
|
14
|
-
def __init__(self, message: str):
|
|
14
|
+
def __init__(self, message: str) -> None:
|
|
15
15
|
super().__init__()
|
|
16
16
|
self.message = message
|
|
17
17
|
|
|
@@ -22,7 +22,9 @@ class ValidationError(Exception):
|
|
|
22
22
|
class DtypeValidationError(ValidationError):
|
|
23
23
|
"""Validation error raised when column dtypes are wrong."""
|
|
24
24
|
|
|
25
|
-
def __init__(
|
|
25
|
+
def __init__(
|
|
26
|
+
self, errors: dict[str, tuple[PolarsDataType, PolarsDataType]]
|
|
27
|
+
) -> None:
|
|
26
28
|
super().__init__(f"{len(errors)} columns have an invalid dtype")
|
|
27
29
|
self.errors = errors
|
|
28
30
|
|
|
@@ -37,7 +39,7 @@ class DtypeValidationError(ValidationError):
|
|
|
37
39
|
class RuleValidationError(ValidationError):
|
|
38
40
|
"""Complex validation error raised when rule validation fails."""
|
|
39
41
|
|
|
40
|
-
def __init__(self, errors: dict[str, int]):
|
|
42
|
+
def __init__(self, errors: dict[str, int]) -> None:
|
|
41
43
|
super().__init__(f"{len(errors)} rules failed validation")
|
|
42
44
|
|
|
43
45
|
# Split into schema errors and column errors
|
|
@@ -75,11 +77,11 @@ class RuleValidationError(ValidationError):
|
|
|
75
77
|
class MemberValidationError(ValidationError):
|
|
76
78
|
"""Validation error raised when multiple members of a collection fail validation."""
|
|
77
79
|
|
|
78
|
-
def __init__(self, errors: dict[str, ValidationError]):
|
|
80
|
+
def __init__(self, errors: dict[str, ValidationError]) -> None:
|
|
79
81
|
super().__init__(f"{len(errors)} members failed validation")
|
|
80
82
|
self.errors = errors
|
|
81
83
|
|
|
82
|
-
def __str__(self):
|
|
84
|
+
def __str__(self) -> str:
|
|
83
85
|
details = [
|
|
84
86
|
f" > Member '{name}' failed validation:\n"
|
|
85
87
|
+ "\n".join(" " + line for line in str(error).split("\n"))
|
|
@@ -95,7 +97,7 @@ class ImplementationError(Exception):
|
|
|
95
97
|
class AnnotationImplementationError(ImplementationError):
|
|
96
98
|
"""Error raised when the annotations of a collection are invalid."""
|
|
97
99
|
|
|
98
|
-
def __init__(self, attr: str, kls: type):
|
|
100
|
+
def __init__(self, attr: str, kls: type) -> None:
|
|
99
101
|
message = (
|
|
100
102
|
"Annotations of a 'dy.Collection' may only be an (optional) "
|
|
101
103
|
f"'dy.LazyFrame', but \"{attr}\" has type '{kls}'."
|
|
@@ -106,7 +108,9 @@ class AnnotationImplementationError(ImplementationError):
|
|
|
106
108
|
class RuleImplementationError(ImplementationError):
|
|
107
109
|
"""Error raised when a rule is implemented incorrectly."""
|
|
108
110
|
|
|
109
|
-
def __init__(
|
|
111
|
+
def __init__(
|
|
112
|
+
self, name: str, return_dtype: pl.DataType, is_group_rule: bool
|
|
113
|
+
) -> None:
|
|
110
114
|
if is_group_rule:
|
|
111
115
|
details = (
|
|
112
116
|
" When implementing a group rule (i.e. when using the `group_by` "
|
|
@@ -28,7 +28,9 @@ class FailureInfo(Generic[S]):
|
|
|
28
28
|
#: The schema used to create the input data frame.
|
|
29
29
|
schema: type[S]
|
|
30
30
|
|
|
31
|
-
def __init__(
|
|
31
|
+
def __init__(
|
|
32
|
+
self, lf: pl.LazyFrame, rule_columns: list[str], schema: type[S]
|
|
33
|
+
) -> None:
|
|
32
34
|
self._lf = lf
|
|
33
35
|
self._rule_columns = rule_columns
|
|
34
36
|
self.schema = schema
|
|
@@ -71,7 +73,7 @@ class FailureInfo(Generic[S]):
|
|
|
71
73
|
|
|
72
74
|
# ---------------------------------- PERSISTENCE --------------------------------- #
|
|
73
75
|
|
|
74
|
-
def write_parquet(self, file: str | Path | IO[bytes]):
|
|
76
|
+
def write_parquet(self, file: str | Path | IO[bytes]) -> None:
|
|
75
77
|
"""Write the failure info to a Parquet file.
|
|
76
78
|
|
|
77
79
|
Args:
|
|
@@ -50,7 +50,7 @@ TYPED_LAZYFRAME_FULLNAME = "dataframely._typing.LazyFrame"
|
|
|
50
50
|
# --------------------------------------- RULES -------------------------------------- #
|
|
51
51
|
|
|
52
52
|
|
|
53
|
-
def mark_rules_as_staticmethod(ctx: ClassDefContext):
|
|
53
|
+
def mark_rules_as_staticmethod(ctx: ClassDefContext) -> None:
|
|
54
54
|
"""Mark all methods decorated with `@rule` as `staticmethod`s."""
|
|
55
55
|
info = ctx.cls.info
|
|
56
56
|
for sym in info.names.values():
|
|
@@ -199,7 +199,7 @@ def _convert_dy_column_to_dtype(
|
|
|
199
199
|
def store_typed_dict_type_for_schema(
|
|
200
200
|
ctx: ClassDefContext,
|
|
201
201
|
schema_registry: dict[str, TypedDictType],
|
|
202
|
-
):
|
|
202
|
+
) -> None:
|
|
203
203
|
"""Add `TypedDictType` inferred from the schema's columns to a given registry."""
|
|
204
204
|
|
|
205
205
|
schema_type = ctx.cls.info
|
|
@@ -336,11 +336,13 @@ def alter_dataframe_iter_rows_return_type(
|
|
|
336
336
|
|
|
337
337
|
|
|
338
338
|
class DataframelyPlugin(Plugin):
|
|
339
|
-
def __init__(self, options: Options):
|
|
339
|
+
def __init__(self, options: Options) -> None:
|
|
340
340
|
super().__init__(options)
|
|
341
341
|
self.schema_registry: dict[str, TypedDictType] = {}
|
|
342
342
|
|
|
343
|
-
def get_base_class_hook(
|
|
343
|
+
def get_base_class_hook(
|
|
344
|
+
self, fullname: str
|
|
345
|
+
) -> Callable[[ClassDefContext], None] | None:
|
|
344
346
|
# Given a class, check whether it is a subclass of `dy.Schema`. If so, mark
|
|
345
347
|
# all methods decorated with `@rule` as staticmethods.
|
|
346
348
|
# Also, store the `TypedDictType` for the schema in a registry to allow downstream
|
|
@@ -349,13 +351,12 @@ class DataframelyPlugin(Plugin):
|
|
|
349
351
|
if sym and isinstance(sym.node, TypeInfo):
|
|
350
352
|
if any(base.fullname == SCHEMA_FULLNAME for base in sym.node.mro):
|
|
351
353
|
|
|
352
|
-
def _hook(ctx: ClassDefContext) ->
|
|
354
|
+
def _hook(ctx: ClassDefContext) -> None:
|
|
353
355
|
mark_rules_as_staticmethod(ctx)
|
|
354
356
|
store_typed_dict_type_for_schema(
|
|
355
357
|
ctx,
|
|
356
358
|
self.schema_registry,
|
|
357
359
|
)
|
|
358
|
-
return True
|
|
359
360
|
|
|
360
361
|
return _hook
|
|
361
362
|
return None
|
|
@@ -68,7 +68,7 @@ html_favicon = "_static/favicon.ico"
|
|
|
68
68
|
# Copied and adapted from
|
|
69
69
|
# https://github.com/pandas-dev/pandas/blob/4a14d064187367cacab3ff4652a12a0e45d0711b/doc/source/conf.py#L613-L659
|
|
70
70
|
# Required configuration function to use sphinx.ext.linkcode
|
|
71
|
-
def linkcode_resolve(domain, info):
|
|
71
|
+
def linkcode_resolve(domain: str, info: dict[str, str]) -> str | None:
|
|
72
72
|
"""Determine the URL corresponding to a given Python object."""
|
|
73
73
|
if domain != "py":
|
|
74
74
|
return None
|
|
@@ -162,7 +162,7 @@ expectations on the schema of the data frame, e.g.:
|
|
|
162
162
|
|
|
163
163
|
::
|
|
164
164
|
|
|
165
|
-
def train_model(df: dy.DataFrame[HouseSchema]):
|
|
165
|
+
def train_model(df: dy.DataFrame[HouseSchema]) -> None:
|
|
166
166
|
...
|
|
167
167
|
|
|
168
168
|
The type checker (typically ``mypy``) then ensures that it is actually a
|
|
@@ -19,10 +19,11 @@ description = "A declarative, polars-native data frame validation library"
|
|
|
19
19
|
name = "dataframely"
|
|
20
20
|
readme = "README.md"
|
|
21
21
|
requires-python = ">=3.11"
|
|
22
|
-
version = "1.
|
|
22
|
+
version = "1.1.0"
|
|
23
23
|
|
|
24
24
|
[project.urls]
|
|
25
|
-
|
|
25
|
+
Repository = "https://github.com/quantco/dataframely"
|
|
26
|
+
Documentation = "https://dataframely.readthedocs.io/"
|
|
26
27
|
|
|
27
28
|
[tool.maturin]
|
|
28
29
|
module-name = "dataframely._extre"
|
|
@@ -66,6 +67,7 @@ quote-style = "double"
|
|
|
66
67
|
|
|
67
68
|
[tool.mypy]
|
|
68
69
|
check_untyped_defs = true
|
|
70
|
+
disallow_untyped_defs = true
|
|
69
71
|
exclude = ["docs/"]
|
|
70
72
|
explicit_package_bases = true
|
|
71
73
|
no_implicit_optional = true
|
|
@@ -25,32 +25,32 @@ class MyCollection(dy.Collection):
|
|
|
25
25
|
second: dy.LazyFrame[MySecondSchema] | None
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
def test_common_primary_keys():
|
|
28
|
+
def test_common_primary_keys() -> None:
|
|
29
29
|
assert MyCollection.common_primary_keys() == ["a"]
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
def test_members():
|
|
32
|
+
def test_members() -> None:
|
|
33
33
|
members = MyCollection.members()
|
|
34
34
|
assert not members["first"].is_optional
|
|
35
35
|
assert members["second"].is_optional
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
def test_member_schemas():
|
|
38
|
+
def test_member_schemas() -> None:
|
|
39
39
|
schemas = MyCollection.member_schemas()
|
|
40
40
|
assert schemas == {"first": MyFirstSchema, "second": MySecondSchema}
|
|
41
41
|
|
|
42
42
|
|
|
43
|
-
def test_required_members():
|
|
43
|
+
def test_required_members() -> None:
|
|
44
44
|
required_members = MyCollection.required_members()
|
|
45
45
|
assert required_members == {"first"}
|
|
46
46
|
|
|
47
47
|
|
|
48
|
-
def test_optional_members():
|
|
48
|
+
def test_optional_members() -> None:
|
|
49
49
|
optional_members = MyCollection.optional_members()
|
|
50
50
|
assert optional_members == {"second"}
|
|
51
51
|
|
|
52
52
|
|
|
53
|
-
def test_cast():
|
|
53
|
+
def test_cast() -> None:
|
|
54
54
|
collection = MyCollection.cast(
|
|
55
55
|
{
|
|
56
56
|
"first": pl.LazyFrame({"a": [1, 2, 3]}),
|
|
@@ -74,7 +74,7 @@ def test_cast():
|
|
|
74
74
|
{"first": pl.LazyFrame({"a": [1, 2, 3]}, schema={"a": pl.UInt8})},
|
|
75
75
|
],
|
|
76
76
|
)
|
|
77
|
-
def test_to_dict(expected: dict[str, pl.LazyFrame]):
|
|
77
|
+
def test_to_dict(expected: dict[str, pl.LazyFrame]) -> None:
|
|
78
78
|
collection = MyCollection.validate(expected)
|
|
79
79
|
|
|
80
80
|
# Check that export looks as expected
|
|
@@ -87,7 +87,7 @@ def test_to_dict(expected: dict[str, pl.LazyFrame]):
|
|
|
87
87
|
assert MyCollection.is_valid(observed)
|
|
88
88
|
|
|
89
89
|
|
|
90
|
-
def test_collect_all():
|
|
90
|
+
def test_collect_all() -> None:
|
|
91
91
|
collection = MyCollection.cast(
|
|
92
92
|
{
|
|
93
93
|
"first": pl.LazyFrame({"a": [1, 2, 3]}).filter(pl.col("a") < 3),
|
|
@@ -106,7 +106,7 @@ def test_collect_all():
|
|
|
106
106
|
assert len(out.second.collect()) == 2
|
|
107
107
|
|
|
108
108
|
|
|
109
|
-
def test_collect_all_optional():
|
|
109
|
+
def test_collect_all_optional() -> None:
|
|
110
110
|
collection = MyCollection.cast({"first": pl.LazyFrame({"a": [1, 2, 3]})})
|
|
111
111
|
out = collection.collect_all()
|
|
112
112
|
|
|
@@ -118,7 +118,9 @@ def test_collect_all_optional():
|
|
|
118
118
|
@pytest.mark.parametrize(
|
|
119
119
|
"read_fn", [MyCollection.scan_parquet, MyCollection.read_parquet]
|
|
120
120
|
)
|
|
121
|
-
def test_read_write_parquet(
|
|
121
|
+
def test_read_write_parquet(
|
|
122
|
+
tmp_path: Path, read_fn: Callable[[Path], MyCollection]
|
|
123
|
+
) -> None:
|
|
122
124
|
collection = MyCollection.cast(
|
|
123
125
|
{
|
|
124
126
|
"first": pl.LazyFrame({"a": [1, 2, 3]}),
|
|
@@ -139,7 +141,7 @@ def test_read_write_parquet(tmp_path: Path, read_fn: Callable[[Path], MyCollecti
|
|
|
139
141
|
)
|
|
140
142
|
def test_read_write_parquet_optional(
|
|
141
143
|
tmp_path: Path, read_fn: Callable[[Path], MyCollection]
|
|
142
|
-
):
|
|
144
|
+
) -> None:
|
|
143
145
|
collection = MyCollection.cast({"first": pl.LazyFrame({"a": [1, 2, 3]})})
|
|
144
146
|
collection.write_parquet(tmp_path)
|
|
145
147
|
|