diff --git a/.github/workflows/downstream_tests.yml b/.github/workflows/downstream_tests.yml index 229b474d35..d5fa3123c0 100644 --- a/.github/workflows/downstream_tests.yml +++ b/.github/workflows/downstream_tests.yml @@ -474,7 +474,7 @@ jobs: validoopsie: strategy: matrix: - python-version: ["3.9", "3.12"] # 3.9 and 3.12 are enough to cover all the tests + python-version: ["3.10", "3.13"] # these are enough to cover all the tests os: ["ubuntu-latest"] runs-on: ${{ matrix.os }} steps: diff --git a/.github/workflows/extremes.yml b/.github/workflows/extremes.yml index c3b20f64e4..1f03355ced 100644 --- a/.github/workflows/extremes.yml +++ b/.github/workflows/extremes.yml @@ -11,7 +11,7 @@ jobs: minimum_versions: strategy: matrix: - python-version: ["3.9"] + python-version: ["3.10"] os: [ubuntu-latest] runs-on: ${{ matrix.os }} @@ -27,7 +27,18 @@ jobs: cache-suffix: min-versions-${{ matrix.python-version }} cache-dependency-glob: "pyproject.toml" - name: install-minimum-versions - run: uv pip install pipdeptree tox virtualenv setuptools pandas==1.1.3 polars==0.20.4 numpy==1.19.3 pyarrow==13.0.0 "pyarrow-stubs<17" scipy==1.6.0 scikit-learn==1.1.0 duckdb==1.1 tzdata + run: | + uv pip install \ + pipdeptree tox virtualenv setuptools tzdata \ + pandas==1.3.4 \ + polars==0.20.4 \ + numpy==1.21.4 \ + pyarrow==13.0.0 \ + "pyarrow-stubs<17" \ + scipy==1.7.3 \ + scikit-learn==1.1.0 \ + duckdb==1.1 \ + --system - name: install-reqs run: | uv pip install -e . --group tests @@ -36,11 +47,11 @@ jobs: - name: Assert dependencies run: | DEPS=$(uv pip freeze) - echo "$DEPS" | grep 'pandas==1.1.3' + echo "$DEPS" | grep 'pandas==1.3.4' echo "$DEPS" | grep 'polars==0.20.4' - echo "$DEPS" | grep 'numpy==1.19.3' + echo "$DEPS" | grep 'numpy==1.21.4' echo "$DEPS" | grep 'pyarrow==13.0.0' - echo "$DEPS" | grep 'scipy==1.6.0' + echo "$DEPS" | grep 'scipy==1.7.3' echo "$DEPS" | grep 'scikit-learn==1.1.0' echo "$DEPS" | grep 'duckdb==1.1' - name: Run pytest @@ -49,7 +60,7 @@ jobs: pretty_old_versions: strategy: matrix: - python-version: ["3.9"] + python-version: ["3.10"] os: [ubuntu-latest] runs-on: ${{ matrix.os }} steps: @@ -64,7 +75,18 @@ jobs: cache-suffix: pretty-old-versions-${{ matrix.python-version }} cache-dependency-glob: "pyproject.toml" - name: install-pretty-old-versions - run: uv pip install pipdeptree tox virtualenv setuptools pandas==1.1.5 polars==0.20.4 numpy==1.19.3 pyarrow==14.0.0 "pyarrow-stubs<17" scipy==1.6.0 scikit-learn==1.1.0 duckdb==1.2 tzdata + run: | + uv pip install \ + pipdeptree tox virtualenv setuptools tzdata \ + pandas==1.4.1 \ + polars==0.20.4 \ + numpy==1.22.0 \ + pyarrow==14.0.0 \ + "pyarrow-stubs<17" \ + scipy==1.8.0 \ + scikit-learn==1.1.0 \ + duckdb==1.2 \ + --system - name: install-reqs run: uv pip install -e . --group tests - name: show-deps @@ -74,11 +96,11 @@ jobs: - name: Assert pretty old versions dependencies run : | DEPS=$(uv pip freeze) - echo "$DEPS" | grep 'pandas==1.1.5' + echo "$DEPS" | grep 'pandas==1.4.1' echo "$DEPS" | grep 'polars==0.20.4' - echo "$DEPS" | grep 'numpy==1.19.3' + echo "$DEPS" | grep 'numpy==1.22.0' echo "$DEPS" | grep 'pyarrow==14.0.0' - echo "$DEPS" | grep 'scipy==1.6.0' + echo "$DEPS" | grep 'scipy==1.8.0' echo "$DEPS" | grep 'scikit-learn==1.1.0' echo "$DEPS" | grep 'duckdb==1.2' - name: Run pytest diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 9f6205d1cc..402981997d 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -8,10 +8,10 @@ env: PYTEST_ADDOPTS: "--numprocesses=logical" UV_SYSTEM_PYTHON: 1 jobs: - pytest-39: + pytest-310: strategy: matrix: - python-version: ["3.9"] + python-version: ["3.10"] os: [windows-latest, ubuntu-latest] runs-on: ${{ matrix.os }} steps: @@ -23,7 +23,7 @@ jobs: uses: astral-sh/setup-uv@v7 with: enable-cache: "true" - cache-suffix: pytest-39-${{ matrix.python-version }} + cache-suffix: pytest-310-${{ matrix.python-version }} cache-dependency-glob: "pyproject.toml" - name: install-reqs run: uv pip install -e ".[pandas,polars,pyarrow]" --group tests @@ -37,7 +37,7 @@ jobs: pytest-windows: strategy: matrix: - python-version: ["3.10", "3.12"] + python-version: ["3.10", "3.13"] os: [windows-latest] runs-on: ${{ matrix.os }} steps: diff --git a/.github/workflows/random_ci_pytest.yml b/.github/workflows/random_ci_pytest.yml index 2950989871..009b947b34 100644 --- a/.github/workflows/random_ci_pytest.yml +++ b/.github/workflows/random_ci_pytest.yml @@ -11,7 +11,7 @@ jobs: tox: strategy: matrix: - python-version: ["3.9"] + python-version: ["3.10"] os: [ubuntu-latest] runs-on: ${{ matrix.os }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b0df17f26d..5b46aaf3a2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -52,7 +52,7 @@ repos: # TODO(unassigned): replace with ruff once https://github.com/astral-sh/ruff/issues/2302 is addressed. name: flake8-typing-imports alias: flake8-typing-imports - entry: flake8 --select TYP --min-python-version=3.9.0 + entry: flake8 --select TYP --min-python-version=3.10.0 # Keep in sync with `darglint` so the same venv is reused. additional_dependencies: [darglint==1.8.1, flake8-typing-imports==1.17.0] - repo: local diff --git a/narwhals/_arrow/dataframe.py b/narwhals/_arrow/dataframe.py index 0d65641541..44f44c1d95 100644 --- a/narwhals/_arrow/dataframe.py +++ b/narwhals/_arrow/dataframe.py @@ -25,7 +25,6 @@ parse_columns_to_drop, scale_bytes, supports_arrow_c_stream, - zip_strict, ) from narwhals.dependencies import is_numpy_array_1d from narwhals.exceptions import ShapeError @@ -35,10 +34,11 @@ from io import BytesIO from pathlib import Path from types import ModuleType + from typing import TypeAlias import pandas as pd import polars as pl - from typing_extensions import Self, TypeAlias, TypeIs + from typing_extensions import Self, TypeIs from narwhals._arrow.expr import ArrowExpr from narwhals._arrow.group_by import ArrowGroupBy @@ -253,7 +253,7 @@ def rows(self, *, named: bool) -> list[tuple[Any, ...]] | list[dict[str, Any]]: return self.native.to_pylist() def iter_columns(self) -> Iterator[ArrowSeries]: - for name, series in zip_strict(self.columns, self.native.itercolumns()): + for name, series in zip(self.columns, self.native.itercolumns(), strict=True): yield ArrowSeries.from_native(series, context=self, name=name) _iter_columns = iter_columns @@ -267,7 +267,7 @@ def iter_rows( if not named: for i in range(0, num_rows, buffer_size): rows = df[i : i + buffer_size].to_pydict().values() - yield from zip_strict(*rows) + yield from zip(*rows, strict=True) else: for i in range(0, num_rows, buffer_size): yield from df[i : i + buffer_size].to_pylist() @@ -479,7 +479,7 @@ def sort(self, *by: str, descending: bool | Sequence[bool], nulls_last: bool) -> else: sorting = [ (key, "descending" if is_descending else "ascending") - for key, is_descending in zip_strict(by, descending) + for key, is_descending in zip(by, descending, strict=True) ] null_placement = "at_end" if nulls_last else "at_start" @@ -496,7 +496,7 @@ def top_k(self, k: int, *, by: Iterable[str], reverse: bool | Sequence[bool]) -> else: sorting = [ (key, "ascending" if is_ascending else "descending") - for key, is_ascending in zip_strict(by, reverse) + for key, is_ascending in zip(by, reverse, strict=True) ] return self._with_native( self.native.take(pc.select_k_unstable(self.native, k, sorting)), # type: ignore[call-overload] diff --git a/narwhals/_arrow/expr.py b/narwhals/_arrow/expr.py index f93fc285c1..8347e5d55c 100644 --- a/narwhals/_arrow/expr.py +++ b/narwhals/_arrow/expr.py @@ -196,7 +196,7 @@ def func(df: ArrowDataFrame) -> Sequence[ArrowSeries]: # noqa: PLR0914 group_keys: list[str] = [] encoded_cols: list[ArrowExpr] = [] - for col_name, has_null in zip(partition_tbl.columns, has_nulls): + for col_name, has_null in zip(partition_tbl.columns, has_nulls, strict=False): if not has_null: group_keys.append(col_name) else: diff --git a/narwhals/_arrow/group_by.py b/narwhals/_arrow/group_by.py index 8c4e65346d..d5a04f1994 100644 --- a/narwhals/_arrow/group_by.py +++ b/narwhals/_arrow/group_by.py @@ -186,7 +186,7 @@ def agg(self, *exprs: ArrowExpr) -> ArrowDataFrame: new_column_names = [new_column_names[i] for i in index_map] result_simple = result_simple.rename_columns(new_column_names) return self.compliant._with_native(result_simple).rename( - dict(zip(self._keys, self._output_key_names)) + dict(zip(self._keys, self._output_key_names, strict=False)) ) def __iter__(self) -> Iterator[tuple[Any, ArrowDataFrame]]: diff --git a/narwhals/_arrow/series.py b/narwhals/_arrow/series.py index 650217bd3d..7495dc05d3 100644 --- a/narwhals/_arrow/series.py +++ b/narwhals/_arrow/series.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Callable, Literal, cast, overload +from typing import TYPE_CHECKING, Any, Literal, cast, overload import pyarrow as pa import pyarrow.compute as pc @@ -37,12 +37,13 @@ from narwhals.exceptions import InvalidOperationError, ShapeError if TYPE_CHECKING: - from collections.abc import Iterable, Iterator, Sequence + from collections.abc import Callable, Iterable, Iterator, Sequence from types import ModuleType + from typing import TypeAlias import pandas as pd import polars as pl - from typing_extensions import Self, TypeAlias, TypeIs + from typing_extensions import Self, TypeIs from narwhals._arrow.dataframe import ArrowDataFrame from narwhals._arrow.namespace import ArrowNamespace @@ -203,7 +204,7 @@ def from_numpy(cls, data: Into1DArray, /, *, context: _LimitedContext) -> Self: def _align_full_broadcast(cls, *series: Self) -> Sequence[Self]: lengths = [len(s) for s in series] target_length = max( - length for length, s in zip(lengths, series) if not s._broadcast + length for length, s in zip(lengths, series, strict=False) if not s._broadcast ) fast_path = all(_len == target_length for _len in lengths) if fast_path: diff --git a/narwhals/_arrow/series_dt.py b/narwhals/_arrow/series_dt.py index 6f29767f14..3489698928 100644 --- a/narwhals/_arrow/series_dt.py +++ b/narwhals/_arrow/series_dt.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Callable, ClassVar, cast +from typing import TYPE_CHECKING, Any, ClassVar, cast import pyarrow as pa import pyarrow.compute as pc @@ -22,9 +22,8 @@ from narwhals._duration import Interval if TYPE_CHECKING: - from collections.abc import Mapping - - from typing_extensions import TypeAlias + from collections.abc import Callable, Mapping + from typing import TypeAlias from narwhals._arrow.series import ArrowSeries from narwhals._arrow.typing import ChunkedArrayAny, ScalarAny diff --git a/narwhals/_arrow/typing.py b/narwhals/_arrow/typing.py index 8180663f1f..bd15b16d66 100644 --- a/narwhals/_arrow/typing.py +++ b/narwhals/_arrow/typing.py @@ -7,13 +7,7 @@ ) if TYPE_CHECKING: - import sys - from typing import Generic, Literal - - if sys.version_info >= (3, 10): - from typing import TypeAlias - else: - from typing_extensions import TypeAlias + from typing import Generic, Literal, TypeAlias import pyarrow as pa from pyarrow.__lib_pxi.table import ( diff --git a/narwhals/_arrow/utils.py b/narwhals/_arrow/utils.py index 5a678b5bf4..9d6ed73ffd 100644 --- a/narwhals/_arrow/utils.py +++ b/narwhals/_arrow/utils.py @@ -11,9 +11,9 @@ if TYPE_CHECKING: from collections.abc import Iterable, Iterator, Mapping - from typing import Literal + from typing import Literal, TypeAlias - from typing_extensions import TypeAlias, TypeIs + from typing_extensions import TypeIs from narwhals._arrow.series import ArrowSeries from narwhals._arrow.typing import ( diff --git a/narwhals/_compliant/any_namespace.py b/narwhals/_compliant/any_namespace.py index 27354eb7ff..bcffd193e5 100644 --- a/narwhals/_compliant/any_namespace.py +++ b/narwhals/_compliant/any_namespace.py @@ -7,7 +7,7 @@ from narwhals._utils import CompliantT_co, _StoresCompliant if TYPE_CHECKING: - from typing import Callable + from collections.abc import Callable from narwhals._compliant.typing import Accessor from narwhals.typing import NonNestedLiteral, TimeUnit diff --git a/narwhals/_compliant/dataframe.py b/narwhals/_compliant/dataframe.py index 3e7810616c..09d78fcf06 100644 --- a/narwhals/_compliant/dataframe.py +++ b/narwhals/_compliant/dataframe.py @@ -45,11 +45,12 @@ from io import BytesIO from pathlib import Path from types import ModuleType + from typing import TypeAlias import pandas as pd import polars as pl import pyarrow as pa - from typing_extensions import Self, TypeAlias + from typing_extensions import Self from narwhals._compliant.group_by import CompliantGroupBy, DataFrameGroupBy from narwhals._compliant.namespace import EagerNamespace @@ -419,7 +420,11 @@ def __getitem__( # noqa: C901, PLR0912 return compliant.select() if is_boolean_selector(columns): compliant = compliant.simple_select( - *(col for col, select in zip(compliant.columns, columns) if select) + *( + col + for col, select in zip(compliant.columns, columns, strict=False) + if select + ) ) elif is_index_selector(columns): if is_slice_index(columns) or is_range(columns): diff --git a/narwhals/_compliant/expr.py b/narwhals/_compliant/expr.py index 1a138f35f3..3f8cee7658 100644 --- a/narwhals/_compliant/expr.py +++ b/narwhals/_compliant/expr.py @@ -2,7 +2,7 @@ from functools import partial from operator import methodcaller -from typing import TYPE_CHECKING, Any, Callable, Generic, Literal, Protocol +from typing import TYPE_CHECKING, Any, Generic, Literal, Protocol from narwhals._compliant.any_namespace import ( CatNamespace, @@ -27,17 +27,12 @@ LazyExprT, NativeExprT, ) -from narwhals._utils import ( - _StoresCompliant, - not_implemented, - qualified_type_name, - zip_strict, -) +from narwhals._utils import _StoresCompliant, not_implemented, qualified_type_name from narwhals.dependencies import is_numpy_array, is_numpy_scalar from narwhals.exceptions import MultiOutputExpressionError if TYPE_CHECKING: - from collections.abc import Sequence + from collections.abc import Callable, Sequence from typing_extensions import Self, TypeIs @@ -294,13 +289,17 @@ def func(df: EagerDataFrameT) -> list[EagerSeriesT]: if alias_output_names: return [ series.alias(name) - for series, name in zip_strict( - self(df), alias_output_names(self._evaluate_output_names(df)) + for series, name in zip( + self(df), + alias_output_names(self._evaluate_output_names(df)), + strict=True, ) ] return [ series.alias(name) - for series, name in zip_strict(self(df), self._evaluate_output_names(df)) + for series, name in zip( + self(df), self._evaluate_output_names(df), strict=True + ) ] return self.__class__( @@ -374,7 +373,7 @@ def _reuse_series_inner( ] aliases, names = self._evaluate_aliases(df), (s.name for s in out) if any( - alias != name for alias, name in zip_strict(aliases, names) + alias != name for alias, name in zip(aliases, names, strict=True) ): # pragma: no cover msg = ( f"Safety assertion failed, please report a bug to https://github.com/narwhals-dev/narwhals/issues\n" @@ -750,7 +749,7 @@ def func(df: EagerDataFrameT) -> Sequence[EagerSeriesT]: _first_in, _first_out = udf_series_in[0], udf_series_out[0] result: Sequence[EagerSeriesT] - it = zip_strict(udf_series_out, output_names) + it = zip(udf_series_out, output_names, strict=True) if is_numpy_array(_first_out) or is_numpy_scalar(_first_out): from_numpy = partial(_first_in.from_numpy, context=self) result = tuple(from_numpy(arr).alias(out_name) for arr, out_name in it) diff --git a/narwhals/_compliant/group_by.py b/narwhals/_compliant/group_by.py index cb62edf7ac..7ebb9687b7 100644 --- a/narwhals/_compliant/group_by.py +++ b/narwhals/_compliant/group_by.py @@ -1,7 +1,7 @@ from __future__ import annotations from itertools import chain -from typing import TYPE_CHECKING, Any, Callable, ClassVar, Protocol, TypeVar +from typing import TYPE_CHECKING, Any, ClassVar, Protocol, TypeVar from narwhals._compliant.typing import ( CompliantDataFrameT, @@ -15,10 +15,10 @@ ImplExprT_contra, NarwhalsAggregation, ) -from narwhals._utils import is_sequence_of, zip_strict +from narwhals._utils import is_sequence_of if TYPE_CHECKING: - from collections.abc import Iterable, Iterator, Mapping, Sequence + from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence from narwhals._compliant.expr import ImplExpr @@ -109,7 +109,7 @@ def _temporary_name(key: str) -> str: if (metadata := key._metadata) and metadata.expansion_kind.is_multi_output() # otherwise it's single named and we can use Expr.alias else key.alias(_temporary_name(new_names[0])) - for key, new_names in zip_strict(keys, keys_aliases) + for key, new_names in zip(keys, keys_aliases, strict=True) ] return ( compliant_frame.with_columns(*safe_keys), diff --git a/narwhals/_compliant/namespace.py b/narwhals/_compliant/namespace.py index 08570ad675..c0b3bcb9c0 100644 --- a/narwhals/_compliant/namespace.py +++ b/narwhals/_compliant/namespace.py @@ -25,8 +25,9 @@ if TYPE_CHECKING: from collections.abc import Collection, Iterable, Iterator, KeysView, Sequence + from typing import TypeAlias - from typing_extensions import TypeAlias, TypeIs + from typing_extensions import TypeIs from narwhals._compliant.selectors import CompliantSelectorNamespace from narwhals._utils import Implementation, Version @@ -153,7 +154,10 @@ def align(df: CompliantFrameT, columns: KeysView[str]) -> CompliantFrameT: # Even if all fields are present, we always reorder the columns to match between frames. return df.simple_select(*union_names) - return [align(frame, schema.keys()) for frame, schema in zip(frames, schemas)] + return [ + align(frame, schema.keys()) + for frame, schema in zip(frames, schemas, strict=False) + ] class DepthTrackingNamespace( diff --git a/narwhals/_compliant/selectors.py b/narwhals/_compliant/selectors.py index bb4f612d10..71bb3d6988 100644 --- a/narwhals/_compliant/selectors.py +++ b/narwhals/_compliant/selectors.py @@ -12,14 +12,14 @@ dtype_matches_time_unit_and_time_zone, get_column_names, is_compliant_dataframe, - zip_strict, ) if TYPE_CHECKING: from collections.abc import Collection, Iterable, Iterator, Sequence from datetime import timezone + from typing import TypeAlias - from typing_extensions import Self, TypeAlias, TypeIs + from typing_extensions import Self, TypeIs from narwhals._compliant.expr import NativeExpr from narwhals._compliant.typing import ( @@ -75,7 +75,7 @@ def _iter_columns_dtypes( self, df: FrameT, / ) -> Iterator[tuple[SeriesOrExprT, DType]]: ... def _iter_columns_names(self, df: FrameT, /) -> Iterator[tuple[SeriesOrExprT, str]]: - yield from zip_strict(self._iter_columns(df), df.columns) + yield from zip(self._iter_columns(df), df.columns, strict=True) def _is_dtype( self: CompliantSelectorNamespace[FrameT, SeriesOrExprT], dtype: type[DType], / @@ -191,7 +191,7 @@ def _iter_columns(self, df: LazyFrameT) -> Iterator[ExprT]: yield from df._iter_columns() def _iter_columns_dtypes(self, df: LazyFrameT, /) -> Iterator[tuple[ExprT, DType]]: - yield from zip_strict(self._iter_columns(df), df.schema.values()) + yield from zip(self._iter_columns(df), df.schema.values(), strict=True) class CompliantSelector( @@ -244,7 +244,7 @@ def series(df: FrameT) -> Sequence[SeriesOrExprT]: lhs_names, rhs_names = _eval_lhs_rhs(df, self, other) return [ x - for x, name in zip_strict(self(df), lhs_names) + for x, name in zip(self(df), lhs_names, strict=True) if name not in rhs_names ] @@ -271,7 +271,7 @@ def series(df: FrameT) -> Sequence[SeriesOrExprT]: return [ *( x - for x, name in zip_strict(self(df), lhs_names) + for x, name in zip(self(df), lhs_names, strict=True) if name not in rhs_names ), *other(df), @@ -298,7 +298,9 @@ def __and__( def series(df: FrameT) -> Sequence[SeriesOrExprT]: lhs_names, rhs_names = _eval_lhs_rhs(df, self, other) return [ - x for x, name in zip_strict(self(df), lhs_names) if name in rhs_names + x + for x, name in zip(self(df), lhs_names, strict=True) + if name in rhs_names ] def names(df: FrameT) -> Sequence[str]: diff --git a/narwhals/_compliant/typing.py b/narwhals/_compliant/typing.py index 52d583bb53..7b48ec12ed 100644 --- a/narwhals/_compliant/typing.py +++ b/narwhals/_compliant/typing.py @@ -1,10 +1,10 @@ from __future__ import annotations -from collections.abc import Sequence -from typing import TYPE_CHECKING, Any, Callable, Literal, TypedDict, TypeVar +from collections.abc import Callable, Sequence +from typing import TYPE_CHECKING, Any, Literal, TypedDict, TypeVar if TYPE_CHECKING: - from typing_extensions import TypeAlias + from typing import TypeAlias from narwhals._compliant.dataframe import ( CompliantDataFrame, diff --git a/narwhals/_dask/dataframe.py b/narwhals/_dask/dataframe.py index 15bc3fab7e..809e94372a 100644 --- a/narwhals/_dask/dataframe.py +++ b/narwhals/_dask/dataframe.py @@ -16,7 +16,6 @@ generate_temporary_column_name, not_implemented, parse_columns_to_drop, - zip_strict, ) from narwhals.exceptions import MultiOutputExpressionError from narwhals.typing import CompliantLazyFrame @@ -26,9 +25,10 @@ from io import BytesIO from pathlib import Path from types import ModuleType + from typing import TypeAlias import dask.dataframe.dask_expr as dx - from typing_extensions import Self, TypeAlias, TypeIs + from typing_extensions import Self, TypeIs from narwhals._compliant.typing import CompliantDataFrameAny from narwhals._dask.expr import DaskExpr @@ -319,7 +319,7 @@ def _join_left( ) extra = [ right_key if right_key not in self.columns else f"{right_key}{suffix}" - for left_key, right_key in zip_strict(left_on, right_on) + for left_key, right_key in zip(left_on, right_on, strict=True) if right_key != left_key ] return result_native.drop(columns=extra) @@ -379,7 +379,7 @@ def _join_semi( other_native = self._join_filter_rename( other=other, columns_to_select=list(right_on), - columns_mapping=dict(zip(right_on, left_on)), + columns_mapping=dict(zip(right_on, left_on, strict=False)), ) return self.native.dropna(subset=left_on, how="any").merge( other_native, how="inner", left_on=left_on, right_on=left_on @@ -394,7 +394,7 @@ def _join_anti( other_native = self._join_filter_rename( other=other, columns_to_select=list(right_on), - columns_mapping=dict(zip(right_on, left_on)), + columns_mapping=dict(zip(right_on, left_on, strict=False)), ) df = self.native.merge( other_native.dropna(subset=left_on, how="any"), diff --git a/narwhals/_dask/expr.py b/narwhals/_dask/expr.py index 75a3cbb964..354d3b5621 100644 --- a/narwhals/_dask/expr.py +++ b/narwhals/_dask/expr.py @@ -1,7 +1,7 @@ from __future__ import annotations import warnings -from typing import TYPE_CHECKING, Any, Callable, cast +from typing import TYPE_CHECKING, Any, cast import pandas as pd @@ -26,7 +26,7 @@ from narwhals.exceptions import InvalidOperationError if TYPE_CHECKING: - from collections.abc import Sequence + from collections.abc import Callable, Sequence import dask.dataframe.dask_expr as dx from typing_extensions import Self @@ -672,7 +672,7 @@ def replace_strict( msg = "`replace_strict` requires an explicit value for `default` for dask backend." raise ValueError(msg) - mapping = dict(zip(old, new)) + mapping = dict(zip(old, new, strict=False)) old_ = list(old) def func(df: DaskLazyFrame) -> list[dx.Series]: diff --git a/narwhals/_dask/group_by.py b/narwhals/_dask/group_by.py index 144cc0c3fa..4fd044f774 100644 --- a/narwhals/_dask/group_by.py +++ b/narwhals/_dask/group_by.py @@ -1,22 +1,21 @@ from __future__ import annotations from functools import partial -from typing import TYPE_CHECKING, Any, Callable, ClassVar +from typing import TYPE_CHECKING, Any, ClassVar import dask.dataframe as dd from narwhals._compliant import DepthTrackingGroupBy from narwhals._dask.utils import make_group_by_kwargs from narwhals._expression_parsing import evaluate_output_names_and_aliases -from narwhals._utils import zip_strict if TYPE_CHECKING: - from collections.abc import Mapping, Sequence + from collections.abc import Callable, Mapping, Sequence + from typing import TypeAlias import pandas as pd from dask.dataframe.api import GroupBy as _DaskGroupBy from pandas.core.groupby import SeriesGroupBy as _PandasSeriesGroupBy - from typing_extensions import TypeAlias from narwhals._compliant.typing import NarwhalsAggregation from narwhals._dask.dataframe import DaskLazyFrame @@ -113,7 +112,7 @@ def agg(self, *exprs: DaskExpr) -> DaskLazyFrame: return ( self.compliant.simple_select(*self._keys) .unique(self._keys, keep="any", order_by=None) - .rename(dict(zip(self._keys, self._output_key_names))) + .rename(dict(zip(self._keys, self._output_key_names, strict=False))) ) self._ensure_all_simple(exprs) @@ -140,9 +139,9 @@ def agg(self, *exprs: DaskExpr) -> DaskLazyFrame: agg_fn = agg_fn(**last_node.kwargs) if callable(agg_fn) else agg_fn simple_aggregations.update( (alias, (output_name, agg_fn)) - for alias, output_name in zip_strict(aliases, output_names) + for alias, output_name in zip(aliases, output_names, strict=True) ) return DaskLazyFrame( self._grouped.agg(**simple_aggregations).reset_index(), version=self.compliant._version, - ).rename(dict(zip(self._keys, self._output_key_names))) + ).rename(dict(zip(self._keys, self._output_key_names, strict=False))) diff --git a/narwhals/_dask/namespace.py b/narwhals/_dask/namespace.py index 818eb10581..c08b00150f 100644 --- a/narwhals/_dask/namespace.py +++ b/narwhals/_dask/namespace.py @@ -22,7 +22,7 @@ combine_alias_output_names, combine_evaluate_output_names, ) -from narwhals._utils import Implementation, is_nested_literal, not_implemented, zip_strict +from narwhals._utils import Implementation, is_nested_literal, not_implemented if TYPE_CHECKING: from collections.abc import Iterable, Iterator @@ -240,7 +240,7 @@ def func(df: DaskLazyFrame) -> list[dx.Series]: ) else: init_value, *values = [ - s.where(~nm, "") for s, nm in zip_strict(series, null_mask) + s.where(~nm, "") for s, nm in zip(series, null_mask, strict=True) ] separators = ( @@ -249,7 +249,7 @@ def func(df: DaskLazyFrame) -> list[dx.Series]: ) result = reduce( operator.add, - (s + v for s, v in zip_strict(separators, values)), + (s + v for s, v in zip(separators, values, strict=True)), init_value, ) diff --git a/narwhals/_dask/utils.py b/narwhals/_dask/utils.py index 6f99fcd6ca..b2e7040cc0 100644 --- a/narwhals/_dask/utils.py +++ b/narwhals/_dask/utils.py @@ -34,7 +34,7 @@ def evaluate_exprs(df: DaskLazyFrame, /, *exprs: DaskExpr) -> list[tuple[str, dx if len(aliases) != len(native_series_list): # pragma: no cover msg = f"Internal error: got aliases {aliases}, but only got {len(native_series_list)} results" raise AssertionError(msg) - native_results.extend(zip(aliases, native_series_list)) + native_results.extend(zip(aliases, native_series_list, strict=False)) return native_results diff --git a/narwhals/_duckdb/dataframe.py b/narwhals/_duckdb/dataframe.py index 23ef4daa65..e410e4ace9 100644 --- a/narwhals/_duckdb/dataframe.py +++ b/narwhals/_duckdb/dataframe.py @@ -27,7 +27,6 @@ generate_temporary_column_name, parse_columns_to_drop, requires, - zip_strict, ) from narwhals.dependencies import get_duckdb from narwhals.exceptions import InvalidOperationError @@ -236,15 +235,17 @@ def schema(self) -> dict[str, DType]: if self._cached_native_schema is None: # Note: prefer `self._cached_native_schema` over `functools.cached_property` # due to Python3.13 failures. - self._cached_native_schema = dict(zip(self.columns, self.native.types)) + self._cached_native_schema = dict( + zip(self.columns, self.native.types, strict=False) + ) deferred_time_zone = DeferredTimeZone(self.native) return { column_name: native_to_narwhals_dtype( duckdb_dtype, self._version, deferred_time_zone ) - for column_name, duckdb_dtype in zip_strict( - self.native.columns, self.native.types + for column_name, duckdb_dtype in zip( + self.native.columns, self.native.types, strict=True ) } @@ -309,7 +310,7 @@ def join( assert right_on is not None # noqa: S101 it = ( col(f'lhs."{left}"') == col(f'rhs."{right}"') - for left, right in zip_strict(left_on, right_on) + for left, right in zip(left_on, right_on, strict=True) ) condition: Expression = reduce(and_, it) rel = self.native.set_alias("lhs").join( @@ -354,7 +355,7 @@ def join_asof( if by_left is not None and by_right is not None: conditions.extend( col(f'lhs."{left}"') == col(f'rhs."{right}"') - for left, right in zip_strict(by_left, by_right) + for left, right in zip(by_left, by_right, strict=True) ) else: by_left = by_right = [] @@ -426,12 +427,12 @@ def sort(self, *by: str, descending: bool | Sequence[bool], nulls_last: bool) -> if nulls_last: it = ( col(name).nulls_last() if not desc else col(name).desc().nulls_last() - for name, desc in zip_strict(by, descending) + for name, desc in zip(by, descending, strict=True) ) else: it = ( col(name).nulls_first() if not desc else col(name).desc().nulls_first() - for name, desc in zip_strict(by, descending) + for name, desc in zip(by, descending, strict=True) ) return self._with_native(self.native.sort(*it)) diff --git a/narwhals/_duckdb/expr.py b/narwhals/_duckdb/expr.py index 7a0977a54c..3a5a759f3a 100644 --- a/narwhals/_duckdb/expr.py +++ b/narwhals/_duckdb/expr.py @@ -1,7 +1,7 @@ from __future__ import annotations import operator -from typing import TYPE_CHECKING, Any, Callable, cast +from typing import TYPE_CHECKING, Any, cast from duckdb import CoalesceOperator, StarExpression @@ -24,7 +24,7 @@ from narwhals._utils import Implementation, Version, extend_bool, no_default if TYPE_CHECKING: - from collections.abc import Sequence + from collections.abc import Callable, Sequence from duckdb import Expression from typing_extensions import Self diff --git a/narwhals/_duckdb/group_by.py b/narwhals/_duckdb/group_by.py index 09960bb172..7e7ac5d0b3 100644 --- a/narwhals/_duckdb/group_by.py +++ b/narwhals/_duckdb/group_by.py @@ -35,5 +35,5 @@ def agg(self, *exprs: DuckDBExpr) -> DuckDBLazyFrame: ) return self.compliant._with_native(result).rename( - dict(zip(self._keys, self._output_key_names)) + dict(zip(self._keys, self._output_key_names, strict=False)) ) diff --git a/narwhals/_duckdb/namespace.py b/narwhals/_duckdb/namespace.py index 27f290f435..8d60217ce3 100644 --- a/narwhals/_duckdb/namespace.py +++ b/narwhals/_duckdb/namespace.py @@ -28,7 +28,7 @@ evaluate_output_names_and_aliases, ) from narwhals._sql.namespace import SQLNamespace -from narwhals._utils import Implementation, requires, zip_strict +from narwhals._utils import Implementation, requires if TYPE_CHECKING: from collections.abc import Iterable, Mapping @@ -195,8 +195,10 @@ def func(df: DuckDBLazyFrame) -> list[Expression]: names_to_cols: Mapping[str, Expression] = { alias: native_expr for expr in exprs - for native_expr, _, alias in zip_strict( - expr(df), *evaluate_output_names_and_aliases(expr, df, []) + for native_expr, _, alias in zip( + expr(df), + *evaluate_output_names_and_aliases(expr, df, []), + strict=True, ) } field_args = ", ".join( diff --git a/narwhals/_duckdb/utils.py b/narwhals/_duckdb/utils.py index a4b803b49b..6e758c0062 100644 --- a/narwhals/_duckdb/utils.py +++ b/narwhals/_duckdb/utils.py @@ -6,20 +6,14 @@ import duckdb from duckdb import Expression -from narwhals._utils import ( - Implementation, - Version, - extend_bool, - isinstance_or_issubclass, - zip_strict, -) +from narwhals._utils import Implementation, Version, extend_bool, isinstance_or_issubclass from narwhals.exceptions import ColumnNotFoundError if TYPE_CHECKING: from collections.abc import Mapping, Sequence + from typing import TypeAlias from duckdb import DuckDBPyRelation - from typing_extensions import TypeAlias from narwhals._compliant.typing import CompliantLazyFrameAny from narwhals._duckdb.dataframe import DuckDBLazyFrame @@ -112,7 +106,7 @@ def evaluate_exprs_and_aliases( if len(output_names) != len(native_series_list): # pragma: no cover msg = f"Internal error: got output names {output_names}, but only got {len(native_series_list)} results" raise AssertionError(msg) - native_results.extend(zip(output_names, native_series_list)) + native_results.extend(zip(output_names, native_series_list, strict=False)) return native_results @@ -349,7 +343,9 @@ def generate_order_by_sql( return "" by_sql = ",".join( f"{parse_into_expression(x)} {DESCENDING_TO_ORDER[_descending]} {NULLS_LAST_TO_NULLS_POS[_nulls_last]}" - for x, _descending, _nulls_last in zip_strict(order_by, descending, nulls_last) + for x, _descending, _nulls_last in zip( + order_by, descending, nulls_last, strict=True + ) ) return f"order by {by_sql}" diff --git a/narwhals/_duration.py b/narwhals/_duration.py index 8e678be7ff..40e330a276 100644 --- a/narwhals/_duration.py +++ b/narwhals/_duration.py @@ -8,8 +8,7 @@ if TYPE_CHECKING: from collections.abc import Container, Mapping - - from typing_extensions import TypeAlias + from typing import TypeAlias __all__ = ["IntervalUnit"] diff --git a/narwhals/_expression_parsing.py b/narwhals/_expression_parsing.py index 2c7285259f..69ad8e85f7 100644 --- a/narwhals/_expression_parsing.py +++ b/narwhals/_expression_parsing.py @@ -5,9 +5,8 @@ from __future__ import annotations from enum import Enum, auto -from typing import TYPE_CHECKING, Any, Callable, Literal, cast +from typing import TYPE_CHECKING, Any, Literal, cast -from narwhals._utils import zip_strict from narwhals.dependencies import is_numpy_array_1d from narwhals.exceptions import ( InvalidIntoExprError, @@ -16,7 +15,7 @@ ) if TYPE_CHECKING: - from collections.abc import Iterator, Sequence + from collections.abc import Callable, Iterator, Sequence from typing_extensions import Never, TypeIs @@ -80,12 +79,13 @@ def evaluate_output_names_and_aliases( else output_names ) if exclude and expr._metadata.expansion_kind.is_multi_unnamed(): - output_names, aliases = zip_strict( + output_names, aliases = zip( *[ (x, alias) - for x, alias in zip_strict(output_names, aliases) + for x, alias in zip(output_names, aliases, strict=True) if x not in exclude - ] + ], + strict=True, ) return output_names, aliases diff --git a/narwhals/_ibis/dataframe.py b/narwhals/_ibis/dataframe.py index 43b4f2b6ce..b3beffce02 100644 --- a/narwhals/_ibis/dataframe.py +++ b/narwhals/_ibis/dataframe.py @@ -18,7 +18,6 @@ not_implemented, parse_columns_to_drop, to_pyarrow_table, - zip_strict, ) from narwhals.exceptions import InvalidOperationError @@ -26,11 +25,12 @@ from collections.abc import Iterable, Iterator, Mapping, Sequence from pathlib import Path from types import ModuleType + from typing import TypeAlias import pandas as pd import pyarrow as pa from ibis.expr.operations import Binary - from typing_extensions import Self, TypeAlias, TypeIs + from typing_extensions import Self, TypeIs from narwhals._compliant.typing import CompliantDataFrameAny from narwhals._ibis.group_by import IbisGroupBy @@ -311,7 +311,7 @@ def _convert_predicates( return left_on return [ cast("ir.BooleanColumn", (self.native[left] == other.native[right])) - for left, right in zip_strict(left_on, right_on) + for left, right in zip(left_on, right_on, strict=True) ] def collect_schema(self) -> dict[str, DType]: diff --git a/narwhals/_ibis/expr.py b/narwhals/_ibis/expr.py index 9de16232bb..2293da99f9 100644 --- a/narwhals/_ibis/expr.py +++ b/narwhals/_ibis/expr.py @@ -1,7 +1,7 @@ from __future__ import annotations import operator -from typing import TYPE_CHECKING, Any, Callable, TypeVar, cast +from typing import TYPE_CHECKING, Any, TypeVar, cast import ibis @@ -26,11 +26,10 @@ extend_bool, no_default, not_implemented, - zip_strict, ) if TYPE_CHECKING: - from collections.abc import Iterator, Sequence + from collections.abc import Callable, Iterator, Sequence import ibis.expr.types as ir from typing_extensions import Self @@ -157,7 +156,7 @@ def _sort( (True, False): desc_nulls_first, (True, True): desc_nulls_last, } - for col, _desc, _nulls_last in zip_strict(cols, descending, nulls_last): + for col, _desc, _nulls_last in zip(cols, descending, nulls_last, strict=True): yield mapping[(_desc, _nulls_last)](col) @classmethod diff --git a/narwhals/_ibis/expr_dt.py b/narwhals/_ibis/expr_dt.py index 7d98dba0df..95fb6a4329 100644 --- a/narwhals/_ibis/expr_dt.py +++ b/narwhals/_ibis/expr_dt.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Callable +from typing import TYPE_CHECKING, Any from narwhals._duration import Interval from narwhals._ibis.utils import ( @@ -12,6 +12,8 @@ from narwhals._utils import not_implemented if TYPE_CHECKING: + from collections.abc import Callable + import ibis.expr.types as ir from narwhals._ibis.expr import IbisExpr diff --git a/narwhals/_ibis/expr_str.py b/narwhals/_ibis/expr_str.py index c6564ba74c..2bab9d5c89 100644 --- a/narwhals/_ibis/expr_str.py +++ b/narwhals/_ibis/expr_str.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Callable +from typing import TYPE_CHECKING, Any import ibis from ibis.expr.datatypes import Timestamp @@ -9,8 +9,10 @@ from narwhals._utils import _is_naive_format, not_implemented if TYPE_CHECKING: + from collections.abc import Callable + from typing import TypeAlias + import ibis.expr.types as ir - from typing_extensions import TypeAlias from narwhals._ibis.expr import IbisExpr diff --git a/narwhals/_ibis/group_by.py b/narwhals/_ibis/group_by.py index 44b1d0cb40..9b4d25a68e 100644 --- a/narwhals/_ibis/group_by.py +++ b/narwhals/_ibis/group_by.py @@ -29,4 +29,4 @@ def agg(self, *exprs: IbisExpr) -> IbisLazyFrame: native = self.compliant.native return self.compliant._with_native( native.group_by(self._keys).aggregate(*self._evaluate_exprs(exprs)) - ).rename(dict(zip(self._keys, self._output_key_names))) + ).rename(dict(zip(self._keys, self._output_key_names, strict=False))) diff --git a/narwhals/_ibis/namespace.py b/narwhals/_ibis/namespace.py index 013af303b0..9b81c5d602 100644 --- a/narwhals/_ibis/namespace.py +++ b/narwhals/_ibis/namespace.py @@ -19,7 +19,7 @@ from narwhals._ibis.selectors import IbisSelectorNamespace from narwhals._ibis.utils import function, lit, narwhals_to_native_dtype from narwhals._sql.namespace import SQLNamespace -from narwhals._utils import Implementation, zip_strict +from narwhals._utils import Implementation if TYPE_CHECKING: from collections.abc import Iterable, Mapping, Sequence @@ -167,8 +167,10 @@ def func(df: IbisLazyFrame) -> list[ir.Value]: names_to_cols: Mapping[str, ir.Value] = { alias: native_expr for expr in exprs - for native_expr, _, alias in zip_strict( - expr(df), *evaluate_output_names_and_aliases(expr, df, []) + for native_expr, _, alias in zip( + expr(df), + *evaluate_output_names_and_aliases(expr, df, []), + strict=True, ) } return [ibis.struct(names_to_cols)] diff --git a/narwhals/_ibis/utils.py b/narwhals/_ibis/utils.py index eed667efec..f0554c6d33 100644 --- a/narwhals/_ibis/utils.py +++ b/narwhals/_ibis/utils.py @@ -11,11 +11,12 @@ if TYPE_CHECKING: from collections.abc import Callable, Mapping from datetime import timedelta + from typing import TypeAlias import ibis.expr.types as ir from ibis.common.temporal import TimestampUnit from ibis.expr.datatypes import DataType as IbisDataType - from typing_extensions import TypeAlias, TypeIs + from typing_extensions import TypeIs from narwhals._duration import IntervalUnit from narwhals._ibis.dataframe import IbisLazyFrame @@ -117,7 +118,7 @@ def evaluate_exprs(df: IbisLazyFrame, /, *exprs: IbisExpr) -> list[tuple[str, ir if len(output_names) != len(native_series_list): # pragma: no cover msg = f"Internal error: got output names {output_names}, but only got {len(native_series_list)} results" raise AssertionError(msg) - native_results.extend(zip(output_names, native_series_list)) + native_results.extend(zip(output_names, native_series_list, strict=False)) return native_results diff --git a/narwhals/_namespace.py b/narwhals/_namespace.py index 2d4480cc06..87b2f2b359 100644 --- a/narwhals/_namespace.py +++ b/narwhals/_namespace.py @@ -37,8 +37,9 @@ from narwhals._utils import Implementation, Version if TYPE_CHECKING: + from typing import TypeAlias + import pandas as pd - from typing_extensions import TypeAlias from narwhals._arrow.namespace import ArrowNamespace from narwhals._dask.namespace import DaskNamespace diff --git a/narwhals/_native.py b/narwhals/_native.py index 2e06ac7041..93d5db271d 100644 --- a/narwhals/_native.py +++ b/narwhals/_native.py @@ -105,7 +105,7 @@ class NativeDask(NativeLazyFrame, Protocol): from __future__ import annotations from collections.abc import Callable, Collection, Iterable, Sized -from typing import TYPE_CHECKING, Any, Protocol, TypeVar, Union, cast +from typing import TYPE_CHECKING, Any, Protocol, TypeVar, cast from narwhals.dependencies import ( get_cudf, @@ -122,12 +122,14 @@ class NativeDask(NativeLazyFrame, Protocol): ) if TYPE_CHECKING: + from typing import TypeAlias + import duckdb import pandas as pd import polars as pl import pyarrow as pa from sqlframe.base.dataframe import BaseDataFrame as _BaseDataFrame - from typing_extensions import Self, TypeAlias, TypeIs + from typing_extensions import Self, TypeIs SQLFrameDataFrame = _BaseDataFrame[Any, Any, Any, Any, Any] T = TypeVar("T") @@ -303,8 +305,8 @@ def dropDuplicatesWithinWatermark(self, *arg: Any, **kwargs: Any) -> Any: ... # ... return df.shape """ -IntoLazyFrame: TypeAlias = Union[NativeLazyFrame, NativeIbis] -IntoFrame: TypeAlias = Union[IntoDataFrame, IntoLazyFrame] +IntoLazyFrame: TypeAlias = NativeLazyFrame | NativeIbis +IntoFrame: TypeAlias = IntoDataFrame | IntoLazyFrame """Anything which can be converted to a Narwhals DataFrame or LazyFrame. Use this if your function can accept an object which can be converted to either diff --git a/narwhals/_pandas_like/dataframe.py b/narwhals/_pandas_like/dataframe.py index d2afe6b2ce..a03d5ed136 100644 --- a/narwhals/_pandas_like/dataframe.py +++ b/narwhals/_pandas_like/dataframe.py @@ -1,8 +1,8 @@ from __future__ import annotations -from collections.abc import Iterable, Iterator, Mapping, Sequence +from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence from itertools import chain, product -from typing import TYPE_CHECKING, Any, Callable, Literal, cast, overload +from typing import TYPE_CHECKING, Any, Literal, cast, overload import numpy as np @@ -31,7 +31,6 @@ generate_temporary_column_name, parse_columns_to_drop, scale_bytes, - zip_strict, ) from narwhals.dependencies import is_pandas_like_dataframe from narwhals.exceptions import InvalidOperationError, ShapeError @@ -41,10 +40,11 @@ from io import BytesIO from pathlib import Path from types import ModuleType + from typing import TypeAlias import pandas as pd import polars as pl - from typing_extensions import Self, TypeAlias, TypeIs + from typing_extensions import Self, TypeIs from narwhals._compliant.typing import CompliantDataFrameAny, CompliantLazyFrameAny from narwhals._pandas_like.expr import PandasLikeExpr @@ -185,7 +185,7 @@ def from_dict( implementation=context._implementation, version=context._version, ) - for ((key, dtype), backend) in zip(schema.items(), backends) + for ((key, dtype), backend) in zip(schema.items(), backends, strict=False) if dtype is not None } native = native.astype(native_schema) @@ -220,7 +220,7 @@ def from_dicts( implementation=context._implementation, version=context._version, ) - for ((key, dtype), backend) in zip(schema.items(), backends) + for ((key, dtype), backend) in zip(schema.items(), backends, strict=False) if dtype is not None } native = native.astype(native_schema) @@ -418,7 +418,7 @@ def iter_rows( else: col_names = self.native.columns for row in self.native.itertuples(index=False): - yield dict(zip(col_names, row)) + yield dict(zip(col_names, row, strict=False)) @property def schema(self) -> dict[str, DType]: @@ -628,7 +628,7 @@ def _join_left( ) extra = [ right_key if right_key not in self.columns else f"{right_key}{suffix}" - for left_key, right_key in zip_strict(left_on, right_on) + for left_key, right_key in zip(left_on, right_on, strict=True) if right_key != left_key ] # NOTE: Keep `inplace=True` to avoid making a redundant copy. @@ -696,7 +696,7 @@ def _join_semi( other_native = self._join_filter_rename( other=other, columns_to_select=list(right_on), - columns_mapping=dict(zip(right_on, left_on)), + columns_mapping=dict(zip(right_on, left_on, strict=False)), ) return self.native.dropna(subset=left_on, how="any").merge( other_native, how="inner", left_on=left_on, right_on=left_on @@ -722,7 +722,7 @@ def _join_anti( other_native = self._join_filter_rename( other=other, columns_to_select=list(right_on), - columns_mapping=dict(zip(right_on, left_on)), + columns_mapping=dict(zip(right_on, left_on, strict=True)), ) result_native = self.native.merge( other_native.dropna(subset=left_on, how="any"), diff --git a/narwhals/_pandas_like/expr.py b/narwhals/_pandas_like/expr.py index 944a136b11..15de66f082 100644 --- a/narwhals/_pandas_like/expr.py +++ b/narwhals/_pandas_like/expr.py @@ -334,14 +334,6 @@ def func(df: PandasLikeDataFrame) -> Sequence[PandasLikeSeries]: # noqa: C901, else: res_native = getattr(rolling, pandas_function_name)() elif function_name.startswith("ewm"): - if self._implementation.is_pandas() and ( - self._implementation._backend_version() - ) < (1, 2): # pragma: no cover - msg = ( - "Exponentially weighted calculation is not available in over " - f"context for pandas versions older than 1.2.0, found {self._implementation._backend_version()}." - ) - raise NotImplementedError(msg) ewm = grouped[list(aliases)].ewm(**pandas_kwargs) assert pandas_function_name is not None # help mypy # noqa: S101 res_native = getattr(ewm, pandas_function_name)() diff --git a/narwhals/_pandas_like/group_by.py b/narwhals/_pandas_like/group_by.py index d3426f55ea..6a85202752 100644 --- a/narwhals/_pandas_like/group_by.py +++ b/narwhals/_pandas_like/group_by.py @@ -10,15 +10,15 @@ from narwhals._exceptions import issue_warning from narwhals._expression_parsing import evaluate_output_names_and_aliases from narwhals._pandas_like.utils import make_group_by_kwargs -from narwhals._utils import zip_strict from narwhals.dependencies import is_pandas_like_dataframe if TYPE_CHECKING: from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence + from typing import TypeAlias import pandas as pd from pandas.api.typing import DataFrameGroupBy as _NativeGroupBy - from typing_extensions import TypeAlias, Unpack + from typing_extensions import Unpack from narwhals._compliant.typing import NarwhalsAggregation, ScalarKwargs from narwhals._pandas_like.dataframe import PandasLikeDataFrame @@ -317,7 +317,7 @@ def _select_results( return ( self.compliant._with_native(df, validate_column_names=False) .simple_select(*self._keys, *new_names) - .rename(dict(zip(self._keys, self._output_key_names))) + .rename(dict(zip(self._keys, self._output_key_names, strict=False))) ) def _getitem_aggs( @@ -358,7 +358,7 @@ def fn(df: pd.DataFrame) -> pd.Series[Any]: for expr in exprs for keys in expr(compliant) ) - out_group, out_names = zip_strict(*results) if results else ([], []) + out_group, out_names = zip(*results, strict=True) if results else ([], []) return into_series(out_group, index=out_names, context=ns).native return fn diff --git a/narwhals/_pandas_like/namespace.py b/narwhals/_pandas_like/namespace.py index 4aba68d5cd..23cbe50ff0 100644 --- a/narwhals/_pandas_like/namespace.py +++ b/narwhals/_pandas_like/namespace.py @@ -17,12 +17,10 @@ from narwhals._pandas_like.series import PandasLikeSeries from narwhals._pandas_like.typing import NativeDataFrameT, NativeSeriesT from narwhals._pandas_like.utils import is_non_nullable_boolean -from narwhals._utils import zip_strict if TYPE_CHECKING: from collections.abc import Iterable, Sequence - - from typing_extensions import TypeAlias + from typing import TypeAlias from narwhals._utils import Implementation, Version from narwhals.typing import CorrelationMethod, IntoDType, PythonLiteral @@ -338,7 +336,7 @@ def func(df: PandasLikeDataFrame) -> list[PandasLikeSeries]: # error: Cannot determine type of "values" [has-type] values: list[PandasLikeSeries] init_value, *values = ( - s.zip_with(~nm, "") for s, nm in zip_strict(series, null_mask) + s.zip_with(~nm, "") for s, nm in zip(series, null_mask, strict=True) ) sep_array = init_value._with_native( init_value.__native_namespace__().Series( @@ -351,7 +349,7 @@ def func(df: PandasLikeDataFrame) -> list[PandasLikeSeries]: separators = (sep_array.zip_with(~nm, "") for nm in null_mask[:-1]) result = reduce( operator.add, - (s + v for s, v in zip_strict(separators, values)), + (s + v for s, v in zip(separators, values, strict=True)), init_value, ) diff --git a/narwhals/_pandas_like/series.py b/narwhals/_pandas_like/series.py index e0c4e23b02..4017638e17 100644 --- a/narwhals/_pandas_like/series.py +++ b/narwhals/_pandas_like/series.py @@ -2,7 +2,7 @@ import operator import warnings -from typing import TYPE_CHECKING, Any, Callable, Literal, overload +from typing import TYPE_CHECKING, Any, Literal, overload from narwhals._compliant import EagerSeries, EagerSeriesHist from narwhals._pandas_like.series_cat import PandasLikeSeriesCatNamespace @@ -29,13 +29,14 @@ from narwhals.exceptions import InvalidOperationError if TYPE_CHECKING: - from collections.abc import Hashable, Iterable, Iterator, Sequence + from collections.abc import Callable, Hashable, Iterable, Iterator, Sequence from types import ModuleType + from typing import TypeAlias import pandas as pd import polars as pl import pyarrow as pa - from typing_extensions import Self, TypeAlias, TypeIs + from typing_extensions import Self, TypeIs from narwhals._arrow.typing import ChunkedArrayAny from narwhals._compliant.series import HistData @@ -207,7 +208,7 @@ def _align_full_broadcast(cls, *series: Self) -> Sequence[Self]: Series = series[0].__native_namespace__().Series lengths = [len(s) for s in series] target_length = max( - length for length, s in zip(lengths, series) if not s._broadcast + length for length, s in zip(lengths, series, strict=False) if not s._broadcast ) idx = series[lengths.index(target_length)].native.index reindexed = [] @@ -300,11 +301,8 @@ def scatter( ) series = native_series if in_place else native_series.copy(deep=True) - if impl.is_pandas(): - if in_place and NUMPY_VERSION < (2,): # pragma: no cover - values_native = values_native.copy() - if self._backend_version < (1, 2): - indices_native = indices_native.to_numpy() + if impl.is_pandas() and in_place and NUMPY_VERSION < (2,): # pragma: no cover + values_native = values_native.copy() series.iloc[indices_native] = values_native diff --git a/narwhals/_pandas_like/typing.py b/narwhals/_pandas_like/typing.py index 55c644bb5e..139bde96f8 100644 --- a/narwhals/_pandas_like/typing.py +++ b/narwhals/_pandas_like/typing.py @@ -5,10 +5,9 @@ from narwhals._typing_compat import TypeVar if TYPE_CHECKING: - from typing import Any + from typing import Any, TypeAlias import pandas as pd - from typing_extensions import TypeAlias from narwhals._native import ( NativePandasLikeDataFrame, diff --git a/narwhals/_pandas_like/utils.py b/narwhals/_pandas_like/utils.py index 136a5cac23..c70a00a04d 100644 --- a/narwhals/_pandas_like/utils.py +++ b/narwhals/_pandas_like/utils.py @@ -3,7 +3,7 @@ import functools import operator import re -from typing import TYPE_CHECKING, Any, Callable, Literal, TypeVar, cast +from typing import TYPE_CHECKING, Any, Literal, TypeVar, cast import numpy as np import pandas as pd @@ -30,13 +30,14 @@ from narwhals.exceptions import ShapeError if TYPE_CHECKING: - from collections.abc import Iterable, Iterator, Mapping + from collections.abc import Callable, Iterable, Iterator, Mapping from types import ModuleType + from typing import TypeAlias import pyarrow as pa from pandas._typing import Dtype as PandasDtype from pandas.core.dtypes.dtypes import BaseMaskedDtype - from typing_extensions import TypeAlias, TypeIs + from typing_extensions import TypeIs from narwhals._duration import IntervalUnit from narwhals._pandas_like.expr import PandasLikeExpr @@ -339,32 +340,15 @@ def native_to_narwhals_dtype( raise AssertionError(msg) -if Implementation.PANDAS._backend_version() >= (1, 2): - - def is_dtype_numpy_nullable(dtype: Any) -> TypeIs[BaseMaskedDtype]: - """Return `True` if `dtype` is `"numpy_nullable"`.""" - # NOTE: We need a sentinel as the positive case is `BaseMaskedDtype.base = None` - # See https://github.com/narwhals-dev/narwhals/pull/2740#discussion_r2171667055 - sentinel = object() - return ( - isinstance(dtype, pd.api.extensions.ExtensionDtype) - and getattr(dtype, "base", sentinel) is None - ) -else: # pragma: no cover - - def is_dtype_numpy_nullable(dtype: Any) -> TypeIs[BaseMaskedDtype]: - # NOTE: `base` attribute was added between 1.1-1.2 - # Checking by isinstance requires using an import path that is no longer valid - # `1.1`: https://github.com/pandas-dev/pandas/blob/b5958ee1999e9aead1938c0bba2b674378807b3d/pandas/core/arrays/masked.py#L37 - # `1.2`: https://github.com/pandas-dev/pandas/blob/7c48ff4409c622c582c56a5702373f726de08e96/pandas/core/arrays/masked.py#L41 - # `1.5`: https://github.com/pandas-dev/pandas/blob/35b0d1dcadf9d60722c055ee37442dc76a29e64c/pandas/core/dtypes/dtypes.py#L1609 - if isinstance(dtype, pd.api.extensions.ExtensionDtype): - from pandas.core.arrays.masked import ( # type: ignore[attr-defined] - BaseMaskedDtype as OldBaseMaskedDtype, # pyright: ignore[reportAttributeAccessIssue] - ) - - return isinstance(dtype, OldBaseMaskedDtype) - return False +def is_dtype_numpy_nullable(dtype: Any) -> TypeIs[BaseMaskedDtype]: + """Return `True` if `dtype` is `"numpy_nullable"`.""" + # NOTE: We need a sentinel as the positive case is `BaseMaskedDtype.base = None` + # See https://github.com/narwhals-dev/narwhals/pull/2740#discussion_r2171667055 + sentinel = object() + return ( + isinstance(dtype, pd.api.extensions.ExtensionDtype) + and getattr(dtype, "base", sentinel) is None + ) def get_dtype_backend(dtype: Any, implementation: Implementation) -> DTypeBackend: diff --git a/narwhals/_polars/dataframe.py b/narwhals/_polars/dataframe.py index 0848967e9a..3c746c188c 100644 --- a/narwhals/_polars/dataframe.py +++ b/narwhals/_polars/dataframe.py @@ -33,13 +33,13 @@ from narwhals.exceptions import ColumnNotFoundError if TYPE_CHECKING: - from collections.abc import Iterable + from collections.abc import Callable, Iterable from types import ModuleType - from typing import Callable + from typing import TypeAlias import pandas as pd import pyarrow as pa - from typing_extensions import Self, TypeAlias, TypeIs + from typing_extensions import Self, TypeIs from narwhals._compliant.typing import CompliantDataFrameAny, CompliantLazyFrameAny from narwhals._polars.expr import PolarsExpr @@ -477,7 +477,11 @@ def __getitem__( # noqa: C901, PLR0912 return self.select() if is_boolean_selector(columns): native = native.select( - *(col for col, select in zip(native.columns, columns) if select) + *( + col + for col, select in zip(native.columns, columns, strict=False) + if select + ) ) elif is_index_selector(columns): if is_slice_index(columns) or is_range(columns): diff --git a/narwhals/_polars/expr.py b/narwhals/_polars/expr.py index 1e2b546014..3f9d652ac9 100644 --- a/narwhals/_polars/expr.py +++ b/narwhals/_polars/expr.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Callable, ClassVar, cast +from typing import TYPE_CHECKING, Any, ClassVar, cast import polars as pl @@ -19,7 +19,7 @@ from narwhals._utils import Implementation, no_default, requires if TYPE_CHECKING: - from collections.abc import Sequence + from collections.abc import Callable, Sequence from typing_extensions import Self diff --git a/narwhals/_polars/namespace.py b/narwhals/_polars/namespace.py index eca964bbdb..e7e0f2692c 100644 --- a/narwhals/_polars/namespace.py +++ b/narwhals/_polars/namespace.py @@ -8,7 +8,7 @@ from narwhals._polars.expr import PolarsExpr from narwhals._polars.series import PolarsSeries from narwhals._polars.utils import extract_args_kwargs, narwhals_to_native_dtype -from narwhals._utils import Implementation, requires, zip_strict +from narwhals._utils import Implementation, requires from narwhals.dependencies import is_numpy_array_2d from narwhals.dtypes import DType @@ -180,7 +180,7 @@ def concat_str( else: init_value, *values = [ pl.when(nm).then(pl.lit("")).otherwise(expr.cast(pl.String())) - for expr, nm in zip_strict(pl_exprs, null_mask) + for expr, nm in zip(pl_exprs, null_mask, strict=True) ] separators = [ pl.when(~nm).then(sep).otherwise(pl.lit("")) for nm in null_mask[:-1] @@ -189,7 +189,7 @@ def concat_str( result = pl.fold( # type: ignore[assignment] acc=init_value, function=operator.add, - exprs=[s + v for s, v in zip_strict(separators, values)], + exprs=[s + v for s, v in zip(separators, values, strict=True)], ) return self._expr(result, version=self._version) diff --git a/narwhals/_polars/series.py b/narwhals/_polars/series.py index 5a8397522a..7b1cf530c6 100644 --- a/narwhals/_polars/series.py +++ b/narwhals/_polars/series.py @@ -26,11 +26,11 @@ if TYPE_CHECKING: from collections.abc import Iterable, Iterator, Mapping, Sequence from types import ModuleType - from typing import Literal, TypeVar + from typing import Literal, TypeAlias, TypeVar import pandas as pd import pyarrow as pa - from typing_extensions import Self, TypeAlias, TypeIs + from typing_extensions import Self, TypeIs from narwhals._polars.dataframe import Method, PolarsDataFrame from narwhals._polars.namespace import PolarsNamespace diff --git a/narwhals/_spark_like/dataframe.py b/narwhals/_spark_like/dataframe.py index 18eb945843..b8b407dd8a 100644 --- a/narwhals/_spark_like/dataframe.py +++ b/narwhals/_spark_like/dataframe.py @@ -24,7 +24,6 @@ not_implemented, parse_columns_to_drop, to_pyarrow_table, - zip_strict, ) from narwhals.exceptions import InvalidOperationError @@ -33,12 +32,13 @@ from io import BytesIO from pathlib import Path from types import ModuleType + from typing import TypeAlias import pyarrow as pa from sqlframe.base.column import Column from sqlframe.base.dataframe import BaseDataFrame from sqlframe.base.window import Window - from typing_extensions import Self, TypeAlias, TypeIs + from typing_extensions import Self, TypeIs from narwhals._compliant.typing import CompliantDataFrameAny from narwhals._spark_like.expr import SparkLikeExpr @@ -338,7 +338,7 @@ def sort(self, *by: str, descending: bool | Sequence[bool], nulls_last: bool) -> for d in descending ) - sort_cols = [sort_f(col) for col, sort_f in zip_strict(by, sort_funcs)] + sort_cols = [sort_f(col) for col, sort_f in zip(by, sort_funcs, strict=True)] return self._with_native(self.native.sort(*sort_cols)) def top_k(self, k: int, *, by: Iterable[str], reverse: bool | Sequence[bool]) -> Self: @@ -347,7 +347,7 @@ def top_k(self, k: int, *, by: Iterable[str], reverse: bool | Sequence[bool]) -> sort_funcs = ( self._F.desc_nulls_last if not d else self._F.asc_nulls_last for d in reverse ) - sort_cols = [sort_f(col) for col, sort_f in zip_strict(by, sort_funcs)] + sort_cols = [sort_f(col) for col, sort_f in zip(by, sort_funcs, strict=True)] return self._with_native(self.native.sort(*sort_cols).limit(k)) def drop_nulls(self, subset: Sequence[str] | None) -> Self: @@ -418,7 +418,7 @@ def join( ) rename_mapping = { - **dict(zip(right_on_, left_on_)), + **dict(zip(right_on_, left_on_, strict=False)), **{ colname: f"{colname}{suffix}" if colname in left_columns else colname for colname in right_cols_to_rename @@ -448,7 +448,9 @@ def join( and_, ( getattr(self.native, left_key) == getattr(other_native, right_key) - for left_key, right_key in zip_strict(left_on_, right_on_remapped) + for left_key, right_key in zip( + left_on_, right_on_remapped, strict=True + ) ), ) if how == "full" diff --git a/narwhals/_spark_like/expr.py b/narwhals/_spark_like/expr.py index 4fc0883e66..95cde685ea 100644 --- a/narwhals/_spark_like/expr.py +++ b/narwhals/_spark_like/expr.py @@ -1,7 +1,7 @@ from __future__ import annotations import operator -from typing import TYPE_CHECKING, Any, Callable, ClassVar, Literal, cast +from typing import TYPE_CHECKING, Any, ClassVar, Literal, cast from narwhals._spark_like.expr_dt import SparkLikeExprDateTimeNamespace from narwhals._spark_like.expr_list import SparkLikeExprListNamespace @@ -21,15 +21,15 @@ extend_bool, no_default, not_implemented, - zip_strict, ) if TYPE_CHECKING: - from collections.abc import Iterator, Mapping, Sequence + from collections.abc import Callable, Iterator, Mapping, Sequence + from typing import TypeAlias from sqlframe.base.column import Column from sqlframe.base.window import Window, WindowSpec - from typing_extensions import Self, TypeAlias + from typing_extensions import Self from narwhals._compliant import WindowInputs from narwhals._compliant.typing import ( @@ -160,7 +160,7 @@ def _sort( } yield from ( mapping[(_desc, _nulls_last)](col) - for col, _desc, _nulls_last in zip_strict(cols, descending, nulls_last) + for col, _desc, _nulls_last in zip(cols, descending, nulls_last, strict=True) ) def partition_by(self, *cols: Column | str) -> WindowSpec: @@ -380,7 +380,7 @@ def replace_strict( F = self._F - mapping = dict(zip(old, new)) + mapping = dict(zip(old, new, strict=False)) mapping_expr = F.create_map([F.lit(x) for x in chain(*mapping.items())]) def func(df: SparkLikeLazyFrame) -> list[Column]: diff --git a/narwhals/_spark_like/group_by.py b/narwhals/_spark_like/group_by.py index 154ee6e1eb..9a6fb19c75 100644 --- a/narwhals/_spark_like/group_by.py +++ b/narwhals/_spark_like/group_by.py @@ -33,5 +33,5 @@ def agg(self, *exprs: SparkLikeExpr) -> SparkLikeLazyFrame: ) return self.compliant._with_native(result).rename( - dict(zip(self._keys, self._output_key_names)) + dict(zip(self._keys, self._output_key_names, strict=False)) ) diff --git a/narwhals/_spark_like/namespace.py b/narwhals/_spark_like/namespace.py index 201e87eea6..03deefb424 100644 --- a/narwhals/_spark_like/namespace.py +++ b/narwhals/_spark_like/namespace.py @@ -20,7 +20,6 @@ true_divide, ) from narwhals._sql.namespace import SQLNamespace -from narwhals._utils import zip_strict if TYPE_CHECKING: from collections.abc import Iterable, Mapping @@ -250,8 +249,10 @@ def func(df: SparkLikeLazyFrame) -> list[Column]: names_to_cols: Mapping[str, Column] = { alias: native_expr for expr in exprs - for native_expr, _, alias in zip_strict( - expr(df), *evaluate_output_names_and_aliases(expr, df, []) + for native_expr, _, alias in zip( + expr(df), + *evaluate_output_names_and_aliases(expr, df, []), + strict=True, ) } aliased = (col.alias(name) for name, col in names_to_cols.items()) diff --git a/narwhals/_spark_like/utils.py b/narwhals/_spark_like/utils.py index d7ac768928..02247636ff 100644 --- a/narwhals/_spark_like/utils.py +++ b/narwhals/_spark_like/utils.py @@ -13,11 +13,11 @@ if TYPE_CHECKING: from collections.abc import Mapping + from typing import TypeAlias import sqlframe.base.types as sqlframe_types from sqlframe.base.column import Column from sqlframe.base.session import _BaseSession as Session - from typing_extensions import TypeAlias from narwhals._compliant.typing import CompliantLazyFrameAny from narwhals._spark_like.dataframe import SparkLikeLazyFrame @@ -215,7 +215,7 @@ def evaluate_exprs( if len(output_names) != len(native_series_list): # pragma: no cover msg = f"Internal error: got output names {output_names}, but only got {len(native_series_list)} results" raise AssertionError(msg) - native_results.extend(zip(output_names, native_series_list)) + native_results.extend(zip(output_names, native_series_list, strict=False)) return native_results diff --git a/narwhals/_sql/dataframe.py b/narwhals/_sql/dataframe.py index b2ead124ef..7a812875c6 100644 --- a/narwhals/_sql/dataframe.py +++ b/narwhals/_sql/dataframe.py @@ -14,8 +14,9 @@ if TYPE_CHECKING: from collections.abc import Sequence + from typing import TypeAlias - from typing_extensions import Self, TypeAlias + from typing_extensions import Self from narwhals._compliant.window import WindowInputs from narwhals._sql.expr import SQLExpr diff --git a/narwhals/_sql/expr.py b/narwhals/_sql/expr.py index 63fdec5562..d392b454f9 100644 --- a/narwhals/_sql/expr.py +++ b/narwhals/_sql/expr.py @@ -1,7 +1,7 @@ from __future__ import annotations import operator as op -from typing import TYPE_CHECKING, Any, Callable, Literal, Protocol +from typing import TYPE_CHECKING, Any, Literal, Protocol from narwhals._compliant.expr import LazyExpr from narwhals._compliant.typing import ( @@ -27,7 +27,7 @@ from narwhals.exceptions import InvalidOperationError if TYPE_CHECKING: - from collections.abc import Sequence + from collections.abc import Callable, Sequence from typing_extensions import Self diff --git a/narwhals/_sql/group_by.py b/narwhals/_sql/group_by.py index 3fc4dbb44b..dc3b3393c5 100644 --- a/narwhals/_sql/group_by.py +++ b/narwhals/_sql/group_by.py @@ -5,7 +5,6 @@ from narwhals._compliant.group_by import CompliantGroupBy, ParseKeysGroupBy from narwhals._compliant.typing import CompliantLazyFrameT, NativeExprT_co from narwhals._sql.typing import SQLExprT_contra -from narwhals._utils import zip_strict if TYPE_CHECKING: from collections.abc import Iterable, Iterator @@ -29,13 +28,13 @@ def _evaluate_expr(self, expr: SQLExprT_contra, /) -> Iterator[NativeExprT_co]: native_exprs = expr(self.compliant) if expr._is_multi_output_unnamed(): exclude = {*self._keys, *self._output_key_names} - for native_expr, name, alias in zip_strict( - native_exprs, output_names, aliases + for native_expr, name, alias in zip( + native_exprs, output_names, aliases, strict=True ): if name not in exclude: yield expr._alias_native(native_expr, alias) else: - for native_expr, alias in zip_strict(native_exprs, aliases): + for native_expr, alias in zip(native_exprs, aliases, strict=True): yield expr._alias_native(native_expr, alias) def _evaluate_exprs( diff --git a/narwhals/_translate.py b/narwhals/_translate.py index 48531e144c..7580fe33d6 100644 --- a/narwhals/_translate.py +++ b/narwhals/_translate.py @@ -69,8 +69,10 @@ class OtherConvertible( from narwhals._typing_compat import TypeVar if TYPE_CHECKING: + from typing import TypeAlias + import pyarrow as pa - from typing_extensions import Required, Self, TypeAlias, TypeIs + from typing_extensions import Required, Self, TypeIs class ArrowStreamExportable(Protocol): diff --git a/narwhals/_typing.py b/narwhals/_typing.py index c4307c2680..8361d96303 100644 --- a/narwhals/_typing.py +++ b/narwhals/_typing.py @@ -1,13 +1,13 @@ from __future__ import annotations from types import ModuleType -from typing import TYPE_CHECKING, Literal, Union +from typing import TYPE_CHECKING, Literal from narwhals._typing_compat import TypeVar from narwhals._utils import Implementation, _NoDefault if TYPE_CHECKING: - from typing_extensions import TypeAlias + from typing import TypeAlias # `str` aliases _Polars: TypeAlias = Literal["polars"] @@ -92,7 +92,7 @@ """ BackendT = TypeVar("BackendT", bound=Backend) -IntoBackend: TypeAlias = Union[BackendT, ModuleType] +IntoBackend: TypeAlias = BackendT | ModuleType """Anything that can be converted into a [`narwhals.Implementation`][]. `backend` can be specified in three ways. diff --git a/narwhals/_typing_compat.py b/narwhals/_typing_compat.py index 47cbf3464a..ff1e5b49e1 100644 --- a/narwhals/_typing_compat.py +++ b/narwhals/_typing_compat.py @@ -22,7 +22,7 @@ from typing import TYPE_CHECKING, Any if TYPE_CHECKING: - from typing import Callable + from collections.abc import Callable if sys.version_info >= (3, 13): from typing import TypeVar diff --git a/narwhals/_utils.py b/narwhals/_utils.py index 70b6f5d342..dacde97716 100644 --- a/narwhals/_utils.py +++ b/narwhals/_utils.py @@ -3,26 +3,31 @@ import os import re import sys -from collections.abc import Collection, Container, Iterable, Iterator, Mapping, Sequence +from collections.abc import ( + Callable, + Collection, + Container, + Iterable, + Iterator, + Mapping, + Sequence, +) from datetime import timezone from enum import Enum, auto -from functools import cache, lru_cache, partial, wraps +from functools import cache, lru_cache, wraps from importlib.util import find_spec from inspect import getattr_static, getdoc -from itertools import chain from operator import attrgetter from pathlib import Path from secrets import token_hex from typing import ( TYPE_CHECKING, Any, - Callable, Final, Generic, Literal, Protocol, TypeVar, - Union, cast, overload, ) @@ -56,18 +61,12 @@ if TYPE_CHECKING: from collections.abc import Set # noqa: PYI025 from types import ModuleType + from typing import Concatenate, TypeAlias import pandas as pd import polars as pl import pyarrow as pa - from typing_extensions import ( - Concatenate, - LiteralString, - ParamSpec, - Self, - TypeAlias, - TypeIs, - ) + from typing_extensions import LiteralString, ParamSpec, Self, TypeIs from narwhals._compliant import CompliantExprT, CompliantSeriesT, NativeSeriesT_co from narwhals._compliant.any_namespace import NamespaceAccessor @@ -139,7 +138,7 @@ UnknownBackendName: TypeAlias = str FrameOrSeriesT = TypeVar( - "FrameOrSeriesT", bound=Union[LazyFrame[Any], DataFrame[Any], Series[Any]] + "FrameOrSeriesT", bound=LazyFrame[Any] | DataFrame[Any] | Series[Any] ) _T1 = TypeVar("_T1") @@ -602,8 +601,8 @@ def is_pyspark_pre_4(implementation: Implementation) -> bool: MIN_VERSIONS: Mapping[Implementation, tuple[int, ...]] = { - Implementation.PANDAS: (1, 1, 3), - Implementation.MODIN: (0, 8, 2), + Implementation.PANDAS: (1, 3, 4), + Implementation.MODIN: (0, 22, 0), Implementation.CUDF: (24, 10), Implementation.PYARROW: (13,), Implementation.PYSPARK: (3, 5), @@ -1035,46 +1034,6 @@ def maybe_reset_index(obj: FrameOrSeriesT) -> FrameOrSeriesT: return obj_any -if TYPE_CHECKING: - zip_strict = partial(zip, strict=True) -else: - import sys - - if sys.version_info >= (3, 10): - zip_strict = partial(zip, strict=True) - else: # pragma: no cover - # https://stackoverflow.com/questions/32954486/zip-iterators-asserting-for-equal-length-in-python/69485272#69485272 - - def zip_strict(*iterables: Iterable[Any]) -> Iterable[tuple[Any, ...]]: - # For trivial cases, use pure zip. - if len(iterables) < 2: - return zip(*iterables) - # Tail for the first iterable - first_stopped = False - - def first_tail() -> Any: - nonlocal first_stopped - first_stopped = True - return - yield - - # Tail for the zip - def zip_tail() -> Any: - if not first_stopped: # pragma: no cover - msg = "zip_strict: first iterable is longer" - raise ValueError(msg) - for _ in chain.from_iterable(rest): # pragma: no cover - msg = "zip_strict: first iterable is shorter" - raise ValueError(msg) - yield - - # Put the pieces together - iterables_it = iter(iterables) - first = chain(next(iterables_it), first_tail()) - rest = list(map(iter, iterables_it)) - return chain(zip(first, *rest), zip_tail()) - - def _is_range_index(obj: Any, native_namespace: Any) -> TypeIs[pd.RangeIndex]: return isinstance(obj, native_namespace.RangeIndex) @@ -1674,7 +1633,7 @@ def _remap_full_join_keys( right_keys_suffixed = ( f"{key}{suffix}" if key in left_on else key for key in right_on ) - return dict(zip(right_on, right_keys_suffixed)) + return dict(zip(right_on, right_keys_suffixed, strict=False)) def _into_arrow_table(data: IntoArrowTable, context: _LimitedContext, /) -> pa.Table: diff --git a/narwhals/dataframe.py b/narwhals/dataframe.py index bd602efd52..4933c48499 100644 --- a/narwhals/dataframe.py +++ b/narwhals/dataframe.py @@ -6,7 +6,6 @@ from typing import ( TYPE_CHECKING, Any, - Callable, ClassVar, Generic, Literal, @@ -43,7 +42,6 @@ predicates_contains_list_of_bool, qualified_type_name, supports_arrow_c_stream, - zip_strict, ) from narwhals.dependencies import is_numpy_array_2d, is_pyarrow_table from narwhals.exceptions import ( @@ -57,15 +55,16 @@ from narwhals.translate import to_native if TYPE_CHECKING: - from collections.abc import Iterable, Iterator, Mapping, Sequence + from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence from io import BytesIO from pathlib import Path from types import ModuleType + from typing import Concatenate, TypeAlias import pandas as pd import polars as pl import pyarrow as pa - from typing_extensions import Concatenate, ParamSpec, Self, TypeAlias + from typing_extensions import ParamSpec, Self from narwhals._compliant import CompliantDataFrame, CompliantLazyFrame from narwhals._compliant.typing import CompliantExprAny @@ -1797,7 +1796,7 @@ def group_by( _keys = [ k if is_expr else col(k) - for k, is_expr in zip_strict(flat_keys, key_is_expr_or_series) + for k, is_expr in zip(flat_keys, key_is_expr_or_series, strict=True) ] expr_flat_keys = self._flatten_and_extract(*_keys) check_expressions_preserve_length( @@ -3023,7 +3022,8 @@ def group_by( raise NotImplementedError(msg) _keys = [ - k if is_expr else col(k) for k, is_expr in zip_strict(flat_keys, key_is_expr) + k if is_expr else col(k) + for k, is_expr in zip(flat_keys, key_is_expr, strict=True) ] expr_flat_keys = self._flatten_and_extract(*_keys) check_expressions_preserve_length( diff --git a/narwhals/dependencies.py b/narwhals/dependencies.py index 9a7830166d..bbc49e4f71 100644 --- a/narwhals/dependencies.py +++ b/narwhals/dependencies.py @@ -9,6 +9,8 @@ from narwhals._exceptions import issue_warning if TYPE_CHECKING: + from typing import TypeGuard + import cudf import dask.dataframe as dd import duckdb @@ -19,7 +21,7 @@ import pyarrow as pa import pyspark.sql as pyspark_sql from pyspark.sql.connect.dataframe import DataFrame as PySparkConnectDataFrame - from typing_extensions import TypeGuard, TypeIs + from typing_extensions import TypeIs from narwhals._spark_like.dataframe import SQLFrameDataFrame from narwhals.dataframe import DataFrame, LazyFrame diff --git a/narwhals/expr.py b/narwhals/expr.py index 9b6f616f71..e69631fc87 100644 --- a/narwhals/expr.py +++ b/narwhals/expr.py @@ -1,8 +1,8 @@ from __future__ import annotations import math -from collections.abc import Iterable, Mapping, Sequence -from typing import TYPE_CHECKING, Any, Callable +from collections.abc import Callable, Iterable, Mapping, Sequence +from typing import TYPE_CHECKING, Any from narwhals._expression_parsing import ExprKind, ExprNode, evaluate_nodes from narwhals._utils import ( @@ -23,9 +23,9 @@ from narwhals.translate import to_native if TYPE_CHECKING: - from typing import NoReturn, TypeVar + from typing import Concatenate, NoReturn, TypeVar - from typing_extensions import Concatenate, ParamSpec, Self + from typing_extensions import ParamSpec, Self from narwhals._compliant import CompliantExpr, CompliantNamespace from narwhals._typing import NoDefault diff --git a/narwhals/expr_name.py b/narwhals/expr_name.py index 64525aae56..820c9f682b 100644 --- a/narwhals/expr_name.py +++ b/narwhals/expr_name.py @@ -1,10 +1,12 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Callable, Generic, TypeVar +from typing import TYPE_CHECKING, Generic, TypeVar from narwhals._expression_parsing import ExprKind, ExprNode if TYPE_CHECKING: + from collections.abc import Callable + from narwhals.expr import Expr ExprT = TypeVar("ExprT", bound="Expr") diff --git a/narwhals/functions.py b/narwhals/functions.py index 53772a063b..5a368e965a 100644 --- a/narwhals/functions.py +++ b/narwhals/functions.py @@ -36,8 +36,9 @@ if TYPE_CHECKING: from types import ModuleType + from typing import TypeAlias - from typing_extensions import Self, TypeAlias, TypeIs + from typing_extensions import Self, TypeIs from narwhals._native import NativeDataFrame, NativeLazyFrame, NativeSeries from narwhals._translate import IntoArrowTable diff --git a/narwhals/plugins.py b/narwhals/plugins.py index 982f9fe986..e550bebca0 100644 --- a/narwhals/plugins.py +++ b/narwhals/plugins.py @@ -2,7 +2,7 @@ import sys from functools import cache -from typing import TYPE_CHECKING, Any, Protocol, cast +from typing import TYPE_CHECKING, Any, Protocol from narwhals._compliant import CompliantNamespace from narwhals._typing_compat import TypeVar @@ -10,8 +10,9 @@ if TYPE_CHECKING: from collections.abc import Iterator from importlib.metadata import EntryPoints + from typing import TypeAlias - from typing_extensions import LiteralString, TypeAlias + from typing_extensions import LiteralString from narwhals._compliant.typing import ( CompliantDataFrameAny, @@ -44,8 +45,6 @@ def _discover_entrypoints() -> EntryPoints: from importlib.metadata import entry_points as eps group = "narwhals.plugins" - if sys.version_info < (3, 10): - return cast("EntryPoints", eps().get(group, ())) return eps(group=group) diff --git a/narwhals/schema.py b/narwhals/schema.py index b459be68fa..ddad3fa747 100644 --- a/narwhals/schema.py +++ b/narwhals/schema.py @@ -11,7 +11,7 @@ from functools import partial from typing import TYPE_CHECKING, cast -from narwhals._utils import Implementation, Version, qualified_type_name, zip_strict +from narwhals._utils import Implementation, Version, qualified_type_name from narwhals.dependencies import ( get_cudf, is_cudf_dtype, @@ -305,7 +305,9 @@ def to_pandas( raise ValueError(msg) return { name: to_native_dtype(dtype=dtype, dtype_backend=backend) - for name, dtype, backend in zip_strict(self.keys(), self.values(), backends) + for name, dtype, backend in zip( + self.keys(), self.values(), backends, strict=True + ) } def to_polars(self) -> pl.Schema: diff --git a/narwhals/series.py b/narwhals/series.py index 6acc476245..c0e88291cb 100644 --- a/narwhals/series.py +++ b/narwhals/series.py @@ -1,18 +1,9 @@ from __future__ import annotations import math -from collections.abc import Iterable, Iterator, Mapping, Sequence +from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence from functools import partial -from typing import ( - TYPE_CHECKING, - Any, - Callable, - ClassVar, - Generic, - Literal, - cast, - overload, -) +from typing import TYPE_CHECKING, Any, ClassVar, Generic, Literal, cast, overload from narwhals._expression_parsing import ExprKind, ExprNode from narwhals._utils import ( diff --git a/narwhals/stable/v1/__init__.py b/narwhals/stable/v1/__init__.py index a974d3c565..bdb25b5567 100644 --- a/narwhals/stable/v1/__init__.py +++ b/narwhals/stable/v1/__init__.py @@ -1,7 +1,7 @@ from __future__ import annotations from functools import wraps -from typing import TYPE_CHECKING, Any, Callable, Final, Literal, cast, overload +from typing import TYPE_CHECKING, Any, Final, Literal, cast, overload import narwhals as nw from narwhals import exceptions, functions as nw_f @@ -72,7 +72,7 @@ from narwhals.translate import _from_native_impl, get_native_namespace, to_py_scalar if TYPE_CHECKING: - from collections.abc import Iterable, Mapping, Sequence + from collections.abc import Callable, Iterable, Mapping, Sequence from types import ModuleType from typing_extensions import ParamSpec, Self, Unpack diff --git a/narwhals/stable/v1/typing.py b/narwhals/stable/v1/typing.py index a154a1a3f8..8ec49dd628 100644 --- a/narwhals/stable/v1/typing.py +++ b/narwhals/stable/v1/typing.py @@ -2,13 +2,13 @@ from typing import TYPE_CHECKING, Any, Protocol, Union -from narwhals._native import IntoSeries +from narwhals._native import IntoSeries, NativeDuckDB from narwhals._typing_compat import TypeVar if TYPE_CHECKING: - from typing_extensions import TypeAlias + from typing import TypeAlias - from narwhals._native import NativeDataFrame, NativeDuckDB, NativeLazyFrame + from narwhals._native import NativeDataFrame, NativeLazyFrame from narwhals.stable.v1 import DataFrame, Expr, LazyFrame, Series class DataFrameLike(Protocol): diff --git a/narwhals/stable/v2/__init__.py b/narwhals/stable/v2/__init__.py index 3f480b4ca8..cb69f217a8 100644 --- a/narwhals/stable/v2/__init__.py +++ b/narwhals/stable/v2/__init__.py @@ -1,7 +1,7 @@ from __future__ import annotations from functools import wraps -from typing import TYPE_CHECKING, Any, Callable, Final, Literal, cast, overload +from typing import TYPE_CHECKING, Any, Final, Literal, cast, overload import narwhals as nw from narwhals import exceptions, functions as nw_f @@ -71,7 +71,7 @@ from narwhals.translate import _from_native_impl, get_native_namespace, to_py_scalar if TYPE_CHECKING: - from collections.abc import Iterable, Mapping, Sequence + from collections.abc import Callable, Iterable, Mapping, Sequence from typing_extensions import ParamSpec, Self, Unpack diff --git a/narwhals/stable/v2/typing.py b/narwhals/stable/v2/typing.py index c95a85b787..1f9e48e4fa 100644 --- a/narwhals/stable/v2/typing.py +++ b/narwhals/stable/v2/typing.py @@ -14,7 +14,7 @@ ) if TYPE_CHECKING: - from typing_extensions import TypeAlias + from typing import TypeAlias from narwhals.stable.v2 import DataFrame, Expr, LazyFrame, Series diff --git a/narwhals/testing/asserts/series.py b/narwhals/testing/asserts/series.py index 1408da0309..5bdcbb2ea7 100644 --- a/narwhals/testing/asserts/series.py +++ b/narwhals/testing/asserts/series.py @@ -1,16 +1,17 @@ from __future__ import annotations from functools import partial -from typing import TYPE_CHECKING, Any, Callable +from typing import TYPE_CHECKING, Any -from narwhals._utils import qualified_type_name, zip_strict +from narwhals._utils import qualified_type_name from narwhals.dependencies import is_narwhals_series from narwhals.dtypes import Array, Boolean, Categorical, List, String, Struct from narwhals.functions import new_series from narwhals.testing.asserts.utils import raise_series_assertion_error if TYPE_CHECKING: - from typing_extensions import TypeAlias + from collections.abc import Callable + from typing import TypeAlias from narwhals.series import Series from narwhals.typing import IntoSeriesT, SeriesT @@ -242,7 +243,7 @@ def _check_list_like( # `check_order` value at the top level. impl = left_vals.implementation try: - for left_val, right_val in zip_strict(left_vals, right_vals): + for left_val, right_val in zip(left_vals, right_vals, strict=True): check_fn( new_series("", values=left_val, dtype=left_dtype.inner, backend=impl), new_series("", values=right_val, dtype=right_dtype.inner, backend=impl), @@ -264,7 +265,9 @@ def _check_struct( # * dtype differs, regardless of `check_dtypes=False` # * order applies only at top level try: - for left_field, right_field in zip_strict(left_dtype.fields, right_dtype.fields): + for left_field, right_field in zip( + left_dtype.fields, right_dtype.fields, strict=True + ): check_fn( left_vals.struct.field(left_field.name), right_vals.struct.field(right_field.name), diff --git a/narwhals/testing/asserts/utils.py b/narwhals/testing/asserts/utils.py index 75528e454a..d0b583b3d8 100644 --- a/narwhals/testing/asserts/utils.py +++ b/narwhals/testing/asserts/utils.py @@ -5,7 +5,9 @@ from narwhals.dependencies import is_narwhals_series if TYPE_CHECKING: - from typing_extensions import Never, TypeAlias + from typing import TypeAlias + + from typing_extensions import Never # NOTE: These aliases are created to facilitate autocompletion. # Feel free to extend them as you please when adding new features. diff --git a/narwhals/translate.py b/narwhals/translate.py index 9452111bdd..fb949adf7a 100644 --- a/narwhals/translate.py +++ b/narwhals/translate.py @@ -3,7 +3,7 @@ import datetime as dt from decimal import Decimal from functools import wraps -from typing import TYPE_CHECKING, Any, Callable, Literal, TypeVar, overload +from typing import TYPE_CHECKING, Any, Literal, TypeVar, overload from narwhals import plugins from narwhals._constants import EPOCH, MS_PER_SECOND @@ -38,6 +38,8 @@ ) if TYPE_CHECKING: + from collections.abc import Callable + from typing_extensions import Unpack from narwhals._translate import ( diff --git a/narwhals/typing.py b/narwhals/typing.py index c99507f380..5dd77931f7 100644 --- a/narwhals/typing.py +++ b/narwhals/typing.py @@ -22,12 +22,12 @@ from collections.abc import Sequence from decimal import Decimal from types import ModuleType + from typing import TypeAlias import numpy as np import pandas as pd import polars as pl import pyarrow as pa - from typing_extensions import TypeAlias from narwhals import dtypes from narwhals.dataframe import DataFrame, LazyFrame diff --git a/pyproject.toml b/pyproject.toml index ed0741f692..fafe07b457 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ build-backend = "hatchling.build" name = "narwhals" version = "2.19.0" dependencies = [] -requires-python = ">=3.9" +requires-python = ">=3.10" authors = [ { name = "Marco Gorelli", email = "hello_narwhals@proton.me" }, ] @@ -135,7 +135,7 @@ include = [ [tool.ruff] line-length = 90 fix = true -target-version = "py39" +target-version = "py310" extend-exclude = ["**/this.py"] [tool.ruff.lint] @@ -378,7 +378,7 @@ warn_return_any = false [tool.pyright] pythonPlatform = "All" # NOTE (`pyarrow-stubs` do unsafe `TypeAlias` and `TypeVar` imports) -# pythonVersion = "3.9" +# pythonVersion = "3.10" reportMissingTypeArgument = "error" reportIncompatibleMethodOverride = "error" reportMissingImports = "none" diff --git a/test-plugin/test_plugin/dataframe.py b/test-plugin/test_plugin/dataframe.py index 74d539b2ec..d7f1f237c7 100644 --- a/test-plugin/test_plugin/dataframe.py +++ b/test-plugin/test_plugin/dataframe.py @@ -11,7 +11,9 @@ from narwhals.typing import CompliantLazyFrame if TYPE_CHECKING: - from typing_extensions import Self, TypeAlias + from typing import TypeAlias + + from typing_extensions import Self from narwhals import LazyFrame # noqa: F401 diff --git a/tests/conftest.py b/tests/conftest.py index 3e80bcdff4..5f0ebdce68 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,7 +5,7 @@ from copy import deepcopy from functools import lru_cache from importlib.util import find_spec -from typing import TYPE_CHECKING, Any, Callable, cast +from typing import TYPE_CHECKING, Any, cast import pytest @@ -14,14 +14,14 @@ from tests.utils import ID_PANDAS_LIKE, PANDAS_VERSION, pyspark_session, sqlframe_session if TYPE_CHECKING: - from collections.abc import Sequence + from collections.abc import Callable, Sequence + from typing import TypeAlias import ibis import pandas as pd import polars as pl import pyarrow as pa from ibis.backends.duckdb import Backend as IbisDuckDBBackend - from typing_extensions import TypeAlias from narwhals._native import NativeDask, NativeDuckDB, NativePySpark, NativeSQLFrame from narwhals._typing import EagerAllowed @@ -199,7 +199,9 @@ def _constructor(obj: Data) -> NativePySpark: index_col_name = generate_temporary_column_name(n_bytes=8, columns=list(_obj)) _obj[index_col_name] = list(range(len(_obj[next(iter(_obj))]))) result = ( - session.createDataFrame([*zip(*_obj.values())], schema=[*_obj.keys()]) + session.createDataFrame( + [*zip(*_obj.values(), strict=False)], schema=[*_obj.keys()] + ) .repartition(2) .orderBy(index_col_name) .drop(index_col_name) @@ -213,7 +215,7 @@ def sqlframe_pyspark_lazy_constructor(obj: Data) -> NativeSQLFrame: # pragma: n pytest.importorskip("sqlframe") pytest.importorskip("duckdb") session = sqlframe_session() - return session.createDataFrame([*zip(*obj.values())], schema=[*obj.keys()]) + return session.createDataFrame([*zip(*obj.values(), strict=False)], schema=list(obj)) @lru_cache(maxsize=1) @@ -311,7 +313,7 @@ def pytest_generate_tests(metafunc: pytest.Metafunc) -> None: elif "constructor_pandas_like" in metafunc.fixturenames: pandas_like_constructors = [] pandas_like_constructors_ids = [] - for fn, name in zip(eager_constructors, eager_constructors_ids): + for fn, name in zip(eager_constructors, eager_constructors_ids, strict=False): if name in ID_PANDAS_LIKE: pandas_like_constructors.append(fn) pandas_like_constructors_ids.append(name) diff --git a/tests/dependencies/is_native_dataframe_series_raise_test.py b/tests/dependencies/is_native_dataframe_series_raise_test.py index 5e22fc6107..a865553822 100644 --- a/tests/dependencies/is_native_dataframe_series_raise_test.py +++ b/tests/dependencies/is_native_dataframe_series_raise_test.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, Callable +from typing import TYPE_CHECKING, Any import pytest @@ -25,6 +25,9 @@ is_pyarrow_table, ) +if TYPE_CHECKING: + from collections.abc import Callable + @pytest.mark.parametrize( "is_native_dataframe", diff --git a/tests/expr_and_series/cast_test.py b/tests/expr_and_series/cast_test.py index 90282ea596..14d27fd1d1 100644 --- a/tests/expr_and_series/cast_test.py +++ b/tests/expr_and_series/cast_test.py @@ -111,7 +111,7 @@ def test_cast(constructor: Constructor) -> None: *[nw.col(col_).cast(dtype) for col_, dtype in cast_map.items()] ).collect_schema() - for (key, ltype), rtype in zip(result.items(), cast_map.values()): + for (key, ltype), rtype in zip(result.items(), cast_map.values(), strict=False): if "modin_constructor" in str(constructor) and key in MODIN_XFAIL_COLUMNS: # TODO(unassigned): in modin we end up with `' None: def test_fill_null_pandas_downcast() -> None: + # !NOTE: pandas 1.4.0 does NOT downcast to 'object' dtype. After filling + # null's, the dtype will be inferred to be 'bool' instead. pytest.importorskip("pandas") import pandas as pd diff --git a/tests/expr_and_series/over_test.py b/tests/expr_and_series/over_test.py index 7fbb8f0f9f..154396bc87 100644 --- a/tests/expr_and_series/over_test.py +++ b/tests/expr_and_series/over_test.py @@ -499,8 +499,6 @@ def test_over_ewm_mean( if any(x in str(constructor_eager) for x in ("pyarrow_table", "modin", "cudf")): # not implemented request.applymarker(pytest.mark.xfail) - if "pandas" in str(constructor_eager) and PANDAS_VERSION < (1, 2): - request.applymarker(pytest.mark.xfail(reason="too old, not implemented")) data = {"a": [0.0, 1.0, 3.0, 5.0, 7.0, 7.5], "b": [1, 1, 1, 2, 2, 2]} diff --git a/tests/expr_and_series/rolling_std_test.py b/tests/expr_and_series/rolling_std_test.py index f027b450d9..b27617623f 100644 --- a/tests/expr_and_series/rolling_std_test.py +++ b/tests/expr_and_series/rolling_std_test.py @@ -8,7 +8,6 @@ import narwhals as nw from tests.utils import ( DUCKDB_VERSION, - PANDAS_VERSION, POLARS_VERSION, Constructor, ConstructorEager, @@ -309,10 +308,8 @@ def test_rolling_std_expr_lazy_grouped( center: bool, ddof: int, ) -> None: - if ( - ("polars" in str(constructor) and POLARS_VERSION < (1, 10)) - or ("duckdb" in str(constructor) and DUCKDB_VERSION < (1, 3)) - or ("pandas" in str(constructor) and PANDAS_VERSION < (1, 2)) + if ("polars" in str(constructor) and POLARS_VERSION < (1, 10)) or ( + "duckdb" in str(constructor) and DUCKDB_VERSION < (1, 3) ): pytest.skip() if any(x in str(constructor) for x in ("dask", "pyarrow_table")): diff --git a/tests/expr_and_series/rolling_var_test.py b/tests/expr_and_series/rolling_var_test.py index b38ba5f077..f80c8764ac 100644 --- a/tests/expr_and_series/rolling_var_test.py +++ b/tests/expr_and_series/rolling_var_test.py @@ -10,7 +10,6 @@ import narwhals as nw from tests.utils import ( DUCKDB_VERSION, - PANDAS_VERSION, POLARS_VERSION, Constructor, ConstructorEager, @@ -264,10 +263,8 @@ def test_rolling_var_expr_lazy_grouped( center: bool, ddof: int, ) -> None: - if ( - ("polars" in str(constructor) and POLARS_VERSION < (1, 10)) - or ("duckdb" in str(constructor) and DUCKDB_VERSION < (1, 3)) - or ("pandas" in str(constructor) and PANDAS_VERSION < (1, 2)) + if ("polars" in str(constructor) and POLARS_VERSION < (1, 10)) or ( + "duckdb" in str(constructor) and DUCKDB_VERSION < (1, 3) ): pytest.skip() if any(x in str(constructor) for x in ("dask", "pyarrow_table")): diff --git a/tests/frame/schema_test.py b/tests/frame/schema_test.py index d90916b029..7039686921 100644 --- a/tests/frame/schema_test.py +++ b/tests/frame/schema_test.py @@ -13,9 +13,9 @@ if TYPE_CHECKING: from collections.abc import Callable, Sequence + from typing import TypeAlias import polars as pl - from typing_extensions import TypeAlias from narwhals.typing import ( DTypeBackend, diff --git a/tests/frame/with_row_index_test.py b/tests/frame/with_row_index_test.py index 10310ca7f6..cfc47b23f7 100644 --- a/tests/frame/with_row_index_test.py +++ b/tests/frame/with_row_index_test.py @@ -7,7 +7,6 @@ import narwhals as nw from tests.utils import ( DUCKDB_VERSION, - PANDAS_VERSION, POLARS_VERSION, Constructor, ConstructorEager, @@ -38,11 +37,6 @@ def test_with_row_index_eager(constructor_eager: ConstructorEager) -> None: def test_with_row_index_lazy( constructor: Constructor, order_by: str | Sequence[str], expected_index: list[int] ) -> None: - if ( - "pandas" in str(constructor) and PANDAS_VERSION < (1, 3) and order_by == "abc" - ): # pragma: no cover - reason = "ValueError: first not supported for non-numeric data." - pytest.skip(reason=reason) if "duckdb" in str(constructor) and DUCKDB_VERSION < (1, 3): pytest.skip() if "polars" in str(constructor) and POLARS_VERSION < (1, 10): @@ -83,9 +77,6 @@ def test_with_row_index_lazy_meaner_examples( # https://github.com/narwhals-dev/narwhals/issues/3289 if "polars" in str(constructor) and POLARS_VERSION < (1, 10): pytest.skip() - if "pandas" in str(constructor) and PANDAS_VERSION < (1, 3): # pragma: no cover - reason = "ValueError: first not supported for non-numeric data." - pytest.skip(reason=reason) if "duckdb" in str(constructor) and DUCKDB_VERSION < (1, 3): pytest.skip() data = {"a": ["A", "B", "A"], "b": [1, 2, 3], "c": [9, 2, 4]} diff --git a/tests/hypothesis/getitem_test.py b/tests/hypothesis/getitem_test.py index 759a292f97..115154ea61 100644 --- a/tests/hypothesis/getitem_test.py +++ b/tests/hypothesis/getitem_test.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Callable, cast +from typing import TYPE_CHECKING, Any, cast import hypothesis.strategies as st import pytest @@ -11,7 +11,7 @@ from tests.utils import assert_equal_data if TYPE_CHECKING: - from collections.abc import Sequence + from collections.abc import Callable, Sequence from narwhals.typing import IntoDataFrame diff --git a/tests/namespace_test.py b/tests/namespace_test.py index e94a6690c1..e0a6c09a23 100644 --- a/tests/namespace_test.py +++ b/tests/namespace_test.py @@ -2,8 +2,8 @@ import re from collections import deque -from collections.abc import Iterable, Sequence -from typing import TYPE_CHECKING, Any, Callable, TypeVar, cast +from collections.abc import Callable, Iterable, Sequence +from typing import TYPE_CHECKING, Any, TypeVar, cast import pytest @@ -12,7 +12,9 @@ from narwhals._utils import Version if TYPE_CHECKING: - from typing_extensions import Never, TypeAlias, assert_type # noqa: F401 + from typing import TypeAlias + + from typing_extensions import Never, assert_type # noqa: F401 from narwhals._arrow.namespace import ArrowNamespace # noqa: F401 from narwhals._compliant import CompliantNamespace diff --git a/tests/preserve_pandas_like_columns_name_attr_test.py b/tests/preserve_pandas_like_columns_name_attr_test.py index 3127040bee..70e7d06e24 100644 --- a/tests/preserve_pandas_like_columns_name_attr_test.py +++ b/tests/preserve_pandas_like_columns_name_attr_test.py @@ -1,12 +1,14 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Callable +from typing import TYPE_CHECKING import pytest import narwhals as nw if TYPE_CHECKING: + from collections.abc import Callable + import pandas as pd diff --git a/tests/read_scan_test.py b/tests/read_scan_test.py index 4548f76a87..2bb8938e0b 100644 --- a/tests/read_scan_test.py +++ b/tests/read_scan_test.py @@ -22,8 +22,7 @@ from collections.abc import Mapping from pathlib import Path from types import ModuleType - - from typing_extensions import TypeAlias + from typing import TypeAlias from narwhals._typing import EagerAllowed, _LazyOnly, _SparkLike from narwhals.typing import FileSource diff --git a/tests/series_only/from_iterable_test.py b/tests/series_only/from_iterable_test.py index 4daac24ea8..6ebb4115c0 100644 --- a/tests/series_only/from_iterable_test.py +++ b/tests/series_only/from_iterable_test.py @@ -20,8 +20,7 @@ Sequence, ValuesView, ) - - from typing_extensions import TypeAlias + from typing import TypeAlias from narwhals._typing import EagerAllowed from narwhals.typing import IntoDType diff --git a/tests/testing/assert_series_equal_test.py b/tests/testing/assert_series_equal_test.py index c4826c695e..778a314c80 100644 --- a/tests/testing/assert_series_equal_test.py +++ b/tests/testing/assert_series_equal_test.py @@ -2,7 +2,7 @@ import re from contextlib import AbstractContextManager, nullcontext as does_not_raise -from typing import TYPE_CHECKING, Any, Callable +from typing import TYPE_CHECKING, Any import pytest @@ -11,7 +11,8 @@ from tests.utils import PANDAS_VERSION, POLARS_VERSION, PYARROW_VERSION if TYPE_CHECKING: - from typing_extensions import TypeAlias + from collections.abc import Callable + from typing import TypeAlias from narwhals.typing import IntoSchema, IntoSeriesT from tests.conftest import Data diff --git a/tests/utils.py b/tests/utils.py index 4d01223b2a..0f3736733a 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -4,24 +4,25 @@ import os import sys import warnings +from collections.abc import Callable from datetime import date, datetime from pathlib import Path -from typing import TYPE_CHECKING, Any, Callable, cast +from typing import TYPE_CHECKING, Any, cast import pytest import narwhals as nw -from narwhals._utils import Implementation, parse_version, zip_strict +from narwhals._utils import Implementation, parse_version from narwhals.dependencies import get_pandas from narwhals.translate import from_native if TYPE_CHECKING: from collections.abc import Mapping, Sequence + from typing import TypeAlias import pandas as pd from pyspark.sql import SparkSession from sqlframe.duckdb import DuckDBSession - from typing_extensions import TypeAlias from narwhals._native import NativeLazyFrame from narwhals.typing import Frame, IntoDataFrame, TimeUnit @@ -112,7 +113,7 @@ def assert_equal_data(result: Any, expected: Mapping[str, Any]) -> None: if hasattr(result, "columns"): for idx, (col, key) in enumerate( - zip_strict(result.columns, list(expected.keys())) + zip(result.columns, list(expected.keys()), strict=True) ): assert col == key, f"Expected column name {key} at index {idx}, found {col}" result = {key: _to_comparable_list(result[key]) for key in expected} @@ -122,7 +123,7 @@ def assert_equal_data(result: Any, expected: Mapping[str, Any]) -> None: for key, expected_value in expected.items(): result_value = result[key] - for i, (lhs, rhs) in enumerate(zip_strict(result_value, expected_value)): + for i, (lhs, rhs) in enumerate(zip(result_value, expected_value, strict=True)): if isinstance(lhs, float) and not math.isnan(lhs): are_equivalent_values = rhs is not None and math.isclose( lhs, rhs, rel_tol=0, abs_tol=1e-6 @@ -135,7 +136,8 @@ def assert_equal_data(result: Any, expected: Mapping[str, Any]) -> None: are_equivalent_values = rhs is None elif isinstance(lhs, list) and isinstance(rhs, list): are_equivalent_values = all( - left_side == right_side for left_side, right_side in zip(lhs, rhs) + left_side == right_side + for left_side, right_side in zip(lhs, rhs, strict=False) ) elif is_pd_na(lhs): are_equivalent_values = is_pd_na(rhs) diff --git a/tests/utils_test.py b/tests/utils_test.py index c8be4131fb..1a6934f98a 100644 --- a/tests/utils_test.py +++ b/tests/utils_test.py @@ -4,7 +4,7 @@ import string from dataclasses import dataclass from itertools import chain -from typing import TYPE_CHECKING, Any, Callable, ClassVar, Protocol, cast +from typing import TYPE_CHECKING, Any, ClassVar, Protocol, cast import hypothesis.strategies as st import pytest @@ -26,7 +26,7 @@ from pandas.testing import assert_frame_equal, assert_index_equal, assert_series_equal if TYPE_CHECKING: - from collections.abc import Iterable, Iterator + from collections.abc import Callable, Iterable, Iterator from types import ModuleType from typing_extensions import Self diff --git a/tests/v1_test.py b/tests/v1_test.py index 9882c4ed15..bfdfd51efa 100644 --- a/tests/v1_test.py +++ b/tests/v1_test.py @@ -5,7 +5,7 @@ from collections import deque from contextlib import nullcontext as does_not_raise from datetime import datetime, timedelta -from typing import TYPE_CHECKING, Any, Callable, cast +from typing import TYPE_CHECKING, Any, cast import pytest @@ -44,7 +44,7 @@ ) if TYPE_CHECKING: - from collections.abc import Sequence + from collections.abc import Callable, Sequence from typing_extensions import assert_type diff --git a/tpch/typing_.py b/tpch/typing_.py index 13fbba2a75..a18dd0cbcb 100644 --- a/tpch/typing_.py +++ b/tpch/typing_.py @@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, Any, Literal, Protocol if TYPE_CHECKING: - from typing_extensions import TypeAlias + from typing import TypeAlias import narwhals as nw diff --git a/utils/check_docstrings.py b/utils/check_docstrings.py index b940a1c352..4c3ffe7b99 100644 --- a/utils/check_docstrings.py +++ b/utils/check_docstrings.py @@ -4,7 +4,6 @@ import ast import doctest -import os import subprocess import sys import sysconfig @@ -36,14 +35,7 @@ def find_ruff_bin() -> Path: if scripts_path.is_file(): return scripts_path - if sys.version_info >= (3, 10): - user_scheme = sysconfig.get_preferred_scheme("user") - elif os.name == "nt": - user_scheme = "nt_user" - elif sys.platform == "darwin" and sys._framework: - user_scheme = "osx_framework_user" - else: - user_scheme = "posix_user" + user_scheme = sysconfig.get_preferred_scheme("user") user_path = Path(sysconfig.get_path("scripts", scheme=user_scheme)) / ruff_exe if user_path.is_file(): diff --git a/utils/generate_random_versions.py b/utils/generate_random_versions.py index 94e61397f1..9c00fca931 100644 --- a/utils/generate_random_versions.py +++ b/utils/generate_random_versions.py @@ -4,8 +4,6 @@ from pathlib import Path PANDAS_AND_NUMPY_VERSION = [ - ("1.1.5", "1.19.5"), - ("1.2.5", "1.20.3"), ("1.3.5", "1.21.6"), ("1.4.4", "1.22.4"), ("1.5.3", "1.23.5"),