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..9544ba350 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, @@ -69,6 +68,7 @@ from pandas.core.arrays.categorical import ( 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 +190,10 @@ from pandas._typing import ( NaPosition, NsmallestNlargestKeep, ObjectDtypeArg, + PandasAstypeComplexDtypeArg, + PandasAstypeFloatDtypeArg, + PandasAstypeTimedeltaDtypeArg, + PandasAstypeTimestampDtypeArg, PeriodFrequency, QuantileInterpolation, RandomState, @@ -343,41 +347,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 +385,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 +438,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 +452,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 +519,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 +535,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 +1451,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..34530dfee 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -27,16 +27,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 +77,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 +668,9 @@ def pytest_warns_bounded( if upper_exception is None: return nullcontext() return suppress(upper_exception) + + +def exception_on_platform(dtype: type | str | ExtensionDtype) -> type[Exception] | None: + if (WINDOWS or MAC) 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 new file mode 100644 index 000000000..18ebe72a7 --- /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, + exception_on_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: + 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) + + 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..f2dc35b7a --- /dev/null +++ b/tests/indexes/test_index_float.py @@ -0,0 +1,162 @@ +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, + exception_on_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: + 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) + + 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]) + + 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) + + 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..a1c0e0cc5 --- /dev/null +++ b/tests/series/test_series_float.py @@ -0,0 +1,151 @@ +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, + exception_on_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: + 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) + + 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]) + + 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: + 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]")