From cbe5d33fd91f5e794bc93e28f670d6194a64f3f8 Mon Sep 17 00:00:00 2001 From: sneakers-the-rat Date: Wed, 31 Jul 2024 13:06:10 -0700 Subject: [PATCH 01/15] initial vendoring, pre-fixing --- README.md | 9 +- licenses/nptyping.txt | 7 + pyproject.toml | 5 + src/numpydantic/vendor/__init__.py | 0 src/numpydantic/vendor/nptyping/__init__.py | 188 ++++++++++ .../vendor/nptyping/assert_isinstance.py | 52 +++ .../vendor/nptyping/base_meta_classes.py | 248 +++++++++++++ src/numpydantic/vendor/nptyping/error.py | 47 +++ src/numpydantic/vendor/nptyping/ndarray.py | 196 ++++++++++ .../vendor/nptyping/nptyping_type.py | 30 ++ .../vendor/nptyping/package_info.py | 37 ++ .../vendor/nptyping/pandas_/__init__.py | 0 .../vendor/nptyping/pandas_/dataframe.py | 139 +++++++ .../vendor/nptyping/pandas_/dataframe.pyi | 27 ++ .../vendor/nptyping/pandas_/typing_.py | 33 ++ src/numpydantic/vendor/nptyping/py.typed | 0 src/numpydantic/vendor/nptyping/recarray.py | 78 ++++ src/numpydantic/vendor/nptyping/recarray.pyi | 27 ++ src/numpydantic/vendor/nptyping/shape.py | 75 ++++ src/numpydantic/vendor/nptyping/shape.pyi | 36 ++ .../vendor/nptyping/shape_expression.py | 190 ++++++++++ src/numpydantic/vendor/nptyping/structure.py | 107 ++++++ src/numpydantic/vendor/nptyping/structure.pyi | 38 ++ .../vendor/nptyping/structure_expression.py | 339 ++++++++++++++++++ src/numpydantic/vendor/nptyping/typing_.py | 185 ++++++++++ src/numpydantic/vendor/nptyping/typing_.pyi | 114 ++++++ 26 files changed, 2206 insertions(+), 1 deletion(-) create mode 100644 licenses/nptyping.txt create mode 100644 src/numpydantic/vendor/__init__.py create mode 100644 src/numpydantic/vendor/nptyping/__init__.py create mode 100644 src/numpydantic/vendor/nptyping/assert_isinstance.py create mode 100644 src/numpydantic/vendor/nptyping/base_meta_classes.py create mode 100644 src/numpydantic/vendor/nptyping/error.py create mode 100644 src/numpydantic/vendor/nptyping/ndarray.py create mode 100644 src/numpydantic/vendor/nptyping/nptyping_type.py create mode 100644 src/numpydantic/vendor/nptyping/package_info.py create mode 100644 src/numpydantic/vendor/nptyping/pandas_/__init__.py create mode 100644 src/numpydantic/vendor/nptyping/pandas_/dataframe.py create mode 100644 src/numpydantic/vendor/nptyping/pandas_/dataframe.pyi create mode 100644 src/numpydantic/vendor/nptyping/pandas_/typing_.py create mode 100644 src/numpydantic/vendor/nptyping/py.typed create mode 100644 src/numpydantic/vendor/nptyping/recarray.py create mode 100644 src/numpydantic/vendor/nptyping/recarray.pyi create mode 100644 src/numpydantic/vendor/nptyping/shape.py create mode 100644 src/numpydantic/vendor/nptyping/shape.pyi create mode 100644 src/numpydantic/vendor/nptyping/shape_expression.py create mode 100644 src/numpydantic/vendor/nptyping/structure.py create mode 100644 src/numpydantic/vendor/nptyping/structure.pyi create mode 100644 src/numpydantic/vendor/nptyping/structure_expression.py create mode 100644 src/numpydantic/vendor/nptyping/typing_.py create mode 100644 src/numpydantic/vendor/nptyping/typing_.pyi diff --git a/README.md b/README.md index e69c8b2..b7ceeff 100644 --- a/README.md +++ b/README.md @@ -416,4 +416,11 @@ dumped = instance.model_dump_json(context={'zarr_dump_array': True}) "hexdigest": "c51604eace325fe42bbebf39146c0956bd2ed13c" } } -``` \ No newline at end of file +``` + +## Vendored Dependencies + +We have vendored dependencies in the `src/numpydantic/vendor` package, +and reproduced their licenses in the `licenses` directory. + +- [nptyping](https://github.com/ramonhagenaars/nptyping) - `numpydantic.vendor.nptyping` - `/licenses/nptyping.txt` \ No newline at end of file diff --git a/licenses/nptyping.txt b/licenses/nptyping.txt new file mode 100644 index 0000000..5a89c4e --- /dev/null +++ b/licenses/nptyping.txt @@ -0,0 +1,7 @@ +Copyright 2022, Ramon Hagenaars + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/pyproject.toml b/pyproject.toml index 68ef104..71d6d5e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -131,4 +131,9 @@ fixable = ["ALL"] [tool.mypy] plugins = [ "pydantic.mypy" +] + +[tool.coverage.run] +omit = [ + "src/numpydantic/vendor/*" ] \ No newline at end of file diff --git a/src/numpydantic/vendor/__init__.py b/src/numpydantic/vendor/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/numpydantic/vendor/nptyping/__init__.py b/src/numpydantic/vendor/nptyping/__init__.py new file mode 100644 index 0000000..5fd5b2c --- /dev/null +++ b/src/numpydantic/vendor/nptyping/__init__.py @@ -0,0 +1,188 @@ +""" +MIT License + +Copyright (c) 2023 Ramon Hagenaars + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" +from nptyping.assert_isinstance import assert_isinstance +from nptyping.error import ( + InvalidArgumentsError, + InvalidDTypeError, + InvalidShapeError, + InvalidStructureError, + NPTypingError, +) +from nptyping.ndarray import NDArray +from nptyping.package_info import __version__ +from nptyping.pandas_.dataframe import DataFrame +from nptyping.recarray import RecArray +from nptyping.shape import Shape +from nptyping.shape_expression import ( + normalize_shape_expression, + validate_shape_expression, +) +from nptyping.structure import Structure +from nptyping.typing_ import ( + Bool, + Bool8, + Byte, + Bytes, + Bytes0, + CDouble, + CFloat, + Character, + CLongDouble, + CLongFloat, + Complex, + Complex64, + Complex128, + ComplexFloating, + CSingle, + Datetime64, + Double, + DType, + Flexible, + Float, + Float16, + Float32, + Float64, + Floating, + Half, + Inexact, + Int, + Int0, + Int8, + Int16, + Int32, + Int64, + IntC, + Integer, + IntP, + LongComplex, + LongDouble, + LongFloat, + LongLong, + Number, + Object, + Object0, + Short, + SignedInteger, + Single, + SingleComplex, + Str0, + String, + Timedelta64, + UByte, + UInt, + UInt0, + UInt8, + UInt16, + UInt32, + UInt64, + UIntC, + UIntP, + ULongLong, + Unicode, + UnsignedInteger, + UShort, + Void, + Void0, +) + +__all__ = [ + "NDArray", + "RecArray", + "assert_isinstance", + "validate_shape_expression", + "normalize_shape_expression", + "NPTypingError", + "InvalidArgumentsError", + "InvalidShapeError", + "InvalidStructureError", + "InvalidDTypeError", + "Shape", + "Structure", + "__version__", + "DType", + "Number", + "Bool", + "Bool8", + "Object", + "Object0", + "Datetime64", + "Integer", + "SignedInteger", + "Int8", + "Int16", + "Int32", + "Int64", + "Byte", + "Short", + "IntC", + "IntP", + "Int0", + "Int", + "LongLong", + "Timedelta64", + "UnsignedInteger", + "UInt8", + "UInt16", + "UInt32", + "UInt64", + "UByte", + "UShort", + "UIntC", + "UIntP", + "UInt0", + "UInt", + "ULongLong", + "Inexact", + "Floating", + "Float16", + "Float32", + "Float64", + "Half", + "Single", + "Double", + "Float", + "LongDouble", + "LongFloat", + "ComplexFloating", + "Complex64", + "Complex128", + "CSingle", + "SingleComplex", + "CDouble", + "Complex", + "CFloat", + "CLongDouble", + "CLongFloat", + "LongComplex", + "Flexible", + "Void", + "Void0", + "Character", + "Bytes", + "String", + "Bytes0", + "Unicode", + "Str0", + "DataFrame", +] diff --git a/src/numpydantic/vendor/nptyping/assert_isinstance.py b/src/numpydantic/vendor/nptyping/assert_isinstance.py new file mode 100644 index 0000000..5ae4eb1 --- /dev/null +++ b/src/numpydantic/vendor/nptyping/assert_isinstance.py @@ -0,0 +1,52 @@ +""" +MIT License + +Copyright (c) 2023 Ramon Hagenaars + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" +from typing import ( + Any, + Optional, + Type, + TypeVar, +) + +try: + from typing import TypeGuard # type: ignore[attr-defined] +except ImportError: # pragma: no cover + from typing_extensions import TypeGuard # type: ignore[attr-defined] + +TYPE = TypeVar("TYPE") + + +def assert_isinstance( + instance: Any, cls: Type[TYPE], message: Optional[str] = None +) -> TypeGuard[TYPE]: + """ + A TypeGuard function that is equivalent to `assert instance, cls, message` + that hides nasty MyPy or IDE warnings. + :param instance: the instance that is checked against cls. + :param cls: the class + :param message: any message that is displayed when the assert check fails. + :return: the type of cls. + """ + message = message or f"instance={instance!r}, cls={cls!r}" + assert isinstance(instance, cls), message + return True diff --git a/src/numpydantic/vendor/nptyping/base_meta_classes.py b/src/numpydantic/vendor/nptyping/base_meta_classes.py new file mode 100644 index 0000000..b9ca13a --- /dev/null +++ b/src/numpydantic/vendor/nptyping/base_meta_classes.py @@ -0,0 +1,248 @@ +""" +MIT License + +Copyright (c) 2023 Ramon Hagenaars + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" +from abc import ABCMeta, abstractmethod +from inspect import FrameInfo +from typing import ( + Any, + Dict, + List, + Optional, + Set, + Tuple, + TypeVar, +) + +from nptyping.error import InvalidArgumentsError, NPTypingError + +_T = TypeVar("_T") + + +class InconstructableMeta(ABCMeta): + """ + Makes it impossible for a class to get instantiated. + """ + + def __call__(cls, *_: Any, **__: Any) -> None: + raise NPTypingError( + f"Cannot instantiate nptyping.{cls.__name__}. Did you mean to use [ ] ?" + ) + + +class ImmutableMeta(ABCMeta): + """ + Makes it impossible to changes values on a class. + """ + + def __setattr__(cls, key: str, value: Any) -> None: + if key not in ("_abc_impl", "__abstractmethods__"): + raise NPTypingError(f"Cannot set values to nptyping.{cls.__name__}.") + + +class FinalMeta(ABCMeta): + """ + Makes it impossible for classes to inherit from some class. + + An concrete inheriting meta class requires to define a name for its + implementation. The class with this name will be the only class that is + allowed to use that concrete meta class. + """ + + _name_per_meta_cls: Dict[type, Optional[str]] = {} + + def __init_subclass__(cls, implementation: Optional[str] = None) -> None: + # implementation is made Optional here, to allow other meta classes to + # inherit. + cls._name_per_meta_cls[cls] = implementation + + def __new__(cls, name: str, *args: Any, **kwargs: Any) -> type: + if name == cls._name_per_meta_cls[cls]: + assert name, "cls_name not set" + return type.__new__(cls, name, *args, **kwargs) + + raise NPTypingError(f"Cannot subclass nptyping.{cls._name_per_meta_cls[cls]}.") + + +class MaybeCheckableMeta(ABCMeta): + """ + Makes instance and subclass checks raise by default. + """ + + def __instancecheck__(cls, instance: Any) -> bool: + raise NPTypingError( + f"Instance checking is not supported for nptyping.{cls.__name__}." + ) + + def __subclasscheck__(cls, subclass: Any) -> bool: + raise NPTypingError( + f"Subclass checking is not supported for nptyping.{cls.__name__}." + ) + + +class PrintableMeta(ABCMeta): + """ + Ensures that a class can be printed nicely. + """ + + @abstractmethod + def __str__(cls) -> str: + ... # pragma: no cover + + def __repr__(cls) -> str: + return str(cls) + + +class SubscriptableMeta(ABCMeta): + """ + Makes a class subscriptable: it accepts arguments between brackets and a + new type is returned for every unique set of arguments. + """ + + _all_types: Dict[Tuple[type, Tuple[Any, ...]], type] = {} + _parameterized: bool = False + + @abstractmethod + def _get_item(cls, item: Any) -> Tuple[Any, ...]: + ... # pragma: no cover + + def _get_module(cls, stack: List[FrameInfo], module: str) -> str: + # The magic below makes Python's help function display a meaningful + # text with nptyping types. + return "typing" if stack[1][3] == "formatannotation" else module + + def _get_additional_values( + cls, item: Any # pylint: disable=unused-argument + ) -> Dict[str, Any]: + # This method is invoked after _get_item and right before returning + # the result of __getitem__. It can be overridden to provide extra + # values that are to be set as attributes on the new type. + return {} + + def __getitem__(cls, item: Any) -> type: + if getattr(cls, "_parameterized", False): + raise NPTypingError(f"Type nptyping.{cls} is already parameterized.") + + args = cls._get_item(item) + additional_values = cls._get_additional_values(item) + assert hasattr(cls, "__args__"), "A SubscriptableMeta must have __args__." + if args != cls.__args__: # type: ignore[attr-defined] + result = cls._create_type(args, additional_values) + else: + result = cls + + return result + + def _create_type( + cls, args: Tuple[Any, ...], additional_values: Dict[str, Any] + ) -> type: + key = (cls, args) + if key not in cls._all_types: + cls._all_types[key] = type( + cls.__name__, + (cls,), + {"__args__": args, "_parameterized": True, **additional_values}, + ) + return cls._all_types[key] + + +class ComparableByArgsMeta(ABCMeta): + """ + Makes a class comparable by means of its __args__. + """ + + __args__: Tuple[Any, ...] + + def __eq__(cls, other: Any) -> bool: + return ( + hasattr(cls, "__args__") + and hasattr(other, "__args__") + and cls.__args__ == other.__args__ + ) + + def __hash__(cls) -> int: + return hash(cls.__args__) + + +class ContainerMeta( + InconstructableMeta, + ImmutableMeta, + FinalMeta, + MaybeCheckableMeta, + PrintableMeta, + SubscriptableMeta, + ComparableByArgsMeta, + ABCMeta, +): + """ + Base meta class for "containers" such as Shape and Structure. + """ + + _known_expressions: Set[str] = set() + __args__: Tuple[str, ...] + + @abstractmethod + def _validate_expression(cls, item: str) -> None: + ... # pragma: no cover + + @abstractmethod + def _normalize_expression(cls, item: str) -> str: + ... # pragma: no cover + + def _get_item(cls, item: Any) -> Tuple[Any, ...]: + if not isinstance(item, str): + raise InvalidArgumentsError( + f"Unexpected argument of type {type(item)}, expecting a string." + ) + + if item in cls._known_expressions: + # No need to do costly validations and normalizations if it has been done + # before. + return (item,) + + cls._validate_expression(item) + norm_shape_expression = cls._normalize_expression(item) + cls._known_expressions.add(norm_shape_expression) + return (norm_shape_expression,) + + def __subclasscheck__(cls, subclass: Any) -> bool: + type_match = type(subclass) == type( # pylint: disable=unidiomatic-typecheck + cls + ) + return type_match and ( + subclass.__args__ == cls.__args__ or not cls._parameterized + ) + + def __str__(cls) -> str: + return f"{cls.__name__}['{cls.__args__[0]}']" + + def __eq__(cls, other: Any) -> bool: + result = cls is other + if not result and hasattr(cls, "__args__") and hasattr(other, "__args__"): + normalized_args = tuple( + cls._normalize_expression(str(arg)) for arg in other.__args__ + ) + result = cls.__args__ == normalized_args + return result + + def __hash__(cls) -> int: + return hash(cls.__args__) diff --git a/src/numpydantic/vendor/nptyping/error.py b/src/numpydantic/vendor/nptyping/error.py new file mode 100644 index 0000000..237864e --- /dev/null +++ b/src/numpydantic/vendor/nptyping/error.py @@ -0,0 +1,47 @@ +""" +MIT License + +Copyright (c) 2023 Ramon Hagenaars + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + + +class NPTypingError(Exception): + """Base error for all NPTyping errors.""" + + +class InvalidArgumentsError(NPTypingError): + """Raised when a invalid arguments are provided to an nptyping type.""" + + +class InvalidShapeError(NPTypingError): + """Raised when a shape is considered not valid.""" + + +class InvalidStructureError(NPTypingError): + """Raised when a structure is considered not valid.""" + + +class InvalidDTypeError(NPTypingError): + """Raised when an argument is not a DType.""" + + +class DependencyError(NPTypingError): + """Raised when a dependency has not been installed.""" diff --git a/src/numpydantic/vendor/nptyping/ndarray.py b/src/numpydantic/vendor/nptyping/ndarray.py new file mode 100644 index 0000000..adeea12 --- /dev/null +++ b/src/numpydantic/vendor/nptyping/ndarray.py @@ -0,0 +1,196 @@ +""" +MIT License + +Copyright (c) 2023 Ramon Hagenaars + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" +import inspect +from abc import ABC +from typing import Any, Tuple + +import numpy as np + +from nptyping.base_meta_classes import ( + FinalMeta, + ImmutableMeta, + InconstructableMeta, + MaybeCheckableMeta, + PrintableMeta, + SubscriptableMeta, +) +from nptyping.error import InvalidArgumentsError +from nptyping.nptyping_type import NPTypingType +from nptyping.shape import Shape +from nptyping.shape_expression import check_shape +from nptyping.structure import Structure +from nptyping.structure_expression import check_structure, check_type_names +from nptyping.typing_ import ( + DType, + dtype_per_name, + name_per_dtype, +) + + +class NDArrayMeta( + SubscriptableMeta, + InconstructableMeta, + ImmutableMeta, + FinalMeta, + MaybeCheckableMeta, + PrintableMeta, + implementation="NDArray", +): + """ + Metaclass that is coupled to nptyping.NDArray. It contains all actual logic + such as instance checking. + """ + + __args__: Tuple[Shape, DType] + _parameterized: bool + + @property + def __module__(cls) -> str: + return cls._get_module(inspect.stack(), "nptyping.ndarray") + + def _get_item(cls, item: Any) -> Tuple[Any, ...]: + cls._check_item(item) + shape, dtype = cls._get_from_tuple(item) + return shape, dtype + + def __instancecheck__( # pylint: disable=bad-mcs-method-argument + self, instance: Any + ) -> bool: + shape, dtype = self.__args__ + dtype_is_structure = issubclass(dtype, Structure) + structure_is_ok = dtype_is_structure and check_structure( + instance.dtype, dtype, dtype_per_name + ) + return ( + isinstance(instance, np.ndarray) + and (shape is Any or check_shape(instance.shape, shape)) + and ( + dtype is Any + or structure_is_ok + or issubclass(instance.dtype.type, dtype) + ) + ) + + def __str__(cls) -> str: + shape, dtype = cls.__args__ + return ( + f"{cls.__name__}[{cls._shape_expression_to_str(shape)}, " + f"{cls._dtype_to_str(dtype)}]" + ) + + def _is_literal_like(cls, item: Any) -> bool: + # item is a Literal or "Literal enough" (ducktyping). + return hasattr(item, "__args__") + + def _check_item(cls, item: Any) -> None: + # Check if the item is what we expect and raise if it is not. + if not isinstance(item, tuple): + raise InvalidArgumentsError(f"Unexpected argument of type {type(item)}.") + if len(item) > 2: + raise InvalidArgumentsError(f"Unexpected argument {item[2]}.") + + def _get_from_tuple(cls, item: Tuple[Any, ...]) -> Tuple[Shape, DType]: + # Return the Shape Expression and DType from a tuple. + shape = cls._get_shape(item[0]) + dtype = cls._get_dtype(item[1]) + return shape, dtype + + def _get_shape(cls, dtype_candidate: Any) -> Shape: + if dtype_candidate is Any or dtype_candidate is Shape: + shape = Any + elif issubclass(dtype_candidate, Shape): + shape = dtype_candidate + elif cls._is_literal_like(dtype_candidate): + shape_expression = dtype_candidate.__args__[0] + shape = Shape[shape_expression] + else: + raise InvalidArgumentsError( + f"Unexpected argument '{dtype_candidate}', expecting" + " Shape[]" + " or Literal[]" + " or typing.Any." + ) + return shape + + def _get_dtype(cls, dtype_candidate: Any) -> DType: + is_dtype = isinstance(dtype_candidate, type) and issubclass( + dtype_candidate, np.generic + ) + if dtype_candidate is Any: + dtype = Any + elif is_dtype: + dtype = dtype_candidate + elif issubclass(dtype_candidate, Structure): + dtype = dtype_candidate + check_type_names(dtype, dtype_per_name) + elif cls._is_literal_like(dtype_candidate): + structure_expression = dtype_candidate.__args__[0] + dtype = Structure[structure_expression] + check_type_names(dtype, dtype_per_name) + else: + raise InvalidArgumentsError( + f"Unexpected argument '{dtype_candidate}', expecting" + " Structure[]" + " or Literal[]" + " or a dtype" + " or typing.Any." + ) + return dtype + + def _dtype_to_str(cls, dtype: Any) -> str: + if dtype is Any: + result = "Any" + elif issubclass(dtype, Structure): + result = str(dtype) + else: + result = name_per_dtype[dtype] + return result + + def _shape_expression_to_str(cls, shape_expression: Any) -> str: + return "Any" if shape_expression is Any else str(shape_expression) + + +class NDArray(NPTypingType, ABC, metaclass=NDArrayMeta): + """ + An nptyping equivalent of numpy ndarray. + + ## No arguments means an NDArray with any DType and any shape. + >>> NDArray + NDArray[Any, Any] + + ## You can provide a DType and a Shape Expression. + >>> from nptyping import Int32, Shape + >>> NDArray[Shape["2, 2"], Int32] + NDArray[Shape['2, 2'], Int32] + + ## Instance checking can be done and the shape is also checked. + >>> import numpy as np + >>> isinstance(np.array([[1, 2], [3, 4]]), NDArray[Shape['2, 2'], Int32]) + True + >>> isinstance(np.array([[1, 2], [3, 4], [5, 6]]), NDArray[Shape['2, 2'], Int32]) + False + + """ + + __args__ = (Any, Any) diff --git a/src/numpydantic/vendor/nptyping/nptyping_type.py b/src/numpydantic/vendor/nptyping/nptyping_type.py new file mode 100644 index 0000000..35d4897 --- /dev/null +++ b/src/numpydantic/vendor/nptyping/nptyping_type.py @@ -0,0 +1,30 @@ +""" +MIT License + +Copyright (c) 2023 Ramon Hagenaars + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" +from abc import ABC + + +class NPTypingType(ABC): + """ + Baseclass for all nptyping types. + """ diff --git a/src/numpydantic/vendor/nptyping/package_info.py b/src/numpydantic/vendor/nptyping/package_info.py new file mode 100644 index 0000000..ee15ac8 --- /dev/null +++ b/src/numpydantic/vendor/nptyping/package_info.py @@ -0,0 +1,37 @@ +""" +MIT License + +Copyright (c) 2023 Ramon Hagenaars + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" +__title__ = "nptyping" +__version__ = "2.5.0" +__author__ = "Ramon Hagenaars" +__author_email__ = "ramon.hagenaars@gmail.com" +__description__ = "Type hints for NumPy." +__url__ = "https://github.com/ramonhagenaars/nptyping" +__license__ = "MIT" +__python_versions__ = [ + "3.7", + "3.8", + "3.9", + "3.10", + "3.11", +] diff --git a/src/numpydantic/vendor/nptyping/pandas_/__init__.py b/src/numpydantic/vendor/nptyping/pandas_/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/numpydantic/vendor/nptyping/pandas_/dataframe.py b/src/numpydantic/vendor/nptyping/pandas_/dataframe.py new file mode 100644 index 0000000..e108b27 --- /dev/null +++ b/src/numpydantic/vendor/nptyping/pandas_/dataframe.py @@ -0,0 +1,139 @@ +""" +MIT License + +Copyright (c) 2023 Ramon Hagenaars + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" +import inspect +from abc import ABC +from typing import Any, Tuple + +import numpy as np + +from nptyping import InvalidArgumentsError +from nptyping.base_meta_classes import ( + FinalMeta, + ImmutableMeta, + InconstructableMeta, + MaybeCheckableMeta, + PrintableMeta, + SubscriptableMeta, +) +from nptyping.error import DependencyError +from nptyping.nptyping_type import NPTypingType +from nptyping.pandas_.typing_ import dtype_per_name +from nptyping.structure import Structure +from nptyping.structure_expression import check_structure + +try: + import pandas as pd +except ImportError: # pragma: no cover + pd = None # type: ignore[misc, assignment] + + +class DataFrameMeta( + SubscriptableMeta, + InconstructableMeta, + ImmutableMeta, + FinalMeta, + MaybeCheckableMeta, + PrintableMeta, + implementation="DataFrame", +): + """ + Metaclass that is coupled to nptyping.DataFrame. It contains all actual logic + such as instance checking. + """ + + __args__: Tuple[Structure] + _parameterized: bool + + def __instancecheck__( # pylint: disable=bad-mcs-method-argument + self, instance: Any + ) -> bool: + structure = self.__args__[0] + + if pd is None: + raise DependencyError( # pragma: no cover + "Pandas needs to be installed for instance checking. Use `pip " + "install nptyping[pandas]` or `pip install nptyping[complete]`" + ) + + if not isinstance(instance, pd.DataFrame): + return False + + if structure is Any: + return True + + structured_dtype = np.dtype( + [(column, dtype.str) for column, dtype in instance.dtypes.items()] + ) + return check_structure(structured_dtype, structure, dtype_per_name) + + def _get_item(cls, item: Any) -> Tuple[Structure]: + if item is Any: + return (Any,) + cls._check_item(item) + return (Structure[getattr(item, "__args__")[0]],) + + def __str__(cls) -> str: + structure = cls.__args__[0] + structure_str = "Any" if structure is Any else structure.__args__[0] + return f"{cls.__name__}[{structure_str}]" + + def __repr__(cls) -> str: + structure = cls.__args__[0] + structure_str = "Any" if structure is Any else structure + return f"{cls.__name__}[{structure_str}]" + + @property + def __module__(cls) -> str: + return cls._get_module(inspect.stack(), "nptyping.pandas_.dataframe") + + def _check_item(cls, item: Any) -> None: + # Check if the item is what we expect and raise if it is not. + if not hasattr(item, "__args__"): + raise InvalidArgumentsError(f"Unexpected argument of type {type(item)}.") + + +class DataFrame(NPTypingType, ABC, metaclass=DataFrameMeta): + """ + An nptyping equivalent of pandas DataFrame. + + ## No arguments means a DataFrame of any structure. + >>> DataFrame + DataFrame[Any] + + ## You can use Structure Expression. + >>> from nptyping import DataFrame, Structure + >>> DataFrame[Structure["x: Int, y: Int"]] + DataFrame[Structure['[x, y]: Int']] + + ## Instance checking can be done and the structure is also checked. + >>> import pandas as pd + >>> df = pd.DataFrame({'x': [1, 2, 3], 'y': [4., 5., 6.]}) + >>> isinstance(df, DataFrame[Structure['x: Int, y: Float']]) + True + >>> isinstance(df, DataFrame[Structure['x: Float, y: Int']]) + False + + """ + + __args__ = (Any,) diff --git a/src/numpydantic/vendor/nptyping/pandas_/dataframe.pyi b/src/numpydantic/vendor/nptyping/pandas_/dataframe.pyi new file mode 100644 index 0000000..edab03f --- /dev/null +++ b/src/numpydantic/vendor/nptyping/pandas_/dataframe.pyi @@ -0,0 +1,27 @@ +""" +MIT License + +Copyright (c) 2023 Ramon Hagenaars + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + +import pandas as pd + +DataFrame = pd.DataFrame diff --git a/src/numpydantic/vendor/nptyping/pandas_/typing_.py b/src/numpydantic/vendor/nptyping/pandas_/typing_.py new file mode 100644 index 0000000..29f2188 --- /dev/null +++ b/src/numpydantic/vendor/nptyping/pandas_/typing_.py @@ -0,0 +1,33 @@ +""" +MIT License + +Copyright (c) 2023 Ramon Hagenaars + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" +from nptyping.typing_ import Object +from nptyping.typing_ import dtype_per_name as dtype_per_name_default + +dtype_per_name = { + **dtype_per_name_default, # type: ignore[arg-type] + # Override the `String` and `Str` to point to `Object`. Pandas uses Object + # for string types in Dataframes and Series. + "String": Object, + "Str": Object, +} diff --git a/src/numpydantic/vendor/nptyping/py.typed b/src/numpydantic/vendor/nptyping/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/src/numpydantic/vendor/nptyping/recarray.py b/src/numpydantic/vendor/nptyping/recarray.py new file mode 100644 index 0000000..b3bbbc5 --- /dev/null +++ b/src/numpydantic/vendor/nptyping/recarray.py @@ -0,0 +1,78 @@ +""" +MIT License + +Copyright (c) 2023 Ramon Hagenaars + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" +import inspect +from typing import Any, Tuple + +import numpy as np + +from nptyping.error import InvalidArgumentsError +from nptyping.ndarray import NDArray, NDArrayMeta +from nptyping.structure import Structure +from nptyping.typing_ import DType + + +class RecArrayMeta(NDArrayMeta, implementation="RecArray"): + """ + Metaclass that is coupled to nptyping.RecArray. It takes most of its logic + from NDArrayMeta. + """ + + def _get_item(cls, item: Any) -> Tuple[Any, ...]: + cls._check_item(item) + shape, dtype = cls._get_from_tuple(item) + return shape, dtype + + def _get_dtype(cls, dtype_candidate: Any) -> DType: + if not issubclass(dtype_candidate, Structure) and dtype_candidate is not Any: + raise InvalidArgumentsError( + f"Unexpected argument {dtype_candidate}. Expecting a Structure." + ) + return dtype_candidate + + @property + def __module__(cls) -> str: + return cls._get_module(inspect.stack(), "nptyping.recarray") + + def __instancecheck__( # pylint: disable=bad-mcs-method-argument + self, instance: Any + ) -> bool: + return isinstance(instance, np.recarray) and NDArrayMeta.__instancecheck__( + self, instance + ) + + +class RecArray(NDArray, metaclass=RecArrayMeta): + """ + An nptyping equivalent of numpy recarray. + + ## RecArrays can take a Shape and must take a Structure + >>> from nptyping import Shape, Structure + >>> RecArray[Shape["2, 2"], Structure["x: Float, y: Float"]] + RecArray[Shape['2, 2'], Structure['[x, y]: Float']] + + ## Or Any + >>> from typing import Any + >>> RecArray[Shape["2, 2"], Any] + RecArray[Shape['2, 2'], Any] + """ diff --git a/src/numpydantic/vendor/nptyping/recarray.pyi b/src/numpydantic/vendor/nptyping/recarray.pyi new file mode 100644 index 0000000..4e67f8a --- /dev/null +++ b/src/numpydantic/vendor/nptyping/recarray.pyi @@ -0,0 +1,27 @@ +""" +MIT License + +Copyright (c) 2023 Ramon Hagenaars + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + +import numpy as np + +RecArray = np.recarray diff --git a/src/numpydantic/vendor/nptyping/shape.py b/src/numpydantic/vendor/nptyping/shape.py new file mode 100644 index 0000000..5dc05f5 --- /dev/null +++ b/src/numpydantic/vendor/nptyping/shape.py @@ -0,0 +1,75 @@ +""" +MIT License + +Copyright (c) 2023 Ramon Hagenaars + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" +from abc import ABC +from typing import Any, Dict + +from nptyping.base_meta_classes import ContainerMeta +from nptyping.nptyping_type import NPTypingType +from nptyping.shape_expression import ( + get_dimensions, + normalize_shape_expression, + remove_labels, + validate_shape_expression, +) + + +class ShapeMeta(ContainerMeta, implementation="Shape"): + """ + Metaclass that is coupled to nptyping.Shape. + """ + + def _validate_expression(cls, item: str) -> None: + validate_shape_expression(item) + + def _normalize_expression(cls, item: str) -> str: + return normalize_shape_expression(item) + + def _get_additional_values(cls, item: Any) -> Dict[str, Any]: + dim_strings = get_dimensions(item) + dim_string_without_labels = remove_labels(dim_strings) + return {"prepared_args": dim_string_without_labels} + + +class Shape(NPTypingType, ABC, metaclass=ShapeMeta): + """ + A container for shape expressions that describe the shape of an multi + dimensional array. + + Simple example: + + >>> Shape['2, 2'] + Shape['2, 2'] + + A Shape can be compared to a typing.Literal. You can use Literals in + NDArray as well. + + >>> from typing import Literal + + >>> Shape['2, 2'] == Literal['2, 2'] + True + + """ + + __args__ = ("*, ...",) + prepared_args = "*, ..." diff --git a/src/numpydantic/vendor/nptyping/shape.pyi b/src/numpydantic/vendor/nptyping/shape.pyi new file mode 100644 index 0000000..96d2eb6 --- /dev/null +++ b/src/numpydantic/vendor/nptyping/shape.pyi @@ -0,0 +1,36 @@ +""" +MIT License + +Copyright (c) 2023 Ramon Hagenaars + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" +try: + from typing import Literal # type: ignore[attr-defined] +except ImportError: + from typing_extensions import Literal # type: ignore[attr-defined,misc,assignment] + +from typing import Any, cast + +# For MyPy: +Shape = cast(Literal, Shape) # type: ignore[has-type,misc,valid-type] + +# For PyRight: +class Shape: # type: ignore[no-redef] + def __class_getitem__(cls, item: Any) -> Any: ... diff --git a/src/numpydantic/vendor/nptyping/shape_expression.py b/src/numpydantic/vendor/nptyping/shape_expression.py new file mode 100644 index 0000000..bcaf79b --- /dev/null +++ b/src/numpydantic/vendor/nptyping/shape_expression.py @@ -0,0 +1,190 @@ +""" +MIT License + +Copyright (c) 2023 Ramon Hagenaars + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" +import re +import string +from functools import lru_cache +from typing import ( + TYPE_CHECKING, + Any, + Dict, + List, + Union, +) + +from nptyping.error import InvalidShapeError +from nptyping.typing_ import ShapeExpression, ShapeTuple + +if TYPE_CHECKING: + from nptyping.shape import Shape # pragma: no cover + + +@lru_cache() +def check_shape(shape: ShapeTuple, target: "Shape") -> bool: + """ + Check whether the given shape corresponds to the given shape_expression. + :param shape: the shape in question. + :param target: the shape expression to which shape is tested. + :return: True if the given shape corresponds to shape_expression. + """ + target_shape = _handle_ellipsis(shape, target.prepared_args) + return _check_dimensions_against_shape(shape, target_shape) + + +def validate_shape_expression(shape_expression: Union[ShapeExpression, Any]) -> None: + """ + Validate shape_expression and raise an InvalidShapeError if it is not + considered valid. + :param shape_expression: the shape expression to validate. + :return: None. + """ + shape_expression_no_quotes = shape_expression.replace("'", "").replace('"', "") + if shape_expression is not Any and not re.match( + _REGEX_SHAPE_EXPRESSION, shape_expression_no_quotes + ): + raise InvalidShapeError( + f"'{shape_expression}' is not a valid shape expression." + ) + + +def normalize_shape_expression(shape_expression: ShapeExpression) -> ShapeExpression: + """ + Normalize the given shape expression, e.g. by removing whitespaces, making + similar expressions look the same. + :param shape_expression: the shape expression that is to be normalized. + :return: a normalized shape expression. + """ + shape_expression = shape_expression.replace("'", "").replace('"', "") + # Replace whitespaces right before labels with $. + shape_expression = re.sub(rf"\s*{_REGEX_LABEL}", r"$\1", shape_expression) + # Let all commas be followed by a $. + shape_expression = shape_expression.replace(",", ",$") + # Remove all whitespaces left. + shape_expression = re.sub(r"\s*", "", shape_expression) + # Remove $ right after a bracket. + shape_expression = re.sub(r"\[\$+", "[", shape_expression) + # Replace $ with a single space. + shape_expression = re.sub(r"\$+", " ", shape_expression) + return shape_expression + + +def get_dimensions(shape_expression: str) -> List[str]: + """ + Find all "break downs" (the parts between brackets) in a shape expressions + and replace them with mere dimension sizes. + + :param shape_expression: the shape expression that gets the break downs replaced. + :return: a list of dimensions without break downs. + """ + shape_expression_without_breakdowns = shape_expression + for dim_breakdown in re.findall( + r"(\[[^\]]+\])", shape_expression_without_breakdowns + ): + dim_size = len(dim_breakdown.split(",")) + shape_expression_without_breakdowns = ( + shape_expression_without_breakdowns.replace(dim_breakdown, str(dim_size)) + ) + return shape_expression_without_breakdowns.split(",") + + +def remove_labels(dimensions: List[str]) -> List[str]: + """ + Remove all labels (words that start with a lowercase). + + :param dimensions: a list of dimensions. + :return: a copy of the given list without labels. + """ + return [re.sub(r"\b[a-z]\w*", "", dim).strip() for dim in dimensions] + + +def _check_dimensions_against_shape(shape: ShapeTuple, target: List[str]) -> bool: + # Walk through the shape and test them against the given target, + # taking into consideration variables, wildcards, etc. + + if len(shape) != len(target): + return False + shape_as_strings = (str(dim) for dim in shape) + variables: Dict[str, str] = {} + for dim, target_dim in zip(shape_as_strings, target): + if _is_wildcard(target_dim) or _is_assignable_var(dim, target_dim, variables): + continue + if dim != target_dim: + return False + return True + + +def _handle_ellipsis(shape: ShapeTuple, target: List[str]) -> List[str]: + # Let the ellipsis allows for any number of dimensions by replacing the + # ellipsis with the dimension size repeated the number of times that + # corresponds to the shape of the instance. + if target[-1] == "...": + dim_to_repeat = target[-2] + target = target[0:-1] + if len(shape) > len(target): + difference = len(shape) - len(target) + target += difference * [dim_to_repeat] + return target + + +def _is_assignable_var(dim: str, target_dim: str, variables: Dict[str, str]) -> bool: + # Return whether target_dim is a variable and can be assigned with dim. + return _is_variable(target_dim) and _can_assign_variable(dim, target_dim, variables) + + +def _is_variable(dim: str) -> bool: + # Return whether dim is a variable. + return dim[0] in string.ascii_uppercase + + +def _can_assign_variable(dim: str, target_dim: str, variables: Dict[str, str]) -> bool: + # Check and assign a variable. + assignable = variables.get(target_dim) in (None, dim) + variables[target_dim] = dim + return assignable + + +def _is_wildcard(dim: str) -> bool: + # Return whether dim is a wildcard (i.e. the character that takes any + # dimension size). + return dim == "*" + + +_REGEX_SEPARATOR = r"(\s*,\s*)" +_REGEX_DIMENSION_SIZE = r"(\s*[0-9]+\s*)" +_REGEX_VARIABLE = r"(\s*\b[A-Z]\w*\s*)" +_REGEX_LABEL = r"(\s*\b[a-z]\w*\s*)" +_REGEX_LABELS = rf"({_REGEX_LABEL}({_REGEX_SEPARATOR}{_REGEX_LABEL})*)" +_REGEX_WILDCARD = r"(\s*\*\s*)" +_REGEX_DIMENSION_BREAKDOWN = rf"(\s*\[{_REGEX_LABELS}\]\s*)" +_REGEX_DIMENSION = ( + rf"({_REGEX_DIMENSION_SIZE}" + rf"|{_REGEX_VARIABLE}" + rf"|{_REGEX_WILDCARD}" + rf"|{_REGEX_DIMENSION_BREAKDOWN})" +) +_REGEX_DIMENSION_WITH_LABEL = rf"({_REGEX_DIMENSION}(\s+{_REGEX_LABEL})*)" +_REGEX_DIMENSIONS = ( + rf"{_REGEX_DIMENSION_WITH_LABEL}({_REGEX_SEPARATOR}{_REGEX_DIMENSION_WITH_LABEL})*" +) +_REGEX_DIMENSIONS_ELLIPSIS = rf"({_REGEX_DIMENSIONS}{_REGEX_SEPARATOR}\.\.\.\s*)" +_REGEX_SHAPE_EXPRESSION = rf"^({_REGEX_DIMENSIONS}|{_REGEX_DIMENSIONS_ELLIPSIS})$" diff --git a/src/numpydantic/vendor/nptyping/structure.py b/src/numpydantic/vendor/nptyping/structure.py new file mode 100644 index 0000000..0b95dfb --- /dev/null +++ b/src/numpydantic/vendor/nptyping/structure.py @@ -0,0 +1,107 @@ +""" +MIT License + +Copyright (c) 2023 Ramon Hagenaars + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" +from abc import ABC +from typing import ( + Any, + Dict, + List, +) + +from nptyping.base_meta_classes import ContainerMeta +from nptyping.nptyping_type import NPTypingType +from nptyping.structure_expression import ( + create_name_to_type_dict, + normalize_structure_expression, + validate_structure_expression, +) + + +class StructureMeta(ContainerMeta, implementation="Structure"): + """ + Metaclass that is coupled to nptyping.Structure. + """ + + __args__ = tuple() + + def _validate_expression(cls, item: str) -> None: + validate_structure_expression(item) + + def _normalize_expression(cls, item: str) -> str: + return normalize_structure_expression(item) + + def _get_additional_values(cls, item: Any) -> Dict[str, Any]: + return { + "_type_per_name": create_name_to_type_dict(item), + "_has_wildcard": item.replace(" ", "").endswith(",*"), + } + + +class Structure(NPTypingType, ABC, metaclass=StructureMeta): + """ + A container for structure expressions that describe the structured dtype of + an array. + + Simple example: + + >>> Structure["x: Float, y: Float"] + Structure['[x, y]: Float'] + + """ + + _type_per_name = {} + _has_wildcard = False + + @classmethod + def has_wildcard(cls) -> bool: + """ + Returns whether this Structure has a wildcard for any other columns. + :return: True if this Structure expresses "any other columns". + """ + return cls._has_wildcard + + @classmethod + def get_types(cls) -> List[str]: + """ + Return a list of all types (strings) in this Structure. + :return: a list of all types in this Structure. + """ + return list(set(cls._type_per_name.values())) + + @classmethod + def get_names(cls) -> List[str]: + """ + Return a list of all names in this Structure. + :return: a list of all names in this Structure. + """ + return list(cls._type_per_name.keys()) + + @classmethod + def get_type(cls, name: str) -> str: + """ + Get the type (str) that corresponds to the given name. For example for + Structure["x: Float"], get_type("x") would give "Float". + :param name: the name of which the type is to be returned. + :return: the type as a string that corresponds to that name. + """ + return cls._type_per_name[name] diff --git a/src/numpydantic/vendor/nptyping/structure.pyi b/src/numpydantic/vendor/nptyping/structure.pyi new file mode 100644 index 0000000..415cf4e --- /dev/null +++ b/src/numpydantic/vendor/nptyping/structure.pyi @@ -0,0 +1,38 @@ +""" +MIT License + +Copyright (c) 2023 Ramon Hagenaars + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" +try: + from typing import Literal # type: ignore[attr-defined] +except ImportError: + from typing_extensions import Literal # type: ignore[attr-defined,misc,assignment] + +from typing import Any, cast + +import numpy as np + +# For MyPy: +Structure = cast(Literal, Structure) # type: ignore[has-type,misc,valid-type] + +# For PyRight: +class Structure(np.dtype[Any]): # type: ignore[no-redef,misc] + def __class_getitem__(cls, item: Any) -> Any: ... diff --git a/src/numpydantic/vendor/nptyping/structure_expression.py b/src/numpydantic/vendor/nptyping/structure_expression.py new file mode 100644 index 0000000..ee1236c --- /dev/null +++ b/src/numpydantic/vendor/nptyping/structure_expression.py @@ -0,0 +1,339 @@ +""" +MIT License + +Copyright (c) 2023 Ramon Hagenaars + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" +import re +from collections import Counter, defaultdict +from difflib import get_close_matches +from typing import ( + TYPE_CHECKING, + Any, + Dict, + Generator, + List, + Mapping, + Tuple, + Type, + Union, +) + +import numpy as np + +from nptyping.error import InvalidShapeError, InvalidStructureError +from nptyping.shape import Shape +from nptyping.shape_expression import ( + check_shape, + normalize_shape_expression, + validate_shape_expression, +) +from nptyping.typing_ import StructureExpression + +if TYPE_CHECKING: + from nptyping.structure import Structure # pragma: no cover + + +def validate_structure_expression( + structure_expression: Union[StructureExpression, Any] +) -> None: + """ + Validate the given structure_expression and raise an InvalidStructureError + if it is deemed invalid. + :param structure_expression: the structure expression in question. + :return: None. + """ + if structure_expression is not Any: + if not re.match(_REGEX_STRUCTURE_EXPRESSION, structure_expression): + raise InvalidStructureError( + f"'{structure_expression}' is not a valid structure expression." + ) + _validate_structure_expression_contains_no_multiple_field_names( + structure_expression + ) + _validate_sub_array_expressions(structure_expression) + + +def check_structure( + structured_dtype: np.dtype, # type: ignore[type-arg] + target: "Structure", + type_per_name: Dict[str, type], +) -> bool: + """ + Check the given structured_dtype against the given target Structure and + return whether it corresponds (True) or not (False). The given dictionary + contains the vocabulary context for the check. + :param structured_dtype: the dtype in question. + :param target: the target Structure that is checked against. + :param type_per_name: a dict that holds the types by their names as they + occur in a structure expression. + :return: True if the given dtype is valid with the given target. + """ + fields: Mapping[str, Any] = structured_dtype.fields or {} # type: ignore[assignment] + + # Add the wildcard to the lexicon. We want to do this here to keep + # knowledge on wildcards in one place (this module). + type_per_name_with_wildcard: Dict[str, type] = { + **type_per_name, + "*": object, + } # type: ignore[arg-type] + + if target.has_wildcard(): + # Check from the Target's perspective. All fields in the Target should be + # in the subject. + def iterator() -> Generator[Tuple[str, Tuple[np.dtype, int]], None, None]: # type: ignore[type-arg] # pylint: disable=line-too-long + for name_ in target.get_names(): + yield name_, fields.get(name_) # type: ignore[misc] + + else: + # Check from the subject's perspective. All fields in the subject + # should be in the target. + if set(target.get_names()) != set(fields.keys()): + return False + + def iterator() -> Generator[Tuple[str, Tuple[np.dtype, int]], None, None]: # type: ignore[type-arg] # pylint: disable=line-too-long + for name_, dtype_tuple_ in fields.items(): + yield name_, dtype_tuple_ # type: ignore[misc] + + for name, dtype_tuple in iterator(): + field_in_target_not_in_subject = dtype_tuple is None + if field_in_target_not_in_subject or not _check_structure_field( + name, dtype_tuple, target, type_per_name_with_wildcard + ): + return False + return True + + +def _check_structure_field( + name: str, + dtype_tuple: Tuple[np.dtype, int], # type: ignore[type-arg] + target: "Structure", + type_per_name_with_wildcard: Dict[str, type], +) -> bool: + dtype = dtype_tuple[0] + target_type_name = target.get_type(name) + target_type_shape_match = re.search(_REGEX_FIELD_SHAPE, target_type_name) + actual_type = dtype.type + if target_type_shape_match: + if not dtype.subdtype: + # the dtype does not contain a shape. + return False + actual_type = dtype.subdtype[0].type + target_type_shape = target_type_shape_match.group(1) + shape_corresponds = check_shape(dtype.shape, Shape[target_type_shape]) + if not shape_corresponds: + return False + target_type_name = target_type_name.replace( + target_type_shape_match.group(0), "" + ) + check_type_name(target_type_name, type_per_name_with_wildcard) + target_type = type_per_name_with_wildcard[target_type_name] + return issubclass(actual_type, target_type) + + +def check_type_names( + structure: "Structure", type_per_name: Dict[str, Type[object]] +) -> None: + """ + Check the given structure for any invalid type names in the given context + of type_per_name. Raises an InvalidStructureError if a type name is + invalid. + :param structure: the Structure that is checked. + :param type_per_name: the context that determines which type names are valid. + :return: None. + """ + for type_ in structure.get_types(): + check_type_name(type_, type_per_name) + + +def check_type_name(type_name: str, type_per_name: Dict[str, Type[object]]) -> None: + """ + Check if the given type_name is in type_per_name and raise a meaningful + error if not. + :param type_name: the key that is checked to be in type_per_name. + :param type_per_name: a dict that is looked in for type_name. + :return: None. + """ + # Remove any subarray stuff here. + type_name = type_name.split("[")[0] + if type_name not in type_per_name: + close_matches = get_close_matches( + type_name, type_per_name.keys(), 3, cutoff=0.4 + ) + close_matches_str = ", ".join(f"'{match}'" for match in close_matches) + extra_help = "" + if len(close_matches) > 1: + extra_help = f" Did you mean one of {close_matches_str}?" + elif close_matches: + extra_help = f" Did you mean {close_matches_str}?" + raise InvalidStructureError( # pylint: disable=raise-missing-from + f"Type '{type_name}' is not valid in this context.{extra_help}" + ) + + +def normalize_structure_expression( + structure_expression: StructureExpression, +) -> StructureExpression: + """ + Normalize the given structure expression, e.g. by removing whitespaces, + making similar expressions look the same. + :param structure_expression: the structure expression that is to be normalized. + :return: a normalized structure expression. + """ + structure_expression = re.sub(r"\s*", "", structure_expression) + type_to_names_dict = _create_type_to_names_dict(structure_expression) + normalized_structure_expression = _type_to_names_dict_to_str(type_to_names_dict) + result = normalized_structure_expression.replace(",", ", ").replace(" ", " ") + has_wildcard_end = structure_expression.replace(" ", "").endswith(",*") + if has_wildcard_end: + result += ", *" + return result + + +def create_name_to_type_dict( + structure_expression: StructureExpression, +) -> Dict[str, str]: + """ + Create a dict with a name as key and a type (str) as value from the given + structure expression. Structure["x: Int, y: Float"] would yield + {"x: "Int", "y": "Float"}. + :param structure_expression: the structure expression from which the dict + is extracted. + :return: a dict with names and their types, both as strings. + """ + type_to_names_dict = _create_type_to_names_dict(structure_expression) + return { + name.strip(): type_.strip() + for type_, names in type_to_names_dict.items() + for name in names + } + + +def _validate_structure_expression_contains_no_multiple_field_names( + structure_expression: StructureExpression, +) -> None: + # Validate that there are not multiple occurrences of the same field names. + matches = re.findall(_REGEX_FIELD, re.sub(r"\s*", "", structure_expression)) + field_name_combinations = [match[0].split(":")[0] for match in matches] + field_names: List[str] = [] + for field_name_combination in field_name_combinations: + field_name_combination_match = re.match( + _REGEX_FIELD_NAMES_COMBINATION, field_name_combination + ) + if field_name_combination_match: + field_names += field_name_combination_match.group(2).split(_SEPARATOR) + else: + field_names.append(field_name_combination) + field_name_counter = Counter(field_names) + field_names_occurring_multiple_times = [ + field_name for field_name, amount in field_name_counter.items() if amount > 1 + ] + if field_names_occurring_multiple_times: + # If there are multiple, just raise about the first. Otherwise the + # error message gets bloated. + field_name_under_fire = field_names_occurring_multiple_times[0] + raise InvalidStructureError( + f"Field names may occur only once in a structure expression." + f" Field name '{field_name_under_fire}' occurs" + f" {field_name_counter[field_name_under_fire]} times in" + f" '{structure_expression}'." + ) + + +def _validate_sub_array_expressions(structure_expression: str) -> None: + # Validate that the given structure expression does not contain any shape + # expressions for sub arrays that are invalid. + for field_match in re.findall(_REGEX_FIELD, structure_expression): + field_type = field_match[0].split(_FIELD_TYPE_POINTER)[1] + type_shape_match = re.search(_REGEX_FIELD_SHAPE, field_type) + if type_shape_match: + type_shape = type_shape_match[1] + try: + validate_shape_expression(type_shape) + except InvalidShapeError as err: + raise InvalidStructureError( + f"'{structure_expression}' is not a valid structure" + f" expression; {str(err)}" + ) from err + + +def _create_type_to_names_dict( + structure_expression: StructureExpression, +) -> Dict[str, List[str]]: + # Create a dictionary with field names per type, sorted by type and then by + # name. + names_per_type: Dict[str, List[str]] = defaultdict(list) + for field_match in re.findall(_REGEX_FIELD, structure_expression): + field_name_combination, field_type = field_match[0].split(_FIELD_TYPE_POINTER) + field_name_combination_match = re.match( + _REGEX_FIELD_NAMES_COMBINATION, field_name_combination + ) + field_type_shape_match = re.search(_REGEX_FIELD_SHAPE, field_type) + if field_name_combination_match: + field_names = field_name_combination_match.group(2).split(_SEPARATOR) + else: + field_names = [field_name_combination] + if field_type_shape_match: + type_shape = field_type_shape_match.group(1) + normalized_type_shape = normalize_shape_expression(type_shape) + field_type = field_type.replace( + field_type_shape_match.group(0), f"[{normalized_type_shape}]" + ) + names_per_type[field_type] += field_names + return { + field_type: sorted(names_per_type[field_type]) + for field_type in sorted(names_per_type.keys()) + } + + +def _type_to_names_dict_to_str(type_to_names_dict: Dict[str, List[str]]) -> str: + # Turn the given dict into a structure expression. + field_strings = [] + for field_type, field_names in type_to_names_dict.items(): + field_names_joined = f"{_SEPARATOR}".join(field_names) + if len(field_names) > 1: + field_names_joined = f"[{field_names_joined}]" + field_strings.append(f"{field_names_joined}{_FIELD_TYPE_POINTER} {field_type}") + return f"{_SEPARATOR}".join(field_strings) + + +_SEPARATOR = "," +_FIELD_TYPE_POINTER = ":" +_REGEX_SEPARATOR = rf"(\s*{_SEPARATOR}\s*)" +_REGEX_FIELD_NAME = r"(\s*[a-zA-Z]\w*\s*)" +_REGEX_FIELD_NAMES = rf"({_REGEX_FIELD_NAME}({_REGEX_SEPARATOR}{_REGEX_FIELD_NAME})+)" +_REGEX_FIELD_NAMES_COMBINATION = rf"(\s*\[{_REGEX_FIELD_NAMES}\]\s*)" +_REGEX_FIELD_LEFT = rf"({_REGEX_FIELD_NAME}|{_REGEX_FIELD_NAMES_COMBINATION})" +_REGEX_FIELD_TYPE = r"(\s*[a-zA-Z]\w*\s*)" +_REGEX_FIELD_TYPE_WILDCARD = r"(\s*\*\s*)" +_REGEX_FIELD_SHAPE = r"\[([^\]]+)\]" +_REGEX_FIELD_SHAPE_MAYBE = rf"\s*({_REGEX_FIELD_SHAPE})?\s*" +_REGEX_FIELD_RIGHT = ( + rf"({_REGEX_FIELD_TYPE}|{_REGEX_FIELD_TYPE_WILDCARD}){_REGEX_FIELD_SHAPE_MAYBE}" +) +_REGEX_FIELD_TYPE_POINTER = rf"(\s*{_FIELD_TYPE_POINTER}\s*)" +_REGEX_FIELD = ( + rf"(\s*{_REGEX_FIELD_LEFT}{_REGEX_FIELD_TYPE_POINTER}{_REGEX_FIELD_RIGHT}\s*)" +) +_REGEX_STRUCTURE_EXPRESSION = ( + rf"^({_REGEX_FIELD}" + rf"({_REGEX_SEPARATOR}{_REGEX_FIELD})*" + rf"({_REGEX_SEPARATOR}{_REGEX_FIELD_TYPE_WILDCARD})?)$" +) diff --git a/src/numpydantic/vendor/nptyping/typing_.py b/src/numpydantic/vendor/nptyping/typing_.py new file mode 100644 index 0000000..2639e07 --- /dev/null +++ b/src/numpydantic/vendor/nptyping/typing_.py @@ -0,0 +1,185 @@ +""" +MIT License + +Copyright (c) 2023 Ramon Hagenaars + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + +try: + from typing import ( # type: ignore[attr-defined,misc] # pylint: disable=unused-import + Literal, + TypeAlias, + TypeGuard, + final, + ) +except ImportError: # pragma: no cover + from typing_extensions import ( # type: ignore[attr-defined,misc] + Literal, + TypeAlias, + TypeGuard, + final, + ) + +from typing import Tuple, Union + +import numpy as np + +ShapeExpression: TypeAlias = str +StructureExpression: TypeAlias = str +DType: TypeAlias = Union[np.generic, StructureExpression] +ShapeTuple: TypeAlias = Tuple[int, ...] + +Number = np.number +Bool = np.bool_ +Bool8 = np.bool8 +Obj = np.object_ # Obj is a common abbreviation and should be usable. +Object = np.object_ +Object0 = np.object0 +Datetime64 = np.datetime64 +Integer = np.integer +SignedInteger = np.signedinteger +Int8 = np.int8 +Int16 = np.int16 +Int32 = np.int32 +Int64 = np.int64 +Byte = np.byte +Short = np.short +IntC = np.intc +IntP = np.intp +Int0 = np.int0 +Int = np.integer # Int should translate to the "generic" int type. +Int_ = np.int_ +LongLong = np.longlong +Timedelta64 = np.timedelta64 +UnsignedInteger = np.unsignedinteger +UInt8 = np.uint8 +UInt16 = np.uint16 +UInt32 = np.uint32 +UInt64 = np.uint64 +UByte = np.ubyte +UShort = np.ushort +UIntC = np.uintc +UIntP = np.uintp +UInt0 = np.uint0 +UInt = np.uint +ULongLong = np.ulonglong +Inexact = np.inexact +Floating = np.floating +Float16 = np.float16 +Float32 = np.float32 +Float64 = np.float64 +Half = np.half +Single = np.single +Double = np.double +Float = np.float_ +LongDouble = np.longdouble +LongFloat = np.longfloat +ComplexFloating = np.complexfloating +Complex64 = np.complex64 +Complex128 = np.complex128 +CSingle = np.csingle +SingleComplex = np.singlecomplex +CDouble = np.cdouble +Complex = np.complex_ +CFloat = np.cfloat +CLongDouble = np.clongdouble +CLongFloat = np.clongfloat +LongComplex = np.longcomplex +Flexible = np.flexible +Void = np.void +Void0 = np.void0 +Character = np.character +Bytes = np.bytes_ +Str = np.str_ +String = np.string_ +Bytes0 = np.bytes0 +Unicode = np.unicode_ +Str0 = np.str0 + +dtypes = [ + (Number, "Number"), + (Bool, "Bool"), + (Bool8, "Bool8"), + (Obj, "Obj"), + (Object, "Object"), + (Object0, "Object0"), + (Datetime64, "Datetime64"), + (Integer, "Integer"), + (SignedInteger, "SignedInteger"), + (Int8, "Int8"), + (Int16, "Int16"), + (Int32, "Int32"), + (Int64, "Int64"), + (Byte, "Byte"), + (Short, "Short"), + (IntC, "IntC"), + (IntP, "IntP"), + (Int0, "Int0"), + (Int, "Int"), + (LongLong, "LongLong"), + (Timedelta64, "Timedelta64"), + (UnsignedInteger, "UnsignedInteger"), + (UInt8, "UInt8"), + (UInt16, "UInt16"), + (UInt32, "UInt32"), + (UInt64, "UInt64"), + (UByte, "UByte"), + (UShort, "UShort"), + (UIntC, "UIntC"), + (UIntP, "UIntP"), + (UInt0, "UInt0"), + (UInt, "UInt"), + (ULongLong, "ULongLong"), + (Inexact, "Inexact"), + (Floating, "Floating"), + (Float16, "Float16"), + (Float32, "Float32"), + (Float64, "Float64"), + (Half, "Half"), + (Single, "Single"), + (Double, "Double"), + (Float, "Float"), + (LongDouble, "LongDouble"), + (LongFloat, "LongFloat"), + (ComplexFloating, "ComplexFloating"), + (Complex64, "Complex64"), + (Complex128, "Complex128"), + (CSingle, "CSingle"), + (SingleComplex, "SingleComplex"), + (CDouble, "CDouble"), + (Complex, "Complex"), + (CFloat, "CFloat"), + (CLongDouble, "CLongDouble"), + (CLongFloat, "CLongFloat"), + (LongComplex, "LongComplex"), + (Flexible, "Flexible"), + (Void, "Void"), + (Void0, "Void0"), + (Character, "Character"), + (Bytes, "Bytes"), + (String, "String"), + (Str, "Str"), + (Bytes0, "Bytes0"), + (Unicode, "Unicode"), + (Str0, "Str0"), +] + +name_per_dtype = dict(dtypes) +dtype_per_name = {name: dtype for dtype, name in dtypes} diff --git a/src/numpydantic/vendor/nptyping/typing_.pyi b/src/numpydantic/vendor/nptyping/typing_.pyi new file mode 100644 index 0000000..fcf83ce --- /dev/null +++ b/src/numpydantic/vendor/nptyping/typing_.pyi @@ -0,0 +1,114 @@ +""" +MIT License + +Copyright (c) 2023 Ramon Hagenaars + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + +try: + from typing import ( # type: ignore[attr-defined] # pylint: disable=unused-import + Dict, + TypeAlias, + ) +except ImportError: # pragma: no cover + from typing_extensions import ( + TypeAlias, + ) + +from typing import ( + Any, + Tuple, + Union, +) + +import numpy as np + +ShapeExpression: TypeAlias = str +StructureExpression: TypeAlias = str +DType: TypeAlias = Union[np.generic, StructureExpression] +ShapeTuple: TypeAlias = Tuple[int, ...] + +Number: TypeAlias = np.dtype[np.number[Any]] +Bool: TypeAlias = np.dtype[np.bool_] +Bool8: TypeAlias = np.dtype[np.bool8] +Object: TypeAlias = np.dtype[np.object_] +Object0: TypeAlias = np.dtype[np.object0] +Datetime64: TypeAlias = np.dtype[np.datetime64] +Integer: TypeAlias = np.dtype[np.integer[Any]] +SignedInteger: TypeAlias = np.dtype[np.signedinteger[Any]] +Int8: TypeAlias = np.dtype[np.int8] +Int16: TypeAlias = np.dtype[np.int16] +Int32: TypeAlias = np.dtype[np.int32] +Int64: TypeAlias = np.dtype[np.int64] +Byte: TypeAlias = np.dtype[np.byte] +Short: TypeAlias = np.dtype[np.short] +IntC: TypeAlias = np.dtype[np.intc] +IntP: TypeAlias = np.dtype[np.intp] +Int0: TypeAlias = np.dtype[np.int0] +Int: TypeAlias = np.dtype[np.int_] +LongLong: TypeAlias = np.dtype[np.longlong] +Timedelta64: TypeAlias = np.dtype[np.timedelta64] +UnsignedInteger: TypeAlias = np.dtype[np.unsignedinteger[Any]] +UInt8: TypeAlias = np.dtype[np.uint8] +UInt16: TypeAlias = np.dtype[np.uint16] +UInt32: TypeAlias = np.dtype[np.uint32] +UInt64: TypeAlias = np.dtype[np.uint64] +UByte: TypeAlias = np.dtype[np.ubyte] +UShort: TypeAlias = np.dtype[np.ushort] +UIntC: TypeAlias = np.dtype[np.uintc] +UIntP: TypeAlias = np.dtype[np.uintp] +UInt0: TypeAlias = np.dtype[np.uint0] +UInt: TypeAlias = np.dtype[np.uint] +ULongLong: TypeAlias = np.dtype[np.ulonglong] +Inexact: TypeAlias = np.dtype[np.inexact[Any]] +Floating: TypeAlias = np.dtype[np.floating[Any]] +Float16: TypeAlias = np.dtype[np.float16] +Float32: TypeAlias = np.dtype[np.float32] +Float64: TypeAlias = np.dtype[np.float64] +Half: TypeAlias = np.dtype[np.half] +Single: TypeAlias = np.dtype[np.single] +Double: TypeAlias = np.dtype[np.double] +Float: TypeAlias = np.dtype[np.float_] +LongDouble: TypeAlias = np.dtype[np.longdouble] +LongFloat: TypeAlias = np.dtype[np.longfloat] +ComplexFloating: TypeAlias = np.dtype[np.complexfloating[Any, Any]] +Complex64: TypeAlias = np.dtype[np.complex64] +Complex128: TypeAlias = np.dtype[np.complex128] +CSingle: TypeAlias = np.dtype[np.csingle] +SingleComplex: TypeAlias = np.dtype[np.singlecomplex] +CDouble: TypeAlias = np.dtype[np.cdouble] +Complex: TypeAlias = np.dtype[np.complex_] +CFloat: TypeAlias = np.dtype[np.cfloat] +CLongDouble: TypeAlias = np.dtype[np.clongdouble] +CLongFloat: TypeAlias = np.dtype[np.clongfloat] +LongComplex: TypeAlias = np.dtype[np.longcomplex] +Flexible: TypeAlias = np.dtype[np.flexible] +Void: TypeAlias = np.dtype[np.void] +Void0: TypeAlias = np.dtype[np.void0] +Character: TypeAlias = np.dtype[np.character] +Bytes: TypeAlias = np.dtype[np.bytes_] +Str: TypeAlias = np.dtype[np.str_] +String: TypeAlias = np.dtype[np.string_] +Bytes0: TypeAlias = np.dtype[np.bytes0] +Unicode: TypeAlias = np.dtype[np.unicode_] +Str0: TypeAlias = np.dtype[np.str0] + +dtype_per_name: Dict[str, np.dtype[Any]] +name_per_dtype: Dict[np.dtype[Any], str] From dba550f41b45b19936ed6ddccab535de36014643 Mon Sep 17 00:00:00 2001 From: sneakers-the-rat Date: Wed, 31 Jul 2024 13:15:17 -0700 Subject: [PATCH 02/15] lint and format nptyping --- src/numpydantic/vendor/__init__.py | 10 ++++++++++ src/numpydantic/vendor/nptyping/__init__.py | 1 + .../vendor/nptyping/assert_isinstance.py | 1 + .../vendor/nptyping/base_meta_classes.py | 15 +++++++------- src/numpydantic/vendor/nptyping/ndarray.py | 2 +- .../vendor/nptyping/nptyping_type.py | 3 ++- .../vendor/nptyping/package_info.py | 1 + .../vendor/nptyping/pandas_/__init__.py | 3 +++ .../vendor/nptyping/pandas_/dataframe.py | 4 ++-- .../vendor/nptyping/pandas_/typing_.py | 1 + src/numpydantic/vendor/nptyping/recarray.py | 2 +- src/numpydantic/vendor/nptyping/shape.py | 1 + src/numpydantic/vendor/nptyping/shape.pyi | 1 + .../vendor/nptyping/shape_expression.py | 3 ++- src/numpydantic/vendor/nptyping/structure.py | 1 + src/numpydantic/vendor/nptyping/structure.pyi | 1 + .../vendor/nptyping/structure_expression.py | 5 ++--- src/numpydantic/vendor/nptyping/typing_.py | 20 +++++-------------- 18 files changed, 43 insertions(+), 32 deletions(-) diff --git a/src/numpydantic/vendor/__init__.py b/src/numpydantic/vendor/__init__.py index e69de29..9af2c76 100644 --- a/src/numpydantic/vendor/__init__.py +++ b/src/numpydantic/vendor/__init__.py @@ -0,0 +1,10 @@ +""" +Vendored modules - see licenses in /licenses. + +Currently consists just of nptyping, as it is no longer maintained +and pins a version of numpy<2, and we have to do an increasing +number of awkward monkeypatches for perf and customization reasons. + +This vendored module will slowly be worked out of the code and +all its functionality replaced. +""" diff --git a/src/numpydantic/vendor/nptyping/__init__.py b/src/numpydantic/vendor/nptyping/__init__.py index 5fd5b2c..35e11cc 100644 --- a/src/numpydantic/vendor/nptyping/__init__.py +++ b/src/numpydantic/vendor/nptyping/__init__.py @@ -21,6 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ + from nptyping.assert_isinstance import assert_isinstance from nptyping.error import ( InvalidArgumentsError, diff --git a/src/numpydantic/vendor/nptyping/assert_isinstance.py b/src/numpydantic/vendor/nptyping/assert_isinstance.py index 5ae4eb1..e6d4187 100644 --- a/src/numpydantic/vendor/nptyping/assert_isinstance.py +++ b/src/numpydantic/vendor/nptyping/assert_isinstance.py @@ -21,6 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ + from typing import ( Any, Optional, diff --git a/src/numpydantic/vendor/nptyping/base_meta_classes.py b/src/numpydantic/vendor/nptyping/base_meta_classes.py index b9ca13a..c2ca1e3 100644 --- a/src/numpydantic/vendor/nptyping/base_meta_classes.py +++ b/src/numpydantic/vendor/nptyping/base_meta_classes.py @@ -21,6 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ + from abc import ABCMeta, abstractmethod from inspect import FrameInfo from typing import ( @@ -44,6 +45,7 @@ class InconstructableMeta(ABCMeta): """ def __call__(cls, *_: Any, **__: Any) -> None: + """Raise an error if constructed""" raise NPTypingError( f"Cannot instantiate nptyping.{cls.__name__}. Did you mean to use [ ] ?" ) @@ -76,6 +78,7 @@ class FinalMeta(ABCMeta): cls._name_per_meta_cls[cls] = implementation def __new__(cls, name: str, *args: Any, **kwargs: Any) -> type: + """Prevent subclasses, return from internal dict instead""" if name == cls._name_per_meta_cls[cls]: assert name, "cls_name not set" return type.__new__(cls, name, *args, **kwargs) @@ -105,8 +108,7 @@ class PrintableMeta(ABCMeta): """ @abstractmethod - def __str__(cls) -> str: - ... # pragma: no cover + def __str__(cls) -> str: ... # pragma: no cover def __repr__(cls) -> str: return str(cls) @@ -122,8 +124,7 @@ class SubscriptableMeta(ABCMeta): _parameterized: bool = False @abstractmethod - def _get_item(cls, item: Any) -> Tuple[Any, ...]: - ... # pragma: no cover + def _get_item(cls, item: Any) -> Tuple[Any, ...]: ... # pragma: no cover def _get_module(cls, stack: List[FrameInfo], module: str) -> str: # The magic below makes Python's help function display a meaningful @@ -201,12 +202,10 @@ class ContainerMeta( __args__: Tuple[str, ...] @abstractmethod - def _validate_expression(cls, item: str) -> None: - ... # pragma: no cover + def _validate_expression(cls, item: str) -> None: ... # pragma: no cover @abstractmethod - def _normalize_expression(cls, item: str) -> str: - ... # pragma: no cover + def _normalize_expression(cls, item: str) -> str: ... # pragma: no cover def _get_item(cls, item: Any) -> Tuple[Any, ...]: if not isinstance(item, str): diff --git a/src/numpydantic/vendor/nptyping/ndarray.py b/src/numpydantic/vendor/nptyping/ndarray.py index adeea12..a3a8daf 100644 --- a/src/numpydantic/vendor/nptyping/ndarray.py +++ b/src/numpydantic/vendor/nptyping/ndarray.py @@ -21,12 +21,12 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ + import inspect from abc import ABC from typing import Any, Tuple import numpy as np - from nptyping.base_meta_classes import ( FinalMeta, ImmutableMeta, diff --git a/src/numpydantic/vendor/nptyping/nptyping_type.py b/src/numpydantic/vendor/nptyping/nptyping_type.py index 35d4897..f8d90f0 100644 --- a/src/numpydantic/vendor/nptyping/nptyping_type.py +++ b/src/numpydantic/vendor/nptyping/nptyping_type.py @@ -21,10 +21,11 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ + from abc import ABC -class NPTypingType(ABC): +class NPTypingType(ABC): # noqa: B024 """ Baseclass for all nptyping types. """ diff --git a/src/numpydantic/vendor/nptyping/package_info.py b/src/numpydantic/vendor/nptyping/package_info.py index ee15ac8..d72b631 100644 --- a/src/numpydantic/vendor/nptyping/package_info.py +++ b/src/numpydantic/vendor/nptyping/package_info.py @@ -21,6 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ + __title__ = "nptyping" __version__ = "2.5.0" __author__ = "Ramon Hagenaars" diff --git a/src/numpydantic/vendor/nptyping/pandas_/__init__.py b/src/numpydantic/vendor/nptyping/pandas_/__init__.py index e69de29..c74bb5a 100644 --- a/src/numpydantic/vendor/nptyping/pandas_/__init__.py +++ b/src/numpydantic/vendor/nptyping/pandas_/__init__.py @@ -0,0 +1,3 @@ +""" +Pandas dataframe types +""" diff --git a/src/numpydantic/vendor/nptyping/pandas_/dataframe.py b/src/numpydantic/vendor/nptyping/pandas_/dataframe.py index e108b27..9dd5ee7 100644 --- a/src/numpydantic/vendor/nptyping/pandas_/dataframe.py +++ b/src/numpydantic/vendor/nptyping/pandas_/dataframe.py @@ -21,12 +21,12 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ + import inspect from abc import ABC from typing import Any, Tuple import numpy as np - from nptyping import InvalidArgumentsError from nptyping.base_meta_classes import ( FinalMeta, @@ -91,7 +91,7 @@ class DataFrameMeta( if item is Any: return (Any,) cls._check_item(item) - return (Structure[getattr(item, "__args__")[0]],) + return (Structure[item.__args__[0]],) def __str__(cls) -> str: structure = cls.__args__[0] diff --git a/src/numpydantic/vendor/nptyping/pandas_/typing_.py b/src/numpydantic/vendor/nptyping/pandas_/typing_.py index 29f2188..37dfdbe 100644 --- a/src/numpydantic/vendor/nptyping/pandas_/typing_.py +++ b/src/numpydantic/vendor/nptyping/pandas_/typing_.py @@ -21,6 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ + from nptyping.typing_ import Object from nptyping.typing_ import dtype_per_name as dtype_per_name_default diff --git a/src/numpydantic/vendor/nptyping/recarray.py b/src/numpydantic/vendor/nptyping/recarray.py index b3bbbc5..a90b13b 100644 --- a/src/numpydantic/vendor/nptyping/recarray.py +++ b/src/numpydantic/vendor/nptyping/recarray.py @@ -21,11 +21,11 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ + import inspect from typing import Any, Tuple import numpy as np - from nptyping.error import InvalidArgumentsError from nptyping.ndarray import NDArray, NDArrayMeta from nptyping.structure import Structure diff --git a/src/numpydantic/vendor/nptyping/shape.py b/src/numpydantic/vendor/nptyping/shape.py index 5dc05f5..c30aefc 100644 --- a/src/numpydantic/vendor/nptyping/shape.py +++ b/src/numpydantic/vendor/nptyping/shape.py @@ -21,6 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ + from abc import ABC from typing import Any, Dict diff --git a/src/numpydantic/vendor/nptyping/shape.pyi b/src/numpydantic/vendor/nptyping/shape.pyi index 96d2eb6..eeb64ed 100644 --- a/src/numpydantic/vendor/nptyping/shape.pyi +++ b/src/numpydantic/vendor/nptyping/shape.pyi @@ -21,6 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ + try: from typing import Literal # type: ignore[attr-defined] except ImportError: diff --git a/src/numpydantic/vendor/nptyping/shape_expression.py b/src/numpydantic/vendor/nptyping/shape_expression.py index bcaf79b..8d55a56 100644 --- a/src/numpydantic/vendor/nptyping/shape_expression.py +++ b/src/numpydantic/vendor/nptyping/shape_expression.py @@ -21,6 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ + import re import string from functools import lru_cache @@ -39,7 +40,7 @@ if TYPE_CHECKING: from nptyping.shape import Shape # pragma: no cover -@lru_cache() +@lru_cache def check_shape(shape: ShapeTuple, target: "Shape") -> bool: """ Check whether the given shape corresponds to the given shape_expression. diff --git a/src/numpydantic/vendor/nptyping/structure.py b/src/numpydantic/vendor/nptyping/structure.py index 0b95dfb..71da5ef 100644 --- a/src/numpydantic/vendor/nptyping/structure.py +++ b/src/numpydantic/vendor/nptyping/structure.py @@ -21,6 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ + from abc import ABC from typing import ( Any, diff --git a/src/numpydantic/vendor/nptyping/structure.pyi b/src/numpydantic/vendor/nptyping/structure.pyi index 415cf4e..608c094 100644 --- a/src/numpydantic/vendor/nptyping/structure.pyi +++ b/src/numpydantic/vendor/nptyping/structure.pyi @@ -21,6 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ + try: from typing import Literal # type: ignore[attr-defined] except ImportError: diff --git a/src/numpydantic/vendor/nptyping/structure_expression.py b/src/numpydantic/vendor/nptyping/structure_expression.py index ee1236c..f175381 100644 --- a/src/numpydantic/vendor/nptyping/structure_expression.py +++ b/src/numpydantic/vendor/nptyping/structure_expression.py @@ -21,6 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ + import re from collections import Counter, defaultdict from difflib import get_close_matches @@ -37,7 +38,6 @@ from typing import ( ) import numpy as np - from nptyping.error import InvalidShapeError, InvalidStructureError from nptyping.shape import Shape from nptyping.shape_expression import ( @@ -109,8 +109,7 @@ def check_structure( return False def iterator() -> Generator[Tuple[str, Tuple[np.dtype, int]], None, None]: # type: ignore[type-arg] # pylint: disable=line-too-long - for name_, dtype_tuple_ in fields.items(): - yield name_, dtype_tuple_ # type: ignore[misc] + yield from fields.items() for name, dtype_tuple in iterator(): field_in_target_not_in_subject = dtype_tuple is None diff --git a/src/numpydantic/vendor/nptyping/typing_.py b/src/numpydantic/vendor/nptyping/typing_.py index 2639e07..8f86e33 100644 --- a/src/numpydantic/vendor/nptyping/typing_.py +++ b/src/numpydantic/vendor/nptyping/typing_.py @@ -22,22 +22,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ -try: - from typing import ( # type: ignore[attr-defined,misc] # pylint: disable=unused-import - Literal, - TypeAlias, - TypeGuard, - final, - ) -except ImportError: # pragma: no cover - from typing_extensions import ( # type: ignore[attr-defined,misc] - Literal, - TypeAlias, - TypeGuard, - final, - ) -from typing import Tuple, Union +from typing import ( # type: ignore[attr-defined,misc] # pylint: disable=unused-import + Tuple, + TypeAlias, + Union, +) import numpy as np From 4c911d2516317850137d7d0c8ddb24d253c21f12 Mon Sep 17 00:00:00 2001 From: sneakers-the-rat Date: Wed, 31 Jul 2024 14:08:01 -0700 Subject: [PATCH 03/15] update import location --- src/numpydantic/ndarray.py | 12 +++++------ src/numpydantic/schema.py | 4 ++-- src/numpydantic/shape.py | 10 ++++----- src/numpydantic/vendor/nptyping/__init__.py | 20 +++++++++--------- .../vendor/nptyping/base_meta_classes.py | 13 ++++++++---- src/numpydantic/vendor/nptyping/ndarray.py | 21 +++++++++++-------- .../vendor/nptyping/pandas_/dataframe.py | 16 +++++++------- .../vendor/nptyping/pandas_/typing_.py | 4 ++-- src/numpydantic/vendor/nptyping/recarray.py | 11 +++++----- src/numpydantic/vendor/nptyping/shape.py | 6 +++--- .../vendor/nptyping/shape_expression.py | 4 ++-- src/numpydantic/vendor/nptyping/structure.py | 6 +++--- src/numpydantic/vendor/nptyping/structure.pyi | 11 +++++++++- .../vendor/nptyping/structure_expression.py | 8 +++---- 14 files changed, 82 insertions(+), 64 deletions(-) diff --git a/src/numpydantic/ndarray.py b/src/numpydantic/ndarray.py index 5ad42ba..81efb7f 100644 --- a/src/numpydantic/ndarray.py +++ b/src/numpydantic/ndarray.py @@ -16,12 +16,12 @@ Extension of nptyping NDArray for pydantic that allows for JSON-Schema serializa from typing import TYPE_CHECKING, Any, Tuple import numpy as np -from nptyping.error import InvalidArgumentsError -from nptyping.ndarray import NDArrayMeta as _NDArrayMeta -from nptyping.nptyping_type import NPTypingType -from nptyping.structure import Structure -from nptyping.structure_expression import check_type_names -from nptyping.typing_ import ( +from numpydantic.vendor.nptyping.error import InvalidArgumentsError +from numpydantic.vendor.nptyping.ndarray import NDArrayMeta as _NDArrayMeta +from numpydantic.vendor.nptyping.nptyping_type import NPTypingType +from numpydantic.vendor.nptyping.structure import Structure +from numpydantic.vendor.nptyping.structure_expression import check_type_names +from numpydantic.vendor.nptyping.typing_ import ( dtype_per_name, ) from pydantic import GetJsonSchemaHandler diff --git a/src/numpydantic/schema.py b/src/numpydantic/schema.py index 084ac7c..0a6c568 100644 --- a/src/numpydantic/schema.py +++ b/src/numpydantic/schema.py @@ -7,7 +7,7 @@ import hashlib import json from typing import TYPE_CHECKING, Any, Callable, Optional, Union -import nptyping.structure +from numpydantic.vendor.nptyping.structure import StructureMeta import numpy as np from pydantic import SerializationInfo from pydantic_core import CoreSchema, core_schema @@ -45,7 +45,7 @@ def _numeric_dtype(dtype: DtypeType, _handler: _handler_type) -> CoreSchema: def _lol_dtype(dtype: DtypeType, _handler: _handler_type) -> CoreSchema: """Get the innermost dtype schema to use in the generated pydantic schema""" - if isinstance(dtype, nptyping.structure.StructureMeta): # pragma: no cover + if isinstance(dtype, StructureMeta): # pragma: no cover raise NotImplementedError("Structured dtypes are currently unsupported") if isinstance(dtype, tuple): diff --git a/src/numpydantic/shape.py b/src/numpydantic/shape.py index 366e572..62a567f 100644 --- a/src/numpydantic/shape.py +++ b/src/numpydantic/shape.py @@ -29,15 +29,15 @@ from abc import ABC from functools import lru_cache from typing import Any, Dict, List, Union -from nptyping.base_meta_classes import ContainerMeta -from nptyping.error import InvalidShapeError, NPTypingError -from nptyping.nptyping_type import NPTypingType -from nptyping.shape_expression import ( +from numpydantic.vendor.nptyping.base_meta_classes import ContainerMeta +from numpydantic.vendor.nptyping.error import InvalidShapeError, NPTypingError +from numpydantic.vendor.nptyping.nptyping_type import NPTypingType +from numpydantic.vendor.nptyping.shape_expression import ( get_dimensions, normalize_shape_expression, remove_labels, ) -from nptyping.typing_ import ShapeExpression, ShapeTuple +from numpydantic.vendor.nptyping.typing_ import ShapeExpression, ShapeTuple class ShapeMeta(ContainerMeta, implementation="Shape"): diff --git a/src/numpydantic/vendor/nptyping/__init__.py b/src/numpydantic/vendor/nptyping/__init__.py index 35e11cc..8641108 100644 --- a/src/numpydantic/vendor/nptyping/__init__.py +++ b/src/numpydantic/vendor/nptyping/__init__.py @@ -22,25 +22,25 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ -from nptyping.assert_isinstance import assert_isinstance -from nptyping.error import ( +from numpydantic.vendor.nptyping.assert_isinstance import assert_isinstance +from numpydantic.vendor.nptyping.error import ( InvalidArgumentsError, InvalidDTypeError, InvalidShapeError, InvalidStructureError, NPTypingError, ) -from nptyping.ndarray import NDArray -from nptyping.package_info import __version__ -from nptyping.pandas_.dataframe import DataFrame -from nptyping.recarray import RecArray -from nptyping.shape import Shape -from nptyping.shape_expression import ( +from numpydantic.vendor.nptyping.ndarray import NDArray +from numpydantic.vendor.nptyping.package_info import __version__ +from numpydantic.vendor.nptyping.pandas_.dataframe import DataFrame +from numpydantic.vendor.nptyping.recarray import RecArray +from numpydantic.vendor.nptyping.shape import Shape +from numpydantic.vendor.nptyping.shape_expression import ( normalize_shape_expression, validate_shape_expression, ) -from nptyping.structure import Structure -from nptyping.typing_ import ( +from numpydantic.vendor.nptyping.structure import Structure +from numpydantic.vendor.nptyping.typing_ import ( Bool, Bool8, Byte, diff --git a/src/numpydantic/vendor/nptyping/base_meta_classes.py b/src/numpydantic/vendor/nptyping/base_meta_classes.py index c2ca1e3..01b5c12 100644 --- a/src/numpydantic/vendor/nptyping/base_meta_classes.py +++ b/src/numpydantic/vendor/nptyping/base_meta_classes.py @@ -23,7 +23,8 @@ SOFTWARE. """ from abc import ABCMeta, abstractmethod -from inspect import FrameInfo +from inspect import FrameInfo, getframeinfo +from types import FrameType from typing import ( Any, Dict, @@ -34,7 +35,7 @@ from typing import ( TypeVar, ) -from nptyping.error import InvalidArgumentsError, NPTypingError +from numpydantic.vendor.nptyping.error import InvalidArgumentsError, NPTypingError _T = TypeVar("_T") @@ -126,10 +127,14 @@ class SubscriptableMeta(ABCMeta): @abstractmethod def _get_item(cls, item: Any) -> Tuple[Any, ...]: ... # pragma: no cover - def _get_module(cls, stack: List[FrameInfo], module: str) -> str: + def _get_module(cls, stack: FrameType, module: str) -> str: # The magic below makes Python's help function display a meaningful # text with nptyping types. - return "typing" if stack[1][3] == "formatannotation" else module + return ( + "typing" + if getframeinfo(stack.f_back).function == "formatannotation" + else module + ) def _get_additional_values( cls, item: Any # pylint: disable=unused-argument diff --git a/src/numpydantic/vendor/nptyping/ndarray.py b/src/numpydantic/vendor/nptyping/ndarray.py index a3a8daf..c891af5 100644 --- a/src/numpydantic/vendor/nptyping/ndarray.py +++ b/src/numpydantic/vendor/nptyping/ndarray.py @@ -27,7 +27,7 @@ from abc import ABC from typing import Any, Tuple import numpy as np -from nptyping.base_meta_classes import ( +from numpydantic.vendor.nptyping.base_meta_classes import ( FinalMeta, ImmutableMeta, InconstructableMeta, @@ -35,13 +35,16 @@ from nptyping.base_meta_classes import ( PrintableMeta, SubscriptableMeta, ) -from nptyping.error import InvalidArgumentsError -from nptyping.nptyping_type import NPTypingType -from nptyping.shape import Shape -from nptyping.shape_expression import check_shape -from nptyping.structure import Structure -from nptyping.structure_expression import check_structure, check_type_names -from nptyping.typing_ import ( +from numpydantic.vendor.nptyping.error import InvalidArgumentsError +from numpydantic.vendor.nptyping.nptyping_type import NPTypingType +from numpydantic.vendor.nptyping.shape import Shape +from numpydantic.vendor.nptyping.shape_expression import check_shape +from numpydantic.vendor.nptyping.structure import Structure +from numpydantic.vendor.nptyping.structure_expression import ( + check_structure, + check_type_names, +) +from numpydantic.vendor.nptyping.typing_ import ( DType, dtype_per_name, name_per_dtype, @@ -67,7 +70,7 @@ class NDArrayMeta( @property def __module__(cls) -> str: - return cls._get_module(inspect.stack(), "nptyping.ndarray") + return cls._get_module(inspect.currentframe(), "nptyping.ndarray") def _get_item(cls, item: Any) -> Tuple[Any, ...]: cls._check_item(item) diff --git a/src/numpydantic/vendor/nptyping/pandas_/dataframe.py b/src/numpydantic/vendor/nptyping/pandas_/dataframe.py index 9dd5ee7..dd270cf 100644 --- a/src/numpydantic/vendor/nptyping/pandas_/dataframe.py +++ b/src/numpydantic/vendor/nptyping/pandas_/dataframe.py @@ -27,8 +27,8 @@ from abc import ABC from typing import Any, Tuple import numpy as np -from nptyping import InvalidArgumentsError -from nptyping.base_meta_classes import ( +from numpydantic.vendor.nptyping import InvalidArgumentsError +from numpydantic.vendor.nptyping.base_meta_classes import ( FinalMeta, ImmutableMeta, InconstructableMeta, @@ -36,11 +36,11 @@ from nptyping.base_meta_classes import ( PrintableMeta, SubscriptableMeta, ) -from nptyping.error import DependencyError -from nptyping.nptyping_type import NPTypingType -from nptyping.pandas_.typing_ import dtype_per_name -from nptyping.structure import Structure -from nptyping.structure_expression import check_structure +from numpydantic.vendor.nptyping.error import DependencyError +from numpydantic.vendor.nptyping.nptyping_type import NPTypingType +from numpydantic.vendor.nptyping.pandas_.typing_ import dtype_per_name +from numpydantic.vendor.nptyping.structure import Structure +from numpydantic.vendor.nptyping.structure_expression import check_structure try: import pandas as pd @@ -105,7 +105,7 @@ class DataFrameMeta( @property def __module__(cls) -> str: - return cls._get_module(inspect.stack(), "nptyping.pandas_.dataframe") + return cls._get_module(inspect.currentframe(), "nptyping.ndarray") def _check_item(cls, item: Any) -> None: # Check if the item is what we expect and raise if it is not. diff --git a/src/numpydantic/vendor/nptyping/pandas_/typing_.py b/src/numpydantic/vendor/nptyping/pandas_/typing_.py index 37dfdbe..461fa1f 100644 --- a/src/numpydantic/vendor/nptyping/pandas_/typing_.py +++ b/src/numpydantic/vendor/nptyping/pandas_/typing_.py @@ -22,8 +22,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ -from nptyping.typing_ import Object -from nptyping.typing_ import dtype_per_name as dtype_per_name_default +from numpydantic.vendor.nptyping.typing_ import Object +from numpydantic.vendor.nptyping.typing_ import dtype_per_name as dtype_per_name_default dtype_per_name = { **dtype_per_name_default, # type: ignore[arg-type] diff --git a/src/numpydantic/vendor/nptyping/recarray.py b/src/numpydantic/vendor/nptyping/recarray.py index a90b13b..931a135 100644 --- a/src/numpydantic/vendor/nptyping/recarray.py +++ b/src/numpydantic/vendor/nptyping/recarray.py @@ -26,10 +26,11 @@ import inspect from typing import Any, Tuple import numpy as np -from nptyping.error import InvalidArgumentsError -from nptyping.ndarray import NDArray, NDArrayMeta -from nptyping.structure import Structure -from nptyping.typing_ import DType + +from numpydantic.vendor.nptyping.error import InvalidArgumentsError +from numpydantic.vendor.nptyping.ndarray import NDArray, NDArrayMeta +from numpydantic.vendor.nptyping.structure import Structure +from numpydantic.vendor.nptyping.typing_ import DType class RecArrayMeta(NDArrayMeta, implementation="RecArray"): @@ -52,7 +53,7 @@ class RecArrayMeta(NDArrayMeta, implementation="RecArray"): @property def __module__(cls) -> str: - return cls._get_module(inspect.stack(), "nptyping.recarray") + return cls._get_module(inspect.currentframe(), "nptyping.ndarray") def __instancecheck__( # pylint: disable=bad-mcs-method-argument self, instance: Any diff --git a/src/numpydantic/vendor/nptyping/shape.py b/src/numpydantic/vendor/nptyping/shape.py index c30aefc..b4d4698 100644 --- a/src/numpydantic/vendor/nptyping/shape.py +++ b/src/numpydantic/vendor/nptyping/shape.py @@ -25,9 +25,9 @@ SOFTWARE. from abc import ABC from typing import Any, Dict -from nptyping.base_meta_classes import ContainerMeta -from nptyping.nptyping_type import NPTypingType -from nptyping.shape_expression import ( +from numpydantic.vendor.nptyping.base_meta_classes import ContainerMeta +from numpydantic.vendor.nptyping.nptyping_type import NPTypingType +from numpydantic.vendor.nptyping.shape_expression import ( get_dimensions, normalize_shape_expression, remove_labels, diff --git a/src/numpydantic/vendor/nptyping/shape_expression.py b/src/numpydantic/vendor/nptyping/shape_expression.py index 8d55a56..6234031 100644 --- a/src/numpydantic/vendor/nptyping/shape_expression.py +++ b/src/numpydantic/vendor/nptyping/shape_expression.py @@ -33,8 +33,8 @@ from typing import ( Union, ) -from nptyping.error import InvalidShapeError -from nptyping.typing_ import ShapeExpression, ShapeTuple +from numpydantic.vendor.nptyping.error import InvalidShapeError +from numpydantic.vendor.nptyping.typing_ import ShapeExpression, ShapeTuple if TYPE_CHECKING: from nptyping.shape import Shape # pragma: no cover diff --git a/src/numpydantic/vendor/nptyping/structure.py b/src/numpydantic/vendor/nptyping/structure.py index 71da5ef..8ad5d5b 100644 --- a/src/numpydantic/vendor/nptyping/structure.py +++ b/src/numpydantic/vendor/nptyping/structure.py @@ -29,9 +29,9 @@ from typing import ( List, ) -from nptyping.base_meta_classes import ContainerMeta -from nptyping.nptyping_type import NPTypingType -from nptyping.structure_expression import ( +from numpydantic.vendor.nptyping.base_meta_classes import ContainerMeta +from numpydantic.vendor.nptyping.nptyping_type import NPTypingType +from numpydantic.vendor.nptyping.structure_expression import ( create_name_to_type_dict, normalize_structure_expression, validate_structure_expression, diff --git a/src/numpydantic/vendor/nptyping/structure.pyi b/src/numpydantic/vendor/nptyping/structure.pyi index 608c094..a0ba8eb 100644 --- a/src/numpydantic/vendor/nptyping/structure.pyi +++ b/src/numpydantic/vendor/nptyping/structure.pyi @@ -27,9 +27,18 @@ try: except ImportError: from typing_extensions import Literal # type: ignore[attr-defined,misc,assignment] -from typing import Any, cast +from typing import Any, Dict, cast import numpy as np +from numpydantic.vendor.nptyping.base_meta_classes import ContainerMeta + +class StructureMeta(ContainerMeta, implementation="Structure"): + + __args__ = tuple() + + def _validate_expression(cls, item: str) -> None: ... + def _normalize_expression(cls, item: str) -> str: ... + def _get_additional_values(cls, item: Any) -> Dict[str, Any]: ... # For MyPy: Structure = cast(Literal, Structure) # type: ignore[has-type,misc,valid-type] diff --git a/src/numpydantic/vendor/nptyping/structure_expression.py b/src/numpydantic/vendor/nptyping/structure_expression.py index f175381..dd9d8f1 100644 --- a/src/numpydantic/vendor/nptyping/structure_expression.py +++ b/src/numpydantic/vendor/nptyping/structure_expression.py @@ -38,14 +38,14 @@ from typing import ( ) import numpy as np -from nptyping.error import InvalidShapeError, InvalidStructureError -from nptyping.shape import Shape -from nptyping.shape_expression import ( +from numpydantic.vendor.nptyping.error import InvalidShapeError, InvalidStructureError +from numpydantic.vendor.nptyping.shape import Shape +from numpydantic.vendor.nptyping.shape_expression import ( check_shape, normalize_shape_expression, validate_shape_expression, ) -from nptyping.typing_ import StructureExpression +from numpydantic.vendor.nptyping.typing_ import StructureExpression if TYPE_CHECKING: from nptyping.structure import Structure # pragma: no cover From 4604e7e7bbee2dea4270482986fbc68eb3cd0a59 Mon Sep 17 00:00:00 2001 From: sneakers-the-rat Date: Wed, 31 Jul 2024 14:08:26 -0700 Subject: [PATCH 04/15] apply monkeypatches in vendored nptyping, remove monkeypatch module and call --- src/numpydantic/__init__.py | 4 --- src/numpydantic/monkeypatch.py | 66 ---------------------------------- 2 files changed, 70 deletions(-) delete mode 100644 src/numpydantic/monkeypatch.py diff --git a/src/numpydantic/__init__.py b/src/numpydantic/__init__.py index d251f8d..803d8d2 100644 --- a/src/numpydantic/__init__.py +++ b/src/numpydantic/__init__.py @@ -1,10 +1,6 @@ -# ruff: noqa: E402 # ruff: noqa: F401 # ruff: noqa: I001 # ruff: noqa: D104 -from numpydantic.monkeypatch import apply_patches - -apply_patches() from numpydantic.ndarray import NDArray from numpydantic.meta import update_ndarray_stub diff --git a/src/numpydantic/monkeypatch.py b/src/numpydantic/monkeypatch.py deleted file mode 100644 index 05607e5..0000000 --- a/src/numpydantic/monkeypatch.py +++ /dev/null @@ -1,66 +0,0 @@ -""" -Functions to monkeypatch dependent packages - most notably nptyping -""" - -# ruff: noqa: ANN001 - - -def patch_npytyping_perf() -> None: - """ - npytyping makes an expensive call to inspect.stack() - that makes imports of pydantic models take ~200x longer than - they should: - - References: - - https://github.com/ramonhagenaars/nptyping/issues/110 - """ - import inspect - from types import FrameType - - from nptyping import base_meta_classes, ndarray, recarray - from nptyping.pandas_ import dataframe - - # make a new __module__ methods for the affected classes - - def new_module_ndarray(cls) -> str: # pragma: no cover - return cls._get_module(inspect.currentframe(), "nptyping.ndarray") - - def new_module_recarray(cls) -> str: # pragma: no cover - return cls._get_module(inspect.currentframe(), "nptyping.recarray") - - def new_module_dataframe(cls) -> str: # pragma: no cover - return cls._get_module(inspect.currentframe(), "nptyping.pandas_.dataframe") - - # and a new _get_module method for the parent class - def new_get_module(cls, stack: FrameType, module: str) -> str: # pragma: no cover - return ( - "typing" - if inspect.getframeinfo(stack.f_back).function == "formatannotation" - else module - ) - - # now apply the patches - ndarray.NDArrayMeta.__module__ = property(new_module_ndarray) - recarray.RecArrayMeta.__module__ = property(new_module_recarray) - dataframe.DataFrameMeta.__module__ = property(new_module_dataframe) - base_meta_classes.SubscriptableMeta._get_module = new_get_module - - -def patch_nptyping_warnings() -> None: - """ - nptyping shits out a bunch of numpy deprecation warnings from using - olde aliases - - References: - - https://github.com/ramonhagenaars/nptyping/issues/113 - - https://github.com/ramonhagenaars/nptyping/issues/102 - """ - import warnings - - warnings.filterwarnings("ignore", category=DeprecationWarning, module="nptyping.*") - - -def apply_patches() -> None: - """Apply all monkeypatches!""" - patch_npytyping_perf() - patch_nptyping_warnings() From 33067037413704ef8a6ddc75f6cfaccaa06160f7 Mon Sep 17 00:00:00 2001 From: sneakers-the-rat Date: Wed, 31 Jul 2024 14:15:24 -0700 Subject: [PATCH 05/15] update old numpy dtypes, remove them, lint --- pyproject.toml | 4 +++ src/numpydantic/dtype.py | 14 ++------ src/numpydantic/interface/video.py | 6 ++-- src/numpydantic/maps.py | 2 +- src/numpydantic/ndarray.py | 16 ++++----- src/numpydantic/schema.py | 2 +- src/numpydantic/types.py | 2 +- .../vendor/nptyping/base_meta_classes.py | 3 +- src/numpydantic/vendor/nptyping/ndarray.py | 1 + .../vendor/nptyping/pandas_/dataframe.py | 1 + .../vendor/nptyping/structure_expression.py | 1 + src/numpydantic/vendor/nptyping/typing_.py | 33 +++++-------------- 12 files changed, 33 insertions(+), 52 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 71d6d5e..05b97ea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -101,6 +101,10 @@ select = [ "I", # annotations "ANN", + # perf + "PERF", + # numpy + "NPY", ## ---------- # pydocstyle # undocumented public objects diff --git a/src/numpydantic/dtype.py b/src/numpydantic/dtype.py index 55bcca6..4c0cc80 100644 --- a/src/numpydantic/dtype.py +++ b/src/numpydantic/dtype.py @@ -69,9 +69,7 @@ Half = np.half Single = np.single Double = np.double LongDouble = np.longdouble -LongFloat = np.longfloat Float = ( - np.float_, np.float16, np.float32, np.float64, @@ -84,32 +82,24 @@ ComplexFloating = np.complexfloating Complex64 = np.complex64 Complex128 = np.complex128 CSingle = np.csingle -SingleComplex = np.singlecomplex CDouble = np.cdouble -CFloat = np.cfloat CLongDouble = np.clongdouble -CLongFloat = np.clongfloat Complex = ( - np.complex_, np.complexfloating, np.complex64, np.complex128, np.csingle, - np.singlecomplex, np.cdouble, - np.cfloat, np.clongdouble, - np.clongfloat, ) -LongComplex = np.longcomplex Flexible = np.flexible Void = np.void Character = np.character Bytes = np.bytes_ Str = np.str_ -String = np.string_ -Unicode = np.unicode_ +String = np.str_ +Unicode = np.str_ Number = tuple( [ diff --git a/src/numpydantic/interface/video.py b/src/numpydantic/interface/video.py index 0e1a50d..4f2048d 100644 --- a/src/numpydantic/interface/video.py +++ b/src/numpydantic/interface/video.py @@ -144,9 +144,9 @@ class VideoProxy: elif isinstance(item, slice): # slice of frames item = self._complete_slice(item) - frames = [] - for i in range(item.start, item.stop, item.step): - frames.append(self._get_frame(i)) + frames = [ + self._get_frame(i) for i in range(item.start, item.stop, item.step) + ] return np.stack(frames) else: # slices are passed as tuples diff --git a/src/numpydantic/maps.py b/src/numpydantic/maps.py index 38e4925..d80d33e 100644 --- a/src/numpydantic/maps.py +++ b/src/numpydantic/maps.py @@ -21,7 +21,7 @@ np_to_python = { **{n: int for n in dt.Integer}, **{n: float for n in dt.Float}, **{n: complex for n in dt.Complex}, - **{n: str for n in (np.character, np.str_, np.string_, np.unicode_)}, + **{n: str for n in (np.character, np.str_, np.bytes_, np.str_)}, } """Map from python types to numpy""" diff --git a/src/numpydantic/ndarray.py b/src/numpydantic/ndarray.py index 81efb7f..42fc3f8 100644 --- a/src/numpydantic/ndarray.py +++ b/src/numpydantic/ndarray.py @@ -16,14 +16,6 @@ Extension of nptyping NDArray for pydantic that allows for JSON-Schema serializa from typing import TYPE_CHECKING, Any, Tuple import numpy as np -from numpydantic.vendor.nptyping.error import InvalidArgumentsError -from numpydantic.vendor.nptyping.ndarray import NDArrayMeta as _NDArrayMeta -from numpydantic.vendor.nptyping.nptyping_type import NPTypingType -from numpydantic.vendor.nptyping.structure import Structure -from numpydantic.vendor.nptyping.structure_expression import check_type_names -from numpydantic.vendor.nptyping.typing_ import ( - dtype_per_name, -) from pydantic import GetJsonSchemaHandler from pydantic_core import core_schema @@ -38,6 +30,14 @@ from numpydantic.schema import ( make_json_schema, ) from numpydantic.types import DtypeType, ShapeType +from numpydantic.vendor.nptyping.error import InvalidArgumentsError +from numpydantic.vendor.nptyping.ndarray import NDArrayMeta as _NDArrayMeta +from numpydantic.vendor.nptyping.nptyping_type import NPTypingType +from numpydantic.vendor.nptyping.structure import Structure +from numpydantic.vendor.nptyping.structure_expression import check_type_names +from numpydantic.vendor.nptyping.typing_ import ( + dtype_per_name, +) if TYPE_CHECKING: # pragma: no cover from nptyping.base_meta_classes import SubscriptableMeta diff --git a/src/numpydantic/schema.py b/src/numpydantic/schema.py index 0a6c568..8e7e1bf 100644 --- a/src/numpydantic/schema.py +++ b/src/numpydantic/schema.py @@ -7,7 +7,6 @@ import hashlib import json from typing import TYPE_CHECKING, Any, Callable, Optional, Union -from numpydantic.vendor.nptyping.structure import StructureMeta import numpy as np from pydantic import SerializationInfo from pydantic_core import CoreSchema, core_schema @@ -17,6 +16,7 @@ from numpydantic import dtype as dt from numpydantic.interface import Interface from numpydantic.maps import np_to_python from numpydantic.types import DtypeType, NDArrayType, ShapeType +from numpydantic.vendor.nptyping.structure import StructureMeta if TYPE_CHECKING: from numpydantic import Shape diff --git a/src/numpydantic/types.py b/src/numpydantic/types.py index c629fbf..ef01cee 100644 --- a/src/numpydantic/types.py +++ b/src/numpydantic/types.py @@ -8,7 +8,7 @@ Note that these are types as in python typing types, not classes. from typing import Any, Protocol, Tuple, Union, runtime_checkable -from nptyping import DType +from numpydantic.vendor.nptyping import DType ShapeType = Union[Tuple[int, ...], Any] DtypeType = Union[str, type, Any, DType] diff --git a/src/numpydantic/vendor/nptyping/base_meta_classes.py b/src/numpydantic/vendor/nptyping/base_meta_classes.py index 01b5c12..d943e20 100644 --- a/src/numpydantic/vendor/nptyping/base_meta_classes.py +++ b/src/numpydantic/vendor/nptyping/base_meta_classes.py @@ -23,12 +23,11 @@ SOFTWARE. """ from abc import ABCMeta, abstractmethod -from inspect import FrameInfo, getframeinfo +from inspect import getframeinfo from types import FrameType from typing import ( Any, Dict, - List, Optional, Set, Tuple, diff --git a/src/numpydantic/vendor/nptyping/ndarray.py b/src/numpydantic/vendor/nptyping/ndarray.py index c891af5..90a4793 100644 --- a/src/numpydantic/vendor/nptyping/ndarray.py +++ b/src/numpydantic/vendor/nptyping/ndarray.py @@ -27,6 +27,7 @@ from abc import ABC from typing import Any, Tuple import numpy as np + from numpydantic.vendor.nptyping.base_meta_classes import ( FinalMeta, ImmutableMeta, diff --git a/src/numpydantic/vendor/nptyping/pandas_/dataframe.py b/src/numpydantic/vendor/nptyping/pandas_/dataframe.py index dd270cf..f734acb 100644 --- a/src/numpydantic/vendor/nptyping/pandas_/dataframe.py +++ b/src/numpydantic/vendor/nptyping/pandas_/dataframe.py @@ -27,6 +27,7 @@ from abc import ABC from typing import Any, Tuple import numpy as np + from numpydantic.vendor.nptyping import InvalidArgumentsError from numpydantic.vendor.nptyping.base_meta_classes import ( FinalMeta, diff --git a/src/numpydantic/vendor/nptyping/structure_expression.py b/src/numpydantic/vendor/nptyping/structure_expression.py index dd9d8f1..625f770 100644 --- a/src/numpydantic/vendor/nptyping/structure_expression.py +++ b/src/numpydantic/vendor/nptyping/structure_expression.py @@ -38,6 +38,7 @@ from typing import ( ) import numpy as np + from numpydantic.vendor.nptyping.error import InvalidShapeError, InvalidStructureError from numpydantic.vendor.nptyping.shape import Shape from numpydantic.vendor.nptyping.shape_expression import ( diff --git a/src/numpydantic/vendor/nptyping/typing_.py b/src/numpydantic/vendor/nptyping/typing_.py index 8f86e33..6b05eb6 100644 --- a/src/numpydantic/vendor/nptyping/typing_.py +++ b/src/numpydantic/vendor/nptyping/typing_.py @@ -22,7 +22,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ - from typing import ( # type: ignore[attr-defined,misc] # pylint: disable=unused-import Tuple, TypeAlias, @@ -38,10 +37,8 @@ ShapeTuple: TypeAlias = Tuple[int, ...] Number = np.number Bool = np.bool_ -Bool8 = np.bool8 Obj = np.object_ # Obj is a common abbreviation and should be usable. Object = np.object_ -Object0 = np.object0 Datetime64 = np.datetime64 Integer = np.integer SignedInteger = np.signedinteger @@ -53,7 +50,6 @@ Byte = np.byte Short = np.short IntC = np.intc IntP = np.intp -Int0 = np.int0 Int = np.integer # Int should translate to the "generic" int type. Int_ = np.int_ LongLong = np.longlong @@ -67,7 +63,6 @@ UByte = np.ubyte UShort = np.ushort UIntC = np.uintc UIntP = np.uintp -UInt0 = np.uint0 UInt = np.uint ULongLong = np.ulonglong Inexact = np.inexact @@ -78,38 +73,33 @@ Float64 = np.float64 Half = np.half Single = np.single Double = np.double -Float = np.float_ +Float = np.float64 LongDouble = np.longdouble -LongFloat = np.longfloat +LongFloat = np.longdouble ComplexFloating = np.complexfloating Complex64 = np.complex64 Complex128 = np.complex128 CSingle = np.csingle -SingleComplex = np.singlecomplex +SingleComplex = np.complex64 CDouble = np.cdouble -Complex = np.complex_ -CFloat = np.cfloat +Complex = np.complex128 +CFloat = np.complex128 CLongDouble = np.clongdouble -CLongFloat = np.clongfloat -LongComplex = np.longcomplex +CLongFloat = np.clongdouble +LongComplex = np.clongdouble Flexible = np.flexible Void = np.void -Void0 = np.void0 Character = np.character Bytes = np.bytes_ Str = np.str_ -String = np.string_ -Bytes0 = np.bytes0 -Unicode = np.unicode_ -Str0 = np.str0 +String = np.str_ +Unicode = np.str_ dtypes = [ (Number, "Number"), (Bool, "Bool"), - (Bool8, "Bool8"), (Obj, "Obj"), (Object, "Object"), - (Object0, "Object0"), (Datetime64, "Datetime64"), (Integer, "Integer"), (SignedInteger, "SignedInteger"), @@ -121,7 +111,6 @@ dtypes = [ (Short, "Short"), (IntC, "IntC"), (IntP, "IntP"), - (Int0, "Int0"), (Int, "Int"), (LongLong, "LongLong"), (Timedelta64, "Timedelta64"), @@ -134,7 +123,6 @@ dtypes = [ (UShort, "UShort"), (UIntC, "UIntC"), (UIntP, "UIntP"), - (UInt0, "UInt0"), (UInt, "UInt"), (ULongLong, "ULongLong"), (Inexact, "Inexact"), @@ -161,14 +149,11 @@ dtypes = [ (LongComplex, "LongComplex"), (Flexible, "Flexible"), (Void, "Void"), - (Void0, "Void0"), (Character, "Character"), (Bytes, "Bytes"), (String, "String"), (Str, "Str"), - (Bytes0, "Bytes0"), (Unicode, "Unicode"), - (Str0, "Str0"), ] name_per_dtype = dict(dtypes) From 40c8e9d95dbe02c7abdfd05ddbbd73d46a2c5652 Mon Sep 17 00:00:00 2001 From: sneakers-the-rat Date: Wed, 31 Jul 2024 15:50:24 -0700 Subject: [PATCH 06/15] remove nptyping dependency, update CI, remove old numpy types --- .github/workflows/lint.yml | 28 +++++ .github/workflows/tests.yml | 49 ++++++--- pyproject.toml | 3 +- src/numpydantic/dtype.py | 2 - src/numpydantic/schema.py | 2 +- src/numpydantic/vendor/nptyping/__init__.py | 7 -- src/numpydantic/vendor/nptyping/typing_.py | 12 ++- src/numpydantic/vendor/nptyping/typing_.pyi | 114 -------------------- tests/test_ndarray.py | 3 +- tox.ini | 14 +++ 10 files changed, 90 insertions(+), 144 deletions(-) create mode 100644 .github/workflows/lint.yml delete mode 100644 src/numpydantic/vendor/nptyping/typing_.pyi create mode 100644 tox.ini diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..aeb668b --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,28 @@ +name: Lint + +on: + push: + branches: + - main + pull_request: + branches: + - main + + +permissions: + contents: read + +jobs: + ruff: + name: Ruff Linting + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: chartboost/ruff-action@v1 + + black: + name: Black Formatting + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: psf/black@stable diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 31f586e..2c7e079 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -2,14 +2,34 @@ name: Tests on: push: + branches: + - main + pull_request: + branches: + - main jobs: test: strategy: matrix: - python-version: ["3.9", "3.11", "3.12"] + platform: ["ubuntu-latest", "macos-latest", "windows-latest"] + numpy-version: ["<2.0.0", ">=2.0.0"] + python-version: ["3.9", "3.10", "3.11", "3.12"] + exclude: + - numpy-version: "<2.0.0" + python-version: "3.10" + - numpy-version: "<2.0.0" + python-version: "3.11" + - platform: "macos-latest" + python-version: "3.10" + - platform: "macos-latest" + python-version: "3.11" + - platform: "windows-latest" + python-version: "3.10" + - platform: "windows-latest" + python-version: "3.11" - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 @@ -23,19 +43,24 @@ jobs: - name: Install dependencies run: pip install -e ".[tests,linkml]" + - name: Install numpy version + run: pip install "numpy${{ matrix.numpy-version }}" + - name: Run Tests run: pytest - - name: Report coverage - if: ${{ matrix.python-version }} == "3.11" - run: "coveralls --service=github" - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Coveralls Parallel + uses: coverallsapp/github-action@v2 + with: + flag-name: run-${{ join(matrix.*, '-') }} + parallel: true - lint: + finish-coverage: + needs: test + if: ${{ always() }} runs-on: ubuntu-latest - continue-on-error: true steps: - - uses: actions/checkout@v3 - - uses: chartboost/ruff-action@v1 - - uses: psf/black@stable \ No newline at end of file + - name: Coveralls Finished + uses: coverallsapp/github-action@v2 + with: + parallel-finished: true \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 05b97ea..12a1b50 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,8 +7,8 @@ authors = [ ] dependencies = [ "pydantic>=2.3.0", - "nptyping>=2.5.0", "numpy>=1.24.0", + "typing-extensions>=4.11.0;python_version<'3.11'" ] homepage = "https://numpydantic.readthedocs.io" requires-python = "<4.0,>=3.9" @@ -40,7 +40,6 @@ tests = [ "coverage>=6.1.1", "pytest-cov<5.0.0,>=4.1.0", "coveralls<4.0.0,>=3.3.1", - "typing-extensions>=4.11.0;python_version<'3.11'", ] docs = [ "sphinx<8.0.0,>=7.2.6", diff --git a/src/numpydantic/dtype.py b/src/numpydantic/dtype.py index 4c0cc80..12d766a 100644 --- a/src/numpydantic/dtype.py +++ b/src/numpydantic/dtype.py @@ -78,14 +78,12 @@ Float = ( ) Floating = Float -ComplexFloating = np.complexfloating Complex64 = np.complex64 Complex128 = np.complex128 CSingle = np.csingle CDouble = np.cdouble CLongDouble = np.clongdouble Complex = ( - np.complexfloating, np.complex64, np.complex128, np.csingle, diff --git a/src/numpydantic/schema.py b/src/numpydantic/schema.py index 8e7e1bf..bf3fd90 100644 --- a/src/numpydantic/schema.py +++ b/src/numpydantic/schema.py @@ -50,7 +50,7 @@ def _lol_dtype(dtype: DtypeType, _handler: _handler_type) -> CoreSchema: if isinstance(dtype, tuple): # if it's a meta-type that refers to a generic float/int, just make that - if dtype == dt.Float: + if dtype in (dt.Float, dt.Number): array_type = core_schema.float_schema() elif dtype == dt.Integer: array_type = core_schema.int_schema() diff --git a/src/numpydantic/vendor/nptyping/__init__.py b/src/numpydantic/vendor/nptyping/__init__.py index 8641108..4ca8fdb 100644 --- a/src/numpydantic/vendor/nptyping/__init__.py +++ b/src/numpydantic/vendor/nptyping/__init__.py @@ -42,10 +42,8 @@ from numpydantic.vendor.nptyping.shape_expression import ( from numpydantic.vendor.nptyping.structure import Structure from numpydantic.vendor.nptyping.typing_ import ( Bool, - Bool8, Byte, Bytes, - Bytes0, CDouble, CFloat, Character, @@ -68,7 +66,6 @@ from numpydantic.vendor.nptyping.typing_ import ( Half, Inexact, Int, - Int0, Int8, Int16, Int32, @@ -82,17 +79,14 @@ from numpydantic.vendor.nptyping.typing_ import ( LongLong, Number, Object, - Object0, Short, SignedInteger, Single, SingleComplex, - Str0, String, Timedelta64, UByte, UInt, - UInt0, UInt8, UInt16, UInt32, @@ -104,7 +98,6 @@ from numpydantic.vendor.nptyping.typing_ import ( UnsignedInteger, UShort, Void, - Void0, ) __all__ = [ diff --git a/src/numpydantic/vendor/nptyping/typing_.py b/src/numpydantic/vendor/nptyping/typing_.py index 6b05eb6..6caf3b9 100644 --- a/src/numpydantic/vendor/nptyping/typing_.py +++ b/src/numpydantic/vendor/nptyping/typing_.py @@ -22,11 +22,13 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ -from typing import ( # type: ignore[attr-defined,misc] # pylint: disable=unused-import - Tuple, - TypeAlias, - Union, -) +import sys +from typing import Tuple, Union + +if sys.version_info.minor >= 10: + from typing import TypeAlias +else: + from typing_extensions import TypeAlias import numpy as np diff --git a/src/numpydantic/vendor/nptyping/typing_.pyi b/src/numpydantic/vendor/nptyping/typing_.pyi deleted file mode 100644 index fcf83ce..0000000 --- a/src/numpydantic/vendor/nptyping/typing_.pyi +++ /dev/null @@ -1,114 +0,0 @@ -""" -MIT License - -Copyright (c) 2023 Ramon Hagenaars - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -""" - -try: - from typing import ( # type: ignore[attr-defined] # pylint: disable=unused-import - Dict, - TypeAlias, - ) -except ImportError: # pragma: no cover - from typing_extensions import ( - TypeAlias, - ) - -from typing import ( - Any, - Tuple, - Union, -) - -import numpy as np - -ShapeExpression: TypeAlias = str -StructureExpression: TypeAlias = str -DType: TypeAlias = Union[np.generic, StructureExpression] -ShapeTuple: TypeAlias = Tuple[int, ...] - -Number: TypeAlias = np.dtype[np.number[Any]] -Bool: TypeAlias = np.dtype[np.bool_] -Bool8: TypeAlias = np.dtype[np.bool8] -Object: TypeAlias = np.dtype[np.object_] -Object0: TypeAlias = np.dtype[np.object0] -Datetime64: TypeAlias = np.dtype[np.datetime64] -Integer: TypeAlias = np.dtype[np.integer[Any]] -SignedInteger: TypeAlias = np.dtype[np.signedinteger[Any]] -Int8: TypeAlias = np.dtype[np.int8] -Int16: TypeAlias = np.dtype[np.int16] -Int32: TypeAlias = np.dtype[np.int32] -Int64: TypeAlias = np.dtype[np.int64] -Byte: TypeAlias = np.dtype[np.byte] -Short: TypeAlias = np.dtype[np.short] -IntC: TypeAlias = np.dtype[np.intc] -IntP: TypeAlias = np.dtype[np.intp] -Int0: TypeAlias = np.dtype[np.int0] -Int: TypeAlias = np.dtype[np.int_] -LongLong: TypeAlias = np.dtype[np.longlong] -Timedelta64: TypeAlias = np.dtype[np.timedelta64] -UnsignedInteger: TypeAlias = np.dtype[np.unsignedinteger[Any]] -UInt8: TypeAlias = np.dtype[np.uint8] -UInt16: TypeAlias = np.dtype[np.uint16] -UInt32: TypeAlias = np.dtype[np.uint32] -UInt64: TypeAlias = np.dtype[np.uint64] -UByte: TypeAlias = np.dtype[np.ubyte] -UShort: TypeAlias = np.dtype[np.ushort] -UIntC: TypeAlias = np.dtype[np.uintc] -UIntP: TypeAlias = np.dtype[np.uintp] -UInt0: TypeAlias = np.dtype[np.uint0] -UInt: TypeAlias = np.dtype[np.uint] -ULongLong: TypeAlias = np.dtype[np.ulonglong] -Inexact: TypeAlias = np.dtype[np.inexact[Any]] -Floating: TypeAlias = np.dtype[np.floating[Any]] -Float16: TypeAlias = np.dtype[np.float16] -Float32: TypeAlias = np.dtype[np.float32] -Float64: TypeAlias = np.dtype[np.float64] -Half: TypeAlias = np.dtype[np.half] -Single: TypeAlias = np.dtype[np.single] -Double: TypeAlias = np.dtype[np.double] -Float: TypeAlias = np.dtype[np.float_] -LongDouble: TypeAlias = np.dtype[np.longdouble] -LongFloat: TypeAlias = np.dtype[np.longfloat] -ComplexFloating: TypeAlias = np.dtype[np.complexfloating[Any, Any]] -Complex64: TypeAlias = np.dtype[np.complex64] -Complex128: TypeAlias = np.dtype[np.complex128] -CSingle: TypeAlias = np.dtype[np.csingle] -SingleComplex: TypeAlias = np.dtype[np.singlecomplex] -CDouble: TypeAlias = np.dtype[np.cdouble] -Complex: TypeAlias = np.dtype[np.complex_] -CFloat: TypeAlias = np.dtype[np.cfloat] -CLongDouble: TypeAlias = np.dtype[np.clongdouble] -CLongFloat: TypeAlias = np.dtype[np.clongfloat] -LongComplex: TypeAlias = np.dtype[np.longcomplex] -Flexible: TypeAlias = np.dtype[np.flexible] -Void: TypeAlias = np.dtype[np.void] -Void0: TypeAlias = np.dtype[np.void0] -Character: TypeAlias = np.dtype[np.character] -Bytes: TypeAlias = np.dtype[np.bytes_] -Str: TypeAlias = np.dtype[np.str_] -String: TypeAlias = np.dtype[np.string_] -Bytes0: TypeAlias = np.dtype[np.bytes0] -Unicode: TypeAlias = np.dtype[np.unicode_] -Str0: TypeAlias = np.dtype[np.str0] - -dtype_per_name: Dict[str, np.dtype[Any]] -name_per_dtype: Dict[np.dtype[Any], str] diff --git a/tests/test_ndarray.py b/tests/test_ndarray.py index 9e45c1e..62ae367 100644 --- a/tests/test_ndarray.py +++ b/tests/test_ndarray.py @@ -7,11 +7,12 @@ import json import numpy as np from pydantic import BaseModel, ValidationError, Field -from nptyping import Number + from numpydantic import NDArray, Shape from numpydantic.exceptions import ShapeError, DtypeError from numpydantic import dtype +from numpydantic.dtype import Number def test_ndarray_type(): diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..9f1fefc --- /dev/null +++ b/tox.ini @@ -0,0 +1,14 @@ +[tox] +requires = + tox>=4 +env_list = py{39,310,311,312}-numpy{1,2} + +[testenv] +package = editable +extras = + tests +deps = + py{39,310,311,312}-numpy1: numpy<2.0.0 + py{39,310,311,312}-numpy2: numpy>=2.0.0 +commands = + python -m pytest From c57a6d614b106d316c659bef1690882c11a6f288 Mon Sep 17 00:00:00 2001 From: sneakers-the-rat Date: Wed, 31 Jul 2024 15:53:26 -0700 Subject: [PATCH 07/15] update lockfile --- pdm.lock | 407 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 206 insertions(+), 201 deletions(-) diff --git a/pdm.lock b/pdm.lock index ed47804..41818a0 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "arrays", "dask", "dev", "docs", "hdf5", "tests", "video"] strategy = ["cross_platform", "inherit_metadata"] lock_version = "4.4.2" -content_hash = "sha256:221d8b83742574a9be8f41986d0def3392a7b248d1bb4fa5d3676e5fd2ebbefb" +content_hash = "sha256:62c153ae2eb738b173ac882538810a727eee2ac364ea674c0d8a1ef310bbefd6" [[package]] name = "alabaster" @@ -133,13 +133,13 @@ files = [ [[package]] name = "certifi" -version = "2024.6.2" +version = "2024.7.4" requires_python = ">=3.6" summary = "Python package for providing Mozilla's CA Bundle." groups = ["dev", "docs", "tests"] files = [ - {file = "certifi-2024.6.2-py3-none-any.whl", hash = "sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56"}, - {file = "certifi-2024.6.2.tar.gz", hash = "sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516"}, + {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, + {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, ] [[package]] @@ -352,7 +352,7 @@ files = [ [[package]] name = "dask" -version = "2024.6.2" +version = "2024.7.1" requires_python = ">=3.9" summary = "Parallel PyData with Task Scheduling" groups = ["arrays", "dask", "dev", "tests"] @@ -362,13 +362,13 @@ dependencies = [ "fsspec>=2021.09.0", "importlib-metadata>=4.13.0; python_version < \"3.12\"", "packaging>=20.0", - "partd>=1.2.0", + "partd>=1.4.0", "pyyaml>=5.3.1", "toolz>=0.10.0", ] files = [ - {file = "dask-2024.6.2-py3-none-any.whl", hash = "sha256:81b80ee015b2e057b93bb2d1bf13a866136e762e2b24bf54b6b621e8b86b7708"}, - {file = "dask-2024.6.2.tar.gz", hash = "sha256:d429d6b19e85fd1306ac37c188aaf99d03bbe69a6fe59d2b42882b2ac188686f"}, + {file = "dask-2024.7.1-py3-none-any.whl", hash = "sha256:dd046840050376c317de90629db5c6197adda820176cf3e2df10c3219d11951f"}, + {file = "dask-2024.7.1.tar.gz", hash = "sha256:dbaef2d50efee841a9d981a218cfeb50392fc9a95e0403b6d680450e4f50d531"}, ] [[package]] @@ -393,14 +393,14 @@ files = [ [[package]] name = "exceptiongroup" -version = "1.2.1" +version = "1.2.2" requires_python = ">=3.7" summary = "Backport of PEP 654 (exception groups)" groups = ["dev", "tests"] marker = "python_version < \"3.11\"" files = [ - {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"}, - {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"}, + {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, + {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, ] [[package]] @@ -428,7 +428,7 @@ files = [ [[package]] name = "furo" -version = "2024.5.6" +version = "2024.7.18" requires_python = ">=3.8" summary = "A clean customisable Sphinx documentation theme." groups = ["dev", "docs"] @@ -439,8 +439,8 @@ dependencies = [ "sphinx<8.0,>=6.0", ] files = [ - {file = "furo-2024.5.6-py3-none-any.whl", hash = "sha256:490a00d08c0a37ecc90de03ae9227e8eb5d6f7f750edf9807f398a2bdf2358de"}, - {file = "furo-2024.5.6.tar.gz", hash = "sha256:81f205a6605ebccbb883350432b4831c0196dd3d1bc92f61e1f459045b3d2b0b"}, + {file = "furo-2024.7.18-py3-none-any.whl", hash = "sha256:b192c7c1f59805494c8ed606d9375fdac6e6ba8178e747e72bc116745fb7e13f"}, + {file = "furo-2024.7.18.tar.gz", hash = "sha256:37b08c5fccc95d46d8712c8be97acd46043963895edde05b0f4f135d58325c83"}, ] [[package]] @@ -518,7 +518,7 @@ files = [ [[package]] name = "importlib-metadata" -version = "8.0.0" +version = "8.2.0" requires_python = ">=3.8" summary = "Read metadata from Python packages" groups = ["arrays", "dask", "dev", "docs", "tests"] @@ -527,8 +527,8 @@ dependencies = [ "zipp>=0.5", ] files = [ - {file = "importlib_metadata-8.0.0-py3-none-any.whl", hash = "sha256:15584cf2b1bf449d98ff8a6ff1abef57bf20f3ac6454f431736cd3e660921b2f"}, - {file = "importlib_metadata-8.0.0.tar.gz", hash = "sha256:188bd24e4c346d3f0a933f275c2fec67050326a856b9a359881d7c2a697e8812"}, + {file = "importlib_metadata-8.2.0-py3-none-any.whl", hash = "sha256:11901fa0c2f97919b288679932bb64febaeacf289d18ac84dd68cb2e74213369"}, + {file = "importlib_metadata-8.2.0.tar.gz", hash = "sha256:72e8d4399996132204f9a16dcc751af254a48f8d1b20b9ff0f98d4a8f901e73d"}, ] [[package]] @@ -697,21 +697,6 @@ files = [ {file = "networkx-3.2.1.tar.gz", hash = "sha256:9f1bb5cf3409bf324e0a722c20bdb4c20ee39bf1c30ce8ae499c8502b0b5e0c6"}, ] -[[package]] -name = "nptyping" -version = "2.5.0" -requires_python = ">=3.7" -summary = "Type hints for NumPy." -groups = ["arrays", "default", "dev", "tests"] -dependencies = [ - "numpy<2.0.0,>=1.20.0; python_version >= \"3.8\"", - "typing-extensions<5.0.0,>=4.0.0; python_version < \"3.10\"", -] -files = [ - {file = "nptyping-2.5.0-py3-none-any.whl", hash = "sha256:764e51836faae33a7ae2e928af574cfb701355647accadcc89f2ad793630b7c8"}, - {file = "nptyping-2.5.0.tar.gz", hash = "sha256:e3d35b53af967e6fb407c3016ff9abae954d3a0568f7cc13a461084224e8e20a"}, -] - [[package]] name = "numcodecs" version = "0.12.1" @@ -743,47 +728,56 @@ files = [ [[package]] name = "numpy" -version = "1.26.4" +version = "2.0.1" requires_python = ">=3.9" summary = "Fundamental package for array computing in Python" groups = ["arrays", "default", "dev", "hdf5", "tests", "video"] files = [ - {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, - {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, - {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"}, - {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"}, - {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"}, - {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"}, - {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"}, - {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"}, - {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, - {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, - {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, - {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, - {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, - {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, - {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, - {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, - {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, - {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, - {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, - {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, - {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, - {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, - {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, - {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, - {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"}, - {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"}, - {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"}, - {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"}, - {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"}, - {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"}, - {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"}, - {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"}, - {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"}, - {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"}, - {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"}, - {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, + {file = "numpy-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0fbb536eac80e27a2793ffd787895242b7f18ef792563d742c2d673bfcb75134"}, + {file = "numpy-2.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:69ff563d43c69b1baba77af455dd0a839df8d25e8590e79c90fcbe1499ebde42"}, + {file = "numpy-2.0.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:1b902ce0e0a5bb7704556a217c4f63a7974f8f43e090aff03fcf262e0b135e02"}, + {file = "numpy-2.0.1-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:f1659887361a7151f89e79b276ed8dff3d75877df906328f14d8bb40bb4f5101"}, + {file = "numpy-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4658c398d65d1b25e1760de3157011a80375da861709abd7cef3bad65d6543f9"}, + {file = "numpy-2.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4127d4303b9ac9f94ca0441138acead39928938660ca58329fe156f84b9f3015"}, + {file = "numpy-2.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e5eeca8067ad04bc8a2a8731183d51d7cbaac66d86085d5f4766ee6bf19c7f87"}, + {file = "numpy-2.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9adbd9bb520c866e1bfd7e10e1880a1f7749f1f6e5017686a5fbb9b72cf69f82"}, + {file = "numpy-2.0.1-cp310-cp310-win32.whl", hash = "sha256:7b9853803278db3bdcc6cd5beca37815b133e9e77ff3d4733c247414e78eb8d1"}, + {file = "numpy-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:81b0893a39bc5b865b8bf89e9ad7807e16717f19868e9d234bdaf9b1f1393868"}, + {file = "numpy-2.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:75b4e316c5902d8163ef9d423b1c3f2f6252226d1aa5cd8a0a03a7d01ffc6268"}, + {file = "numpy-2.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6e4eeb6eb2fced786e32e6d8df9e755ce5be920d17f7ce00bc38fcde8ccdbf9e"}, + {file = "numpy-2.0.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a1e01dcaab205fbece13c1410253a9eea1b1c9b61d237b6fa59bcc46e8e89343"}, + {file = "numpy-2.0.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:a8fc2de81ad835d999113ddf87d1ea2b0f4704cbd947c948d2f5513deafe5a7b"}, + {file = "numpy-2.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a3d94942c331dd4e0e1147f7a8699a4aa47dffc11bf8a1523c12af8b2e91bbe"}, + {file = "numpy-2.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15eb4eca47d36ec3f78cde0a3a2ee24cf05ca7396ef808dda2c0ddad7c2bde67"}, + {file = "numpy-2.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b83e16a5511d1b1f8a88cbabb1a6f6a499f82c062a4251892d9ad5d609863fb7"}, + {file = "numpy-2.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f87fec1f9bc1efd23f4227becff04bd0e979e23ca50cc92ec88b38489db3b55"}, + {file = "numpy-2.0.1-cp311-cp311-win32.whl", hash = "sha256:36d3a9405fd7c511804dc56fc32974fa5533bdeb3cd1604d6b8ff1d292b819c4"}, + {file = "numpy-2.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:08458fbf403bff5e2b45f08eda195d4b0c9b35682311da5a5a0a0925b11b9bd8"}, + {file = "numpy-2.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6bf4e6f4a2a2e26655717a1983ef6324f2664d7011f6ef7482e8c0b3d51e82ac"}, + {file = "numpy-2.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6fddc5fe258d3328cd8e3d7d3e02234c5d70e01ebe377a6ab92adb14039cb4"}, + {file = "numpy-2.0.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:5daab361be6ddeb299a918a7c0864fa8618af66019138263247af405018b04e1"}, + {file = "numpy-2.0.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:ea2326a4dca88e4a274ba3a4405eb6c6467d3ffbd8c7d38632502eaae3820587"}, + {file = "numpy-2.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:529af13c5f4b7a932fb0e1911d3a75da204eff023ee5e0e79c1751564221a5c8"}, + {file = "numpy-2.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6790654cb13eab303d8402354fabd47472b24635700f631f041bd0b65e37298a"}, + {file = "numpy-2.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cbab9fc9c391700e3e1287666dfd82d8666d10e69a6c4a09ab97574c0b7ee0a7"}, + {file = "numpy-2.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:99d0d92a5e3613c33a5f01db206a33f8fdf3d71f2912b0de1739894668b7a93b"}, + {file = "numpy-2.0.1-cp312-cp312-win32.whl", hash = "sha256:173a00b9995f73b79eb0191129f2455f1e34c203f559dd118636858cc452a1bf"}, + {file = "numpy-2.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:bb2124fdc6e62baae159ebcfa368708867eb56806804d005860b6007388df171"}, + {file = "numpy-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bfc085b28d62ff4009364e7ca34b80a9a080cbd97c2c0630bb5f7f770dae9414"}, + {file = "numpy-2.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8fae4ebbf95a179c1156fab0b142b74e4ba4204c87bde8d3d8b6f9c34c5825ef"}, + {file = "numpy-2.0.1-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:72dc22e9ec8f6eaa206deb1b1355eb2e253899d7347f5e2fae5f0af613741d06"}, + {file = "numpy-2.0.1-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:ec87f5f8aca726117a1c9b7083e7656a9d0d606eec7299cc067bb83d26f16e0c"}, + {file = "numpy-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f682ea61a88479d9498bf2091fdcd722b090724b08b31d63e022adc063bad59"}, + {file = "numpy-2.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8efc84f01c1cd7e34b3fb310183e72fcdf55293ee736d679b6d35b35d80bba26"}, + {file = "numpy-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3fdabe3e2a52bc4eff8dc7a5044342f8bd9f11ef0934fcd3289a788c0eb10018"}, + {file = "numpy-2.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:24a0e1befbfa14615b49ba9659d3d8818a0f4d8a1c5822af8696706fbda7310c"}, + {file = "numpy-2.0.1-cp39-cp39-win32.whl", hash = "sha256:f9cf5ea551aec449206954b075db819f52adc1638d46a6738253a712d553c7b4"}, + {file = "numpy-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:e9e81fa9017eaa416c056e5d9e71be93d05e2c3c2ab308d23307a8bc4443c368"}, + {file = "numpy-2.0.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:61728fba1e464f789b11deb78a57805c70b2ed02343560456190d0501ba37b0f"}, + {file = "numpy-2.0.1-pp39-pypy39_pp73-macosx_14_0_x86_64.whl", hash = "sha256:12f5d865d60fb9734e60a60f1d5afa6d962d8d4467c120a1c0cda6eb2964437d"}, + {file = "numpy-2.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eacf3291e263d5a67d8c1a581a8ebbcfd6447204ef58828caf69a5e3e8c75990"}, + {file = "numpy-2.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2c3a346ae20cfd80b6cfd3e60dc179963ef2ea58da5ec074fd3d9e7a1e7ba97f"}, + {file = "numpy-2.0.1.tar.gz", hash = "sha256:485b87235796410c3519a699cfe1faab097e509e90ebb05dcd098db2ae87e7b3"}, ] [[package]] @@ -874,23 +868,24 @@ files = [ [[package]] name = "pydantic" -version = "2.7.4" +version = "2.8.2" requires_python = ">=3.8" summary = "Data validation using Python type hints" groups = ["arrays", "default", "dev", "docs", "tests"] dependencies = [ "annotated-types>=0.4.0", - "pydantic-core==2.18.4", - "typing-extensions>=4.6.1", + "pydantic-core==2.20.1", + "typing-extensions>=4.12.2; python_version >= \"3.13\"", + "typing-extensions>=4.6.1; python_version < \"3.13\"", ] files = [ - {file = "pydantic-2.7.4-py3-none-any.whl", hash = "sha256:ee8538d41ccb9c0a9ad3e0e5f07bf15ed8015b481ced539a1759d8cc89ae90d0"}, - {file = "pydantic-2.7.4.tar.gz", hash = "sha256:0c84efd9548d545f63ac0060c1e4d39bb9b14db8b3c0652338aecc07b5adec52"}, + {file = "pydantic-2.8.2-py3-none-any.whl", hash = "sha256:73ee9fddd406dc318b885c7a2eab8a6472b68b8fb5ba8150949fc3db939f23c8"}, + {file = "pydantic-2.8.2.tar.gz", hash = "sha256:6f62c13d067b0755ad1c21a34bdd06c0c12625a22b0fc09c6b149816604f7c2a"}, ] [[package]] name = "pydantic-core" -version = "2.18.4" +version = "2.20.1" requires_python = ">=3.8" summary = "Core functionality for Pydantic validation and serialization" groups = ["arrays", "default", "dev", "docs", "tests"] @@ -898,78 +893,88 @@ dependencies = [ "typing-extensions!=4.7.0,>=4.6.0", ] files = [ - {file = "pydantic_core-2.18.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f76d0ad001edd426b92233d45c746fd08f467d56100fd8f30e9ace4b005266e4"}, - {file = "pydantic_core-2.18.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:59ff3e89f4eaf14050c8022011862df275b552caef8082e37b542b066ce1ff26"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a55b5b16c839df1070bc113c1f7f94a0af4433fcfa1b41799ce7606e5c79ce0a"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4d0dcc59664fcb8974b356fe0a18a672d6d7cf9f54746c05f43275fc48636851"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8951eee36c57cd128f779e641e21eb40bc5073eb28b2d23f33eb0ef14ffb3f5d"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4701b19f7e3a06ea655513f7938de6f108123bf7c86bbebb1196eb9bd35cf724"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e00a3f196329e08e43d99b79b286d60ce46bed10f2280d25a1718399457e06be"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:97736815b9cc893b2b7f663628e63f436018b75f44854c8027040e05230eeddb"}, - {file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6891a2ae0e8692679c07728819b6e2b822fb30ca7445f67bbf6509b25a96332c"}, - {file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bc4ff9805858bd54d1a20efff925ccd89c9d2e7cf4986144b30802bf78091c3e"}, - {file = "pydantic_core-2.18.4-cp310-none-win32.whl", hash = "sha256:1b4de2e51bbcb61fdebd0ab86ef28062704f62c82bbf4addc4e37fa4b00b7cbc"}, - {file = "pydantic_core-2.18.4-cp310-none-win_amd64.whl", hash = "sha256:6a750aec7bf431517a9fd78cb93c97b9b0c496090fee84a47a0d23668976b4b0"}, - {file = "pydantic_core-2.18.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:942ba11e7dfb66dc70f9ae66b33452f51ac7bb90676da39a7345e99ffb55402d"}, - {file = "pydantic_core-2.18.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b2ebef0e0b4454320274f5e83a41844c63438fdc874ea40a8b5b4ecb7693f1c4"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a642295cd0c8df1b86fc3dced1d067874c353a188dc8e0f744626d49e9aa51c4"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f09baa656c904807e832cf9cce799c6460c450c4ad80803517032da0cd062e2"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98906207f29bc2c459ff64fa007afd10a8c8ac080f7e4d5beff4c97086a3dabd"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19894b95aacfa98e7cb093cd7881a0c76f55731efad31073db4521e2b6ff5b7d"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fbbdc827fe5e42e4d196c746b890b3d72876bdbf160b0eafe9f0334525119c8"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f85d05aa0918283cf29a30b547b4df2fbb56b45b135f9e35b6807cb28bc47951"}, - {file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e85637bc8fe81ddb73fda9e56bab24560bdddfa98aa64f87aaa4e4b6730c23d2"}, - {file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2f5966897e5461f818e136b8451d0551a2e77259eb0f73a837027b47dc95dab9"}, - {file = "pydantic_core-2.18.4-cp311-none-win32.whl", hash = "sha256:44c7486a4228413c317952e9d89598bcdfb06399735e49e0f8df643e1ccd0558"}, - {file = "pydantic_core-2.18.4-cp311-none-win_amd64.whl", hash = "sha256:8a7164fe2005d03c64fd3b85649891cd4953a8de53107940bf272500ba8a788b"}, - {file = "pydantic_core-2.18.4-cp311-none-win_arm64.whl", hash = "sha256:4e99bc050fe65c450344421017f98298a97cefc18c53bb2f7b3531eb39bc7805"}, - {file = "pydantic_core-2.18.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:6f5c4d41b2771c730ea1c34e458e781b18cc668d194958e0112455fff4e402b2"}, - {file = "pydantic_core-2.18.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2fdf2156aa3d017fddf8aea5adfba9f777db1d6022d392b682d2a8329e087cef"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4748321b5078216070b151d5271ef3e7cc905ab170bbfd27d5c83ee3ec436695"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:847a35c4d58721c5dc3dba599878ebbdfd96784f3fb8bb2c356e123bdcd73f34"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c40d4eaad41f78e3bbda31b89edc46a3f3dc6e171bf0ecf097ff7a0ffff7cb1"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:21a5e440dbe315ab9825fcd459b8814bb92b27c974cbc23c3e8baa2b76890077"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01dd777215e2aa86dfd664daed5957704b769e726626393438f9c87690ce78c3"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4b06beb3b3f1479d32befd1f3079cc47b34fa2da62457cdf6c963393340b56e9"}, - {file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:564d7922e4b13a16b98772441879fcdcbe82ff50daa622d681dd682175ea918c"}, - {file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0eb2a4f660fcd8e2b1c90ad566db2b98d7f3f4717c64fe0a83e0adb39766d5b8"}, - {file = "pydantic_core-2.18.4-cp312-none-win32.whl", hash = "sha256:8b8bab4c97248095ae0c4455b5a1cd1cdd96e4e4769306ab19dda135ea4cdb07"}, - {file = "pydantic_core-2.18.4-cp312-none-win_amd64.whl", hash = "sha256:14601cdb733d741b8958224030e2bfe21a4a881fb3dd6fbb21f071cabd48fa0a"}, - {file = "pydantic_core-2.18.4-cp312-none-win_arm64.whl", hash = "sha256:c1322d7dd74713dcc157a2b7898a564ab091ca6c58302d5c7b4c07296e3fd00f"}, - {file = "pydantic_core-2.18.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:44a688331d4a4e2129140a8118479443bd6f1905231138971372fcde37e43528"}, - {file = "pydantic_core-2.18.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a2fdd81edd64342c85ac7cf2753ccae0b79bf2dfa063785503cb85a7d3593223"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86110d7e1907ab36691f80b33eb2da87d780f4739ae773e5fc83fb272f88825f"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46387e38bd641b3ee5ce247563b60c5ca098da9c56c75c157a05eaa0933ed154"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:123c3cec203e3f5ac7b000bd82235f1a3eced8665b63d18be751f115588fea30"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dc1803ac5c32ec324c5261c7209e8f8ce88e83254c4e1aebdc8b0a39f9ddb443"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53db086f9f6ab2b4061958d9c276d1dbe3690e8dd727d6abf2321d6cce37fa94"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:abc267fa9837245cc28ea6929f19fa335f3dc330a35d2e45509b6566dc18be23"}, - {file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a0d829524aaefdebccb869eed855e2d04c21d2d7479b6cada7ace5448416597b"}, - {file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:509daade3b8649f80d4e5ff21aa5673e4ebe58590b25fe42fac5f0f52c6f034a"}, - {file = "pydantic_core-2.18.4-cp39-none-win32.whl", hash = "sha256:ca26a1e73c48cfc54c4a76ff78df3727b9d9f4ccc8dbee4ae3f73306a591676d"}, - {file = "pydantic_core-2.18.4-cp39-none-win_amd64.whl", hash = "sha256:c67598100338d5d985db1b3d21f3619ef392e185e71b8d52bceacc4a7771ea7e"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:574d92eac874f7f4db0ca653514d823a0d22e2354359d0759e3f6a406db5d55d"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1f4d26ceb5eb9eed4af91bebeae4b06c3fb28966ca3a8fb765208cf6b51102ab"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77450e6d20016ec41f43ca4a6c63e9fdde03f0ae3fe90e7c27bdbeaece8b1ed4"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d323a01da91851a4f17bf592faf46149c9169d68430b3146dcba2bb5e5719abc"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43d447dd2ae072a0065389092a231283f62d960030ecd27565672bd40746c507"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:578e24f761f3b425834f297b9935e1ce2e30f51400964ce4801002435a1b41ef"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:81b5efb2f126454586d0f40c4d834010979cb80785173d1586df845a632e4e6d"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ab86ce7c8f9bea87b9d12c7f0af71102acbf5ecbc66c17796cff45dae54ef9a5"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:90afc12421df2b1b4dcc975f814e21bc1754640d502a2fbcc6d41e77af5ec312"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:51991a89639a912c17bef4b45c87bd83593aee0437d8102556af4885811d59f5"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:293afe532740370aba8c060882f7d26cfd00c94cae32fd2e212a3a6e3b7bc15e"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b48ece5bde2e768197a2d0f6e925f9d7e3e826f0ad2271120f8144a9db18d5c8"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:eae237477a873ab46e8dd748e515c72c0c804fb380fbe6c85533c7de51f23a8f"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:834b5230b5dfc0c1ec37b2fda433b271cbbc0e507560b5d1588e2cc1148cf1ce"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e858ac0a25074ba4bce653f9b5d0a85b7456eaddadc0ce82d3878c22489fa4ee"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2fd41f6eff4c20778d717af1cc50eca52f5afe7805ee530a4fbd0bae284f16e9"}, - {file = "pydantic_core-2.18.4.tar.gz", hash = "sha256:ec3beeada09ff865c344ff3bc2f427f5e6c26401cc6113d77e372c3fdac73864"}, + {file = "pydantic_core-2.20.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3acae97ffd19bf091c72df4d726d552c473f3576409b2a7ca36b2f535ffff4a3"}, + {file = "pydantic_core-2.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:41f4c96227a67a013e7de5ff8f20fb496ce573893b7f4f2707d065907bffdbd6"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f239eb799a2081495ea659d8d4a43a8f42cd1fe9ff2e7e436295c38a10c286a"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53e431da3fc53360db73eedf6f7124d1076e1b4ee4276b36fb25514544ceb4a3"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1f62b2413c3a0e846c3b838b2ecd6c7a19ec6793b2a522745b0869e37ab5bc1"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d41e6daee2813ecceea8eda38062d69e280b39df793f5a942fa515b8ed67953"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d482efec8b7dc6bfaedc0f166b2ce349df0011f5d2f1f25537ced4cfc34fd98"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e93e1a4b4b33daed65d781a57a522ff153dcf748dee70b40c7258c5861e1768a"}, + {file = "pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7c4ea22b6739b162c9ecaaa41d718dfad48a244909fe7ef4b54c0b530effc5a"}, + {file = "pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4f2790949cf385d985a31984907fecb3896999329103df4e4983a4a41e13e840"}, + {file = "pydantic_core-2.20.1-cp310-none-win32.whl", hash = "sha256:5e999ba8dd90e93d57410c5e67ebb67ffcaadcea0ad973240fdfd3a135506250"}, + {file = "pydantic_core-2.20.1-cp310-none-win_amd64.whl", hash = "sha256:512ecfbefef6dac7bc5eaaf46177b2de58cdf7acac8793fe033b24ece0b9566c"}, + {file = "pydantic_core-2.20.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d2a8fa9d6d6f891f3deec72f5cc668e6f66b188ab14bb1ab52422fe8e644f312"}, + {file = "pydantic_core-2.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:175873691124f3d0da55aeea1d90660a6ea7a3cfea137c38afa0a5ffabe37b88"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37eee5b638f0e0dcd18d21f59b679686bbd18917b87db0193ae36f9c23c355fc"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25e9185e2d06c16ee438ed39bf62935ec436474a6ac4f9358524220f1b236e43"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:150906b40ff188a3260cbee25380e7494ee85048584998c1e66df0c7a11c17a6"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ad4aeb3e9a97286573c03df758fc7627aecdd02f1da04516a86dc159bf70121"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3f3ed29cd9f978c604708511a1f9c2fdcb6c38b9aae36a51905b8811ee5cbf1"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b0dae11d8f5ded51699c74d9548dcc5938e0804cc8298ec0aa0da95c21fff57b"}, + {file = "pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:faa6b09ee09433b87992fb5a2859efd1c264ddc37280d2dd5db502126d0e7f27"}, + {file = "pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9dc1b507c12eb0481d071f3c1808f0529ad41dc415d0ca11f7ebfc666e66a18b"}, + {file = "pydantic_core-2.20.1-cp311-none-win32.whl", hash = "sha256:fa2fddcb7107e0d1808086ca306dcade7df60a13a6c347a7acf1ec139aa6789a"}, + {file = "pydantic_core-2.20.1-cp311-none-win_amd64.whl", hash = "sha256:40a783fb7ee353c50bd3853e626f15677ea527ae556429453685ae32280c19c2"}, + {file = "pydantic_core-2.20.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:595ba5be69b35777474fa07f80fc260ea71255656191adb22a8c53aba4479231"}, + {file = "pydantic_core-2.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a4f55095ad087474999ee28d3398bae183a66be4823f753cd7d67dd0153427c9"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9aa05d09ecf4c75157197f27cdc9cfaeb7c5f15021c6373932bf3e124af029f"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e97fdf088d4b31ff4ba35db26d9cc472ac7ef4a2ff2badeabf8d727b3377fc52"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc633a9fe1eb87e250b5c57d389cf28998e4292336926b0b6cdaee353f89a237"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d573faf8eb7e6b1cbbcb4f5b247c60ca8be39fe2c674495df0eb4318303137fe"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26dc97754b57d2fd00ac2b24dfa341abffc380b823211994c4efac7f13b9e90e"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:33499e85e739a4b60c9dac710c20a08dc73cb3240c9a0e22325e671b27b70d24"}, + {file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bebb4d6715c814597f85297c332297c6ce81e29436125ca59d1159b07f423eb1"}, + {file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:516d9227919612425c8ef1c9b869bbbee249bc91912c8aaffb66116c0b447ebd"}, + {file = "pydantic_core-2.20.1-cp312-none-win32.whl", hash = "sha256:469f29f9093c9d834432034d33f5fe45699e664f12a13bf38c04967ce233d688"}, + {file = "pydantic_core-2.20.1-cp312-none-win_amd64.whl", hash = "sha256:035ede2e16da7281041f0e626459bcae33ed998cca6a0a007a5ebb73414ac72d"}, + {file = "pydantic_core-2.20.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:0827505a5c87e8aa285dc31e9ec7f4a17c81a813d45f70b1d9164e03a813a686"}, + {file = "pydantic_core-2.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19c0fa39fa154e7e0b7f82f88ef85faa2a4c23cc65aae2f5aea625e3c13c735a"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa223cd1e36b642092c326d694d8bf59b71ddddc94cdb752bbbb1c5c91d833b"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c336a6d235522a62fef872c6295a42ecb0c4e1d0f1a3e500fe949415761b8a19"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7eb6a0587eded33aeefea9f916899d42b1799b7b14b8f8ff2753c0ac1741edac"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70c8daf4faca8da5a6d655f9af86faf6ec2e1768f4b8b9d0226c02f3d6209703"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9fa4c9bf273ca41f940bceb86922a7667cd5bf90e95dbb157cbb8441008482c"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:11b71d67b4725e7e2a9f6e9c0ac1239bbc0c48cce3dc59f98635efc57d6dac83"}, + {file = "pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:270755f15174fb983890c49881e93f8f1b80f0b5e3a3cc1394a255706cabd203"}, + {file = "pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c81131869240e3e568916ef4c307f8b99583efaa60a8112ef27a366eefba8ef0"}, + {file = "pydantic_core-2.20.1-cp313-none-win32.whl", hash = "sha256:b91ced227c41aa29c672814f50dbb05ec93536abf8f43cd14ec9521ea09afe4e"}, + {file = "pydantic_core-2.20.1-cp313-none-win_amd64.whl", hash = "sha256:65db0f2eefcaad1a3950f498aabb4875c8890438bc80b19362cf633b87a8ab20"}, + {file = "pydantic_core-2.20.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:b03f7941783b4c4a26051846dea594628b38f6940a2fdc0df00b221aed39314c"}, + {file = "pydantic_core-2.20.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1eedfeb6089ed3fad42e81a67755846ad4dcc14d73698c120a82e4ccf0f1f9f6"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:635fee4e041ab9c479e31edda27fcf966ea9614fff1317e280d99eb3e5ab6fe2"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:77bf3ac639c1ff567ae3b47f8d4cc3dc20f9966a2a6dd2311dcc055d3d04fb8a"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ed1b0132f24beeec5a78b67d9388656d03e6a7c837394f99257e2d55b461611"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6514f963b023aeee506678a1cf821fe31159b925c4b76fe2afa94cc70b3222b"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10d4204d8ca33146e761c79f83cc861df20e7ae9f6487ca290a97702daf56006"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2d036c7187b9422ae5b262badb87a20a49eb6c5238b2004e96d4da1231badef1"}, + {file = "pydantic_core-2.20.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9ebfef07dbe1d93efb94b4700f2d278494e9162565a54f124c404a5656d7ff09"}, + {file = "pydantic_core-2.20.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6b9d9bb600328a1ce523ab4f454859e9d439150abb0906c5a1983c146580ebab"}, + {file = "pydantic_core-2.20.1-cp39-none-win32.whl", hash = "sha256:784c1214cb6dd1e3b15dd8b91b9a53852aed16671cc3fbe4786f4f1db07089e2"}, + {file = "pydantic_core-2.20.1-cp39-none-win_amd64.whl", hash = "sha256:d2fe69c5434391727efa54b47a1e7986bb0186e72a41b203df8f5b0a19a4f669"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a45f84b09ac9c3d35dfcf6a27fd0634d30d183205230a0ebe8373a0e8cfa0906"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d02a72df14dfdbaf228424573a07af10637bd490f0901cee872c4f434a735b94"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2b27e6af28f07e2f195552b37d7d66b150adbaa39a6d327766ffd695799780f"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:084659fac3c83fd674596612aeff6041a18402f1e1bc19ca39e417d554468482"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:242b8feb3c493ab78be289c034a1f659e8826e2233786e36f2893a950a719bb6"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:38cf1c40a921d05c5edc61a785c0ddb4bed67827069f535d794ce6bcded919fc"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e0bbdd76ce9aa5d4209d65f2b27fc6e5ef1312ae6c5333c26db3f5ade53a1e99"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:254ec27fdb5b1ee60684f91683be95e5133c994cc54e86a0b0963afa25c8f8a6"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:407653af5617f0757261ae249d3fba09504d7a71ab36ac057c938572d1bc9331"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:c693e916709c2465b02ca0ad7b387c4f8423d1db7b4649c551f27a529181c5ad"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b5ff4911aea936a47d9376fd3ab17e970cc543d1b68921886e7f64bd28308d1"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:177f55a886d74f1808763976ac4efd29b7ed15c69f4d838bbd74d9d09cf6fa86"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:964faa8a861d2664f0c7ab0c181af0bea66098b1919439815ca8803ef136fc4e"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4dd484681c15e6b9a977c785a345d3e378d72678fd5f1f3c0509608da24f2ac0"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f6d6cff3538391e8486a431569b77921adfcdef14eb18fbf19b7c0a5294d4e6a"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a6d511cc297ff0883bc3708b465ff82d7560193169a8b93260f74ecb0a5e08a7"}, + {file = "pydantic_core-2.20.1.tar.gz", hash = "sha256:26ca695eeee5f9f1aeeb211ffc12f10bcb6f71e2989988fda61dabd65db878d4"}, ] [[package]] name = "pydantic-settings" -version = "2.3.4" +version = "2.4.0" requires_python = ">=3.8" summary = "Settings management using Pydantic" groups = ["dev", "docs"] @@ -978,8 +983,8 @@ dependencies = [ "python-dotenv>=0.21.0", ] files = [ - {file = "pydantic_settings-2.3.4-py3-none-any.whl", hash = "sha256:11ad8bacb68a045f00e4f862c7a718c8a9ec766aa8fd4c32e39a0594b207b53a"}, - {file = "pydantic_settings-2.3.4.tar.gz", hash = "sha256:c5802e3d62b78e82522319bbc9b8f8ffb28ad1c988a99311d04f2a6051fca0a7"}, + {file = "pydantic_settings-2.4.0-py3-none-any.whl", hash = "sha256:bb6849dc067f1687574c12a639e231f3a6feeed0a12d710c1382045c5db1c315"}, + {file = "pydantic_settings-2.4.0.tar.gz", hash = "sha256:ed81c3a0f46392b4d7c0a565c05884e6e54b3456e6f0fe4d8814981172dc9a88"}, ] [[package]] @@ -995,7 +1000,7 @@ files = [ [[package]] name = "pytest" -version = "8.2.2" +version = "8.3.2" requires_python = ">=3.8" summary = "pytest: simple powerful testing with Python" groups = ["dev", "tests"] @@ -1004,12 +1009,12 @@ dependencies = [ "exceptiongroup>=1.0.0rc8; python_version < \"3.11\"", "iniconfig", "packaging", - "pluggy<2.0,>=1.5", + "pluggy<2,>=1.5", "tomli>=1; python_version < \"3.11\"", ] files = [ - {file = "pytest-8.2.2-py3-none-any.whl", hash = "sha256:c434598117762e2bd304e526244f67bf66bbd7b5d6cf22138be51ff661980343"}, - {file = "pytest-8.2.2.tar.gz", hash = "sha256:de4bb8104e201939ccdc688b27a89a7be2079b22e2bd2b07f806b6ba71117977"}, + {file = "pytest-8.3.2-py3-none-any.whl", hash = "sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5"}, + {file = "pytest-8.3.2.tar.gz", hash = "sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce"}, ] [[package]] @@ -1114,29 +1119,29 @@ files = [ [[package]] name = "ruff" -version = "0.5.0" +version = "0.5.5" requires_python = ">=3.7" summary = "An extremely fast Python linter and code formatter, written in Rust." groups = ["dev"] files = [ - {file = "ruff-0.5.0-py3-none-linux_armv6l.whl", hash = "sha256:ee770ea8ab38918f34e7560a597cc0a8c9a193aaa01bfbd879ef43cb06bd9c4c"}, - {file = "ruff-0.5.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:38f3b8327b3cb43474559d435f5fa65dacf723351c159ed0dc567f7ab735d1b6"}, - {file = "ruff-0.5.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7594f8df5404a5c5c8f64b8311169879f6cf42142da644c7e0ba3c3f14130370"}, - {file = "ruff-0.5.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:adc7012d6ec85032bc4e9065110df205752d64010bed5f958d25dbee9ce35de3"}, - {file = "ruff-0.5.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d505fb93b0fabef974b168d9b27c3960714d2ecda24b6ffa6a87ac432905ea38"}, - {file = "ruff-0.5.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9dc5cfd3558f14513ed0d5b70ce531e28ea81a8a3b1b07f0f48421a3d9e7d80a"}, - {file = "ruff-0.5.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:db3ca35265de239a1176d56a464b51557fce41095c37d6c406e658cf80bbb362"}, - {file = "ruff-0.5.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b1a321c4f68809fddd9b282fab6a8d8db796b270fff44722589a8b946925a2a8"}, - {file = "ruff-0.5.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2c4dfcd8d34b143916994b3876b63d53f56724c03f8c1a33a253b7b1e6bf2a7d"}, - {file = "ruff-0.5.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81e5facfc9f4a674c6a78c64d38becfbd5e4f739c31fcd9ce44c849f1fad9e4c"}, - {file = "ruff-0.5.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e589e27971c2a3efff3fadafb16e5aef7ff93250f0134ec4b52052b673cf988d"}, - {file = "ruff-0.5.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d2ffbc3715a52b037bcb0f6ff524a9367f642cdc5817944f6af5479bbb2eb50e"}, - {file = "ruff-0.5.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:cd096e23c6a4f9c819525a437fa0a99d1c67a1b6bb30948d46f33afbc53596cf"}, - {file = "ruff-0.5.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:46e193b36f2255729ad34a49c9a997d506e58f08555366b2108783b3064a0e1e"}, - {file = "ruff-0.5.0-py3-none-win32.whl", hash = "sha256:49141d267100f5ceff541b4e06552e98527870eafa1acc9dec9139c9ec5af64c"}, - {file = "ruff-0.5.0-py3-none-win_amd64.whl", hash = "sha256:e9118f60091047444c1b90952736ee7b1792910cab56e9b9a9ac20af94cd0440"}, - {file = "ruff-0.5.0-py3-none-win_arm64.whl", hash = "sha256:ed5c4df5c1fb4518abcb57725b576659542bdbe93366f4f329e8f398c4b71178"}, - {file = "ruff-0.5.0.tar.gz", hash = "sha256:eb641b5873492cf9bd45bc9c5ae5320648218e04386a5f0c264ad6ccce8226a1"}, + {file = "ruff-0.5.5-py3-none-linux_armv6l.whl", hash = "sha256:605d589ec35d1da9213a9d4d7e7a9c761d90bba78fc8790d1c5e65026c1b9eaf"}, + {file = "ruff-0.5.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:00817603822a3e42b80f7c3298c8269e09f889ee94640cd1fc7f9329788d7bf8"}, + {file = "ruff-0.5.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:187a60f555e9f865a2ff2c6984b9afeffa7158ba6e1eab56cb830404c942b0f3"}, + {file = "ruff-0.5.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe26fc46fa8c6e0ae3f47ddccfbb136253c831c3289bba044befe68f467bfb16"}, + {file = "ruff-0.5.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4ad25dd9c5faac95c8e9efb13e15803cd8bbf7f4600645a60ffe17c73f60779b"}, + {file = "ruff-0.5.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f70737c157d7edf749bcb952d13854e8f745cec695a01bdc6e29c29c288fc36e"}, + {file = "ruff-0.5.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:cfd7de17cef6ab559e9f5ab859f0d3296393bc78f69030967ca4d87a541b97a0"}, + {file = "ruff-0.5.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a09b43e02f76ac0145f86a08e045e2ea452066f7ba064fd6b0cdccb486f7c3e7"}, + {file = "ruff-0.5.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d0b856cb19c60cd40198be5d8d4b556228e3dcd545b4f423d1ad812bfdca5884"}, + {file = "ruff-0.5.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3687d002f911e8a5faf977e619a034d159a8373514a587249cc00f211c67a091"}, + {file = "ruff-0.5.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ac9dc814e510436e30d0ba535f435a7f3dc97f895f844f5b3f347ec8c228a523"}, + {file = "ruff-0.5.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:af9bdf6c389b5add40d89b201425b531e0a5cceb3cfdcc69f04d3d531c6be74f"}, + {file = "ruff-0.5.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d40a8533ed545390ef8315b8e25c4bb85739b90bd0f3fe1280a29ae364cc55d8"}, + {file = "ruff-0.5.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:cab904683bf9e2ecbbe9ff235bfe056f0eba754d0168ad5407832928d579e7ab"}, + {file = "ruff-0.5.5-py3-none-win32.whl", hash = "sha256:696f18463b47a94575db635ebb4c178188645636f05e934fdf361b74edf1bb2d"}, + {file = "ruff-0.5.5-py3-none-win_amd64.whl", hash = "sha256:50f36d77f52d4c9c2f1361ccbfbd09099a1b2ea5d2b2222c586ab08885cf3445"}, + {file = "ruff-0.5.5-py3-none-win_arm64.whl", hash = "sha256:3191317d967af701f1b73a31ed5788795936e423b7acce82a2b63e26eb3e89d6"}, + {file = "ruff-0.5.5.tar.gz", hash = "sha256:cc5516bdb4858d972fbc31d246bdb390eab8df1a26e2353be2dbc0c2d7f5421a"}, ] [[package]] @@ -1173,22 +1178,22 @@ files = [ [[package]] name = "sphinx" -version = "7.3.7" +version = "7.4.7" requires_python = ">=3.9" summary = "Python documentation generator" groups = ["dev", "docs"] dependencies = [ - "Jinja2>=3.0", - "Pygments>=2.14", + "Jinja2>=3.1", + "Pygments>=2.17", "alabaster~=0.7.14", - "babel>=2.9", - "colorama>=0.4.5; sys_platform == \"win32\"", - "docutils<0.22,>=0.18.1", + "babel>=2.13", + "colorama>=0.4.6; sys_platform == \"win32\"", + "docutils<0.22,>=0.20", "imagesize>=1.3", - "importlib-metadata>=4.8; python_version < \"3.10\"", - "packaging>=21.0", - "requests>=2.25.0", - "snowballstemmer>=2.0", + "importlib-metadata>=6.0; python_version < \"3.10\"", + "packaging>=23.0", + "requests>=2.30.0", + "snowballstemmer>=2.2", "sphinxcontrib-applehelp", "sphinxcontrib-devhelp", "sphinxcontrib-htmlhelp>=2.0.0", @@ -1198,8 +1203,8 @@ dependencies = [ "tomli>=2; python_version < \"3.11\"", ] files = [ - {file = "sphinx-7.3.7-py3-none-any.whl", hash = "sha256:413f75440be4cacf328f580b4274ada4565fb2187d696a84970c23f77b64d8c3"}, - {file = "sphinx-7.3.7.tar.gz", hash = "sha256:a4a7db75ed37531c05002d56ed6948d4c42f473a36f46e1382b0bd76ca9627bc"}, + {file = "sphinx-7.4.7-py3-none-any.whl", hash = "sha256:c2419e2135d11f1951cd994d6eb18a1835bd8fdd8429f9ca375dc1f3281bd239"}, + {file = "sphinx-7.4.7.tar.gz", hash = "sha256:242f92a7ea7e6c5b406fdc2615413890ba9f699114a9c09192d7dfead2ee9cfe"}, ] [[package]] @@ -1251,35 +1256,35 @@ files = [ [[package]] name = "sphinxcontrib-applehelp" -version = "1.0.8" +version = "2.0.0" requires_python = ">=3.9" summary = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" groups = ["dev", "docs"] files = [ - {file = "sphinxcontrib_applehelp-1.0.8-py3-none-any.whl", hash = "sha256:cb61eb0ec1b61f349e5cc36b2028e9e7ca765be05e49641c97241274753067b4"}, - {file = "sphinxcontrib_applehelp-1.0.8.tar.gz", hash = "sha256:c40a4f96f3776c4393d933412053962fac2b84f4c99a7982ba42e09576a70619"}, + {file = "sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5"}, + {file = "sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1"}, ] [[package]] name = "sphinxcontrib-devhelp" -version = "1.0.6" +version = "2.0.0" requires_python = ">=3.9" summary = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents" groups = ["dev", "docs"] files = [ - {file = "sphinxcontrib_devhelp-1.0.6-py3-none-any.whl", hash = "sha256:6485d09629944511c893fa11355bda18b742b83a2b181f9a009f7e500595c90f"}, - {file = "sphinxcontrib_devhelp-1.0.6.tar.gz", hash = "sha256:9893fd3f90506bc4b97bdb977ceb8fbd823989f4316b28c3841ec128544372d3"}, + {file = "sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2"}, + {file = "sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad"}, ] [[package]] name = "sphinxcontrib-htmlhelp" -version = "2.0.5" +version = "2.1.0" requires_python = ">=3.9" summary = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" groups = ["dev", "docs"] files = [ - {file = "sphinxcontrib_htmlhelp-2.0.5-py3-none-any.whl", hash = "sha256:393f04f112b4d2f53d93448d4bce35842f62b307ccdc549ec1585e950bc35e04"}, - {file = "sphinxcontrib_htmlhelp-2.0.5.tar.gz", hash = "sha256:0dc87637d5de53dd5eec3a6a01753b1ccf99494bd756aafecd74b4fa9e729015"}, + {file = "sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8"}, + {file = "sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9"}, ] [[package]] @@ -1306,29 +1311,29 @@ files = [ [[package]] name = "sphinxcontrib-qthelp" -version = "1.0.7" +version = "2.0.0" requires_python = ">=3.9" summary = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents" groups = ["dev", "docs"] files = [ - {file = "sphinxcontrib_qthelp-1.0.7-py3-none-any.whl", hash = "sha256:e2ae3b5c492d58fcbd73281fbd27e34b8393ec34a073c792642cd8e529288182"}, - {file = "sphinxcontrib_qthelp-1.0.7.tar.gz", hash = "sha256:053dedc38823a80a7209a80860b16b722e9e0209e32fea98c90e4e6624588ed6"}, + {file = "sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb"}, + {file = "sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab"}, ] [[package]] name = "sphinxcontrib-serializinghtml" -version = "1.1.10" +version = "2.0.0" requires_python = ">=3.9" summary = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)" groups = ["dev", "docs"] files = [ - {file = "sphinxcontrib_serializinghtml-1.1.10-py3-none-any.whl", hash = "sha256:326369b8df80a7d2d8d7f99aa5ac577f51ea51556ed974e7716cfd4fca3f6cb7"}, - {file = "sphinxcontrib_serializinghtml-1.1.10.tar.gz", hash = "sha256:93f3f5dc458b91b192fe10c397e324f262cf163d79f3282c158e8436a2c4511f"}, + {file = "sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331"}, + {file = "sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d"}, ] [[package]] name = "starlette" -version = "0.37.2" +version = "0.38.2" requires_python = ">=3.8" summary = "The little ASGI library that shines." groups = ["dev"] @@ -1337,8 +1342,8 @@ dependencies = [ "typing-extensions>=3.10.0; python_version < \"3.10\"", ] files = [ - {file = "starlette-0.37.2-py3-none-any.whl", hash = "sha256:6fe59f29268538e5d0d182f2791a479a0c64638e6935d1c6989e63fb2699c6ee"}, - {file = "starlette-0.37.2.tar.gz", hash = "sha256:9af890290133b79fc3db55474ade20f6220a364a0402e0b556e7cd5e1e093823"}, + {file = "starlette-0.38.2-py3-none-any.whl", hash = "sha256:4ec6a59df6bbafdab5f567754481657f7ed90dc9d69b0c9ff017907dd54faeff"}, + {file = "starlette-0.38.2.tar.gz", hash = "sha256:c7c0441065252160993a1a37cf2a73bb64d271b17303e0b0c1eb7191cfb12d75"}, ] [[package]] @@ -1388,7 +1393,7 @@ files = [ [[package]] name = "uvicorn" -version = "0.30.1" +version = "0.30.4" requires_python = ">=3.8" summary = "The lightning-fast ASGI server." groups = ["dev"] @@ -1398,8 +1403,8 @@ dependencies = [ "typing-extensions>=4.0; python_version < \"3.11\"", ] files = [ - {file = "uvicorn-0.30.1-py3-none-any.whl", hash = "sha256:cd17daa7f3b9d7a24de3617820e634d0933b69eed8e33a516071174427238c81"}, - {file = "uvicorn-0.30.1.tar.gz", hash = "sha256:d46cd8e0fd80240baffbcd9ec1012a712938754afcf81bce56c024c1656aece8"}, + {file = "uvicorn-0.30.4-py3-none-any.whl", hash = "sha256:06b00e3087e58c6865c284143c0c42f810b32ff4f265ab19d08c566f74a08728"}, + {file = "uvicorn-0.30.4.tar.gz", hash = "sha256:00db9a9e3711a5fa59866e2b02fac69d8dc70ce0814aaec9a66d1d9e5c832a30"}, ] [[package]] From f4a2ec0fb3038f360308d5184f4dae3047af9621 Mon Sep 17 00:00:00 2001 From: sneakers-the-rat Date: Wed, 31 Jul 2024 15:59:07 -0700 Subject: [PATCH 08/15] fix ruff problem from newer ruff version --- src/numpydantic/vendor/nptyping/base_meta_classes.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/numpydantic/vendor/nptyping/base_meta_classes.py b/src/numpydantic/vendor/nptyping/base_meta_classes.py index d943e20..337e629 100644 --- a/src/numpydantic/vendor/nptyping/base_meta_classes.py +++ b/src/numpydantic/vendor/nptyping/base_meta_classes.py @@ -228,9 +228,7 @@ class ContainerMeta( return (norm_shape_expression,) def __subclasscheck__(cls, subclass: Any) -> bool: - type_match = type(subclass) == type( # pylint: disable=unidiomatic-typecheck - cls - ) + type_match = type(subclass) is type(cls) return type_match and ( subclass.__args__ == cls.__args__ or not cls._parameterized ) From 44777ab70ac85627f1fd1190c62356aa201633c7 Mon Sep 17 00:00:00 2001 From: sneakers-the-rat Date: Wed, 31 Jul 2024 15:59:46 -0700 Subject: [PATCH 09/15] debugging CI --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2c7e079..8da4395 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -29,7 +29,7 @@ jobs: - platform: "windows-latest" python-version: "3.11" - runs-on: ${{ matrix.os }} + runs-on: ${{ matrix.platform }} steps: - uses: actions/checkout@v2 From 2887841bdb7a2ed434ee67b94e24ed372f1ee9b6 Mon Sep 17 00:00:00 2001 From: sneakers-the-rat Date: Wed, 31 Jul 2024 16:04:10 -0700 Subject: [PATCH 10/15] coveralls broken on mac? --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8da4395..50fcc81 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -50,7 +50,7 @@ jobs: run: pytest - name: Coveralls Parallel - uses: coverallsapp/github-action@v2 + uses: coverallsapp/github-action@v2.3.0 with: flag-name: run-${{ join(matrix.*, '-') }} parallel: true @@ -61,6 +61,6 @@ jobs: runs-on: ubuntu-latest steps: - name: Coveralls Finished - uses: coverallsapp/github-action@v2 + uses: coverallsapp/github-action@v2.3.0 with: parallel-finished: true \ No newline at end of file From 528c1f57dc543fca2ac6b2616edc6eb095bdb01e Mon Sep 17 00:00:00 2001 From: sneakers-the-rat Date: Wed, 31 Jul 2024 16:11:42 -0700 Subject: [PATCH 11/15] coveralls broken on mac? --- .github/workflows/tests.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 50fcc81..f86d8ba 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -41,7 +41,7 @@ jobs: cache: 'pip' - name: Install dependencies - run: pip install -e ".[tests,linkml]" + run: pip install -e ".[tests]" - name: Install numpy version run: pip install "numpy${{ matrix.numpy-version }}" @@ -54,6 +54,7 @@ jobs: with: flag-name: run-${{ join(matrix.*, '-') }} parallel: true + debug: true finish-coverage: needs: test From 886cc893d8e5778017d03a4aded4b42bdd23709b Mon Sep 17 00:00:00 2001 From: sneakers-the-rat Date: Wed, 31 Jul 2024 16:20:22 -0700 Subject: [PATCH 12/15] exclude mac from coveralls --- .github/workflows/tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f86d8ba..63979cb 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -51,6 +51,7 @@ jobs: - name: Coveralls Parallel uses: coverallsapp/github-action@v2.3.0 + if: runner.os != 'macOS' with: flag-name: run-${{ join(matrix.*, '-') }} parallel: true From 0a930eed35d4425967907a8c8816413255b11b06 Mon Sep 17 00:00:00 2001 From: sneakers-the-rat Date: Wed, 31 Jul 2024 16:25:59 -0700 Subject: [PATCH 13/15] windows doesn't like to let go of files --- tests/fixtures.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/fixtures.py b/tests/fixtures.py index e780058..7c14b35 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -1,6 +1,7 @@ import shutil from pathlib import Path from typing import Any, Callable, Optional, Tuple, Type, Union +from warnings import warn import h5py import numpy as np @@ -25,7 +26,13 @@ def tmp_output_dir(request: pytest.FixtureRequest) -> Path: yield path if not request.config.getvalue("--with-output"): - shutil.rmtree(str(path)) + try: + shutil.rmtree(str(path)) + except PermissionError as e: + # sporadic error on windows machines... + warn( + f"Temporary directory could not be removed due to a permissions error: \n{str(e)}" + ) @pytest.fixture(scope="function") From a64bb2186f94d6f9bba871628b404a4ea13a8c24 Mon Sep 17 00:00:00 2001 From: sneakers-the-rat Date: Wed, 31 Jul 2024 16:45:48 -0700 Subject: [PATCH 14/15] changelog, bump version, fill in coverage --- docs/changelog.md | 27 +++++++++++++++++++++ pyproject.toml | 2 +- src/numpydantic/schema.py | 4 ++-- tests/test_ndarray.py | 50 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 3 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 389fc07..2df9c76 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -2,6 +2,33 @@ ## 1.* +### 1.2.3 - 24-07-31 - Vendor `nptyping` + +`nptyping` vendored into `numpydantic.vendor.nptyping` - +`nptyping` is no longer maintained, and pins `numpy<2`. +It also has many obnoxious warnings and we have to monkeypatch it +so it performs halfway decently. Since we are en-route to deprecating +usage of `nptyping` anyway, in the meantime we have just vendored it in +(it is MIT licensed, included) so that we can make those changes ourselves +and have to patch less of it. Currently the whole package is vendored with +modifications, but will be whittled away until we have replaced it with +updated type specification system :) + +Bugfix: +- [#2](https://github.com/p2p-ld/numpydantic/issues/2) - Support `numpy>=2` +- Remove deprecated numpy dtypes + +CI: +- Add windows and mac tests +- Add testing with numpy>=2 and <2 + +DevOps: +- Make a tox file for local testing, not used in CI. + +Tidying: +- Remove `monkeypatch` module! we don't need it anymore! + everything has either been upstreamed or vendored. + ### 1.2.2 - 24-07-31 Add `datetime` map to numpy's :class:`numpy.datetime64` type diff --git a/pyproject.toml b/pyproject.toml index 12a1b50..3da4cd6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "numpydantic" -version = "1.2.2" +version = "1.2.3" description = "Type and shape validation and serialization for numpy arrays in pydantic models" authors = [ {name = "sneakers-the-rat", email = "sneakers-the-rat@protonmail.com"}, diff --git a/src/numpydantic/schema.py b/src/numpydantic/schema.py index bf3fd90..9636190 100644 --- a/src/numpydantic/schema.py +++ b/src/numpydantic/schema.py @@ -18,7 +18,7 @@ from numpydantic.maps import np_to_python from numpydantic.types import DtypeType, NDArrayType, ShapeType from numpydantic.vendor.nptyping.structure import StructureMeta -if TYPE_CHECKING: +if TYPE_CHECKING: # pragma: no cover from numpydantic import Shape _handler_type = Callable[[Any], core_schema.CoreSchema] @@ -143,7 +143,7 @@ def list_of_lists_schema(shape: "Shape", array_type: CoreSchema) -> ListSchema: arg = int(arg) arg_min = arg arg_max = arg - except ValueError as e: + except ValueError as e: # pragma: no cover raise ValueError( "Array shapes must be integers, wildcards, ellipses, or " diff --git a/tests/test_ndarray.py b/tests/test_ndarray.py index 62ae367..203a4f2 100644 --- a/tests/test_ndarray.py +++ b/tests/test_ndarray.py @@ -40,6 +40,56 @@ def test_ndarray_type(): instance = Model(array=np.zeros((2, 3)), array_any=np.ones((3, 4, 5))) +def test_schema_unsupported_type(): + """ + Complex numbers should just be made with an `any` schema + """ + + class Model(BaseModel): + array: NDArray[Shape["2 x, * y"], complex] + + schema = Model.model_json_schema() + assert schema["properties"]["array"]["items"] == { + "items": {}, + "type": "array", + } + + +def test_schema_tuple(): + """ + Types specified as tupled should have their schemas as a union + """ + + class Model(BaseModel): + array: NDArray[Shape["2 x, * y"], (np.uint8, np.uint16)] + + schema = Model.model_json_schema() + assert schema["properties"]["array"]["items"] == { + "items": { + "anyOf": [ + {"maximum": 255, "minimum": 0, "type": "integer"}, + {"maximum": 65535, "minimum": 0, "type": "integer"}, + ] + }, + "type": "array", + } + + +def test_schema_number(): + """ + np.numeric should just be the float schema + """ + + class Model(BaseModel): + array: NDArray[Shape["2 x, * y"], np.number] + + schema = Model.model_json_schema() + assert schema["properties"]["array"]["items"] == { + "items": {"type": "number"}, + "type": "array", + } + + def test_ndarray_union(): class Model(BaseModel): array: Optional[ From fef528f3bcc290b44290df8392801b247c759735 Mon Sep 17 00:00:00 2001 From: sneakers-the-rat Date: Wed, 31 Jul 2024 16:51:37 -0700 Subject: [PATCH 15/15] make test order insensitive --- tests/test_ndarray.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/tests/test_ndarray.py b/tests/test_ndarray.py index 203a4f2..7b512ef 100644 --- a/tests/test_ndarray.py +++ b/tests/test_ndarray.py @@ -64,15 +64,12 @@ def test_schema_tuple(): array: NDArray[Shape["2 x, * y"], (np.uint8, np.uint16)] schema = Model.model_json_schema() - assert schema["properties"]["array"]["items"] == { - "items": { - "anyOf": [ - {"maximum": 255, "minimum": 0, "type": "integer"}, - {"maximum": 65535, "minimum": 0, "type": "integer"}, - ] - }, - "type": "array", - } + assert "anyOf" in schema["properties"]["array"]["items"]["items"] + conditions = schema["properties"]["array"]["items"]["items"]["anyOf"] + + assert all([i["type"] == "integer" for i in conditions]) + assert sorted([i["maximum"] for i in conditions]) == [255, 65535] + assert all([i["minimum"] == 0 for i in conditions]) def test_schema_number():