From 3b2e23d194c7a7be855b9a10a1cf37594743bc77 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Thu, 27 Nov 2025 20:02:04 +0100 Subject: [PATCH 1/7] use copy --- .gitignore | 3 + pandas-stubs/_typing.pyi | 88 ++--- tests/__init__.py | 434 +------------------------ tests/arrays/test_extension_array.py | 19 +- tests/arrays/test_floating_array.py | 6 +- tests/conftest.py | 15 + tests/extension/decimal/array.py | 18 +- tests/indexes/test_categoricalindex.py | 12 +- tests/indexes/test_datetime_index.py | 18 +- tests/indexes/test_index_float.py | 3 +- tests/indexes/test_indexes.py | 23 +- tests/indexes/test_rangeindex.py | 18 +- tests/scalars/test_scalars.py | 26 +- tests/series/test_series.py | 58 +++- tests/series/test_series_float.py | 12 +- tests/test_extension.py | 11 +- tests/test_frame.py | 16 +- tests/test_interval.py | 8 +- tests/test_pandas.py | 40 ++- tests/test_string_accessors.py | 7 +- tests/test_styler.py | 14 +- tests/test_timefuncs.py | 36 +- tests/test_windowing.py | 14 +- 23 files changed, 326 insertions(+), 573 deletions(-) create mode 100644 tests/conftest.py diff --git a/.gitignore b/.gitignore index 932deb9a1..8dbfcd6fb 100644 --- a/.gitignore +++ b/.gitignore @@ -135,3 +135,6 @@ dmypy.json .pyre/ /poetry.lock .idea/**/* + +# duplication of stub specific objects for testing +tests/_typing.py diff --git a/pandas-stubs/_typing.pyi b/pandas-stubs/_typing.pyi index bb06c6ff1..018c3443b 100644 --- a/pandas-stubs/_typing.pyi +++ b/pandas-stubs/_typing.pyi @@ -14,6 +14,7 @@ from os import PathLike from re import Pattern import sys from typing import ( + TYPE_CHECKING, Any, Generic, Literal, @@ -25,7 +26,6 @@ from typing import ( overload, ) -from _typeshed import _T_contra import numpy as np from numpy import typing as npt import pandas as pd @@ -87,13 +87,15 @@ HashableT5 = TypeVar("HashableT5", bound=Hashable) ArrayLike: TypeAlias = ExtensionArray | npt.NDArray[Any] AnyArrayLike: TypeAlias = ArrayLike | Index | Series -AnyArrayLikeInt: TypeAlias = ( - IntegerArray | Index[int] | Series[int] | npt.NDArray[np.integer] -) +if TYPE_CHECKING: # noqa: PYI002 + AnyArrayLikeInt: TypeAlias = ( + IntegerArray | Index[int] | Series[int] | npt.NDArray[np.integer] + ) # list-like _T_co = TypeVar("_T_co", covariant=True) +_T_contra = TypeVar("_T_contra", contravariant=True) class SequenceNotStr(Protocol[_T_co]): @overload @@ -966,8 +968,9 @@ class SupportsDType(Protocol[GenericT_co]): # Similar to npt.DTypeLike but leaves out np.dtype and None for use in overloads DTypeLike: TypeAlias = type[Any] | tuple[Any, Any] | list[Any] | str -IndexType: TypeAlias = slice | np_ndarray_anyint | Index | list[int] | Series[int] -MaskType: TypeAlias = Series[bool] | np_ndarray_bool | list[bool] +if TYPE_CHECKING: # noqa: PYI002 + IndexType: TypeAlias = slice | np_ndarray_anyint | Index | list[int] | Series[int] + MaskType: TypeAlias = Series[bool] | np_ndarray_bool | list[bool] # Scratch types for generics @@ -1039,48 +1042,49 @@ Function: TypeAlias = np.ufunc | Callable[..., Any] # shared HashableT and HashableT#. This one can be used if the identical # type is need in a function that uses GroupByObjectNonScalar _HashableTa = TypeVar("_HashableTa", bound=Hashable) -ByT = TypeVar( - "ByT", - bound=str - | bytes - | datetime.date - | datetime.datetime - | datetime.timedelta - | np.datetime64 - | np.timedelta64 - | bool - | int - | float - | complex - | Scalar - | Period - | Interval[int | float | Timestamp | Timedelta] - | tuple, -) -# Use a distinct SeriesByT when using groupby with Series of known dtype. -# Essentially, an intersection between Series S1 TypeVar, and ByT TypeVar -SeriesByT = TypeVar( - "SeriesByT", - bound=str - | bytes - | datetime.date - | bool - | int - | float - | complex - | datetime.datetime - | datetime.timedelta - | Period - | Interval[int | float | Timestamp | Timedelta], -) +if TYPE_CHECKING: # noqa: PYI002 + ByT = TypeVar( + "ByT", + bound=str + | bytes + | datetime.date + | datetime.datetime + | datetime.timedelta + | np.datetime64 + | np.timedelta64 + | bool + | int + | float + | complex + | Scalar + | Period + | Interval[int | float | Timestamp | Timedelta] + | tuple, + ) + # Use a distinct SeriesByT when using groupby with Series of known dtype. + # Essentially, an intersection between Series S1 TypeVar, and ByT TypeVar + SeriesByT = TypeVar( + "SeriesByT", + bound=str + | bytes + | datetime.date + | bool + | int + | float + | complex + | datetime.datetime + | datetime.timedelta + | Period + | Interval[int | float | Timestamp | Timedelta], + ) GroupByObjectNonScalar: TypeAlias = ( tuple | list[_HashableTa] | Function | list[Function] | list[Series] - | np.ndarray - | list[np.ndarray] + | np_ndarray + | list[np_ndarray] | Mapping[Label, Any] | list[Mapping[Label, Any]] | list[Index] diff --git a/tests/__init__.py b/tests/__init__.py index 34530dfee..f11a46a34 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -10,14 +10,11 @@ TYPE_CHECKING, Final, Literal, - TypeAlias, - TypeVar, get_args, get_origin, ) import numpy as np -import numpy.typing as npt import pandas as pd # Next set of imports is to keep the private imports needed for testing @@ -30,436 +27,7 @@ from pandas.core.dtypes.base import ExtensionDtype if TYPE_CHECKING: - from pandas._typing import ( - BooleanDtypeArg as BooleanDtypeArg, - BuiltinDtypeArg as BuiltinDtypeArg, - BytesDtypeArg as BytesDtypeArg, - CategoryDtypeArg as CategoryDtypeArg, - ComplexDtypeArg as ComplexDtypeArg, - Dtype as Dtype, - FloatDtypeArg as FloatDtypeArg, - IntDtypeArg as IntDtypeArg, - NumpyFloat16DtypeArg as NumpyFloat16DtypeArg, - NumpyNotTimeDtypeArg as NumpyNotTimeDtypeArg, - ObjectDtypeArg as ObjectDtypeArg, - PandasAstypeComplexDtypeArg as PandasAstypeComplexDtypeArg, - PandasAstypeFloatDtypeArg as PandasAstypeFloatDtypeArg, - PandasAstypeTimedeltaDtypeArg as PandasAstypeTimedeltaDtypeArg, - PandasAstypeTimestampDtypeArg as PandasAstypeTimestampDtypeArg, - PandasBooleanDtypeArg as PandasBooleanDtypeArg, - PandasFloatDtypeArg as PandasFloatDtypeArg, - StrDtypeArg as StrDtypeArg, - T as T, - TimedeltaDtypeArg as TimedeltaDtypeArg, - TimestampDtypeArg as TimestampDtypeArg, - UIntDtypeArg as UIntDtypeArg, - VoidDtypeArg as VoidDtypeArg, - np_1darray as np_1darray, - np_1darray_anyint as np_1darray_anyint, - np_1darray_bool as np_1darray_bool, - np_1darray_bytes as np_1darray_bytes, - np_1darray_complex as np_1darray_complex, - np_1darray_dt as np_1darray_dt, - np_1darray_float as np_1darray_float, - np_1darray_int64 as np_1darray_int64, - np_1darray_intp as np_1darray_intp, - np_1darray_object as np_1darray_object, - np_1darray_str as np_1darray_str, - np_1darray_td as np_1darray_td, - np_2darray as np_2darray, - np_ndarray as np_ndarray, - np_ndarray_bool as np_ndarray_bool, - np_ndarray_dt as np_ndarray_dt, - np_ndarray_int as np_ndarray_int, - np_ndarray_intp as np_ndarray_intp, - np_ndarray_num as np_ndarray_num, - np_ndarray_str as np_ndarray_str, - np_ndarray_td as np_ndarray_td, - ) -else: - # Builtin bool type and its string alias - BuiltinBooleanDtypeArg: TypeAlias = type[bool] | Literal["bool"] - # Pandas nullable boolean type and its string alias - PandasBooleanDtypeArg: TypeAlias = pd.BooleanDtype | Literal["boolean"] - # Numpy bool type - # https://numpy.org/doc/stable/reference/arrays.scalars.html#numpy.bool_ - NumpyBooleanDtypeArg: TypeAlias = type[np.bool_] | Literal["?", "b1", "bool_"] - # PyArrow boolean type and its string alias - PyArrowBooleanDtypeArg: TypeAlias = Literal["bool[pyarrow]", "boolean[pyarrow]"] - BooleanDtypeArg: TypeAlias = ( - BuiltinBooleanDtypeArg - | PandasBooleanDtypeArg - | NumpyBooleanDtypeArg - | PyArrowBooleanDtypeArg - ) - # Builtin integer type and its string alias - BuiltinIntDtypeArg: TypeAlias = type[int] | Literal["int"] - # Pandas nullable integer types and their string aliases - PandasIntDtypeArg: TypeAlias = ( - pd.Int8Dtype - | pd.Int16Dtype - | pd.Int32Dtype - | pd.Int64Dtype - | Literal["Int8", "Int16", "Int32", "Int64"] - ) - # Numpy signed integer types and their string aliases - NumpyIntDtypeArg: TypeAlias = ( - # https://numpy.org/doc/stable/reference/arrays.scalars.html#numpy.byte - type[np.byte] # noqa: PYI030 - | Literal["b", "i1", "int8", "byte"] - # https://numpy.org/doc/stable/reference/arrays.scalars.html#numpy.short - | type[np.short] - | Literal["h", "i2", "int16", "short"] - # https://numpy.org/doc/stable/reference/arrays.scalars.html#numpy.intc - | type[np.intc] - | Literal["i", "i4", "int32", "intc"] - # https://numpy.org/doc/stable/reference/arrays.scalars.html#numpy.int_ - | type[np.int_] - | Literal["l", "i8", "int64", "int_", "long"] - # https://numpy.org/doc/stable/reference/arrays.scalars.html#numpy.longlong - | type[np.longlong] - | Literal["q", "longlong"] # NOTE: int128 not assigned - # https://numpy.org/doc/stable/reference/arrays.scalars.html#numpy.intp - | type[np.intp] # signed pointer (=`intptr_t`, platform dependent) - | Literal["p", "intp"] - ) - # PyArrow integer types and their string aliases - PyArrowIntDtypeArg: TypeAlias = Literal[ - "int8[pyarrow]", "int16[pyarrow]", "int32[pyarrow]", "int64[pyarrow]" - ] - IntDtypeArg: TypeAlias = ( - BuiltinIntDtypeArg | PandasIntDtypeArg | NumpyIntDtypeArg | PyArrowIntDtypeArg - ) - # Pandas nullable unsigned integer types and their string aliases - PandasUIntDtypeArg: TypeAlias = ( - pd.UInt8Dtype - | pd.UInt16Dtype - | pd.UInt32Dtype - | pd.UInt64Dtype - | Literal["UInt8", "UInt16", "UInt32", "UInt64"] - ) - # Numpy unsigned integer types and their string aliases - NumpyUIntDtypeArg: TypeAlias = ( - # https://numpy.org/doc/stable/reference/arrays.scalars.html#numpy.ubyte - type[np.ubyte] # noqa: PYI030 - | Literal["B", "u1", "uint8", "ubyte"] - # https://numpy.org/doc/stable/reference/arrays.scalars.html#numpy.ushort - | type[np.ushort] - | Literal["H", "u2", "uint16", "ushort"] - # https://numpy.org/doc/stable/reference/arrays.scalars.html#numpy.uintc - | type[np.uintc] - | Literal["I", "u4", "uint32", "uintc"] - # https://numpy.org/doc/stable/reference/arrays.scalars.html#numpy.uint - | type[np.uint] - | Literal["L", "u8", "uint", "ulong", "uint64"] - # https://numpy.org/doc/stable/reference/arrays.scalars.html#numpy.ulonglong - | type[np.ulonglong] - | Literal["Q", "ulonglong"] # NOTE: uint128 not assigned - # https://numpy.org/doc/stable/reference/arrays.scalars.html#numpy.uintp - | type[np.uintp] # unsigned pointer (=`uintptr_t`, platform dependent) - | Literal["P", "uintp"] - ) - # PyArrow unsigned integer types and their string aliases - PyArrowUIntDtypeArg: TypeAlias = Literal[ - "uint8[pyarrow]", "uint16[pyarrow]", "uint32[pyarrow]", "uint64[pyarrow]" - ] - UIntDtypeArg: TypeAlias = ( - PandasUIntDtypeArg | NumpyUIntDtypeArg | PyArrowUIntDtypeArg - ) - # Builtin float type and its string alias - BuiltinFloatDtypeArg: TypeAlias = type[float] | Literal["float"] - # Pandas nullable float types and their string aliases - PandasFloatDtypeArg: TypeAlias = ( - pd.Float32Dtype | pd.Float64Dtype | Literal["Float32", "Float64"] - ) - PandasAstypeFloatDtypeArg: TypeAlias = Literal["float_", "longfloat"] - # Numpy float types and their string aliases - NumpyFloat16DtypeArg: TypeAlias = ( - # NOTE: Alias np.float16 only on Linux x86_64, use np.half instead - # https://numpy.org/doc/stable/reference/arrays.scalars.html#numpy.half - type[np.half] - | Literal["e", "f2", " None: diff --git a/tests/arrays/test_floating_array.py b/tests/arrays/test_floating_array.py index cb3f9c5a6..0c703bd36 100644 --- a/tests/arrays/test_floating_array.py +++ b/tests/arrays/test_floating_array.py @@ -8,11 +8,13 @@ from tests import ( PANDAS_FLOAT_ARGS, - PandasFloatDtypeArg, check, exception_on_platform, ) +if TYPE_CHECKING: + from pandas._typing import PandasFloatDtypeArg + def test_constructor() -> None: check(assert_type(pd.array([1.0]), FloatingArray), FloatingArray) @@ -27,7 +29,7 @@ def test_constructor() -> None: @pytest.mark.parametrize(("dtype", "target_dtype"), PANDAS_FLOAT_ARGS.items(), ids=repr) -def test_constructor_dtype(dtype: PandasFloatDtypeArg, target_dtype: type) -> None: +def test_constructor_dtype(dtype: "PandasFloatDtypeArg", target_dtype: type) -> None: exc = exception_on_platform(dtype) if exc: with pytest.raises(exc, match=rf"data type {dtype!r} not understood"): diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 000000000..8c3d70912 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,15 @@ +from collections.abc import Generator +from pathlib import Path +from shutil import copyfile + +import pytest + +target = Path(__file__).parent / "_typing.py" +copyfile(Path(__file__).parents[1] / "pandas-stubs" / "_typing.pyi", target) + + +@pytest.fixture(autouse=True, scope="session") +def setup_typing_module() -> Generator[None, None, None]: + """Ensure that tests._typing is removed after running tests""" + yield + target.unlink() diff --git a/tests/extension/decimal/array.py b/tests/extension/decimal/array.py index b0ce317f6..c8f5e2ee0 100644 --- a/tests/extension/decimal/array.py +++ b/tests/extension/decimal/array.py @@ -11,6 +11,7 @@ import numbers import sys from typing import ( + TYPE_CHECKING, Any, cast, overload, @@ -52,11 +53,18 @@ pandas_dtype, ) -from tests import ( - np_1darray, - np_1darray_bool, - np_ndarray, -) +if TYPE_CHECKING: + from pandas._typing import ( + np_1darray, + np_1darray_bool, + np_ndarray, + ) +else: + from tests._typing import ( + np_1darray, + np_1darray_bool, + np_ndarray, + ) @register_extension_dtype diff --git a/tests/indexes/test_categoricalindex.py b/tests/indexes/test_categoricalindex.py index dff2b7db8..4c3f4f104 100644 --- a/tests/indexes/test_categoricalindex.py +++ b/tests/indexes/test_categoricalindex.py @@ -1,14 +1,18 @@ from __future__ import annotations +from typing import TYPE_CHECKING + import pandas as pd from typing_extensions import ( assert_type, ) -from tests import ( - check, - np_1darray_intp, -) +from tests import check + +if TYPE_CHECKING: + from pandas._typing import np_1darray_intp +else: + from tests._typing import np_1darray_intp def test_categoricalindex_unique() -> None: diff --git a/tests/indexes/test_datetime_index.py b/tests/indexes/test_datetime_index.py index d91442944..bf2e29b19 100644 --- a/tests/indexes/test_datetime_index.py +++ b/tests/indexes/test_datetime_index.py @@ -1,16 +1,24 @@ from __future__ import annotations from datetime import time +from typing import TYPE_CHECKING import numpy as np import pandas as pd from typing_extensions import assert_type -from tests import ( - check, - np_1darray_bool, - np_1darray_intp, -) +from tests import check + +if TYPE_CHECKING: + from pandas._typing import ( + np_1darray_bool, + np_1darray_intp, + ) +else: + from tests._typing import ( + np_1darray_bool, + np_1darray_intp, + ) def test_index_relops() -> None: diff --git a/tests/indexes/test_index_float.py b/tests/indexes/test_index_float.py index 7c213b590..dad6a83bb 100644 --- a/tests/indexes/test_index_float.py +++ b/tests/indexes/test_index_float.py @@ -8,7 +8,6 @@ from tests import ( ASTYPE_FLOAT_NOT_NUMPY16_ARGS, TYPE_FLOAT_NOT_NUMPY16_ARGS, - PandasAstypeFloatDtypeArg, check, exception_on_platform, ) @@ -16,6 +15,8 @@ if TYPE_CHECKING: from pandas.core.indexes.base import FloatNotNumpy16DtypeArg + from pandas._typing import PandasAstypeFloatDtypeArg + def test_constructor() -> None: check(assert_type(pd.Index([1.0]), "pd.Index[float]"), pd.Index, np.floating) diff --git a/tests/indexes/test_indexes.py b/tests/indexes/test_indexes.py index f155efaff..2f697fc7b 100644 --- a/tests/indexes/test_indexes.py +++ b/tests/indexes/test_indexes.py @@ -27,16 +27,27 @@ PD_LTE_23, TYPE_CHECKING_INVALID_USAGE, check, - np_1darray, - np_1darray_bool, - np_1darray_int64, - np_1darray_intp, - np_ndarray_dt, pytest_warns_bounded, ) if TYPE_CHECKING: - from tests import Dtype # noqa: F401 + from pandas._typing import ( + Dtype, + np_1darray, + np_1darray_bool, + np_1darray_int64, + np_1darray_intp, + np_ndarray_dt, + ) +else: + from tests._typing import ( + np_1darray, + np_1darray_bool, + np_1darray_int64, + np_1darray_intp, + np_ndarray_dt, + ) + from tests._typing import Dtype # noqa: F401 def test_index_unique() -> None: diff --git a/tests/indexes/test_rangeindex.py b/tests/indexes/test_rangeindex.py index 8cdb1b822..7bad9ba1e 100644 --- a/tests/indexes/test_rangeindex.py +++ b/tests/indexes/test_rangeindex.py @@ -1,14 +1,16 @@ from __future__ import annotations +from typing import TYPE_CHECKING + import pandas as pd -from typing_extensions import ( - assert_type, -) - -from tests import ( - check, - np_1darray_intp, -) +from typing_extensions import assert_type + +from tests import check + +if TYPE_CHECKING: + from pandas._typing import np_1darray_intp +else: + from tests._typing import np_1darray_intp def test_rangeindex_floordiv() -> None: diff --git a/tests/scalars/test_scalars.py b/tests/scalars/test_scalars.py index 36c7f59fc..40cf88617 100644 --- a/tests/scalars/test_scalars.py +++ b/tests/scalars/test_scalars.py @@ -3,6 +3,7 @@ import datetime import datetime as dt from typing import ( + TYPE_CHECKING, Any, Literal, TypeAlias, @@ -25,12 +26,6 @@ PD_LTE_23, TYPE_CHECKING_INVALID_USAGE, check, - np_1darray_bool, - np_2darray, - np_ndarray, - np_ndarray_bool, - np_ndarray_dt, - np_ndarray_td, pytest_warns_bounded, ) @@ -39,6 +34,25 @@ Day, ) +if TYPE_CHECKING: + from pandas._typing import ( + np_1darray_bool, + np_2darray, + np_ndarray, + np_ndarray_bool, + np_ndarray_dt, + np_ndarray_td, + ) +else: + from tests._typing import ( + np_1darray_bool, + np_2darray, + np_ndarray, + np_ndarray_bool, + np_ndarray_dt, + np_ndarray_td, + ) + if not PD_LTE_23: from pandas.errors import Pandas4Warning # type: ignore[attr-defined] # pyright: ignore[reportAttributeAccessIssue,reportRedeclaration] # isort: skip else: diff --git a/tests/series/test_series.py b/tests/series/test_series.py index dc2417a8d..195a5ee6c 100644 --- a/tests/series/test_series.py +++ b/tests/series/test_series.py @@ -60,22 +60,8 @@ PD_LTE_23, TYPE_CHECKING_INVALID_USAGE, WINDOWS, - PandasAstypeComplexDtypeArg, - PandasAstypeTimedeltaDtypeArg, - PandasAstypeTimestampDtypeArg, check, ensure_clean, - np_1darray, - np_1darray_anyint, - np_1darray_bool, - np_1darray_bytes, - np_1darray_complex, - np_1darray_dt, - np_1darray_float, - np_1darray_object, - np_1darray_str, - np_1darray_td, - np_ndarray_num, pytest_warns_bounded, ) from tests.extension.decimal.array import DecimalDtype @@ -91,18 +77,60 @@ ) if TYPE_CHECKING: - from tests import ( + from pandas._typing import ( BooleanDtypeArg, BytesDtypeArg, CategoryDtypeArg, ComplexDtypeArg, IntDtypeArg, ObjectDtypeArg, + PandasAstypeComplexDtypeArg, + PandasAstypeTimedeltaDtypeArg, + PandasAstypeTimestampDtypeArg, StrDtypeArg, TimedeltaDtypeArg, TimestampDtypeArg, UIntDtypeArg, VoidDtypeArg, + np_1darray, + np_1darray_anyint, + np_1darray_bool, + np_1darray_bytes, + np_1darray_complex, + np_1darray_dt, + np_1darray_float, + np_1darray_object, + np_1darray_str, + np_1darray_td, + np_ndarray_num, + ) +else: + from tests._typing import ( + BooleanDtypeArg, + BytesDtypeArg, + CategoryDtypeArg, + ComplexDtypeArg, + IntDtypeArg, + ObjectDtypeArg, + PandasAstypeComplexDtypeArg, + PandasAstypeTimedeltaDtypeArg, + PandasAstypeTimestampDtypeArg, + StrDtypeArg, + TimedeltaDtypeArg, + TimestampDtypeArg, + UIntDtypeArg, + VoidDtypeArg, + np_1darray, + np_1darray_anyint, + np_1darray_bool, + np_1darray_bytes, + np_1darray_complex, + np_1darray_dt, + np_1darray_float, + np_1darray_object, + np_1darray_str, + np_1darray_td, + np_ndarray_num, ) if not PD_LTE_23: diff --git a/tests/series/test_series_float.py b/tests/series/test_series_float.py index a1c0e0cc5..24511b383 100644 --- a/tests/series/test_series_float.py +++ b/tests/series/test_series_float.py @@ -8,12 +8,16 @@ from tests import ( ASTYPE_FLOAT_ARGS, TYPE_FLOAT_ARGS, - FloatDtypeArg, - PandasAstypeFloatDtypeArg, check, exception_on_platform, ) +if TYPE_CHECKING: + from pandas._typing import ( + FloatDtypeArg, + PandasAstypeFloatDtypeArg, + ) + def test_constructor() -> None: check(assert_type(pd.Series([1.0]), "pd.Series[float]"), pd.Series, np.floating) @@ -45,7 +49,7 @@ def test_constructor() -> None: @pytest.mark.parametrize(("dtype", "target_dtype"), TYPE_FLOAT_ARGS.items()) -def test_constructor_dtype(dtype: FloatDtypeArg, target_dtype: type) -> None: +def test_constructor_dtype(dtype: "FloatDtypeArg", target_dtype: type) -> None: exc = exception_on_platform(dtype) if exc: with pytest.raises(exc, match=rf"data type {dtype!r} not understood"): @@ -99,7 +103,7 @@ def test_constructor_dtype(dtype: FloatDtypeArg, target_dtype: type) -> None: ("cast_arg", "target_type"), ASTYPE_FLOAT_ARGS.items(), ids=repr ) def test_astype_float( - cast_arg: FloatDtypeArg | PandasAstypeFloatDtypeArg, target_type: type + cast_arg: "FloatDtypeArg | PandasAstypeFloatDtypeArg", target_type: type ) -> None: s = pd.Series([1, 2, 3]) diff --git a/tests/test_extension.py b/tests/test_extension.py index 3ae383334..fa2fbce38 100644 --- a/tests/test_extension.py +++ b/tests/test_extension.py @@ -1,4 +1,5 @@ import decimal +from typing import TYPE_CHECKING import numpy as np import pandas as pd @@ -7,15 +8,17 @@ from pandas.core.indexers import check_array_indexer from typing_extensions import assert_type -from tests import ( - check, - np_1darray_bool, -) +from tests import check from tests.extension.decimal.array import ( DecimalArray, DecimalDtype, ) +if TYPE_CHECKING: + from pandas._typing import np_1darray_bool +else: + from tests._typing import np_1darray_bool + def test_constructor() -> None: arr = DecimalArray([decimal.Decimal("1.0"), decimal.Decimal("2.0")]) diff --git a/tests/test_frame.py b/tests/test_frame.py index e4b8797a3..35a411eaf 100644 --- a/tests/test_frame.py +++ b/tests/test_frame.py @@ -56,9 +56,6 @@ TYPE_CHECKING_INVALID_USAGE, check, ensure_clean, - np_1darray, - np_2darray, - np_ndarray, pytest_warns_bounded, ) @@ -76,8 +73,19 @@ if TYPE_CHECKING: from pandas.core.frame import _PandasNamedTuple - from pandas._typing import S1 + from pandas._typing import ( + S1, + np_1darray, + np_2darray, + np_ndarray, + ) else: + from tests._typing import ( + np_1darray, + np_2darray, + np_ndarray, + ) + _PandasNamedTuple: TypeAlias = tuple if not PD_LTE_23: diff --git a/tests/test_interval.py b/tests/test_interval.py index a8e9c92b5..f752608d2 100644 --- a/tests/test_interval.py +++ b/tests/test_interval.py @@ -1,5 +1,7 @@ from __future__ import annotations +from typing import TYPE_CHECKING + import numpy as np import pandas as pd from typing_extensions import assert_type @@ -7,9 +9,13 @@ from tests import ( TYPE_CHECKING_INVALID_USAGE, check, - np_1darray_bool, ) +if TYPE_CHECKING: + from pandas._typing import np_1darray_bool +else: + from tests._typing import np_1darray_bool + def test_interval_init() -> None: check( diff --git a/tests/test_pandas.py b/tests/test_pandas.py index ef5faf76c..20fbccb89 100644 --- a/tests/test_pandas.py +++ b/tests/test_pandas.py @@ -32,20 +32,38 @@ PD_LTE_23, TYPE_CHECKING_INVALID_USAGE, check, - np_1darray, - np_1darray_anyint, - np_1darray_bool, - np_1darray_dt, - np_1darray_float, - np_1darray_int64, - np_1darray_intp, - np_1darray_td, - np_2darray, - np_ndarray, - np_ndarray_bool, pytest_warns_bounded, ) +if TYPE_CHECKING: + from pandas._typing import ( + np_1darray, + np_1darray_anyint, + np_1darray_bool, + np_1darray_dt, + np_1darray_float, + np_1darray_int64, + np_1darray_intp, + np_1darray_td, + np_2darray, + np_ndarray, + np_ndarray_bool, + ) +else: + from tests._typing import ( + np_1darray, + np_1darray_anyint, + np_1darray_bool, + np_1darray_dt, + np_1darray_float, + np_1darray_int64, + np_1darray_intp, + np_1darray_td, + np_2darray, + np_ndarray, + np_ndarray_bool, + ) + def test_types_to_datetime() -> None: df = pd.DataFrame({"year": [2015, 2016], "month": [2, 3], "day": [4, 5]}) diff --git a/tests/test_string_accessors.py b/tests/test_string_accessors.py index 33033ee40..beff964f3 100644 --- a/tests/test_string_accessors.py +++ b/tests/test_string_accessors.py @@ -1,5 +1,6 @@ import functools import re +from typing import TYPE_CHECKING import numpy as np import pandas as pd @@ -9,9 +10,13 @@ from tests import ( TYPE_CHECKING_INVALID_USAGE, check, - np_1darray_bool, ) +if TYPE_CHECKING: + from pandas._typing import np_1darray_bool +else: + from tests._typing import np_1darray_bool + DATA = ["applep", "bananap", "Cherryp", "DATEp", "eGGpLANTp", "123p", "23.45p"] DATA_BYTES = [b"applep", b"bananap"] diff --git a/tests/test_styler.py b/tests/test_styler.py index bbaf44bf7..eedffb2bf 100644 --- a/tests/test_styler.py +++ b/tests/test_styler.py @@ -23,21 +23,25 @@ PD_LTE_23, check, ensure_clean, - np_ndarray_str, ) from pandas.io.formats.style import Styler -DF = DataFrame({"a": [1, 2, 3], "b": [3.14, 2.72, 1.61]}) - -PWD = Path(__file__).parent.resolve() - if TYPE_CHECKING: + from pandas._typing import np_ndarray_str + from pandas.io.formats.style_render import StyleExportDict else: + from tests._typing import np_ndarray_str + StyleExportDict = object +DF = DataFrame({"a": [1, 2, 3], "b": [3.14, 2.72, 1.61]}) + +PWD = Path(__file__).parent.resolve() + + @pytest.fixture(autouse=True) def reset_style() -> None: DF.style.clear() diff --git a/tests/test_timefuncs.py b/tests/test_timefuncs.py index 6d599ecfa..4bbc460b4 100644 --- a/tests/test_timefuncs.py +++ b/tests/test_timefuncs.py @@ -1,7 +1,10 @@ from __future__ import annotations import datetime as dt -from typing import TypeAlias +from typing import ( + TYPE_CHECKING, + TypeAlias, +) from dateutil.relativedelta import ( FR, @@ -28,14 +31,6 @@ PD_LTE_23, TYPE_CHECKING_INVALID_USAGE, check, - np_1darray, - np_1darray_bool, - np_1darray_bytes, - np_1darray_dt, - np_1darray_int64, - np_1darray_object, - np_1darray_str, - np_1darray_td, pytest_warns_bounded, ) @@ -51,6 +46,29 @@ Day, ) +if TYPE_CHECKING: + from pandas._typing import ( + np_1darray, + np_1darray_bool, + np_1darray_bytes, + np_1darray_dt, + np_1darray_int64, + np_1darray_object, + np_1darray_str, + np_1darray_td, + ) +else: + from tests._typing import ( + np_1darray, + np_1darray_bool, + np_1darray_bytes, + np_1darray_dt, + np_1darray_int64, + np_1darray_object, + np_1darray_str, + np_1darray_td, + ) + if not PD_LTE_23: from pandas.errors import Pandas4Warning # type: ignore[attr-defined] # pyright: ignore[reportAttributeAccessIssue,reportRedeclaration] # isort: skip else: diff --git a/tests/test_windowing.py b/tests/test_windowing.py index 7e302b449..a52f95e89 100644 --- a/tests/test_windowing.py +++ b/tests/test_windowing.py @@ -1,4 +1,5 @@ import datetime as dt +from typing import TYPE_CHECKING import numpy as np import pandas as pd @@ -23,13 +24,22 @@ from tests import ( PD_LTE_23, check, - np_1darray_intp, - np_ndarray, pytest_warns_bounded, ) from pandas.tseries.frequencies import to_offset +if TYPE_CHECKING: + from pandas._typing import ( + np_1darray_intp, + np_ndarray, + ) +else: + from tests._typing import ( + np_1darray_intp, + np_ndarray, + ) + IDX = date_range("1/1/2000", periods=700, freq="D") S = Series(np.random.standard_normal(700)) DF = DataFrame({"col1": S, "col2": S}) From 1ee17e8893111c885ed54f61138e7b72bf81fa44 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Thu, 27 Nov 2025 20:53:37 +0100 Subject: [PATCH 2/7] fix numpy --- pandas-stubs/core/base.pyi | 3 +++ pandas-stubs/core/indexes/base.pyi | 9 +++++++++ pandas-stubs/core/series.pyi | 29 ++++++++++++++++++++------- tests/series/test_series.py | 22 ++++++++++---------- tests/test_timefuncs.py | 32 +++++++++++------------------- 5 files changed, 58 insertions(+), 37 deletions(-) diff --git a/pandas-stubs/core/base.pyi b/pandas-stubs/core/base.pyi index 1e3a93bf9..1dc1d1c2c 100644 --- a/pandas-stubs/core/base.pyi +++ b/pandas-stubs/core/base.pyi @@ -10,6 +10,7 @@ from typing import ( Literal, Protocol, TypeAlias, + TypeVar, final, overload, type_check_only, @@ -53,6 +54,8 @@ from pandas._typing import ( ) from pandas.util._decorators import cache_readonly +T_INTERVAL_NP = TypeVar("T_INTERVAL_NP", bound=np.bytes_ | np.str_) + class NoNewAttributesMixin: def __setattr__(self, key: str, value: Any) -> None: ... diff --git a/pandas-stubs/core/indexes/base.pyi b/pandas-stubs/core/indexes/base.pyi index 42ef02183..201e1e8eb 100644 --- a/pandas-stubs/core/indexes/base.pyi +++ b/pandas-stubs/core/indexes/base.pyi @@ -32,6 +32,7 @@ import numpy as np from pandas.core.arrays.boolean import BooleanArray from pandas.core.arrays.floating import FloatingArray from pandas.core.base import ( + T_INTERVAL_NP, ArrayIndexTimedeltaNoSeq, ElementOpsMixin, IndexComplex, @@ -1168,6 +1169,14 @@ class Index(IndexOpsMixin[S1], ElementOpsMixin[S1]): @type_check_only class _IndexSubclassBase(Index[S1], Generic[S1, GenericT_co]): + @overload + def to_numpy( + self: _IndexSubclassBase[Interval], + dtype: type[T_INTERVAL_NP], + copy: bool = False, + na_value: Scalar = ..., + **kwargs: Any, + ) -> np_1darray: ... @overload def to_numpy( self, diff --git a/pandas-stubs/core/series.pyi b/pandas-stubs/core/series.pyi index 9b5fc9619..a31926ced 100644 --- a/pandas-stubs/core/series.pyi +++ b/pandas-stubs/core/series.pyi @@ -29,7 +29,6 @@ from typing import ( NoReturn, Protocol, TypeAlias, - TypeVar, final, overload, type_check_only, @@ -71,6 +70,7 @@ from pandas.core.arrays.datetimes import DatetimeArray from pandas.core.arrays.floating import FloatingArray from pandas.core.arrays.timedeltas import TimedeltaArray from pandas.core.base import ( + T_INTERVAL_NP, ArrayIndexSeriesTimedeltaNoSeq, ArrayIndexTimedeltaNoSeq, ElementOpsMixin, @@ -189,6 +189,7 @@ from pandas._typing import ( MaskType, NaPosition, NsmallestNlargestKeep, + NumpyStrDtypeArg, ObjectDtypeArg, PandasAstypeComplexDtypeArg, PandasAstypeFloatDtypeArg, @@ -251,8 +252,6 @@ from pandas.core.dtypes.dtypes import CategoricalDtype from pandas.plotting import PlotAccessor -_T_INTERVAL_NP = TypeVar("_T_INTERVAL_NP", bound=np.bytes_ | np.str_) - @type_check_only class _SupportsAdd(Protocol[_T_co]): def __add__(self, value: Self, /) -> _T_co: ... @@ -4503,7 +4502,7 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): copy: bool = False, na_value: Scalar = ..., **kwargs: Any, - ) -> np_1darray_bytes: ... + ) -> np_1darray: ... @overload def to_numpy( self: Series[Interval], @@ -4515,11 +4514,11 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): @overload def to_numpy( self: Series[Interval], - dtype: type[_T_INTERVAL_NP], + dtype: type[T_INTERVAL_NP], copy: bool = False, na_value: Scalar = ..., **kwargs: Any, - ) -> np_1darray[_T_INTERVAL_NP]: ... + ) -> np_1darray: ... @overload def to_numpy( self: Series[int], @@ -4555,12 +4554,28 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): @overload def to_numpy( self: Series[_str], - dtype: DTypeLike | None = None, + dtype: NumpyStrDtypeArg, copy: bool = False, na_value: Scalar = ..., **kwargs: Any, ) -> np_1darray_str: ... @overload + def to_numpy( + self: Series[_str], + dtype: DTypeLike, + copy: bool = False, + na_value: Scalar = ..., + **kwargs: Any, + ) -> np_1darray: ... + @overload + def to_numpy( + self: Series[_str], + dtype: None = None, + copy: bool = False, + na_value: Scalar = ..., + **kwargs: Any, + ) -> np_1darray_object: ... + @overload def to_numpy( self: Series[bytes], dtype: DTypeLike | None = None, diff --git a/tests/series/test_series.py b/tests/series/test_series.py index 195a5ee6c..3842c4f54 100644 --- a/tests/series/test_series.py +++ b/tests/series/test_series.py @@ -100,7 +100,6 @@ np_1darray_dt, np_1darray_float, np_1darray_object, - np_1darray_str, np_1darray_td, np_ndarray_num, ) @@ -128,7 +127,6 @@ np_1darray_dt, np_1darray_float, np_1darray_object, - np_1darray_str, np_1darray_td, np_ndarray_num, ) @@ -2032,18 +2030,22 @@ def test_dtype_type() -> None: def test_types_to_numpy() -> None: s = pd.Series(["a", "b", "c"], dtype=str) - check(assert_type(s.to_numpy(), np_1darray_str), np_1darray_str) + check(assert_type(s.to_numpy(), np_1darray_object), np_1darray_object) + check( # None: def test_to_numpy() -> None: """Test Series.to_numpy for different types.""" s_str = pd.Series(["a", "b", "c"], dtype=str) - check(assert_type(s_str.to_numpy(), np_1darray_str), np_1darray_str) + check(assert_type(s_str.to_numpy(), np_1darray_object), np_1darray_object) s_bytes = pd.Series(["a", "b", "c"]).astype(bytes) check(assert_type(s_bytes.to_numpy(), np_1darray_bytes), np_1darray, np.bytes_) diff --git a/tests/test_timefuncs.py b/tests/test_timefuncs.py index 4bbc460b4..b9b3ffdc4 100644 --- a/tests/test_timefuncs.py +++ b/tests/test_timefuncs.py @@ -50,22 +50,18 @@ from pandas._typing import ( np_1darray, np_1darray_bool, - np_1darray_bytes, np_1darray_dt, np_1darray_int64, np_1darray_object, - np_1darray_str, np_1darray_td, ) else: from tests._typing import ( np_1darray, np_1darray_bool, - np_1darray_bytes, np_1darray_dt, np_1darray_int64, np_1darray_object, - np_1darray_str, np_1darray_td, ) @@ -976,11 +972,11 @@ def test_series_types_to_numpy() -> None: ) check( assert_type(o_s.to_numpy(dtype="bytes", copy=True), np_1darray), - np_1darray_bytes, + np_1darray, ) check( assert_type(i_s.to_numpy(dtype="bytes", copy=True), np_1darray), - np_1darray_bytes, + np_1darray, ) # passed dtype-like with statically known generic @@ -1009,13 +1005,12 @@ def test_series_types_to_numpy() -> None: np_1darray, np.int64, ) - check( - assert_type(o_s.to_numpy(dtype=np.bytes_), np_1darray_bytes), np_1darray_bytes - ) - check( - assert_type(i_s.to_numpy(dtype=np.bytes_), np_1darray_bytes), np_1darray_bytes - ) - check(assert_type(i_s.to_numpy(dtype=np.str_), np_1darray_str), np_1darray_str) + # |S20, not bytes_ + check(assert_type(o_s.to_numpy(dtype=np.bytes_), np_1darray), np_1darray) + # |S6, not bytes_ + check(assert_type(i_s.to_numpy(dtype=np.bytes_), np_1darray), np_1darray) + # None: @@ -1074,10 +1069,8 @@ def test_index_types_to_numpy() -> None: np_1darray, dtype=np.integer, ) - check( - assert_type(i_i.to_numpy(dtype="bytes", copy=True), np_1darray), - np_1darray_bytes, - ) + # |S6, not bytes_ + check(assert_type(i_i.to_numpy(dtype="bytes", copy=True), np_1darray), np_1darray) # passed dtype-like with statically known generic check( @@ -1095,9 +1088,8 @@ def test_index_types_to_numpy() -> None: np_1darray, np.int64, ) - check( - assert_type(i_i.to_numpy(dtype=np.bytes_), np_1darray_bytes), np_1darray_bytes - ) + # |S6, not bytes_ + check(assert_type(i_i.to_numpy(dtype=np.bytes_), np_1darray), np_1darray) def test_to_timedelta_units() -> None: From 3a09258da04d84c091a23a3566d742e06ae30176 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Thu, 27 Nov 2025 20:53:37 +0100 Subject: [PATCH 3/7] pyright --- pandas-stubs/core/indexes/base.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas-stubs/core/indexes/base.pyi b/pandas-stubs/core/indexes/base.pyi index 201e1e8eb..07b38172c 100644 --- a/pandas-stubs/core/indexes/base.pyi +++ b/pandas-stubs/core/indexes/base.pyi @@ -1194,7 +1194,7 @@ class _IndexSubclassBase(Index[S1], Generic[S1, GenericT_co]): **kwargs: Any, ) -> np_1darray[GenericT]: ... @overload - def to_numpy( + def to_numpy( # pyright: ignore[reportIncompatibleMethodOverride] self, dtype: DTypeLike, copy: bool = False, From a8906d056ef8b26604c2a18084899d6bab2ed457 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Sun, 30 Nov 2025 18:55:14 +0100 Subject: [PATCH 4/7] make writing tests easier for contributors --- .gitignore | 4 +- tests/_typing.py | 84 ++++++++++++++++++++++++++ tests/arrays/test_extension_array.py | 16 ++--- tests/arrays/test_floating_array.py | 6 +- tests/conftest.py | 12 +++- tests/extension/decimal/array.py | 18 ++---- tests/indexes/test_categoricalindex.py | 8 +-- tests/indexes/test_datetime_index.py | 16 ++--- tests/indexes/test_index_float.py | 3 +- tests/indexes/test_indexes.py | 29 +++------ tests/indexes/test_rangeindex.py | 8 +-- tests/scalars/test_scalars.py | 32 +++------- tests/series/test_series.py | 81 ++++++++----------------- tests/series/test_series_float.py | 10 ++- tests/test_extension.py | 7 +-- tests/test_frame.py | 17 ++---- tests/test_interval.py | 8 +-- tests/test_pandas.py | 42 ++++--------- tests/test_string_accessors.py | 7 +-- tests/test_styler.py | 5 +- tests/test_timefuncs.py | 28 +++------ tests/test_windowing.py | 16 ++--- 22 files changed, 196 insertions(+), 261 deletions(-) create mode 100644 tests/_typing.py diff --git a/.gitignore b/.gitignore index 8dbfcd6fb..23a99d0ee 100644 --- a/.gitignore +++ b/.gitignore @@ -136,5 +136,5 @@ dmypy.json /poetry.lock .idea/**/* -# duplication of stub specific objects for testing -tests/_typing.py +# duplication of stub specific objects upon testing +tests/_typing.pyi diff --git a/tests/_typing.py b/tests/_typing.py new file mode 100644 index 000000000..9695520f6 --- /dev/null +++ b/tests/_typing.py @@ -0,0 +1,84 @@ +# Committed content should be import-only. At runtime of tests, this file will +# be replaced by pandas-stubs/_typing.pyi. + +from pandas._typing import ( + BooleanDtypeArg, + BytesDtypeArg, + CategoryDtypeArg, + ComplexDtypeArg, + Dtype, + FloatDtypeArg, + IntDtypeArg, + ObjectDtypeArg, + PandasAstypeComplexDtypeArg, + PandasAstypeFloatDtypeArg, + PandasAstypeTimedeltaDtypeArg, + PandasAstypeTimestampDtypeArg, + PandasFloatDtypeArg, + StrDtypeArg, + TimedeltaDtypeArg, + TimestampDtypeArg, + UIntDtypeArg, + VoidDtypeArg, + np_1darray, + np_1darray_anyint, + np_1darray_bool, + np_1darray_bytes, + np_1darray_complex, + np_1darray_dt, + np_1darray_float, + np_1darray_int64, + np_1darray_intp, + np_1darray_object, + np_1darray_td, + np_2darray, + np_ndarray, + np_ndarray_bool, + np_ndarray_dt, + np_ndarray_num, + np_ndarray_str, + np_ndarray_td, +) + +__all__ = [ + "np_1darray", + "np_2darray", + "np_ndarray", + "np_1darray_bool", + "BooleanDtypeArg", + "BytesDtypeArg", + "CategoryDtypeArg", + "ComplexDtypeArg", + "IntDtypeArg", + "np_ndarray_str", + "ObjectDtypeArg", + "PandasAstypeComplexDtypeArg", + "PandasAstypeTimedeltaDtypeArg", + "PandasAstypeTimestampDtypeArg", + "StrDtypeArg", + "TimedeltaDtypeArg", + "TimestampDtypeArg", + "UIntDtypeArg", + "PandasFloatDtypeArg", + "VoidDtypeArg", + "np_1darray", + "np_1darray_anyint", + "np_1darray_bool", + "np_1darray_bytes", + "np_1darray_complex", + "np_1darray_dt", + "np_1darray_float", + "np_ndarray_bool", + "np_ndarray_dt", + "np_1darray_object", + "np_1darray_td", + "np_1darray_int64", + "np_ndarray_num", + "FloatDtypeArg", + "PandasAstypeFloatDtypeArg", + "np_ndarray_bool", + "np_ndarray_dt", + "np_ndarray_td", + "np_1darray_intp", + "Dtype", +] diff --git a/tests/arrays/test_extension_array.py b/tests/arrays/test_extension_array.py index ca75670b5..2c00d1b74 100644 --- a/tests/arrays/test_extension_array.py +++ b/tests/arrays/test_extension_array.py @@ -1,5 +1,4 @@ # Test common ExtensionArray methods -from typing import TYPE_CHECKING import numpy as np import pandas as pd @@ -14,17 +13,10 @@ from pandas._typing import ArrayLike from tests import check - -if TYPE_CHECKING: - from pandas._typing import ( - np_1darray, - np_1darray_intp, - ) -else: - from tests._typing import ( - np_1darray, - np_1darray_intp, - ) +from tests._typing import ( + np_1darray, + np_1darray_intp, +) def test_ea_common() -> None: diff --git a/tests/arrays/test_floating_array.py b/tests/arrays/test_floating_array.py index 0c703bd36..856489f0e 100644 --- a/tests/arrays/test_floating_array.py +++ b/tests/arrays/test_floating_array.py @@ -11,9 +11,7 @@ check, exception_on_platform, ) - -if TYPE_CHECKING: - from pandas._typing import PandasFloatDtypeArg +from tests._typing import PandasFloatDtypeArg def test_constructor() -> None: @@ -29,7 +27,7 @@ def test_constructor() -> None: @pytest.mark.parametrize(("dtype", "target_dtype"), PANDAS_FLOAT_ARGS.items(), ids=repr) -def test_constructor_dtype(dtype: "PandasFloatDtypeArg", target_dtype: type) -> None: +def test_constructor_dtype(dtype: PandasFloatDtypeArg, target_dtype: type) -> None: exc = exception_on_platform(dtype) if exc: with pytest.raises(exc, match=rf"data type {dtype!r} not understood"): diff --git a/tests/conftest.py b/tests/conftest.py index 8c3d70912..9c8795fb9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,12 +4,18 @@ import pytest +# Use pandas._testing from the stubs for testing +groundtruth = Path(__file__).parents[1] / "pandas-stubs" / "_typing.pyi" target = Path(__file__).parent / "_typing.py" -copyfile(Path(__file__).parents[1] / "pandas-stubs" / "_typing.pyi", target) +staging = Path(__file__).parent / "_typing.pyi" +copyfile(target, staging) +copyfile(groundtruth, target) @pytest.fixture(autouse=True, scope="session") def setup_typing_module() -> Generator[None, None, None]: - """Ensure that tests._typing is removed after running tests""" + """Ensure that tests._typing is recovered after running tests""" yield - target.unlink() + + copyfile(staging, target) + staging.unlink() diff --git a/tests/extension/decimal/array.py b/tests/extension/decimal/array.py index c8f5e2ee0..5c8950f77 100644 --- a/tests/extension/decimal/array.py +++ b/tests/extension/decimal/array.py @@ -11,7 +11,6 @@ import numbers import sys from typing import ( - TYPE_CHECKING, Any, cast, overload, @@ -53,18 +52,11 @@ pandas_dtype, ) -if TYPE_CHECKING: - from pandas._typing import ( - np_1darray, - np_1darray_bool, - np_ndarray, - ) -else: - from tests._typing import ( - np_1darray, - np_1darray_bool, - np_ndarray, - ) +from tests._typing import ( + np_1darray, + np_1darray_bool, + np_ndarray, +) @register_extension_dtype diff --git a/tests/indexes/test_categoricalindex.py b/tests/indexes/test_categoricalindex.py index 4c3f4f104..54a4d0f6f 100644 --- a/tests/indexes/test_categoricalindex.py +++ b/tests/indexes/test_categoricalindex.py @@ -1,18 +1,12 @@ from __future__ import annotations -from typing import TYPE_CHECKING - import pandas as pd from typing_extensions import ( assert_type, ) from tests import check - -if TYPE_CHECKING: - from pandas._typing import np_1darray_intp -else: - from tests._typing import np_1darray_intp +from tests._typing import np_1darray_intp def test_categoricalindex_unique() -> None: diff --git a/tests/indexes/test_datetime_index.py b/tests/indexes/test_datetime_index.py index bf2e29b19..8e10197dd 100644 --- a/tests/indexes/test_datetime_index.py +++ b/tests/indexes/test_datetime_index.py @@ -1,24 +1,16 @@ from __future__ import annotations from datetime import time -from typing import TYPE_CHECKING import numpy as np import pandas as pd from typing_extensions import assert_type from tests import check - -if TYPE_CHECKING: - from pandas._typing import ( - np_1darray_bool, - np_1darray_intp, - ) -else: - from tests._typing import ( - np_1darray_bool, - np_1darray_intp, - ) +from tests._typing import ( + np_1darray_bool, + np_1darray_intp, +) def test_index_relops() -> None: diff --git a/tests/indexes/test_index_float.py b/tests/indexes/test_index_float.py index dad6a83bb..9ca0af64b 100644 --- a/tests/indexes/test_index_float.py +++ b/tests/indexes/test_index_float.py @@ -11,12 +11,11 @@ check, exception_on_platform, ) +from tests._typing import PandasAstypeFloatDtypeArg if TYPE_CHECKING: from pandas.core.indexes.base import FloatNotNumpy16DtypeArg - from pandas._typing import PandasAstypeFloatDtypeArg - def test_constructor() -> None: check(assert_type(pd.Index([1.0]), "pd.Index[float]"), pd.Index, np.floating) diff --git a/tests/indexes/test_indexes.py b/tests/indexes/test_indexes.py index 2f697fc7b..704b0b41e 100644 --- a/tests/indexes/test_indexes.py +++ b/tests/indexes/test_indexes.py @@ -3,7 +3,6 @@ from collections.abc import Hashable import datetime as dt from typing import ( - TYPE_CHECKING, Any, cast, ) @@ -23,31 +22,21 @@ assert_type, ) +from pandas._typing import Dtype # noqa: F401 + from tests import ( PD_LTE_23, TYPE_CHECKING_INVALID_USAGE, check, pytest_warns_bounded, ) - -if TYPE_CHECKING: - from pandas._typing import ( - Dtype, - np_1darray, - np_1darray_bool, - np_1darray_int64, - np_1darray_intp, - np_ndarray_dt, - ) -else: - from tests._typing import ( - np_1darray, - np_1darray_bool, - np_1darray_int64, - np_1darray_intp, - np_ndarray_dt, - ) - from tests._typing import Dtype # noqa: F401 +from tests._typing import ( + np_1darray, + np_1darray_bool, + np_1darray_int64, + np_1darray_intp, + np_ndarray_dt, +) def test_index_unique() -> None: diff --git a/tests/indexes/test_rangeindex.py b/tests/indexes/test_rangeindex.py index 7bad9ba1e..c2ea6553b 100644 --- a/tests/indexes/test_rangeindex.py +++ b/tests/indexes/test_rangeindex.py @@ -1,16 +1,10 @@ from __future__ import annotations -from typing import TYPE_CHECKING - import pandas as pd from typing_extensions import assert_type from tests import check - -if TYPE_CHECKING: - from pandas._typing import np_1darray_intp -else: - from tests._typing import np_1darray_intp +from tests._typing import np_1darray_intp def test_rangeindex_floordiv() -> None: diff --git a/tests/scalars/test_scalars.py b/tests/scalars/test_scalars.py index 40cf88617..d58f305d6 100644 --- a/tests/scalars/test_scalars.py +++ b/tests/scalars/test_scalars.py @@ -3,7 +3,6 @@ import datetime import datetime as dt from typing import ( - TYPE_CHECKING, Any, Literal, TypeAlias, @@ -15,9 +14,7 @@ import pandas as pd from pandas.api.typing import NaTType import pytz -from typing_extensions import ( - assert_type, -) +from typing_extensions import assert_type from pandas._libs.tslibs.timedeltas import Components from pandas._typing import TimeUnit @@ -28,31 +25,20 @@ check, pytest_warns_bounded, ) +from tests._typing import ( + np_1darray_bool, + np_2darray, + np_ndarray, + np_ndarray_bool, + np_ndarray_dt, + np_ndarray_td, +) from pandas.tseries.offsets import ( BaseOffset, Day, ) -if TYPE_CHECKING: - from pandas._typing import ( - np_1darray_bool, - np_2darray, - np_ndarray, - np_ndarray_bool, - np_ndarray_dt, - np_ndarray_td, - ) -else: - from tests._typing import ( - np_1darray_bool, - np_2darray, - np_ndarray, - np_ndarray_bool, - np_ndarray_dt, - np_ndarray_td, - ) - if not PD_LTE_23: from pandas.errors import Pandas4Warning # type: ignore[attr-defined] # pyright: ignore[reportAttributeAccessIssue,reportRedeclaration] # isort: skip else: diff --git a/tests/series/test_series.py b/tests/series/test_series.py index 3842c4f54..004fddbb5 100644 --- a/tests/series/test_series.py +++ b/tests/series/test_series.py @@ -64,6 +64,32 @@ ensure_clean, pytest_warns_bounded, ) +from tests._typing import ( + BooleanDtypeArg, + BytesDtypeArg, + CategoryDtypeArg, + ComplexDtypeArg, + IntDtypeArg, + ObjectDtypeArg, + PandasAstypeComplexDtypeArg, + PandasAstypeTimedeltaDtypeArg, + PandasAstypeTimestampDtypeArg, + StrDtypeArg, + TimedeltaDtypeArg, + TimestampDtypeArg, + UIntDtypeArg, + VoidDtypeArg, + np_1darray, + np_1darray_anyint, + np_1darray_bool, + np_1darray_bytes, + np_1darray_complex, + np_1darray_dt, + np_1darray_float, + np_1darray_object, + np_1darray_td, + np_ndarray_num, +) from tests.extension.decimal.array import DecimalDtype from pandas.io.formats.format import EngFormatter @@ -76,61 +102,6 @@ YearEnd, ) -if TYPE_CHECKING: - from pandas._typing import ( - BooleanDtypeArg, - BytesDtypeArg, - CategoryDtypeArg, - ComplexDtypeArg, - IntDtypeArg, - ObjectDtypeArg, - PandasAstypeComplexDtypeArg, - PandasAstypeTimedeltaDtypeArg, - PandasAstypeTimestampDtypeArg, - StrDtypeArg, - TimedeltaDtypeArg, - TimestampDtypeArg, - UIntDtypeArg, - VoidDtypeArg, - np_1darray, - np_1darray_anyint, - np_1darray_bool, - np_1darray_bytes, - np_1darray_complex, - np_1darray_dt, - np_1darray_float, - np_1darray_object, - np_1darray_td, - np_ndarray_num, - ) -else: - from tests._typing import ( - BooleanDtypeArg, - BytesDtypeArg, - CategoryDtypeArg, - ComplexDtypeArg, - IntDtypeArg, - ObjectDtypeArg, - PandasAstypeComplexDtypeArg, - PandasAstypeTimedeltaDtypeArg, - PandasAstypeTimestampDtypeArg, - StrDtypeArg, - TimedeltaDtypeArg, - TimestampDtypeArg, - UIntDtypeArg, - VoidDtypeArg, - np_1darray, - np_1darray_anyint, - np_1darray_bool, - np_1darray_bytes, - np_1darray_complex, - np_1darray_dt, - np_1darray_float, - np_1darray_object, - np_1darray_td, - np_ndarray_num, - ) - if not PD_LTE_23: from pandas.errors import Pandas4Warning # type: ignore[attr-defined] # pyright: ignore[reportAttributeAccessIssue,reportRedeclaration] # isort: skip else: diff --git a/tests/series/test_series_float.py b/tests/series/test_series_float.py index 24511b383..54a1c88e6 100644 --- a/tests/series/test_series_float.py +++ b/tests/series/test_series_float.py @@ -11,12 +11,10 @@ check, exception_on_platform, ) - -if TYPE_CHECKING: - from pandas._typing import ( - FloatDtypeArg, - PandasAstypeFloatDtypeArg, - ) +from tests._typing import ( + FloatDtypeArg, + PandasAstypeFloatDtypeArg, +) def test_constructor() -> None: diff --git a/tests/test_extension.py b/tests/test_extension.py index fa2fbce38..c2e0c52f2 100644 --- a/tests/test_extension.py +++ b/tests/test_extension.py @@ -1,5 +1,4 @@ import decimal -from typing import TYPE_CHECKING import numpy as np import pandas as pd @@ -9,16 +8,12 @@ from typing_extensions import assert_type from tests import check +from tests._typing import np_1darray_bool from tests.extension.decimal.array import ( DecimalArray, DecimalDtype, ) -if TYPE_CHECKING: - from pandas._typing import np_1darray_bool -else: - from tests._typing import np_1darray_bool - def test_constructor() -> None: arr = DecimalArray([decimal.Decimal("1.0"), decimal.Decimal("2.0")]) diff --git a/tests/test_frame.py b/tests/test_frame.py index 35a411eaf..b5be57660 100644 --- a/tests/test_frame.py +++ b/tests/test_frame.py @@ -58,6 +58,11 @@ ensure_clean, pytest_warns_bounded, ) +from tests._typing import ( + np_1darray, + np_2darray, + np_ndarray, +) from pandas.io.formats.format import EngFormatter from pandas.io.formats.style import Styler @@ -73,18 +78,8 @@ if TYPE_CHECKING: from pandas.core.frame import _PandasNamedTuple - from pandas._typing import ( - S1, - np_1darray, - np_2darray, - np_ndarray, - ) + from pandas._typing import S1 else: - from tests._typing import ( - np_1darray, - np_2darray, - np_ndarray, - ) _PandasNamedTuple: TypeAlias = tuple diff --git a/tests/test_interval.py b/tests/test_interval.py index f752608d2..398bda276 100644 --- a/tests/test_interval.py +++ b/tests/test_interval.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import TYPE_CHECKING - import numpy as np import pandas as pd from typing_extensions import assert_type @@ -10,11 +8,7 @@ TYPE_CHECKING_INVALID_USAGE, check, ) - -if TYPE_CHECKING: - from pandas._typing import np_1darray_bool -else: - from tests._typing import np_1darray_bool +from tests._typing import np_1darray_bool def test_interval_init() -> None: diff --git a/tests/test_pandas.py b/tests/test_pandas.py index 20fbccb89..d34cdf738 100644 --- a/tests/test_pandas.py +++ b/tests/test_pandas.py @@ -34,35 +34,19 @@ check, pytest_warns_bounded, ) - -if TYPE_CHECKING: - from pandas._typing import ( - np_1darray, - np_1darray_anyint, - np_1darray_bool, - np_1darray_dt, - np_1darray_float, - np_1darray_int64, - np_1darray_intp, - np_1darray_td, - np_2darray, - np_ndarray, - np_ndarray_bool, - ) -else: - from tests._typing import ( - np_1darray, - np_1darray_anyint, - np_1darray_bool, - np_1darray_dt, - np_1darray_float, - np_1darray_int64, - np_1darray_intp, - np_1darray_td, - np_2darray, - np_ndarray, - np_ndarray_bool, - ) +from tests._typing import ( + np_1darray, + np_1darray_anyint, + np_1darray_bool, + np_1darray_dt, + np_1darray_float, + np_1darray_int64, + np_1darray_intp, + np_1darray_td, + np_2darray, + np_ndarray, + np_ndarray_bool, +) def test_types_to_datetime() -> None: diff --git a/tests/test_string_accessors.py b/tests/test_string_accessors.py index beff964f3..eda346ada 100644 --- a/tests/test_string_accessors.py +++ b/tests/test_string_accessors.py @@ -1,6 +1,5 @@ import functools import re -from typing import TYPE_CHECKING import numpy as np import pandas as pd @@ -11,11 +10,7 @@ TYPE_CHECKING_INVALID_USAGE, check, ) - -if TYPE_CHECKING: - from pandas._typing import np_1darray_bool -else: - from tests._typing import np_1darray_bool +from tests._typing import np_1darray_bool DATA = ["applep", "bananap", "Cherryp", "DATEp", "eGGpLANTp", "123p", "23.45p"] DATA_BYTES = [b"applep", b"bananap"] diff --git a/tests/test_styler.py b/tests/test_styler.py index eedffb2bf..c7e764ef4 100644 --- a/tests/test_styler.py +++ b/tests/test_styler.py @@ -24,16 +24,13 @@ check, ensure_clean, ) +from tests._typing import np_ndarray_str from pandas.io.formats.style import Styler if TYPE_CHECKING: - from pandas._typing import np_ndarray_str - from pandas.io.formats.style_render import StyleExportDict else: - from tests._typing import np_ndarray_str - StyleExportDict = object diff --git a/tests/test_timefuncs.py b/tests/test_timefuncs.py index b9b3ffdc4..42fcaa4d0 100644 --- a/tests/test_timefuncs.py +++ b/tests/test_timefuncs.py @@ -2,7 +2,6 @@ import datetime as dt from typing import ( - TYPE_CHECKING, TypeAlias, ) @@ -33,6 +32,14 @@ check, pytest_warns_bounded, ) +from tests._typing import ( + np_1darray, + np_1darray_bool, + np_1darray_dt, + np_1darray_int64, + np_1darray_object, + np_1darray_td, +) from pandas.tseries.frequencies import to_offset from pandas.tseries.holiday import USFederalHolidayCalendar @@ -46,25 +53,6 @@ Day, ) -if TYPE_CHECKING: - from pandas._typing import ( - np_1darray, - np_1darray_bool, - np_1darray_dt, - np_1darray_int64, - np_1darray_object, - np_1darray_td, - ) -else: - from tests._typing import ( - np_1darray, - np_1darray_bool, - np_1darray_dt, - np_1darray_int64, - np_1darray_object, - np_1darray_td, - ) - if not PD_LTE_23: from pandas.errors import Pandas4Warning # type: ignore[attr-defined] # pyright: ignore[reportAttributeAccessIssue,reportRedeclaration] # isort: skip else: diff --git a/tests/test_windowing.py b/tests/test_windowing.py index a52f95e89..ced325fed 100644 --- a/tests/test_windowing.py +++ b/tests/test_windowing.py @@ -1,5 +1,4 @@ import datetime as dt -from typing import TYPE_CHECKING import numpy as np import pandas as pd @@ -26,20 +25,13 @@ check, pytest_warns_bounded, ) +from tests._typing import ( + np_1darray_intp, + np_ndarray, +) from pandas.tseries.frequencies import to_offset -if TYPE_CHECKING: - from pandas._typing import ( - np_1darray_intp, - np_ndarray, - ) -else: - from tests._typing import ( - np_1darray_intp, - np_ndarray, - ) - IDX = date_range("1/1/2000", periods=700, freq="D") S = Series(np.random.standard_normal(700)) DF = DataFrame({"col1": S, "col2": S}) From 5817f2bcd1cef7c83c257b2c35191a2806ae4250 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Sun, 30 Nov 2025 19:06:33 +0100 Subject: [PATCH 5/7] remove extra blank lines --- tests/arrays/test_extension_array.py | 1 - tests/test_frame.py | 1 - tests/test_timefuncs.py | 4 +--- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/arrays/test_extension_array.py b/tests/arrays/test_extension_array.py index 2c00d1b74..290c30b4c 100644 --- a/tests/arrays/test_extension_array.py +++ b/tests/arrays/test_extension_array.py @@ -1,5 +1,4 @@ # Test common ExtensionArray methods - import numpy as np import pandas as pd from pandas.core.arrays import ExtensionArray diff --git a/tests/test_frame.py b/tests/test_frame.py index b5be57660..32529509f 100644 --- a/tests/test_frame.py +++ b/tests/test_frame.py @@ -80,7 +80,6 @@ from pandas._typing import S1 else: - _PandasNamedTuple: TypeAlias = tuple if not PD_LTE_23: diff --git a/tests/test_timefuncs.py b/tests/test_timefuncs.py index 42fcaa4d0..3b05c0f8a 100644 --- a/tests/test_timefuncs.py +++ b/tests/test_timefuncs.py @@ -1,9 +1,7 @@ from __future__ import annotations import datetime as dt -from typing import ( - TypeAlias, -) +from typing import TypeAlias from dateutil.relativedelta import ( FR, From d6c22b73e19c1f5fc3142aa7aa3eb1c61201ebdd Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Sun, 30 Nov 2025 22:19:17 +0100 Subject: [PATCH 6/7] https://github.com/pandas-dev/pandas-stubs/pull/1508#discussion_r2574695253 --- tests/_typing.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/_typing.py b/tests/_typing.py index 9695520f6..3ff6b6fff 100644 --- a/tests/_typing.py +++ b/tests/_typing.py @@ -1,5 +1,10 @@ -# Committed content should be import-only. At runtime of tests, this file will -# be replaced by pandas-stubs/_typing.pyi. +# This file serves as a stub file for static type checkers +# (pyright does not like it if I call the file tests/_typing.pyi). +# In tests/conftest.py, this file is copied to tests/_typing.pyi, and +# pandas-stubs/_typing.pyi is copied to tests/_typing.py. +# After tests are done, tests/_typing.pyi is copied back to +# pandas-stubs/_typing.pyi. +# Do not commit this file if its content is identical to pandas-stubs/_typing.pyi. from pandas._typing import ( BooleanDtypeArg, From 3a505084dd47d5de793d5cf2c27deaf07c33eb70 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Sun, 30 Nov 2025 22:19:32 +0100 Subject: [PATCH 7/7] https://github.com/pandas-dev/pandas-stubs/pull/1508#discussion_r2574690362 --- tests/series/test_series_float.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/series/test_series_float.py b/tests/series/test_series_float.py index 54a1c88e6..f9bd0852f 100644 --- a/tests/series/test_series_float.py +++ b/tests/series/test_series_float.py @@ -47,7 +47,7 @@ def test_constructor() -> None: @pytest.mark.parametrize(("dtype", "target_dtype"), TYPE_FLOAT_ARGS.items()) -def test_constructor_dtype(dtype: "FloatDtypeArg", target_dtype: type) -> None: +def test_constructor_dtype(dtype: FloatDtypeArg, target_dtype: type) -> None: exc = exception_on_platform(dtype) if exc: with pytest.raises(exc, match=rf"data type {dtype!r} not understood"): @@ -101,7 +101,7 @@ def test_constructor_dtype(dtype: "FloatDtypeArg", target_dtype: type) -> None: ("cast_arg", "target_type"), ASTYPE_FLOAT_ARGS.items(), ids=repr ) def test_astype_float( - cast_arg: "FloatDtypeArg | PandasAstypeFloatDtypeArg", target_type: type + cast_arg: FloatDtypeArg | PandasAstypeFloatDtypeArg, target_type: type ) -> None: s = pd.Series([1, 2, 3])