From f99f004559e62e0c97012cd4016e284bc566b3a5 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Sun, 5 Oct 2025 19:57:32 +0200 Subject: [PATCH 01/16] chore(ruff): add rules --- .pre-commit-config.yaml | 16 ++++++++-------- pyproject.toml | 42 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 45 insertions(+), 13 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e08722d4d..1f45d60ed 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,14 +2,6 @@ minimum_pre_commit_version: 2.15.0 ci: autofix_prs: false repos: -- repo: https://github.com/psf/black - rev: 25.9.0 - hooks: - - id: black -- repo: https://github.com/PyCQA/isort - rev: 6.0.1 - hooks: - - id: isort - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.13.3 hooks: @@ -21,3 +13,11 @@ repos: - id: codespell additional_dependencies: [ tomli ] args: [-L, "THIRDPARTY"] +- repo: https://github.com/PyCQA/isort + rev: 6.0.1 + hooks: + - id: isort +- repo: https://github.com/psf/black + rev: 25.9.0 + hooks: + - id: black diff --git a/pyproject.toml b/pyproject.toml index 48d2c42f0..9ce85360b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -176,16 +176,48 @@ fix = true [tool.ruff.lint] -extend-select = ["B007", "B018", "PYI", "UP", "RUF100", "RUF059"] +extend-select = ["ALL"] ignore = [ - "E501", # https://docs.astral.sh/ruff/rules/line-too-long/ - "E731", # https://docs.astral.sh/ruff/rules/lambda-assignment/ - "PYI042", # https://docs.astral.sh/ruff/rules/snake-case-type-alias/ + # The following rules are ignored permanently for good reasons + "COM", # https://docs.astral.sh/ruff/rules/#flake8-commas-com + "D", # https://docs.astral.sh/ruff/rules/#pydocstyle-d + "E501", # handled by black https://docs.astral.sh/ruff/rules/line-too-long/ + "EM", # https://docs.astral.sh/ruff/rules/#flake8-errmsg-em + "FBT", # https://docs.astral.sh/ruff/rules/#flake8-boolean-trap-fbt + "I", # handled by isort ] [tool.ruff.lint.per-file-ignores] -"_*.pyi" = ["PYI001"] +"*.pyi" = [ + # The following rules are ignored permanently for good reasons + "A001", # https://docs.astral.sh/ruff/rules/builtin-variable-shadowing/ + "A002", # https://docs.astral.sh/ruff/rules/builtin-argument-shadowing/ + "A004", # https://docs.astral.sh/ruff/rules/builtin-import-shadowing/ + "N", # https://docs.astral.sh/ruff/rules/#pep8-naming-n + "PYI001", # https://docs.astral.sh/ruff/rules/unprefixed-type-param/ + "PYI042", # https://docs.astral.sh/ruff/rules/snake-case-type-alias/ + "TD002", # https://docs.astral.sh/ruff/rules/missing-todo-author/ + # The following rules are ignored temporarily. Either fix them or move to the permanent list above. + "ERA", "S", "ANN", "FIX002", "TD003", "TD004", "PLR0402", "PLC0105" + ] +"scripts/*" = [ + # The following rules are ignored permanently for good reasons + "S603", # https://docs.astral.sh/ruff/rules/subprocess-without-shell-equals-true/ + # The following rules are ignored temporarily. Either fix them or move to the permanent list above. + "TRY", "PT", "BLE" +] +"tests/*" = [ + # The following rules are ignored permanently for good reasons + "E731", # https://docs.astral.sh/ruff/rules/lambda-assignment/ + "PD", # Pandas is here + "PLC0415", # https://docs.astral.sh/ruff/rules/import-outside-top-level/ + "S101", # https://docs.astral.sh/ruff/rules/assert/ + "S301", # https://docs.astral.sh/ruff/rules/suspicious-pickle-usage/ + "SLF001", # https://docs.astral.sh/ruff/rules/private-member-access/ + # The following rules are ignored temporarily. Either fix them or move to the permanent list above. + "ANN", "ARG", "ERA", "RUF", "SIM", "TRY", "PT", "NPY", "N", "DTZ", "PLR", "TC", "PGH", "PTH", "S311", "C901", "FIX", "TD", "A001", "PYI042", "ANN201" +] [tool.mypy] From c664a0d9cdff55a015cafa7f34b9e76be1f0aa56 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Sun, 5 Oct 2025 20:21:49 +0200 Subject: [PATCH 02/16] refactor(ruff): safe --- pandas-stubs/_libs/tslibs/__init__.pyi | 12 ++++++------ pandas-stubs/core/groupby/__init__.pyi | 4 ++-- pandas-stubs/testing.pyi | 2 +- tests/__init__.py | 13 +++++-------- tests/extension/decimal/array.py | 20 ++++++++------------ tests/indexes/test_indexes.py | 4 +++- tests/series/test_series.py | 2 +- tests/test_frame.py | 2 +- tests/test_pandas.py | 2 +- tests/test_timefuncs.py | 2 +- 10 files changed, 29 insertions(+), 34 deletions(-) diff --git a/pandas-stubs/_libs/tslibs/__init__.pyi b/pandas-stubs/_libs/tslibs/__init__.pyi index e760e5285..a090f12cd 100644 --- a/pandas-stubs/_libs/tslibs/__init__.pyi +++ b/pandas-stubs/_libs/tslibs/__init__.pyi @@ -1,14 +1,14 @@ __all__ = [ - "Period", - "Timestamp", - "Timedelta", + "BaseOffset", "NaT", "NaTType", + "OutOfBoundsDatetime", + "Period", + "Tick", + "Timedelta", + "Timestamp", "iNaT", "nat_strings", - "BaseOffset", - "Tick", - "OutOfBoundsDatetime", ] from pandas._libs.tslibs.nattype import ( NaT, diff --git a/pandas-stubs/core/groupby/__init__.pyi b/pandas-stubs/core/groupby/__init__.pyi index 27ed3b004..73131e337 100644 --- a/pandas-stubs/core/groupby/__init__.pyi +++ b/pandas-stubs/core/groupby/__init__.pyi @@ -8,8 +8,8 @@ from pandas.core.groupby.grouper import Grouper as Grouper __all__ = [ "DataFrameGroupBy", - "NamedAgg", - "SeriesGroupBy", "GroupBy", "Grouper", + "NamedAgg", + "SeriesGroupBy", ] diff --git a/pandas-stubs/testing.pyi b/pandas-stubs/testing.pyi index 312d8629d..c0a2b17b8 100644 --- a/pandas-stubs/testing.pyi +++ b/pandas-stubs/testing.pyi @@ -8,6 +8,6 @@ from pandas._testing import ( __all__ = [ "assert_extension_array_equal", "assert_frame_equal", - "assert_series_equal", "assert_index_equal", + "assert_series_equal", ] diff --git a/tests/__init__.py b/tests/__init__.py index 89dd623e2..dd26a62c8 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -87,8 +87,7 @@ def check( and get_origin(shape_type) is tuple and (tuple_args := get_args(shape_type)) and ... not in tuple_args # fixed-length tuple - and (arr_ndim := getattr(actual, "ndim")) - != (expected_ndim := len(tuple_args)) + and (arr_ndim := actual.ndim) != (expected_ndim := len(tuple_args)) ): raise RuntimeError( f"Array has wrong dimension {arr_ndim}, expected {expected_ndim}" @@ -100,7 +99,7 @@ def check( and (dtype_args := get_args(dtype_type)) and isinstance((expected_dtype := dtype_args[0]), type) and issubclass(expected_dtype, np.generic) - and (arr_dtype := getattr(actual, "dtype")) != expected_dtype + and (arr_dtype := actual.dtype) != expected_dtype ): raise RuntimeError( f"Array has wrong dtype {arr_dtype}, expected {expected_dtype.__name__}" @@ -208,8 +207,6 @@ def pytest_warns_bounded( current = Version(version_str) if lb < current < ub: return pytest.warns(warning, match=match) - else: - if upper_exception is None: - return nullcontext() - else: - return suppress(upper_exception) + if upper_exception is None: + return nullcontext() + return suppress(upper_exception) diff --git a/tests/extension/decimal/array.py b/tests/extension/decimal/array.py index b043996f1..c103aa334 100644 --- a/tests/extension/decimal/array.py +++ b/tests/extension/decimal/array.py @@ -150,21 +150,18 @@ def __array_ufunc__(self, ufunc: np.ufunc, method: str, *inputs, **kwargs: Any): def reconstruct(x): if isinstance(x, (decimal.Decimal, numbers.Number)): return x - else: - return DecimalArray._from_sequence(x) + return DecimalArray._from_sequence(x) if ufunc.nout > 1: return tuple(reconstruct(x) for x in result) - else: - return reconstruct(result) + return reconstruct(result) def __getitem__(self, item): if isinstance(item, numbers.Integral): return self._data[item] - else: - # array, slice. - item = check_array_indexer(self, item) - return type(self)(self._data[item]) + # array, slice. + item = check_array_indexer(self, item) + return type(self)(self._data[item]) def take( self, indexer: TakeIndexer, *, allow_fill: bool = False, fill_value=None @@ -208,10 +205,9 @@ def __len__(self) -> int: def __contains__(self, item) -> bool | np.bool_: if not isinstance(item, decimal.Decimal): return False - elif item.is_nan(): + if item.is_nan(): return self.isna().any() - else: - return super().__contains__(item) + return super().__contains__(item) @property def nbytes(self) -> int: @@ -270,7 +266,7 @@ def convert_values(param): # If the operator is not defined for the underlying objects, # a TypeError should be raised - res = [op(a, b) for (a, b) in zip(lvalues, rvalues)] + res = [op(a, b) for (a, b) in zip(lvalues, rvalues, strict=False)] return np.asarray(res, dtype=bool) diff --git a/tests/indexes/test_indexes.py b/tests/indexes/test_indexes.py index 2ba3c0bc4..d4de35096 100644 --- a/tests/indexes/test_indexes.py +++ b/tests/indexes/test_indexes.py @@ -120,7 +120,9 @@ def test_multiindex_constructors() -> None: pd.MultiIndex, ) check( - assert_type(pd.MultiIndex.from_tuples(zip([1, 2], [3, 4])), pd.MultiIndex), + assert_type( + pd.MultiIndex.from_tuples(zip([1, 2], [3, 4], strict=False)), pd.MultiIndex + ), pd.MultiIndex, ) check( diff --git a/tests/series/test_series.py b/tests/series/test_series.py index 0a9aa85ba..5a130f056 100644 --- a/tests/series/test_series.py +++ b/tests/series/test_series.py @@ -2141,7 +2141,7 @@ def func() -> MySeries[float]: def test_change_to_dict_return_type() -> None: id = [1, 2, 3] value = ["a", "b", "c"] - df = pd.DataFrame(zip(id, value), columns=["id", "value"]) + df = pd.DataFrame(zip(id, value, strict=False), columns=["id", "value"]) fd = df.set_index("id")["value"].to_dict() check(assert_type(fd, dict[Any, Any]), dict) diff --git a/tests/test_frame.py b/tests/test_frame.py index e504be7d4..9f9fb242b 100644 --- a/tests/test_frame.py +++ b/tests/test_frame.py @@ -3894,7 +3894,7 @@ def cond1(x: int) -> bool: def test_setitem_loc() -> None: # GH 254 df = pd.DataFrame.from_dict( - {view: (True, True, True) for view in ["A", "B", "C"]}, orient="index" + dict.fromkeys(["A", "B", "C"], (True, True, True)), orient="index" ) df.loc[["A", "C"]] = False my_arr = ["A", "C"] diff --git a/tests/test_pandas.py b/tests/test_pandas.py index 0d9917271..0fd5e74ec 100644 --- a/tests/test_pandas.py +++ b/tests/test_pandas.py @@ -828,7 +828,7 @@ def test_wide_to_long() -> None: "A1980": {0: "d", 1: "e", 2: "f"}, "B1970": {0: 2.5, 1: 1.2, 2: 0.7}, "B1980": {0: 3.2, 1: 1.3, 2: 0.1}, - "X": dict(zip(range(3), np.random.randn(3))), + "X": dict(zip(range(3), np.random.randn(3), strict=False)), } ) df["id"] = df.index diff --git a/tests/test_timefuncs.py b/tests/test_timefuncs.py index 03115f88a..881863afc 100644 --- a/tests/test_timefuncs.py +++ b/tests/test_timefuncs.py @@ -1186,7 +1186,7 @@ def test_to_timedelta_index() -> None: ] arg2 = tuple(arg0) arg3 = tuple(arg1) - arg4 = range(0, 10) + arg4 = range(10) arg5 = np.arange(10) arg6 = pd.Index(arg5) check( From d120bea33f3f33b296dbff0339b13803573fd153 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Sun, 5 Oct 2025 20:22:28 +0200 Subject: [PATCH 03/16] refactor(ruff): unsafe --- scripts/test/__init__.py | 6 ++-- tests/indexes/test_indexes.py | 4 +-- tests/series/test_series.py | 8 ++--- tests/test_errors.py | 58 +++++++++++++++++------------------ tests/test_frame.py | 11 +++---- tests/test_pandas.py | 4 +-- tests/test_plotting.py | 4 +-- 7 files changed, 46 insertions(+), 49 deletions(-) diff --git a/scripts/test/__init__.py b/scripts/test/__init__.py index 57366bdba..eb7412b9a 100644 --- a/scripts/test/__init__.py +++ b/scripts/test/__init__.py @@ -51,7 +51,7 @@ def stubtest(allowlist: str, check_missing: bool, nightly: bool) -> None: steps = _DIST_STEPS[:2] if nightly: steps.append(_step.nightly) - run_job(steps + [stubtest]) + run_job([*steps, stubtest]) def pytest(nightly: bool) -> None: @@ -59,9 +59,9 @@ def pytest(nightly: bool) -> None: pytest_step = _step.pytest if nightly: setup_steps = [_step.nightly] - run_job(setup_steps + [pytest_step]) + run_job([*setup_steps, pytest_step]) def mypy_src(mypy_nightly: bool) -> None: steps = [_step.mypy_nightly] if mypy_nightly else [] - run_job(steps + [_step.mypy_src]) + run_job([*steps, _step.mypy_src]) diff --git a/tests/indexes/test_indexes.py b/tests/indexes/test_indexes.py index d4de35096..6abcec489 100644 --- a/tests/indexes/test_indexes.py +++ b/tests/indexes/test_indexes.py @@ -154,10 +154,10 @@ def test_column_contains() -> None: # https://github.com/microsoft/python-type-stubs/issues/199 df = pd.DataFrame({"A": [1, 2], "B": ["c", "d"], "E": [3, 4]}) - collist = [column for column in df.columns] + collist = list(df.columns) check(assert_type(collist, list[str]), list, str) - collist2 = [column for column in df.columns[df.columns.str.contains("A|B")]] + collist2 = list(df.columns[df.columns.str.contains("A|B")]) check(assert_type(collist2, list[str]), list, str) length = len(df.columns[df.columns.str.contains("A|B")]) diff --git a/tests/series/test_series.py b/tests/series/test_series.py index 5a130f056..32d512e2c 100644 --- a/tests/series/test_series.py +++ b/tests/series/test_series.py @@ -1910,7 +1910,7 @@ def test_resample() -> None: # GH 181 N = 10 index = pd.date_range("1/1/2000", periods=N, freq="min") - x = [x for x in range(N)] + x = list(range(N)) s = pd.Series(x, index=index, dtype=float) check(assert_type(s.resample("2min").std(), "pd.Series[float]"), pd.Series, float) check(assert_type(s.resample("2min").var(), "pd.Series[float]"), pd.Series, float) @@ -2976,7 +2976,7 @@ def test_astype_other() -> None: def test_all_astype_args_tested() -> None: """Check that all relevant numpy type aliases are tested.""" - NUMPY_ALIASES: set[str] = {k for k in np.sctypeDict} + NUMPY_ALIASES: set[str] = set(np.sctypeDict) EXCLUDED_ALIASES = { "datetime64", "m", @@ -3631,7 +3631,7 @@ def test_series_unique_timedelta() -> None: def test_slice_timestamp() -> None: dti = pd.date_range("1/1/2025", "2/28/2025") - s = pd.Series([i for i in range(len(dti))], index=dti) + s = pd.Series(list(range(len(dti))), index=dti) # For `s1`, see discussion in GH 397. Needs mypy fix. # s1 = s.loc["2025-01-15":"2025-01-20"] @@ -3708,7 +3708,7 @@ def test_series_bool_fails() -> None: if s == "foo": # pyright: ignore[reportGeneralTypeIssues] # Next line is unreachable. _a = s[0] - assert False + raise AssertionError except ValueError: pass diff --git a/tests/test_errors.py b/tests/test_errors.py index 673eea08d..23be71fd8 100644 --- a/tests/test_errors.py +++ b/tests/test_errors.py @@ -19,7 +19,7 @@ class Foo: def test_dtype_warning() -> None: with pytest.warns(errors.DtypeWarning): - warnings.warn("", errors.DtypeWarning) + warnings.warn("", errors.DtypeWarning, stacklevel=2) def test_duplicate_label_error() -> None: @@ -29,7 +29,7 @@ def test_duplicate_label_error() -> None: def test_empry_data_error() -> None: with pytest.raises(errors.EmptyDataError): - raise errors.EmptyDataError() + raise errors.EmptyDataError def test_in_casting_nan_error() -> None: @@ -59,69 +59,69 @@ def test_numba_util_error() -> None: def test_option_error() -> None: with pytest.raises(errors.OptionError): - raise errors.OptionError() + raise errors.OptionError def test_out_of_bounds_datetime() -> None: with pytest.raises(errors.OutOfBoundsDatetime): - raise errors.OutOfBoundsDatetime() + raise errors.OutOfBoundsDatetime def test_out_of_bounds_timedelta() -> None: with pytest.raises(errors.OutOfBoundsTimedelta): - raise errors.OutOfBoundsTimedelta() + raise errors.OutOfBoundsTimedelta def test_parser_error() -> None: with pytest.raises(errors.ParserError): - raise errors.ParserError() + raise errors.ParserError def test_parser_warning() -> None: with pytest.warns(errors.ParserWarning): - warnings.warn("", errors.ParserWarning) + warnings.warn("", errors.ParserWarning, stacklevel=2) def test_performance_warning() -> None: with pytest.warns(errors.PerformanceWarning): - warnings.warn("", errors.PerformanceWarning) + warnings.warn("", errors.PerformanceWarning, stacklevel=2) def test_unsorted_index_error() -> None: with pytest.raises(errors.UnsortedIndexError): - raise errors.UnsortedIndexError() + raise errors.UnsortedIndexError def test_unsupported_function_call() -> None: with pytest.raises(errors.UnsupportedFunctionCall): - raise errors.UnsupportedFunctionCall() + raise errors.UnsupportedFunctionCall def test_data_error() -> None: with pytest.raises(errors.DataError): - raise errors.DataError() + raise errors.DataError def test_specification_error() -> None: with pytest.raises(errors.SpecificationError): - raise errors.SpecificationError() + raise errors.SpecificationError def test_setting_with_copy_error() -> None: if PD_LTE_23: with pytest.raises(errors.SettingWithCopyError): - raise errors.SettingWithCopyError() + raise errors.SettingWithCopyError def test_setting_with_copy_warning() -> None: if PD_LTE_23: with pytest.warns(errors.SettingWithCopyWarning): - warnings.warn("", errors.SettingWithCopyWarning) + warnings.warn("", errors.SettingWithCopyWarning, stacklevel=2) def test_numexpr_clobbering_error() -> None: with pytest.raises(errors.NumExprClobberingError): - raise errors.NumExprClobberingError() + raise errors.NumExprClobberingError def test_undefined_variable_error() -> None: @@ -131,12 +131,12 @@ def test_undefined_variable_error() -> None: def test_indexing_error() -> None: with pytest.raises(errors.IndexingError): - raise errors.IndexingError() + raise errors.IndexingError def test_pyperclip_exception() -> None: with pytest.raises(errors.PyperclipException): - raise errors.PyperclipException() + raise errors.PyperclipException @pytest.mark.skipif(not WINDOWS, reason="Windows only") @@ -147,59 +147,59 @@ def test_pyperclip_windows_exception() -> None: def test_css_warning() -> None: with pytest.warns(errors.CSSWarning): - warnings.warn("", errors.CSSWarning) + warnings.warn("", errors.CSSWarning, stacklevel=2) def test_possible_data_loss_error() -> None: with pytest.raises(errors.PossibleDataLossError): - raise errors.PossibleDataLossError() + raise errors.PossibleDataLossError def test_closed_file_error() -> None: with pytest.raises(errors.ClosedFileError): - raise errors.ClosedFileError() + raise errors.ClosedFileError def test_incompatibility_warning() -> None: with pytest.warns(errors.IncompatibilityWarning): - warnings.warn("", errors.IncompatibilityWarning) + warnings.warn("", errors.IncompatibilityWarning, stacklevel=2) def test_attribute_conflict_warning() -> None: with pytest.warns(errors.AttributeConflictWarning): - warnings.warn("", errors.AttributeConflictWarning) + warnings.warn("", errors.AttributeConflictWarning, stacklevel=2) def test_database_error() -> None: with pytest.raises(errors.DatabaseError): - raise errors.DatabaseError() + raise errors.DatabaseError def test_possible_precision_loss() -> None: with pytest.warns(errors.PossiblePrecisionLoss): - warnings.warn("", errors.PossiblePrecisionLoss) + warnings.warn("", errors.PossiblePrecisionLoss, stacklevel=2) def test_value_label_type_mismatch() -> None: with pytest.warns(errors.ValueLabelTypeMismatch): - warnings.warn("", errors.ValueLabelTypeMismatch) + warnings.warn("", errors.ValueLabelTypeMismatch, stacklevel=2) def test_invalid_column_name() -> None: with pytest.warns(errors.InvalidColumnName): - warnings.warn("", errors.InvalidColumnName) + warnings.warn("", errors.InvalidColumnName, stacklevel=2) def test_categorical_conversion_warning() -> None: with pytest.warns(errors.CategoricalConversionWarning): - warnings.warn("", errors.CategoricalConversionWarning) + warnings.warn("", errors.CategoricalConversionWarning, stacklevel=2) def test_invalid_version() -> None: with pytest.raises(errors.InvalidVersion): - raise errors.InvalidVersion() + raise errors.InvalidVersion def test_no_buffer_present() -> None: with pytest.raises(errors.NoBufferPresent): - raise errors.NoBufferPresent() + raise errors.NoBufferPresent diff --git a/tests/test_frame.py b/tests/test_frame.py index 9f9fb242b..612371842 100644 --- a/tests/test_frame.py +++ b/tests/test_frame.py @@ -437,7 +437,7 @@ def test_types_filter() -> None: check(assert_type(df.filter(like="1"), pd.DataFrame), pd.DataFrame) # GH964 Docs state `items` is `list-like` check( - assert_type(df.filter(items=("col2", "col2", 1, tuple([4]))), pd.DataFrame), + assert_type(df.filter(items=("col2", "col2", 1, (4,))), pd.DataFrame), pd.DataFrame, ) @@ -3637,9 +3637,9 @@ def sample_to_df(x: pd.DataFrame) -> pd.DataFrame: def test_resample() -> None: # GH 181 N = 10 - x = [x for x in range(N)] + x = list(range(N)) index = pd.date_range("1/1/2000", periods=N, freq="min") - x = [x for x in range(N)] + x = list(range(N)) df = pd.DataFrame({"a": x, "b": x, "c": x}, index=index) check(assert_type(df.resample("2min").std(), pd.DataFrame), pd.DataFrame) check(assert_type(df.resample("2min").var(), pd.DataFrame), pd.DataFrame) @@ -4129,11 +4129,10 @@ def select3(_: pd.DataFrame) -> int: def test_npint_loc_indexer() -> None: # GH 508 - df = pd.DataFrame(dict(x=[1, 2, 3]), index=np.array([10, 20, 30], dtype="uint64")) + df = pd.DataFrame({"x": [1, 2, 3]}, index=np.array([10, 20, 30], dtype="uint64")) def get_NDArray(df: pd.DataFrame, key: npt.NDArray[np.uint64]) -> pd.DataFrame: - df2 = df.loc[key] - return df2 + return df.loc[key] a: npt.NDArray[np.uint64] = np.array([10, 30], dtype="uint64") check(assert_type(get_NDArray(df, a), pd.DataFrame), pd.DataFrame) diff --git a/tests/test_pandas.py b/tests/test_pandas.py index 0fd5e74ec..cd92ffb6d 100644 --- a/tests/test_pandas.py +++ b/tests/test_pandas.py @@ -241,9 +241,7 @@ def test_types_concat() -> None: check(assert_type(pd.concat({1: df, None: df2}), pd.DataFrame), pd.DataFrame) check( - assert_type( - pd.concat(map(lambda _: s2, ["some_value", 3]), axis=1), pd.DataFrame - ), + assert_type(pd.concat((s2 for _ in ["some_value", 3]), axis=1), pd.DataFrame), pd.DataFrame, ) adict = {"a": df, 2: df2} diff --git a/tests/test_plotting.py b/tests/test_plotting.py index a27725059..46d5b37a4 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -594,7 +594,7 @@ def test_plot_subplot_changes_150() -> None: def test_grouped_dataframe_boxplot(close_figures) -> None: - tuples = [t for t in itertools.product(range(10), range(2))] + tuples = list(itertools.product(range(10), range(2))) index = pd.MultiIndex.from_tuples(tuples, names=["lvl0", "lvl1"]) df = pd.DataFrame( data=np.random.randn(len(index), 2), columns=["A", "B"], index=index @@ -630,7 +630,7 @@ def test_grouped_dataframe_boxplot_single(close_figures) -> None: is put separately to make sure that we have no Axes already created. It will fail with `orientation="horizontal"`. """ - tuples = [t for t in itertools.product(range(10), range(2))] + tuples = list(itertools.product(range(10), range(2))) index = pd.MultiIndex.from_tuples(tuples, names=["lvl0", "lvl1"]) df = pd.DataFrame( data=np.random.randn(len(index), 2), columns=["A", "B"], index=index From 1b34b9953104c2cc880bf70120b143c6439691f6 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Mon, 6 Oct 2025 12:55:03 +0200 Subject: [PATCH 04/16] fix: typing --- tests/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/__init__.py b/tests/__init__.py index dd26a62c8..d58d4fffb 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -87,7 +87,7 @@ def check( and get_origin(shape_type) is tuple and (tuple_args := get_args(shape_type)) and ... not in tuple_args # fixed-length tuple - and (arr_ndim := actual.ndim) != (expected_ndim := len(tuple_args)) + and (arr_ndim := actual.ndim) != (expected_ndim := len(tuple_args)) # type: ignore[attr-defined] # pyright: ignore[reportAttributeAccessIssue] ): raise RuntimeError( f"Array has wrong dimension {arr_ndim}, expected {expected_ndim}" @@ -99,7 +99,7 @@ def check( and (dtype_args := get_args(dtype_type)) and isinstance((expected_dtype := dtype_args[0]), type) and issubclass(expected_dtype, np.generic) - and (arr_dtype := actual.dtype) != expected_dtype + and (arr_dtype := actual.dtype) != expected_dtype # type: ignore[attr-defined] # pyright: ignore[reportAttributeAccessIssue] ): raise RuntimeError( f"Array has wrong dtype {arr_dtype}, expected {expected_dtype.__name__}" From 85ab0f97c59b7aa7f5f58d5323f91f6063fb192c Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Tue, 7 Oct 2025 15:55:45 +0200 Subject: [PATCH 05/16] fix: conftest --- conftest.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/conftest.py b/conftest.py index 9dc46f43b..ce5187a65 100644 --- a/conftest.py +++ b/conftest.py @@ -1,10 +1,11 @@ +from collections.abc import Generator import gc import pytest @pytest.fixture -def mpl_cleanup(): +def mpl_cleanup() -> Generator[None, None, None]: """ Ensure Matplotlib is cleaned up around a test. From 6847e8657878201653b49f401c18b72e680b9eb9 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Tue, 7 Oct 2025 16:46:39 +0200 Subject: [PATCH 06/16] fix: pyrefly --- pandas-stubs/_typing.pyi | 1 + pandas-stubs/core/arrays/categorical.pyi | 2 ++ pandas-stubs/core/arrays/datetimelike.pyi | 1 + pandas-stubs/core/arrays/interval.pyi | 1 + pandas-stubs/core/arrays/sparse/array.pyi | 1 + pandas-stubs/core/frame.pyi | 8 ++++++++ pandas-stubs/core/groupby/generic.pyi | 3 +++ pandas-stubs/core/indexes/datetimes.pyi | 1 + pandas-stubs/core/indexes/interval.pyi | 1 + pandas-stubs/core/indexes/multi.pyi | 1 + pandas-stubs/core/indexes/period.pyi | 2 ++ pandas-stubs/core/indexes/range.pyi | 1 + pandas-stubs/core/indexes/timedeltas.pyi | 5 +++++ pandas-stubs/core/series.pyi | 9 +++++++++ pyproject.toml | 2 +- 15 files changed, 38 insertions(+), 1 deletion(-) diff --git a/pandas-stubs/_typing.pyi b/pandas-stubs/_typing.pyi index 887d2a240..0d9bc9627 100644 --- a/pandas-stubs/_typing.pyi +++ b/pandas-stubs/_typing.pyi @@ -1080,6 +1080,7 @@ Incomplete: TypeAlias = Any class Just(Protocol, Generic[T]): @property # type: ignore[override] @override + # pyrefly: ignore # bad-override def __class__(self, /) -> type[T]: ... @__class__.setter @override diff --git a/pandas-stubs/core/arrays/categorical.pyi b/pandas-stubs/core/arrays/categorical.pyi index 0992f1a56..64b56047c 100644 --- a/pandas-stubs/core/arrays/categorical.pyi +++ b/pandas-stubs/core/arrays/categorical.pyi @@ -114,8 +114,10 @@ class Categorical(ExtensionArray): ) -> Categorical: ... def __len__(self) -> int: ... def __iter__(self): ... + # pyrefly: ignore # bad-param-name-override def __contains__(self, key) -> bool: ... @overload + # pyrefly: ignore # bad-param-name-override def __getitem__(self, key: ScalarIndexer) -> Any: ... @overload def __getitem__( diff --git a/pandas-stubs/core/arrays/datetimelike.pyi b/pandas-stubs/core/arrays/datetimelike.pyi index 74f62e405..44c6f0bd6 100644 --- a/pandas-stubs/core/arrays/datetimelike.pyi +++ b/pandas-stubs/core/arrays/datetimelike.pyi @@ -71,6 +71,7 @@ class DatetimeLikeArrayMixin(ExtensionOpsMixin, ExtensionArray): def size(self) -> int: ... def __len__(self) -> int: ... @overload + # pyrefly: ignore # bad-param-name-override def __getitem__(self, key: ScalarIndexer) -> DTScalarOrNaT: ... @overload def __getitem__( diff --git a/pandas-stubs/core/arrays/interval.pyi b/pandas-stubs/core/arrays/interval.pyi index ad97dd547..744a33d7d 100644 --- a/pandas-stubs/core/arrays/interval.pyi +++ b/pandas-stubs/core/arrays/interval.pyi @@ -60,6 +60,7 @@ class IntervalArray(IntervalMixin, ExtensionArray): def __iter__(self): ... def __len__(self) -> int: ... @overload + # pyrefly: ignore # bad-param-name-override def __getitem__(self, key: ScalarIndexer) -> IntervalOrNA: ... @overload def __getitem__(self, key: SequenceIndexer) -> Self: ... diff --git a/pandas-stubs/core/arrays/sparse/array.pyi b/pandas-stubs/core/arrays/sparse/array.pyi index 433a56dfd..8114269ad 100644 --- a/pandas-stubs/core/arrays/sparse/array.pyi +++ b/pandas-stubs/core/arrays/sparse/array.pyi @@ -60,6 +60,7 @@ class SparseArray(ExtensionArray, ExtensionOpsMixin): def unique(self): ... def value_counts(self, dropna: bool = True): ... @overload + # pyrefly: ignore # bad-param-name-override def __getitem__(self, key: ScalarIndexer) -> Any: ... @overload def __getitem__( diff --git a/pandas-stubs/core/frame.pyi b/pandas-stubs/core/frame.pyi index e330064ee..5e2cbc2f3 100644 --- a/pandas-stubs/core/frame.pyi +++ b/pandas-stubs/core/frame.pyi @@ -172,6 +172,7 @@ _T_MUTABLE_MAPPING = TypeVar("_T_MUTABLE_MAPPING", bound=MutableMapping, covaria class _iLocIndexerFrame(_iLocIndexer, Generic[_T]): @overload + # pyrefly: ignore # bad-param-name-override def __getitem__(self, idx: tuple[int, int]) -> Scalar: ... @overload def __getitem__(self, idx: IndexingInt) -> Series: ... @@ -189,6 +190,7 @@ class _iLocIndexerFrame(_iLocIndexer, Generic[_T]): | tuple[slice] ), ) -> _T: ... + # pyrefly: ignore # bad-param-name-override def __setitem__( self, idx: ( @@ -213,6 +215,7 @@ class _iLocIndexerFrame(_iLocIndexer, Generic[_T]): class _LocIndexerFrame(_LocIndexer, Generic[_T]): @overload + # pyrefly: ignore # bad-param-name-override def __getitem__(self, idx: Scalar) -> Series | _T: ... @overload def __getitem__( # type: ignore[overload-overlap] @@ -266,6 +269,7 @@ class _LocIndexerFrame(_LocIndexer, Generic[_T]): @overload def __getitem__(self, idx: tuple[Scalar, slice]) -> Series | _T: ... @overload + # pyrefly: ignore # bad-param-name-override def __setitem__( self, idx: ( @@ -291,7 +295,9 @@ class _LocIndexerFrame(_LocIndexer, Generic[_T]): ) -> None: ... class _iAtIndexerFrame(_iAtIndexer): + # pyrefly: ignore # bad-param-name-override def __getitem__(self, idx: tuple[int, int]) -> Scalar: ... + # pyrefly: ignore # bad-param-name-override def __setitem__( self, idx: tuple[int, int], @@ -299,6 +305,7 @@ class _iAtIndexerFrame(_iAtIndexer): ) -> None: ... class _AtIndexerFrame(_AtIndexer): + # pyrefly: ignore # bad-param-name-override def __getitem__( self, idx: tuple[ @@ -310,6 +317,7 @@ class _AtIndexerFrame(_AtIndexer): int | StrLike | tuple[Scalar, ...], ], ) -> Scalar: ... + # pyrefly: ignore # bad-param-name-override def __setitem__( self, idx: ( diff --git a/pandas-stubs/core/groupby/generic.pyi b/pandas-stubs/core/groupby/generic.pyi index 40670c59d..38c2d09da 100644 --- a/pandas-stubs/core/groupby/generic.pyi +++ b/pandas-stubs/core/groupby/generic.pyi @@ -204,6 +204,7 @@ class SeriesGroupBy(GroupBy[Series[S2]], Generic[S2, ByT]): def unique(self) -> Series: ... # Overrides that provide more precise return types over the GroupBy class @final # type: ignore[misc] + # pyrefly: ignore # bad-override def __iter__( # pyright: ignore[reportIncompatibleMethodOverride] self, ) -> Iterator[tuple[ByT, Series[S2]]]: ... @@ -448,9 +449,11 @@ class DataFrameGroupBy(GroupBy[DataFrame], Generic[ByT, _TT]): ) -> Series: ... # Series[Axes] but this is not allowed @property def dtypes(self) -> Series: ... + # pyrefly: ignore # bad-param-name-override def __getattr__(self, name: str) -> SeriesGroupBy[Any, ByT]: ... # Overrides that provide more precise return types over the GroupBy class @final # type: ignore[misc] + # pyrefly: ignore # bad-override def __iter__( # pyright: ignore[reportIncompatibleMethodOverride] self, ) -> Iterator[tuple[ByT, DataFrame]]: ... diff --git a/pandas-stubs/core/indexes/datetimes.pyi b/pandas-stubs/core/indexes/datetimes.pyi index 16e1a8c22..94710415a 100644 --- a/pandas-stubs/core/indexes/datetimes.pyi +++ b/pandas-stubs/core/indexes/datetimes.pyi @@ -68,6 +68,7 @@ class DatetimeIndex( self, other: timedelta | BaseOffset ) -> Self: ... @overload # type: ignore[override] + # pyrefly: ignore # bad-override def __sub__( self, other: datetime | np.datetime64 | np_ndarray_dt | Self ) -> TimedeltaIndex: ... diff --git a/pandas-stubs/core/indexes/interval.pyi b/pandas-stubs/core/indexes/interval.pyi index 43c3595cd..d53d5bba2 100644 --- a/pandas-stubs/core/indexes/interval.pyi +++ b/pandas-stubs/core/indexes/interval.pyi @@ -239,6 +239,7 @@ class IntervalIndex(ExtensionIndex[IntervalT, np.object_], IntervalMixin): @property def length(self) -> Index: ... @overload # type: ignore[override] + # pyrefly: ignore # bad-override def __getitem__( self, idx: ( diff --git a/pandas-stubs/core/indexes/multi.pyi b/pandas-stubs/core/indexes/multi.pyi index b64cd1f2d..e66c845ec 100644 --- a/pandas-stubs/core/indexes/multi.pyi +++ b/pandas-stubs/core/indexes/multi.pyi @@ -123,6 +123,7 @@ class MultiIndex(Index): def levshape(self): ... def __reduce__(self): ... @overload # type: ignore[override] + # pyrefly: ignore # bad-override def __getitem__( self, idx: slice | np_ndarray_anyint | Sequence[int] | Index | MaskType, diff --git a/pandas-stubs/core/indexes/period.pyi b/pandas-stubs/core/indexes/period.pyi index 158f38698..84abc62ea 100644 --- a/pandas-stubs/core/indexes/period.pyi +++ b/pandas-stubs/core/indexes/period.pyi @@ -43,6 +43,7 @@ class PeriodIndex(DatetimeIndexOpsMixin[pd.Period, np.object_], PeriodIndexField self, other: datetime.timedelta ) -> Self: ... @overload # type: ignore[override] + # pyrefly: ignore # bad-override def __sub__(self, other: Period) -> Index: ... @overload def __sub__(self, other: Self) -> Index: ... @@ -55,6 +56,7 @@ class PeriodIndex(DatetimeIndexOpsMixin[pd.Period, np.object_], PeriodIndexField self, other: TimedeltaIndex | pd.Timedelta ) -> Self: ... @overload # type: ignore[override] + # pyrefly: ignore # bad-override def __rsub__(self, other: Period) -> Index: ... @overload def __rsub__(self, other: Self) -> Index: ... diff --git a/pandas-stubs/core/indexes/range.pyi b/pandas-stubs/core/indexes/range.pyi index 5e4055fb8..5f5e13013 100644 --- a/pandas-stubs/core/indexes/range.pyi +++ b/pandas-stubs/core/indexes/range.pyi @@ -85,6 +85,7 @@ class RangeIndex(_IndexSubclassBase[int, np.int64]): self, other: list[HashableT] | Index, sort: bool | None = None ) -> Index | Index[int] | RangeIndex: ... @overload # type: ignore[override] + # pyrefly: ignore # bad-override def __getitem__( self, idx: slice | np_ndarray_anyint | Sequence[int] | Index | MaskType, diff --git a/pandas-stubs/core/indexes/timedeltas.pyi b/pandas-stubs/core/indexes/timedeltas.pyi index 5b0915c6b..de2bee2bd 100644 --- a/pandas-stubs/core/indexes/timedeltas.pyi +++ b/pandas-stubs/core/indexes/timedeltas.pyi @@ -56,6 +56,7 @@ class TimedeltaIndex( # various ignores needed for mypy, as we do want to restrict what can be used in # arithmetic for these types @overload # type: ignore[override] + # pyrefly: ignore # bad-override def __add__(self, other: Period) -> PeriodIndex: ... @overload def __add__(self, other: dt.datetime | DatetimeIndex) -> DatetimeIndex: ... @@ -64,6 +65,7 @@ class TimedeltaIndex( self, other: dt.timedelta | Self ) -> Self: ... @overload # type: ignore[override] + # pyrefly: ignore # bad-override def __radd__(self, other: Period) -> PeriodIndex: ... @overload def __radd__(self, other: dt.datetime | DatetimeIndex) -> DatetimeIndex: ... @@ -75,6 +77,7 @@ class TimedeltaIndex( self, other: dt.timedelta | np.timedelta64 | np_ndarray_td | BaseOffset | Self ) -> Self: ... @overload # type: ignore[override] + # pyrefly: ignore # bad-override def __rsub__( self, other: dt.timedelta | np.timedelta64 | np_ndarray_td | BaseOffset | Self ) -> Self: ... @@ -115,6 +118,7 @@ class TimedeltaIndex( ), ) -> Self: ... @overload # type: ignore[override] + # pyrefly: ignore # bad-override def __truediv__(self, other: float | Sequence[float]) -> Self: ... @overload def __truediv__( # pyright: ignore[reportIncompatibleMethodOverride] @@ -122,6 +126,7 @@ class TimedeltaIndex( ) -> Index[float]: ... def __rtruediv__(self, other: dt.timedelta | Sequence[dt.timedelta]) -> Index[float]: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] @overload # type: ignore[override] + # pyrefly: ignore # bad-override def __floordiv__(self, other: num | Sequence[float]) -> Self: ... @overload def __floordiv__( # pyright: ignore[reportIncompatibleMethodOverride] diff --git a/pandas-stubs/core/series.pyi b/pandas-stubs/core/series.pyi index a0513eaf4..445f2a17d 100644 --- a/pandas-stubs/core/series.pyi +++ b/pandas-stubs/core/series.pyi @@ -234,11 +234,13 @@ class SupportsTruedivInt(Protocol[_T_co]): class _iLocIndexerSeries(_iLocIndexer, Generic[S1]): # get item @overload + # pyrefly: ignore # bad-param-name-override def __getitem__(self, idx: IndexingInt) -> S1: ... @overload def __getitem__(self, idx: Index | slice | np_ndarray_anyint) -> Series[S1]: ... # set item @overload + # pyrefly: ignore # bad-param-name-override def __setitem__(self, idx: int, value: S1 | None) -> None: ... @overload def __setitem__( @@ -272,6 +274,7 @@ class _LocIndexerSeries(_LocIndexer, Generic[S1]): # be s.loc[1, :], or s.loc[pd.IndexSlice[1, :]] ) -> Series[S1]: ... @overload + # pyrefly: ignore # bad-param-name-override def __setitem__( self, idx: Index | MaskType | slice, @@ -2208,6 +2211,7 @@ class Series(IndexOpsMixin[S1], NDFrame): ) -> Series[_str]: ... # ignore needed for mypy as we want different results based on the arguments @overload # type: ignore[override] + # pyrefly: ignore # bad-override def __and__( # pyright: ignore[reportOverlappingOverload] self, other: bool | list[int] | MaskType ) -> Series[bool]: ... @@ -3060,6 +3064,7 @@ class Series(IndexOpsMixin[S1], NDFrame): def __pow__(self, other: num | _ListLike | Series[S1]) -> Series[S1]: ... # ignore needed for mypy as we want different results based on the arguments @overload # type: ignore[override] + # pyrefly: ignore # bad-override def __or__( # pyright: ignore[reportOverlappingOverload] self, other: bool | list[int] | MaskType ) -> Series[bool]: ... @@ -3067,6 +3072,7 @@ class Series(IndexOpsMixin[S1], NDFrame): def __or__(self, other: int | np_ndarray_anyint | Series[int]) -> Series[int]: ... # ignore needed for mypy as we want different results based on the arguments @overload # type: ignore[override] + # pyrefly: ignore # bad-override def __rand__( # pyright: ignore[reportOverlappingOverload] self, other: bool | MaskType | list[int] ) -> Series[bool]: ... @@ -3077,6 +3083,7 @@ class Series(IndexOpsMixin[S1], NDFrame): def __rpow__(self, other: num | _ListLike | Series[S1]) -> Series[S1]: ... # ignore needed for mypy as we want different results based on the arguments @overload # type: ignore[override] + # pyrefly: ignore # bad-override def __ror__( # pyright: ignore[reportOverlappingOverload] self, other: bool | MaskType | list[int] ) -> Series[bool]: ... @@ -3084,6 +3091,7 @@ class Series(IndexOpsMixin[S1], NDFrame): def __ror__(self, other: int | np_ndarray_anyint | Series[int]) -> Series[int]: ... # ignore needed for mypy as we want different results based on the arguments @overload # type: ignore[override] + # pyrefly: ignore # bad-override def __rxor__( # pyright: ignore[reportOverlappingOverload] self, other: bool | MaskType | list[int] ) -> Series[bool]: ... @@ -4244,6 +4252,7 @@ class Series(IndexOpsMixin[S1], NDFrame): rdiv = rtruediv # ignore needed for mypy as we want different results based on the arguments @overload # type: ignore[override] + # pyrefly: ignore # bad-override def __xor__( # pyright: ignore[reportOverlappingOverload] self, other: bool | MaskType | list[int] ) -> Series[bool]: ... diff --git a/pyproject.toml b/pyproject.toml index 9ce85360b..3a0112fdf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,7 +42,7 @@ pyarrow = ">=10.0.1" pytest = ">=8.4.2" pyright = ">=1.1.406" ty = ">=0.0.1a21" -pyrefly = ">=0.35.0" +pyrefly = ">=0.36.1" poethepoet = ">=0.16.5" loguru = ">=0.6.0" typing-extensions = ">=4.4.0" From 06f1136a85239969d731d2eb4b06b8edb0de6040 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Tue, 7 Oct 2025 16:46:39 +0200 Subject: [PATCH 07/16] fix: pyrefly --- pyproject.toml | 1 + tests/extension/decimal/array.py | 2 +- tests/indexes/test_indexes.py | 4 +--- tests/series/test_series.py | 2 +- tests/test_pandas.py | 2 +- 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 3a0112fdf..8b18ccbed 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -209,6 +209,7 @@ ignore = [ ] "tests/*" = [ # The following rules are ignored permanently for good reasons + "B905", # https://docs.astral.sh/ruff/rules/zip-without-explicit-strict/ "E731", # https://docs.astral.sh/ruff/rules/lambda-assignment/ "PD", # Pandas is here "PLC0415", # https://docs.astral.sh/ruff/rules/import-outside-top-level/ diff --git a/tests/extension/decimal/array.py b/tests/extension/decimal/array.py index c103aa334..ad3a5ae17 100644 --- a/tests/extension/decimal/array.py +++ b/tests/extension/decimal/array.py @@ -266,7 +266,7 @@ def convert_values(param): # If the operator is not defined for the underlying objects, # a TypeError should be raised - res = [op(a, b) for (a, b) in zip(lvalues, rvalues, strict=False)] + res = [op(a, b) for (a, b) in zip(lvalues, rvalues)] return np.asarray(res, dtype=bool) diff --git a/tests/indexes/test_indexes.py b/tests/indexes/test_indexes.py index 6abcec489..892cea004 100644 --- a/tests/indexes/test_indexes.py +++ b/tests/indexes/test_indexes.py @@ -120,9 +120,7 @@ def test_multiindex_constructors() -> None: pd.MultiIndex, ) check( - assert_type( - pd.MultiIndex.from_tuples(zip([1, 2], [3, 4], strict=False)), pd.MultiIndex - ), + assert_type(pd.MultiIndex.from_tuples(zip([1, 2], [3, 4])), pd.MultiIndex), pd.MultiIndex, ) check( diff --git a/tests/series/test_series.py b/tests/series/test_series.py index 32d512e2c..0c9aad503 100644 --- a/tests/series/test_series.py +++ b/tests/series/test_series.py @@ -2141,7 +2141,7 @@ def func() -> MySeries[float]: def test_change_to_dict_return_type() -> None: id = [1, 2, 3] value = ["a", "b", "c"] - df = pd.DataFrame(zip(id, value, strict=False), columns=["id", "value"]) + df = pd.DataFrame(zip(id, value), columns=["id", "value"]) fd = df.set_index("id")["value"].to_dict() check(assert_type(fd, dict[Any, Any]), dict) diff --git a/tests/test_pandas.py b/tests/test_pandas.py index cd92ffb6d..519ba491c 100644 --- a/tests/test_pandas.py +++ b/tests/test_pandas.py @@ -826,7 +826,7 @@ def test_wide_to_long() -> None: "A1980": {0: "d", 1: "e", 2: "f"}, "B1970": {0: 2.5, 1: 1.2, 2: 0.7}, "B1980": {0: 3.2, 1: 1.3, 2: 0.1}, - "X": dict(zip(range(3), np.random.randn(3), strict=False)), + "X": dict(zip(range(3), np.random.randn(3))), } ) df["id"] = df.index From 3e7bb89e87b860d259985677dce630ccdbe226d9 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Tue, 7 Oct 2025 23:33:35 +0200 Subject: [PATCH 08/16] fix(comment): C417 https://github.com/pandas-dev/pandas-stubs/pull/1412/files#r2410872686 --- tests/test_pandas.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/test_pandas.py b/tests/test_pandas.py index 519ba491c..ac3d6ec2a 100644 --- a/tests/test_pandas.py +++ b/tests/test_pandas.py @@ -241,7 +241,10 @@ def test_types_concat() -> None: check(assert_type(pd.concat({1: df, None: df2}), pd.DataFrame), pd.DataFrame) check( - assert_type(pd.concat((s2 for _ in ["some_value", 3]), axis=1), pd.DataFrame), + assert_type( + pd.concat(map(lambda _: s2, ["some_value", 3]), axis=1), # noqa: C417 + pd.DataFrame, + ), pd.DataFrame, ) adict = {"a": df, 2: df2} From faa516e047154a203016bad42c4607385e212eb4 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Tue, 7 Oct 2025 23:40:14 +0200 Subject: [PATCH 09/16] chore(ruff): SLF001 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 8b18ccbed..334c550a6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -215,8 +215,8 @@ ignore = [ "PLC0415", # https://docs.astral.sh/ruff/rules/import-outside-top-level/ "S101", # https://docs.astral.sh/ruff/rules/assert/ "S301", # https://docs.astral.sh/ruff/rules/suspicious-pickle-usage/ - "SLF001", # https://docs.astral.sh/ruff/rules/private-member-access/ # The following rules are ignored temporarily. Either fix them or move to the permanent list above. + "SLF001", # https://docs.astral.sh/ruff/rules/private-member-access/ "ANN", "ARG", "ERA", "RUF", "SIM", "TRY", "PT", "NPY", "N", "DTZ", "PLR", "TC", "PGH", "PTH", "S311", "C901", "FIX", "TD", "A001", "PYI042", "ANN201" ] From 2e81ce6ce3dedf6d6d4269ba1f9a8908ea694b8a Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Tue, 7 Oct 2025 23:42:16 +0200 Subject: [PATCH 10/16] feat(ruff): S301 --- pyproject.toml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 334c550a6..67e3fcf5c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -214,11 +214,14 @@ ignore = [ "PD", # Pandas is here "PLC0415", # https://docs.astral.sh/ruff/rules/import-outside-top-level/ "S101", # https://docs.astral.sh/ruff/rules/assert/ - "S301", # https://docs.astral.sh/ruff/rules/suspicious-pickle-usage/ # The following rules are ignored temporarily. Either fix them or move to the permanent list above. "SLF001", # https://docs.astral.sh/ruff/rules/private-member-access/ "ANN", "ARG", "ERA", "RUF", "SIM", "TRY", "PT", "NPY", "N", "DTZ", "PLR", "TC", "PGH", "PTH", "S311", "C901", "FIX", "TD", "A001", "PYI042", "ANN201" ] +"tests/test_io.py" = [ + # The following rules are ignored permanently for good reasons + "S301", # https://docs.astral.sh/ruff/rules/suspicious-pickle-usage/ +] [tool.mypy] From 5cb4685fe67228f6c5b18db42d9660f88468cce9 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Tue, 7 Oct 2025 23:43:39 +0200 Subject: [PATCH 11/16] fix(ruff): TD004 --- pandas-stubs/io/orc.pyi | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas-stubs/io/orc.pyi b/pandas-stubs/io/orc.pyi index 7039598e7..489ef433e 100644 --- a/pandas-stubs/io/orc.pyi +++ b/pandas-stubs/io/orc.pyi @@ -14,7 +14,7 @@ def read_orc( path: FilePath | ReadBuffer[bytes], columns: list[HashableT] | None = None, dtype_backend: DtypeBackend | _NoDefaultDoNotUse = "numpy_nullable", - # TODO type with the correct pyarrow types + # TODO: type with the correct pyarrow types # filesystem: pyarrow.fs.FileSystem | fsspec.spec.AbstractFileSystem filesystem: Any | None = None, **kwargs: Any, diff --git a/pyproject.toml b/pyproject.toml index 67e3fcf5c..740ff6fce 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -199,7 +199,7 @@ ignore = [ "PYI042", # https://docs.astral.sh/ruff/rules/snake-case-type-alias/ "TD002", # https://docs.astral.sh/ruff/rules/missing-todo-author/ # The following rules are ignored temporarily. Either fix them or move to the permanent list above. - "ERA", "S", "ANN", "FIX002", "TD003", "TD004", "PLR0402", "PLC0105" + "ERA", "S", "ANN", "FIX002", "TD003", "PLR0402", "PLC0105" ] "scripts/*" = [ # The following rules are ignored permanently for good reasons From 83518db58a4aba36a32975ebd7dc9c5343c84d07 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Tue, 7 Oct 2025 23:45:26 +0200 Subject: [PATCH 12/16] chore(ruff): ERA, S --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 740ff6fce..f9fefb65f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -199,7 +199,7 @@ ignore = [ "PYI042", # https://docs.astral.sh/ruff/rules/snake-case-type-alias/ "TD002", # https://docs.astral.sh/ruff/rules/missing-todo-author/ # The following rules are ignored temporarily. Either fix them or move to the permanent list above. - "ERA", "S", "ANN", "FIX002", "TD003", "PLR0402", "PLC0105" + "ERA001", "ANN", "FIX002", "TD003", "PLR0402", "PLC0105" ] "scripts/*" = [ # The following rules are ignored permanently for good reasons From b6394f9e9d7555b4a5ebddfb396926858da1a584 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Tue, 7 Oct 2025 23:59:24 +0200 Subject: [PATCH 13/16] fix(ruff): tweak rules --- pyproject.toml | 21 ++++++++++++++------- tests/test_pandas.py | 2 +- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index f9fefb65f..252a75a07 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -182,7 +182,6 @@ ignore = [ "COM", # https://docs.astral.sh/ruff/rules/#flake8-commas-com "D", # https://docs.astral.sh/ruff/rules/#pydocstyle-d "E501", # handled by black https://docs.astral.sh/ruff/rules/line-too-long/ - "EM", # https://docs.astral.sh/ruff/rules/#flake8-errmsg-em "FBT", # https://docs.astral.sh/ruff/rules/#flake8-boolean-trap-fbt "I", # handled by isort ] @@ -191,18 +190,21 @@ ignore = [ [tool.ruff.lint.per-file-ignores] "*.pyi" = [ # The following rules are ignored permanently for good reasons - "A001", # https://docs.astral.sh/ruff/rules/builtin-variable-shadowing/ "A002", # https://docs.astral.sh/ruff/rules/builtin-argument-shadowing/ - "A004", # https://docs.astral.sh/ruff/rules/builtin-import-shadowing/ + "FIX002", # https://docs.astral.sh/ruff/rules/line-contains-todo/ "N", # https://docs.astral.sh/ruff/rules/#pep8-naming-n - "PYI001", # https://docs.astral.sh/ruff/rules/unprefixed-type-param/ - "PYI042", # https://docs.astral.sh/ruff/rules/snake-case-type-alias/ "TD002", # https://docs.astral.sh/ruff/rules/missing-todo-author/ # The following rules are ignored temporarily. Either fix them or move to the permanent list above. - "ERA001", "ANN", "FIX002", "TD003", "PLR0402", "PLC0105" + "A001", # https://docs.astral.sh/ruff/rules/builtin-variable-shadowing/ + "A004", # https://docs.astral.sh/ruff/rules/builtin-import-shadowing/ + "PYI001", # https://docs.astral.sh/ruff/rules/unprefixed-type-param/ + "PYI042", # https://docs.astral.sh/ruff/rules/snake-case-type-alias/ + "TD003", # https://docs.astral.sh/ruff/rules/missing-todo-link/ + "ERA001", "ANN", "PLR0402", "PLC0105" ] "scripts/*" = [ # The following rules are ignored permanently for good reasons + "EM", # https://docs.astral.sh/ruff/rules/#flake8-errmsg-em "S603", # https://docs.astral.sh/ruff/rules/subprocess-without-shell-equals-true/ # The following rules are ignored temporarily. Either fix them or move to the permanent list above. "TRY", "PT", "BLE" @@ -211,12 +213,17 @@ ignore = [ # The following rules are ignored permanently for good reasons "B905", # https://docs.astral.sh/ruff/rules/zip-without-explicit-strict/ "E731", # https://docs.astral.sh/ruff/rules/lambda-assignment/ + "EM", # https://docs.astral.sh/ruff/rules/#flake8-errmsg-em + "FIX002", # https://docs.astral.sh/ruff/rules/line-contains-todo/ "PD", # Pandas is here "PLC0415", # https://docs.astral.sh/ruff/rules/import-outside-top-level/ "S101", # https://docs.astral.sh/ruff/rules/assert/ + "TD002", # https://docs.astral.sh/ruff/rules/missing-todo-author/ # The following rules are ignored temporarily. Either fix them or move to the permanent list above. + "A001", # https://docs.astral.sh/ruff/rules/builtin-variable-shadowing/ + "PYI042", # https://docs.astral.sh/ruff/rules/snake-case-type-alias/ "SLF001", # https://docs.astral.sh/ruff/rules/private-member-access/ - "ANN", "ARG", "ERA", "RUF", "SIM", "TRY", "PT", "NPY", "N", "DTZ", "PLR", "TC", "PGH", "PTH", "S311", "C901", "FIX", "TD", "A001", "PYI042", "ANN201" + "ANN", "ARG", "ERA", "RUF", "SIM", "TRY", "PT", "NPY", "N", "DTZ", "PLR", "TC", "PGH", "PTH", "S311", "C901", "ANN201" ] "tests/test_io.py" = [ # The following rules are ignored permanently for good reasons diff --git a/tests/test_pandas.py b/tests/test_pandas.py index ac3d6ec2a..ff4289c2e 100644 --- a/tests/test_pandas.py +++ b/tests/test_pandas.py @@ -18,7 +18,7 @@ ) import pandas.util as pdutil -# TODO: github.com/pandas-dev/pandas/issues/55023 +# TODO: pandas-dev/pandas#55023 import pytest from typing_extensions import ( Never, From ace7a243b2864c6514da878abadba656627a45cd Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Wed, 8 Oct 2025 00:03:17 +0200 Subject: [PATCH 14/16] chore: pyrefly and mypy --- pandas-stubs/core/arrays/categorical.pyi | 2 -- pandas-stubs/core/arrays/datetimelike.pyi | 1 - pandas-stubs/core/arrays/interval.pyi | 1 - pandas-stubs/core/arrays/sparse/array.pyi | 1 - pandas-stubs/core/frame.pyi | 8 -------- pandas-stubs/core/groupby/generic.pyi | 1 - pandas-stubs/core/series.pyi | 3 --- pyproject.toml | 5 ++++- 8 files changed, 4 insertions(+), 18 deletions(-) diff --git a/pandas-stubs/core/arrays/categorical.pyi b/pandas-stubs/core/arrays/categorical.pyi index 64b56047c..0992f1a56 100644 --- a/pandas-stubs/core/arrays/categorical.pyi +++ b/pandas-stubs/core/arrays/categorical.pyi @@ -114,10 +114,8 @@ class Categorical(ExtensionArray): ) -> Categorical: ... def __len__(self) -> int: ... def __iter__(self): ... - # pyrefly: ignore # bad-param-name-override def __contains__(self, key) -> bool: ... @overload - # pyrefly: ignore # bad-param-name-override def __getitem__(self, key: ScalarIndexer) -> Any: ... @overload def __getitem__( diff --git a/pandas-stubs/core/arrays/datetimelike.pyi b/pandas-stubs/core/arrays/datetimelike.pyi index 44c6f0bd6..74f62e405 100644 --- a/pandas-stubs/core/arrays/datetimelike.pyi +++ b/pandas-stubs/core/arrays/datetimelike.pyi @@ -71,7 +71,6 @@ class DatetimeLikeArrayMixin(ExtensionOpsMixin, ExtensionArray): def size(self) -> int: ... def __len__(self) -> int: ... @overload - # pyrefly: ignore # bad-param-name-override def __getitem__(self, key: ScalarIndexer) -> DTScalarOrNaT: ... @overload def __getitem__( diff --git a/pandas-stubs/core/arrays/interval.pyi b/pandas-stubs/core/arrays/interval.pyi index 744a33d7d..ad97dd547 100644 --- a/pandas-stubs/core/arrays/interval.pyi +++ b/pandas-stubs/core/arrays/interval.pyi @@ -60,7 +60,6 @@ class IntervalArray(IntervalMixin, ExtensionArray): def __iter__(self): ... def __len__(self) -> int: ... @overload - # pyrefly: ignore # bad-param-name-override def __getitem__(self, key: ScalarIndexer) -> IntervalOrNA: ... @overload def __getitem__(self, key: SequenceIndexer) -> Self: ... diff --git a/pandas-stubs/core/arrays/sparse/array.pyi b/pandas-stubs/core/arrays/sparse/array.pyi index 8114269ad..433a56dfd 100644 --- a/pandas-stubs/core/arrays/sparse/array.pyi +++ b/pandas-stubs/core/arrays/sparse/array.pyi @@ -60,7 +60,6 @@ class SparseArray(ExtensionArray, ExtensionOpsMixin): def unique(self): ... def value_counts(self, dropna: bool = True): ... @overload - # pyrefly: ignore # bad-param-name-override def __getitem__(self, key: ScalarIndexer) -> Any: ... @overload def __getitem__( diff --git a/pandas-stubs/core/frame.pyi b/pandas-stubs/core/frame.pyi index 5e2cbc2f3..e330064ee 100644 --- a/pandas-stubs/core/frame.pyi +++ b/pandas-stubs/core/frame.pyi @@ -172,7 +172,6 @@ _T_MUTABLE_MAPPING = TypeVar("_T_MUTABLE_MAPPING", bound=MutableMapping, covaria class _iLocIndexerFrame(_iLocIndexer, Generic[_T]): @overload - # pyrefly: ignore # bad-param-name-override def __getitem__(self, idx: tuple[int, int]) -> Scalar: ... @overload def __getitem__(self, idx: IndexingInt) -> Series: ... @@ -190,7 +189,6 @@ class _iLocIndexerFrame(_iLocIndexer, Generic[_T]): | tuple[slice] ), ) -> _T: ... - # pyrefly: ignore # bad-param-name-override def __setitem__( self, idx: ( @@ -215,7 +213,6 @@ class _iLocIndexerFrame(_iLocIndexer, Generic[_T]): class _LocIndexerFrame(_LocIndexer, Generic[_T]): @overload - # pyrefly: ignore # bad-param-name-override def __getitem__(self, idx: Scalar) -> Series | _T: ... @overload def __getitem__( # type: ignore[overload-overlap] @@ -269,7 +266,6 @@ class _LocIndexerFrame(_LocIndexer, Generic[_T]): @overload def __getitem__(self, idx: tuple[Scalar, slice]) -> Series | _T: ... @overload - # pyrefly: ignore # bad-param-name-override def __setitem__( self, idx: ( @@ -295,9 +291,7 @@ class _LocIndexerFrame(_LocIndexer, Generic[_T]): ) -> None: ... class _iAtIndexerFrame(_iAtIndexer): - # pyrefly: ignore # bad-param-name-override def __getitem__(self, idx: tuple[int, int]) -> Scalar: ... - # pyrefly: ignore # bad-param-name-override def __setitem__( self, idx: tuple[int, int], @@ -305,7 +299,6 @@ class _iAtIndexerFrame(_iAtIndexer): ) -> None: ... class _AtIndexerFrame(_AtIndexer): - # pyrefly: ignore # bad-param-name-override def __getitem__( self, idx: tuple[ @@ -317,7 +310,6 @@ class _AtIndexerFrame(_AtIndexer): int | StrLike | tuple[Scalar, ...], ], ) -> Scalar: ... - # pyrefly: ignore # bad-param-name-override def __setitem__( self, idx: ( diff --git a/pandas-stubs/core/groupby/generic.pyi b/pandas-stubs/core/groupby/generic.pyi index 38c2d09da..76e543ae8 100644 --- a/pandas-stubs/core/groupby/generic.pyi +++ b/pandas-stubs/core/groupby/generic.pyi @@ -449,7 +449,6 @@ class DataFrameGroupBy(GroupBy[DataFrame], Generic[ByT, _TT]): ) -> Series: ... # Series[Axes] but this is not allowed @property def dtypes(self) -> Series: ... - # pyrefly: ignore # bad-param-name-override def __getattr__(self, name: str) -> SeriesGroupBy[Any, ByT]: ... # Overrides that provide more precise return types over the GroupBy class @final # type: ignore[misc] diff --git a/pandas-stubs/core/series.pyi b/pandas-stubs/core/series.pyi index 445f2a17d..9b41a239c 100644 --- a/pandas-stubs/core/series.pyi +++ b/pandas-stubs/core/series.pyi @@ -234,13 +234,11 @@ class SupportsTruedivInt(Protocol[_T_co]): class _iLocIndexerSeries(_iLocIndexer, Generic[S1]): # get item @overload - # pyrefly: ignore # bad-param-name-override def __getitem__(self, idx: IndexingInt) -> S1: ... @overload def __getitem__(self, idx: Index | slice | np_ndarray_anyint) -> Series[S1]: ... # set item @overload - # pyrefly: ignore # bad-param-name-override def __setitem__(self, idx: int, value: S1 | None) -> None: ... @overload def __setitem__( @@ -274,7 +272,6 @@ class _LocIndexerSeries(_LocIndexer, Generic[S1]): # be s.loc[1, :], or s.loc[pd.IndexSlice[1, :]] ) -> Series[S1]: ... @overload - # pyrefly: ignore # bad-param-name-override def __setitem__( self, idx: Index | MaskType | slice, diff --git a/pyproject.toml b/pyproject.toml index 252a75a07..e97c0555c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,7 +36,7 @@ types-pytz = ">= 2022.1.1" numpy = ">= 1.23.5" [tool.poetry.group.dev.dependencies] -mypy = "1.18.2" +mypy = ">=1.18.2" pandas = "2.3.2" pyarrow = ">=10.0.1" pytest = ">=8.4.2" @@ -295,6 +295,9 @@ reportPrivateUsage = false reportMissingModuleSource = true useLibraryCodeForTypes = false +[tool.pyrefly.errors] +bad-param-name-override = false # TODO: report to pyrefly https://github.com/pandas-dev/pandas-stubs/pull/1412#pullrequestreview-3310645279 + [tool.codespell] ignore-words-list = "indext, mose, sav, ser" From f251900ab6399e72b11aff1244bd212180afb6d2 Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Wed, 8 Oct 2025 00:11:02 +0200 Subject: [PATCH 15/16] fix(comment): B009 https://github.com/pandas-dev/pandas-stubs/pull/1412/files/85ab0f97c59b7aa7f5f58d5323f91f6063fb192c#r2410965434 --- tests/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/__init__.py b/tests/__init__.py index d58d4fffb..0e10b6b24 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -87,7 +87,8 @@ def check( and get_origin(shape_type) is tuple and (tuple_args := get_args(shape_type)) and ... not in tuple_args # fixed-length tuple - and (arr_ndim := actual.ndim) != (expected_ndim := len(tuple_args)) # type: ignore[attr-defined] # pyright: ignore[reportAttributeAccessIssue] + and (arr_ndim := getattr(actual, "ndim")) # noqa: B009 + != (expected_ndim := len(tuple_args)) ): raise RuntimeError( f"Array has wrong dimension {arr_ndim}, expected {expected_ndim}" @@ -99,7 +100,7 @@ def check( and (dtype_args := get_args(dtype_type)) and isinstance((expected_dtype := dtype_args[0]), type) and issubclass(expected_dtype, np.generic) - and (arr_dtype := actual.dtype) != expected_dtype # type: ignore[attr-defined] # pyright: ignore[reportAttributeAccessIssue] + and (arr_dtype := getattr(actual, "dtype")) != expected_dtype # noqa: B009 ): raise RuntimeError( f"Array has wrong dtype {arr_dtype}, expected {expected_dtype.__name__}" From 0a322badf79765e1b0a308ac4d390d68f68a344d Mon Sep 17 00:00:00 2001 From: cmp0xff Date: Wed, 8 Oct 2025 00:19:55 +0200 Subject: [PATCH 16/16] chore: ANN --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index e97c0555c..78270bfa0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -223,7 +223,7 @@ ignore = [ "A001", # https://docs.astral.sh/ruff/rules/builtin-variable-shadowing/ "PYI042", # https://docs.astral.sh/ruff/rules/snake-case-type-alias/ "SLF001", # https://docs.astral.sh/ruff/rules/private-member-access/ - "ANN", "ARG", "ERA", "RUF", "SIM", "TRY", "PT", "NPY", "N", "DTZ", "PLR", "TC", "PGH", "PTH", "S311", "C901", "ANN201" + "ANN001", "ANN002", "ANN201", "ANN202", "ANN204", "ANN206", "ANN401", "ARG", "ERA", "RUF", "SIM", "TRY", "PT", "NPY", "N", "DTZ", "PLR", "TC", "PGH", "PTH", "S311", "C901" ] "tests/test_io.py" = [ # The following rules are ignored permanently for good reasons