diff --git a/tests/__init__.py b/tests/__init__.py index f41304829..551ef44f4 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -3,6 +3,7 @@ from contextlib import ( AbstractContextManager, nullcontext, + suppress, ) import os import platform @@ -56,6 +57,7 @@ def pytest_warns_bounded( lower: str | None = None, upper: str | None = None, version_str: str | None = None, + upper_exception: type[Exception] | None = None, ) -> AbstractContextManager: """ Version conditional pytest.warns context manager @@ -77,6 +79,9 @@ def pytest_warns_bounded( version_str: str, optional The version string to use. If None, then uses the pandas version. Can be used to check a python version as well + upper_exception: Exception, optional + Exception to catch if the pandas version is greater than or equal to + the upper bound Notes ----- @@ -108,6 +113,15 @@ def pytest_warns_bounded( # Python version 3.11 and above will raise an error # if the warning is not issued pass + + with pytest_warns_bounded( + UserWarning, match="foo", lower="1.2.99", upper="1.5.99", + upper_exception=AttributeError + ): + # Versions between 1.3.x and 1.5.x will raise an error + # Above 1.5.x, we expect an `AttributeError` to be raised + pass + """ lb = Version("0.0.0") if lower is None else Version(lower) ub = Version("9999.0.0") if upper is None else Version(upper) @@ -118,4 +132,7 @@ def pytest_warns_bounded( if lb < current < ub: return pytest.warns(warning, match=match) else: - return nullcontext() + if upper_exception is None: + return nullcontext() + else: + return suppress(upper_exception) diff --git a/tests/test_frame.py b/tests/test_frame.py index 6f41544c1..6aa0c014d 100644 --- a/tests/test_frame.py +++ b/tests/test_frame.py @@ -1300,7 +1300,7 @@ def test_groupby_series_methods() -> None: gb.min().loc[2] gb.nlargest().loc[2] gb.nsmallest().loc[2] - gb.nth(0).loc[2] + gb.nth(0).loc[1] def test_indexslice_setitem(): diff --git a/tests/test_indexes.py b/tests/test_indexes.py index cd4d5c9ca..7a900542c 100644 --- a/tests/test_indexes.py +++ b/tests/test_indexes.py @@ -7,10 +7,12 @@ from numpy import typing as npt import pandas as pd from pandas.core.indexes.numeric import NumericIndex -import pytest from typing_extensions import assert_type -from tests import check +from tests import ( + check, + pytest_warns_bounded, +) def test_index_unique() -> None: @@ -166,7 +168,12 @@ def test_index_relops() -> None: def test_range_index_union(): - with pytest.warns(FutureWarning, match="pandas.Int64Index"): + with pytest_warns_bounded( + FutureWarning, + match="pandas.Int64Index", + upper="1.5.99", + upper_exception=AttributeError, + ): check( assert_type( pd.RangeIndex(0, 10).union(pd.RangeIndex(10, 20)), diff --git a/tests/test_pandas.py b/tests/test_pandas.py index e295ea9dc..a605e80c3 100644 --- a/tests/test_pandas.py +++ b/tests/test_pandas.py @@ -21,8 +21,10 @@ from pandas._typing import Scalar from tests import ( + PD_LTE_15, TYPE_CHECKING_INVALID_USAGE, check, + pytest_warns_bounded, ) @@ -752,14 +754,29 @@ def test_factorize() -> None: def test_index_unqiue() -> None: ci = pd.CategoricalIndex(["a", "b", "a", "c"]) dti = pd.DatetimeIndex([pd.Timestamp(2000, 1, 1)]) - with pytest.warns(FutureWarning, match="pandas.Float64Index is deprecated"): + with pytest_warns_bounded( + FutureWarning, + match="pandas.Float64Index is deprecated", + upper="1.5.99", + upper_exception=AttributeError, + ): fi = pd.Float64Index([1.0, 2.0]) i = pd.Index(["a", "b", "c", "a"]) - with pytest.warns(FutureWarning, match="pandas.Int64Index is deprecated"): + with pytest_warns_bounded( + FutureWarning, + match="pandas.Int64Index is deprecated", + upper="1.5.99", + upper_exception=AttributeError, + ): i64i = pd.Int64Index([1, 2, 3, 4]) pi = pd.period_range("2000Q1", periods=2, freq="Q") ri = pd.RangeIndex(0, 10) - with pytest.warns(FutureWarning, match="pandas.UInt64Index is deprecated"): + with pytest_warns_bounded( + FutureWarning, + match="pandas.UInt64Index is deprecated", + upper="1.5.99", + upper_exception=AttributeError, + ): ui = pd.UInt64Index([0, 1, 2, 3, 5]) tdi = pd.timedelta_range("1 day", "10 days", periods=10) mi = pd.MultiIndex.from_product([["a", "b"], ["apple", "banana"]]) @@ -767,18 +784,19 @@ def test_index_unqiue() -> None: check(assert_type(pd.unique(ci), pd.CategoricalIndex), pd.CategoricalIndex) check(assert_type(pd.unique(dti), np.ndarray), np.ndarray) - check(assert_type(pd.unique(fi), np.ndarray), np.ndarray) check(assert_type(pd.unique(i), np.ndarray), np.ndarray) - check(assert_type(pd.unique(i64i), np.ndarray), np.ndarray) check(assert_type(pd.unique(pi), pd.PeriodIndex), pd.PeriodIndex) check(assert_type(pd.unique(ri), np.ndarray), np.ndarray) - check(assert_type(pd.unique(ui), np.ndarray), np.ndarray) check(assert_type(pd.unique(tdi), np.ndarray), np.ndarray) check(assert_type(pd.unique(mi), np.ndarray), np.ndarray) check( assert_type(pd.unique(interval_i), "pd.IntervalIndex[pd.Interval[int]]"), pd.IntervalIndex, ) + if PD_LTE_15: + check(assert_type(pd.unique(fi), np.ndarray), np.ndarray) + check(assert_type(pd.unique(i64i), np.ndarray), np.ndarray) + check(assert_type(pd.unique(ui), np.ndarray), np.ndarray) def test_cut() -> None: @@ -1422,7 +1440,12 @@ def test_crosstab_args() -> None: ), pd.DataFrame, ) - with pytest.warns(FutureWarning): + with pytest_warns_bounded( + FutureWarning, + "pivot_table dropped a column because", + upper="1.5.99", + upper_exception=TypeError, + ): check( assert_type( pd.crosstab(a, b, values=pd.Categorical(values), aggfunc=np.sum), diff --git a/tests/test_scalars.py b/tests/test_scalars.py index 53dbd28ba..48c21e7c9 100644 --- a/tests/test_scalars.py +++ b/tests/test_scalars.py @@ -13,7 +13,6 @@ import numpy as np from numpy import typing as npt import pandas as pd -import pytest import pytz from typing_extensions import ( TypeAlias, @@ -29,6 +28,7 @@ from tests import ( TYPE_CHECKING_INVALID_USAGE, check, + pytest_warns_bounded, ) from pandas.tseries.offsets import Day @@ -671,9 +671,14 @@ def test_timedelta_add_sub() -> None: def test_timedelta_mul_div() -> None: td = pd.Timedelta("1 day") - with pytest.warns(FutureWarning): - i_idx = cast(pd.Int64Index, pd.Index([1, 2, 3], dtype=int)) - f_idx = cast(pd.Float64Index, pd.Index([1.2, 2.2, 3.4], dtype=float)) + i_idx = pd.Index([1, 2, 3], dtype=int) + f_idx = pd.Index([1.2, 2.2, 3.4], dtype=float) + + with pytest_warns_bounded( + FutureWarning, upper="1.5.99", match="", upper_exception=AttributeError + ): + i_idx = cast(pd.Int64Index, i_idx) + f_idx = cast(pd.Float64Index, f_idx) np_intp_arr: npt.NDArray[np.integer] = np.array([1, 2, 3]) np_float_arr: npt.NDArray[np.floating] = np.array([1.2, 2.2, 3.4]) @@ -788,9 +793,14 @@ def test_timedelta_mul_div() -> None: def test_timedelta_mod_abs_unary() -> None: td = pd.Timedelta("1 day") - with pytest.warns(FutureWarning): - i_idx = cast(pd.Int64Index, pd.Index([1, 2, 3], dtype=int)) - f_idx = cast(pd.Float64Index, pd.Index([1.2, 2.2, 3.4], dtype=float)) + i_idx = pd.Index([1, 2, 3], dtype=int) + f_idx = pd.Index([1.2, 2.2, 3.4], dtype=float) + + with pytest_warns_bounded( + FutureWarning, upper="1.5.99", match="", upper_exception=AttributeError + ): + i_idx = cast(pd.Int64Index, i_idx) + f_idx = cast(pd.Float64Index, f_idx) check(assert_type(td % 3, pd.Timedelta), pd.Timedelta) check(assert_type(td % 3.5, pd.Timedelta), pd.Timedelta) diff --git a/tests/test_series.py b/tests/test_series.py index f17b7c7e3..12beb716d 100644 --- a/tests/test_series.py +++ b/tests/test_series.py @@ -36,6 +36,7 @@ PD_LTE_15, TYPE_CHECKING_INVALID_USAGE, check, + pytest_warns_bounded, ) if TYPE_CHECKING: @@ -259,16 +260,30 @@ def test_types_shift() -> None: def test_types_rank() -> None: - s = pd.Series([1, 1, 2, 5, 6, np.nan, "milion"]) - with pytest.warns(FutureWarning, match="Dropping of nuisance columns"): + s = pd.Series([1, 1, 2, 5, 6, np.nan]) + if PD_LTE_15: + s[6] = "milion" + with pytest_warns_bounded( + FutureWarning, + match="Dropping of nuisance columns", + upper="1.5.99", + ): s.rank() - with pytest.warns(FutureWarning, match="Dropping of nuisance columns"): + with pytest_warns_bounded( + FutureWarning, match="Dropping of nuisance columns", upper="1.5.99" + ): s.rank(axis=0, na_option="bottom") - with pytest.warns(FutureWarning, match="Dropping of nuisance columns"): + with pytest_warns_bounded( + FutureWarning, match="Dropping of nuisance columns", upper="1.5.99" + ): s.rank(method="min", pct=True) - with pytest.warns(FutureWarning, match="Dropping of nuisance columns"): + with pytest_warns_bounded( + FutureWarning, match="Dropping of nuisance columns", upper="1.5.99" + ): s.rank(method="dense", ascending=True) - with pytest.warns(FutureWarning, match="Calling Series.rank with numeric_only"): + with pytest_warns_bounded( + FutureWarning, match="Calling Series.rank with numeric_only", upper="1.5.99" + ): s.rank(method="first", numeric_only=True) s2 = pd.Series([1, 1, 2, 5, 6, np.nan]) s2.rank(method="first", numeric_only=True) @@ -643,17 +658,25 @@ def test_types_transform() -> None: def test_types_describe() -> None: s = pd.Series([1, 2, 3, np.datetime64("2000-01-01")]) - with pytest.warns(DeprecationWarning, match="elementwise comparison failed"): + with pytest_warns_bounded( + DeprecationWarning, match="elementwise comparison failed", upper="1.5.99" + ): s.describe() - with pytest.warns(DeprecationWarning, match="elementwise comparison failed"): + with pytest_warns_bounded( + DeprecationWarning, match="elementwise comparison failed", upper="1.5.99" + ): s.describe(percentiles=[0.5], include="all") - with pytest.warns(DeprecationWarning, match="elementwise comparison failed"): + with pytest_warns_bounded( + DeprecationWarning, match="elementwise comparison failed", upper="1.5.99" + ): s.describe(exclude=np.number) if PD_LTE_15: # datetime_is_numeric param added in 1.1.0 # https://pandas.pydata.org/docs/whatsnew/v1.1.0.html # Remove in 2.0.0 - with pytest.warns(DeprecationWarning, match="elementwise comparison failed"): + with pytest_warns_bounded( + DeprecationWarning, match="elementwise comparison failed", upper="1.5.99" + ): s.describe(datetime_is_numeric=True)