Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
3 changes: 2 additions & 1 deletion conftest.py
Original file line number Diff line number Diff line change
@@ -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.

Expand Down
12 changes: 6 additions & 6 deletions pandas-stubs/_libs/tslibs/__init__.pyi
Original file line number Diff line number Diff line change
@@ -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,
Expand Down
4 changes: 2 additions & 2 deletions pandas-stubs/core/groupby/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ from pandas.core.groupby.grouper import Grouper as Grouper

__all__ = [
"DataFrameGroupBy",
"NamedAgg",
"SeriesGroupBy",
"GroupBy",
"Grouper",
"NamedAgg",
"SeriesGroupBy",
]
2 changes: 1 addition & 1 deletion pandas-stubs/testing.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -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",
]
42 changes: 37 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curious as to why we can't include this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They enforce

msg = "something"
raise ValueError(msg)

instead of

raise ValueError("something")

I find them annoying, especially that we are raising only for tests and CI here in pandas-stubs.

"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/
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would be nice if we could make this work. Might require converting some str to _str

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to the list below

"A002", # https://docs.astral.sh/ruff/rules/builtin-argument-shadowing/
"A004", # https://docs.astral.sh/ruff/rules/builtin-import-shadowing/
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we do this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to the list below

"N", # https://docs.astral.sh/ruff/rules/#pep8-naming-n
"PYI001", # https://docs.astral.sh/ruff/rules/unprefixed-type-param/
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should try to fix this eventually

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to the list below

"PYI042", # https://docs.astral.sh/ruff/rules/snake-case-type-alias/
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another one we should hope to fix

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to the list below

"TD002", # https://docs.astral.sh/ruff/rules/missing-todo-author/
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO is in one file, so can we make this a single file ignore?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

# The following rules are ignored temporarily. Either fix them or move to the permanent list above.
"ERA", "S", "ANN", "FIX002", "TD003", "TD004", "PLR0402", "PLC0105"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Surprised that "S" would get picked up in our code in a PYI file
  • FIX002 and TD002 are related
  • TD003 is surprising because are single TODO does have a link
  • TD004 is easy to fix since we have one TODO

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • S and most of ERA removed
  • I don't think we'll comply to FIX002. It is fine for me to leave TODOs.
  • There are a few TODOs without link. We can fix them by adding issues.
  • TD004 fixed

]
"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/
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can limit this to one file tests/test_io.py

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

"SLF001", # https://docs.astral.sh/ruff/rules/private-member-access/
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should aim to fix this one so put it in the second list below

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved below

# 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"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • FIX and TD relates to TODO, so we can do a per-file ignore
  • Surprised that ANN201 isn't already taken care of because that's the one that requires return types on functions

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • FIX and TD now more detailed
  • ANN201 will be fixed in a separate PR

]


[tool.mypy]
Expand Down
6 changes: 3 additions & 3 deletions scripts/test/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,17 @@ 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:
setup_steps = []
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])
13 changes: 5 additions & 8 deletions tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)) # type: ignore[attr-defined] # pyright: ignore[reportAttributeAccessIssue]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is annoying, because getattr() is a good usage here. If there is a RUFF rule that is causing this fix, let's ignore it on a per-file basis.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getattr is no better than ., it also raises an error when the attribute is not there. Nevertheless, f251900

):
raise RuntimeError(
f"Array has wrong dimension {arr_ndim}, expected {expected_ndim}"
Expand All @@ -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 # type: ignore[attr-defined] # pyright: ignore[reportAttributeAccessIssue]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment about doing a per-file ignore

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getattr is no better than ., it also raises an error when the attribute is not there. Nevertheless, f251900

):
raise RuntimeError(
f"Array has wrong dtype {arr_dtype}, expected {expected_dtype.__name__}"
Expand Down Expand Up @@ -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)
20 changes: 8 additions & 12 deletions tests/extension/decimal/array.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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)]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is annoying. strict=False is the default, so we shouldn't have to specify it. Can you ignore that rule with ruff?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


return np.asarray(res, dtype=bool)

Expand Down
8 changes: 5 additions & 3 deletions tests/indexes/test_indexes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment about zip

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

),
pd.MultiIndex,
)
check(
Expand Down Expand Up @@ -152,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")])
Expand Down
10 changes: 5 additions & 5 deletions tests/series/test_series.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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"])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment about zip

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fd = df.set_index("id")["value"].to_dict()
check(assert_type(fd, dict[Any, Any]), dict)

Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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"]
Expand Down Expand Up @@ -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

Expand Down
Loading
Loading