diff --git a/pyproject.toml b/pyproject.toml index 6f6892450b..4dad0480a6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -77,8 +77,8 @@ typing = [ # keep some of these pinned and bump periodically so there's fewer s "pytest", "pandas-stubs==2.3.0.250703", "typing_extensions", - "mypy~=1.15.0", - "pyrefly==0.64.0", + "mypy==2.1.0", + "pyrefly==1.0.0", "pyright", "pyarrow-stubs==19.2", "narwhals[dask]", @@ -322,6 +322,7 @@ exclude_also = [ 'if "(cudf|modin|pyspark|ibis)" in', "if not .*.is_pandas", "if is_ibis_table", + "if MYPY", "except ModuleNotFoundError", 'request.applymarker\(pytest.mark.xfail', 'backend_version <', @@ -386,6 +387,7 @@ ignore = [ "../../../**/Lib", # stdlib "../../../**/typeshed*" # typeshed-fallback ] +defineConstant = { "MYPY" = false } [tool.pyrefly] search-path = ["src", "."] diff --git a/src/narwhals/_arrow/dataframe.py b/src/narwhals/_arrow/dataframe.py index 44f44c1d95..3e5297d5a7 100644 --- a/src/narwhals/_arrow/dataframe.py +++ b/src/narwhals/_arrow/dataframe.py @@ -1,7 +1,7 @@ from __future__ import annotations from collections.abc import Collection, Iterator, Mapping, Sequence -from typing import TYPE_CHECKING, Any, Literal, cast, overload +from typing import TYPE_CHECKING, Any, Final, Literal, cast, overload import pyarrow as pa import pyarrow.compute as pc @@ -77,6 +77,8 @@ "full outer", ] +MYPY: Final = False + class ArrowDataFrame( EagerDataFrame["ArrowSeries", "ArrowExpr", "pa.Table", "ChunkedArrayAny"] @@ -320,6 +322,8 @@ def _select_multi_index( # **Doesn't accept `ndarray`** elif is_numpy_array_1d(columns): selector = columns.tolist() + elif MYPY: + selector = columns # type: ignore[assignment] else: selector = columns return self._with_native(self.native.select(selector)) diff --git a/src/narwhals/_compliant/dataframe.py b/src/narwhals/_compliant/dataframe.py index 09d78fcf06..912dc5afb5 100644 --- a/src/narwhals/_compliant/dataframe.py +++ b/src/narwhals/_compliant/dataframe.py @@ -2,7 +2,7 @@ from collections.abc import Iterator, Mapping, Sequence, Sized from itertools import chain -from typing import TYPE_CHECKING, Any, Literal, Protocol, TypeVar, overload +from typing import TYPE_CHECKING, Any, Final, Literal, Protocol, TypeVar, overload from narwhals._compliant.typing import ( CompliantDataFrameAny, @@ -80,6 +80,8 @@ Incomplete: TypeAlias = Any +MYPY: Final = False + __all__ = ["CompliantDataFrame", "CompliantFrame", "CompliantLazyFrame", "EagerDataFrame"] T = TypeVar("T") @@ -434,11 +436,18 @@ def __getitem__( # noqa: C901, PLR0912 else: compliant = compliant._select_multi_index(columns) elif isinstance(columns, slice): - compliant = compliant._select_slice_name(columns) + if MYPY: + # https://github.com/python/mypy/issues/21508 + compliant = compliant._select_slice_name(columns) # type: ignore[arg-type] + else: + compliant = compliant._select_slice_name(columns) elif is_compliant_series(columns): compliant = self._select_multi_name(columns.native) elif is_sequence_like(columns): compliant = self._select_multi_name(columns) + elif MYPY: + # https://github.com/python/mypy/issues/21508 + pass else: assert_never(columns) @@ -450,7 +459,11 @@ def __getitem__( # noqa: C901, PLR0912 elif is_compliant_series(rows): compliant = compliant._gather(rows.native) elif is_sized_multi_index_selector(rows): - compliant = compliant._gather(rows) + if MYPY: # noqa: SIM108 + # https://github.com/python/mypy/issues/21508 + compliant = compliant._gather(rows) # type: ignore[arg-type] + else: + compliant = compliant._gather(rows) else: assert_never(rows) diff --git a/src/narwhals/_compliant/group_by.py b/src/narwhals/_compliant/group_by.py index 7ebb9687b7..84d39c2a52 100644 --- a/src/narwhals/_compliant/group_by.py +++ b/src/narwhals/_compliant/group_by.py @@ -161,7 +161,8 @@ def _remap_expr_name( Arguments: name: Name of a `nw.Expr` aggregation method. """ - return cls._REMAP_AGGS.get(name, name) + result: NativeAggregationT_co = cls._REMAP_AGGS.get(name, name) + return result @classmethod def _leaf_name(cls, expr: DepthTrackingExprAny, /) -> NarwhalsAggregation | Any: diff --git a/src/narwhals/_compliant/series.py b/src/narwhals/_compliant/series.py index 077e6a86ad..d340f7e957 100644 --- a/src/narwhals/_compliant/series.py +++ b/src/narwhals/_compliant/series.py @@ -347,7 +347,8 @@ class EagerSeriesStructNamespace( # type: ignore[misc] ): ... -class EagerSeriesHist(Protocol[NativeSeriesT, _CountsT_co]): +# mypy says: `Covariant type variable "_CountsT_co" used in protocol where invariant one is expected` +class EagerSeriesHist(Protocol[NativeSeriesT, _CountsT_co]): # type: ignore[misc] _series: EagerSeries[NativeSeriesT] _breakpoint: bool _data: HistData[NativeSeriesT, _CountsT_co] diff --git a/src/narwhals/_dask/expr.py b/src/narwhals/_dask/expr.py index b6e5d4d844..3961b5ee7e 100644 --- a/src/narwhals/_dask/expr.py +++ b/src/narwhals/_dask/expr.py @@ -715,5 +715,5 @@ def dt(self) -> DaskExprDateTimeNamespace: last = not_implemented() # namespaces - list: not_implemented = not_implemented() # type: ignore[assignment] - struct: not_implemented = not_implemented() # type: ignore[assignment] + list: Any = not_implemented() + struct: Any = not_implemented() diff --git a/src/narwhals/_duration.py b/src/narwhals/_duration.py index 40e330a276..b9350cf41c 100644 --- a/src/narwhals/_duration.py +++ b/src/narwhals/_duration.py @@ -57,8 +57,7 @@ def to_timedelta( msg = f"Creating timedelta with {self.unit} unit is not supported." raise NotImplementedError(msg) kwd = UNIT_TO_TIMEDELTA[self.unit] - # error: Keywords must be strings (bad mypy) - return dt.timedelta(**{kwd: self.multiple}) # type: ignore[misc] + return dt.timedelta(**{kwd: self.multiple}) @classmethod def parse(cls, every: str) -> Interval: diff --git a/src/narwhals/_pandas_like/dataframe.py b/src/narwhals/_pandas_like/dataframe.py index 23b17796df..edc8a90034 100644 --- a/src/narwhals/_pandas_like/dataframe.py +++ b/src/narwhals/_pandas_like/dataframe.py @@ -1180,7 +1180,7 @@ def pivot( remapped = self._pivot_remap_column_names( columns, n_on=len(on), n_values=len(values), separator=separator ) - result.columns = remapped # type: ignore[assignment] + result.columns = remapped result.columns.names = [""] return self._with_native(result.reset_index()) diff --git a/src/narwhals/_polars/dataframe.py b/src/narwhals/_polars/dataframe.py index 3c746c188c..9b806e3635 100644 --- a/src/narwhals/_polars/dataframe.py +++ b/src/narwhals/_polars/dataframe.py @@ -1,7 +1,7 @@ from __future__ import annotations from collections.abc import Iterator, Mapping, Sequence, Sized -from typing import TYPE_CHECKING, Any, Generic, Literal, TypeVar, cast, overload +from typing import TYPE_CHECKING, Any, Final, Generic, Literal, TypeVar, cast, overload import polars as pl @@ -64,6 +64,8 @@ T = TypeVar("T") R = TypeVar("R") +MYPY: Final = False + Method: TypeAlias = "Callable[..., R]" """Generic alias representing all methods implemented via `__getattr__`. @@ -222,7 +224,7 @@ def join( return self._with_native( self.native.join( other=other.native, - how=how_native, # type: ignore[arg-type] + how=how_native, left_on=left_on, right_on=right_on, suffix=suffix, @@ -495,11 +497,11 @@ def __getitem__( # noqa: C901, PLR0912 else: native = native[:, columns] elif isinstance(columns, slice): - native = native.select( - self.columns[ - slice(*convert_str_slice_to_int_slice(columns, self.columns)) - ] - ) + if MYPY: + int_slice = convert_str_slice_to_int_slice(columns, self.columns) # type: ignore[arg-type] + else: + int_slice = convert_str_slice_to_int_slice(columns, self.columns) + native = native.select(self.columns[slice(*int_slice)]) elif is_compliant_series(columns): native = native.select(columns.native.to_list()) elif is_sequence_like(columns): diff --git a/src/narwhals/_polars/namespace.py b/src/narwhals/_polars/namespace.py index e7e0f2692c..9b596f8f4b 100644 --- a/src/narwhals/_polars/namespace.py +++ b/src/narwhals/_polars/namespace.py @@ -237,7 +237,7 @@ class PolarsSelectorNamespace: def __init__(self, context: _LimitedContext, /) -> None: self._version = context._version - def by_dtype(self, dtypes: Iterable[DType]) -> PolarsExpr: + def by_dtype(self, dtypes: Iterable[IntoDType]) -> PolarsExpr: native_dtypes = [ narwhals_to_native_dtype(dtype, self._version).__class__ if isinstance(dtype, type) and issubclass(dtype, DType) diff --git a/src/narwhals/_polars/utils.py b/src/narwhals/_polars/utils.py index 75a61b0d19..553de13114 100644 --- a/src/narwhals/_polars/utils.py +++ b/src/narwhals/_polars/utils.py @@ -2,7 +2,7 @@ import abc from functools import lru_cache -from typing import TYPE_CHECKING, Any, ClassVar, Final, Protocol, TypeVar, overload +from typing import TYPE_CHECKING, Any, ClassVar, Final, Protocol, TypeVar, cast, overload import polars as pl @@ -73,7 +73,7 @@ def extract_native(obj: _StoresNative[NativeT]) -> NativeT: ... @overload def extract_native(obj: T) -> T: ... def extract_native(obj: _StoresNative[NativeT] | T) -> NativeT | T: - return obj.native if _is_compliant_polars(obj) else obj + return obj.native if _is_compliant_polars(obj) else cast("T", obj) def _is_compliant_polars( diff --git a/test-plugin/test_plugin/dataframe.py b/test-plugin/test_plugin/dataframe.py index d7f1f237c7..56bc928cf7 100644 --- a/test-plugin/test_plugin/dataframe.py +++ b/test-plugin/test_plugin/dataframe.py @@ -47,10 +47,10 @@ def _with_version(self, version: Version) -> Self: __native_namespace__ = not_implemented() # Properties - schema = not_implemented() # type: ignore[assignment] + schema: Any = not_implemented() # Static - _is_native = not_implemented() # type: ignore[assignment] + _is_native = not_implemented() # Helpers _iter_columns = not_implemented() diff --git a/tests/expr_and_series/over_test.py b/tests/expr_and_series/over_test.py index 35efb9a1c9..72ace4c99d 100644 --- a/tests/expr_and_series/over_test.py +++ b/tests/expr_and_series/over_test.py @@ -245,7 +245,7 @@ def test_over_anonymous_cumulative( context = ( pytest.raises(NotImplementedError) if df.implementation.is_pyarrow() - else pytest.raises(KeyError) # type: ignore[arg-type] + else pytest.raises(KeyError) if df.implementation.is_modin() or (df.implementation.is_pandas() and PANDAS_VERSION < (1, 3)) # TODO(unassigned): bug in old pandas + modin. diff --git a/tests/v1_test.py b/tests/v1_test.py index bfdfd51efa..21c0ce1826 100644 --- a/tests/v1_test.py +++ b/tests/v1_test.py @@ -591,7 +591,8 @@ def __dataframe__(self) -> None: # pragma: no cover mockdf = MockDf() result = nw_v1.from_native(mockdf, eager_only=True, strict=False) - assert result is mockdf + # mypy issue? + assert result is mockdf # type: ignore[comparison-overlap] def test_from_native_lazyframe() -> None: