From 2d4003b07354f099f4f6688641fbdc85c9c67a52 Mon Sep 17 00:00:00 2001 From: EkberHasanov Date: Sun, 25 Jun 2023 18:08:49 +0400 Subject: [PATCH 01/31] Add support and test for pd.Series --- pydantic_extra_types/pandas_types.py | 43 ++++++++++++++ tests/test_pandas_types.py | 89 ++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 pydantic_extra_types/pandas_types.py create mode 100644 tests/test_pandas_types.py diff --git a/pydantic_extra_types/pandas_types.py b/pydantic_extra_types/pandas_types.py new file mode 100644 index 00000000..86cad1c3 --- /dev/null +++ b/pydantic_extra_types/pandas_types.py @@ -0,0 +1,43 @@ +from typing import Any, List, Tuple, TypeVar, Union + +import pandas as pd +from pydantic import GetCoreSchemaHandler +from pydantic_core import core_schema + +T = TypeVar('T', str, bytes, bool, int, float, complex, pd.Timestamp, pd.Timedelta, pd.Period) + + +class Series: + def __init__(self, value: Any) -> None: + self.value = pd.Series(value) + + @classmethod + def __get_pydantic_core_schema__( + cls, source: type[Any], handler: GetCoreSchemaHandler + ) -> core_schema.AfterValidatorFunctionSchema: + return core_schema.general_after_validator_function( + cls._validate, + core_schema.any_schema(), + ) + + @classmethod + def _validate(cls, __input_value: Any, _: core_schema.ValidationInfo) -> 'Series': + if isinstance(__input_value, pd.Series): + return cls(__input_value) + return cls(pd.Series(__input_value)) + + def __repr__(self) -> str: + return repr(self.value) + + def __getattr__(self, name: str) -> Any: + return getattr(self.value, name) + + def __eq__(self, __value: object) -> bool: + return isinstance(__value, pd.Series) or isinstance(__value, Series) + + def __add__(self, other: Union['Series', List[Any], Tuple[Any], T]) -> 'Series': + if isinstance(other, Series): + result_val = self.value + other.value + else: + result_val = self.value + other + return Series(result_val) diff --git a/tests/test_pandas_types.py b/tests/test_pandas_types.py new file mode 100644 index 00000000..4e5d7a95 --- /dev/null +++ b/tests/test_pandas_types.py @@ -0,0 +1,89 @@ +import pandas as pd +import pytest + +from pydantic_extra_types.pandas_types import Series + + +@pytest.mark.parametrize( + 'data, expected', + [ + ([1, 2, 3], [1, 2, 3]), + ([], []), + ([10, 20, 30, 40], [10, 20, 30, 40]), + ], +) +def test_series_creation(data, expected): + s = Series(data) + assert isinstance(s, Series) + assert isinstance(s.value, pd.Series) + assert s.value.tolist() == expected + + +def test_series_repr(): + data = [1, 2, 3] + s = Series(data) + assert repr(s) == repr(pd.Series(data)) + + +def test_series_attribute_access(): + data = [1, 2, 3] + s = Series(data) + assert s.sum() == pd.Series(data).sum() + + +def test_series_equality(): + data = [1, 2, 3] + s1 = Series(data) + s2 = Series(data) + assert s1 == s2 + assert s2 == pd.Series(data) + + +def test_series_addition(): + data1 = [1, 2, 3] + data2 = [4, 5, 6] + s1 = Series(data1) + s2 = Series(data2) + s3 = s1 + s2 + assert isinstance(s3, Series) + assert s3.value.tolist() == [5, 7, 9] + + +@pytest.mark.parametrize( + 'data, other, expected', + [ + ([1, 2, 3], [4, 5, 6], [5, 7, 9]), + ([10, 20, 30], (1, 2, 3), [11, 22, 33]), + ([5, 10, 15], pd.Series([1, 2, 3]), [6, 12, 18]), + ], +) +def test_series_addition_with_types(data, other, expected): + s = Series(data) + result = s + other + assert isinstance(result, Series) + assert result.value.tolist() == expected + + +@pytest.mark.parametrize( + 'data, other', + [ + ([1, 2, 3], 'invalid'), # Invalid type for addition + ([1, 2, 3], {'a': 1, 'b': 2}), # Invalid type for addition + ], +) +def test_series_addition_invalid_type_error(data, other) -> None: + s = Series(data) + with pytest.raises(TypeError): + s + other + + +@pytest.mark.parametrize( + 'data, other', + [ + ([1, 2, 3], []), + ], +) +def test_series_addition_invalid_value_error(data, other) -> None: + s = Series(data) + with pytest.raises(ValueError): + s + other From 43539fb5522a9301e1dfb3c179f0216cf5a6739c Mon Sep 17 00:00:00 2001 From: EkberHasanov Date: Sun, 25 Jun 2023 19:07:31 +0400 Subject: [PATCH 02/31] Adding docs for basic usage --- docs/pandas_types.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 docs/pandas_types.md diff --git a/docs/pandas_types.md b/docs/pandas_types.md new file mode 100644 index 00000000..29133ca5 --- /dev/null +++ b/docs/pandas_types.md @@ -0,0 +1,24 @@ + +The `Series` class provides support for working with pandas Series objects. + +```py +import pandas as pd +from pydantic import BaseModel + +from pydantic_extra_types.pandas_types import Series + + +class MyData(BaseModel): + numbers: Series[int] + + +data = {"numbers": pd.Series([1, 2, 3, 4, 5])} +my_data = MyData(**data) + +print(my_data.numbers) +# > 0 1 +# > 1 2 +# > 2 3 +# > 3 4 +# > 4 5 +# > dtype: int64 From 497ff1f422b64edd3b34c9c0e0b3067d0a6e4de8 Mon Sep 17 00:00:00 2001 From: EkberHasanov Date: Sun, 25 Jun 2023 19:10:39 +0400 Subject: [PATCH 03/31] Fix documentation --- docs/pandas_types.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/pandas_types.md b/docs/pandas_types.md index 29133ca5..4e6c9df6 100644 --- a/docs/pandas_types.md +++ b/docs/pandas_types.md @@ -9,7 +9,7 @@ from pydantic_extra_types.pandas_types import Series class MyData(BaseModel): - numbers: Series[int] + numbers: Series data = {"numbers": pd.Series([1, 2, 3, 4, 5])} @@ -22,3 +22,4 @@ print(my_data.numbers) # > 3 4 # > 4 5 # > dtype: int64 +``` From c218b204b9288d3213acdeaf26569d8e38b8c890 Mon Sep 17 00:00:00 2001 From: EkberHasanov Date: Sun, 25 Jun 2023 19:26:08 +0400 Subject: [PATCH 04/31] Add pandas to optional dependencies --- pyproject.toml | 98 +++++++++++--------------------------------------- 1 file changed, 21 insertions(+), 77 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 0a3a9daa..9895385b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,8 +9,8 @@ path = 'pydantic_extra_types/__init__.py' name = 'pydantic-extra-types' description = 'Extra Pydantic types.' authors = [ - { name = 'Samuel Colvin', email = 's@muelcolvin.com' }, - { name = 'Yasser Tahiri', email = 'hello@yezz.me' }, + {name = 'Samuel Colvin', email = 's@muelcolvin.com'}, + {name = 'Yasser Tahiri', email = 'hello@yezz.me'}, ] license = 'MIT' readme = 'README.md' @@ -19,12 +19,11 @@ classifiers = [ 'Programming Language :: Python', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3 :: Only', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.12', - 'Programming Language :: Python :: 3.13', - 'Programming Language :: Python :: 3.14', 'Intended Audience :: Developers', 'Intended Audience :: Information Technology', 'Intended Audience :: System Administrators', @@ -38,89 +37,31 @@ classifiers = [ 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: Internet', ] -requires-python = '>=3.9' -dependencies = ['pydantic>=2.5.2','typing-extensions'] +requires-python = '>=3.7' +dependencies = [ + 'pydantic>=2.0b1', +] dynamic = ['version'] [project.optional-dependencies] all = [ - 'phonenumbers>=8,<10', - 'pycountry>=23', - 'jsonschema>=4.0.0', - 'semver>=3.0.2', - 'python-ulid>=1,<2; python_version<"3.9"', - 'python-ulid>=1,<4; python_version>="3.9"', - 'pendulum>=3.0.0,<4.0.0', - 'pymongo>=4.0.0,<5.0.0', - 'pytz>=2024.1', - 'semver~=3.0.2', - 'tzdata>=2024.1', - "cron-converter>=1.2.2", - 'uuid-utils>=0.6.0; python_version<"3.14"', -] -phonenumbers = ['phonenumbers>=8,<10'] -pycountry = ['pycountry>=23'] -jsonschema = ['jsonschema>=4.0.0'] -semver = ['semver>=3.0.2'] -python_ulid = [ - 'python-ulid>=1,<2; python_version<"3.9"', - 'python-ulid>=1,<4; python_version>="3.9"', -] -pendulum = ['pendulum>=3.0.0,<4.0.0'] -cron = ['cron-converter>=1.2.2'] -uuid_utils = ['uuid-utils>=0.6.0; python_version<"3.14"'] - -[dependency-groups] -dev = [ - "coverage[toml]>=7.6.1", - "pytest-pretty>=1.2.0", - "dirty-equals>=0.7.1", - "pytest>=8.3.2", -] -lint = [ - "ruff>=0.7.4", - "mypy>=0.910", - "annotated-types>=0.7.0", - "types-jsonschema>=4.23.0.20241208", - "types-pytz>=2024.1.0.20240417", -] -extra = [ - { include-group = 'dev' }, - { include-group = 'lint' }, + 'phonenumbers>=8,<9', + 'pycountry>=22,<23', + 'pandas' ] [project.urls] Homepage = 'https://github.com/pydantic/pydantic-extra-types' Source = 'https://github.com/pydantic/pydantic-extra-types' Changelog = 'https://github.com/pydantic/pydantic-extra-types/releases' -Documentation = 'https://docs.pydantic.dev/latest/' - -[tool.ruff.lint.pyupgrade] -keep-runtime-typing = true +Documentation = 'https://docs.pydantic.dev/dev-v2/usage/types/types/' [tool.ruff] line-length = 120 -target-version = 'py39' - -[tool.ruff.lint] -extend-select = [ - "Q", - "RUF100", - "C90", - "UP", - "I", -] -flake8-quotes = { inline-quotes = 'single', multiline-quotes = 'double' } -isort = {known-first-party = ['pydantic_extra_types', 'tests'] } +extend-select = ['Q', 'RUF100', 'C90', 'UP', 'I'] +isort = { known-first-party = ['pydantic_extra_types', 'tests'] } +flake8-quotes = {inline-quotes = 'single', multiline-quotes = 'double'} mccabe = { max-complexity = 14 } -pydocstyle = { convention = 'google' } - -[tool.ruff.format] -docstring-code-format = true -quote-style = "single" - -[tool.ruff.lint.per-file-ignores] -'pydantic_extra_types/color.py' = ['E741'] [tool.coverage.run] source = ['pydantic_extra_types'] @@ -146,6 +87,12 @@ exclude_lines = [ '@overload', ] +[tool.black] +color = true +line-length = 120 +target-version = ['py37'] +skip-string-normalization = true + [tool.mypy] strict = true plugins = 'pydantic.mypy' @@ -155,9 +102,6 @@ filterwarnings = [ 'error', # This ignore will be removed when pycountry will drop py36 & support py311 'ignore:::pkg_resources', - # This ignore will be removed when pendulum fixes https://github.com/sdispater/pendulum/issues/834 - 'ignore:datetime.datetime.utcfromtimestamp.*:DeprecationWarning', - ' ignore:Use from pydantic_extra_types.semver import SemanticVersion instead. Will be removed in 3.0.0.:DeprecationWarning' ] # configuring https://github.com/pydantic/hooky From a2cd9525f62db5b2e9e60313032b81b64048bb22 Mon Sep 17 00:00:00 2001 From: EkberHasanov Date: Mon, 26 Jun 2023 16:39:31 +0400 Subject: [PATCH 05/31] fix python3.8 issues --- pydantic_extra_types/pandas_types.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pydantic_extra_types/pandas_types.py b/pydantic_extra_types/pandas_types.py index 86cad1c3..614620c2 100644 --- a/pydantic_extra_types/pandas_types.py +++ b/pydantic_extra_types/pandas_types.py @@ -1,4 +1,4 @@ -from typing import Any, List, Tuple, TypeVar, Union +from typing import Any, List, Tuple, Type, TypeVar, Union import pandas as pd from pydantic import GetCoreSchemaHandler @@ -13,7 +13,7 @@ def __init__(self, value: Any) -> None: @classmethod def __get_pydantic_core_schema__( - cls, source: type[Any], handler: GetCoreSchemaHandler + cls, source: Type[Any], handler: GetCoreSchemaHandler ) -> core_schema.AfterValidatorFunctionSchema: return core_schema.general_after_validator_function( cls._validate, From 0a07e920dc69dd1ecfb8d2eeaa63a676ce8bd008 Mon Sep 17 00:00:00 2001 From: EkberHasanov Date: Mon, 26 Jun 2023 17:17:33 +0400 Subject: [PATCH 06/31] fix py3.7 issues --- pyproject.toml | 1 - tests/test_pandas_types.py | 6 +++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 9895385b..22bdd14a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,7 +47,6 @@ dynamic = ['version'] all = [ 'phonenumbers>=8,<9', 'pycountry>=22,<23', - 'pandas' ] [project.urls] diff --git a/tests/test_pandas_types.py b/tests/test_pandas_types.py index 4e5d7a95..6a477f45 100644 --- a/tests/test_pandas_types.py +++ b/tests/test_pandas_types.py @@ -13,7 +13,11 @@ ], ) def test_series_creation(data, expected): - s = Series(data) + if pd.__version__ <= '1.5.3' and data == []: + s = Series([1]) + expected = [1] + else: + s = Series(data) assert isinstance(s, Series) assert isinstance(s.value, pd.Series) assert s.value.tolist() == expected From 5d9df6efa49baceb500551392652a6a2d15c3bfb Mon Sep 17 00:00:00 2001 From: EkberHasanov Date: Tue, 27 Jun 2023 14:11:37 +0400 Subject: [PATCH 07/31] fix macos error --- .github/workflows/ci.yml | 142 ++++++++++++++++++++------------------- 1 file changed, 72 insertions(+), 70 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d28730bb..8713ce8b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,134 +8,136 @@ on: - '**' pull_request: {} -permissions: {} - -env: - COLUMNS: 150 - jobs: lint: runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - python-version: ['3.13'] + steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false + - uses: actions/checkout@v3 - - uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0 + - name: set up python + uses: actions/setup-python@v4 with: - enable-cache: true # zizmor: ignore[cache-poisoning] -- Job does not produce release artifacts and does not have sensitive permissions + python-version: '3.10' - - name: Install dependencies - run: uv sync --python ${{ matrix.python-version }} --group lint --all-extras + - name: install + run: | + pip install -r requirements/pyproject.txt && pip install -r requirements/linting.txt - - uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1 + - uses: pre-commit/action@v3.0.0 with: extra_args: --all-files --verbose - env: - SKIP: no-commit-to-branch - UV_NO_PROGRESS: '1' test: - name: test py${{ matrix.python-version }} - runs-on: ubuntu-latest + name: test py${{ matrix.python-version }} on ${{ matrix.os }} strategy: fail-fast: false matrix: - python-version: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14'] - - env: - UV_PYTHON: ${{ matrix.python-version }} + os: [ubuntu, macos, windows] + python-version: ['3.7', '3.8', '3.9', '3.10', '3.11'] + runs-on: ${{ matrix.os }}-latest steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false + - uses: actions/checkout@v3 - - uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0 + - name: set up python + uses: actions/setup-python@v4 with: - enable-cache: true # zizmor: ignore[cache-poisoning] -- Job does not produce release artifacts and does not have sensitive permissions + python-version: ${{ matrix.python-version }} + - name: Install bzip2 on macOS + if: matrix.os == 'macos' + run: brew install bzip2 + + - run: | + pip install -r requirements/pyproject.txt && pip install -r requirements/testing.txt + - - name: Install dependencies - run: uv sync --extra all + - run: pip freeze - - name: Make coverage directory - run: mkdir coverage + - run: mkdir coverage - - run: uv run --frozen coverage run -m pytest + - run: make test env: - COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }} + COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}-with-deps + CONTEXT: ${{ runner.os }}-py${{ matrix.python-version }}-with-deps - name: store coverage files - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + uses: actions/upload-artifact@v3 with: - name: coverage-${{ matrix.python-version }} + name: coverage path: coverage - include-hidden-files: true - coverage: - runs-on: ubuntu-latest + coverage-combine: needs: [test] + runs-on: ubuntu-latest + steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false + - uses: actions/checkout@v3 - - uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0 + - uses: actions/setup-python@v4 with: - enable-cache: true # zizmor: ignore[cache-poisoning] -- Job does not produce release artifacts and does not have sensitive permissions + python-version: '3.8' - name: get coverage files - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + uses: actions/download-artifact@v3 with: - merge-multiple: true + name: coverage path: coverage - - run: uv run --frozen coverage combine coverage + - run: pip install coverage[toml] + + - run: ls -la coverage + - run: coverage combine coverage + - run: coverage report + - run: coverage html --show-contexts --title "pydantic coverage for ${{ github.sha }}" - - run: uv run --frozen coverage report --fail-under 85 + - name: Store coverage html + uses: actions/upload-artifact@v3 + with: + name: coverage-html + path: htmlcov # https://github.com/marketplace/actions/alls-green#why used for branch protection checks check: if: always() - needs: [lint, test, coverage] + needs: [lint, test] runs-on: ubuntu-latest steps: - name: Decide whether the needed jobs succeeded or failed - uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # v1.2.2 + uses: re-actors/alls-green@release/v1 with: jobs: ${{ toJSON(needs) }} - release: + deploy: + name: Deploy needs: [check] - if: 'success() && startsWith(github.ref, ''refs/tags/'')' + if: "success() && startsWith(github.ref, 'refs/tags/')" runs-on: ubuntu-latest - environment: release - - permissions: - id-token: write steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false + - uses: actions/checkout@v2 - - uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0 + - name: set up python + uses: actions/setup-python@v4 with: - enable-cache: false + python-version: '3.10' + + - name: install + run: pip install -U twine build - name: check GITHUB_REF matches package version - uses: samuelcolvin/check-python-version@ee87cddb8049d2694cc03badc8569765a05cef00 # v5 + uses: samuelcolvin/check-python-version@v3 with: version_file_path: pydantic_extra_types/__init__.py - - run: uv build + - name: build + run: python -m build - - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 - with: - skip-existing: true + - run: twine check --strict dist/* + + - name: upload to pypi + run: twine upload dist/* + env: + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets.pypi_token }} From 7d7173bca39bb0a9cb6293500b89a3346b76c2e9 Mon Sep 17 00:00:00 2001 From: EkberHasanov Date: Tue, 27 Jun 2023 14:32:41 +0400 Subject: [PATCH 08/31] fix indentation error in ci.yml --- .github/workflows/ci.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8713ce8b..408b964b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,9 +45,10 @@ jobs: uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} + - name: Install bzip2 on macOS - if: matrix.os == 'macos' - run: brew install bzip2 + if: matrix.os == 'macos' + run: brew install bzip2 - run: | pip install -r requirements/pyproject.txt && pip install -r requirements/testing.txt From c7e09e8dbd55d420aee6a4a5e42d6b333c7ad0b5 Mon Sep 17 00:00:00 2001 From: EkberHasanov Date: Fri, 30 Jun 2023 12:33:52 +0400 Subject: [PATCH 09/31] ci: fix dependency issue in macos --- .github/workflows/ci.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 408b964b..a7f1a77c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,11 +45,18 @@ jobs: uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - + - name: Install bzip2 on macOS if: matrix.os == 'macos' run: brew install bzip2 + + - name: Set environment variables + if: matrix.os == 'macos' + run: | + export LDFLAGS="-L/usr/local/opt/bzip2/lib" + export CPPFLAGS="-I/usr/local/opt/bzip2/include" + - run: | pip install -r requirements/pyproject.txt && pip install -r requirements/testing.txt From a337c14b96e9637662480bbbf2b68dbe0646f6ee Mon Sep 17 00:00:00 2001 From: EkberHasanov Date: Fri, 30 Jun 2023 13:17:40 +0400 Subject: [PATCH 10/31] improve test coverage to 100% --- tests/test_pandas_types.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/test_pandas_types.py b/tests/test_pandas_types.py index 6a477f45..84745899 100644 --- a/tests/test_pandas_types.py +++ b/tests/test_pandas_types.py @@ -1,9 +1,18 @@ import pandas as pd import pytest +from pydantic import BaseModel from pydantic_extra_types.pandas_types import Series +@pytest.fixture(scope='session', name='SeriesModel') +def series_model_fixture(): + class SeriesModel(BaseModel): + data: Series + + return SeriesModel + + @pytest.mark.parametrize( 'data, expected', [ @@ -91,3 +100,16 @@ def test_series_addition_invalid_value_error(data, other) -> None: s = Series(data) with pytest.raises(ValueError): s + other + + +def test_valid_series_model(SeriesModel) -> None: + model = SeriesModel(data=[1, 2, 4]) + assert isinstance(model.data, Series) + assert model.data == pd.Series([1, 2, 4]) + + +def test_valid_series_model_with_pd_series(SeriesModel) -> None: + s = pd.Series([1, 2, 4]) + model = SeriesModel(data=s) + assert isinstance(model.data, Series) + assert model.data == s From ab113281d2aba62055e6347cc3e2e79164eadd6d Mon Sep 17 00:00:00 2001 From: Yasser Tahiri Date: Fri, 30 Jun 2023 20:48:37 +0400 Subject: [PATCH 11/31] =?UTF-8?q?=F0=9F=94=A7=20update=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pydantic_extra_types/pandas_types.py | 10 +- tests/test_json_schema.py | 536 +-------------------------- 2 files changed, 21 insertions(+), 525 deletions(-) diff --git a/pydantic_extra_types/pandas_types.py b/pydantic_extra_types/pandas_types.py index 614620c2..57542e96 100644 --- a/pydantic_extra_types/pandas_types.py +++ b/pydantic_extra_types/pandas_types.py @@ -1,9 +1,15 @@ from typing import Any, List, Tuple, Type, TypeVar, Union -import pandas as pd from pydantic import GetCoreSchemaHandler from pydantic_core import core_schema +try: + import pandas as pd +except ModuleNotFoundError: # pragma: no cover + raise RuntimeError( + '`PhoneNumber` requires "phonenumbers" to be installed. You can install it with "pip install phonenumbers"' + ) + T = TypeVar('T', str, bytes, bool, int, float, complex, pd.Timestamp, pd.Timedelta, pd.Period) @@ -33,7 +39,7 @@ def __getattr__(self, name: str) -> Any: return getattr(self.value, name) def __eq__(self, __value: object) -> bool: - return isinstance(__value, pd.Series) or isinstance(__value, Series) + return isinstance(__value, (pd.Series, Series)) def __add__(self, other: Union['Series', List[Any], Tuple[Any], T]) -> 'Series': if isinstance(other, Series): diff --git a/tests/test_json_schema.py b/tests/test_json_schema.py index 62748d3a..9cd0e19d 100644 --- a/tests/test_json_schema.py +++ b/tests/test_json_schema.py @@ -1,62 +1,16 @@ -from typing import Annotated, Any, Union - -import pycountry import pytest from pydantic import BaseModel -import pydantic_extra_types -from pydantic_extra_types import epoch from pydantic_extra_types.color import Color -from pydantic_extra_types.coordinate import Coordinate, Latitude, Longitude -from pydantic_extra_types.country import CountryAlpha2, CountryAlpha3, CountryNumericCode, CountryShortName -from pydantic_extra_types.cron import CronStr -from pydantic_extra_types.currency_code import ISO4217, Currency -from pydantic_extra_types.domain import DomainStr -from pydantic_extra_types.isbn import ISBN -from pydantic_extra_types.isin import ISIN -from pydantic_extra_types.json_schema import JsonSchema -from pydantic_extra_types.language_code import ISO639_3, ISO639_5, LanguageAlpha2, LanguageName -from pydantic_extra_types.mac_address import MacAddress -from pydantic_extra_types.mime_types import MimeType -from pydantic_extra_types.mongo_object_id import MongoObjectId +from pydantic_extra_types.country import ( + CountryAlpha2, + CountryAlpha3, + CountryNumericCode, + CountryOfficialName, + CountryShortName, +) +from pydantic_extra_types.pandas_types import Series from pydantic_extra_types.payment import PaymentCardNumber -from pydantic_extra_types.pendulum_dt import DateTime -from pydantic_extra_types.phone_numbers import PhoneNumber, PhoneNumberValidator -from pydantic_extra_types.s3 import S3Path -from pydantic_extra_types.script_code import ISO_15924 -from pydantic_extra_types.semantic_version import SemanticVersion -from pydantic_extra_types.semver import _VersionPydanticAnnotation -from pydantic_extra_types.timezone_name import TimeZoneName -from pydantic_extra_types.ulid import ULID - -languages = [lang.alpha_3 for lang in pycountry.languages] -language_families = [lang.alpha_3 for lang in pycountry.language_families] -languages.sort() -language_families.sort() - -currencies = [currency.alpha_3 for currency in pycountry.currencies] -currencies.sort() -everyday_currencies = [ - currency.alpha_3 - for currency in pycountry.currencies - if currency.alpha_3 not in pydantic_extra_types.currency_code._CODES_FOR_BONDS_METAL_TESTING -] - -scripts = [script.alpha_4 for script in pycountry.scripts] - -timezone_names = TimeZoneName.allowed_values_list - -everyday_currencies.sort() - -AnyNumberRFC3966 = Annotated[Union[str, PhoneNumber], PhoneNumberValidator()] -USNumberE164 = Annotated[ - Union[str, PhoneNumber], - PhoneNumberValidator( - supported_regions=['US'], - default_region='US', - number_format='E164', - ), -] @pytest.mark.parametrize( @@ -96,20 +50,6 @@ 'type': 'object', }, ), - ( - CronStr, - { - 'title': 'Model', - 'type': 'object', - 'properties': { - 'x': { - 'title': 'X', - 'type': 'string', - } - }, - 'required': ['x'], - }, - ), ( CountryAlpha3, { @@ -129,205 +69,7 @@ }, ), ( - CountryShortName, - { - 'properties': {'x': {'title': 'X', 'type': 'string'}}, - 'required': ['x'], - 'title': 'Model', - 'type': 'object', - }, - ), - ( - MacAddress, - { - 'properties': { - 'x': { - 'title': 'X', - 'type': 'string', - } - }, - 'required': ['x'], - 'title': 'Model', - 'type': 'object', - }, - ), - ( - Latitude, - { - 'properties': { - 'x': { - 'anyOf': [ - {'maximum': 90.0, 'minimum': -90.0, 'type': 'number'}, - {'type': 'string', 'pattern': '^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$'}, - ], - 'title': 'X', - } - }, - 'required': ['x'], - 'title': 'Model', - 'type': 'object', - }, - ), - ( - Longitude, - { - 'properties': { - 'x': { - 'anyOf': [ - {'maximum': 180.0, 'minimum': -180.0, 'type': 'number'}, - {'type': 'string', 'pattern': '^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$'}, - ], - 'title': 'X', - } - }, - 'required': ['x'], - 'title': 'Model', - 'type': 'object', - }, - ), - ( - Coordinate, - { - '$defs': { - 'Coordinate': { - 'properties': { - 'latitude': { - 'anyOf': [ - {'maximum': 90.0, 'minimum': -90.0, 'type': 'number'}, - {'type': 'string', 'pattern': '^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$'}, - ], - 'title': 'Latitude', - }, - 'longitude': { - 'anyOf': [ - {'maximum': 180.0, 'minimum': -180.0, 'type': 'number'}, - {'type': 'string', 'pattern': '^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$'}, - ], - 'title': 'Longitude', - }, - }, - 'required': ['latitude', 'longitude'], - 'title': 'Coordinate', - 'type': 'object', - } - }, - 'properties': { - 'x': { - 'anyOf': [ - {'$ref': '#/$defs/Coordinate'}, - { - 'maxItems': 2, - 'minItems': 2, - 'prefixItems': [ - { - 'anyOf': [ - {'type': 'number'}, - {'type': 'string', 'pattern': '^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$'}, - ] - }, - { - 'anyOf': [ - {'type': 'number'}, - {'type': 'string', 'pattern': '^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$'}, - ] - }, - ], - 'type': 'array', - }, - {'type': 'string'}, - ], - 'title': 'X', - }, - }, - 'required': ['x'], - 'title': 'Model', - 'type': 'object', - }, - ), - ( - ULID, - { - 'properties': { - 'x': { - 'anyOf': [ - {'type': 'integer'}, - {'format': 'binary', 'type': 'string'}, - {'type': 'string'}, - {'format': 'uuid', 'type': 'string'}, - ], - 'title': 'X', - } - }, - 'required': ['x'], - 'title': 'Model', - 'type': 'object', - }, - ), - ( - ISBN, - { - 'properties': { - 'x': { - 'title': 'X', - 'type': 'string', - } - }, - 'required': ['x'], - 'title': 'Model', - 'type': 'object', - }, - ), - ( - JsonSchema, - { - 'properties': { - 'x': { - 'anyOf': [ - {'type': 'string'}, - {'additionalProperties': True, 'type': 'object'}, - ], - 'title': 'X', - } - }, - 'required': ['x'], - 'title': 'Model', - 'type': 'object', - }, - ), - ( - ISIN, - { - 'properties': { - 'x': { - 'title': 'X', - 'type': 'string', - } - }, - 'required': ['x'], - 'title': 'Model', - 'type': 'object', - }, - ), - ( - DateTime, - { - 'properties': {'x': {'format': 'date-time', 'title': 'X', 'type': 'string'}}, - 'required': ['x'], - 'title': 'Model', - 'type': 'object', - }, - ), - ( - LanguageAlpha2, - { - 'properties': {'x': {'pattern': '^\\w{2}$', 'title': 'X', 'type': 'string'}}, - 'required': ['x'], - 'title': 'Model', - 'type': 'object', - }, - ), - ( - LanguageName, + CountryOfficialName, { 'properties': {'x': {'title': 'X', 'type': 'string'}}, 'required': ['x'], @@ -336,123 +78,7 @@ }, ), ( - ISO639_3, - { - 'properties': { - 'x': { - 'title': 'X', - 'type': 'string', - 'enum': languages, - 'maxLength': 3, - 'minLength': 3, - } - }, - 'required': ['x'], - 'title': 'Model', - 'type': 'object', - }, - ), - ( - ISO639_5, - { - 'properties': { - 'x': { - 'title': 'X', - 'type': 'string', - 'enum': language_families, - 'maxLength': 3, - 'minLength': 3, - } - }, - 'required': ['x'], - 'title': 'Model', - 'type': 'object', - }, - ), - ( - ISO4217, - { - 'properties': { - 'x': { - 'title': 'X', - 'type': 'string', - 'enum': currencies, - 'maxLength': 3, - 'minLength': 3, - } - }, - 'required': ['x'], - 'title': 'Model', - 'type': 'object', - }, - ), - ( - Currency, - { - 'properties': { - 'x': { - 'title': 'X', - 'type': 'string', - 'enum': everyday_currencies, - 'maxLength': 3, - 'minLength': 3, - } - }, - 'required': ['x'], - 'title': 'Model', - 'type': 'object', - }, - ), - ( - ISO_15924, - { - 'properties': { - 'x': { - 'title': 'X', - 'type': 'string', - 'enum': scripts, - 'maxLength': 4, - 'minLength': 4, - } - }, - 'required': ['x'], - 'title': 'Model', - 'type': 'object', - }, - ), - ( - SemanticVersion, - { - 'properties': { - 'x': { - 'title': 'X', - 'type': 'string', - 'pattern': r'^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$', - } - }, - 'required': ['x'], - 'title': 'Model', - 'type': 'object', - }, - ), - ( - TimeZoneName, - { - 'properties': { - 'x': { - 'title': 'X', - 'type': 'string', - 'enum': timezone_names, - 'minLength': 1, - } - }, - 'required': ['x'], - 'title': 'Model', - 'type': 'object', - }, - ), - ( - _VersionPydanticAnnotation, + CountryShortName, { 'properties': {'x': {'title': 'X', 'type': 'string'}}, 'required': ['x'], @@ -461,153 +87,17 @@ }, ), ( - PhoneNumber, - { - 'title': 'Model', - 'type': 'object', - 'properties': { - 'x': { - 'title': 'X', - 'type': 'string', - 'format': 'phone', - } - }, - 'required': ['x'], - }, - ), - ( - AnyNumberRFC3966, - { - 'title': 'Model', - 'type': 'object', - 'properties': { - 'x': { - 'title': 'X', - 'type': 'string', - 'format': 'phone', - } - }, - 'required': ['x'], - }, - ), - ( - USNumberE164, - { - 'title': 'Model', - 'type': 'object', - 'properties': { - 'x': { - 'title': 'X', - 'type': 'string', - 'format': 'phone', - } - }, - 'required': ['x'], - }, - ), - ( - S3Path, - { - 'title': 'Model', - 'type': 'object', - 'properties': { - 'x': { - 'pattern': '^s3://([^/]+)/(.*?([^/]+)/?)$', - 'title': 'X', - 'type': 'string', - }, - }, - 'required': [ - 'x', - ], - }, - ), - ( - DomainStr, - { - 'title': 'Model', - 'type': 'object', - 'properties': { - 'x': { - 'title': 'X', - 'type': 'string', - }, - }, - 'required': [ - 'x', - ], - }, - ), - ( - epoch.Integer, - { - 'title': 'Model', - 'type': 'object', - 'properties': { - 'x': { - 'title': 'X', - 'type': 'integer', - 'format': 'date-time', - }, - }, - 'required': [ - 'x', - ], - }, - ), - ( - epoch.Number, - { - 'title': 'Model', - 'type': 'object', - 'properties': { - 'x': { - 'title': 'X', - 'type': 'number', - 'format': 'date-time', - }, - }, - 'required': [ - 'x', - ], - }, - ), - ( - MongoObjectId, + Series, { - 'title': 'Model', - 'type': 'object', - 'properties': { - 'x': { - 'maxLength': MongoObjectId.OBJECT_ID_LENGTH, - 'minLength': MongoObjectId.OBJECT_ID_LENGTH, - 'title': 'X', - 'type': 'string', - }, - }, + 'properties': {'x': {'title': 'X'}}, 'required': ['x'], - }, - ), - ( - MimeType, - { 'title': 'Model', 'type': 'object', - 'properties': { - 'x': { - 'pattern': r'^[a-zA-Z0-9][a-zA-Z0-9!#$&\-\^_+.]+/[a-zA-Z0-9][a-zA-Z0-9!#$&\-\^_+.]+$', - 'title': 'X', - 'type': 'string', - }, - }, - 'required': ['x'], }, ), ], ) -def test_json_schema(cls: Any, expected: dict[str, Any]) -> None: - """Test the model_json_schema implementation for all extra types.""" - +def test_json_schema(cls, expected): class Model(BaseModel): x: cls From 88de1b0caf8ebb75bb7fdded8705e286837ae75b Mon Sep 17 00:00:00 2001 From: Yasser Tahiri Date: Fri, 30 Jun 2023 20:48:50 +0400 Subject: [PATCH 12/31] =?UTF-8?q?=F0=9F=8D=B1=20update=20requirements?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 22bdd14a..545ee81a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,7 +39,7 @@ classifiers = [ ] requires-python = '>=3.7' dependencies = [ - 'pydantic>=2.0b1', + 'pydantic>=2.0', ] dynamic = ['version'] @@ -47,6 +47,7 @@ dynamic = ['version'] all = [ 'phonenumbers>=8,<9', 'pycountry>=22,<23', + 'pandas>=1.3,<3', ] [project.urls] From eeff98a87cbeccdc8df55a64f5db2602c5de7ee3 Mon Sep 17 00:00:00 2001 From: Yasser Tahiri Date: Fri, 30 Jun 2023 21:06:29 +0400 Subject: [PATCH 13/31] =?UTF-8?q?=F0=9F=8D=B1=20Fix=20Requirements?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 545ee81a..34e9f9b5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,7 +47,7 @@ dynamic = ['version'] all = [ 'phonenumbers>=8,<9', 'pycountry>=22,<23', - 'pandas>=1.3,<3', + 'pandas>=1.3,<2', ] [project.urls] From f3f385307e18289511358767de0e13d6eba8730e Mon Sep 17 00:00:00 2001 From: Yasser Tahiri Date: Fri, 30 Jun 2023 21:14:55 +0400 Subject: [PATCH 14/31] =?UTF-8?q?=F0=9F=8D=B1=20Fix=20Requirements?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 34e9f9b5..ccf5fb2d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,7 +47,7 @@ dynamic = ['version'] all = [ 'phonenumbers>=8,<9', 'pycountry>=22,<23', - 'pandas>=1.3,<2', + 'pandas==1.3.5', ] [project.urls] From 43a7e8a87c38c9e434390df4f6c743c6c635f767 Mon Sep 17 00:00:00 2001 From: Yasser Tahiri Date: Sat, 1 Jul 2023 22:11:42 +0400 Subject: [PATCH 15/31] Update pydantic_extra_types/pandas_types.py Co-authored-by: Akbar <98239031+EkberHasanov@users.noreply.github.com> --- pydantic_extra_types/pandas_types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pydantic_extra_types/pandas_types.py b/pydantic_extra_types/pandas_types.py index 57542e96..f706b58a 100644 --- a/pydantic_extra_types/pandas_types.py +++ b/pydantic_extra_types/pandas_types.py @@ -7,7 +7,7 @@ import pandas as pd except ModuleNotFoundError: # pragma: no cover raise RuntimeError( - '`PhoneNumber` requires "phonenumbers" to be installed. You can install it with "pip install phonenumbers"' + '`Series` requires "pandas" to be installed. You can install it with "pip install pandas"' ) T = TypeVar('T', str, bytes, bool, int, float, complex, pd.Timestamp, pd.Timedelta, pd.Period) From e8117c49f6b6b517412e62d5e3eed0e76ed6614f Mon Sep 17 00:00:00 2001 From: EkberHasanov Date: Fri, 7 Jul 2023 01:49:10 +0400 Subject: [PATCH 16/31] Inheriting directly from pd.Series --- m.py | 50 ++++++++++++++++++++++++++++ pydantic_extra_types/pandas_types.py | 35 +++---------------- tests/test_pandas_types.py | 24 ++++++------- 3 files changed, 66 insertions(+), 43 deletions(-) create mode 100644 m.py diff --git a/m.py b/m.py new file mode 100644 index 00000000..5ea874ad --- /dev/null +++ b/m.py @@ -0,0 +1,50 @@ +from pydantic import BaseModel, validator +from pydantic_extra_types.pandas_types import Series +import pandas as pd + + +""" +from typing import Any, Type, TypeVar + +import pandas as pd +from pydantic import GetCoreSchemaHandler +from pydantic_core import core_schema + +T = TypeVar('T', str, bytes, bool, int, float, complex, pd.Timestamp, pd.Timedelta, pd.Period) + + +class Series(pd.Series): # type: ignore + @classmethod + def __get_pydantic_core_schema__( + cls, source: Type[Any], handler: GetCoreSchemaHandler + ) -> core_schema.AfterValidatorFunctionSchema: + return core_schema.general_after_validator_function( + cls._validate, + core_schema.any_schema(), + ) + + @classmethod + def _validate(cls, __input_value: Any, _: core_schema.ValidationInfo) -> 'Series': + return cls(__input_value) +""" + + +class Model(BaseModel): + x: Series + + +m = Model(x=[1, 2, 4]) +s = pd.Series([1, 2, 4]) +t = Series(data=[1, 2, 4], index=["a", "b", "d"]) + +print(m) +print(m.x) + +print(s) + +print(m.x == s) + +print(isinstance(m.x, pd.Series)) + +print(t) +print(t.index) diff --git a/pydantic_extra_types/pandas_types.py b/pydantic_extra_types/pandas_types.py index f706b58a..dbb41878 100644 --- a/pydantic_extra_types/pandas_types.py +++ b/pydantic_extra_types/pandas_types.py @@ -1,22 +1,13 @@ -from typing import Any, List, Tuple, Type, TypeVar, Union +from typing import Any, Type, TypeVar +import pandas as pd from pydantic import GetCoreSchemaHandler from pydantic_core import core_schema -try: - import pandas as pd -except ModuleNotFoundError: # pragma: no cover - raise RuntimeError( - '`Series` requires "pandas" to be installed. You can install it with "pip install pandas"' - ) - T = TypeVar('T', str, bytes, bool, int, float, complex, pd.Timestamp, pd.Timedelta, pd.Period) -class Series: - def __init__(self, value: Any) -> None: - self.value = pd.Series(value) - +class Series(pd.Series): # type: ignore @classmethod def __get_pydantic_core_schema__( cls, source: Type[Any], handler: GetCoreSchemaHandler @@ -28,22 +19,4 @@ def __get_pydantic_core_schema__( @classmethod def _validate(cls, __input_value: Any, _: core_schema.ValidationInfo) -> 'Series': - if isinstance(__input_value, pd.Series): - return cls(__input_value) - return cls(pd.Series(__input_value)) - - def __repr__(self) -> str: - return repr(self.value) - - def __getattr__(self, name: str) -> Any: - return getattr(self.value, name) - - def __eq__(self, __value: object) -> bool: - return isinstance(__value, (pd.Series, Series)) - - def __add__(self, other: Union['Series', List[Any], Tuple[Any], T]) -> 'Series': - if isinstance(other, Series): - result_val = self.value + other.value - else: - result_val = self.value + other - return Series(result_val) + return cls(__input_value) diff --git a/tests/test_pandas_types.py b/tests/test_pandas_types.py index 84745899..c11f7cac 100644 --- a/tests/test_pandas_types.py +++ b/tests/test_pandas_types.py @@ -28,8 +28,8 @@ def test_series_creation(data, expected): else: s = Series(data) assert isinstance(s, Series) - assert isinstance(s.value, pd.Series) - assert s.value.tolist() == expected + assert isinstance(s, pd.Series) + assert s.tolist() == expected def test_series_repr(): @@ -48,8 +48,8 @@ def test_series_equality(): data = [1, 2, 3] s1 = Series(data) s2 = Series(data) - assert s1 == s2 - assert s2 == pd.Series(data) + assert s1.equals(other=s2) + assert s2.equals(pd.Series(data)) def test_series_addition(): @@ -58,8 +58,8 @@ def test_series_addition(): s1 = Series(data1) s2 = Series(data2) s3 = s1 + s2 - assert isinstance(s3, Series) - assert s3.value.tolist() == [5, 7, 9] + assert isinstance(s3, pd.Series) + assert s3.tolist() == [5, 7, 9] @pytest.mark.parametrize( @@ -73,8 +73,8 @@ def test_series_addition(): def test_series_addition_with_types(data, other, expected): s = Series(data) result = s + other - assert isinstance(result, Series) - assert result.value.tolist() == expected + assert isinstance(result, pd.Series) + assert result.tolist() == expected @pytest.mark.parametrize( @@ -104,12 +104,12 @@ def test_series_addition_invalid_value_error(data, other) -> None: def test_valid_series_model(SeriesModel) -> None: model = SeriesModel(data=[1, 2, 4]) - assert isinstance(model.data, Series) - assert model.data == pd.Series([1, 2, 4]) + assert isinstance(model.data, pd.Series) + assert model.data.equals(pd.Series([1, 2, 4])) def test_valid_series_model_with_pd_series(SeriesModel) -> None: s = pd.Series([1, 2, 4]) model = SeriesModel(data=s) - assert isinstance(model.data, Series) - assert model.data == s + assert isinstance(model.data, pd.Series) + assert model.data.equals(s) From ca0c9dbee98d7f9cf540b93fe5b6d72f647a583d Mon Sep 17 00:00:00 2001 From: Akbar <98239031+EkberHasanov@users.noreply.github.com> Date: Wed, 12 Jul 2023 22:01:19 +0400 Subject: [PATCH 17/31] delete extra files --- m.py | 50 -------------------------------------------------- 1 file changed, 50 deletions(-) delete mode 100644 m.py diff --git a/m.py b/m.py deleted file mode 100644 index 5ea874ad..00000000 --- a/m.py +++ /dev/null @@ -1,50 +0,0 @@ -from pydantic import BaseModel, validator -from pydantic_extra_types.pandas_types import Series -import pandas as pd - - -""" -from typing import Any, Type, TypeVar - -import pandas as pd -from pydantic import GetCoreSchemaHandler -from pydantic_core import core_schema - -T = TypeVar('T', str, bytes, bool, int, float, complex, pd.Timestamp, pd.Timedelta, pd.Period) - - -class Series(pd.Series): # type: ignore - @classmethod - def __get_pydantic_core_schema__( - cls, source: Type[Any], handler: GetCoreSchemaHandler - ) -> core_schema.AfterValidatorFunctionSchema: - return core_schema.general_after_validator_function( - cls._validate, - core_schema.any_schema(), - ) - - @classmethod - def _validate(cls, __input_value: Any, _: core_schema.ValidationInfo) -> 'Series': - return cls(__input_value) -""" - - -class Model(BaseModel): - x: Series - - -m = Model(x=[1, 2, 4]) -s = pd.Series([1, 2, 4]) -t = Series(data=[1, 2, 4], index=["a", "b", "d"]) - -print(m) -print(m.x) - -print(s) - -print(m.x == s) - -print(isinstance(m.x, pd.Series)) - -print(t) -print(t.index) From dba691b6e887771fd6f1494f856b43b69d861591 Mon Sep 17 00:00:00 2001 From: EkberHasanov Date: Sun, 25 Feb 2024 23:33:24 +0400 Subject: [PATCH 18/31] upgrading version of pandas --- pydantic_extra_types/pandas_types.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pydantic_extra_types/pandas_types.py b/pydantic_extra_types/pandas_types.py index dbb41878..02a03205 100644 --- a/pydantic_extra_types/pandas_types.py +++ b/pydantic_extra_types/pandas_types.py @@ -1,16 +1,16 @@ -from typing import Any, Type, TypeVar +from __future__ import annotations + +from typing import Any import pandas as pd from pydantic import GetCoreSchemaHandler from pydantic_core import core_schema -T = TypeVar('T', str, bytes, bool, int, float, complex, pd.Timestamp, pd.Timedelta, pd.Period) - class Series(pd.Series): # type: ignore @classmethod def __get_pydantic_core_schema__( - cls, source: Type[Any], handler: GetCoreSchemaHandler + cls, source: type[Any], handler: GetCoreSchemaHandler ) -> core_schema.AfterValidatorFunctionSchema: return core_schema.general_after_validator_function( cls._validate, @@ -18,5 +18,5 @@ def __get_pydantic_core_schema__( ) @classmethod - def _validate(cls, __input_value: Any, _: core_schema.ValidationInfo) -> 'Series': + def _validate(cls, __input_value: Any, _: core_schema.ValidationInfo) -> Series: return cls(__input_value) From 6d245834d95c386eb122f5501d3ca20b63db689e Mon Sep 17 00:00:00 2001 From: Akbar <98239031+EkberHasanov@users.noreply.github.com> Date: Sun, 25 Feb 2024 23:45:31 +0400 Subject: [PATCH 19/31] Update test_json_schema.py --- tests/test_json_schema.py | 118 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 114 insertions(+), 4 deletions(-) diff --git a/tests/test_json_schema.py b/tests/test_json_schema.py index 9cd0e19d..dd37c91c 100644 --- a/tests/test_json_schema.py +++ b/tests/test_json_schema.py @@ -2,15 +2,19 @@ from pydantic import BaseModel from pydantic_extra_types.color import Color +from pydantic_extra_types.coordinate import Coordinate, Latitude, Longitude from pydantic_extra_types.country import ( CountryAlpha2, CountryAlpha3, CountryNumericCode, - CountryOfficialName, CountryShortName, ) +from pydantic_extra_types.isbn import ISBN +from pydantic_extra_types.mac_address import MacAddress from pydantic_extra_types.pandas_types import Series from pydantic_extra_types.payment import PaymentCardNumber +from pydantic_extra_types.pendulum_dt import DateTime +from pydantic_extra_types.ulid import ULID @pytest.mark.parametrize( @@ -69,7 +73,7 @@ }, ), ( - CountryOfficialName, + CountryShortName, { 'properties': {'x': {'title': 'X', 'type': 'string'}}, 'required': ['x'], @@ -78,9 +82,115 @@ }, ), ( - CountryShortName, + MacAddress, { - 'properties': {'x': {'title': 'X', 'type': 'string'}}, + 'properties': { + 'x': { + 'title': 'X', + 'type': 'string', + } + }, + 'required': ['x'], + 'title': 'Model', + 'type': 'object', + }, + ), + ( + Latitude, + { + 'properties': { + 'x': { + 'maximum': 90.0, + 'minimum': -90.0, + 'title': 'X', + 'type': 'number', + } + }, + 'required': ['x'], + 'title': 'Model', + 'type': 'object', + }, + ), + ( + Longitude, + { + 'properties': { + 'x': { + 'maximum': 180.0, + 'minimum': -180.0, + 'title': 'X', + 'type': 'number', + } + }, + 'required': ['x'], + 'title': 'Model', + 'type': 'object', + }, + ), + ( + Coordinate, + { + '$defs': { + 'Coordinate': { + 'properties': { + 'latitude': {'maximum': 90.0, 'minimum': -90.0, 'title': 'Latitude', 'type': 'number'}, + 'longitude': {'maximum': 180.0, 'minimum': -180.0, 'title': 'Longitude', 'type': 'number'}, + }, + 'required': ['latitude', 'longitude'], + 'title': 'Coordinate', + 'type': 'object', + } + }, + 'properties': { + 'x': { + 'anyOf': [ + {'$ref': '#/$defs/Coordinate'}, + { + 'maxItems': 2, + 'minItems': 2, + 'prefixItems': [ + {'type': 'number'}, + {'type': 'number'}, + ], + 'type': 'array', + }, + {'type': 'string'}, + ], + 'title': 'X', + }, + }, + 'required': ['x'], + 'title': 'Model', + 'type': 'object', + }, + ), + ( + ULID, + { + 'properties': { + 'x': { + 'anyOf': [{'type': 'integer'}, {'format': 'binary', 'type': 'string'}, {'type': 'string'}], + 'title': 'X', + } + }, + 'required': ['x'], + 'title': 'Model', + 'type': 'object', + }, + ), + ( + ISBN, + { + 'properties': {'x': {'title': 'X'}}, + 'required': ['x'], + 'title': 'Model', + 'type': 'object', + }, + ), + ( + DateTime, + { + 'properties': {'x': {'format': 'date-time', 'title': 'X', 'type': 'string'}}, 'required': ['x'], 'title': 'Model', 'type': 'object', From 7068c410589c22e3464511ce699cb839a8e1c6af Mon Sep 17 00:00:00 2001 From: EkberHasanov Date: Mon, 26 Feb 2024 00:08:21 +0400 Subject: [PATCH 20/31] fixing some issues --- pydantic_extra_types/isbn.py | 35 +++++++++++++++++----------- pydantic_extra_types/pandas_types.py | 4 ++-- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/pydantic_extra_types/isbn.py b/pydantic_extra_types/isbn.py index 978f7abe..0e0ac6d7 100644 --- a/pydantic_extra_types/isbn.py +++ b/pydantic_extra_types/isbn.py @@ -1,11 +1,12 @@ -"""The `pydantic_extra_types.isbn` module provides functionality to receive and validate ISBN. +""" +The `pydantic_extra_types.isbn` module provides functionality to recieve and validate ISBN. -ISBN (International Standard Book Number) is a numeric commercial book identifier which is intended to be unique. This module provides an ISBN type for Pydantic models. +ISBN (International Standard Book Number) is a numeric commercial book identifier which is intended to be unique. +This module provides a ISBN type for Pydantic models. """ from __future__ import annotations -import itertools as it from typing import Any from pydantic import GetCoreSchemaHandler @@ -13,7 +14,7 @@ def isbn10_digit_calc(isbn: str) -> str: - """Calculate the ISBN-10 check digit from the provided str value. More information on the validation algorithm on [Wikipedia](https://en.wikipedia.org/wiki/ISBN#Check_digits) + """Calc a ISBN-10 last digit from the provided str value. More information of validation algorithm on [Wikipedia](https://en.wikipedia.org/wiki/ISBN#Check_digits) Args: isbn: The str value representing the ISBN in 10 digits. @@ -22,13 +23,16 @@ def isbn10_digit_calc(isbn: str) -> str: The calculated last digit of the ISBN-10 value. """ total = sum(int(digit) * (10 - idx) for idx, digit in enumerate(isbn[:9])) - diff = (11 - total) % 11 - valid_check_digit = 'X' if diff == 10 else str(diff) + + for check_digit in range(1, 11): + if (total + check_digit) % 11 == 0: + valid_check_digit = 'X' if check_digit == 10 else str(check_digit) + return valid_check_digit def isbn13_digit_calc(isbn: str) -> str: - """Calc a ISBN-13 last digit from the provided str value. More information on the validation algorithm on [Wikipedia](https://en.wikipedia.org/wiki/ISBN#Check_digits) + """Calc a ISBN-13 last digit from the provided str value. More information of validation algorithm on [Wikipedia](https://en.wikipedia.org/wiki/ISBN#Check_digits) Args: isbn: The str value representing the ISBN in 13 digits. @@ -36,9 +40,9 @@ def isbn13_digit_calc(isbn: str) -> str: Returns: The calculated last digit of the ISBN-13 value. """ - total = sum(int(digit) * factor for digit, factor in zip(isbn[:12], it.cycle((1, 3)))) + total = sum(int(digit) * (1 if idx % 2 == 0 else 3) for idx, digit in enumerate(isbn[:12])) - check_digit = (10 - total) % 10 + check_digit = (10 - (total % 10)) % 10 return str(check_digit) @@ -55,16 +59,16 @@ class ISBN(str): class Book(BaseModel): isbn: ISBN - - book = Book(isbn='8537809667') + book = Book(isbn="8537809667") print(book) - # > isbn='9788537809662' + #> isbn='9788537809662' ``` """ @classmethod def __get_pydantic_core_schema__(cls, source: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: - """Return a Pydantic CoreSchema with the ISBN validation. + """ + Return a Pydantic CoreSchema with the ISBN validation. Args: source: The source type to be converted. @@ -81,7 +85,8 @@ def __get_pydantic_core_schema__(cls, source: type[Any], handler: GetCoreSchemaH @classmethod def _validate(cls, __input_value: str, _: Any) -> str: - """Validate a ISBN from the provided str value. + """ + Validate a ISBN from the provided str value. Args: __input_value: The str value to be validated. @@ -107,6 +112,7 @@ def validate_isbn_format(value: str) -> None: Raises: PydanticCustomError: If the ISBN is not valid. """ + isbn_length = len(value) if isbn_length not in (10, 13): @@ -138,6 +144,7 @@ def convert_isbn10_to_isbn13(value: str) -> str: Returns: The converted ISBN or the original value if no conversion is necessary. """ + if len(value) == 10: base_isbn = f'978{value[:-1]}' isbn13_digit = isbn13_digit_calc(base_isbn) diff --git a/pydantic_extra_types/pandas_types.py b/pydantic_extra_types/pandas_types.py index 02a03205..820f34e9 100644 --- a/pydantic_extra_types/pandas_types.py +++ b/pydantic_extra_types/pandas_types.py @@ -11,8 +11,8 @@ class Series(pd.Series): # type: ignore @classmethod def __get_pydantic_core_schema__( cls, source: type[Any], handler: GetCoreSchemaHandler - ) -> core_schema.AfterValidatorFunctionSchema: - return core_schema.general_after_validator_function( + ) -> core_schema.BeforeValidatorFunctionSchema: + return core_schema.no_info_before_validator_function( cls._validate, core_schema.any_schema(), ) From f6679dc16e9bab4e3ca55e70de3779f5e1b9acda Mon Sep 17 00:00:00 2001 From: EkberHasanov Date: Thu, 29 Feb 2024 15:44:14 +0400 Subject: [PATCH 21/31] change core_schema func --- pydantic_extra_types/pandas_types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pydantic_extra_types/pandas_types.py b/pydantic_extra_types/pandas_types.py index 820f34e9..914eed15 100644 --- a/pydantic_extra_types/pandas_types.py +++ b/pydantic_extra_types/pandas_types.py @@ -12,7 +12,7 @@ class Series(pd.Series): # type: ignore def __get_pydantic_core_schema__( cls, source: type[Any], handler: GetCoreSchemaHandler ) -> core_schema.BeforeValidatorFunctionSchema: - return core_schema.no_info_before_validator_function( + return core_schema.general_before_validator_function( cls._validate, core_schema.any_schema(), ) From 3bf6d65279034656b94c56b85ade419c5bd1910a Mon Sep 17 00:00:00 2001 From: Akbar <98239031+EkberHasanov@users.noreply.github.com> Date: Thu, 29 Feb 2024 15:52:14 +0400 Subject: [PATCH 22/31] Update test_json_schema.py --- tests/test_json_schema.py | 83 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 3 deletions(-) diff --git a/tests/test_json_schema.py b/tests/test_json_schema.py index dd37c91c..3c170b2e 100644 --- a/tests/test_json_schema.py +++ b/tests/test_json_schema.py @@ -1,6 +1,8 @@ +import pycountry import pytest from pydantic import BaseModel +import pydantic_extra_types from pydantic_extra_types.color import Color from pydantic_extra_types.coordinate import Coordinate, Latitude, Longitude from pydantic_extra_types.country import ( @@ -9,13 +11,29 @@ CountryNumericCode, CountryShortName, ) +from pydantic_extra_types.currency_code import ISO4217, Currency from pydantic_extra_types.isbn import ISBN +from pydantic_extra_types.language_code import ISO639_3, ISO639_5 from pydantic_extra_types.mac_address import MacAddress -from pydantic_extra_types.pandas_types import Series from pydantic_extra_types.payment import PaymentCardNumber from pydantic_extra_types.pendulum_dt import DateTime from pydantic_extra_types.ulid import ULID +languages = [lang.alpha_3 for lang in pycountry.languages] +language_families = [lang.alpha_3 for lang in pycountry.language_families] +languages.sort() +language_families.sort() + +currencies = [currency.alpha_3 for currency in pycountry.currencies] +currencies.sort() +everyday_currencies = [ + currency.alpha_3 + for currency in pycountry.currencies + if currency.alpha_3 not in pydantic_extra_types.currency_code._CODES_FOR_BONDS_METAL_TESTING +] + +everyday_currencies.sort() + @pytest.mark.parametrize( 'cls,expected', @@ -197,9 +215,68 @@ }, ), ( - Series, + ISO639_3, { - 'properties': {'x': {'title': 'X'}}, + 'properties': { + 'x': { + 'title': 'X', + 'type': 'string', + 'enum': languages, + 'maxLength': 3, + 'minLength': 3, + } + }, + 'required': ['x'], + 'title': 'Model', + 'type': 'object', + }, + ), + ( + ISO639_5, + { + 'properties': { + 'x': { + 'title': 'X', + 'type': 'string', + 'enum': language_families, + 'maxLength': 3, + 'minLength': 3, + } + }, + 'required': ['x'], + 'title': 'Model', + 'type': 'object', + }, + ), + ( + ISO4217, + { + 'properties': { + 'x': { + 'title': 'X', + 'type': 'string', + 'enum': currencies, + 'maxLength': 3, + 'minLength': 3, + } + }, + 'required': ['x'], + 'title': 'Model', + 'type': 'object', + }, + ), + ( + Currency, + { + 'properties': { + 'x': { + 'title': 'X', + 'type': 'string', + 'enum': everyday_currencies, + 'maxLength': 3, + 'minLength': 3, + } + }, 'required': ['x'], 'title': 'Model', 'type': 'object', From 109f49311af99bf5bd2d2475d016dc044863cfdd Mon Sep 17 00:00:00 2001 From: Akbar Hasanov Date: Tue, 7 Apr 2026 03:42:08 +0200 Subject: [PATCH 23/31] Restore non-pandas files to match upstream/main --- .github/workflows/ci.yml | 150 +++++++-------- pydantic_extra_types/isbn.py | 35 ++-- pyproject.toml | 98 ++++++++-- tests/test_json_schema.py | 363 +++++++++++++++++++++++++++++++++-- 4 files changed, 504 insertions(+), 142 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a7f1a77c..d28730bb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,144 +8,134 @@ on: - '**' pull_request: {} +permissions: {} + +env: + COLUMNS: 150 + jobs: lint: runs-on: ubuntu-latest - + strategy: + fail-fast: false + matrix: + python-version: ['3.13'] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - - name: set up python - uses: actions/setup-python@v4 + - uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0 with: - python-version: '3.10' + enable-cache: true # zizmor: ignore[cache-poisoning] -- Job does not produce release artifacts and does not have sensitive permissions - - name: install - run: | - pip install -r requirements/pyproject.txt && pip install -r requirements/linting.txt + - name: Install dependencies + run: uv sync --python ${{ matrix.python-version }} --group lint --all-extras - - uses: pre-commit/action@v3.0.0 + - uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1 with: extra_args: --all-files --verbose + env: + SKIP: no-commit-to-branch + UV_NO_PROGRESS: '1' test: - name: test py${{ matrix.python-version }} on ${{ matrix.os }} + name: test py${{ matrix.python-version }} + runs-on: ubuntu-latest strategy: fail-fast: false matrix: - os: [ubuntu, macos, windows] - python-version: ['3.7', '3.8', '3.9', '3.10', '3.11'] + python-version: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14'] - runs-on: ${{ matrix.os }}-latest - steps: - - uses: actions/checkout@v3 + env: + UV_PYTHON: ${{ matrix.python-version }} - - name: set up python - uses: actions/setup-python@v4 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - python-version: ${{ matrix.python-version }} - - - name: Install bzip2 on macOS - if: matrix.os == 'macos' - run: brew install bzip2 - - - - name: Set environment variables - if: matrix.os == 'macos' - run: | - export LDFLAGS="-L/usr/local/opt/bzip2/lib" - export CPPFLAGS="-I/usr/local/opt/bzip2/include" - - - run: | - pip install -r requirements/pyproject.txt && pip install -r requirements/testing.txt + persist-credentials: false + - uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0 + with: + enable-cache: true # zizmor: ignore[cache-poisoning] -- Job does not produce release artifacts and does not have sensitive permissions - - run: pip freeze + - name: Install dependencies + run: uv sync --extra all - - run: mkdir coverage + - name: Make coverage directory + run: mkdir coverage - - run: make test + - run: uv run --frozen coverage run -m pytest env: - COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}-with-deps - CONTEXT: ${{ runner.os }}-py${{ matrix.python-version }}-with-deps + COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }} - name: store coverage files - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: - name: coverage + name: coverage-${{ matrix.python-version }} path: coverage + include-hidden-files: true - coverage-combine: - needs: [test] + coverage: runs-on: ubuntu-latest - + needs: [test] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - - uses: actions/setup-python@v4 + - uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0 with: - python-version: '3.8' + enable-cache: true # zizmor: ignore[cache-poisoning] -- Job does not produce release artifacts and does not have sensitive permissions - name: get coverage files - uses: actions/download-artifact@v3 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: - name: coverage + merge-multiple: true path: coverage - - run: pip install coverage[toml] - - - run: ls -la coverage - - run: coverage combine coverage - - run: coverage report - - run: coverage html --show-contexts --title "pydantic coverage for ${{ github.sha }}" + - run: uv run --frozen coverage combine coverage - - name: Store coverage html - uses: actions/upload-artifact@v3 - with: - name: coverage-html - path: htmlcov + - run: uv run --frozen coverage report --fail-under 85 # https://github.com/marketplace/actions/alls-green#why used for branch protection checks check: if: always() - needs: [lint, test] + needs: [lint, test, coverage] runs-on: ubuntu-latest steps: - name: Decide whether the needed jobs succeeded or failed - uses: re-actors/alls-green@release/v1 + uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # v1.2.2 with: jobs: ${{ toJSON(needs) }} - deploy: - name: Deploy + release: needs: [check] - if: "success() && startsWith(github.ref, 'refs/tags/')" + if: 'success() && startsWith(github.ref, ''refs/tags/'')' runs-on: ubuntu-latest + environment: release - steps: - - uses: actions/checkout@v2 + permissions: + id-token: write - - name: set up python - uses: actions/setup-python@v4 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - python-version: '3.10' + persist-credentials: false - - name: install - run: pip install -U twine build + - uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0 + with: + enable-cache: false - name: check GITHUB_REF matches package version - uses: samuelcolvin/check-python-version@v3 + uses: samuelcolvin/check-python-version@ee87cddb8049d2694cc03badc8569765a05cef00 # v5 with: version_file_path: pydantic_extra_types/__init__.py - - name: build - run: python -m build + - run: uv build - - run: twine check --strict dist/* - - - name: upload to pypi - run: twine upload dist/* - env: - TWINE_USERNAME: __token__ - TWINE_PASSWORD: ${{ secrets.pypi_token }} + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 + with: + skip-existing: true diff --git a/pydantic_extra_types/isbn.py b/pydantic_extra_types/isbn.py index 0e0ac6d7..978f7abe 100644 --- a/pydantic_extra_types/isbn.py +++ b/pydantic_extra_types/isbn.py @@ -1,12 +1,11 @@ -""" -The `pydantic_extra_types.isbn` module provides functionality to recieve and validate ISBN. +"""The `pydantic_extra_types.isbn` module provides functionality to receive and validate ISBN. -ISBN (International Standard Book Number) is a numeric commercial book identifier which is intended to be unique. -This module provides a ISBN type for Pydantic models. +ISBN (International Standard Book Number) is a numeric commercial book identifier which is intended to be unique. This module provides an ISBN type for Pydantic models. """ from __future__ import annotations +import itertools as it from typing import Any from pydantic import GetCoreSchemaHandler @@ -14,7 +13,7 @@ def isbn10_digit_calc(isbn: str) -> str: - """Calc a ISBN-10 last digit from the provided str value. More information of validation algorithm on [Wikipedia](https://en.wikipedia.org/wiki/ISBN#Check_digits) + """Calculate the ISBN-10 check digit from the provided str value. More information on the validation algorithm on [Wikipedia](https://en.wikipedia.org/wiki/ISBN#Check_digits) Args: isbn: The str value representing the ISBN in 10 digits. @@ -23,16 +22,13 @@ def isbn10_digit_calc(isbn: str) -> str: The calculated last digit of the ISBN-10 value. """ total = sum(int(digit) * (10 - idx) for idx, digit in enumerate(isbn[:9])) - - for check_digit in range(1, 11): - if (total + check_digit) % 11 == 0: - valid_check_digit = 'X' if check_digit == 10 else str(check_digit) - + diff = (11 - total) % 11 + valid_check_digit = 'X' if diff == 10 else str(diff) return valid_check_digit def isbn13_digit_calc(isbn: str) -> str: - """Calc a ISBN-13 last digit from the provided str value. More information of validation algorithm on [Wikipedia](https://en.wikipedia.org/wiki/ISBN#Check_digits) + """Calc a ISBN-13 last digit from the provided str value. More information on the validation algorithm on [Wikipedia](https://en.wikipedia.org/wiki/ISBN#Check_digits) Args: isbn: The str value representing the ISBN in 13 digits. @@ -40,9 +36,9 @@ def isbn13_digit_calc(isbn: str) -> str: Returns: The calculated last digit of the ISBN-13 value. """ - total = sum(int(digit) * (1 if idx % 2 == 0 else 3) for idx, digit in enumerate(isbn[:12])) + total = sum(int(digit) * factor for digit, factor in zip(isbn[:12], it.cycle((1, 3)))) - check_digit = (10 - (total % 10)) % 10 + check_digit = (10 - total) % 10 return str(check_digit) @@ -59,16 +55,16 @@ class ISBN(str): class Book(BaseModel): isbn: ISBN - book = Book(isbn="8537809667") + + book = Book(isbn='8537809667') print(book) - #> isbn='9788537809662' + # > isbn='9788537809662' ``` """ @classmethod def __get_pydantic_core_schema__(cls, source: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: - """ - Return a Pydantic CoreSchema with the ISBN validation. + """Return a Pydantic CoreSchema with the ISBN validation. Args: source: The source type to be converted. @@ -85,8 +81,7 @@ def __get_pydantic_core_schema__(cls, source: type[Any], handler: GetCoreSchemaH @classmethod def _validate(cls, __input_value: str, _: Any) -> str: - """ - Validate a ISBN from the provided str value. + """Validate a ISBN from the provided str value. Args: __input_value: The str value to be validated. @@ -112,7 +107,6 @@ def validate_isbn_format(value: str) -> None: Raises: PydanticCustomError: If the ISBN is not valid. """ - isbn_length = len(value) if isbn_length not in (10, 13): @@ -144,7 +138,6 @@ def convert_isbn10_to_isbn13(value: str) -> str: Returns: The converted ISBN or the original value if no conversion is necessary. """ - if len(value) == 10: base_isbn = f'978{value[:-1]}' isbn13_digit = isbn13_digit_calc(base_isbn) diff --git a/pyproject.toml b/pyproject.toml index ccf5fb2d..0a3a9daa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,8 +9,8 @@ path = 'pydantic_extra_types/__init__.py' name = 'pydantic-extra-types' description = 'Extra Pydantic types.' authors = [ - {name = 'Samuel Colvin', email = 's@muelcolvin.com'}, - {name = 'Yasser Tahiri', email = 'hello@yezz.me'}, + { name = 'Samuel Colvin', email = 's@muelcolvin.com' }, + { name = 'Yasser Tahiri', email = 'hello@yezz.me' }, ] license = 'MIT' readme = 'README.md' @@ -19,11 +19,12 @@ classifiers = [ 'Programming Language :: Python', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3 :: Only', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', + 'Programming Language :: Python :: 3.13', + 'Programming Language :: Python :: 3.14', 'Intended Audience :: Developers', 'Intended Audience :: Information Technology', 'Intended Audience :: System Administrators', @@ -37,31 +38,89 @@ classifiers = [ 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: Internet', ] -requires-python = '>=3.7' -dependencies = [ - 'pydantic>=2.0', -] +requires-python = '>=3.9' +dependencies = ['pydantic>=2.5.2','typing-extensions'] dynamic = ['version'] [project.optional-dependencies] all = [ - 'phonenumbers>=8,<9', - 'pycountry>=22,<23', - 'pandas==1.3.5', + 'phonenumbers>=8,<10', + 'pycountry>=23', + 'jsonschema>=4.0.0', + 'semver>=3.0.2', + 'python-ulid>=1,<2; python_version<"3.9"', + 'python-ulid>=1,<4; python_version>="3.9"', + 'pendulum>=3.0.0,<4.0.0', + 'pymongo>=4.0.0,<5.0.0', + 'pytz>=2024.1', + 'semver~=3.0.2', + 'tzdata>=2024.1', + "cron-converter>=1.2.2", + 'uuid-utils>=0.6.0; python_version<"3.14"', +] +phonenumbers = ['phonenumbers>=8,<10'] +pycountry = ['pycountry>=23'] +jsonschema = ['jsonschema>=4.0.0'] +semver = ['semver>=3.0.2'] +python_ulid = [ + 'python-ulid>=1,<2; python_version<"3.9"', + 'python-ulid>=1,<4; python_version>="3.9"', +] +pendulum = ['pendulum>=3.0.0,<4.0.0'] +cron = ['cron-converter>=1.2.2'] +uuid_utils = ['uuid-utils>=0.6.0; python_version<"3.14"'] + +[dependency-groups] +dev = [ + "coverage[toml]>=7.6.1", + "pytest-pretty>=1.2.0", + "dirty-equals>=0.7.1", + "pytest>=8.3.2", +] +lint = [ + "ruff>=0.7.4", + "mypy>=0.910", + "annotated-types>=0.7.0", + "types-jsonschema>=4.23.0.20241208", + "types-pytz>=2024.1.0.20240417", +] +extra = [ + { include-group = 'dev' }, + { include-group = 'lint' }, ] [project.urls] Homepage = 'https://github.com/pydantic/pydantic-extra-types' Source = 'https://github.com/pydantic/pydantic-extra-types' Changelog = 'https://github.com/pydantic/pydantic-extra-types/releases' -Documentation = 'https://docs.pydantic.dev/dev-v2/usage/types/types/' +Documentation = 'https://docs.pydantic.dev/latest/' + +[tool.ruff.lint.pyupgrade] +keep-runtime-typing = true [tool.ruff] line-length = 120 -extend-select = ['Q', 'RUF100', 'C90', 'UP', 'I'] -isort = { known-first-party = ['pydantic_extra_types', 'tests'] } -flake8-quotes = {inline-quotes = 'single', multiline-quotes = 'double'} +target-version = 'py39' + +[tool.ruff.lint] +extend-select = [ + "Q", + "RUF100", + "C90", + "UP", + "I", +] +flake8-quotes = { inline-quotes = 'single', multiline-quotes = 'double' } +isort = {known-first-party = ['pydantic_extra_types', 'tests'] } mccabe = { max-complexity = 14 } +pydocstyle = { convention = 'google' } + +[tool.ruff.format] +docstring-code-format = true +quote-style = "single" + +[tool.ruff.lint.per-file-ignores] +'pydantic_extra_types/color.py' = ['E741'] [tool.coverage.run] source = ['pydantic_extra_types'] @@ -87,12 +146,6 @@ exclude_lines = [ '@overload', ] -[tool.black] -color = true -line-length = 120 -target-version = ['py37'] -skip-string-normalization = true - [tool.mypy] strict = true plugins = 'pydantic.mypy' @@ -102,6 +155,9 @@ filterwarnings = [ 'error', # This ignore will be removed when pycountry will drop py36 & support py311 'ignore:::pkg_resources', + # This ignore will be removed when pendulum fixes https://github.com/sdispater/pendulum/issues/834 + 'ignore:datetime.datetime.utcfromtimestamp.*:DeprecationWarning', + ' ignore:Use from pydantic_extra_types.semver import SemanticVersion instead. Will be removed in 3.0.0.:DeprecationWarning' ] # configuring https://github.com/pydantic/hooky diff --git a/tests/test_json_schema.py b/tests/test_json_schema.py index 3c170b2e..62748d3a 100644 --- a/tests/test_json_schema.py +++ b/tests/test_json_schema.py @@ -1,22 +1,32 @@ +from typing import Annotated, Any, Union + import pycountry import pytest from pydantic import BaseModel import pydantic_extra_types +from pydantic_extra_types import epoch from pydantic_extra_types.color import Color from pydantic_extra_types.coordinate import Coordinate, Latitude, Longitude -from pydantic_extra_types.country import ( - CountryAlpha2, - CountryAlpha3, - CountryNumericCode, - CountryShortName, -) +from pydantic_extra_types.country import CountryAlpha2, CountryAlpha3, CountryNumericCode, CountryShortName +from pydantic_extra_types.cron import CronStr from pydantic_extra_types.currency_code import ISO4217, Currency +from pydantic_extra_types.domain import DomainStr from pydantic_extra_types.isbn import ISBN -from pydantic_extra_types.language_code import ISO639_3, ISO639_5 +from pydantic_extra_types.isin import ISIN +from pydantic_extra_types.json_schema import JsonSchema +from pydantic_extra_types.language_code import ISO639_3, ISO639_5, LanguageAlpha2, LanguageName from pydantic_extra_types.mac_address import MacAddress +from pydantic_extra_types.mime_types import MimeType +from pydantic_extra_types.mongo_object_id import MongoObjectId from pydantic_extra_types.payment import PaymentCardNumber from pydantic_extra_types.pendulum_dt import DateTime +from pydantic_extra_types.phone_numbers import PhoneNumber, PhoneNumberValidator +from pydantic_extra_types.s3 import S3Path +from pydantic_extra_types.script_code import ISO_15924 +from pydantic_extra_types.semantic_version import SemanticVersion +from pydantic_extra_types.semver import _VersionPydanticAnnotation +from pydantic_extra_types.timezone_name import TimeZoneName from pydantic_extra_types.ulid import ULID languages = [lang.alpha_3 for lang in pycountry.languages] @@ -32,8 +42,22 @@ if currency.alpha_3 not in pydantic_extra_types.currency_code._CODES_FOR_BONDS_METAL_TESTING ] +scripts = [script.alpha_4 for script in pycountry.scripts] + +timezone_names = TimeZoneName.allowed_values_list + everyday_currencies.sort() +AnyNumberRFC3966 = Annotated[Union[str, PhoneNumber], PhoneNumberValidator()] +USNumberE164 = Annotated[ + Union[str, PhoneNumber], + PhoneNumberValidator( + supported_regions=['US'], + default_region='US', + number_format='E164', + ), +] + @pytest.mark.parametrize( 'cls,expected', @@ -72,6 +96,20 @@ 'type': 'object', }, ), + ( + CronStr, + { + 'title': 'Model', + 'type': 'object', + 'properties': { + 'x': { + 'title': 'X', + 'type': 'string', + } + }, + 'required': ['x'], + }, + ), ( CountryAlpha3, { @@ -118,10 +156,11 @@ { 'properties': { 'x': { - 'maximum': 90.0, - 'minimum': -90.0, + 'anyOf': [ + {'maximum': 90.0, 'minimum': -90.0, 'type': 'number'}, + {'type': 'string', 'pattern': '^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$'}, + ], 'title': 'X', - 'type': 'number', } }, 'required': ['x'], @@ -134,10 +173,11 @@ { 'properties': { 'x': { - 'maximum': 180.0, - 'minimum': -180.0, + 'anyOf': [ + {'maximum': 180.0, 'minimum': -180.0, 'type': 'number'}, + {'type': 'string', 'pattern': '^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$'}, + ], 'title': 'X', - 'type': 'number', } }, 'required': ['x'], @@ -151,8 +191,20 @@ '$defs': { 'Coordinate': { 'properties': { - 'latitude': {'maximum': 90.0, 'minimum': -90.0, 'title': 'Latitude', 'type': 'number'}, - 'longitude': {'maximum': 180.0, 'minimum': -180.0, 'title': 'Longitude', 'type': 'number'}, + 'latitude': { + 'anyOf': [ + {'maximum': 90.0, 'minimum': -90.0, 'type': 'number'}, + {'type': 'string', 'pattern': '^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$'}, + ], + 'title': 'Latitude', + }, + 'longitude': { + 'anyOf': [ + {'maximum': 180.0, 'minimum': -180.0, 'type': 'number'}, + {'type': 'string', 'pattern': '^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$'}, + ], + 'title': 'Longitude', + }, }, 'required': ['latitude', 'longitude'], 'title': 'Coordinate', @@ -167,8 +219,18 @@ 'maxItems': 2, 'minItems': 2, 'prefixItems': [ - {'type': 'number'}, - {'type': 'number'}, + { + 'anyOf': [ + {'type': 'number'}, + {'type': 'string', 'pattern': '^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$'}, + ] + }, + { + 'anyOf': [ + {'type': 'number'}, + {'type': 'string', 'pattern': '^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$'}, + ] + }, ], 'type': 'array', }, @@ -187,7 +249,12 @@ { 'properties': { 'x': { - 'anyOf': [{'type': 'integer'}, {'format': 'binary', 'type': 'string'}, {'type': 'string'}], + 'anyOf': [ + {'type': 'integer'}, + {'format': 'binary', 'type': 'string'}, + {'type': 'string'}, + {'format': 'uuid', 'type': 'string'}, + ], 'title': 'X', } }, @@ -199,7 +266,43 @@ ( ISBN, { - 'properties': {'x': {'title': 'X'}}, + 'properties': { + 'x': { + 'title': 'X', + 'type': 'string', + } + }, + 'required': ['x'], + 'title': 'Model', + 'type': 'object', + }, + ), + ( + JsonSchema, + { + 'properties': { + 'x': { + 'anyOf': [ + {'type': 'string'}, + {'additionalProperties': True, 'type': 'object'}, + ], + 'title': 'X', + } + }, + 'required': ['x'], + 'title': 'Model', + 'type': 'object', + }, + ), + ( + ISIN, + { + 'properties': { + 'x': { + 'title': 'X', + 'type': 'string', + } + }, 'required': ['x'], 'title': 'Model', 'type': 'object', @@ -214,6 +317,24 @@ 'type': 'object', }, ), + ( + LanguageAlpha2, + { + 'properties': {'x': {'pattern': '^\\w{2}$', 'title': 'X', 'type': 'string'}}, + 'required': ['x'], + 'title': 'Model', + 'type': 'object', + }, + ), + ( + LanguageName, + { + 'properties': {'x': {'title': 'X', 'type': 'string'}}, + 'required': ['x'], + 'title': 'Model', + 'type': 'object', + }, + ), ( ISO639_3, { @@ -282,9 +403,211 @@ 'type': 'object', }, ), + ( + ISO_15924, + { + 'properties': { + 'x': { + 'title': 'X', + 'type': 'string', + 'enum': scripts, + 'maxLength': 4, + 'minLength': 4, + } + }, + 'required': ['x'], + 'title': 'Model', + 'type': 'object', + }, + ), + ( + SemanticVersion, + { + 'properties': { + 'x': { + 'title': 'X', + 'type': 'string', + 'pattern': r'^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$', + } + }, + 'required': ['x'], + 'title': 'Model', + 'type': 'object', + }, + ), + ( + TimeZoneName, + { + 'properties': { + 'x': { + 'title': 'X', + 'type': 'string', + 'enum': timezone_names, + 'minLength': 1, + } + }, + 'required': ['x'], + 'title': 'Model', + 'type': 'object', + }, + ), + ( + _VersionPydanticAnnotation, + { + 'properties': {'x': {'title': 'X', 'type': 'string'}}, + 'required': ['x'], + 'title': 'Model', + 'type': 'object', + }, + ), + ( + PhoneNumber, + { + 'title': 'Model', + 'type': 'object', + 'properties': { + 'x': { + 'title': 'X', + 'type': 'string', + 'format': 'phone', + } + }, + 'required': ['x'], + }, + ), + ( + AnyNumberRFC3966, + { + 'title': 'Model', + 'type': 'object', + 'properties': { + 'x': { + 'title': 'X', + 'type': 'string', + 'format': 'phone', + } + }, + 'required': ['x'], + }, + ), + ( + USNumberE164, + { + 'title': 'Model', + 'type': 'object', + 'properties': { + 'x': { + 'title': 'X', + 'type': 'string', + 'format': 'phone', + } + }, + 'required': ['x'], + }, + ), + ( + S3Path, + { + 'title': 'Model', + 'type': 'object', + 'properties': { + 'x': { + 'pattern': '^s3://([^/]+)/(.*?([^/]+)/?)$', + 'title': 'X', + 'type': 'string', + }, + }, + 'required': [ + 'x', + ], + }, + ), + ( + DomainStr, + { + 'title': 'Model', + 'type': 'object', + 'properties': { + 'x': { + 'title': 'X', + 'type': 'string', + }, + }, + 'required': [ + 'x', + ], + }, + ), + ( + epoch.Integer, + { + 'title': 'Model', + 'type': 'object', + 'properties': { + 'x': { + 'title': 'X', + 'type': 'integer', + 'format': 'date-time', + }, + }, + 'required': [ + 'x', + ], + }, + ), + ( + epoch.Number, + { + 'title': 'Model', + 'type': 'object', + 'properties': { + 'x': { + 'title': 'X', + 'type': 'number', + 'format': 'date-time', + }, + }, + 'required': [ + 'x', + ], + }, + ), + ( + MongoObjectId, + { + 'title': 'Model', + 'type': 'object', + 'properties': { + 'x': { + 'maxLength': MongoObjectId.OBJECT_ID_LENGTH, + 'minLength': MongoObjectId.OBJECT_ID_LENGTH, + 'title': 'X', + 'type': 'string', + }, + }, + 'required': ['x'], + }, + ), + ( + MimeType, + { + 'title': 'Model', + 'type': 'object', + 'properties': { + 'x': { + 'pattern': r'^[a-zA-Z0-9][a-zA-Z0-9!#$&\-\^_+.]+/[a-zA-Z0-9][a-zA-Z0-9!#$&\-\^_+.]+$', + 'title': 'X', + 'type': 'string', + }, + }, + 'required': ['x'], + }, + ), ], ) -def test_json_schema(cls, expected): +def test_json_schema(cls: Any, expected: dict[str, Any]) -> None: + """Test the model_json_schema implementation for all extra types.""" + class Model(BaseModel): x: cls From e3b57ec00394513d0811c181ba26d7d16a3e2140 Mon Sep 17 00:00:00 2001 From: Akbar Hasanov Date: Tue, 7 Apr 2026 03:44:36 +0200 Subject: [PATCH 24/31] build: add pandas optional dependency group --- pyproject.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 0a3a9daa..51f5374c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,6 +43,9 @@ dependencies = ['pydantic>=2.5.2','typing-extensions'] dynamic = ['version'] [project.optional-dependencies] +pandas = [ + 'pandas>=2.0.0', +] all = [ 'phonenumbers>=8,<10', 'pycountry>=23', @@ -57,6 +60,7 @@ all = [ 'tzdata>=2024.1', "cron-converter>=1.2.2", 'uuid-utils>=0.6.0; python_version<"3.14"', + 'pandas>=2.0.0', ] phonenumbers = ['phonenumbers>=8,<10'] pycountry = ['pycountry>=23'] From 50195717e68b2aabd2773b7c52d6746f263e8343 Mon Sep 17 00:00:00 2001 From: Akbar Hasanov Date: Tue, 7 Apr 2026 03:50:59 +0200 Subject: [PATCH 25/31] feat: add Series pydantic type with generic validation Implements Series[T] with pydantic-core no_info_wrap_validator_function, adds stubs for Index and DataFrame, and replaces test file with full test suite covering untyped/typed Series, Index, and DataFrame scenarios. --- pydantic_extra_types/pandas_types.py | 105 ++++++++++- tests/test_pandas_types.py | 256 +++++++++++++++++++-------- 2 files changed, 277 insertions(+), 84 deletions(-) diff --git a/pydantic_extra_types/pandas_types.py b/pydantic_extra_types/pandas_types.py index 914eed15..73144833 100644 --- a/pydantic_extra_types/pandas_types.py +++ b/pydantic_extra_types/pandas_types.py @@ -1,22 +1,109 @@ from __future__ import annotations -from typing import Any +try: + import pandas as pd +except ModuleNotFoundError: # pragma: no cover + raise RuntimeError( + 'The `pandas_types` module requires "pandas" to be installed. ' + "You can install it with \"pip install 'pydantic-extra-types[pandas]'\"." + ) + +from typing import Any, ClassVar -import pandas as pd from pydantic import GetCoreSchemaHandler -from pydantic_core import core_schema +from pydantic_core import PydanticCustomError, core_schema + + +class Series(pd.Series): # type: ignore[misc] + """ + A `pandas.Series` with Pydantic validation support. + + Supports both untyped and typed usage: + + ```python + from pydantic import BaseModel + from pydantic_extra_types.pandas_types import Series + + class MyModel(BaseModel): + values: Series[int] + + model = MyModel(values=[1, 2, 3]) + print(model.values.tolist()) # [1, 2, 3] + ``` + """ + _item_type: ClassVar[type | None] = None + + def __class_getitem__(cls, item: type) -> type: # type: ignore[override] + return type(f'Series[{item.__name__}]', (cls,), {'_item_type': item}) -class Series(pd.Series): # type: ignore @classmethod def __get_pydantic_core_schema__( cls, source: type[Any], handler: GetCoreSchemaHandler - ) -> core_schema.BeforeValidatorFunctionSchema: - return core_schema.general_before_validator_function( + ) -> core_schema.CoreSchema: + if cls._item_type is not None: + item_schema = handler.generate_schema(cls._item_type) + else: + item_schema = core_schema.any_schema() + + list_schema = core_schema.list_schema(item_schema) + + return core_schema.no_info_wrap_validator_function( cls._validate, - core_schema.any_schema(), + list_schema, + serialization=core_schema.plain_serializer_function_ser_schema( + lambda v: v.tolist(), + info_arg=False, + return_schema=core_schema.list_schema(item_schema), + ), ) @classmethod - def _validate(cls, __input_value: Any, _: core_schema.ValidationInfo) -> Series: - return cls(__input_value) + def _validate(cls, value: Any, handler: core_schema.ValidatorFunctionWrapHandler) -> Series: + if isinstance(value, pd.Series): + value = value.tolist() + elif not isinstance(value, list): + try: + value = list(value) + except Exception as exc: + raise PydanticCustomError( + 'series_invalid', + 'Value must be list-like or a pandas Series, got {type}', + {'type': type(value).__name__}, + ) from exc + validated: list[Any] = handler(value) + return Series(validated) + + +class Index: + """Stub — to be implemented (Tasks 5/6).""" + + @classmethod + def __class_getitem__(cls, item: type) -> type: + return type(f'Index[{item.__name__}]', (cls,), {'_item_type': item}) + + @classmethod + def __get_pydantic_core_schema__( + cls, source: type[Any], handler: GetCoreSchemaHandler + ) -> core_schema.CoreSchema: + def _not_implemented(v: Any) -> None: + raise NotImplementedError('Index validation not yet implemented') + + return core_schema.no_info_plain_validator_function(_not_implemented) + + +class DataFrame: + """Stub — to be implemented (Tasks 5/6).""" + + @classmethod + def __class_getitem__(cls, item: type) -> type: + return type(f'DataFrame[{item.__name__}]', (cls,), {'_schema_type': item}) + + @classmethod + def __get_pydantic_core_schema__( + cls, source: type[Any], handler: GetCoreSchemaHandler + ) -> core_schema.CoreSchema: + def _not_implemented(v: Any) -> None: + raise NotImplementedError('DataFrame validation not yet implemented') + + return core_schema.no_info_plain_validator_function(_not_implemented) diff --git a/tests/test_pandas_types.py b/tests/test_pandas_types.py index c11f7cac..862b2ee4 100644 --- a/tests/test_pandas_types.py +++ b/tests/test_pandas_types.py @@ -1,115 +1,221 @@ +from __future__ import annotations + +from typing import TypedDict + import pandas as pd import pytest -from pydantic import BaseModel +from pydantic import BaseModel, ValidationError + +from pydantic_extra_types.pandas_types import DataFrame, Index, Series + + +# --------------------------------------------------------------------------- +# Series +# --------------------------------------------------------------------------- -from pydantic_extra_types.pandas_types import Series +class UntypedSeriesModel(BaseModel): + data: Series -@pytest.fixture(scope='session', name='SeriesModel') -def series_model_fixture(): - class SeriesModel(BaseModel): - data: Series - return SeriesModel +class IntSeriesModel(BaseModel): + data: Series[int] + + +class StrSeriesModel(BaseModel): + data: Series[str] @pytest.mark.parametrize( - 'data, expected', + 'value,expected', [ ([1, 2, 3], [1, 2, 3]), ([], []), - ([10, 20, 30, 40], [10, 20, 30, 40]), + ((10, 20, 30), [10, 20, 30]), + (pd.Series([1, 2, 3]), [1, 2, 3]), + ([1, 'a', None], [1, 'a', None]), ], ) -def test_series_creation(data, expected): - if pd.__version__ <= '1.5.3' and data == []: - s = Series([1]) - expected = [1] - else: - s = Series(data) - assert isinstance(s, Series) - assert isinstance(s, pd.Series) - assert s.tolist() == expected +def test_series_untyped_accepts_any(value, expected): + model = UntypedSeriesModel(data=value) + assert isinstance(model.data, pd.Series) + assert model.data.tolist() == expected -def test_series_repr(): - data = [1, 2, 3] - s = Series(data) - assert repr(s) == repr(pd.Series(data)) +@pytest.mark.parametrize( + 'value,expected', + [ + ([1, 2, 3], [1, 2, 3]), + (pd.Series([4, 5, 6]), [4, 5, 6]), + ((7, 8, 9), [7, 8, 9]), + ], +) +def test_series_typed_int_valid(value, expected): + model = IntSeriesModel(data=value) + assert isinstance(model.data, pd.Series) + assert model.data.tolist() == expected -def test_series_attribute_access(): - data = [1, 2, 3] - s = Series(data) - assert s.sum() == pd.Series(data).sum() +def test_series_typed_int_rejects_strings(): + with pytest.raises(ValidationError): + IntSeriesModel(data=['a', 'b']) -def test_series_equality(): - data = [1, 2, 3] - s1 = Series(data) - s2 = Series(data) - assert s1.equals(other=s2) - assert s2.equals(pd.Series(data)) +def test_series_typed_str_valid(): + model = StrSeriesModel(data=['hello', 'world']) + assert model.data.tolist() == ['hello', 'world'] -def test_series_addition(): - data1 = [1, 2, 3] - data2 = [4, 5, 6] - s1 = Series(data1) - s2 = Series(data2) - s3 = s1 + s2 - assert isinstance(s3, pd.Series) - assert s3.tolist() == [5, 7, 9] +def test_series_typed_str_rejects_ints(): + with pytest.raises(ValidationError): + StrSeriesModel(data=[1, 2, 3]) -@pytest.mark.parametrize( - 'data, other, expected', - [ - ([1, 2, 3], [4, 5, 6], [5, 7, 9]), - ([10, 20, 30], (1, 2, 3), [11, 22, 33]), - ([5, 10, 15], pd.Series([1, 2, 3]), [6, 12, 18]), - ], -) -def test_series_addition_with_types(data, other, expected): - s = Series(data) - result = s + other - assert isinstance(result, pd.Series) - assert result.tolist() == expected +def test_series_invalid_input_type(): + with pytest.raises(ValidationError): + IntSeriesModel(data=42) + + +def test_series_is_pd_series(): + model = IntSeriesModel(data=[1, 2, 3]) + assert isinstance(model.data, pd.Series) + + +# --------------------------------------------------------------------------- +# Index +# --------------------------------------------------------------------------- + + +class UntypedIndexModel(BaseModel): + idx: Index + + +class IntIndexModel(BaseModel): + idx: Index[int] + + +class StrIndexModel(BaseModel): + idx: Index[str] @pytest.mark.parametrize( - 'data, other', + 'value,expected', [ - ([1, 2, 3], 'invalid'), # Invalid type for addition - ([1, 2, 3], {'a': 1, 'b': 2}), # Invalid type for addition + ([1, 2, 3], [1, 2, 3]), + (['a', 'b', 'c'], ['a', 'b', 'c']), + (pd.Index([10, 20]), [10, 20]), ], ) -def test_series_addition_invalid_type_error(data, other) -> None: - s = Series(data) - with pytest.raises(TypeError): - s + other +def test_index_untyped_accepts_any(value, expected): + model = UntypedIndexModel(idx=value) + assert isinstance(model.idx, pd.Index) + assert model.idx.tolist() == expected @pytest.mark.parametrize( - 'data, other', + 'value,expected', [ - ([1, 2, 3], []), + ([1, 2, 3], [1, 2, 3]), + (pd.Index([4, 5, 6]), [4, 5, 6]), ], ) -def test_series_addition_invalid_value_error(data, other) -> None: - s = Series(data) - with pytest.raises(ValueError): - s + other +def test_index_typed_int_valid(value, expected): + model = IntIndexModel(idx=value) + assert isinstance(model.idx, pd.Index) + assert model.idx.tolist() == expected -def test_valid_series_model(SeriesModel) -> None: - model = SeriesModel(data=[1, 2, 4]) - assert isinstance(model.data, pd.Series) - assert model.data.equals(pd.Series([1, 2, 4])) +def test_index_typed_int_rejects_strings(): + with pytest.raises(ValidationError): + IntIndexModel(idx=['a', 'b']) -def test_valid_series_model_with_pd_series(SeriesModel) -> None: - s = pd.Series([1, 2, 4]) - model = SeriesModel(data=s) - assert isinstance(model.data, pd.Series) - assert model.data.equals(s) +def test_index_typed_str_valid(): + model = StrIndexModel(idx=['x', 'y', 'z']) + assert model.idx.tolist() == ['x', 'y', 'z'] + + +def test_index_typed_str_rejects_ints(): + with pytest.raises(ValidationError): + StrIndexModel(idx=[1, 2, 3]) + + +def test_index_invalid_input_type(): + with pytest.raises(ValidationError): + IntIndexModel(idx=99) + + +def test_index_is_pd_index(): + model = IntIndexModel(idx=[1, 2]) + assert isinstance(model.idx, pd.Index) + + +# --------------------------------------------------------------------------- +# DataFrame +# --------------------------------------------------------------------------- + + +class MySchema(TypedDict): + col_a: int + col_b: str + + +class UntypedDataFrameModel(BaseModel): + df: DataFrame + + +class TypedDataFrameModel(BaseModel): + df: DataFrame[MySchema] + + +def test_dataframe_untyped_from_dict(): + model = UntypedDataFrameModel(df={'x': [1, 2], 'y': [3, 4]}) + assert isinstance(model.df, pd.DataFrame) + assert list(model.df.columns) == ['x', 'y'] + + +def test_dataframe_untyped_from_pd_dataframe(): + df = pd.DataFrame({'a': [1], 'b': [2]}) + model = UntypedDataFrameModel(df=df) + assert isinstance(model.df, pd.DataFrame) + + +def test_dataframe_typed_from_dict(): + model = TypedDataFrameModel(df={'col_a': [1, 2], 'col_b': ['x', 'y']}) + assert isinstance(model.df, pd.DataFrame) + assert model.df['col_a'].tolist() == [1, 2] + assert model.df['col_b'].tolist() == ['x', 'y'] + + +def test_dataframe_typed_from_pd_dataframe(): + df = pd.DataFrame({'col_a': [10, 20], 'col_b': ['a', 'b']}) + model = TypedDataFrameModel(df=df) + assert isinstance(model.df, pd.DataFrame) + assert model.df['col_a'].tolist() == [10, 20] + + +def test_dataframe_typed_preserves_extra_columns(): + df = pd.DataFrame({'col_a': [1], 'col_b': ['x'], 'extra': [99]}) + model = TypedDataFrameModel(df=df) + assert 'extra' in model.df.columns + assert model.df['extra'].tolist() == [99] + + +def test_dataframe_typed_missing_column_raises(): + with pytest.raises(ValidationError): + TypedDataFrameModel(df={'col_a': [1, 2]}) # col_b missing + + +def test_dataframe_typed_wrong_element_type_raises(): + with pytest.raises(ValidationError): + TypedDataFrameModel(df={'col_a': ['not_int', 'not_int'], 'col_b': ['x', 'y']}) + + +def test_dataframe_invalid_input_type(): + with pytest.raises(ValidationError): + TypedDataFrameModel(df='not a dataframe') + + +def test_dataframe_is_pd_dataframe(): + model = TypedDataFrameModel(df={'col_a': [1], 'col_b': ['a']}) + assert isinstance(model.df, pd.DataFrame) From 5c4de4bac6811b3dfa307a5db9a90429e6a80af4 Mon Sep 17 00:00:00 2001 From: Akbar Hasanov Date: Tue, 7 Apr 2026 03:56:14 +0200 Subject: [PATCH 26/31] feat: add Index pydantic type with generic validation --- pydantic_extra_types/pandas_types.py | 54 +++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/pydantic_extra_types/pandas_types.py b/pydantic_extra_types/pandas_types.py index 73144833..c05ed92a 100644 --- a/pydantic_extra_types/pandas_types.py +++ b/pydantic_extra_types/pandas_types.py @@ -76,9 +76,25 @@ def _validate(cls, value: Any, handler: core_schema.ValidatorFunctionWrapHandler class Index: - """Stub — to be implemented (Tasks 5/6).""" + """ + A `pandas.Index` with Pydantic validation support. + + Supports both untyped and typed usage: + + ```python + from pydantic import BaseModel + from pydantic_extra_types.pandas_types import Index + + class MyModel(BaseModel): + idx: Index[str] + + model = MyModel(idx=['a', 'b', 'c']) + print(model.idx.tolist()) # ['a', 'b', 'c'] + ``` + """ + + _item_type: ClassVar[type | None] = None - @classmethod def __class_getitem__(cls, item: type) -> type: return type(f'Index[{item.__name__}]', (cls,), {'_item_type': item}) @@ -86,10 +102,38 @@ def __class_getitem__(cls, item: type) -> type: def __get_pydantic_core_schema__( cls, source: type[Any], handler: GetCoreSchemaHandler ) -> core_schema.CoreSchema: - def _not_implemented(v: Any) -> None: - raise NotImplementedError('Index validation not yet implemented') + if cls._item_type is not None: + item_schema = handler.generate_schema(cls._item_type) + else: + item_schema = core_schema.any_schema() - return core_schema.no_info_plain_validator_function(_not_implemented) + list_schema = core_schema.list_schema(item_schema) + + return core_schema.no_info_wrap_validator_function( + cls._validate, + list_schema, + serialization=core_schema.plain_serializer_function_ser_schema( + lambda v: v.tolist(), + info_arg=False, + return_schema=core_schema.list_schema(item_schema), + ), + ) + + @classmethod + def _validate(cls, value: Any, handler: core_schema.ValidatorFunctionWrapHandler) -> pd.Index: + if isinstance(value, pd.Index): + value = value.tolist() + elif not isinstance(value, list): + try: + value = list(value) + except Exception as exc: + raise PydanticCustomError( + 'index_invalid', + 'Value must be list-like or a pandas Index, got {type}', + {'type': type(value).__name__}, + ) from exc + validated: list[Any] = handler(value) + return pd.Index(validated) class DataFrame: From 94cfa2b07a35b88bb724dd5e6ad318713fe6760f Mon Sep 17 00:00:00 2001 From: Akbar Hasanov Date: Tue, 7 Apr 2026 03:58:33 +0200 Subject: [PATCH 27/31] feat: add DataFrame pydantic type with TypedDict column validation --- pydantic_extra_types/pandas_types.py | 77 +++++++++++++++++++++++++--- 1 file changed, 70 insertions(+), 7 deletions(-) diff --git a/pydantic_extra_types/pandas_types.py b/pydantic_extra_types/pandas_types.py index c05ed92a..82b8c1e5 100644 --- a/pydantic_extra_types/pandas_types.py +++ b/pydantic_extra_types/pandas_types.py @@ -137,17 +137,80 @@ def _validate(cls, value: Any, handler: core_schema.ValidatorFunctionWrapHandler class DataFrame: - """Stub — to be implemented (Tasks 5/6).""" + """ + A `pandas.DataFrame` with Pydantic validation support. - @classmethod - def __class_getitem__(cls, item: type) -> type: - return type(f'DataFrame[{item.__name__}]', (cls,), {'_schema_type': item}) + Accepts a TypedDict (or any class with ``__annotations__``) as a type parameter + to validate column names and element types: + + ```python + from typing import TypedDict + from pydantic import BaseModel + from pydantic_extra_types.pandas_types import DataFrame + + class MySchema(TypedDict): + name: str + age: int + + class MyModel(BaseModel): + people: DataFrame[MySchema] + + model = MyModel(people={'name': ['Alice', 'Bob'], 'age': [30, 25]}) + print(model.people) + ``` + """ + + _schema_cls: ClassVar[type | None] = None + + def __class_getitem__(cls, schema_cls: type) -> type: + return type(f'DataFrame[{schema_cls.__name__}]', (cls,), {'_schema_cls': schema_cls}) @classmethod def __get_pydantic_core_schema__( cls, source: type[Any], handler: GetCoreSchemaHandler ) -> core_schema.CoreSchema: - def _not_implemented(v: Any) -> None: - raise NotImplementedError('DataFrame validation not yet implemented') + if cls._schema_cls is not None: + annotations = cls._schema_cls.__annotations__ + fields = { + col: core_schema.typed_dict_field( + core_schema.list_schema(handler.generate_schema(col_type)) + ) + for col, col_type in annotations.items() + } + inner_schema: core_schema.CoreSchema = core_schema.typed_dict_schema(fields) + else: + inner_schema = core_schema.any_schema() - return core_schema.no_info_plain_validator_function(_not_implemented) + return core_schema.no_info_wrap_validator_function( + cls._validate, + inner_schema, + serialization=core_schema.plain_serializer_function_ser_schema( + lambda v: {col: v[col].tolist() for col in v.columns}, + info_arg=False, + ), + ) + + @classmethod + def _validate(cls, value: Any, handler: core_schema.ValidatorFunctionWrapHandler) -> pd.DataFrame: + extra_data: dict[str, Any] = {} + + if isinstance(value, pd.DataFrame): + if cls._schema_cls is not None: + known_cols = set(cls._schema_cls.__annotations__.keys()) + extra_cols = [c for c in value.columns if c not in known_cols] + extra_data = {c: value[c].tolist() for c in extra_cols} + value = {c: value[c].tolist() for c in value.columns if c in known_cols} + else: + value = {c: value[c].tolist() for c in value.columns} + elif not isinstance(value, dict): + raise PydanticCustomError( + 'dataframe_invalid', + 'Value must be a dict or pandas DataFrame, got {type}', + {'type': type(value).__name__}, + ) + + validated = handler(value) + result = pd.DataFrame(validated) + for col, data in extra_data.items(): + result[col] = data + return result From 5aadbe21825cdb9fdd1ad004132505cf8947bb15 Mon Sep 17 00:00:00 2001 From: Akbar Hasanov Date: Tue, 7 Apr 2026 04:00:05 +0200 Subject: [PATCH 28/31] docs: update pandas_types docs for Series, Index, DataFrame --- docs/pandas_types.md | 98 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 84 insertions(+), 14 deletions(-) diff --git a/docs/pandas_types.md b/docs/pandas_types.md index 4e6c9df6..430202e4 100644 --- a/docs/pandas_types.md +++ b/docs/pandas_types.md @@ -1,25 +1,95 @@ +# Pandas Types -The `Series` class provides support for working with pandas Series objects. +Pydantic types for [pandas](https://pandas.pydata.org/) objects. Supports `Series`, `Index`, and `DataFrame` with optional generic type validation. -```py +## Installation + +```bash +pip install "pydantic-extra-types[pandas]" +``` + +## Series + +A validated `pandas.Series`. Use `Series[T]` to validate that every element is of type `T`. + +```python +from pydantic import BaseModel +from pydantic_extra_types.pandas_types import Series + +class MyModel(BaseModel): + values: Series[int] + +model = MyModel(values=[1, 2, 3]) +print(model.values.tolist()) # [1, 2, 3] + +# Also accepts an existing pd.Series import pandas as pd +model = MyModel(values=pd.Series([4, 5, 6])) +print(model.values.tolist()) # [4, 5, 6] +``` + +Use bare `Series` (no type parameter) to accept elements of any type: + +```python +class AnyModel(BaseModel): + values: Series + +model = AnyModel(values=[1, 'two', None]) +``` + +## Index + +A validated `pandas.Index`. Use `Index[T]` to validate element types. + +```python from pydantic import BaseModel +from pydantic_extra_types.pandas_types import Index -from pydantic_extra_types.pandas_types import Series +class MyModel(BaseModel): + idx: Index[str] + +model = MyModel(idx=['a', 'b', 'c']) +print(model.idx.tolist()) # ['a', 'b', 'c'] +``` + +## DataFrame + +A validated `pandas.DataFrame`. Pass a `TypedDict` (or any class with `__annotations__`) as the type parameter to validate column names and element types. + +```python +from typing import TypedDict +from pydantic import BaseModel +from pydantic_extra_types.pandas_types import DataFrame + +class PeopleSchema(TypedDict): + name: str + age: int + +class MyModel(BaseModel): + people: DataFrame[PeopleSchema] + +model = MyModel(people={'name': ['Alice', 'Bob'], 'age': [30, 25]}) +print(model.people) +# name age +# 0 Alice 30 +# 1 Bob 25 +``` + +Extra columns beyond those defined in the schema are preserved without validation: +```python +import pandas as pd -class MyData(BaseModel): - numbers: Series +df = pd.DataFrame({'name': ['Alice'], 'age': [30], 'extra': ['kept']}) +model = MyModel(people=df) +print('extra' in model.people.columns) # True +``` +Use bare `DataFrame` (no type parameter) to accept any dict or `pd.DataFrame`: -data = {"numbers": pd.Series([1, 2, 3, 4, 5])} -my_data = MyData(**data) +```python +class AnyModel(BaseModel): + df: DataFrame -print(my_data.numbers) -# > 0 1 -# > 1 2 -# > 2 3 -# > 3 4 -# > 4 5 -# > dtype: int64 +model = AnyModel(df={'x': [1, 2], 'y': [3, 4]}) ``` From 625e8b32dd06546f1a03755c98a3ca4816d5518a Mon Sep 17 00:00:00 2001 From: Akbar Hasanov Date: Tue, 7 Apr 2026 04:01:15 +0200 Subject: [PATCH 29/31] style: apply ruff format to pandas_types --- pydantic_extra_types/pandas_types.py | 25 ++++++++++++------------- tests/test_pandas_types.py | 1 - 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/pydantic_extra_types/pandas_types.py b/pydantic_extra_types/pandas_types.py index 82b8c1e5..4ac88305 100644 --- a/pydantic_extra_types/pandas_types.py +++ b/pydantic_extra_types/pandas_types.py @@ -5,7 +5,7 @@ except ModuleNotFoundError: # pragma: no cover raise RuntimeError( 'The `pandas_types` module requires "pandas" to be installed. ' - "You can install it with \"pip install 'pydantic-extra-types[pandas]'\"." + 'You can install it with "pip install \'pydantic-extra-types[pandas]\'".' ) from typing import Any, ClassVar @@ -24,9 +24,11 @@ class Series(pd.Series): # type: ignore[misc] from pydantic import BaseModel from pydantic_extra_types.pandas_types import Series + class MyModel(BaseModel): values: Series[int] + model = MyModel(values=[1, 2, 3]) print(model.values.tolist()) # [1, 2, 3] ``` @@ -38,9 +40,7 @@ def __class_getitem__(cls, item: type) -> type: # type: ignore[override] return type(f'Series[{item.__name__}]', (cls,), {'_item_type': item}) @classmethod - def __get_pydantic_core_schema__( - cls, source: type[Any], handler: GetCoreSchemaHandler - ) -> core_schema.CoreSchema: + def __get_pydantic_core_schema__(cls, source: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: if cls._item_type is not None: item_schema = handler.generate_schema(cls._item_type) else: @@ -85,9 +85,11 @@ class Index: from pydantic import BaseModel from pydantic_extra_types.pandas_types import Index + class MyModel(BaseModel): idx: Index[str] + model = MyModel(idx=['a', 'b', 'c']) print(model.idx.tolist()) # ['a', 'b', 'c'] ``` @@ -99,9 +101,7 @@ def __class_getitem__(cls, item: type) -> type: return type(f'Index[{item.__name__}]', (cls,), {'_item_type': item}) @classmethod - def __get_pydantic_core_schema__( - cls, source: type[Any], handler: GetCoreSchemaHandler - ) -> core_schema.CoreSchema: + def __get_pydantic_core_schema__(cls, source: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: if cls._item_type is not None: item_schema = handler.generate_schema(cls._item_type) else: @@ -148,13 +148,16 @@ class DataFrame: from pydantic import BaseModel from pydantic_extra_types.pandas_types import DataFrame + class MySchema(TypedDict): name: str age: int + class MyModel(BaseModel): people: DataFrame[MySchema] + model = MyModel(people={'name': ['Alice', 'Bob'], 'age': [30, 25]}) print(model.people) ``` @@ -166,15 +169,11 @@ def __class_getitem__(cls, schema_cls: type) -> type: return type(f'DataFrame[{schema_cls.__name__}]', (cls,), {'_schema_cls': schema_cls}) @classmethod - def __get_pydantic_core_schema__( - cls, source: type[Any], handler: GetCoreSchemaHandler - ) -> core_schema.CoreSchema: + def __get_pydantic_core_schema__(cls, source: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: if cls._schema_cls is not None: annotations = cls._schema_cls.__annotations__ fields = { - col: core_schema.typed_dict_field( - core_schema.list_schema(handler.generate_schema(col_type)) - ) + col: core_schema.typed_dict_field(core_schema.list_schema(handler.generate_schema(col_type))) for col, col_type in annotations.items() } inner_schema: core_schema.CoreSchema = core_schema.typed_dict_schema(fields) diff --git a/tests/test_pandas_types.py b/tests/test_pandas_types.py index 862b2ee4..d7bc3ef5 100644 --- a/tests/test_pandas_types.py +++ b/tests/test_pandas_types.py @@ -8,7 +8,6 @@ from pydantic_extra_types.pandas_types import DataFrame, Index, Series - # --------------------------------------------------------------------------- # Series # --------------------------------------------------------------------------- From 1c3fd3978cb53442440275b1906bc52521bc2582 Mon Sep 17 00:00:00 2001 From: Akbar Hasanov Date: Tue, 7 Apr 2026 04:12:09 +0200 Subject: [PATCH 30/31] build: regenerate uv.lock with pandas dependency --- uv.lock | 440 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 428 insertions(+), 12 deletions(-) diff --git a/uv.lock b/uv.lock index 2e546a83..7d72ec08 100644 --- a/uv.lock +++ b/uv.lock @@ -2,7 +2,13 @@ version = 1 revision = 3 requires-python = ">=3.9" resolution-markers = [ - "python_full_version >= '3.10'", + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.10.*'", "python_full_version < '3.10'", ] @@ -157,7 +163,13 @@ name = "coverage" version = "7.13.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.10'", + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.10.*'", ] sdist = { url = "https://files.pythonhosted.org/packages/b6/45/2c665ca77ec32ad67e25c77daf1cee28ee4558f3bc571cdbaf88a00b9f23/coverage-7.13.0.tar.gz", hash = "sha256:a394aa27f2d7ff9bc04cf703817773a59ad6dfbd577032e690f961d2460ee936", size = 820905, upload-time = "2025-12-08T13:14:38.055Z" } wheels = [ @@ -297,7 +309,13 @@ name = "dnspython" version = "2.8.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.10'", + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.10.*'", ] sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" } wheels = [ @@ -309,7 +327,7 @@ name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -333,7 +351,13 @@ name = "iniconfig" version = "2.3.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.10'", + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.10.*'", ] sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } wheels = [ @@ -363,7 +387,13 @@ name = "jsonschema" version = "4.26.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.10'", + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.10.*'", ] dependencies = [ { name = "attrs", marker = "python_full_version >= '3.10'" }, @@ -492,7 +522,13 @@ name = "markdown-it-py" version = "4.0.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.10'", + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.10.*'", ] dependencies = [ { name = "mdurl", marker = "python_full_version >= '3.10'" }, @@ -572,6 +608,213 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, ] +[[package]] +name = "numpy" +version = "2.0.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/a9/75/10dd1f8116a8b796cb2c737b674e02d02e80454bda953fa7e65d8c12b016/numpy-2.0.2.tar.gz", hash = "sha256:883c987dee1880e2a864ab0dc9892292582510604156762362d9326444636e78", size = 18902015, upload-time = "2024-08-26T20:19:40.945Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/21/91/3495b3237510f79f5d81f2508f9f13fea78ebfdf07538fc7444badda173d/numpy-2.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:51129a29dbe56f9ca83438b706e2e69a39892b5eda6cedcb6b0c9fdc9b0d3ece", size = 21165245, upload-time = "2024-08-26T20:04:14.625Z" }, + { url = "https://files.pythonhosted.org/packages/05/33/26178c7d437a87082d11019292dce6d3fe6f0e9026b7b2309cbf3e489b1d/numpy-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f15975dfec0cf2239224d80e32c3170b1d168335eaedee69da84fbe9f1f9cd04", size = 13738540, upload-time = "2024-08-26T20:04:36.784Z" }, + { url = "https://files.pythonhosted.org/packages/ec/31/cc46e13bf07644efc7a4bf68df2df5fb2a1a88d0cd0da9ddc84dc0033e51/numpy-2.0.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:8c5713284ce4e282544c68d1c3b2c7161d38c256d2eefc93c1d683cf47683e66", size = 5300623, upload-time = "2024-08-26T20:04:46.491Z" }, + { url = "https://files.pythonhosted.org/packages/6e/16/7bfcebf27bb4f9d7ec67332ffebee4d1bf085c84246552d52dbb548600e7/numpy-2.0.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:becfae3ddd30736fe1889a37f1f580e245ba79a5855bff5f2a29cb3ccc22dd7b", size = 6901774, upload-time = "2024-08-26T20:04:58.173Z" }, + { url = "https://files.pythonhosted.org/packages/f9/a3/561c531c0e8bf082c5bef509d00d56f82e0ea7e1e3e3a7fc8fa78742a6e5/numpy-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2da5960c3cf0df7eafefd806d4e612c5e19358de82cb3c343631188991566ccd", size = 13907081, upload-time = "2024-08-26T20:05:19.098Z" }, + { url = "https://files.pythonhosted.org/packages/fa/66/f7177ab331876200ac7563a580140643d1179c8b4b6a6b0fc9838de2a9b8/numpy-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:496f71341824ed9f3d2fd36cf3ac57ae2e0165c143b55c3a035ee219413f3318", size = 19523451, upload-time = "2024-08-26T20:05:47.479Z" }, + { url = "https://files.pythonhosted.org/packages/25/7f/0b209498009ad6453e4efc2c65bcdf0ae08a182b2b7877d7ab38a92dc542/numpy-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a61ec659f68ae254e4d237816e33171497e978140353c0c2038d46e63282d0c8", size = 19927572, upload-time = "2024-08-26T20:06:17.137Z" }, + { url = "https://files.pythonhosted.org/packages/3e/df/2619393b1e1b565cd2d4c4403bdd979621e2c4dea1f8532754b2598ed63b/numpy-2.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d731a1c6116ba289c1e9ee714b08a8ff882944d4ad631fd411106a30f083c326", size = 14400722, upload-time = "2024-08-26T20:06:39.16Z" }, + { url = "https://files.pythonhosted.org/packages/22/ad/77e921b9f256d5da36424ffb711ae79ca3f451ff8489eeca544d0701d74a/numpy-2.0.2-cp310-cp310-win32.whl", hash = "sha256:984d96121c9f9616cd33fbd0618b7f08e0cfc9600a7ee1d6fd9b239186d19d97", size = 6472170, upload-time = "2024-08-26T20:06:50.361Z" }, + { url = "https://files.pythonhosted.org/packages/10/05/3442317535028bc29cf0c0dd4c191a4481e8376e9f0db6bcf29703cadae6/numpy-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:c7b0be4ef08607dd04da4092faee0b86607f111d5ae68036f16cc787e250a131", size = 15905558, upload-time = "2024-08-26T20:07:13.881Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cf/034500fb83041aa0286e0fb16e7c76e5c8b67c0711bb6e9e9737a717d5fe/numpy-2.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:49ca4decb342d66018b01932139c0961a8f9ddc7589611158cb3c27cbcf76448", size = 21169137, upload-time = "2024-08-26T20:07:45.345Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d9/32de45561811a4b87fbdee23b5797394e3d1504b4a7cf40c10199848893e/numpy-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:11a76c372d1d37437857280aa142086476136a8c0f373b2e648ab2c8f18fb195", size = 13703552, upload-time = "2024-08-26T20:08:06.666Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ca/2f384720020c7b244d22508cb7ab23d95f179fcfff33c31a6eeba8d6c512/numpy-2.0.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:807ec44583fd708a21d4a11d94aedf2f4f3c3719035c76a2bbe1fe8e217bdc57", size = 5298957, upload-time = "2024-08-26T20:08:15.83Z" }, + { url = "https://files.pythonhosted.org/packages/0e/78/a3e4f9fb6aa4e6fdca0c5428e8ba039408514388cf62d89651aade838269/numpy-2.0.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8cafab480740e22f8d833acefed5cc87ce276f4ece12fdaa2e8903db2f82897a", size = 6905573, upload-time = "2024-08-26T20:08:27.185Z" }, + { url = "https://files.pythonhosted.org/packages/a0/72/cfc3a1beb2caf4efc9d0b38a15fe34025230da27e1c08cc2eb9bfb1c7231/numpy-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a15f476a45e6e5a3a79d8a14e62161d27ad897381fecfa4a09ed5322f2085669", size = 13914330, upload-time = "2024-08-26T20:08:48.058Z" }, + { url = "https://files.pythonhosted.org/packages/ba/a8/c17acf65a931ce551fee11b72e8de63bf7e8a6f0e21add4c937c83563538/numpy-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13e689d772146140a252c3a28501da66dfecd77490b498b168b501835041f951", size = 19534895, upload-time = "2024-08-26T20:09:16.536Z" }, + { url = "https://files.pythonhosted.org/packages/ba/86/8767f3d54f6ae0165749f84648da9dcc8cd78ab65d415494962c86fac80f/numpy-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9ea91dfb7c3d1c56a0e55657c0afb38cf1eeae4544c208dc465c3c9f3a7c09f9", size = 19937253, upload-time = "2024-08-26T20:09:46.263Z" }, + { url = "https://files.pythonhosted.org/packages/df/87/f76450e6e1c14e5bb1eae6836478b1028e096fd02e85c1c37674606ab752/numpy-2.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c1c9307701fec8f3f7a1e6711f9089c06e6284b3afbbcd259f7791282d660a15", size = 14414074, upload-time = "2024-08-26T20:10:08.483Z" }, + { url = "https://files.pythonhosted.org/packages/5c/ca/0f0f328e1e59f73754f06e1adfb909de43726d4f24c6a3f8805f34f2b0fa/numpy-2.0.2-cp311-cp311-win32.whl", hash = "sha256:a392a68bd329eafac5817e5aefeb39038c48b671afd242710b451e76090e81f4", size = 6470640, upload-time = "2024-08-26T20:10:19.732Z" }, + { url = "https://files.pythonhosted.org/packages/eb/57/3a3f14d3a759dcf9bf6e9eda905794726b758819df4663f217d658a58695/numpy-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:286cd40ce2b7d652a6f22efdfc6d1edf879440e53e76a75955bc0c826c7e64dc", size = 15910230, upload-time = "2024-08-26T20:10:43.413Z" }, + { url = "https://files.pythonhosted.org/packages/45/40/2e117be60ec50d98fa08c2f8c48e09b3edea93cfcabd5a9ff6925d54b1c2/numpy-2.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:df55d490dea7934f330006d0f81e8551ba6010a5bf035a249ef61a94f21c500b", size = 20895803, upload-time = "2024-08-26T20:11:13.916Z" }, + { url = "https://files.pythonhosted.org/packages/46/92/1b8b8dee833f53cef3e0a3f69b2374467789e0bb7399689582314df02651/numpy-2.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8df823f570d9adf0978347d1f926b2a867d5608f434a7cff7f7908c6570dcf5e", size = 13471835, upload-time = "2024-08-26T20:11:34.779Z" }, + { url = "https://files.pythonhosted.org/packages/7f/19/e2793bde475f1edaea6945be141aef6c8b4c669b90c90a300a8954d08f0a/numpy-2.0.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9a92ae5c14811e390f3767053ff54eaee3bf84576d99a2456391401323f4ec2c", size = 5038499, upload-time = "2024-08-26T20:11:43.902Z" }, + { url = "https://files.pythonhosted.org/packages/e3/ff/ddf6dac2ff0dd50a7327bcdba45cb0264d0e96bb44d33324853f781a8f3c/numpy-2.0.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:a842d573724391493a97a62ebbb8e731f8a5dcc5d285dfc99141ca15a3302d0c", size = 6633497, upload-time = "2024-08-26T20:11:55.09Z" }, + { url = "https://files.pythonhosted.org/packages/72/21/67f36eac8e2d2cd652a2e69595a54128297cdcb1ff3931cfc87838874bd4/numpy-2.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05e238064fc0610c840d1cf6a13bf63d7e391717d247f1bf0318172e759e692", size = 13621158, upload-time = "2024-08-26T20:12:14.95Z" }, + { url = "https://files.pythonhosted.org/packages/39/68/e9f1126d757653496dbc096cb429014347a36b228f5a991dae2c6b6cfd40/numpy-2.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0123ffdaa88fa4ab64835dcbde75dcdf89c453c922f18dced6e27c90d1d0ec5a", size = 19236173, upload-time = "2024-08-26T20:12:44.049Z" }, + { url = "https://files.pythonhosted.org/packages/d1/e9/1f5333281e4ebf483ba1c888b1d61ba7e78d7e910fdd8e6499667041cc35/numpy-2.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:96a55f64139912d61de9137f11bf39a55ec8faec288c75a54f93dfd39f7eb40c", size = 19634174, upload-time = "2024-08-26T20:13:13.634Z" }, + { url = "https://files.pythonhosted.org/packages/71/af/a469674070c8d8408384e3012e064299f7a2de540738a8e414dcfd639996/numpy-2.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec9852fb39354b5a45a80bdab5ac02dd02b15f44b3804e9f00c556bf24b4bded", size = 14099701, upload-time = "2024-08-26T20:13:34.851Z" }, + { url = "https://files.pythonhosted.org/packages/d0/3d/08ea9f239d0e0e939b6ca52ad403c84a2bce1bde301a8eb4888c1c1543f1/numpy-2.0.2-cp312-cp312-win32.whl", hash = "sha256:671bec6496f83202ed2d3c8fdc486a8fc86942f2e69ff0e986140339a63bcbe5", size = 6174313, upload-time = "2024-08-26T20:13:45.653Z" }, + { url = "https://files.pythonhosted.org/packages/b2/b5/4ac39baebf1fdb2e72585c8352c56d063b6126be9fc95bd2bb5ef5770c20/numpy-2.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:cfd41e13fdc257aa5778496b8caa5e856dc4896d4ccf01841daee1d96465467a", size = 15606179, upload-time = "2024-08-26T20:14:08.786Z" }, + { url = "https://files.pythonhosted.org/packages/43/c1/41c8f6df3162b0c6ffd4437d729115704bd43363de0090c7f913cfbc2d89/numpy-2.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9059e10581ce4093f735ed23f3b9d283b9d517ff46009ddd485f1747eb22653c", size = 21169942, upload-time = "2024-08-26T20:14:40.108Z" }, + { url = "https://files.pythonhosted.org/packages/39/bc/fd298f308dcd232b56a4031fd6ddf11c43f9917fbc937e53762f7b5a3bb1/numpy-2.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:423e89b23490805d2a5a96fe40ec507407b8ee786d66f7328be214f9679df6dd", size = 13711512, upload-time = "2024-08-26T20:15:00.985Z" }, + { url = "https://files.pythonhosted.org/packages/96/ff/06d1aa3eeb1c614eda245c1ba4fb88c483bee6520d361641331872ac4b82/numpy-2.0.2-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:2b2955fa6f11907cf7a70dab0d0755159bca87755e831e47932367fc8f2f2d0b", size = 5306976, upload-time = "2024-08-26T20:15:10.876Z" }, + { url = "https://files.pythonhosted.org/packages/2d/98/121996dcfb10a6087a05e54453e28e58694a7db62c5a5a29cee14c6e047b/numpy-2.0.2-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:97032a27bd9d8988b9a97a8c4d2c9f2c15a81f61e2f21404d7e8ef00cb5be729", size = 6906494, upload-time = "2024-08-26T20:15:22.055Z" }, + { url = "https://files.pythonhosted.org/packages/15/31/9dffc70da6b9bbf7968f6551967fc21156207366272c2a40b4ed6008dc9b/numpy-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e795a8be3ddbac43274f18588329c72939870a16cae810c2b73461c40718ab1", size = 13912596, upload-time = "2024-08-26T20:15:42.452Z" }, + { url = "https://files.pythonhosted.org/packages/b9/14/78635daab4b07c0930c919d451b8bf8c164774e6a3413aed04a6d95758ce/numpy-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b258c385842546006213344c50655ff1555a9338e2e5e02a0756dc3e803dd", size = 19526099, upload-time = "2024-08-26T20:16:11.048Z" }, + { url = "https://files.pythonhosted.org/packages/26/4c/0eeca4614003077f68bfe7aac8b7496f04221865b3a5e7cb230c9d055afd/numpy-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fec9451a7789926bcf7c2b8d187292c9f93ea30284802a0ab3f5be8ab36865d", size = 19932823, upload-time = "2024-08-26T20:16:40.171Z" }, + { url = "https://files.pythonhosted.org/packages/f1/46/ea25b98b13dccaebddf1a803f8c748680d972e00507cd9bc6dcdb5aa2ac1/numpy-2.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9189427407d88ff25ecf8f12469d4d39d35bee1db5d39fc5c168c6f088a6956d", size = 14404424, upload-time = "2024-08-26T20:17:02.604Z" }, + { url = "https://files.pythonhosted.org/packages/c8/a6/177dd88d95ecf07e722d21008b1b40e681a929eb9e329684d449c36586b2/numpy-2.0.2-cp39-cp39-win32.whl", hash = "sha256:905d16e0c60200656500c95b6b8dca5d109e23cb24abc701d41c02d74c6b3afa", size = 6476809, upload-time = "2024-08-26T20:17:13.553Z" }, + { url = "https://files.pythonhosted.org/packages/ea/2b/7fc9f4e7ae5b507c1a3a21f0f15ed03e794c1242ea8a242ac158beb56034/numpy-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:a3f4ab0caa7f053f6797fcd4e1e25caee367db3112ef2b6ef82d749530768c73", size = 15911314, upload-time = "2024-08-26T20:17:36.72Z" }, + { url = "https://files.pythonhosted.org/packages/8f/3b/df5a870ac6a3be3a86856ce195ef42eec7ae50d2a202be1f5a4b3b340e14/numpy-2.0.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7f0a0c6f12e07fa94133c8a67404322845220c06a9e80e85999afe727f7438b8", size = 21025288, upload-time = "2024-08-26T20:18:07.732Z" }, + { url = "https://files.pythonhosted.org/packages/2c/97/51af92f18d6f6f2d9ad8b482a99fb74e142d71372da5d834b3a2747a446e/numpy-2.0.2-pp39-pypy39_pp73-macosx_14_0_x86_64.whl", hash = "sha256:312950fdd060354350ed123c0e25a71327d3711584beaef30cdaa93320c392d4", size = 6762793, upload-time = "2024-08-26T20:18:19.125Z" }, + { url = "https://files.pythonhosted.org/packages/12/46/de1fbd0c1b5ccaa7f9a005b66761533e2f6a3e560096682683a223631fe9/numpy-2.0.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26df23238872200f63518dd2aa984cfca675d82469535dc7162dc2ee52d9dd5c", size = 19334885, upload-time = "2024-08-26T20:18:47.237Z" }, + { url = "https://files.pythonhosted.org/packages/cc/dc/d330a6faefd92b446ec0f0dfea4c3207bb1fef3c4771d19cf4543efd2c78/numpy-2.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a46288ec55ebbd58947d31d72be2c63cbf839f0a63b49cb755022310792a3385", size = 15828784, upload-time = "2024-08-26T20:19:11.19Z" }, +] + +[[package]] +name = "numpy" +version = "2.2.6" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.10.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/3e/ed6db5be21ce87955c0cbd3009f2803f59fa08df21b5df06862e2d8e2bdd/numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb", size = 21165245, upload-time = "2025-05-17T21:27:58.555Z" }, + { url = "https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90", size = 14360048, upload-time = "2025-05-17T21:28:21.406Z" }, + { url = "https://files.pythonhosted.org/packages/fd/77/dc2fcfc66943c6410e2bf598062f5959372735ffda175b39906d54f02349/numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163", size = 5340542, upload-time = "2025-05-17T21:28:30.931Z" }, + { url = "https://files.pythonhosted.org/packages/7a/4f/1cb5fdc353a5f5cc7feb692db9b8ec2c3d6405453f982435efc52561df58/numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf", size = 6878301, upload-time = "2025-05-17T21:28:41.613Z" }, + { url = "https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83", size = 14297320, upload-time = "2025-05-17T21:29:02.78Z" }, + { url = "https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915", size = 16801050, upload-time = "2025-05-17T21:29:27.675Z" }, + { url = "https://files.pythonhosted.org/packages/07/b6/89d837eddef52b3d0cec5c6ba0456c1bf1b9ef6a6672fc2b7873c3ec4e2e/numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680", size = 15807034, upload-time = "2025-05-17T21:29:51.102Z" }, + { url = "https://files.pythonhosted.org/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289", size = 18614185, upload-time = "2025-05-17T21:30:18.703Z" }, + { url = "https://files.pythonhosted.org/packages/5b/c5/0064b1b7e7c89137b471ccec1fd2282fceaae0ab3a9550f2568782d80357/numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d", size = 6527149, upload-time = "2025-05-17T21:30:29.788Z" }, + { url = "https://files.pythonhosted.org/packages/a3/dd/4b822569d6b96c39d1215dbae0582fd99954dcbcf0c1a13c61783feaca3f/numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3", size = 12904620, upload-time = "2025-05-17T21:30:48.994Z" }, + { url = "https://files.pythonhosted.org/packages/da/a8/4f83e2aa666a9fbf56d6118faaaf5f1974d456b1823fda0a176eff722839/numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae", size = 21176963, upload-time = "2025-05-17T21:31:19.36Z" }, + { url = "https://files.pythonhosted.org/packages/b3/2b/64e1affc7972decb74c9e29e5649fac940514910960ba25cd9af4488b66c/numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a", size = 14406743, upload-time = "2025-05-17T21:31:41.087Z" }, + { url = "https://files.pythonhosted.org/packages/4a/9f/0121e375000b5e50ffdd8b25bf78d8e1a5aa4cca3f185d41265198c7b834/numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42", size = 5352616, upload-time = "2025-05-17T21:31:50.072Z" }, + { url = "https://files.pythonhosted.org/packages/31/0d/b48c405c91693635fbe2dcd7bc84a33a602add5f63286e024d3b6741411c/numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491", size = 6889579, upload-time = "2025-05-17T21:32:01.712Z" }, + { url = "https://files.pythonhosted.org/packages/52/b8/7f0554d49b565d0171eab6e99001846882000883998e7b7d9f0d98b1f934/numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a", size = 14312005, upload-time = "2025-05-17T21:32:23.332Z" }, + { url = "https://files.pythonhosted.org/packages/b3/dd/2238b898e51bd6d389b7389ffb20d7f4c10066d80351187ec8e303a5a475/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf", size = 16821570, upload-time = "2025-05-17T21:32:47.991Z" }, + { url = "https://files.pythonhosted.org/packages/83/6c/44d0325722cf644f191042bf47eedad61c1e6df2432ed65cbe28509d404e/numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1", size = 15818548, upload-time = "2025-05-17T21:33:11.728Z" }, + { url = "https://files.pythonhosted.org/packages/ae/9d/81e8216030ce66be25279098789b665d49ff19eef08bfa8cb96d4957f422/numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab", size = 18620521, upload-time = "2025-05-17T21:33:39.139Z" }, + { url = "https://files.pythonhosted.org/packages/6a/fd/e19617b9530b031db51b0926eed5345ce8ddc669bb3bc0044b23e275ebe8/numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47", size = 6525866, upload-time = "2025-05-17T21:33:50.273Z" }, + { url = "https://files.pythonhosted.org/packages/31/0a/f354fb7176b81747d870f7991dc763e157a934c717b67b58456bc63da3df/numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303", size = 12907455, upload-time = "2025-05-17T21:34:09.135Z" }, + { url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348, upload-time = "2025-05-17T21:34:39.648Z" }, + { url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362, upload-time = "2025-05-17T21:35:01.241Z" }, + { url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103, upload-time = "2025-05-17T21:35:10.622Z" }, + { url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382, upload-time = "2025-05-17T21:35:21.414Z" }, + { url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462, upload-time = "2025-05-17T21:35:42.174Z" }, + { url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618, upload-time = "2025-05-17T21:36:06.711Z" }, + { url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511, upload-time = "2025-05-17T21:36:29.965Z" }, + { url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" }, + { url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506, upload-time = "2025-05-17T21:37:07.368Z" }, + { url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190, upload-time = "2025-05-17T21:37:26.213Z" }, + { url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828, upload-time = "2025-05-17T21:37:56.699Z" }, + { url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006, upload-time = "2025-05-17T21:38:18.291Z" }, + { url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765, upload-time = "2025-05-17T21:38:27.319Z" }, + { url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736, upload-time = "2025-05-17T21:38:38.141Z" }, + { url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719, upload-time = "2025-05-17T21:38:58.433Z" }, + { url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072, upload-time = "2025-05-17T21:39:22.638Z" }, + { url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213, upload-time = "2025-05-17T21:39:45.865Z" }, + { url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632, upload-time = "2025-05-17T21:40:13.331Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532, upload-time = "2025-05-17T21:43:46.099Z" }, + { url = "https://files.pythonhosted.org/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885, upload-time = "2025-05-17T21:44:05.145Z" }, + { url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467, upload-time = "2025-05-17T21:40:44Z" }, + { url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144, upload-time = "2025-05-17T21:41:05.695Z" }, + { url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217, upload-time = "2025-05-17T21:41:15.903Z" }, + { url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014, upload-time = "2025-05-17T21:41:27.321Z" }, + { url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935, upload-time = "2025-05-17T21:41:49.738Z" }, + { url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122, upload-time = "2025-05-17T21:42:14.046Z" }, + { url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143, upload-time = "2025-05-17T21:42:37.464Z" }, + { url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260, upload-time = "2025-05-17T21:43:05.189Z" }, + { url = "https://files.pythonhosted.org/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225, upload-time = "2025-05-17T21:43:16.254Z" }, + { url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374, upload-time = "2025-05-17T21:43:35.479Z" }, + { url = "https://files.pythonhosted.org/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391, upload-time = "2025-05-17T21:44:35.948Z" }, + { url = "https://files.pythonhosted.org/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754, upload-time = "2025-05-17T21:44:47.446Z" }, + { url = "https://files.pythonhosted.org/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476, upload-time = "2025-05-17T21:45:11.871Z" }, + { url = "https://files.pythonhosted.org/packages/37/48/ac2a9584402fb6c0cd5b5d1a91dcf176b15760130dd386bbafdbfe3640bf/numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00", size = 12812666, upload-time = "2025-05-17T21:45:31.426Z" }, +] + +[[package]] +name = "numpy" +version = "2.4.4" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +sdist = { url = "https://files.pythonhosted.org/packages/d7/9f/b8cef5bffa569759033adda9481211426f12f53299629b410340795c2514/numpy-2.4.4.tar.gz", hash = "sha256:2d390634c5182175533585cc89f3608a4682ccb173cc9bb940b2881c8d6f8fa0", size = 20731587, upload-time = "2026-03-29T13:22:01.298Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/c6/4218570d8c8ecc9704b5157a3348e486e84ef4be0ed3e38218ab473c83d2/numpy-2.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f983334aea213c99992053ede6168500e5f086ce74fbc4acc3f2b00f5762e9db", size = 16976799, upload-time = "2026-03-29T13:18:15.438Z" }, + { url = "https://files.pythonhosted.org/packages/dd/92/b4d922c4a5f5dab9ed44e6153908a5c665b71acf183a83b93b690996e39b/numpy-2.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:72944b19f2324114e9dc86a159787333b77874143efcf89a5167ef83cfee8af0", size = 14971552, upload-time = "2026-03-29T13:18:18.606Z" }, + { url = "https://files.pythonhosted.org/packages/8a/dc/df98c095978fa6ee7b9a9387d1d58cbb3d232d0e69ad169a4ce784bde4fd/numpy-2.4.4-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:86b6f55f5a352b48d7fbfd2dbc3d5b780b2d79f4d3c121f33eb6efb22e9a2015", size = 5476566, upload-time = "2026-03-29T13:18:21.532Z" }, + { url = "https://files.pythonhosted.org/packages/28/34/b3fdcec6e725409223dd27356bdf5a3c2cc2282e428218ecc9cb7acc9763/numpy-2.4.4-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:ba1f4fc670ed79f876f70082eff4f9583c15fb9a4b89d6188412de4d18ae2f40", size = 6806482, upload-time = "2026-03-29T13:18:23.634Z" }, + { url = "https://files.pythonhosted.org/packages/68/62/63417c13aa35d57bee1337c67446761dc25ea6543130cf868eace6e8157b/numpy-2.4.4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a87ec22c87be071b6bdbd27920b129b94f2fc964358ce38f3822635a3e2e03d", size = 15973376, upload-time = "2026-03-29T13:18:26.677Z" }, + { url = "https://files.pythonhosted.org/packages/cf/c5/9fcb7e0e69cef59cf10c746b84f7d58b08bc66a6b7d459783c5a4f6101a6/numpy-2.4.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:df3775294accfdd75f32c74ae39fcba920c9a378a2fc18a12b6820aa8c1fb502", size = 16925137, upload-time = "2026-03-29T13:18:30.14Z" }, + { url = "https://files.pythonhosted.org/packages/7e/43/80020edacb3f84b9efdd1591120a4296462c23fd8db0dde1666f6ef66f13/numpy-2.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0d4e437e295f18ec29bc79daf55e8a47a9113df44d66f702f02a293d93a2d6dd", size = 17329414, upload-time = "2026-03-29T13:18:33.733Z" }, + { url = "https://files.pythonhosted.org/packages/fd/06/af0658593b18a5f73532d377188b964f239eb0894e664a6c12f484472f97/numpy-2.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6aa3236c78803afbcb255045fbef97a9e25a1f6c9888357d205ddc42f4d6eba5", size = 18658397, upload-time = "2026-03-29T13:18:37.511Z" }, + { url = "https://files.pythonhosted.org/packages/e6/ce/13a09ed65f5d0ce5c7dd0669250374c6e379910f97af2c08c57b0608eee4/numpy-2.4.4-cp311-cp311-win32.whl", hash = "sha256:30caa73029a225b2d40d9fae193e008e24b2026b7ee1a867b7ee8d96ca1a448e", size = 6239499, upload-time = "2026-03-29T13:18:40.372Z" }, + { url = "https://files.pythonhosted.org/packages/bd/63/05d193dbb4b5eec1eca73822d80da98b511f8328ad4ae3ca4caf0f4db91d/numpy-2.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:6bbe4eb67390b0a0265a2c25458f6b90a409d5d069f1041e6aff1e27e3d9a79e", size = 12614257, upload-time = "2026-03-29T13:18:42.95Z" }, + { url = "https://files.pythonhosted.org/packages/87/c5/8168052f080c26fa984c413305012be54741c9d0d74abd7fbeeccae3889f/numpy-2.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:fcfe2045fd2e8f3cb0ce9d4ba6dba6333b8fa05bb8a4939c908cd43322d14c7e", size = 10486775, upload-time = "2026-03-29T13:18:45.835Z" }, + { url = "https://files.pythonhosted.org/packages/28/05/32396bec30fb2263770ee910142f49c1476d08e8ad41abf8403806b520ce/numpy-2.4.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:15716cfef24d3a9762e3acdf87e27f58dc823d1348f765bbea6bef8c639bfa1b", size = 16689272, upload-time = "2026-03-29T13:18:49.223Z" }, + { url = "https://files.pythonhosted.org/packages/c5/f3/a983d28637bfcd763a9c7aafdb6d5c0ebf3d487d1e1459ffdb57e2f01117/numpy-2.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23cbfd4c17357c81021f21540da84ee282b9c8fba38a03b7b9d09ba6b951421e", size = 14699573, upload-time = "2026-03-29T13:18:52.629Z" }, + { url = "https://files.pythonhosted.org/packages/9b/fd/e5ecca1e78c05106d98028114f5c00d3eddb41207686b2b7de3e477b0e22/numpy-2.4.4-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:8b3b60bb7cba2c8c81837661c488637eee696f59a877788a396d33150c35d842", size = 5204782, upload-time = "2026-03-29T13:18:55.579Z" }, + { url = "https://files.pythonhosted.org/packages/de/2f/702a4594413c1a8632092beae8aba00f1d67947389369b3777aed783fdca/numpy-2.4.4-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:e4a010c27ff6f210ff4c6ef34394cd61470d01014439b192ec22552ee867f2a8", size = 6552038, upload-time = "2026-03-29T13:18:57.769Z" }, + { url = "https://files.pythonhosted.org/packages/7f/37/eed308a8f56cba4d1fdf467a4fc67ef4ff4bf1c888f5fc980481890104b1/numpy-2.4.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f9e75681b59ddaa5e659898085ae0eaea229d054f2ac0c7e563a62205a700121", size = 15670666, upload-time = "2026-03-29T13:19:00.341Z" }, + { url = "https://files.pythonhosted.org/packages/0a/0d/0e3ecece05b7a7e87ab9fb587855548da437a061326fff64a223b6dcb78a/numpy-2.4.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:81f4a14bee47aec54f883e0cad2d73986640c1590eb9bfaaba7ad17394481e6e", size = 16645480, upload-time = "2026-03-29T13:19:03.63Z" }, + { url = "https://files.pythonhosted.org/packages/34/49/f2312c154b82a286758ee2f1743336d50651f8b5195db18cdb63675ff649/numpy-2.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:62d6b0f03b694173f9fcb1fb317f7222fd0b0b103e784c6549f5e53a27718c44", size = 17020036, upload-time = "2026-03-29T13:19:07.428Z" }, + { url = "https://files.pythonhosted.org/packages/7b/e9/736d17bd77f1b0ec4f9901aaec129c00d59f5d84d5e79bba540ef12c2330/numpy-2.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fbc356aae7adf9e6336d336b9c8111d390a05df88f1805573ebb0807bd06fd1d", size = 18368643, upload-time = "2026-03-29T13:19:10.775Z" }, + { url = "https://files.pythonhosted.org/packages/63/f6/d417977c5f519b17c8a5c3bc9e8304b0908b0e21136fe43bf628a1343914/numpy-2.4.4-cp312-cp312-win32.whl", hash = "sha256:0d35aea54ad1d420c812bfa0385c71cd7cc5bcf7c65fed95fc2cd02fe8c79827", size = 5961117, upload-time = "2026-03-29T13:19:13.464Z" }, + { url = "https://files.pythonhosted.org/packages/2d/5b/e1deebf88ff431b01b7406ca3583ab2bbb90972bbe1c568732e49c844f7e/numpy-2.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:b5f0362dc928a6ecd9db58868fca5e48485205e3855957bdedea308f8672ea4a", size = 12320584, upload-time = "2026-03-29T13:19:16.155Z" }, + { url = "https://files.pythonhosted.org/packages/58/89/e4e856ac82a68c3ed64486a544977d0e7bdd18b8da75b78a577ca31c4395/numpy-2.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:846300f379b5b12cc769334464656bc882e0735d27d9726568bc932fdc49d5ec", size = 10221450, upload-time = "2026-03-29T13:19:18.994Z" }, + { url = "https://files.pythonhosted.org/packages/14/1d/d0a583ce4fefcc3308806a749a536c201ed6b5ad6e1322e227ee4848979d/numpy-2.4.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:08f2e31ed5e6f04b118e49821397f12767934cfdd12a1ce86a058f91e004ee50", size = 16684933, upload-time = "2026-03-29T13:19:22.47Z" }, + { url = "https://files.pythonhosted.org/packages/c1/62/2b7a48fbb745d344742c0277f01286dead15f3f68e4f359fbfcf7b48f70f/numpy-2.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e823b8b6edc81e747526f70f71a9c0a07ac4e7ad13020aa736bb7c9d67196115", size = 14694532, upload-time = "2026-03-29T13:19:25.581Z" }, + { url = "https://files.pythonhosted.org/packages/e5/87/499737bfba066b4a3bebff24a8f1c5b2dee410b209bc6668c9be692580f0/numpy-2.4.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4a19d9dba1a76618dd86b164d608566f393f8ec6ac7c44f0cc879011c45e65af", size = 5199661, upload-time = "2026-03-29T13:19:28.31Z" }, + { url = "https://files.pythonhosted.org/packages/cd/da/464d551604320d1491bc345efed99b4b7034143a85787aab78d5691d5a0e/numpy-2.4.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:d2a8490669bfe99a233298348acc2d824d496dee0e66e31b66a6022c2ad74a5c", size = 6547539, upload-time = "2026-03-29T13:19:30.97Z" }, + { url = "https://files.pythonhosted.org/packages/7d/90/8d23e3b0dafd024bf31bdec225b3bb5c2dbfa6912f8a53b8659f21216cbf/numpy-2.4.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:45dbed2ab436a9e826e302fcdcbe9133f9b0006e5af7168afb8963a6520da103", size = 15668806, upload-time = "2026-03-29T13:19:33.887Z" }, + { url = "https://files.pythonhosted.org/packages/d1/73/a9d864e42a01896bb5974475438f16086be9ba1f0d19d0bb7a07427c4a8b/numpy-2.4.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c901b15172510173f5cb310eae652908340f8dede90fff9e3bf6c0d8dfd92f83", size = 16632682, upload-time = "2026-03-29T13:19:37.336Z" }, + { url = "https://files.pythonhosted.org/packages/34/fb/14570d65c3bde4e202a031210475ae9cde9b7686a2e7dc97ee67d2833b35/numpy-2.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:99d838547ace2c4aace6c4f76e879ddfe02bb58a80c1549928477862b7a6d6ed", size = 17019810, upload-time = "2026-03-29T13:19:40.963Z" }, + { url = "https://files.pythonhosted.org/packages/8a/77/2ba9d87081fd41f6d640c83f26fb7351e536b7ce6dd9061b6af5904e8e46/numpy-2.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0aec54fd785890ecca25a6003fd9a5aed47ad607bbac5cd64f836ad8666f4959", size = 18357394, upload-time = "2026-03-29T13:19:44.859Z" }, + { url = "https://files.pythonhosted.org/packages/a2/23/52666c9a41708b0853fa3b1a12c90da38c507a3074883823126d4e9d5b30/numpy-2.4.4-cp313-cp313-win32.whl", hash = "sha256:07077278157d02f65c43b1b26a3886bce886f95d20aabd11f87932750dfb14ed", size = 5959556, upload-time = "2026-03-29T13:19:47.661Z" }, + { url = "https://files.pythonhosted.org/packages/57/fb/48649b4971cde70d817cf97a2a2fdc0b4d8308569f1dd2f2611959d2e0cf/numpy-2.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:5c70f1cc1c4efbe316a572e2d8b9b9cc44e89b95f79ca3331553fbb63716e2bf", size = 12317311, upload-time = "2026-03-29T13:19:50.67Z" }, + { url = "https://files.pythonhosted.org/packages/ba/d8/11490cddd564eb4de97b4579ef6bfe6a736cc07e94c1598590ae25415e01/numpy-2.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:ef4059d6e5152fa1a39f888e344c73fdc926e1b2dd58c771d67b0acfbf2aa67d", size = 10222060, upload-time = "2026-03-29T13:19:54.229Z" }, + { url = "https://files.pythonhosted.org/packages/99/5d/dab4339177a905aad3e2221c915b35202f1ec30d750dd2e5e9d9a72b804b/numpy-2.4.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4bbc7f303d125971f60ec0aaad5e12c62d0d2c925f0ab1273debd0e4ba37aba5", size = 14822302, upload-time = "2026-03-29T13:19:57.585Z" }, + { url = "https://files.pythonhosted.org/packages/eb/e4/0564a65e7d3d97562ed6f9b0fd0fb0a6f559ee444092f105938b50043876/numpy-2.4.4-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:4d6d57903571f86180eb98f8f0c839fa9ebbfb031356d87f1361be91e433f5b7", size = 5327407, upload-time = "2026-03-29T13:20:00.601Z" }, + { url = "https://files.pythonhosted.org/packages/29/8d/35a3a6ce5ad371afa58b4700f1c820f8f279948cca32524e0a695b0ded83/numpy-2.4.4-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:4636de7fd195197b7535f231b5de9e4b36d2c440b6e566d2e4e4746e6af0ca93", size = 6647631, upload-time = "2026-03-29T13:20:02.855Z" }, + { url = "https://files.pythonhosted.org/packages/f4/da/477731acbd5a58a946c736edfdabb2ac5b34c3d08d1ba1a7b437fa0884df/numpy-2.4.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ad2e2ef14e0b04e544ea2fa0a36463f847f113d314aa02e5b402fdf910ef309e", size = 15727691, upload-time = "2026-03-29T13:20:06.004Z" }, + { url = "https://files.pythonhosted.org/packages/e6/db/338535d9b152beabeb511579598418ba0212ce77cf9718edd70262cc4370/numpy-2.4.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a285b3b96f951841799528cd1f4f01cd70e7e0204b4abebac9463eecfcf2a40", size = 16681241, upload-time = "2026-03-29T13:20:09.417Z" }, + { url = "https://files.pythonhosted.org/packages/e2/a9/ad248e8f58beb7a0219b413c9c7d8151c5d285f7f946c3e26695bdbbe2df/numpy-2.4.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f8474c4241bc18b750be2abea9d7a9ec84f46ef861dbacf86a4f6e043401f79e", size = 17085767, upload-time = "2026-03-29T13:20:13.126Z" }, + { url = "https://files.pythonhosted.org/packages/b5/1a/3b88ccd3694681356f70da841630e4725a7264d6a885c8d442a697e1146b/numpy-2.4.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4e874c976154687c1f71715b034739b45c7711bec81db01914770373d125e392", size = 18403169, upload-time = "2026-03-29T13:20:17.096Z" }, + { url = "https://files.pythonhosted.org/packages/c2/c9/fcfd5d0639222c6eac7f304829b04892ef51c96a75d479214d77e3ce6e33/numpy-2.4.4-cp313-cp313t-win32.whl", hash = "sha256:9c585a1790d5436a5374bac930dad6ed244c046ed91b2b2a3634eb2971d21008", size = 6083477, upload-time = "2026-03-29T13:20:20.195Z" }, + { url = "https://files.pythonhosted.org/packages/d5/e3/3938a61d1c538aaec8ed6fd6323f57b0c2d2d2219512434c5c878db76553/numpy-2.4.4-cp313-cp313t-win_amd64.whl", hash = "sha256:93e15038125dc1e5345d9b5b68aa7f996ec33b98118d18c6ca0d0b7d6198b7e8", size = 12457487, upload-time = "2026-03-29T13:20:22.946Z" }, + { url = "https://files.pythonhosted.org/packages/97/6a/7e345032cc60501721ef94e0e30b60f6b0bd601f9174ebd36389a2b86d40/numpy-2.4.4-cp313-cp313t-win_arm64.whl", hash = "sha256:0dfd3f9d3adbe2920b68b5cd3d51444e13a10792ec7154cd0a2f6e74d4ab3233", size = 10292002, upload-time = "2026-03-29T13:20:25.909Z" }, + { url = "https://files.pythonhosted.org/packages/6e/06/c54062f85f673dd5c04cbe2f14c3acb8c8b95e3384869bb8cc9bff8cb9df/numpy-2.4.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:f169b9a863d34f5d11b8698ead99febeaa17a13ca044961aa8e2662a6c7766a0", size = 16684353, upload-time = "2026-03-29T13:20:29.504Z" }, + { url = "https://files.pythonhosted.org/packages/4c/39/8a320264a84404c74cc7e79715de85d6130fa07a0898f67fb5cd5bd79908/numpy-2.4.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2483e4584a1cb3092da4470b38866634bafb223cbcd551ee047633fd2584599a", size = 14704914, upload-time = "2026-03-29T13:20:33.547Z" }, + { url = "https://files.pythonhosted.org/packages/91/fb/287076b2614e1d1044235f50f03748f31fa287e3dbe6abeb35cdfa351eca/numpy-2.4.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:2d19e6e2095506d1736b7d80595e0f252d76b89f5e715c35e06e937679ea7d7a", size = 5210005, upload-time = "2026-03-29T13:20:36.45Z" }, + { url = "https://files.pythonhosted.org/packages/63/eb/fcc338595309910de6ecabfcef2419a9ce24399680bfb149421fa2df1280/numpy-2.4.4-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:6a246d5914aa1c820c9443ddcee9c02bec3e203b0c080349533fae17727dfd1b", size = 6544974, upload-time = "2026-03-29T13:20:39.014Z" }, + { url = "https://files.pythonhosted.org/packages/44/5d/e7e9044032a716cdfaa3fba27a8e874bf1c5f1912a1ddd4ed071bf8a14a6/numpy-2.4.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:989824e9faf85f96ec9c7761cd8d29c531ad857bfa1daa930cba85baaecf1a9a", size = 15684591, upload-time = "2026-03-29T13:20:42.146Z" }, + { url = "https://files.pythonhosted.org/packages/98/7c/21252050676612625449b4807d6b695b9ce8a7c9e1c197ee6216c8a65c7c/numpy-2.4.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:27a8d92cd10f1382a67d7cf4db7ce18341b66438bdd9f691d7b0e48d104c2a9d", size = 16637700, upload-time = "2026-03-29T13:20:46.204Z" }, + { url = "https://files.pythonhosted.org/packages/b1/29/56d2bbef9465db24ef25393383d761a1af4f446a1df9b8cded4fe3a5a5d7/numpy-2.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e44319a2953c738205bf3354537979eaa3998ed673395b964c1176083dd46252", size = 17035781, upload-time = "2026-03-29T13:20:50.242Z" }, + { url = "https://files.pythonhosted.org/packages/e3/2b/a35a6d7589d21f44cea7d0a98de5ddcbb3d421b2622a5c96b1edf18707c3/numpy-2.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e892aff75639bbef0d2a2cfd55535510df26ff92f63c92cd84ef8d4ba5a5557f", size = 18362959, upload-time = "2026-03-29T13:20:54.019Z" }, + { url = "https://files.pythonhosted.org/packages/64/c9/d52ec581f2390e0f5f85cbfd80fb83d965fc15e9f0e1aec2195faa142cde/numpy-2.4.4-cp314-cp314-win32.whl", hash = "sha256:1378871da56ca8943c2ba674530924bb8ca40cd228358a3b5f302ad60cf875fc", size = 6008768, upload-time = "2026-03-29T13:20:56.912Z" }, + { url = "https://files.pythonhosted.org/packages/fa/22/4cc31a62a6c7b74a8730e31a4274c5dc80e005751e277a2ce38e675e4923/numpy-2.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:715d1c092715954784bc79e1174fc2a90093dc4dc84ea15eb14dad8abdcdeb74", size = 12449181, upload-time = "2026-03-29T13:20:59.548Z" }, + { url = "https://files.pythonhosted.org/packages/70/2e/14cda6f4d8e396c612d1bf97f22958e92148801d7e4f110cabebdc0eef4b/numpy-2.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:2c194dd721e54ecad9ad387c1d35e63dce5c4450c6dc7dd5611283dda239aabb", size = 10496035, upload-time = "2026-03-29T13:21:02.524Z" }, + { url = "https://files.pythonhosted.org/packages/b1/e8/8fed8c8d848d7ecea092dc3469643f9d10bc3a134a815a3b033da1d2039b/numpy-2.4.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2aa0613a5177c264ff5921051a5719d20095ea586ca88cc802c5c218d1c67d3e", size = 14824958, upload-time = "2026-03-29T13:21:05.671Z" }, + { url = "https://files.pythonhosted.org/packages/05/1a/d8007a5138c179c2bf33ef44503e83d70434d2642877ee8fbb230e7c0548/numpy-2.4.4-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:42c16925aa5a02362f986765f9ebabf20de75cdefdca827d14315c568dcab113", size = 5330020, upload-time = "2026-03-29T13:21:08.635Z" }, + { url = "https://files.pythonhosted.org/packages/99/64/ffb99ac6ae93faf117bcbd5c7ba48a7f45364a33e8e458545d3633615dda/numpy-2.4.4-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:874f200b2a981c647340f841730fc3a2b54c9d940566a3c4149099591e2c4c3d", size = 6650758, upload-time = "2026-03-29T13:21:10.949Z" }, + { url = "https://files.pythonhosted.org/packages/6e/6e/795cc078b78a384052e73b2f6281ff7a700e9bf53bcce2ee579d4f6dd879/numpy-2.4.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9b39d38a9bd2ae1becd7eac1303d031c5c110ad31f2b319c6e7d98b135c934d", size = 15729948, upload-time = "2026-03-29T13:21:14.047Z" }, + { url = "https://files.pythonhosted.org/packages/5f/86/2acbda8cc2af5f3d7bfc791192863b9e3e19674da7b5e533fded124d1299/numpy-2.4.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b268594bccac7d7cf5844c7732e3f20c50921d94e36d7ec9b79e9857694b1b2f", size = 16679325, upload-time = "2026-03-29T13:21:17.561Z" }, + { url = "https://files.pythonhosted.org/packages/bc/59/cafd83018f4aa55e0ac6fa92aa066c0a1877b77a615ceff1711c260ffae8/numpy-2.4.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ac6b31e35612a26483e20750126d30d0941f949426974cace8e6b5c58a3657b0", size = 17084883, upload-time = "2026-03-29T13:21:21.106Z" }, + { url = "https://files.pythonhosted.org/packages/f0/85/a42548db84e65ece46ab2caea3d3f78b416a47af387fcbb47ec28e660dc2/numpy-2.4.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8e3ed142f2728df44263aaf5fb1f5b0b99f4070c553a0d7f033be65338329150", size = 18403474, upload-time = "2026-03-29T13:21:24.828Z" }, + { url = "https://files.pythonhosted.org/packages/ed/ad/483d9e262f4b831000062e5d8a45e342166ec8aaa1195264982bca267e62/numpy-2.4.4-cp314-cp314t-win32.whl", hash = "sha256:dddbbd259598d7240b18c9d87c56a9d2fb3b02fe266f49a7c101532e78c1d871", size = 6155500, upload-time = "2026-03-29T13:21:28.205Z" }, + { url = "https://files.pythonhosted.org/packages/c7/03/2fc4e14c7bd4ff2964b74ba90ecb8552540b6315f201df70f137faa5c589/numpy-2.4.4-cp314-cp314t-win_amd64.whl", hash = "sha256:a7164afb23be6e37ad90b2f10426149fd75aee07ca55653d2aa41e66c4ef697e", size = 12637755, upload-time = "2026-03-29T13:21:31.107Z" }, + { url = "https://files.pythonhosted.org/packages/58/78/548fb8e07b1a341746bfbecb32f2c268470f45fa028aacdbd10d9bc73aab/numpy-2.4.4-cp314-cp314t-win_arm64.whl", hash = "sha256:ba203255017337d39f89bdd58417f03c4426f12beed0440cfd933cb15f8669c7", size = 10566643, upload-time = "2026-03-29T13:21:34.339Z" }, + { url = "https://files.pythonhosted.org/packages/6b/33/8fae8f964a4f63ed528264ddf25d2b683d0b663e3cba26961eb838a7c1bd/numpy-2.4.4-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:58c8b5929fcb8287cbd6f0a3fae19c6e03a5c48402ae792962ac465224a629a4", size = 16854491, upload-time = "2026-03-29T13:21:38.03Z" }, + { url = "https://files.pythonhosted.org/packages/bc/d0/1aabee441380b981cf8cdda3ae7a46aa827d1b5a8cce84d14598bc94d6d9/numpy-2.4.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:eea7ac5d2dce4189771cedb559c738a71512768210dc4e4753b107a2048b3d0e", size = 14895830, upload-time = "2026-03-29T13:21:41.509Z" }, + { url = "https://files.pythonhosted.org/packages/a5/b8/aafb0d1065416894fccf4df6b49ef22b8db045187949545bced89c034b8e/numpy-2.4.4-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:51fc224f7ca4d92656d5a5eb315f12eb5fe2c97a66249aa7b5f562528a3be38c", size = 5400927, upload-time = "2026-03-29T13:21:44.747Z" }, + { url = "https://files.pythonhosted.org/packages/d6/77/063baa20b08b431038c7f9ff5435540c7b7265c78cf56012a483019ca72d/numpy-2.4.4-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:28a650663f7314afc3e6ec620f44f333c386aad9f6fc472030865dc0ebb26ee3", size = 6715557, upload-time = "2026-03-29T13:21:47.406Z" }, + { url = "https://files.pythonhosted.org/packages/c7/a8/379542d45a14f149444c5c4c4e7714707239ce9cc1de8c2803958889da14/numpy-2.4.4-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:19710a9ca9992d7174e9c52f643d4272dcd1558c5f7af7f6f8190f633bd651a7", size = 15804253, upload-time = "2026-03-29T13:21:50.753Z" }, + { url = "https://files.pythonhosted.org/packages/a2/c8/f0a45426d6d21e7ea3310a15cf90c43a14d9232c31a837702dba437f3373/numpy-2.4.4-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9b2aec6af35c113b05695ebb5749a787acd63cafc83086a05771d1e1cd1e555f", size = 16753552, upload-time = "2026-03-29T13:21:54.344Z" }, + { url = "https://files.pythonhosted.org/packages/04/74/f4c001f4714c3ad9ce037e18cf2b9c64871a84951eaa0baf683a9ca9301c/numpy-2.4.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f2cf083b324a467e1ab358c105f6cad5ea950f50524668a80c486ff1db24e119", size = 12509075, upload-time = "2026-03-29T13:21:57.644Z" }, +] + [[package]] name = "packaging" version = "25.0" @@ -581,6 +824,147 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, ] +[[package]] +name = "pandas" +version = "2.3.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.10.*'", + "python_full_version < '3.10'", +] +dependencies = [ + { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "python-dateutil", marker = "python_full_version < '3.11'" }, + { name = "pytz", marker = "python_full_version < '3.11'" }, + { name = "tzdata", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/f7/f425a00df4fcc22b292c6895c6831c0c8ae1d9fac1e024d16f98a9ce8749/pandas-2.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:376c6446ae31770764215a6c937f72d917f214b43560603cd60da6408f183b6c", size = 11555763, upload-time = "2025-09-29T23:16:53.287Z" }, + { url = "https://files.pythonhosted.org/packages/13/4f/66d99628ff8ce7857aca52fed8f0066ce209f96be2fede6cef9f84e8d04f/pandas-2.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e19d192383eab2f4ceb30b412b22ea30690c9e618f78870357ae1d682912015a", size = 10801217, upload-time = "2025-09-29T23:17:04.522Z" }, + { url = "https://files.pythonhosted.org/packages/1d/03/3fc4a529a7710f890a239cc496fc6d50ad4a0995657dccc1d64695adb9f4/pandas-2.3.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5caf26f64126b6c7aec964f74266f435afef1c1b13da3b0636c7518a1fa3e2b1", size = 12148791, upload-time = "2025-09-29T23:17:18.444Z" }, + { url = "https://files.pythonhosted.org/packages/40/a8/4dac1f8f8235e5d25b9955d02ff6f29396191d4e665d71122c3722ca83c5/pandas-2.3.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd7478f1463441ae4ca7308a70e90b33470fa593429f9d4c578dd00d1fa78838", size = 12769373, upload-time = "2025-09-29T23:17:35.846Z" }, + { url = "https://files.pythonhosted.org/packages/df/91/82cc5169b6b25440a7fc0ef3a694582418d875c8e3ebf796a6d6470aa578/pandas-2.3.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4793891684806ae50d1288c9bae9330293ab4e083ccd1c5e383c34549c6e4250", size = 13200444, upload-time = "2025-09-29T23:17:49.341Z" }, + { url = "https://files.pythonhosted.org/packages/10/ae/89b3283800ab58f7af2952704078555fa60c807fff764395bb57ea0b0dbd/pandas-2.3.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28083c648d9a99a5dd035ec125d42439c6c1c525098c58af0fc38dd1a7a1b3d4", size = 13858459, upload-time = "2025-09-29T23:18:03.722Z" }, + { url = "https://files.pythonhosted.org/packages/85/72/530900610650f54a35a19476eca5104f38555afccda1aa11a92ee14cb21d/pandas-2.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:503cf027cf9940d2ceaa1a93cfb5f8c8c7e6e90720a2850378f0b3f3b1e06826", size = 11346086, upload-time = "2025-09-29T23:18:18.505Z" }, + { url = "https://files.pythonhosted.org/packages/c1/fa/7ac648108144a095b4fb6aa3de1954689f7af60a14cf25583f4960ecb878/pandas-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:602b8615ebcc4a0c1751e71840428ddebeb142ec02c786e8ad6b1ce3c8dec523", size = 11578790, upload-time = "2025-09-29T23:18:30.065Z" }, + { url = "https://files.pythonhosted.org/packages/9b/35/74442388c6cf008882d4d4bdfc4109be87e9b8b7ccd097ad1e7f006e2e95/pandas-2.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8fe25fc7b623b0ef6b5009149627e34d2a4657e880948ec3c840e9402e5c1b45", size = 10833831, upload-time = "2025-09-29T23:38:56.071Z" }, + { url = "https://files.pythonhosted.org/packages/fe/e4/de154cbfeee13383ad58d23017da99390b91d73f8c11856f2095e813201b/pandas-2.3.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b468d3dad6ff947df92dcb32ede5b7bd41a9b3cceef0a30ed925f6d01fb8fa66", size = 12199267, upload-time = "2025-09-29T23:18:41.627Z" }, + { url = "https://files.pythonhosted.org/packages/bf/c9/63f8d545568d9ab91476b1818b4741f521646cbdd151c6efebf40d6de6f7/pandas-2.3.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b98560e98cb334799c0b07ca7967ac361a47326e9b4e5a7dfb5ab2b1c9d35a1b", size = 12789281, upload-time = "2025-09-29T23:18:56.834Z" }, + { url = "https://files.pythonhosted.org/packages/f2/00/a5ac8c7a0e67fd1a6059e40aa08fa1c52cc00709077d2300e210c3ce0322/pandas-2.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37b5848ba49824e5c30bedb9c830ab9b7751fd049bc7914533e01c65f79791", size = 13240453, upload-time = "2025-09-29T23:19:09.247Z" }, + { url = "https://files.pythonhosted.org/packages/27/4d/5c23a5bc7bd209231618dd9e606ce076272c9bc4f12023a70e03a86b4067/pandas-2.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db4301b2d1f926ae677a751eb2bd0e8c5f5319c9cb3f88b0becbbb0b07b34151", size = 13890361, upload-time = "2025-09-29T23:19:25.342Z" }, + { url = "https://files.pythonhosted.org/packages/8e/59/712db1d7040520de7a4965df15b774348980e6df45c129b8c64d0dbe74ef/pandas-2.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:f086f6fe114e19d92014a1966f43a3e62285109afe874f067f5abbdcbb10e59c", size = 11348702, upload-time = "2025-09-29T23:19:38.296Z" }, + { url = "https://files.pythonhosted.org/packages/9c/fb/231d89e8637c808b997d172b18e9d4a4bc7bf31296196c260526055d1ea0/pandas-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d21f6d74eb1725c2efaa71a2bfc661a0689579b58e9c0ca58a739ff0b002b53", size = 11597846, upload-time = "2025-09-29T23:19:48.856Z" }, + { url = "https://files.pythonhosted.org/packages/5c/bd/bf8064d9cfa214294356c2d6702b716d3cf3bb24be59287a6a21e24cae6b/pandas-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3fd2f887589c7aa868e02632612ba39acb0b8948faf5cc58f0850e165bd46f35", size = 10729618, upload-time = "2025-09-29T23:39:08.659Z" }, + { url = "https://files.pythonhosted.org/packages/57/56/cf2dbe1a3f5271370669475ead12ce77c61726ffd19a35546e31aa8edf4e/pandas-2.3.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecaf1e12bdc03c86ad4a7ea848d66c685cb6851d807a26aa245ca3d2017a1908", size = 11737212, upload-time = "2025-09-29T23:19:59.765Z" }, + { url = "https://files.pythonhosted.org/packages/e5/63/cd7d615331b328e287d8233ba9fdf191a9c2d11b6af0c7a59cfcec23de68/pandas-2.3.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b3d11d2fda7eb164ef27ffc14b4fcab16a80e1ce67e9f57e19ec0afaf715ba89", size = 12362693, upload-time = "2025-09-29T23:20:14.098Z" }, + { url = "https://files.pythonhosted.org/packages/a6/de/8b1895b107277d52f2b42d3a6806e69cfef0d5cf1d0ba343470b9d8e0a04/pandas-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a68e15f780eddf2b07d242e17a04aa187a7ee12b40b930bfdd78070556550e98", size = 12771002, upload-time = "2025-09-29T23:20:26.76Z" }, + { url = "https://files.pythonhosted.org/packages/87/21/84072af3187a677c5893b170ba2c8fbe450a6ff911234916da889b698220/pandas-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084", size = 13450971, upload-time = "2025-09-29T23:20:41.344Z" }, + { url = "https://files.pythonhosted.org/packages/86/41/585a168330ff063014880a80d744219dbf1dd7a1c706e75ab3425a987384/pandas-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:a16dcec078a01eeef8ee61bf64074b4e524a2a3f4b3be9326420cabe59c4778b", size = 10992722, upload-time = "2025-09-29T23:20:54.139Z" }, + { url = "https://files.pythonhosted.org/packages/cd/4b/18b035ee18f97c1040d94debd8f2e737000ad70ccc8f5513f4eefad75f4b/pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713", size = 11544671, upload-time = "2025-09-29T23:21:05.024Z" }, + { url = "https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8", size = 10680807, upload-time = "2025-09-29T23:21:15.979Z" }, + { url = "https://files.pythonhosted.org/packages/16/87/9472cf4a487d848476865321de18cc8c920b8cab98453ab79dbbc98db63a/pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d", size = 11709872, upload-time = "2025-09-29T23:21:27.165Z" }, + { url = "https://files.pythonhosted.org/packages/15/07/284f757f63f8a8d69ed4472bfd85122bd086e637bf4ed09de572d575a693/pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac", size = 12306371, upload-time = "2025-09-29T23:21:40.532Z" }, + { url = "https://files.pythonhosted.org/packages/33/81/a3afc88fca4aa925804a27d2676d22dcd2031c2ebe08aabd0ae55b9ff282/pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c", size = 12765333, upload-time = "2025-09-29T23:21:55.77Z" }, + { url = "https://files.pythonhosted.org/packages/8d/0f/b4d4ae743a83742f1153464cf1a8ecfafc3ac59722a0b5c8602310cb7158/pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493", size = 13418120, upload-time = "2025-09-29T23:22:10.109Z" }, + { url = "https://files.pythonhosted.org/packages/4f/c7/e54682c96a895d0c808453269e0b5928a07a127a15704fedb643e9b0a4c8/pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee", size = 10993991, upload-time = "2025-09-29T23:25:04.889Z" }, + { url = "https://files.pythonhosted.org/packages/f9/ca/3f8d4f49740799189e1395812f3bf23b5e8fc7c190827d55a610da72ce55/pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5", size = 12048227, upload-time = "2025-09-29T23:22:24.343Z" }, + { url = "https://files.pythonhosted.org/packages/0e/5a/f43efec3e8c0cc92c4663ccad372dbdff72b60bdb56b2749f04aa1d07d7e/pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21", size = 11411056, upload-time = "2025-09-29T23:22:37.762Z" }, + { url = "https://files.pythonhosted.org/packages/46/b1/85331edfc591208c9d1a63a06baa67b21d332e63b7a591a5ba42a10bb507/pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78", size = 11645189, upload-time = "2025-09-29T23:22:51.688Z" }, + { url = "https://files.pythonhosted.org/packages/44/23/78d645adc35d94d1ac4f2a3c4112ab6f5b8999f4898b8cdf01252f8df4a9/pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110", size = 12121912, upload-time = "2025-09-29T23:23:05.042Z" }, + { url = "https://files.pythonhosted.org/packages/53/da/d10013df5e6aaef6b425aa0c32e1fc1f3e431e4bcabd420517dceadce354/pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86", size = 12712160, upload-time = "2025-09-29T23:23:28.57Z" }, + { url = "https://files.pythonhosted.org/packages/bd/17/e756653095a083d8a37cbd816cb87148debcfcd920129b25f99dd8d04271/pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc", size = 13199233, upload-time = "2025-09-29T23:24:24.876Z" }, + { url = "https://files.pythonhosted.org/packages/04/fd/74903979833db8390b73b3a8a7d30d146d710bd32703724dd9083950386f/pandas-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0", size = 11540635, upload-time = "2025-09-29T23:25:52.486Z" }, + { url = "https://files.pythonhosted.org/packages/21/00/266d6b357ad5e6d3ad55093a7e8efc7dd245f5a842b584db9f30b0f0a287/pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593", size = 10759079, upload-time = "2025-09-29T23:26:33.204Z" }, + { url = "https://files.pythonhosted.org/packages/ca/05/d01ef80a7a3a12b2f8bbf16daba1e17c98a2f039cbc8e2f77a2c5a63d382/pandas-2.3.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c", size = 11814049, upload-time = "2025-09-29T23:27:15.384Z" }, + { url = "https://files.pythonhosted.org/packages/15/b2/0e62f78c0c5ba7e3d2c5945a82456f4fac76c480940f805e0b97fcbc2f65/pandas-2.3.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b", size = 12332638, upload-time = "2025-09-29T23:27:51.625Z" }, + { url = "https://files.pythonhosted.org/packages/c5/33/dd70400631b62b9b29c3c93d2feee1d0964dc2bae2e5ad7a6c73a7f25325/pandas-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6", size = 12886834, upload-time = "2025-09-29T23:28:21.289Z" }, + { url = "https://files.pythonhosted.org/packages/d3/18/b5d48f55821228d0d2692b34fd5034bb185e854bdb592e9c640f6290e012/pandas-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3", size = 13409925, upload-time = "2025-09-29T23:28:58.261Z" }, + { url = "https://files.pythonhosted.org/packages/a6/3d/124ac75fcd0ecc09b8fdccb0246ef65e35b012030defb0e0eba2cbbbe948/pandas-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5", size = 11109071, upload-time = "2025-09-29T23:32:27.484Z" }, + { url = "https://files.pythonhosted.org/packages/89/9c/0e21c895c38a157e0faa1fb64587a9226d6dd46452cac4532d80c3c4a244/pandas-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec", size = 12048504, upload-time = "2025-09-29T23:29:31.47Z" }, + { url = "https://files.pythonhosted.org/packages/d7/82/b69a1c95df796858777b68fbe6a81d37443a33319761d7c652ce77797475/pandas-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7", size = 11410702, upload-time = "2025-09-29T23:29:54.591Z" }, + { url = "https://files.pythonhosted.org/packages/f9/88/702bde3ba0a94b8c73a0181e05144b10f13f29ebfc2150c3a79062a8195d/pandas-2.3.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450", size = 11634535, upload-time = "2025-09-29T23:30:21.003Z" }, + { url = "https://files.pythonhosted.org/packages/a4/1e/1bac1a839d12e6a82ec6cb40cda2edde64a2013a66963293696bbf31fbbb/pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5", size = 12121582, upload-time = "2025-09-29T23:30:43.391Z" }, + { url = "https://files.pythonhosted.org/packages/44/91/483de934193e12a3b1d6ae7c8645d083ff88dec75f46e827562f1e4b4da6/pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788", size = 12699963, upload-time = "2025-09-29T23:31:10.009Z" }, + { url = "https://files.pythonhosted.org/packages/70/44/5191d2e4026f86a2a109053e194d3ba7a31a2d10a9c2348368c63ed4e85a/pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87", size = 13202175, upload-time = "2025-09-29T23:31:59.173Z" }, + { url = "https://files.pythonhosted.org/packages/56/b4/52eeb530a99e2a4c55ffcd352772b599ed4473a0f892d127f4147cf0f88e/pandas-2.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c503ba5216814e295f40711470446bc3fd00f0faea8a086cbc688808e26f92a2", size = 11567720, upload-time = "2025-09-29T23:33:06.209Z" }, + { url = "https://files.pythonhosted.org/packages/48/4a/2d8b67632a021bced649ba940455ed441ca854e57d6e7658a6024587b083/pandas-2.3.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a637c5cdfa04b6d6e2ecedcb81fc52ffb0fd78ce2ebccc9ea964df9f658de8c8", size = 10810302, upload-time = "2025-09-29T23:33:35.846Z" }, + { url = "https://files.pythonhosted.org/packages/13/e6/d2465010ee0569a245c975dc6967b801887068bc893e908239b1f4b6c1ac/pandas-2.3.3-cp39-cp39-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:854d00d556406bffe66a4c0802f334c9ad5a96b4f1f868adf036a21b11ef13ff", size = 12154874, upload-time = "2025-09-29T23:33:49.939Z" }, + { url = "https://files.pythonhosted.org/packages/1f/18/aae8c0aa69a386a3255940e9317f793808ea79d0a525a97a903366bb2569/pandas-2.3.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bf1f8a81d04ca90e32a0aceb819d34dbd378a98bf923b6398b9a3ec0bf44de29", size = 12790141, upload-time = "2025-09-29T23:34:05.655Z" }, + { url = "https://files.pythonhosted.org/packages/f7/26/617f98de789de00c2a444fbe6301bb19e66556ac78cff933d2c98f62f2b4/pandas-2.3.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:23ebd657a4d38268c7dfbdf089fbc31ea709d82e4923c5ffd4fbd5747133ce73", size = 13208697, upload-time = "2025-09-29T23:34:21.835Z" }, + { url = "https://files.pythonhosted.org/packages/b9/fb/25709afa4552042bd0e15717c75e9b4a2294c3dc4f7e6ea50f03c5136600/pandas-2.3.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5554c929ccc317d41a5e3d1234f3be588248e61f08a74dd17c9eabb535777dc9", size = 13879233, upload-time = "2025-09-29T23:34:35.079Z" }, + { url = "https://files.pythonhosted.org/packages/98/af/7be05277859a7bc399da8ba68b88c96b27b48740b6cf49688899c6eb4176/pandas-2.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:d3e28b3e83862ccf4d85ff19cf8c20b2ae7e503881711ff2d534dc8f761131aa", size = 11359119, upload-time = "2025-09-29T23:34:46.339Z" }, +] + +[[package]] +name = "pandas" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "python-dateutil", marker = "python_full_version >= '3.11'" }, + { name = "tzdata", marker = "(python_full_version >= '3.11' and sys_platform == 'emscripten') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/da/99/b342345300f13440fe9fe385c3c481e2d9a595ee3bab4d3219247ac94e9a/pandas-3.0.2.tar.gz", hash = "sha256:f4753e73e34c8d83221ba58f232433fca2748be8b18dbca02d242ed153945043", size = 4645855, upload-time = "2026-03-31T06:48:30.816Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/97/35/6411db530c618e0e0005187e35aa02ce60ae4c4c4d206964a2f978217c27/pandas-3.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a727a73cbdba2f7458dc82449e2315899d5140b449015d822f515749a46cbbe0", size = 10326926, upload-time = "2026-03-31T06:46:08.29Z" }, + { url = "https://files.pythonhosted.org/packages/c4/d3/b7da1d5d7dbdc5ef52ed7debd2b484313b832982266905315dad5a0bf0b1/pandas-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dbbd4aa20ca51e63b53bbde6a0fa4254b1aaabb74d2f542df7a7959feb1d760c", size = 9926987, upload-time = "2026-03-31T06:46:11.724Z" }, + { url = "https://files.pythonhosted.org/packages/52/77/9b1c2d6070b5dbe239a7bc889e21bfa58720793fb902d1e070695d87c6d0/pandas-3.0.2-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:339dda302bd8369dedeae979cb750e484d549b563c3f54f3922cb8ff4978c5eb", size = 10757067, upload-time = "2026-03-31T06:46:14.903Z" }, + { url = "https://files.pythonhosted.org/packages/20/17/ec40d981705654853726e7ac9aea9ddbb4a5d9cf54d8472222f4f3de06c2/pandas-3.0.2-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:61c2fd96d72b983a9891b2598f286befd4ad262161a609c92dc1652544b46b76", size = 11258787, upload-time = "2026-03-31T06:46:17.683Z" }, + { url = "https://files.pythonhosted.org/packages/90/e3/3f1126d43d3702ca8773871a81c9f15122a1f412342cc56284ffda5b1f70/pandas-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c934008c733b8bbea273ea308b73b3156f0181e5b72960790b09c18a2794fe1e", size = 11771616, upload-time = "2026-03-31T06:46:20.532Z" }, + { url = "https://files.pythonhosted.org/packages/2e/cf/0f4e268e1f5062e44a6bda9f925806721cd4c95c2b808a4c82ebe914f96b/pandas-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:60a80bb4feacbef5e1447a3f82c33209c8b7e07f28d805cfd1fb951e5cb443aa", size = 12337623, upload-time = "2026-03-31T06:46:23.754Z" }, + { url = "https://files.pythonhosted.org/packages/44/a0/97a6339859d4acb2536efb24feb6708e82f7d33b2ed7e036f2983fcced82/pandas-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:ed72cb3f45190874eb579c64fa92d9df74e98fd63e2be7f62bce5ace0ade61df", size = 9897372, upload-time = "2026-03-31T06:46:26.703Z" }, + { url = "https://files.pythonhosted.org/packages/8f/eb/781516b808a99ddf288143cec46b342b3016c3414d137da1fdc3290d8860/pandas-3.0.2-cp311-cp311-win_arm64.whl", hash = "sha256:f12b1a9e332c01e09510586f8ca9b108fd631fd656af82e452d7315ef6df5f9f", size = 9154922, upload-time = "2026-03-31T06:46:30.284Z" }, + { url = "https://files.pythonhosted.org/packages/f3/b0/c20bd4d6d3f736e6bd6b55794e9cd0a617b858eaad27c8f410ea05d953b7/pandas-3.0.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:232a70ebb568c0c4d2db4584f338c1577d81e3af63292208d615907b698a0f18", size = 10347921, upload-time = "2026-03-31T06:46:33.36Z" }, + { url = "https://files.pythonhosted.org/packages/35/d0/4831af68ce30cc2d03c697bea8450e3225a835ef497d0d70f31b8cdde965/pandas-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:970762605cff1ca0d3f71ed4f3a769ea8f85fc8e6348f6e110b8fea7e6eb5a14", size = 9888127, upload-time = "2026-03-31T06:46:36.253Z" }, + { url = "https://files.pythonhosted.org/packages/61/a9/16ea9346e1fc4a96e2896242d9bc674764fb9049b0044c0132502f7a771e/pandas-3.0.2-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aff4e6f4d722e0652707d7bcb190c445fe58428500c6d16005b02401764b1b3d", size = 10399577, upload-time = "2026-03-31T06:46:39.224Z" }, + { url = "https://files.pythonhosted.org/packages/c4/a8/3a61a721472959ab0ce865ef05d10b0d6bfe27ce8801c99f33d4fa996e65/pandas-3.0.2-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ef8b27695c3d3dc78403c9a7d5e59a62d5464a7e1123b4e0042763f7104dc74f", size = 10880030, upload-time = "2026-03-31T06:46:42.412Z" }, + { url = "https://files.pythonhosted.org/packages/da/65/7225c0ea4d6ce9cb2160a7fb7f39804871049f016e74782e5dade4d14109/pandas-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f8d68083e49e16b84734eb1a4dcae4259a75c90fb6e2251ab9a00b61120c06ab", size = 11409468, upload-time = "2026-03-31T06:46:45.2Z" }, + { url = "https://files.pythonhosted.org/packages/fa/5b/46e7c76032639f2132359b5cf4c785dd8cf9aea5ea64699eac752f02b9db/pandas-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:32cc41f310ebd4a296d93515fcac312216adfedb1894e879303987b8f1e2b97d", size = 11936381, upload-time = "2026-03-31T06:46:48.293Z" }, + { url = "https://files.pythonhosted.org/packages/7b/8b/721a9cff6fa6a91b162eb51019c6243b82b3226c71bb6c8ef4a9bd65cbc6/pandas-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:a4785e1d6547d8427c5208b748ae2efb64659a21bd82bf440d4262d02bfa02a4", size = 9744993, upload-time = "2026-03-31T06:46:51.488Z" }, + { url = "https://files.pythonhosted.org/packages/d5/18/7f0bd34ae27b28159aa80f2a6799f47fda34f7fb938a76e20c7b7fe3b200/pandas-3.0.2-cp312-cp312-win_arm64.whl", hash = "sha256:08504503f7101300107ecdc8df73658e4347586db5cfdadabc1592e9d7e7a0fd", size = 9056118, upload-time = "2026-03-31T06:46:54.548Z" }, + { url = "https://files.pythonhosted.org/packages/bf/ca/3e639a1ea6fcd0617ca4e8ca45f62a74de33a56ae6cd552735470b22c8d3/pandas-3.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b5918ba197c951dec132b0c5929a00c0bf05d5942f590d3c10a807f6e15a57d3", size = 10321105, upload-time = "2026-03-31T06:46:57.327Z" }, + { url = "https://files.pythonhosted.org/packages/0b/77/dbc82ff2fb0e63c6564356682bf201edff0ba16c98630d21a1fb312a8182/pandas-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d606a041c89c0a474a4702d532ab7e73a14fe35c8d427b972a625c8e46373668", size = 9864088, upload-time = "2026-03-31T06:46:59.935Z" }, + { url = "https://files.pythonhosted.org/packages/5c/2b/341f1b04bbca2e17e13cd3f08c215b70ef2c60c5356ef1e8c6857449edc7/pandas-3.0.2-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:710246ba0616e86891b58ab95f2495143bb2bc83ab6b06747c74216f583a6ac9", size = 10369066, upload-time = "2026-03-31T06:47:02.792Z" }, + { url = "https://files.pythonhosted.org/packages/12/c5/cbb1ffefb20a93d3f0e1fdcda699fb84976210d411b008f97f48bf6ce27e/pandas-3.0.2-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5d3cfe227c725b1f3dff4278b43d8c784656a42a9325b63af6b1492a8232209e", size = 10876780, upload-time = "2026-03-31T06:47:06.205Z" }, + { url = "https://files.pythonhosted.org/packages/98/fe/2249ae5e0a69bd0ddf17353d0a5d26611d70970111f5b3600cdc8be883e7/pandas-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c3b723df9087a9a9a840e263ebd9f88b64a12075d1bf2ea401a5a42f254f084d", size = 11375181, upload-time = "2026-03-31T06:47:09.383Z" }, + { url = "https://files.pythonhosted.org/packages/de/64/77a38b09e70b6464883b8d7584ab543e748e42c1b5d337a2ee088e0df741/pandas-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a3096110bf9eac0070b7208465f2740e2d8a670d5cb6530b5bb884eca495fd39", size = 11928899, upload-time = "2026-03-31T06:47:12.686Z" }, + { url = "https://files.pythonhosted.org/packages/5e/52/42855bf626868413f761addd574acc6195880ae247a5346477a4361c3acb/pandas-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:07a10f5c36512eead51bc578eb3354ad17578b22c013d89a796ab5eee90cd991", size = 9746574, upload-time = "2026-03-31T06:47:15.64Z" }, + { url = "https://files.pythonhosted.org/packages/88/39/21304ae06a25e8bf9fc820d69b29b2c495b2ae580d1e143146c309941760/pandas-3.0.2-cp313-cp313-win_arm64.whl", hash = "sha256:5fdbfa05931071aba28b408e59226186b01eb5e92bea2ab78b65863ca3228d84", size = 9047156, upload-time = "2026-03-31T06:47:18.595Z" }, + { url = "https://files.pythonhosted.org/packages/72/20/7defa8b27d4f330a903bb68eea33be07d839c5ea6bdda54174efcec0e1d2/pandas-3.0.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:dbc20dea3b9e27d0e66d74c42b2d0c1bed9c2ffe92adea33633e3bedeb5ac235", size = 10756238, upload-time = "2026-03-31T06:47:22.012Z" }, + { url = "https://files.pythonhosted.org/packages/e9/95/49433c14862c636afc0e9b2db83ff16b3ad92959364e52b2955e44c8e94c/pandas-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b75c347eff42497452116ce05ef461822d97ce5b9ff8df6edacb8076092c855d", size = 10408520, upload-time = "2026-03-31T06:47:25.197Z" }, + { url = "https://files.pythonhosted.org/packages/3b/f8/462ad2b5881d6b8ec8e5f7ed2ea1893faa02290d13870a1600fe72ad8efc/pandas-3.0.2-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1478075142e83a5571782ad007fb201ed074bdeac7ebcc8890c71442e96adf7", size = 10324154, upload-time = "2026-03-31T06:47:28.097Z" }, + { url = "https://files.pythonhosted.org/packages/0a/65/d1e69b649cbcddda23ad6e4c40ef935340f6f652a006e5cbc3555ac8adb3/pandas-3.0.2-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5880314e69e763d4c8b27937090de570f1fb8d027059a7ada3f7f8e98bdcb677", size = 10714449, upload-time = "2026-03-31T06:47:30.85Z" }, + { url = "https://files.pythonhosted.org/packages/47/a4/85b59bc65b8190ea3689882db6cdf32a5003c0ccd5a586c30fdcc3ffc4fc/pandas-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b5329e26898896f06035241a626d7c335daa479b9bbc82be7c2742d048e41172", size = 11338475, upload-time = "2026-03-31T06:47:34.026Z" }, + { url = "https://files.pythonhosted.org/packages/1e/c4/bc6966c6e38e5d9478b935272d124d80a589511ed1612a5d21d36f664c68/pandas-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:81526c4afd31971f8b62671442a4b2b51e0aa9acc3819c9f0f12a28b6fcf85f1", size = 11786568, upload-time = "2026-03-31T06:47:36.941Z" }, + { url = "https://files.pythonhosted.org/packages/e8/74/09298ca9740beed1d3504e073d67e128aa07e5ca5ca2824b0c674c0b8676/pandas-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:7cadd7e9a44ec13b621aec60f9150e744cfc7a3dd32924a7e2f45edff31823b0", size = 10488652, upload-time = "2026-03-31T06:47:40.612Z" }, + { url = "https://files.pythonhosted.org/packages/bb/40/c6ea527147c73b24fc15c891c3fcffe9c019793119c5742b8784a062c7db/pandas-3.0.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:db0dbfd2a6cdf3770aa60464d50333d8f3d9165b2f2671bcc299b72de5a6677b", size = 10326084, upload-time = "2026-03-31T06:47:43.834Z" }, + { url = "https://files.pythonhosted.org/packages/95/25/bdb9326c3b5455f8d4d3549fce7abcf967259de146fe2cf7a82368141948/pandas-3.0.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0555c5882688a39317179ab4a0ed41d3ebc8812ab14c69364bbee8fb7a3f6288", size = 9914146, upload-time = "2026-03-31T06:47:46.67Z" }, + { url = "https://files.pythonhosted.org/packages/8d/77/3a227ff3337aa376c60d288e1d61c5d097131d0ac71f954d90a8f369e422/pandas-3.0.2-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:01f31a546acd5574ef77fe199bc90b55527c225c20ccda6601cf6b0fd5ed597c", size = 10444081, upload-time = "2026-03-31T06:47:49.681Z" }, + { url = "https://files.pythonhosted.org/packages/15/88/3cdd54fa279341afa10acf8d2b503556b1375245dccc9315659f795dd2e9/pandas-3.0.2-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:deeca1b5a931fdf0c2212c8a659ade6d3b1edc21f0914ce71ef24456ca7a6535", size = 10897535, upload-time = "2026-03-31T06:47:53.033Z" }, + { url = "https://files.pythonhosted.org/packages/06/9d/98cc7a7624f7932e40f434299260e2917b090a579d75937cb8a57b9d2de3/pandas-3.0.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0f48afd9bb13300ffb5a3316973324c787054ba6665cda0da3fbd67f451995db", size = 11446992, upload-time = "2026-03-31T06:47:56.193Z" }, + { url = "https://files.pythonhosted.org/packages/9a/cd/19ff605cc3760e80602e6826ddef2824d8e7050ed80f2e11c4b079741dc3/pandas-3.0.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6c4d8458b97a35717b62469a4ea0e85abd5ed8687277f5ccfc67f8a5126f8c53", size = 11968257, upload-time = "2026-03-31T06:47:59.137Z" }, + { url = "https://files.pythonhosted.org/packages/db/60/aba6a38de456e7341285102bede27514795c1eaa353bc0e7638b6b785356/pandas-3.0.2-cp314-cp314-win_amd64.whl", hash = "sha256:b35d14bb5d8285d9494fe93815a9e9307c0876e10f1e8e89ac5b88f728ec8dcf", size = 9865893, upload-time = "2026-03-31T06:48:02.038Z" }, + { url = "https://files.pythonhosted.org/packages/08/71/e5ec979dd2e8a093dacb8864598c0ff59a0cee0bbcdc0bfec16a51684d4f/pandas-3.0.2-cp314-cp314-win_arm64.whl", hash = "sha256:63d141b56ef686f7f0d714cfb8de4e320475b86bf4b620aa0b7da89af8cbdbbb", size = 9188644, upload-time = "2026-03-31T06:48:05.045Z" }, + { url = "https://files.pythonhosted.org/packages/f1/6c/7b45d85db19cae1eb524f2418ceaa9d85965dcf7b764ed151386b7c540f0/pandas-3.0.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:140f0cffb1fa2524e874dde5b477d9defe10780d8e9e220d259b2c0874c89d9d", size = 10776246, upload-time = "2026-03-31T06:48:07.789Z" }, + { url = "https://files.pythonhosted.org/packages/a8/3e/7b00648b086c106e81766f25322b48aa8dfa95b55e621dbdf2fdd413a117/pandas-3.0.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ae37e833ff4fed0ba352f6bdd8b73ba3ab3256a85e54edfd1ab51ae40cca0af8", size = 10424801, upload-time = "2026-03-31T06:48:10.897Z" }, + { url = "https://files.pythonhosted.org/packages/da/6e/558dd09a71b53b4008e7fc8a98ec6d447e9bfb63cdaeea10e5eb9b2dabe8/pandas-3.0.2-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4d888a5c678a419a5bb41a2a93818e8ed9fd3172246555c0b37b7cc27027effd", size = 10345643, upload-time = "2026-03-31T06:48:13.7Z" }, + { url = "https://files.pythonhosted.org/packages/be/e3/921c93b4d9a280409451dc8d07b062b503bbec0531d2627e73a756e99a82/pandas-3.0.2-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b444dc64c079e84df91baa8bf613d58405645461cabca929d9178f2cd392398d", size = 10743641, upload-time = "2026-03-31T06:48:16.659Z" }, + { url = "https://files.pythonhosted.org/packages/56/ca/fd17286f24fa3b4d067965d8d5d7e14fe557dd4f979a0b068ac0deaf8228/pandas-3.0.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4544c7a54920de8eeacaa1466a6b7268ecfbc9bc64ab4dbb89c6bbe94d5e0660", size = 11361993, upload-time = "2026-03-31T06:48:19.475Z" }, + { url = "https://files.pythonhosted.org/packages/e4/a5/2f6ed612056819de445a433ca1f2821ac3dab7f150d569a59e9cc105de1d/pandas-3.0.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:734be7551687c00fbd760dc0522ed974f82ad230d4a10f54bf51b80d44a08702", size = 11815274, upload-time = "2026-03-31T06:48:22.695Z" }, + { url = "https://files.pythonhosted.org/packages/00/2f/b622683e99ec3ce00b0854bac9e80868592c5b051733f2cf3a868e5fea26/pandas-3.0.2-cp314-cp314t-win_amd64.whl", hash = "sha256:57a07209bebcbcf768d2d13c9b78b852f9a15978dac41b9e6421a81ad4cdd276", size = 10888530, upload-time = "2026-03-31T06:48:25.806Z" }, + { url = "https://files.pythonhosted.org/packages/cb/2b/f8434233fab2bd66a02ec014febe4e5adced20e2693e0e90a07d118ed30e/pandas-3.0.2-cp314-cp314t-win_arm64.whl", hash = "sha256:5371b72c2d4d415d08765f32d689217a43227484e81b2305b52076e328f6f482", size = 9455341, upload-time = "2026-03-31T06:48:28.418Z" }, +] + [[package]] name = "pathspec" version = "0.12.1" @@ -854,6 +1238,8 @@ all = [ { name = "cron-converter" }, { name = "jsonschema", version = "4.25.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "jsonschema", version = "4.26.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "pandas", version = "3.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "pendulum" }, { name = "phonenumbers" }, { name = "pycountry" }, @@ -871,6 +1257,10 @@ jsonschema = [ { name = "jsonschema", version = "4.25.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, { name = "jsonschema", version = "4.26.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, ] +pandas = [ + { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "pandas", version = "3.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] pendulum = [ { name = "pendulum" }, ] @@ -928,6 +1318,8 @@ requires-dist = [ { name = "cron-converter", marker = "extra == 'cron'", specifier = ">=1.2.2" }, { name = "jsonschema", marker = "extra == 'all'", specifier = ">=4.0.0" }, { name = "jsonschema", marker = "extra == 'jsonschema'", specifier = ">=4.0.0" }, + { name = "pandas", marker = "extra == 'all'", specifier = ">=2.0.0" }, + { name = "pandas", marker = "extra == 'pandas'", specifier = ">=2.0.0" }, { name = "pendulum", marker = "extra == 'all'", specifier = ">=3.0.0,<4.0.0" }, { name = "pendulum", marker = "extra == 'pendulum'", specifier = ">=3.0.0,<4.0.0" }, { name = "phonenumbers", marker = "extra == 'all'", specifier = ">=8,<10" }, @@ -949,7 +1341,7 @@ requires-dist = [ { name = "uuid-utils", marker = "python_full_version < '3.14' and extra == 'all'", specifier = ">=0.6.0" }, { name = "uuid-utils", marker = "python_full_version < '3.14' and extra == 'uuid-utils'", specifier = ">=0.6.0" }, ] -provides-extras = ["all", "cron", "jsonschema", "pendulum", "phonenumbers", "pycountry", "python-ulid", "semver", "uuid-utils"] +provides-extras = ["all", "cron", "jsonschema", "pandas", "pendulum", "phonenumbers", "pycountry", "python-ulid", "semver", "uuid-utils"] [package.metadata.requires-dev] dev = [ @@ -1094,7 +1486,13 @@ name = "pytest" version = "9.0.2" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.10'", + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.10.*'", ] dependencies = [ { name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" }, @@ -1176,7 +1574,13 @@ name = "referencing" version = "0.37.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.10'", + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.10.*'", ] dependencies = [ { name = "attrs", marker = "python_full_version >= '3.10'" }, @@ -1372,7 +1776,13 @@ name = "rpds-py" version = "0.30.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.10'", + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.10.*'", ] sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } wheels = [ @@ -1604,7 +2014,13 @@ name = "types-jsonschema" version = "4.26.0.20260402" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.10'", + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.10.*'", ] dependencies = [ { name = "referencing", version = "0.37.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, From 947e405992f9fecba67f5ce3fbdfc4aedb514211 Mon Sep 17 00:00:00 2001 From: Akbar Hasanov Date: Tue, 7 Apr 2026 04:17:23 +0200 Subject: [PATCH 31/31] fix: add type: ignore[import-untyped] for pandas, remove unused override ignore --- pydantic_extra_types/pandas_types.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pydantic_extra_types/pandas_types.py b/pydantic_extra_types/pandas_types.py index 4ac88305..9a50249f 100644 --- a/pydantic_extra_types/pandas_types.py +++ b/pydantic_extra_types/pandas_types.py @@ -1,7 +1,7 @@ from __future__ import annotations try: - import pandas as pd + import pandas as pd # type: ignore[import-untyped] except ModuleNotFoundError: # pragma: no cover raise RuntimeError( 'The `pandas_types` module requires "pandas" to be installed. ' @@ -36,7 +36,7 @@ class MyModel(BaseModel): _item_type: ClassVar[type | None] = None - def __class_getitem__(cls, item: type) -> type: # type: ignore[override] + def __class_getitem__(cls, item: type) -> type: return type(f'Series[{item.__name__}]', (cls,), {'_item_type': item}) @classmethod