From 68e76cec848723d5085953d16488400f2d766cdc Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Tue, 18 Nov 2025 13:06:39 +0100 Subject: [PATCH 1/8] floating array --- pandas-stubs/_typing.pyi | 338 ++++++++++++-------- pandas-stubs/core/arrays/floating.pyi | 6 +- pandas-stubs/core/arrays/numeric.pyi | 8 + pandas-stubs/core/construction.pyi | 29 +- pandas-stubs/core/indexes/base.pyi | 29 +- pandas-stubs/core/series.pyi | 177 ++++++----- tests/__init__.py | 438 ++++++++++++++++++++++++++ tests/arrays/test_floating_array.py | 48 +++ tests/indexes/test_index_float.py | 164 ++++++++++ tests/series/test_series.py | 123 +------- tests/series/test_series_float.py | 148 +++++++++ 11 files changed, 1167 insertions(+), 341 deletions(-) create mode 100644 tests/arrays/test_floating_array.py create mode 100644 tests/indexes/test_index_float.py create mode 100644 tests/series/test_series_float.py diff --git a/pandas-stubs/_typing.pyi b/pandas-stubs/_typing.pyi index 5ba453ecd..bb06c6ff1 100644 --- a/pandas-stubs/_typing.pyi +++ b/pandas-stubs/_typing.pyi @@ -224,33 +224,35 @@ Dtype: TypeAlias = ExtensionDtype | NpDtype # m # datetime64 +# 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 = ( - # Builtin bool type and its string alias - type[bool] # noqa: PYI030 - | Literal["bool"] - # Pandas nullable boolean type and its string alias - | pd.BooleanDtype - | Literal["boolean"] - # Numpy bool type - # https://numpy.org/doc/stable/reference/arrays.scalars.html#numpy.bool_ - | type[np.bool_] - | Literal["?", "b1", "bool_"] - # PyArrow boolean type and its string alias - | Literal["bool[pyarrow]", "boolean[pyarrow]"] + BuiltinBooleanDtypeArg + | PandasBooleanDtypeArg + | NumpyBooleanDtypeArg + | PyArrowBooleanDtypeArg ) -IntDtypeArg: TypeAlias = ( - # Builtin integer type and its string alias - type[int] # noqa: PYI030 - | Literal["int"] - # Pandas nullable integer types and their string aliases - | pd.Int8Dtype +# 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 +) +# Numpy signed integer types and their string aliases +NumpyIntDtypeArg: TypeAlias = ( # https://numpy.org/doc/stable/reference/arrays.scalars.html#numpy.byte - | type[np.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] @@ -267,19 +269,26 @@ IntDtypeArg: TypeAlias = ( # 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 - | Literal["int8[pyarrow]", "int16[pyarrow]", "int32[pyarrow]", "int64[pyarrow]"] ) -UIntDtypeArg: TypeAlias = ( - # Pandas nullable unsigned integer types and their string aliases - pd.UInt8Dtype # noqa: PYI030 +# 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 +) +# Numpy unsigned integer types and their string aliases +NumpyUIntDtypeArg: TypeAlias = ( # https://numpy.org/doc/stable/reference/arrays.scalars.html#numpy.ubyte - | type[np.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] @@ -296,76 +305,81 @@ UIntDtypeArg: TypeAlias = ( # 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 - | Literal["uint8[pyarrow]", "uint16[pyarrow]", "uint32[pyarrow]", "uint64[pyarrow]"] ) -FloatDtypeArg: TypeAlias = ( - # Builtin float type and its string alias - type[float] # noqa: PYI030 - | Literal["float"] - # Pandas nullable float types and their string aliases - | pd.Float32Dtype - | pd.Float64Dtype - | Literal["Float32", "Float64"] - # Numpy float types and their string aliases +# 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] + type[np.half] | Literal["e", "f2", " NumericDtype: ... diff --git a/pandas-stubs/core/construction.pyi b/pandas-stubs/core/construction.pyi index 58872d197..3638fac78 100644 --- a/pandas-stubs/core/construction.pyi +++ b/pandas-stubs/core/construction.pyi @@ -4,30 +4,43 @@ from typing import overload import numpy as np from pandas.core.arrays.base import ExtensionArray from pandas.core.arrays.boolean import BooleanArray +from pandas.core.arrays.floating import FloatingArray from pandas.core.arrays.integer import IntegerArray from pandas._libs.missing import NAType from pandas._typing import ( - BooleanDtypeArg, - IntDtypeArg, - UIntDtypeArg, + PandasBooleanDtypeArg, + PandasFloatDtypeArg, + PandasIntDtypeArg, + PandasUIntDtypeArg, + np_ndarray_anyint, + np_ndarray_bool, + np_ndarray_float, ) from pandas.core.dtypes.dtypes import ExtensionDtype @overload def array( # type: ignore[overload-overlap] # pyright: ignore[reportOverlappingOverload] - data: Sequence[bool | NAType | None], - dtype: BooleanDtypeArg | None = None, + data: Sequence[bool | np.bool | NAType | None] | np_ndarray_bool | BooleanArray, + dtype: PandasBooleanDtypeArg | None = None, copy: bool = True, ) -> BooleanArray: ... @overload -def array( - data: Sequence[int | NAType | None], - dtype: IntDtypeArg | UIntDtypeArg | None = None, +def array( # type: ignore[overload-overlap] + data: Sequence[int | np.integer | NAType | None] | np_ndarray_anyint | IntegerArray, + dtype: PandasIntDtypeArg | PandasUIntDtypeArg | None = None, copy: bool = True, ) -> IntegerArray: ... @overload +def array( + data: ( + Sequence[float | np.floating | NAType | None] | np_ndarray_float | FloatingArray + ), + dtype: PandasFloatDtypeArg | None = None, + copy: bool = True, +) -> FloatingArray: ... +@overload def array( data: Sequence[object], dtype: str | np.dtype | ExtensionDtype | None = None, diff --git a/pandas-stubs/core/indexes/base.pyi b/pandas-stubs/core/indexes/base.pyi index 1866e9464..c6bc632ea 100644 --- a/pandas-stubs/core/indexes/base.pyi +++ b/pandas-stubs/core/indexes/base.pyi @@ -16,6 +16,7 @@ from typing import ( ClassVar, Generic, Literal, + TypeAlias, final, overload, type_check_only, @@ -30,6 +31,7 @@ from _typeshed import ( ) import numpy as np from pandas.core.arrays.boolean import BooleanArray +from pandas.core.arrays.floating import FloatingArray from pandas.core.base import ( ArrayIndexTimedeltaNoSeq, ElementOpsMixin, @@ -82,6 +84,7 @@ from pandas._typing import ( AnyArrayLikeInt, ArrayLike, AxesData, + BuiltinFloatDtypeArg, CategoryDtypeArg, DropKeep, Dtype, @@ -98,6 +101,10 @@ from pandas._typing import ( Level, MaskType, NaPosition, + NumpyFloatNot16DtypeArg, + PandasAstypeFloatDtypeArg, + PandasFloatDtypeArg, + PyArrowFloatDtypeArg, ReindexMethod, S2_contra, Scalar, @@ -122,6 +129,13 @@ from pandas._typing import ( from pandas.core.dtypes.dtypes import PeriodDtype +FloatNotNumpy16DtypeArg: TypeAlias = ( + BuiltinFloatDtypeArg + | PandasFloatDtypeArg + | NumpyFloatNot16DtypeArg + | PyArrowFloatDtypeArg +) + class InvalidIndexError(Exception): ... class Index(IndexOpsMixin[S1], ElementOpsMixin[S1]): @@ -160,9 +174,8 @@ class Index(IndexOpsMixin[S1], ElementOpsMixin[S1]): @overload def __new__( cls, - data: Sequence[float | np.floating] | IndexOpsMixin[float] | np_ndarray_float, - *, - dtype: Literal["float"] | type_t[float | np.floating] = ..., + data: Sequence[float | np.floating] | np_ndarray_float | FloatingArray, + dtype: None = None, copy: bool = ..., name: Hashable = ..., tupleize_cols: bool = ..., @@ -171,8 +184,7 @@ class Index(IndexOpsMixin[S1], ElementOpsMixin[S1]): def __new__( cls, data: AxesData, - *, - dtype: Literal["float"] | type_t[float | np.floating], + dtype: FloatNotNumpy16DtypeArg, copy: bool = ..., name: Hashable = ..., tupleize_cols: bool = ..., @@ -361,6 +373,13 @@ class Index(IndexOpsMixin[S1], ElementOpsMixin[S1]): @final def ravel(self, order: _str = ...): ... def view(self, cls=...): ... + @overload + def astype( + self, + dtype: FloatNotNumpy16DtypeArg | PandasAstypeFloatDtypeArg, + copy: bool = True, + ) -> Index[float]: ... + @overload def astype(self, dtype: DtypeArg, copy: bool = True) -> Index: ... def take( self, diff --git a/pandas-stubs/core/series.pyi b/pandas-stubs/core/series.pyi index 4e030fb91..a74584f13 100644 --- a/pandas-stubs/core/series.pyi +++ b/pandas-stubs/core/series.pyi @@ -48,7 +48,6 @@ from matplotlib.axes import ( SubplotBase, ) import numpy as np -from numpy import typing as npt from pandas import ( Index, Period, @@ -63,12 +62,15 @@ from pandas.core.api import ( Int64Dtype as Int64Dtype, ) from pandas.core.arrays.base import ExtensionArray -from pandas.core.arrays.boolean import BooleanDtype +from pandas.core.arrays.boolean import ( + BooleanDtype, +) from pandas.core.arrays.categorical import ( Categorical, CategoricalAccessor, ) 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 ( ArrayIndexSeriesTimedeltaNoSeq, @@ -190,6 +192,10 @@ from pandas._typing import ( NaPosition, NsmallestNlargestKeep, ObjectDtypeArg, + PandasAstypeComplexDtypeArg, + PandasAstypeFloatDtypeArg, + PandasAstypeTimedeltaDtypeArg, + PandasAstypeTimestampDtypeArg, PeriodFrequency, QuantileInterpolation, RandomState, @@ -343,41 +349,32 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): __index__: ClassVar[None] __hash__: ClassVar[None] # pyright: ignore[reportIncompatibleMethodOverride] - @overload - def __new__( - cls, - data: npt.NDArray[np.float64], - index: AxesData | None = ..., - dtype: Dtype = ..., - name: Hashable = ..., - copy: bool = ..., - ) -> Series[float]: ... @overload def __new__( cls, data: Sequence[Never], - index: AxesData | None = ..., - dtype: Dtype = ..., - name: Hashable = ..., - copy: bool = ..., + index: AxesData | None = None, + dtype: None = None, + name: Hashable = None, + copy: bool | None = None, ) -> Series: ... @overload def __new__( cls, data: Sequence[list[_str]], - index: AxesData | None = ..., - dtype: Dtype = ..., - name: Hashable = ..., - copy: bool = ..., + index: AxesData | None = None, + dtype: None = None, + name: Hashable = None, + copy: bool | None = None, ) -> Series[list[_str]]: ... @overload def __new__( cls, data: Sequence[_str], - index: AxesData | None = ..., - dtype: Dtype = ..., - name: Hashable = ..., - copy: bool = ..., + index: AxesData | None = None, + dtype: Dtype | None = None, + name: Hashable = None, + copy: bool | None = None, ) -> Series[_str]: ... @overload def __new__( @@ -390,48 +387,48 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): | datetime | date ), - index: AxesData | None = ..., + index: AxesData | None = None, dtype: TimestampDtypeArg = ..., - name: Hashable = ..., - copy: bool = ..., + name: Hashable = None, + copy: bool | None = None, ) -> Series[Timestamp]: ... @overload def __new__( cls, - data: _DataLike, - index: AxesData | None = ..., + data: Sequence[datetime | np.timedelta64] | np_ndarray_dt | DatetimeArray, + index: AxesData | None = None, *, dtype: TimestampDtypeArg, - name: Hashable = ..., - copy: bool = ..., + name: Hashable = None, + copy: bool | None = None, ) -> Series[Timestamp]: ... @overload def __new__( cls, data: _DataLike, - index: AxesData | None = ..., + index: AxesData | None = None, *, dtype: CategoryDtypeArg, - name: Hashable = ..., - copy: bool = ..., + name: Hashable = None, + copy: bool | None = None, ) -> Series[CategoricalDtype]: ... @overload def __new__( cls, data: PeriodIndex | Sequence[Period], - index: AxesData | None = ..., + index: AxesData | None = None, dtype: PeriodDtype = ..., - name: Hashable = ..., - copy: bool = ..., + name: Hashable = None, + copy: bool | None = None, ) -> Series[Period]: ... @overload def __new__( cls, data: Sequence[BaseOffset], - index: AxesData | None = ..., + index: AxesData | None = None, dtype: PeriodDtype = ..., - name: Hashable = ..., - copy: bool = ..., + name: Hashable = None, + copy: bool | None = None, ) -> Series[BaseOffset]: ... @overload def __new__( @@ -443,10 +440,10 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): | np.timedelta64 | timedelta ), - index: AxesData | None = ..., + index: AxesData | None = None, dtype: TimedeltaDtypeArg = ..., - name: Hashable = ..., - copy: bool = ..., + name: Hashable = None, + copy: bool | None = None, ) -> Series[Timedelta]: ... @overload def __new__( @@ -457,56 +454,66 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): | Sequence[Interval[_OrderableT]] | dict[HashableT1, Interval[_OrderableT]] ), - index: AxesData | None = ..., + index: AxesData | None = None, dtype: Literal["Interval"] = ..., - name: Hashable = ..., - copy: bool = ..., + name: Hashable = None, + copy: bool | None = None, ) -> Series[Interval[_OrderableT]]: ... @overload - def __new__( # type: ignore[overload-overlap] + def __new__( # type: ignore[overload-overlap] # pyright: ignore[reportOverlappingOverload] cls, data: Scalar | _DataLike | dict[HashableT1, Any] | None, - index: AxesData | None = ..., + index: AxesData | None = None, *, dtype: type[S1], - name: Hashable = ..., - copy: bool = ..., + name: Hashable = None, + copy: bool | None = None, ) -> Self: ... @overload - def __new__( # type: ignore[overload-overlap] # pyright: ignore[reportOverlappingOverload] + def __new__( # pyright: ignore[reportOverlappingOverload] cls, - data: Sequence[bool], - index: AxesData | None = ..., - dtype: Dtype = ..., - name: Hashable = ..., - copy: bool = ..., + data: Sequence[bool | np.bool], + index: AxesData | None = None, + dtype: BooleanDtypeArg | None = None, + name: Hashable = None, + copy: bool | None = None, ) -> Series[bool]: ... @overload - def __new__( # type: ignore[overload-overlap] + def __new__( cls, - data: Sequence[int], - index: AxesData | None = ..., - dtype: Dtype = ..., - name: Hashable = ..., - copy: bool = ..., + data: Sequence[int | np.integer], + index: AxesData | None = None, + dtype: IntDtypeArg | UIntDtypeArg | None = None, + name: Hashable = None, + copy: bool | None = None, ) -> Series[int]: ... @overload def __new__( cls, - data: Sequence[float], - index: AxesData | None = ..., - dtype: Dtype = ..., - name: Hashable = ..., - copy: bool = ..., + data: Sequence[float | np.floating] | np_ndarray_float | FloatingArray, + index: AxesData | None = None, + dtype: None = None, + name: Hashable = None, + copy: bool | None = None, ) -> Series[float]: ... @overload - def __new__( # type: ignore[overload-cannot-match] # pyright: ignore[reportOverlappingOverload] + def __new__( cls, - data: Sequence[int | float], - index: AxesData | None = ..., - dtype: Dtype = ..., - name: Hashable = ..., - copy: bool = ..., + data: AxesData, + index: None = None, + *, + dtype: FloatDtypeArg, + name: Hashable = None, + copy: bool | None = None, + ) -> Series[float]: ... + @overload + def __new__( + cls, + data: AxesData, + index: AxesData, + dtype: FloatDtypeArg, + name: Hashable = None, + copy: bool | None = None, ) -> Series[float]: ... @overload def __new__( @@ -514,10 +521,10 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): data: ( S1 | _DataLikeS1[S1] | dict[HashableT1, S1] | KeysView[S1] | ValuesView[S1] ), - index: AxesData | None = ..., - dtype: Dtype = ..., - name: Hashable = ..., - copy: bool = ..., + index: AxesData | None = None, + dtype: Dtype | None = None, + name: Hashable = None, + copy: bool | None = None, ) -> Self: ... @overload def __new__( @@ -530,11 +537,11 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): | NaTType | NAType | None - ) = ..., - index: AxesData | None = ..., - dtype: Dtype = ..., - name: Hashable = ..., - copy: bool = ..., + ) = None, + index: AxesData | None = None, + dtype: Dtype | None = None, + name: Hashable = None, + copy: bool | None = None, ) -> Series: ... @property def hasnans(self) -> bool: ... @@ -1446,28 +1453,28 @@ class Series(IndexOpsMixin[S1], ElementOpsMixin[S1], NDFrame): @overload def astype( self, - dtype: FloatDtypeArg, + dtype: FloatDtypeArg | PandasAstypeFloatDtypeArg, copy: _bool = ..., errors: IgnoreRaise = ..., ) -> Series[float]: ... @overload def astype( self, - dtype: ComplexDtypeArg, + dtype: ComplexDtypeArg | PandasAstypeComplexDtypeArg, copy: _bool = ..., errors: IgnoreRaise = ..., ) -> Series[complex]: ... @overload def astype( self, - dtype: TimedeltaDtypeArg, + dtype: TimedeltaDtypeArg | PandasAstypeTimedeltaDtypeArg, copy: _bool = ..., errors: IgnoreRaise = ..., ) -> Series[Timedelta]: ... @overload def astype( self, - dtype: TimestampDtypeArg, + dtype: TimestampDtypeArg | PandasAstypeTimestampDtypeArg, copy: _bool = ..., errors: IgnoreRaise = ..., ) -> Series[Timestamp]: ... diff --git a/tests/__init__.py b/tests/__init__.py index 9227202f5..0433d5133 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,13 +1,16 @@ from __future__ import annotations +from collections.abc import Callable from contextlib import ( AbstractContextManager, nullcontext, suppress, ) +import platform import sys from typing import ( TYPE_CHECKING, + Any, Final, Literal, TypeAlias, @@ -27,16 +30,27 @@ from pandas.util.version import Version import pytest +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, @@ -66,6 +80,364 @@ 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", "= "2.0.0" +NATIVE_FLOAT_ARGS = {float: np.floating, "float": np.floating} +NUMPY_FLOAT16_ARGS = { + np.half: np.half, + "half": np.half, + "e": np.half, + "float16": np.float16, + "f2": np.float16, +} +NUMPY_FLOAT_NOT16_ARGS = { + # numpy float32 + np.single: np.single, + "single": np.single, + "f": np.single, + "float32": np.float32, + "f4": np.float32, + # numpy float64 + np.double: np.double, + "double": np.double, + "d": np.double, + "float64": np.float64, + "f8": np.float64, + # numpy float128 + np.longdouble: np.longdouble, + "g": np.longdouble, +} +PYARROW_FLOAT_ARGS = { + # pyarrow float32 + "float32[pyarrow]": float, + "float[pyarrow]": float, + # pyarrow float64 + "float64[pyarrow]": float, + "double[pyarrow]": float, +} +PANDAS_FLOAT_ARGS = { + # pandas Float32 + pd.Float32Dtype(): np.float32, + "Float32": np.float32, + # pandas Float64 + pd.Float64Dtype(): np.float64, + "Float64": np.float64, +} +TYPE_FLOAT_NOT_NUMPY16_ARGS = ( + NATIVE_FLOAT_ARGS | NUMPY_FLOAT_NOT16_ARGS | PYARROW_FLOAT_ARGS | PANDAS_FLOAT_ARGS +) +TYPE_FLOAT_ARGS = TYPE_FLOAT_NOT_NUMPY16_ARGS | NUMPY_FLOAT16_ARGS +ASTYPE_FLOAT_NOT_NUMPY16_ARGS = { + **TYPE_FLOAT_NOT_NUMPY16_ARGS, + "longdouble": np.longdouble, + "f16": np.longdouble, + # "float96": np.longdouble, # NOTE: unsupported + "float128": np.longdouble, # NOTE: UNIX ONLY +} +ASTYPE_FLOAT_ARGS = ASTYPE_FLOAT_NOT_NUMPY16_ARGS | NUMPY_FLOAT16_ARGS + def check( actual: T, @@ -245,3 +671,15 @@ def pytest_warns_bounded( if upper_exception is None: return nullcontext() return suppress(upper_exception) + + +def skip_platform( + raise_type_error: Callable[[], Any], dtype: type | str | ExtensionDtype +) -> None: + system = platform.system() + if ( + system == "Windows" or system == "Darwin" and platform.processor() == "arm" + ) and dtype in {"f16", "float128"}: + with pytest.raises(TypeError): + raise_type_error() + pytest.skip(f"{system} does not support {dtype}") diff --git a/tests/arrays/test_floating_array.py b/tests/arrays/test_floating_array.py new file mode 100644 index 000000000..f1d33f602 --- /dev/null +++ b/tests/arrays/test_floating_array.py @@ -0,0 +1,48 @@ +from typing import TYPE_CHECKING + +import numpy as np +import pandas as pd +from pandas.core.arrays.floating import FloatingArray +import pytest +from typing_extensions import assert_type + +from tests import ( + PANDAS_FLOAT_ARGS, + PandasFloatDtypeArg, + check, + skip_platform, +) + + +def test_constructor() -> None: + check(assert_type(pd.array([1.0]), FloatingArray), FloatingArray) + check(assert_type(pd.array([1.0, np.float64(1)]), FloatingArray), FloatingArray) + check(assert_type(pd.array([1.0, None]), FloatingArray), FloatingArray) + check(assert_type(pd.array([1.0, pd.NA, None]), FloatingArray), FloatingArray) + + check( + assert_type( # type: ignore[assert-type] # I do not understand + pd.array(np.array([1.0], np.float64)), FloatingArray + ), + FloatingArray, + ) + + check(assert_type(pd.array(pd.array([1.0])), FloatingArray), FloatingArray) + + +@pytest.mark.parametrize(("dtype", "target_dtype"), PANDAS_FLOAT_ARGS.items(), ids=repr) +def test_constructor_dtype(dtype: PandasFloatDtypeArg, target_dtype: type) -> None: + def maker() -> FloatingArray: + return assert_type(pd.array([1.0], dtype=dtype), FloatingArray) + + skip_platform(maker, dtype) + + check(maker(), FloatingArray, target_dtype) + + if TYPE_CHECKING: + # pandas Float32 + assert_type(pd.array([1.0], dtype=pd.Float32Dtype()), FloatingArray) + assert_type(pd.array([1.0], dtype="Float32"), FloatingArray) + # pandas Float64 + assert_type(pd.array([1.0], dtype=pd.Float64Dtype()), FloatingArray) + assert_type(pd.array([1.0], dtype="Float64"), FloatingArray) diff --git a/tests/indexes/test_index_float.py b/tests/indexes/test_index_float.py new file mode 100644 index 000000000..5009a706a --- /dev/null +++ b/tests/indexes/test_index_float.py @@ -0,0 +1,164 @@ +from typing import TYPE_CHECKING + +import numpy as np +import pandas as pd +import pytest +from typing_extensions import assert_type + +from tests import ( + ASTYPE_FLOAT_NOT_NUMPY16_ARGS, + TYPE_FLOAT_NOT_NUMPY16_ARGS, + PandasAstypeFloatDtypeArg, + check, + skip_platform, +) + +if TYPE_CHECKING: + from pandas.core.indexes.base import FloatNotNumpy16DtypeArg + + +def test_constructor() -> None: + check(assert_type(pd.Index([1.0]), "pd.Index[float]"), pd.Index, np.floating) + check( + assert_type(pd.Index([1.0, np.float64(1)]), "pd.Index[float]"), + pd.Index, + np.floating, + ) + check( + assert_type( # type: ignore[assert-type] # I do not understand + pd.Index(np.array([1.0], np.float64)), "pd.Index[float]" + ), + pd.Index, + np.floating, + ) + check( + assert_type(pd.Index(pd.array([1.0])), "pd.Index[float]"), + pd.Index, + np.floating, + ) + check( + assert_type(pd.Index(pd.Index([1.0])), "pd.Index[float]"), + pd.Index, + np.floating, + ) + check( + assert_type(pd.Index(pd.Series([1.0])), "pd.Index[float]"), + pd.Index, + np.floating, + ) + + +@pytest.mark.parametrize(("dtype", "target_dtype"), TYPE_FLOAT_NOT_NUMPY16_ARGS.items()) +def test_constructor_dtype( + dtype: "FloatNotNumpy16DtypeArg", target_dtype: type +) -> None: + def maker() -> "pd.Index[float]": + return assert_type(pd.Index([1.0], dtype=dtype), "pd.Index[float]") + + skip_platform(maker, dtype) + if target_dtype is np.float16: + reason = r"float16 indexes are not supported" + with pytest.raises(NotImplementedError, match=reason): + maker() + pytest.skip(reason) + + check(maker(), pd.Index, target_dtype) + + if TYPE_CHECKING: + # python float + assert_type(pd.Index([1.0], dtype=float), "pd.Index[float]") + assert_type(pd.Index([1.0], dtype="float"), "pd.Index[float]") + # pandas Float32 + assert_type(pd.Index([1.0], dtype=pd.Float32Dtype()), "pd.Index[float]") + assert_type(pd.Index([1.0], dtype="Float32"), "pd.Index[float]") + # pandas Float64 + assert_type(pd.Index([1.0], dtype=pd.Float64Dtype()), "pd.Index[float]") + assert_type(pd.Index([1.0], dtype="Float64"), "pd.Index[float]") + # numpy float32 + assert_type(pd.Index([1.0], dtype=np.single), "pd.Index[float]") + assert_type(pd.Index([1.0], dtype="single"), "pd.Index[float]") + assert_type(pd.Index([1.0], dtype="float32"), "pd.Index[float]") + assert_type(pd.Index([1.0], dtype="f"), "pd.Index[float]") + assert_type(pd.Index([1.0], dtype="f4"), "pd.Index[float]") + # numpy float64 + assert_type(pd.Index([1.0], dtype=np.double), "pd.Index[float]") + assert_type(pd.Index([1.0], dtype="double"), "pd.Index[float]") + assert_type(pd.Index([1.0], dtype="float64"), "pd.Index[float]") + assert_type(pd.Index([1.0], dtype="d"), "pd.Index[float]") + assert_type(pd.Index([1.0], dtype="f8"), "pd.Index[float]") + # numpy float128 + assert_type(pd.Index([1.0], dtype=np.longdouble), "pd.Index[float]") + assert_type(pd.Index([1.0], dtype="longdouble"), "pd.Index[float]") + assert_type(pd.Index([1.0], dtype="float128"), "pd.Index[float]") + assert_type(pd.Index([1.0], dtype="g"), "pd.Index[float]") + assert_type(pd.Index([1.0], dtype="f16"), "pd.Index[float]") + # pyarrow float32 + assert_type(pd.Index([1.0], dtype="float32[pyarrow]"), "pd.Index[float]") + assert_type(pd.Index([1.0], dtype="float[pyarrow]"), "pd.Index[float]") + # pyarrow float64 + assert_type(pd.Index([1.0], dtype="float64[pyarrow]"), "pd.Index[float]") + assert_type(pd.Index([1.0], dtype="double[pyarrow]"), "pd.Index[float]") + + # if TYPE_CHECKING_INVALID_USAGE: + # # numpy float16 + # pd.Index([1.0], dtype=np.half) + # pd.Index([1.0], dtype="half") + # pd.Index([1.0], dtype="float16") + # pd.Index([1.0], dtype="e") + # pd.Index([1.0], dtype="f2") + + +@pytest.mark.parametrize( + ("cast_arg", "target_type"), ASTYPE_FLOAT_NOT_NUMPY16_ARGS.items(), ids=repr +) +def test_astype_float( + cast_arg: "FloatNotNumpy16DtypeArg | PandasAstypeFloatDtypeArg", target_type: type +) -> None: + s = pd.Index([1, 2, 3]) + + skip_platform(lambda: s.astype(cast_arg), cast_arg) + + check(s.astype(cast_arg), pd.Index, target_type) + + if TYPE_CHECKING: + # python float + assert_type(s.astype(float), "pd.Index[float]") + assert_type(s.astype("float"), "pd.Index[float]") + # pandas Float32 + assert_type(s.astype(pd.Float32Dtype()), "pd.Index[float]") + assert_type(s.astype("Float32"), "pd.Index[float]") + # pandas Float64 + assert_type(s.astype(pd.Float64Dtype()), "pd.Index[float]") + assert_type(s.astype("Float64"), "pd.Index[float]") + # numpy float32 + assert_type(s.astype(np.single), "pd.Index[float]") + assert_type(s.astype("single"), "pd.Index[float]") + assert_type(s.astype("float32"), "pd.Index[float]") + assert_type(s.astype("f"), "pd.Index[float]") + assert_type(s.astype("f4"), "pd.Index[float]") + # numpy float64 + assert_type(s.astype(np.double), "pd.Index[float]") + assert_type(s.astype("double"), "pd.Index[float]") + assert_type(s.astype("float64"), "pd.Index[float]") + assert_type(s.astype("d"), "pd.Index[float]") + assert_type(s.astype("f8"), "pd.Index[float]") + # numpy float128 + assert_type(s.astype(np.longdouble), "pd.Index[float]") + assert_type(s.astype("longdouble"), "pd.Index[float]") + assert_type(s.astype("float128"), "pd.Index[float]") + assert_type(s.astype("g"), "pd.Index[float]") + assert_type(s.astype("f16"), "pd.Index[float]") + # pyarrow float32 + assert_type(s.astype("float32[pyarrow]"), "pd.Index[float]") + assert_type(s.astype("float[pyarrow]"), "pd.Index[float]") + # pyarrow float64 + assert_type(s.astype("float64[pyarrow]"), "pd.Index[float]") + assert_type(s.astype("double[pyarrow]"), "pd.Index[float]") + + # if TYPE_CHECKING_INVALID_USAGE: + # # numpy float16 + # s.astype(np.half) + # s.astype("half") + # s.astype("float16") + # s.astype("e") + # s.astype("f2") diff --git a/tests/series/test_series.py b/tests/series/test_series.py index c405aa222..dc2417a8d 100644 --- a/tests/series/test_series.py +++ b/tests/series/test_series.py @@ -54,11 +54,15 @@ from pandas.core.dtypes.dtypes import CategoricalDtype # noqa F401 from tests import ( + ASTYPE_FLOAT_ARGS, LINUX, MAC, PD_LTE_23, TYPE_CHECKING_INVALID_USAGE, WINDOWS, + PandasAstypeComplexDtypeArg, + PandasAstypeTimedeltaDtypeArg, + PandasAstypeTimestampDtypeArg, check, ensure_clean, np_1darray, @@ -92,7 +96,6 @@ BytesDtypeArg, CategoryDtypeArg, ComplexDtypeArg, - FloatDtypeArg, IntDtypeArg, ObjectDtypeArg, StrDtypeArg, @@ -2366,50 +2369,10 @@ def test_change_to_dict_return_type() -> None: ("uint64[pyarrow]", int), ] -ASTYPE_FLOAT_ARGS: list[tuple[FloatDtypeArg, type]] = [ - # python float - (float, np.floating), - ("float", np.floating), - # pandas Float32 - (pd.Float32Dtype(), np.float32), - ("Float32", np.float32), - # pandas Float64 - (pd.Float64Dtype(), np.float64), - ("Float64", np.float64), - # numpy float16 - (np.half, np.half), - ("half", np.half), - ("e", np.half), - ("float16", np.float16), - ("f2", np.float16), - # numpy float32 - (np.single, np.single), - ("single", np.single), - ("f", np.single), - ("float32", np.float32), - ("f4", np.float32), - # numpy float64 - (np.double, np.double), - ("double", np.double), - ("d", np.double), - ("float64", np.float64), - ("f8", np.float64), - # numpy float128 - (np.longdouble, np.longdouble), - ("longdouble", np.longdouble), - ("g", np.longdouble), - ("f16", np.longdouble), - # ("float96", np.longdouble), # NOTE: unsupported - ("float128", np.longdouble), # NOTE: UNIX ONLY - # pyarrow float32 - ("float32[pyarrow]", float), - ("float[pyarrow]", float), - # pyarrow float64 - ("float64[pyarrow]", float), - ("double[pyarrow]", float), -] -ASTYPE_COMPLEX_ARGS: list[tuple[ComplexDtypeArg, type]] = [ +ASTYPE_COMPLEX_ARGS: list[ + tuple[ComplexDtypeArg | PandasAstypeComplexDtypeArg, type] +] = [ # python complex (complex, np.complexfloating), ("complex", np.complexfloating), @@ -2435,7 +2398,9 @@ def test_change_to_dict_return_type() -> None: ] -ASTYPE_TIMESTAMP_ARGS: list[tuple[TimestampDtypeArg, type]] = [ +ASTYPE_TIMESTAMP_ARGS: list[ + tuple[TimestampDtypeArg | PandasAstypeTimestampDtypeArg, type] +] = [ # numpy datetime64 ("datetime64[Y]", datetime.datetime), ("datetime64[M]", datetime.datetime), @@ -2492,7 +2457,9 @@ def test_change_to_dict_return_type() -> None: ] -ASTYPE_TIMEDELTA_ARGS: list[tuple[TimedeltaDtypeArg, type]] = [ +ASTYPE_TIMEDELTA_ARGS: list[ + tuple[TimedeltaDtypeArg | PandasAstypeTimedeltaDtypeArg, type] +] = [ # numpy timedelta64 ("timedelta64[Y]", datetime.timedelta), ("timedelta64[M]", datetime.timedelta), @@ -2741,68 +2708,6 @@ def test_astype_uint(cast_arg: IntDtypeArg, target_type: type) -> None: assert_type(s.astype("uint64[pyarrow]"), "pd.Series[int]") -@pytest.mark.parametrize("cast_arg, target_type", ASTYPE_FLOAT_ARGS, ids=repr) -def test_astype_float(cast_arg: FloatDtypeArg, target_type: type) -> None: - s = pd.Series([1, 2, 3]) - - if platform.system() == "Windows" and cast_arg in ("f16", "float128"): - with pytest.raises(TypeError): - s.astype(cast_arg) - pytest.skip("Windows does not support float128") - - if ( - platform.system() == "Darwin" - and platform.processor() == "arm" - and cast_arg in ("f16", "float128") - ): - with pytest.raises(TypeError): - s.astype(cast_arg) - pytest.skip("MacOS arm does not support float128") - - check(s.astype(cast_arg), pd.Series, target_type) - - if TYPE_CHECKING: - # python float - assert_type(s.astype(float), "pd.Series[float]") - assert_type(s.astype("float"), "pd.Series[float]") - # pandas Float32 - assert_type(s.astype(pd.Float32Dtype()), "pd.Series[float]") - assert_type(s.astype("Float32"), "pd.Series[float]") - # pandas Float64 - assert_type(s.astype(pd.Float64Dtype()), "pd.Series[float]") - assert_type(s.astype("Float64"), "pd.Series[float]") - # numpy float16 - assert_type(s.astype(np.half), "pd.Series[float]") - assert_type(s.astype("half"), "pd.Series[float]") - assert_type(s.astype("float16"), "pd.Series[float]") - assert_type(s.astype("e"), "pd.Series[float]") - assert_type(s.astype("f2"), "pd.Series[float]") - # numpy float32 - assert_type(s.astype(np.single), "pd.Series[float]") - assert_type(s.astype("single"), "pd.Series[float]") - assert_type(s.astype("float32"), "pd.Series[float]") - assert_type(s.astype("f"), "pd.Series[float]") - assert_type(s.astype("f4"), "pd.Series[float]") - # numpy float64 - assert_type(s.astype(np.double), "pd.Series[float]") - assert_type(s.astype("double"), "pd.Series[float]") - assert_type(s.astype("float64"), "pd.Series[float]") - assert_type(s.astype("d"), "pd.Series[float]") - assert_type(s.astype("f8"), "pd.Series[float]") - # numpy float128 - assert_type(s.astype(np.longdouble), "pd.Series[float]") - assert_type(s.astype("longdouble"), "pd.Series[float]") - assert_type(s.astype("float128"), "pd.Series[float]") - assert_type(s.astype("g"), "pd.Series[float]") - assert_type(s.astype("f16"), "pd.Series[float]") - # pyarrow float32 - assert_type(s.astype("float32[pyarrow]"), "pd.Series[float]") - assert_type(s.astype("float[pyarrow]"), "pd.Series[float]") - # pyarrow float64 - assert_type(s.astype("float64[pyarrow]"), "pd.Series[float]") - assert_type(s.astype("double[pyarrow]"), "pd.Series[float]") - - @pytest.mark.parametrize("cast_arg, target_type", ASTYPE_COMPLEX_ARGS, ids=repr) def test_astype_complex(cast_arg: ComplexDtypeArg, target_type: type) -> None: s = pd.Series([1, 2, 3]) @@ -3114,7 +3019,7 @@ def test_all_astype_args_tested() -> None: ASTYPE_BOOL_ARGS + ASTYPE_INT_ARGS + ASTYPE_UINT_ARGS - + ASTYPE_FLOAT_ARGS + + list(ASTYPE_FLOAT_ARGS.items()) + ASTYPE_COMPLEX_ARGS + ASTYPE_TIMEDELTA_ARGS + ASTYPE_TIMESTAMP_ARGS diff --git a/tests/series/test_series_float.py b/tests/series/test_series_float.py new file mode 100644 index 000000000..8dd8a080b --- /dev/null +++ b/tests/series/test_series_float.py @@ -0,0 +1,148 @@ +from typing import TYPE_CHECKING + +import numpy as np +import pandas as pd +import pytest +from typing_extensions import assert_type + +from tests import ( + ASTYPE_FLOAT_ARGS, + TYPE_FLOAT_ARGS, + FloatDtypeArg, + PandasAstypeFloatDtypeArg, + check, + skip_platform, +) + + +def test_constructor() -> None: + check(assert_type(pd.Series([1.0]), "pd.Series[float]"), pd.Series, np.floating) + check( + assert_type(pd.Series([1.0, np.float64(1)]), "pd.Series[float]"), + pd.Series, + np.floating, + ) + check( + assert_type(pd.Series(np.array([1.0], np.float64)), "pd.Series[float]"), + pd.Series, + np.floating, + ) + check( + assert_type(pd.Series(pd.array([1.0])), "pd.Series[float]"), + pd.Series, + np.floating, + ) + check( + assert_type(pd.Series(pd.Index([1.0])), "pd.Series[float]"), + pd.Series, + np.floating, + ) + check( + assert_type(pd.Series(pd.Series([1.0])), "pd.Series[float]"), + pd.Series, + np.floating, + ) + + +@pytest.mark.parametrize(("dtype", "target_dtype"), TYPE_FLOAT_ARGS.items()) +def test_constructor_dtype(dtype: FloatDtypeArg, target_dtype: type) -> None: + def maker() -> "pd.Series[float]": + return assert_type(pd.Series([1.0], dtype=dtype), "pd.Series[float]") + + skip_platform(maker, dtype) + + check(maker(), pd.Series, target_dtype) + if TYPE_CHECKING: + # python float + assert_type(pd.Series([1.0], dtype=float), "pd.Series[float]") + assert_type(pd.Series([1.0], dtype="float"), "pd.Series[float]") + # pandas Float32 + assert_type(pd.Series([1.0], dtype=pd.Float32Dtype()), "pd.Series[float]") + assert_type(pd.Series([1.0], dtype="Float32"), "pd.Series[float]") + # pandas Float64 + assert_type(pd.Series([1.0], dtype=pd.Float64Dtype()), "pd.Series[float]") + assert_type(pd.Series([1.0], dtype="Float64"), "pd.Series[float]") + # numpy float16 + assert_type(pd.Series([1.0], dtype=np.half), "pd.Series[float]") + assert_type(pd.Series([1.0], dtype="half"), "pd.Series[float]") + assert_type(pd.Series([1.0], dtype="float16"), "pd.Series[float]") + assert_type(pd.Series([1.0], dtype="e"), "pd.Series[float]") + assert_type(pd.Series([1.0], dtype="f2"), "pd.Series[float]") + # numpy float32 + assert_type(pd.Series([1.0], dtype=np.single), "pd.Series[float]") + assert_type(pd.Series([1.0], dtype="single"), "pd.Series[float]") + assert_type(pd.Series([1.0], dtype="float32"), "pd.Series[float]") + assert_type(pd.Series([1.0], dtype="f"), "pd.Series[float]") + assert_type(pd.Series([1.0], dtype="f4"), "pd.Series[float]") + # numpy float64 + assert_type(pd.Series([1.0], dtype=np.double), "pd.Series[float]") + assert_type(pd.Series([1.0], dtype="double"), "pd.Series[float]") + assert_type(pd.Series([1.0], dtype="float64"), "pd.Series[float]") + assert_type(pd.Series([1.0], dtype="d"), "pd.Series[float]") + assert_type(pd.Series([1.0], dtype="f8"), "pd.Series[float]") + # numpy float128 + assert_type(pd.Series([1.0], dtype=np.longdouble), "pd.Series[float]") + assert_type(pd.Series([1.0], dtype="longdouble"), "pd.Series[float]") + assert_type(pd.Series([1.0], dtype="float128"), "pd.Series[float]") + assert_type(pd.Series([1.0], dtype="g"), "pd.Series[float]") + assert_type(pd.Series([1.0], dtype="f16"), "pd.Series[float]") + # pyarrow float32 + assert_type(pd.Series([1.0], dtype="float32[pyarrow]"), "pd.Series[float]") + assert_type(pd.Series([1.0], dtype="float[pyarrow]"), "pd.Series[float]") + # pyarrow float64 + assert_type(pd.Series([1.0], dtype="float64[pyarrow]"), "pd.Series[float]") + assert_type(pd.Series([1.0], dtype="double[pyarrow]"), "pd.Series[float]") + + +@pytest.mark.parametrize( + ("cast_arg", "target_type"), ASTYPE_FLOAT_ARGS.items(), ids=repr +) +def test_astype_float( + cast_arg: FloatDtypeArg | PandasAstypeFloatDtypeArg, target_type: type +) -> None: + s = pd.Series([1, 2, 3]) + + skip_platform(lambda: s.astype(cast_arg), cast_arg) + + check(s.astype(cast_arg), pd.Series, target_type) + + if TYPE_CHECKING: + # python float + assert_type(s.astype(float), "pd.Series[float]") + assert_type(s.astype("float"), "pd.Series[float]") + # pandas Float32 + assert_type(s.astype(pd.Float32Dtype()), "pd.Series[float]") + assert_type(s.astype("Float32"), "pd.Series[float]") + # pandas Float64 + assert_type(s.astype(pd.Float64Dtype()), "pd.Series[float]") + assert_type(s.astype("Float64"), "pd.Series[float]") + # numpy float16 + assert_type(s.astype(np.half), "pd.Series[float]") + assert_type(s.astype("half"), "pd.Series[float]") + assert_type(s.astype("float16"), "pd.Series[float]") + assert_type(s.astype("e"), "pd.Series[float]") + assert_type(s.astype("f2"), "pd.Series[float]") + # numpy float32 + assert_type(s.astype(np.single), "pd.Series[float]") + assert_type(s.astype("single"), "pd.Series[float]") + assert_type(s.astype("float32"), "pd.Series[float]") + assert_type(s.astype("f"), "pd.Series[float]") + assert_type(s.astype("f4"), "pd.Series[float]") + # numpy float64 + assert_type(s.astype(np.double), "pd.Series[float]") + assert_type(s.astype("double"), "pd.Series[float]") + assert_type(s.astype("float64"), "pd.Series[float]") + assert_type(s.astype("d"), "pd.Series[float]") + assert_type(s.astype("f8"), "pd.Series[float]") + # numpy float128 + assert_type(s.astype(np.longdouble), "pd.Series[float]") + assert_type(s.astype("longdouble"), "pd.Series[float]") + assert_type(s.astype("float128"), "pd.Series[float]") + assert_type(s.astype("g"), "pd.Series[float]") + assert_type(s.astype("f16"), "pd.Series[float]") + # pyarrow float32 + assert_type(s.astype("float32[pyarrow]"), "pd.Series[float]") + assert_type(s.astype("float[pyarrow]"), "pd.Series[float]") + # pyarrow float64 + assert_type(s.astype("float64[pyarrow]"), "pd.Series[float]") + assert_type(s.astype("double[pyarrow]"), "pd.Series[float]") From f87938de1a8165ddd753a6243132d2aaeb9e0e51 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Tue, 18 Nov 2025 17:52:39 +0100 Subject: [PATCH 2/8] simplify import --- pandas-stubs/core/series.pyi | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pandas-stubs/core/series.pyi b/pandas-stubs/core/series.pyi index a74584f13..9544ba350 100644 --- a/pandas-stubs/core/series.pyi +++ b/pandas-stubs/core/series.pyi @@ -62,9 +62,7 @@ from pandas.core.api import ( Int64Dtype as Int64Dtype, ) from pandas.core.arrays.base import ExtensionArray -from pandas.core.arrays.boolean import ( - BooleanDtype, -) +from pandas.core.arrays.boolean import BooleanDtype from pandas.core.arrays.categorical import ( Categorical, CategoricalAccessor, From cc784d198f509ad6f39b49d26d3c2ff3e54450dd Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Tue, 18 Nov 2025 23:37:59 +0100 Subject: [PATCH 3/8] simplify skip_platform --- tests/__init__.py | 15 +++------------ tests/arrays/test_floating_array.py | 11 +++++------ tests/indexes/test_index_float.py | 24 ++++++++++-------------- tests/series/test_series_float.py | 18 ++++++++++-------- 4 files changed, 28 insertions(+), 40 deletions(-) diff --git a/tests/__init__.py b/tests/__init__.py index 0433d5133..aa262851d 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,6 +1,5 @@ from __future__ import annotations -from collections.abc import Callable from contextlib import ( AbstractContextManager, nullcontext, @@ -10,7 +9,6 @@ import sys from typing import ( TYPE_CHECKING, - Any, Final, Literal, TypeAlias, @@ -468,6 +466,7 @@ LINUX = sys.platform == "linux" WINDOWS = sys.platform in {"win32", "cygwin"} MAC = sys.platform == "darwin" +MAC_ARM = sys.platform == "darwin" and platform.processor() == "arm64" PD_LTE_23 = Version(pd.__version__) < Version("2.3.999") NUMPY20 = np.lib.NumpyVersion(np.__version__) >= "2.0.0" @@ -673,13 +672,5 @@ def pytest_warns_bounded( return suppress(upper_exception) -def skip_platform( - raise_type_error: Callable[[], Any], dtype: type | str | ExtensionDtype -) -> None: - system = platform.system() - if ( - system == "Windows" or system == "Darwin" and platform.processor() == "arm" - ) and dtype in {"f16", "float128"}: - with pytest.raises(TypeError): - raise_type_error() - pytest.skip(f"{system} does not support {dtype}") +def skip_platform(dtype: type | str | ExtensionDtype) -> bool: + return (WINDOWS or MAC_ARM) and dtype in {"f16", "float128"} diff --git a/tests/arrays/test_floating_array.py b/tests/arrays/test_floating_array.py index f1d33f602..49e84babe 100644 --- a/tests/arrays/test_floating_array.py +++ b/tests/arrays/test_floating_array.py @@ -32,12 +32,11 @@ 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 maker() -> FloatingArray: - return assert_type(pd.array([1.0], dtype=dtype), FloatingArray) - - skip_platform(maker, dtype) - - check(maker(), FloatingArray, target_dtype) + if skip_platform(dtype): + with pytest.raises(TypeError): + assert_type(pd.array([1.0], dtype=dtype), FloatingArray) + else: + check(pd.array([1.0], dtype=dtype), FloatingArray, target_dtype) if TYPE_CHECKING: # pandas Float32 diff --git a/tests/indexes/test_index_float.py b/tests/indexes/test_index_float.py index 5009a706a..138e300cb 100644 --- a/tests/indexes/test_index_float.py +++ b/tests/indexes/test_index_float.py @@ -52,17 +52,11 @@ def test_constructor() -> None: def test_constructor_dtype( dtype: "FloatNotNumpy16DtypeArg", target_dtype: type ) -> None: - def maker() -> "pd.Index[float]": - return assert_type(pd.Index([1.0], dtype=dtype), "pd.Index[float]") - - skip_platform(maker, dtype) - if target_dtype is np.float16: - reason = r"float16 indexes are not supported" - with pytest.raises(NotImplementedError, match=reason): - maker() - pytest.skip(reason) - - check(maker(), pd.Index, target_dtype) + if skip_platform(dtype): + with pytest.raises(TypeError): + assert_type(pd.Index([1.0], dtype=dtype), "pd.Index[float]") + else: + check(pd.Index([1.0], dtype=dtype), pd.Index, target_dtype) if TYPE_CHECKING: # python float @@ -116,9 +110,11 @@ def test_astype_float( ) -> None: s = pd.Index([1, 2, 3]) - skip_platform(lambda: s.astype(cast_arg), cast_arg) - - check(s.astype(cast_arg), pd.Index, target_type) + if skip_platform(cast_arg): + with pytest.raises(TypeError): + assert_type(s.astype(cast_arg), "pd.Index[float]") + else: + check(s.astype(cast_arg), pd.Index, target_type) if TYPE_CHECKING: # python float diff --git a/tests/series/test_series_float.py b/tests/series/test_series_float.py index 8dd8a080b..2a346a316 100644 --- a/tests/series/test_series_float.py +++ b/tests/series/test_series_float.py @@ -46,12 +46,12 @@ 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 maker() -> "pd.Series[float]": - return assert_type(pd.Series([1.0], dtype=dtype), "pd.Series[float]") + if skip_platform(dtype): + with pytest.raises(TypeError): + assert_type(pd.Series([1.0], dtype=dtype), "pd.Series[float]") + else: + check(pd.Series([1.0], dtype=dtype), pd.Series, target_dtype) - skip_platform(maker, dtype) - - check(maker(), pd.Series, target_dtype) if TYPE_CHECKING: # python float assert_type(pd.Series([1.0], dtype=float), "pd.Series[float]") @@ -102,9 +102,11 @@ def test_astype_float( ) -> None: s = pd.Series([1, 2, 3]) - skip_platform(lambda: s.astype(cast_arg), cast_arg) - - check(s.astype(cast_arg), pd.Series, target_type) + if skip_platform(cast_arg): + with pytest.raises(TypeError): + assert_type(s.astype(cast_arg), "pd.Series[float]") + else: + check(s.astype(cast_arg), pd.Series, target_type) if TYPE_CHECKING: # python float From 9e2175d9cba9ab3f6617603a73c6e5fad72c0167 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Wed, 19 Nov 2025 00:05:18 +0100 Subject: [PATCH 4/8] fix: mac_arm --- tests/__init__.py | 2 +- tests/arrays/test_floating_array.py | 6 +++--- tests/indexes/test_index_float.py | 10 +++++----- tests/series/test_series_float.py | 10 +++++----- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/__init__.py b/tests/__init__.py index aa262851d..635e396f6 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -672,5 +672,5 @@ def pytest_warns_bounded( return suppress(upper_exception) -def skip_platform(dtype: type | str | ExtensionDtype) -> bool: +def is_dtype_invalid_for_platform(dtype: type | str | ExtensionDtype) -> bool: return (WINDOWS or MAC_ARM) and dtype in {"f16", "float128"} diff --git a/tests/arrays/test_floating_array.py b/tests/arrays/test_floating_array.py index 49e84babe..6b915c207 100644 --- a/tests/arrays/test_floating_array.py +++ b/tests/arrays/test_floating_array.py @@ -10,7 +10,7 @@ PANDAS_FLOAT_ARGS, PandasFloatDtypeArg, check, - skip_platform, + is_dtype_invalid_for_platform, ) @@ -32,8 +32,8 @@ 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: - if skip_platform(dtype): - with pytest.raises(TypeError): + if is_dtype_invalid_for_platform(dtype): + with pytest.raises(TypeError, match=rf"data type {dtype!r} not understood"): assert_type(pd.array([1.0], dtype=dtype), FloatingArray) else: check(pd.array([1.0], dtype=dtype), FloatingArray, target_dtype) diff --git a/tests/indexes/test_index_float.py b/tests/indexes/test_index_float.py index 138e300cb..efef6f27f 100644 --- a/tests/indexes/test_index_float.py +++ b/tests/indexes/test_index_float.py @@ -10,7 +10,7 @@ TYPE_FLOAT_NOT_NUMPY16_ARGS, PandasAstypeFloatDtypeArg, check, - skip_platform, + is_dtype_invalid_for_platform, ) if TYPE_CHECKING: @@ -52,8 +52,8 @@ def test_constructor() -> None: def test_constructor_dtype( dtype: "FloatNotNumpy16DtypeArg", target_dtype: type ) -> None: - if skip_platform(dtype): - with pytest.raises(TypeError): + if is_dtype_invalid_for_platform(dtype): + with pytest.raises(TypeError, match=rf"data type {dtype!r} not understood"): assert_type(pd.Index([1.0], dtype=dtype), "pd.Index[float]") else: check(pd.Index([1.0], dtype=dtype), pd.Index, target_dtype) @@ -110,8 +110,8 @@ def test_astype_float( ) -> None: s = pd.Index([1, 2, 3]) - if skip_platform(cast_arg): - with pytest.raises(TypeError): + if is_dtype_invalid_for_platform(cast_arg): + with pytest.raises(TypeError, match=rf"data type {cast_arg!r} not understood"): assert_type(s.astype(cast_arg), "pd.Index[float]") else: check(s.astype(cast_arg), pd.Index, target_type) diff --git a/tests/series/test_series_float.py b/tests/series/test_series_float.py index 2a346a316..3e734abb5 100644 --- a/tests/series/test_series_float.py +++ b/tests/series/test_series_float.py @@ -11,7 +11,7 @@ FloatDtypeArg, PandasAstypeFloatDtypeArg, check, - skip_platform, + is_dtype_invalid_for_platform, ) @@ -46,8 +46,8 @@ def test_constructor() -> None: @pytest.mark.parametrize(("dtype", "target_dtype"), TYPE_FLOAT_ARGS.items()) def test_constructor_dtype(dtype: FloatDtypeArg, target_dtype: type) -> None: - if skip_platform(dtype): - with pytest.raises(TypeError): + if is_dtype_invalid_for_platform(dtype): + with pytest.raises(TypeError, match=rf"data type {dtype!r} not understood"): assert_type(pd.Series([1.0], dtype=dtype), "pd.Series[float]") else: check(pd.Series([1.0], dtype=dtype), pd.Series, target_dtype) @@ -102,8 +102,8 @@ def test_astype_float( ) -> None: s = pd.Series([1, 2, 3]) - if skip_platform(cast_arg): - with pytest.raises(TypeError): + if is_dtype_invalid_for_platform(cast_arg): + with pytest.raises(TypeError, match=rf"data type {cast_arg!r} not understood"): assert_type(s.astype(cast_arg), "pd.Series[float]") else: check(s.astype(cast_arg), pd.Series, target_type) From ef8354ed3557ced24f08ebb920faa81e12e5b121 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Thu, 20 Nov 2025 22:35:11 +0100 Subject: [PATCH 5/8] exception on platform --- tests/__init__.py | 6 ++++-- tests/arrays/test_floating_array.py | 7 ++++--- tests/indexes/test_index_float.py | 12 +++++++----- tests/series/test_series_float.py | 9 +++++---- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/tests/__init__.py b/tests/__init__.py index 635e396f6..bba6c307d 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -672,5 +672,7 @@ def pytest_warns_bounded( return suppress(upper_exception) -def is_dtype_invalid_for_platform(dtype: type | str | ExtensionDtype) -> bool: - return (WINDOWS or MAC_ARM) and dtype in {"f16", "float128"} +def exception_on_platform(dtype: type | str | ExtensionDtype) -> type[Exception] | None: + if (WINDOWS or MAC_ARM) and dtype in {"f16", "float128"}: + return TypeError + return None diff --git a/tests/arrays/test_floating_array.py b/tests/arrays/test_floating_array.py index 6b915c207..18ebe72a7 100644 --- a/tests/arrays/test_floating_array.py +++ b/tests/arrays/test_floating_array.py @@ -10,7 +10,7 @@ PANDAS_FLOAT_ARGS, PandasFloatDtypeArg, check, - is_dtype_invalid_for_platform, + exception_on_platform, ) @@ -32,8 +32,9 @@ 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: - if is_dtype_invalid_for_platform(dtype): - with pytest.raises(TypeError, match=rf"data type {dtype!r} not understood"): + exc = exception_on_platform(dtype) + if exc: + with pytest.raises(exc, match=rf"data type {dtype!r} not understood"): assert_type(pd.array([1.0], dtype=dtype), FloatingArray) else: check(pd.array([1.0], dtype=dtype), FloatingArray, target_dtype) diff --git a/tests/indexes/test_index_float.py b/tests/indexes/test_index_float.py index efef6f27f..f2dc35b7a 100644 --- a/tests/indexes/test_index_float.py +++ b/tests/indexes/test_index_float.py @@ -10,7 +10,7 @@ TYPE_FLOAT_NOT_NUMPY16_ARGS, PandasAstypeFloatDtypeArg, check, - is_dtype_invalid_for_platform, + exception_on_platform, ) if TYPE_CHECKING: @@ -52,8 +52,9 @@ def test_constructor() -> None: def test_constructor_dtype( dtype: "FloatNotNumpy16DtypeArg", target_dtype: type ) -> None: - if is_dtype_invalid_for_platform(dtype): - with pytest.raises(TypeError, match=rf"data type {dtype!r} not understood"): + exc = exception_on_platform(dtype) + if exc: + with pytest.raises(exc, match=rf"data type {dtype!r} not understood"): assert_type(pd.Index([1.0], dtype=dtype), "pd.Index[float]") else: check(pd.Index([1.0], dtype=dtype), pd.Index, target_dtype) @@ -110,8 +111,9 @@ def test_astype_float( ) -> None: s = pd.Index([1, 2, 3]) - if is_dtype_invalid_for_platform(cast_arg): - with pytest.raises(TypeError, match=rf"data type {cast_arg!r} not understood"): + exc = exception_on_platform(cast_arg) + if exc: + with pytest.raises(exc, match=rf"data type {cast_arg!r} not understood"): assert_type(s.astype(cast_arg), "pd.Index[float]") else: check(s.astype(cast_arg), pd.Index, target_type) diff --git a/tests/series/test_series_float.py b/tests/series/test_series_float.py index 3e734abb5..a1c0e0cc5 100644 --- a/tests/series/test_series_float.py +++ b/tests/series/test_series_float.py @@ -11,7 +11,7 @@ FloatDtypeArg, PandasAstypeFloatDtypeArg, check, - is_dtype_invalid_for_platform, + exception_on_platform, ) @@ -46,8 +46,9 @@ def test_constructor() -> None: @pytest.mark.parametrize(("dtype", "target_dtype"), TYPE_FLOAT_ARGS.items()) def test_constructor_dtype(dtype: FloatDtypeArg, target_dtype: type) -> None: - if is_dtype_invalid_for_platform(dtype): - with pytest.raises(TypeError, match=rf"data type {dtype!r} not understood"): + exc = exception_on_platform(dtype) + if exc: + with pytest.raises(exc, match=rf"data type {dtype!r} not understood"): assert_type(pd.Series([1.0], dtype=dtype), "pd.Series[float]") else: check(pd.Series([1.0], dtype=dtype), pd.Series, target_dtype) @@ -102,7 +103,7 @@ def test_astype_float( ) -> None: s = pd.Series([1, 2, 3]) - if is_dtype_invalid_for_platform(cast_arg): + if exception_on_platform(cast_arg): with pytest.raises(TypeError, match=rf"data type {cast_arg!r} not understood"): assert_type(s.astype(cast_arg), "pd.Series[float]") else: From ee8615a98272a1e0671b43e4ef3a9cf989fd367d Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Thu, 20 Nov 2025 22:36:46 +0100 Subject: [PATCH 6/8] attempt to drop arm for mac --- tests/__init__.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/__init__.py b/tests/__init__.py index bba6c307d..34530dfee 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -5,7 +5,6 @@ nullcontext, suppress, ) -import platform import sys from typing import ( TYPE_CHECKING, @@ -466,7 +465,6 @@ LINUX = sys.platform == "linux" WINDOWS = sys.platform in {"win32", "cygwin"} MAC = sys.platform == "darwin" -MAC_ARM = sys.platform == "darwin" and platform.processor() == "arm64" PD_LTE_23 = Version(pd.__version__) < Version("2.3.999") NUMPY20 = np.lib.NumpyVersion(np.__version__) >= "2.0.0" @@ -673,6 +671,6 @@ def pytest_warns_bounded( def exception_on_platform(dtype: type | str | ExtensionDtype) -> type[Exception] | None: - if (WINDOWS or MAC_ARM) and dtype in {"f16", "float128"}: + if (WINDOWS or MAC) and dtype in {"f16", "float128"}: return TypeError return None From d810f8d1ddf2a712302934d2228e1c8533429b0e Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Tue, 25 Nov 2025 09:57:11 +0100 Subject: [PATCH 7/8] bi-directional type matching https://github.com/pandas-dev/pandas-stubs/pull/1493#discussion_r2558068014 --- tests/arrays/test_floating_array.py | 8 ++------ tests/indexes/test_index_float.py | 9 ++------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/tests/arrays/test_floating_array.py b/tests/arrays/test_floating_array.py index 18ebe72a7..cb3f9c5a6 100644 --- a/tests/arrays/test_floating_array.py +++ b/tests/arrays/test_floating_array.py @@ -20,12 +20,8 @@ def test_constructor() -> None: check(assert_type(pd.array([1.0, None]), FloatingArray), FloatingArray) check(assert_type(pd.array([1.0, pd.NA, None]), FloatingArray), FloatingArray) - check( - assert_type( # type: ignore[assert-type] # I do not understand - pd.array(np.array([1.0], np.float64)), FloatingArray - ), - FloatingArray, - ) + nparr = np.array([1.0], np.float64) + check(assert_type(pd.array(nparr), FloatingArray), FloatingArray) check(assert_type(pd.array(pd.array([1.0])), FloatingArray), FloatingArray) diff --git a/tests/indexes/test_index_float.py b/tests/indexes/test_index_float.py index f2dc35b7a..ba31fa43d 100644 --- a/tests/indexes/test_index_float.py +++ b/tests/indexes/test_index_float.py @@ -24,13 +24,8 @@ def test_constructor() -> None: pd.Index, np.floating, ) - check( - assert_type( # type: ignore[assert-type] # I do not understand - pd.Index(np.array([1.0], np.float64)), "pd.Index[float]" - ), - pd.Index, - np.floating, - ) + nparr = np.array([1.0], np.float64) + check(assert_type(pd.Index(nparr), "pd.Index[float]"), pd.Index, np.floating) check( assert_type(pd.Index(pd.array([1.0])), "pd.Index[float]"), pd.Index, From e7db317c4c0a181e8eb97ec47e1bc13958556c8a Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Tue, 25 Nov 2025 11:50:50 +0100 Subject: [PATCH 8/8] pandas-dev/pandas-stubs#1501 --- tests/indexes/test_index_float.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/indexes/test_index_float.py b/tests/indexes/test_index_float.py index ba31fa43d..7c213b590 100644 --- a/tests/indexes/test_index_float.py +++ b/tests/indexes/test_index_float.py @@ -89,6 +89,7 @@ def test_constructor_dtype( assert_type(pd.Index([1.0], dtype="float64[pyarrow]"), "pd.Index[float]") assert_type(pd.Index([1.0], dtype="double[pyarrow]"), "pd.Index[float]") + # TODO: pandas-dev/pandas-stubs#1501 # if TYPE_CHECKING_INVALID_USAGE: # # numpy float16 # pd.Index([1.0], dtype=np.half)