diff --git a/pandas/tests/arrays/interval/test_interval.py b/pandas/tests/arrays/interval/test_interval.py index 8d4022112f2a4..e16ef37e8799d 100644 --- a/pandas/tests/arrays/interval/test_interval.py +++ b/pandas/tests/arrays/interval/test_interval.py @@ -1,8 +1,6 @@ import numpy as np import pytest -import pandas.util._test_decorators as td - import pandas as pd from pandas import ( Index, @@ -249,12 +247,8 @@ def test_min_max(self, left_right_dtypes, index_or_series_or_array): # Arrow interaction -pyarrow_skip = td.skip_if_no("pyarrow") - - -@pyarrow_skip def test_arrow_extension_type(): - import pyarrow as pa + pa = pytest.importorskip("pyarrow") from pandas.core.arrays.arrow.extension_types import ArrowIntervalType @@ -269,9 +263,8 @@ def test_arrow_extension_type(): assert hash(p1) != hash(p3) -@pyarrow_skip def test_arrow_array(): - import pyarrow as pa + pa = pytest.importorskip("pyarrow") from pandas.core.arrays.arrow.extension_types import ArrowIntervalType @@ -299,9 +292,8 @@ def test_arrow_array(): pa.array(intervals, type=ArrowIntervalType(pa.float64(), "left")) -@pyarrow_skip def test_arrow_array_missing(): - import pyarrow as pa + pa = pytest.importorskip("pyarrow") from pandas.core.arrays.arrow.extension_types import ArrowIntervalType @@ -329,14 +321,13 @@ def test_arrow_array_missing(): assert result.storage.equals(expected) -@pyarrow_skip @pytest.mark.parametrize( "breaks", [[0.0, 1.0, 2.0, 3.0], date_range("2017", periods=4, freq="D")], ids=["float", "datetime64[ns]"], ) def test_arrow_table_roundtrip(breaks): - import pyarrow as pa + pa = pytest.importorskip("pyarrow") from pandas.core.arrays.arrow.extension_types import ArrowIntervalType @@ -363,14 +354,13 @@ def test_arrow_table_roundtrip(breaks): tm.assert_frame_equal(result, expected[0:0]) -@pyarrow_skip @pytest.mark.parametrize( "breaks", [[0.0, 1.0, 2.0, 3.0], date_range("2017", periods=4, freq="D")], ids=["float", "datetime64[ns]"], ) def test_arrow_table_roundtrip_without_metadata(breaks): - import pyarrow as pa + pa = pytest.importorskip("pyarrow") arr = IntervalArray.from_breaks(breaks) arr[1] = None @@ -386,12 +376,11 @@ def test_arrow_table_roundtrip_without_metadata(breaks): tm.assert_frame_equal(result, df) -@pyarrow_skip def test_from_arrow_from_raw_struct_array(): # in case pyarrow lost the Interval extension type (eg on parquet roundtrip # with datetime64[ns] subtype, see GH-45881), still allow conversion # from arrow to IntervalArray - import pyarrow as pa + pa = pytest.importorskip("pyarrow") arr = pa.array([{"left": 0, "right": 1}, {"left": 1, "right": 2}]) dtype = pd.IntervalDtype(np.dtype("int64"), closed="neither") diff --git a/pandas/tests/arrays/string_/test_string.py b/pandas/tests/arrays/string_/test_string.py index 5ca95bd00f136..cfd3314eb5944 100644 --- a/pandas/tests/arrays/string_/test_string.py +++ b/pandas/tests/arrays/string_/test_string.py @@ -5,8 +5,6 @@ import numpy as np import pytest -import pandas.util._test_decorators as td - from pandas.core.dtypes.common import is_dtype_equal import pandas as pd @@ -420,10 +418,9 @@ def test_arrow_array(dtype): assert arr.equals(expected) -@td.skip_if_no("pyarrow") def test_arrow_roundtrip(dtype, string_storage2): # roundtrip possible from arrow 1.0.0 - import pyarrow as pa + pa = pytest.importorskip("pyarrow") data = pd.array(["a", "b", None], dtype=dtype) df = pd.DataFrame({"a": data}) @@ -438,10 +435,9 @@ def test_arrow_roundtrip(dtype, string_storage2): assert result.loc[2, "a"] is pd.NA -@td.skip_if_no("pyarrow") def test_arrow_load_from_zero_chunks(dtype, string_storage2): # GH-41040 - import pyarrow as pa + pa = pytest.importorskip("pyarrow") data = pd.array([], dtype=dtype) df = pd.DataFrame({"a": data}) diff --git a/pandas/tests/frame/test_api.py b/pandas/tests/frame/test_api.py index ac6e883ac3966..7ca5df0451d19 100644 --- a/pandas/tests/frame/test_api.py +++ b/pandas/tests/frame/test_api.py @@ -7,10 +7,7 @@ from pandas._config.config import option_context -from pandas.util._test_decorators import ( - async_mark, - skip_if_no, -) +from pandas.util._test_decorators import async_mark import pandas as pd from pandas import ( @@ -373,9 +370,9 @@ def test_constructor_expanddim(self): with pytest.raises(AttributeError, match=msg): df._constructor_expanddim(np.arange(27).reshape(3, 3, 3)) - @skip_if_no("jinja2") def test_inspect_getmembers(self): # GH38740 + pytest.importorskip("jinja2") df = DataFrame() msg = "DataFrame._data is deprecated" with tm.assert_produces_warning( diff --git a/pandas/tests/frame/test_ufunc.py b/pandas/tests/frame/test_ufunc.py index 74afb573793a9..305c0f8bba8ce 100644 --- a/pandas/tests/frame/test_ufunc.py +++ b/pandas/tests/frame/test_ufunc.py @@ -4,8 +4,6 @@ import numpy as np import pytest -import pandas.util._test_decorators as td - import pandas as pd import pandas._testing as tm from pandas.api.types import is_extension_array_dtype @@ -247,18 +245,14 @@ def test_alignment_deprecation_enforced(): np.add(s2, df1) -@td.skip_if_no("numba") def test_alignment_deprecation_many_inputs_enforced(): # Enforced in 2.0 # https://github.com/pandas-dev/pandas/issues/39184 # test that the deprecation also works with > 2 inputs -> using a numba # written ufunc for this because numpy itself doesn't have such ufuncs - from numba import ( - float64, - vectorize, - ) + numba = pytest.importorskip("numba") - @vectorize([float64(float64, float64, float64)]) + @numba.vectorize([numba.float64(numba.float64, numba.float64, numba.float64)]) def my_ufunc(x, y, z): return x + y + z diff --git a/pandas/tests/generic/test_to_xarray.py b/pandas/tests/generic/test_to_xarray.py index 1fbd82f01213b..d6eacf4f9079b 100644 --- a/pandas/tests/generic/test_to_xarray.py +++ b/pandas/tests/generic/test_to_xarray.py @@ -1,8 +1,6 @@ import numpy as np import pytest -import pandas.util._test_decorators as td - from pandas import ( Categorical, DataFrame, @@ -12,8 +10,9 @@ ) import pandas._testing as tm +pytest.importorskip("xarray") + -@td.skip_if_no("xarray") class TestDataFrameToXArray: @pytest.fixture def df(self): @@ -84,7 +83,6 @@ def test_to_xarray_with_multiindex(self, df): tm.assert_frame_equal(result, expected) -@td.skip_if_no("xarray") class TestSeriesToXArray: def test_to_xarray_index_types(self, index_flat): index = index_flat diff --git a/pandas/tests/groupby/aggregate/test_numba.py b/pandas/tests/groupby/aggregate/test_numba.py index 19fbac682dccb..ee694129f7118 100644 --- a/pandas/tests/groupby/aggregate/test_numba.py +++ b/pandas/tests/groupby/aggregate/test_numba.py @@ -2,7 +2,6 @@ import pytest from pandas.errors import NumbaUtilError -import pandas.util._test_decorators as td from pandas import ( DataFrame, @@ -16,8 +15,9 @@ pytestmark = pytest.mark.single_cpu -@td.skip_if_no("numba") def test_correct_function_signature(): + pytest.importorskip("numba") + def incorrect_function(x): return sum(x) * 2.7 @@ -32,8 +32,9 @@ def incorrect_function(x): data.groupby("key")["data"].agg(incorrect_function, engine="numba") -@td.skip_if_no("numba") def test_check_nopython_kwargs(): + pytest.importorskip("numba") + def incorrect_function(values, index): return sum(values) * 2.7 @@ -48,13 +49,14 @@ def incorrect_function(values, index): data.groupby("key")["data"].agg(incorrect_function, engine="numba", a=1) -@td.skip_if_no("numba") @pytest.mark.filterwarnings("ignore") # Filter warnings when parallel=True and the function can't be parallelized by Numba @pytest.mark.parametrize("jit", [True, False]) @pytest.mark.parametrize("pandas_obj", ["Series", "DataFrame"]) @pytest.mark.parametrize("as_index", [True, False]) def test_numba_vs_cython(jit, pandas_obj, nogil, parallel, nopython, as_index): + pytest.importorskip("numba") + def func_numba(values, index): return np.mean(values) * 2.7 @@ -78,13 +80,14 @@ def func_numba(values, index): tm.assert_equal(result, expected) -@td.skip_if_no("numba") @pytest.mark.filterwarnings("ignore") # Filter warnings when parallel=True and the function can't be parallelized by Numba @pytest.mark.parametrize("jit", [True, False]) @pytest.mark.parametrize("pandas_obj", ["Series", "DataFrame"]) def test_cache(jit, pandas_obj, nogil, parallel, nopython): # Test that the functions are cached correctly if we switch functions + pytest.importorskip("numba") + def func_1(values, index): return np.mean(values) - 3.4 @@ -120,8 +123,9 @@ def func_2(values, index): tm.assert_equal(result, expected) -@td.skip_if_no("numba") def test_use_global_config(): + pytest.importorskip("numba") + def func_1(values, index): return np.mean(values) - 3.4 @@ -135,7 +139,6 @@ def func_1(values, index): tm.assert_frame_equal(expected, result) -@td.skip_if_no("numba") @pytest.mark.parametrize( "agg_kwargs", [ @@ -146,6 +149,7 @@ def func_1(values, index): ], ) def test_multifunc_numba_vs_cython_frame(agg_kwargs): + pytest.importorskip("numba") data = DataFrame( { 0: ["a", "a", "b", "b", "a"], @@ -160,7 +164,6 @@ def test_multifunc_numba_vs_cython_frame(agg_kwargs): tm.assert_frame_equal(result, expected) -@td.skip_if_no("numba") @pytest.mark.parametrize( "agg_kwargs,expected_func", [ @@ -181,6 +184,7 @@ def test_multifunc_numba_vs_cython_frame(agg_kwargs): ], ) def test_multifunc_numba_udf_frame(agg_kwargs, expected_func): + pytest.importorskip("numba") data = DataFrame( { 0: ["a", "a", "b", "b", "a"], @@ -197,12 +201,12 @@ def test_multifunc_numba_udf_frame(agg_kwargs, expected_func): tm.assert_frame_equal(result, expected, check_dtype=False) -@td.skip_if_no("numba") @pytest.mark.parametrize( "agg_kwargs", [{"func": ["min", "max"]}, {"func": "min"}, {"min_val": "min", "max_val": "max"}], ) def test_multifunc_numba_vs_cython_series(agg_kwargs): + pytest.importorskip("numba") labels = ["a", "a", "b", "b", "a"] data = Series([1.0, 2.0, 3.0, 4.0, 5.0]) grouped = data.groupby(labels) @@ -216,7 +220,6 @@ def test_multifunc_numba_vs_cython_series(agg_kwargs): tm.assert_series_equal(result, expected) -@td.skip_if_no("numba") @pytest.mark.single_cpu @pytest.mark.parametrize( "data,agg_kwargs", @@ -250,6 +253,7 @@ def test_multifunc_numba_vs_cython_series(agg_kwargs): ], ) def test_multifunc_numba_kwarg_propagation(data, agg_kwargs): + pytest.importorskip("numba") labels = ["a", "a", "b", "b", "a"] grouped = data.groupby(labels) result = grouped.agg(**agg_kwargs, engine="numba", engine_kwargs={"parallel": True}) @@ -260,9 +264,10 @@ def test_multifunc_numba_kwarg_propagation(data, agg_kwargs): tm.assert_series_equal(result, expected) -@td.skip_if_no("numba") def test_args_not_cached(): # GH 41647 + pytest.importorskip("numba") + def sum_last(values, index, n): return values[-n:].sum() @@ -277,9 +282,10 @@ def sum_last(values, index, n): tm.assert_series_equal(result, expected) -@td.skip_if_no("numba") def test_index_data_correctly_passed(): # GH 43133 + pytest.importorskip("numba") + def f(values, index): return np.mean(index) @@ -291,10 +297,10 @@ def f(values, index): tm.assert_frame_equal(result, expected) -@td.skip_if_no("numba") def test_engine_kwargs_not_cached(): # If the user passes a different set of engine_kwargs don't return the same # jitted function + pytest.importorskip("numba") nogil = True parallel = False nopython = True @@ -319,9 +325,10 @@ def func_kwargs(values, index): tm.assert_frame_equal(result, expected) -@td.skip_if_no("numba") @pytest.mark.filterwarnings("ignore") def test_multiindex_one_key(nogil, parallel, nopython): + pytest.importorskip("numba") + def numba_func(values, index): return 1 @@ -334,8 +341,9 @@ def numba_func(values, index): tm.assert_frame_equal(result, expected) -@td.skip_if_no("numba") def test_multiindex_multi_key_not_supported(nogil, parallel, nopython): + pytest.importorskip("numba") + def numba_func(values, index): return 1 @@ -347,8 +355,8 @@ def numba_func(values, index): ) -@td.skip_if_no("numba") def test_multilabel_numba_vs_cython(numba_supported_reductions): + pytest.importorskip("numba") reduction, kwargs = numba_supported_reductions df = DataFrame( { @@ -368,8 +376,8 @@ def test_multilabel_numba_vs_cython(numba_supported_reductions): tm.assert_frame_equal(direct_res, direct_expected) -@td.skip_if_no("numba") def test_multilabel_udf_numba_vs_cython(): + pytest.importorskip("numba") df = DataFrame( { "A": ["foo", "bar", "foo", "bar", "foo", "bar", "foo", "foo"], diff --git a/pandas/tests/groupby/test_numba.py b/pandas/tests/groupby/test_numba.py index 7d4440b595dff..e36c248c99ad6 100644 --- a/pandas/tests/groupby/test_numba.py +++ b/pandas/tests/groupby/test_numba.py @@ -1,7 +1,5 @@ import pytest -import pandas.util._test_decorators as td - from pandas import ( DataFrame, Series, @@ -10,8 +8,9 @@ pytestmark = pytest.mark.single_cpu +pytest.importorskip("numba") + -@td.skip_if_no("numba") @pytest.mark.filterwarnings("ignore") # Filter warnings when parallel=True and the function can't be parallelized by Numba class TestEngine: diff --git a/pandas/tests/groupby/test_timegrouper.py b/pandas/tests/groupby/test_timegrouper.py index 9f7f537ac2402..3b4daa3625af4 100644 --- a/pandas/tests/groupby/test_timegrouper.py +++ b/pandas/tests/groupby/test_timegrouper.py @@ -11,8 +11,6 @@ import pytest import pytz -import pandas.util._test_decorators as td - import pandas as pd from pandas import ( DataFrame, @@ -906,11 +904,12 @@ def test_groupby_apply_timegrouper_with_nat_apply_squeeze( ) tm.assert_frame_equal(res, expected) - @td.skip_if_no("numba") @pytest.mark.single_cpu def test_groupby_agg_numba_timegrouper_with_nat( self, groupby_with_truncated_bingrouper ): + pytest.importorskip("numba") + # See discussion in GH#43487 gb = groupby_with_truncated_bingrouper diff --git a/pandas/tests/groupby/transform/test_numba.py b/pandas/tests/groupby/transform/test_numba.py index 965691e31d772..61fcc930f116a 100644 --- a/pandas/tests/groupby/transform/test_numba.py +++ b/pandas/tests/groupby/transform/test_numba.py @@ -2,7 +2,6 @@ import pytest from pandas.errors import NumbaUtilError -import pandas.util._test_decorators as td from pandas import ( DataFrame, @@ -14,8 +13,9 @@ pytestmark = pytest.mark.single_cpu -@td.skip_if_no("numba") def test_correct_function_signature(): + pytest.importorskip("numba") + def incorrect_function(x): return x + 1 @@ -30,8 +30,9 @@ def incorrect_function(x): data.groupby("key")["data"].transform(incorrect_function, engine="numba") -@td.skip_if_no("numba") def test_check_nopython_kwargs(): + pytest.importorskip("numba") + def incorrect_function(values, index): return values + 1 @@ -46,13 +47,14 @@ def incorrect_function(values, index): data.groupby("key")["data"].transform(incorrect_function, engine="numba", a=1) -@td.skip_if_no("numba") @pytest.mark.filterwarnings("ignore") # Filter warnings when parallel=True and the function can't be parallelized by Numba @pytest.mark.parametrize("jit", [True, False]) @pytest.mark.parametrize("pandas_obj", ["Series", "DataFrame"]) @pytest.mark.parametrize("as_index", [True, False]) def test_numba_vs_cython(jit, pandas_obj, nogil, parallel, nopython, as_index): + pytest.importorskip("numba") + def func(values, index): return values + 1 @@ -76,13 +78,14 @@ def func(values, index): tm.assert_equal(result, expected) -@td.skip_if_no("numba") @pytest.mark.filterwarnings("ignore") # Filter warnings when parallel=True and the function can't be parallelized by Numba @pytest.mark.parametrize("jit", [True, False]) @pytest.mark.parametrize("pandas_obj", ["Series", "DataFrame"]) def test_cache(jit, pandas_obj, nogil, parallel, nopython): # Test that the functions are cached correctly if we switch functions + pytest.importorskip("numba") + def func_1(values, index): return values + 1 @@ -117,8 +120,9 @@ def func_2(values, index): tm.assert_equal(result, expected) -@td.skip_if_no("numba") def test_use_global_config(): + pytest.importorskip("numba") + def func_1(values, index): return values + 1 @@ -133,11 +137,11 @@ def func_1(values, index): # TODO: Test more than just reductions (e.g. actually test transformations once we have -@td.skip_if_no("numba") @pytest.mark.parametrize( "agg_func", [["min", "max"], "min", {"B": ["min", "max"], "C": "sum"}] ) def test_string_cython_vs_numba(agg_func, numba_supported_reductions): + pytest.importorskip("numba") agg_func, kwargs = numba_supported_reductions data = DataFrame( {0: ["a", "a", "b", "b", "a"], 1: [1.0, 2.0, 3.0, 4.0, 5.0]}, columns=[0, 1] @@ -153,9 +157,10 @@ def test_string_cython_vs_numba(agg_func, numba_supported_reductions): tm.assert_series_equal(result, expected) -@td.skip_if_no("numba") def test_args_not_cached(): # GH 41647 + pytest.importorskip("numba") + def sum_last(values, index, n): return values[-n:].sum() @@ -170,9 +175,10 @@ def sum_last(values, index, n): tm.assert_series_equal(result, expected) -@td.skip_if_no("numba") def test_index_data_correctly_passed(): # GH 43133 + pytest.importorskip("numba") + def f(values, index): return index - 1 @@ -182,10 +188,10 @@ def f(values, index): tm.assert_frame_equal(result, expected) -@td.skip_if_no("numba") def test_engine_kwargs_not_cached(): # If the user passes a different set of engine_kwargs don't return the same # jitted function + pytest.importorskip("numba") nogil = True parallel = False nopython = True @@ -210,9 +216,10 @@ def func_kwargs(values, index): tm.assert_frame_equal(result, expected) -@td.skip_if_no("numba") @pytest.mark.filterwarnings("ignore") def test_multiindex_one_key(nogil, parallel, nopython): + pytest.importorskip("numba") + def numba_func(values, index): return 1 @@ -225,8 +232,9 @@ def numba_func(values, index): tm.assert_frame_equal(result, expected) -@td.skip_if_no("numba") def test_multiindex_multi_key_not_supported(nogil, parallel, nopython): + pytest.importorskip("numba") + def numba_func(values, index): return 1 @@ -238,8 +246,8 @@ def numba_func(values, index): ) -@td.skip_if_no("numba") def test_multilabel_numba_vs_cython(numba_supported_reductions): + pytest.importorskip("numba") reduction, kwargs = numba_supported_reductions df = DataFrame( { @@ -255,8 +263,8 @@ def test_multilabel_numba_vs_cython(numba_supported_reductions): tm.assert_frame_equal(res_agg, expected_agg) -@td.skip_if_no("numba") def test_multilabel_udf_numba_vs_cython(): + pytest.importorskip("numba") df = DataFrame( { "A": ["foo", "bar", "foo", "bar", "foo", "bar", "foo", "foo"], diff --git a/pandas/tests/io/excel/test_readers.py b/pandas/tests/io/excel/test_readers.py index 7df5b928858d8..b0a5998a47679 100644 --- a/pandas/tests/io/excel/test_readers.py +++ b/pandas/tests/io/excel/test_readers.py @@ -632,13 +632,12 @@ def test_dtype_backend_and_dtype(self, read_ext): ) tm.assert_frame_equal(result, df) - @td.skip_if_no("pyarrow") def test_dtype_backend_string(self, read_ext, string_storage): # GH#36712 if read_ext in (".xlsb", ".xls"): pytest.skip(f"No engine for filetype: '{read_ext}'") - import pyarrow as pa + pa = pytest.importorskip("pyarrow") with pd.option_context("mode.string_storage", string_storage): df = DataFrame( diff --git a/pandas/tests/io/formats/style/test_matplotlib.py b/pandas/tests/io/formats/style/test_matplotlib.py index 1485bd64e4b57..fb7a77f1ddb27 100644 --- a/pandas/tests/io/formats/style/test_matplotlib.py +++ b/pandas/tests/io/formats/style/test_matplotlib.py @@ -1,3 +1,5 @@ +import gc + import numpy as np import pytest @@ -15,6 +17,26 @@ from pandas.io.formats.style import Styler +@pytest.fixture(autouse=True) +def mpl_cleanup(): + # matplotlib/testing/decorators.py#L24 + # 1) Resets units registry + # 2) Resets rc_context + # 3) Closes all figures + mpl = pytest.importorskip("matplotlib") + mpl_units = pytest.importorskip("matplotlib.units") + plt = pytest.importorskip("matplotlib.pyplot") + orig_units_registry = mpl_units.registry.copy() + with mpl.rc_context(): + mpl.use("template") + yield + mpl_units.registry.clear() + mpl_units.registry.update(orig_units_registry) + plt.close("all") + # https://matplotlib.org/stable/users/prev_whats_new/whats_new_3.6.0.html#garbage-collection-is-no-longer-run-on-figure-close # noqa: E501 + gc.collect(1) + + @pytest.fixture def df(): return DataFrame([[1, 2], [2, 4]], columns=["A", "B"]) diff --git a/pandas/tests/io/formats/test_to_excel.py b/pandas/tests/io/formats/test_to_excel.py index 2a0f9f59972ef..927a9f4961f6f 100644 --- a/pandas/tests/io/formats/test_to_excel.py +++ b/pandas/tests/io/formats/test_to_excel.py @@ -7,7 +7,6 @@ import pytest from pandas.errors import CSSWarning -import pandas.util._test_decorators as td import pandas._testing as tm @@ -336,12 +335,11 @@ def tests_css_named_colors_valid(): assert len(color) == 6 and all(c in upper_hexs for c in color) -@td.skip_if_no_mpl def test_css_named_colors_from_mpl_present(): - from matplotlib.colors import CSS4_COLORS as mpl_colors + mpl_colors = pytest.importorskip("matplotlib.colors") pd_colors = CSSToExcelConverter.NAMED_COLORS - for name, color in mpl_colors.items(): + for name, color in mpl_colors.CSS4_COLORS.items(): assert name in pd_colors and pd_colors[name] == color[1:] diff --git a/pandas/tests/io/formats/test_to_string.py b/pandas/tests/io/formats/test_to_string.py index 96761db8bf752..0c260f0af0a8d 100644 --- a/pandas/tests/io/formats/test_to_string.py +++ b/pandas/tests/io/formats/test_to_string.py @@ -5,8 +5,6 @@ import numpy as np import pytest -import pandas.util._test_decorators as td - from pandas import ( DataFrame, Series, @@ -342,9 +340,9 @@ def test_to_string_max_rows_zero(data, expected): assert result == expected -@td.skip_if_no("pyarrow") def test_to_string_string_dtype(): # GH#50099 + pytest.importorskip("pyarrow") df = DataFrame({"x": ["foo", "bar", "baz"], "y": ["a", "b", "c"], "z": [1, 2, 3]}) df = df.astype( {"x": "string[pyarrow]", "y": "string[python]", "z": "int64[pyarrow]"} diff --git a/pandas/tests/io/parser/test_network.py b/pandas/tests/io/parser/test_network.py index dd702259a9558..613284ad096d2 100644 --- a/pandas/tests/io/parser/test_network.py +++ b/pandas/tests/io/parser/test_network.py @@ -76,10 +76,10 @@ def tips_df(datapath): @pytest.mark.usefixtures("s3_resource") @td.skip_if_not_us_locale() class TestS3: - @td.skip_if_no("s3fs") def test_parse_public_s3_bucket(self, s3_public_bucket_with_data, tips_df, s3so): # more of an integration test due to the not-public contents portion # can probably mock this though. + pytest.importorskip("s3fs") for ext, comp in [("", None), (".gz", "gzip"), (".bz2", "bz2")]: df = read_csv( f"s3://{s3_public_bucket_with_data.name}/tips.csv" + ext, @@ -90,9 +90,9 @@ def test_parse_public_s3_bucket(self, s3_public_bucket_with_data, tips_df, s3so) assert not df.empty tm.assert_frame_equal(df, tips_df) - @td.skip_if_no("s3fs") def test_parse_private_s3_bucket(self, s3_private_bucket_with_data, tips_df, s3so): # Read public file from bucket with not-public contents + pytest.importorskip("s3fs") df = read_csv( f"s3://{s3_private_bucket_with_data.name}/tips.csv", storage_options=s3so ) @@ -254,10 +254,10 @@ def test_write_s3_csv_fails(self, tips_df, s3so): ) @pytest.mark.xfail(reason="GH#39155 s3fs upgrade", strict=False) - @td.skip_if_no("pyarrow") def test_write_s3_parquet_fails(self, tips_df, s3so): # GH 27679 # Attempting to write to an invalid S3 path should raise + pytest.importorskip("pyarrow") import botocore # GH 34087 @@ -329,11 +329,11 @@ def test_read_s3_with_hash_in_key(self, s3_public_bucket_with_data, tips_df, s3s ) tm.assert_frame_equal(tips_df, result) - @td.skip_if_no("pyarrow") def test_read_feather_s3_file_path( self, s3_public_bucket_with_data, feather_file, s3so ): # GH 29055 + pytest.importorskip("pyarrow") expected = read_feather(feather_file) res = read_feather( f"s3://{s3_public_bucket_with_data.name}/simple_dataset.feather", diff --git a/pandas/tests/io/parser/test_upcast.py b/pandas/tests/io/parser/test_upcast.py index 7cfaac997e3b1..bc4c4c2e24e9c 100644 --- a/pandas/tests/io/parser/test_upcast.py +++ b/pandas/tests/io/parser/test_upcast.py @@ -5,7 +5,6 @@ _maybe_upcast, na_values, ) -import pandas.util._test_decorators as td import pandas as pd from pandas import NA @@ -85,11 +84,10 @@ def test_maybe_upcaste_all_nan(): tm.assert_extension_array_equal(result, expected) -@td.skip_if_no("pyarrow") @pytest.mark.parametrize("val", [na_values[np.object_], "c"]) def test_maybe_upcast_object(val, string_storage): # GH#36712 - import pyarrow as pa + pa = pytest.importorskip("pyarrow") with pd.option_context("mode.string_storage", string_storage): arr = np.array(["a", "b", val], dtype=np.object_) diff --git a/pandas/tests/io/test_common.py b/pandas/tests/io/test_common.py index 435b9bdade944..a7ece6a6d7b08 100644 --- a/pandas/tests/io/test_common.py +++ b/pandas/tests/io/test_common.py @@ -147,9 +147,8 @@ def test_bytesiowrapper_returns_correct_bytes(self): assert result == data.encode("utf-8") # Test that pyarrow can handle a file opened with get_handle - @td.skip_if_no("pyarrow") def test_get_handle_pyarrow_compat(self): - from pyarrow import csv + pa_csv = pytest.importorskip("pyarrow.csv") # Test latin1, ucs-2, and ucs-4 chars data = """a,b,c @@ -161,7 +160,7 @@ def test_get_handle_pyarrow_compat(self): ) s = StringIO(data) with icom.get_handle(s, "rb", is_text=False) as handles: - df = csv.read_csv(handles.handle).to_pandas() + df = pa_csv.read_csv(handles.handle).to_pandas() tm.assert_frame_equal(df, expected) assert not s.closed diff --git a/pandas/tests/io/test_fsspec.py b/pandas/tests/io/test_fsspec.py index fe5818620b9a9..030505f617b97 100644 --- a/pandas/tests/io/test_fsspec.py +++ b/pandas/tests/io/test_fsspec.py @@ -140,17 +140,18 @@ def test_excel_options(fsspectest): assert fsspectest.test[0] == "read" -@td.skip_if_no("fastparquet") def test_to_parquet_new_file(cleared_fs, df1): """Regression test for writing to a not-yet-existent GCS Parquet file.""" + pytest.importorskip("fastparquet") + df1.to_parquet( "memory://test/test.csv", index=True, engine="fastparquet", compression=None ) -@td.skip_if_no("pyarrow") def test_arrowparquet_options(fsspectest): """Regression test for writing to a not-yet-existent GCS Parquet file.""" + pytest.importorskip("pyarrow") df = DataFrame({"a": [0]}) df.to_parquet( "testmem://test/test.csv", @@ -168,9 +169,10 @@ def test_arrowparquet_options(fsspectest): @td.skip_array_manager_not_yet_implemented # TODO(ArrayManager) fastparquet -@td.skip_if_no("fastparquet") def test_fastparquet_options(fsspectest): """Regression test for writing to a not-yet-existent GCS Parquet file.""" + pytest.importorskip("fastparquet") + df = DataFrame({"a": [0]}) df.to_parquet( "testmem://test/test.csv", @@ -188,8 +190,8 @@ def test_fastparquet_options(fsspectest): @pytest.mark.single_cpu -@td.skip_if_no("s3fs") def test_from_s3_csv(s3_public_bucket_with_data, tips_file, s3so): + pytest.importorskip("s3fs") tm.assert_equal( read_csv( f"s3://{s3_public_bucket_with_data.name}/tips.csv", storage_options=s3so @@ -213,8 +215,8 @@ def test_from_s3_csv(s3_public_bucket_with_data, tips_file, s3so): @pytest.mark.single_cpu @pytest.mark.parametrize("protocol", ["s3", "s3a", "s3n"]) -@td.skip_if_no("s3fs") def test_s3_protocols(s3_public_bucket_with_data, tips_file, protocol, s3so): + pytest.importorskip("s3fs") tm.assert_equal( read_csv( f"{protocol}://{s3_public_bucket_with_data.name}/tips.csv", @@ -226,9 +228,10 @@ def test_s3_protocols(s3_public_bucket_with_data, tips_file, protocol, s3so): @pytest.mark.single_cpu @td.skip_array_manager_not_yet_implemented # TODO(ArrayManager) fastparquet -@td.skip_if_no("s3fs") -@td.skip_if_no("fastparquet") def test_s3_parquet(s3_public_bucket, s3so, df1): + pytest.importorskip("fastparquet") + pytest.importorskip("s3fs") + fn = f"s3://{s3_public_bucket.name}/test.parquet" df1.to_parquet( fn, index=False, engine="fastparquet", compression=None, storage_options=s3so @@ -244,8 +247,8 @@ def test_not_present_exception(): read_csv("memory://test/test.csv") -@td.skip_if_no("pyarrow") def test_feather_options(fsspectest): + pytest.importorskip("pyarrow") df = DataFrame({"a": [0]}) df.to_feather("testmem://mockfile", storage_options={"test": "feather_write"}) assert fsspectest.test[0] == "feather_write" @@ -291,16 +294,16 @@ def test_stata_options(fsspectest): tm.assert_frame_equal(df, out.astype("int64")) -@td.skip_if_no("tabulate") def test_markdown_options(fsspectest): + pytest.importorskip("tabulate") df = DataFrame({"a": [0]}) df.to_markdown("testmem://mockfile", storage_options={"test": "md_write"}) assert fsspectest.test[0] == "md_write" assert fsspectest.cat("testmem://mockfile") -@td.skip_if_no("pyarrow") def test_non_fsspec_options(): + pytest.importorskip("pyarrow") with pytest.raises(ValueError, match="storage_options"): read_csv("localfile", storage_options={"a": True}) with pytest.raises(ValueError, match="storage_options"): diff --git a/pandas/tests/io/test_html.py b/pandas/tests/io/test_html.py index cafe690e338d6..6cf90749e5b30 100644 --- a/pandas/tests/io/test_html.py +++ b/pandas/tests/io/test_html.py @@ -70,10 +70,9 @@ def assert_framelist_equal(list1, list2, *args, **kwargs): assert not frame_i.empty, "frames are both empty" -@td.skip_if_no("bs4") -@td.skip_if_no("html5lib") def test_bs4_version_fails(monkeypatch, datapath): - import bs4 + bs4 = pytest.importorskip("bs4") + pytest.importorskip("html5lib") monkeypatch.setattr(bs4, "__version__", "4.2") with pytest.raises(ImportError, match="Pandas requires version"): @@ -89,10 +88,11 @@ def test_invalid_flavor(): read_html(StringIO(url), match="google", flavor=flavor) -@td.skip_if_no("bs4") -@td.skip_if_no("lxml") -@td.skip_if_no("html5lib") def test_same_ordering(datapath): + pytest.importorskip("bs4") + pytest.importorskip("lxml") + pytest.importorskip("html5lib") + filename = datapath("io", "data", "html", "valid_markup.html") dfs_lxml = read_html(filename, index_col=0, flavor=["lxml"]) dfs_bs4 = read_html(filename, index_col=0, flavor=["bs4"]) diff --git a/pandas/tests/io/test_orc.py b/pandas/tests/io/test_orc.py index 571d9d5536e20..8483eb0d5c159 100644 --- a/pandas/tests/io/test_orc.py +++ b/pandas/tests/io/test_orc.py @@ -8,8 +8,6 @@ import numpy as np import pytest -import pandas.util._test_decorators as td - import pandas as pd from pandas import read_orc import pandas._testing as tm @@ -243,10 +241,11 @@ def test_orc_reader_snappy_compressed(dirpath): tm.assert_equal(expected, got) -@td.skip_if_no("pyarrow", min_version="7.0.0") def test_orc_roundtrip_file(dirpath): # GH44554 # PyArrow gained ORC write support with the current argument order + pytest.importorskip("pyarrow") + data = { "boolean1": np.array([False, True], dtype="bool"), "byte1": np.array([1, 100], dtype="int8"), @@ -267,10 +266,11 @@ def test_orc_roundtrip_file(dirpath): tm.assert_equal(expected, got) -@td.skip_if_no("pyarrow", min_version="7.0.0") def test_orc_roundtrip_bytesio(): # GH44554 # PyArrow gained ORC write support with the current argument order + pytest.importorskip("pyarrow") + data = { "boolean1": np.array([False, True], dtype="bool"), "byte1": np.array([1, 100], dtype="int8"), @@ -290,17 +290,18 @@ def test_orc_roundtrip_bytesio(): tm.assert_equal(expected, got) -@td.skip_if_no("pyarrow", min_version="7.0.0") def test_orc_writer_dtypes_not_supported(orc_writer_dtypes_not_supported): # GH44554 # PyArrow gained ORC write support with the current argument order + pytest.importorskip("pyarrow") + msg = "The dtype of one or more columns is not supported yet." with pytest.raises(NotImplementedError, match=msg): orc_writer_dtypes_not_supported.to_orc() -@td.skip_if_no("pyarrow", min_version="7.0.0") def test_orc_dtype_backend_pyarrow(): + pytest.importorskip("pyarrow") df = pd.DataFrame( { "string": list("abc"), @@ -334,9 +335,9 @@ def test_orc_dtype_backend_pyarrow(): tm.assert_frame_equal(result, expected) -@td.skip_if_no("pyarrow", min_version="7.0.0") def test_orc_dtype_backend_numpy_nullable(): # GH#50503 + pytest.importorskip("pyarrow") df = pd.DataFrame( { "string": list("abc"), diff --git a/pandas/tests/io/test_parquet.py b/pandas/tests/io/test_parquet.py index 5399cabb61ec3..283d86227c79e 100644 --- a/pandas/tests/io/test_parquet.py +++ b/pandas/tests/io/test_parquet.py @@ -19,7 +19,6 @@ pa_version_under8p0, pa_version_under13p0, ) -import pandas.util._test_decorators as td import pandas as pd import pandas._testing as tm @@ -830,7 +829,6 @@ def test_s3_roundtrip(self, df_compat, s3_public_bucket, pa, s3so): ) @pytest.mark.single_cpu - @td.skip_if_no("s3fs") # also requires flask @pytest.mark.parametrize( "partition_col", [ @@ -841,6 +839,7 @@ def test_s3_roundtrip(self, df_compat, s3_public_bucket, pa, s3so): def test_s3_roundtrip_for_dir( self, df_compat, s3_public_bucket, pa, partition_col, s3so ): + pytest.importorskip("s3fs") # GH #26388 expected_df = df_compat.copy() @@ -868,15 +867,15 @@ def test_s3_roundtrip_for_dir( repeat=1, ) - @td.skip_if_no("pyarrow") def test_read_file_like_obj_support(self, df_compat): + pytest.importorskip("pyarrow") buffer = BytesIO() df_compat.to_parquet(buffer) df_from_buf = read_parquet(buffer) tm.assert_frame_equal(df_compat, df_from_buf) - @td.skip_if_no("pyarrow") def test_expand_user(self, df_compat, monkeypatch): + pytest.importorskip("pyarrow") monkeypatch.setenv("HOME", "TestingUser") monkeypatch.setenv("USERPROFILE", "TestingUser") with pytest.raises(OSError, match=r".*TestingUser.*"): @@ -928,10 +927,10 @@ def test_write_with_schema(self, pa): out_df = df.astype(bool) check_round_trip(df, pa, write_kwargs={"schema": schema}, expected=out_df) - @td.skip_if_no("pyarrow") def test_additional_extension_arrays(self, pa): # test additional ExtensionArrays that are supported through the # __arrow_array__ protocol + pytest.importorskip("pyarrow") df = pd.DataFrame( { "a": pd.Series([1, 2, 3], dtype="Int64"), @@ -944,17 +943,17 @@ def test_additional_extension_arrays(self, pa): df = pd.DataFrame({"a": pd.Series([1, 2, 3, None], dtype="Int64")}) check_round_trip(df, pa) - @td.skip_if_no("pyarrow") def test_pyarrow_backed_string_array(self, pa, string_storage): # test ArrowStringArray supported through the __arrow_array__ protocol + pytest.importorskip("pyarrow") df = pd.DataFrame({"a": pd.Series(["a", None, "c"], dtype="string[pyarrow]")}) with pd.option_context("string_storage", string_storage): check_round_trip(df, pa, expected=df.astype(f"string[{string_storage}]")) - @td.skip_if_no("pyarrow") def test_additional_extension_types(self, pa): # test additional ExtensionArrays that are supported through the # __arrow_array__ protocol + by defining a custom ExtensionType + pytest.importorskip("pyarrow") df = pd.DataFrame( { "c": pd.IntervalIndex.from_tuples([(0, 1), (1, 2), (3, 4)]), @@ -1003,9 +1002,9 @@ def test_timezone_aware_index(self, request, pa, timezone_aware_date_list): # this use-case sets the resolution to 1 minute check_round_trip(df, pa, check_dtype=False) - @td.skip_if_no("pyarrow") def test_filter_row_groups(self, pa): # https://github.com/pandas-dev/pandas/issues/26551 + pytest.importorskip("pyarrow") df = pd.DataFrame({"a": list(range(0, 3))}) with tm.ensure_clean() as path: df.to_parquet(path, pa) diff --git a/pandas/tests/io/test_pickle.py b/pandas/tests/io/test_pickle.py index d66d5d532962c..ac24dc21bab38 100644 --- a/pandas/tests/io/test_pickle.py +++ b/pandas/tests/io/test_pickle.py @@ -456,8 +456,8 @@ def mock_urlopen_read(*args, **kwargs): tm.assert_frame_equal(df, result) -@td.skip_if_no("fsspec") def test_pickle_fsspec_roundtrip(): + pytest.importorskip("fsspec") with tm.ensure_clean(): mockurl = "memory://mockfile" df = tm.makeDataFrame() diff --git a/pandas/tests/io/test_s3.py b/pandas/tests/io/test_s3.py index 35250f1dd3081..9ee3c09631d0e 100644 --- a/pandas/tests/io/test_s3.py +++ b/pandas/tests/io/test_s3.py @@ -2,8 +2,6 @@ import pytest -import pandas.util._test_decorators as td - from pandas import read_csv @@ -19,10 +17,10 @@ def test_streaming_s3_objects(): read_csv(body) -@td.skip_if_no("s3fs") @pytest.mark.single_cpu def test_read_without_creds_from_pub_bucket(s3_public_bucket_with_data, s3so): # GH 34626 + pytest.importorskip("s3fs") result = read_csv( f"s3://{s3_public_bucket_with_data.name}/tips.csv", nrows=3, @@ -31,7 +29,6 @@ def test_read_without_creds_from_pub_bucket(s3_public_bucket_with_data, s3so): assert len(result) == 3 -@td.skip_if_no("s3fs") @pytest.mark.single_cpu def test_read_with_creds_from_pub_bucket(s3_public_bucket_with_data, monkeypatch, s3so): # Ensure we can read from a public bucket with credentials @@ -39,6 +36,7 @@ def test_read_with_creds_from_pub_bucket(s3_public_bucket_with_data, monkeypatch # temporary workaround as moto fails for botocore >= 1.11 otherwise, # see https://github.com/spulec/moto/issues/1924 & 1952 + pytest.importorskip("s3fs") monkeypatch.setenv("AWS_ACCESS_KEY_ID", "foobar_key") monkeypatch.setenv("AWS_SECRET_ACCESS_KEY", "foobar_secret") df = read_csv( diff --git a/pandas/tests/io/xml/test_to_xml.py b/pandas/tests/io/xml/test_to_xml.py index a6ed15f56d8d4..37251a58b0c11 100644 --- a/pandas/tests/io/xml/test_to_xml.py +++ b/pandas/tests/io/xml/test_to_xml.py @@ -866,17 +866,17 @@ def test_encoding_option_str(xml_baby_names, parser): assert output == encoding_expected -@td.skip_if_no("lxml") def test_correct_encoding_file(xml_baby_names): + pytest.importorskip("lxml") df_file = read_xml(xml_baby_names, encoding="ISO-8859-1", parser="lxml") with tm.ensure_clean("test.xml") as path: df_file.to_xml(path, index=False, encoding="ISO-8859-1", parser="lxml") -@td.skip_if_no("lxml") @pytest.mark.parametrize("encoding", ["UTF-8", "UTF-16", "ISO-8859-1"]) def test_wrong_encoding_option_lxml(xml_baby_names, parser, encoding): + pytest.importorskip("lxml") df_file = read_xml(xml_baby_names, encoding="ISO-8859-1", parser="lxml") with tm.ensure_clean("test.xml") as path: @@ -891,8 +891,8 @@ def test_misspelled_encoding(parser, geom_df): # PRETTY PRINT -@td.skip_if_no("lxml") def test_xml_declaration_pretty_print(geom_df): + pytest.importorskip("lxml") expected = """\ @@ -1004,18 +1004,18 @@ def test_unknown_parser(geom_df): """ -@td.skip_if_no("lxml") def test_stylesheet_file_like(xsl_row_field_output, mode, geom_df): + pytest.importorskip("lxml") with open( xsl_row_field_output, mode, encoding="utf-8" if mode == "r" else None ) as f: assert geom_df.to_xml(stylesheet=f) == xsl_expected -@td.skip_if_no("lxml") def test_stylesheet_io(xsl_row_field_output, mode, geom_df): # note: By default the bodies of untyped functions are not checked, # consider using --check-untyped-defs + pytest.importorskip("lxml") xsl_obj: BytesIO | StringIO # type: ignore[annotation-unchecked] with open( @@ -1031,8 +1031,8 @@ def test_stylesheet_io(xsl_row_field_output, mode, geom_df): assert output == xsl_expected -@td.skip_if_no("lxml") def test_stylesheet_buffered_reader(xsl_row_field_output, mode, geom_df): + pytest.importorskip("lxml") with open( xsl_row_field_output, mode, encoding="utf-8" if mode == "r" else None ) as f: @@ -1043,23 +1043,21 @@ def test_stylesheet_buffered_reader(xsl_row_field_output, mode, geom_df): assert output == xsl_expected -@td.skip_if_no("lxml") def test_stylesheet_wrong_path(geom_df): - from lxml.etree import XMLSyntaxError + lxml_etree = pytest.importorskip("lxml.etree") xsl = os.path.join("data", "xml", "row_field_output.xslt") with pytest.raises( - XMLSyntaxError, + lxml_etree.XMLSyntaxError, match=("Start tag expected, '<' not found"), ): geom_df.to_xml(stylesheet=xsl) -@td.skip_if_no("lxml") @pytest.mark.parametrize("val", ["", b""]) def test_empty_string_stylesheet(val, geom_df): - from lxml.etree import XMLSyntaxError + lxml_etree = pytest.importorskip("lxml.etree") msg = "|".join( [ @@ -1070,13 +1068,12 @@ def test_empty_string_stylesheet(val, geom_df): ] ) - with pytest.raises(XMLSyntaxError, match=msg): + with pytest.raises(lxml_etree.XMLSyntaxError, match=msg): geom_df.to_xml(stylesheet=val) -@td.skip_if_no("lxml") def test_incorrect_xsl_syntax(geom_df): - from lxml.etree import XMLSyntaxError + lxml_etree = pytest.importorskip("lxml.etree") xsl = """\ @@ -1099,13 +1096,14 @@ def test_incorrect_xsl_syntax(geom_df): """ - with pytest.raises(XMLSyntaxError, match=("Opening and ending tag mismatch")): + with pytest.raises( + lxml_etree.XMLSyntaxError, match=("Opening and ending tag mismatch") + ): geom_df.to_xml(stylesheet=xsl) -@td.skip_if_no("lxml") def test_incorrect_xsl_eval(geom_df): - from lxml.etree import XSLTParseError + lxml_etree = pytest.importorskip("lxml.etree") xsl = """\ @@ -1128,13 +1126,12 @@ def test_incorrect_xsl_eval(geom_df): """ - with pytest.raises(XSLTParseError, match=("failed to compile")): + with pytest.raises(lxml_etree.XSLTParseError, match=("failed to compile")): geom_df.to_xml(stylesheet=xsl) -@td.skip_if_no("lxml") def test_incorrect_xsl_apply(geom_df): - from lxml.etree import XSLTApplyError + lxml_etree = pytest.importorskip("lxml.etree") xsl = """\ @@ -1148,7 +1145,7 @@ def test_incorrect_xsl_apply(geom_df): """ - with pytest.raises(XSLTApplyError, match=("Cannot resolve URI")): + with pytest.raises(lxml_etree.XSLTApplyError, match=("Cannot resolve URI")): with tm.ensure_clean("test.xml") as path: geom_df.to_xml(path, stylesheet=xsl) @@ -1171,8 +1168,8 @@ def test_stylesheet_with_etree(geom_df): geom_df.to_xml(parser="etree", stylesheet=xsl) -@td.skip_if_no("lxml") def test_style_to_csv(geom_df): + pytest.importorskip("lxml") xsl = """\ @@ -1200,8 +1197,8 @@ def test_style_to_csv(geom_df): assert out_csv == out_xml -@td.skip_if_no("lxml") def test_style_to_string(geom_df): + pytest.importorskip("lxml") xsl = """\ @@ -1234,8 +1231,8 @@ def test_style_to_string(geom_df): assert out_xml == out_str -@td.skip_if_no("lxml") def test_style_to_json(geom_df): + pytest.importorskip("lxml") xsl = """\ @@ -1365,10 +1362,9 @@ def test_unsuported_compression(parser, geom_df): @pytest.mark.single_cpu -@td.skip_if_no("s3fs") -@td.skip_if_no("lxml") def test_s3_permission_output(parser, s3_public_bucket, geom_df): - import s3fs + s3fs = pytest.importorskip("s3fs") + pytest.importorskip("lxml") with tm.external_error_raised((PermissionError, FileNotFoundError)): fs = s3fs.S3FileSystem(anon=True) diff --git a/pandas/tests/io/xml/test_xml.py b/pandas/tests/io/xml/test_xml.py index 19668c2c4cfc0..d36fcdc0f4431 100644 --- a/pandas/tests/io/xml/test_xml.py +++ b/pandas/tests/io/xml/test_xml.py @@ -246,9 +246,9 @@ ) -@td.skip_if_no("lxml") def test_literal_xml_deprecation(): # GH 53809 + pytest.importorskip("lxml") msg = ( "Passing literal xml to 'read_xml' is deprecated and " "will be removed in a future version. To read from a " @@ -287,8 +287,8 @@ def read_xml_iterparse_comp(comp_path, compression_only, **kwargs): # FILE / URL -@td.skip_if_no("lxml") def test_parser_consistency_file(xml_books): + pytest.importorskip("lxml") df_file_lxml = read_xml(xml_books, parser="lxml") df_file_etree = read_xml(xml_books, parser="etree") @@ -459,10 +459,9 @@ def test_file_handle_close(xml_books, parser): assert not f.closed -@td.skip_if_no("lxml") @pytest.mark.parametrize("val", ["", b""]) def test_empty_string_lxml(val): - from lxml.etree import XMLSyntaxError + lxml_etree = pytest.importorskip("lxml.etree") msg = "|".join( [ @@ -471,7 +470,7 @@ def test_empty_string_lxml(val): r"None \(line 0\)", ] ) - with pytest.raises(XMLSyntaxError, match=msg): + with pytest.raises(lxml_etree.XMLSyntaxError, match=msg): if isinstance(val, str): read_xml(StringIO(val), parser="lxml") else: @@ -504,8 +503,8 @@ def test_wrong_file_path(parser): @pytest.mark.network @pytest.mark.single_cpu -@td.skip_if_no("lxml") def test_url(httpserver, xml_file): + pytest.importorskip("lxml") with open(xml_file, encoding="utf-8") as f: httpserver.serve_content(content=f.read()) df_url = read_xml(httpserver.url, xpath=".//book[count(*)=4]") @@ -586,8 +585,8 @@ def test_whitespace(parser): # XPATH -@td.skip_if_no("lxml") def test_empty_xpath_lxml(xml_books): + pytest.importorskip("lxml") with pytest.raises(ValueError, match=("xpath does not return any nodes")): read_xml(xml_books, xpath=".//python", parser="lxml") @@ -599,11 +598,10 @@ def test_bad_xpath_etree(xml_books): read_xml(xml_books, xpath=".//[book]", parser="etree") -@td.skip_if_no("lxml") def test_bad_xpath_lxml(xml_books): - from lxml.etree import XPathEvalError + lxml_etree = pytest.importorskip("lxml.etree") - with pytest.raises(XPathEvalError, match=("Invalid expression")): + with pytest.raises(lxml_etree.XPathEvalError, match=("Invalid expression")): read_xml(xml_books, xpath=".//[book]", parser="lxml") @@ -659,8 +657,8 @@ def test_prefix_namespace(parser): tm.assert_frame_equal(df_iter, df_expected) -@td.skip_if_no("lxml") def test_consistency_default_namespace(): + pytest.importorskip("lxml") df_lxml = read_xml( StringIO(xml_default_nmsp), xpath=".//ns:row", @@ -678,8 +676,8 @@ def test_consistency_default_namespace(): tm.assert_frame_equal(df_lxml, df_etree) -@td.skip_if_no("lxml") def test_consistency_prefix_namespace(): + pytest.importorskip("lxml") df_lxml = read_xml( StringIO(xml_prefix_nmsp), xpath=".//doc:row", @@ -710,17 +708,16 @@ def test_missing_prefix_definition_etree(kml_cta_rail_lines): read_xml(kml_cta_rail_lines, xpath=".//kml:Placemark", parser="etree") -@td.skip_if_no("lxml") def test_missing_prefix_definition_lxml(kml_cta_rail_lines): - from lxml.etree import XPathEvalError + lxml_etree = pytest.importorskip("lxml.etree") - with pytest.raises(XPathEvalError, match=("Undefined namespace prefix")): + with pytest.raises(lxml_etree.XPathEvalError, match=("Undefined namespace prefix")): read_xml(kml_cta_rail_lines, xpath=".//kml:Placemark", parser="lxml") -@td.skip_if_no("lxml") @pytest.mark.parametrize("key", ["", None]) def test_none_namespace_prefix(key): + pytest.importorskip("lxml") with pytest.raises( TypeError, match=("empty namespace prefix is not supported in XPath") ): @@ -832,8 +829,8 @@ def test_empty_elems_only(parser): read_xml(StringIO(xml), xpath="./row", elems_only=True, parser=parser) -@td.skip_if_no("lxml") def test_attribute_centric_xml(): + pytest.importorskip("lxml") xml = """\ @@ -1062,8 +1059,8 @@ def test_ascii_encoding(xml_baby_names, parser): read_xml(xml_baby_names, encoding="ascii", parser=parser) -@td.skip_if_no("lxml") def test_parser_consistency_with_encoding(xml_baby_names): + pytest.importorskip("lxml") df_xpath_lxml = read_xml(xml_baby_names, parser="lxml", encoding="ISO-8859-1") df_xpath_etree = read_xml(xml_baby_names, parser="etree", encoding="iso-8859-1") @@ -1085,8 +1082,8 @@ def test_parser_consistency_with_encoding(xml_baby_names): tm.assert_frame_equal(df_iter_lxml, df_iter_etree) -@td.skip_if_no("lxml") def test_wrong_encoding_for_lxml(): + pytest.importorskip("lxml") # GH#45133 data = """ @@ -1132,8 +1129,8 @@ def test_wrong_parser(xml_books): # STYLESHEET -@td.skip_if_no("lxml") def test_stylesheet_file(kml_cta_rail_lines, xsl_flatten_doc): + pytest.importorskip("lxml") df_style = read_xml( kml_cta_rail_lines, xpath=".//k:Placemark", @@ -1159,8 +1156,8 @@ def test_stylesheet_file(kml_cta_rail_lines, xsl_flatten_doc): tm.assert_frame_equal(df_kml, df_iter) -@td.skip_if_no("lxml") def test_stylesheet_file_like(kml_cta_rail_lines, xsl_flatten_doc, mode): + pytest.importorskip("lxml") with open(xsl_flatten_doc, mode, encoding="utf-8" if mode == "r" else None) as f: df_style = read_xml( kml_cta_rail_lines, @@ -1172,10 +1169,10 @@ def test_stylesheet_file_like(kml_cta_rail_lines, xsl_flatten_doc, mode): tm.assert_frame_equal(df_kml, df_style) -@td.skip_if_no("lxml") def test_stylesheet_io(kml_cta_rail_lines, xsl_flatten_doc, mode): # note: By default the bodies of untyped functions are not checked, # consider using --check-untyped-defs + pytest.importorskip("lxml") xsl_obj: BytesIO | StringIO # type: ignore[annotation-unchecked] with open(xsl_flatten_doc, mode, encoding="utf-8" if mode == "r" else None) as f: @@ -1194,8 +1191,8 @@ def test_stylesheet_io(kml_cta_rail_lines, xsl_flatten_doc, mode): tm.assert_frame_equal(df_kml, df_style) -@td.skip_if_no("lxml") def test_stylesheet_buffered_reader(kml_cta_rail_lines, xsl_flatten_doc, mode): + pytest.importorskip("lxml") with open(xsl_flatten_doc, mode, encoding="utf-8" if mode == "r" else None) as f: xsl_obj = f.read() @@ -1209,8 +1206,8 @@ def test_stylesheet_buffered_reader(kml_cta_rail_lines, xsl_flatten_doc, mode): tm.assert_frame_equal(df_kml, df_style) -@td.skip_if_no("lxml") def test_style_charset(): + pytest.importorskip("lxml") xml = "<中文標籤>12" xsl = """\ @@ -1238,17 +1235,17 @@ def test_style_charset(): tm.assert_frame_equal(df_orig, df_style) -@td.skip_if_no("lxml") def test_not_stylesheet(kml_cta_rail_lines, xml_books): - from lxml.etree import XSLTParseError + lxml_etree = pytest.importorskip("lxml.etree") - with pytest.raises(XSLTParseError, match=("document is not a stylesheet")): + with pytest.raises( + lxml_etree.XSLTParseError, match=("document is not a stylesheet") + ): read_xml(kml_cta_rail_lines, stylesheet=xml_books) -@td.skip_if_no("lxml") def test_incorrect_xsl_syntax(kml_cta_rail_lines): - from lxml.etree import XMLSyntaxError + lxml_etree = pytest.importorskip("lxml.etree") xsl = """\ """ with pytest.raises( - XMLSyntaxError, match=("Extra content at the end of the document") + lxml_etree.XMLSyntaxError, match=("Extra content at the end of the document") ): read_xml(kml_cta_rail_lines, stylesheet=xsl) -@td.skip_if_no("lxml") def test_incorrect_xsl_eval(kml_cta_rail_lines): - from lxml.etree import XSLTParseError + lxml_etree = pytest.importorskip("lxml.etree") xsl = """\ """ - with pytest.raises(XSLTParseError, match=("failed to compile")): + with pytest.raises(lxml_etree.XSLTParseError, match=("failed to compile")): read_xml(kml_cta_rail_lines, stylesheet=xsl) -@td.skip_if_no("lxml") def test_incorrect_xsl_apply(kml_cta_rail_lines): - from lxml.etree import XSLTApplyError + lxml_etree = pytest.importorskip("lxml.etree") xsl = """\ @@ -1320,27 +1315,26 @@ def test_incorrect_xsl_apply(kml_cta_rail_lines): """ - with pytest.raises(XSLTApplyError, match=("Cannot resolve URI")): + with pytest.raises(lxml_etree.XSLTApplyError, match=("Cannot resolve URI")): read_xml(kml_cta_rail_lines, stylesheet=xsl) -@td.skip_if_no("lxml") def test_wrong_stylesheet(kml_cta_rail_lines, xml_data_path): - from lxml.etree import XMLSyntaxError + xml_etree = pytest.importorskip("lxml.etree") xsl = xml_data_path / "flatten.xsl" with pytest.raises( - XMLSyntaxError, + xml_etree.XMLSyntaxError, match=("Start tag expected, '<' not found"), ): read_xml(kml_cta_rail_lines, stylesheet=xsl) -@td.skip_if_no("lxml") def test_stylesheet_file_close(kml_cta_rail_lines, xsl_flatten_doc, mode): # note: By default the bodies of untyped functions are not checked, # consider using --check-untyped-defs + pytest.importorskip("lxml") xsl_obj: BytesIO | StringIO # type: ignore[annotation-unchecked] with open(xsl_flatten_doc, mode, encoding="utf-8" if mode == "r" else None) as f: @@ -1354,17 +1348,17 @@ def test_stylesheet_file_close(kml_cta_rail_lines, xsl_flatten_doc, mode): assert not f.closed -@td.skip_if_no("lxml") def test_stylesheet_with_etree(kml_cta_rail_lines, xsl_flatten_doc): + pytest.importorskip("lxml") with pytest.raises( ValueError, match=("To use stylesheet, you need lxml installed") ): read_xml(kml_cta_rail_lines, parser="etree", stylesheet=xsl_flatten_doc) -@td.skip_if_no("lxml") @pytest.mark.parametrize("val", ["", b""]) def test_empty_stylesheet(val): + pytest.importorskip("lxml") msg = ( "Passing literal xml to 'read_xml' is deprecated and " "will be removed in a future version. To read from a " @@ -1667,8 +1661,8 @@ def test_empty_data(xml_books, parser): ) -@td.skip_if_no("lxml") def test_online_stylesheet(): + pytest.importorskip("lxml") xml = """\ @@ -1998,9 +1992,9 @@ def test_unsuported_compression(parser): @pytest.mark.network @pytest.mark.single_cpu -@td.skip_if_no("s3fs") -@td.skip_if_no("lxml") def test_s3_parser_consistency(s3_public_bucket_with_data, s3so): + pytest.importorskip("s3fs") + pytest.importorskip("lxml") s3 = f"s3://{s3_public_bucket_with_data.name}/books.xml" df_lxml = read_xml(s3, parser="lxml", storage_options=s3so) diff --git a/pandas/tests/plotting/test_hist_method.py b/pandas/tests/plotting/test_hist_method.py index a2f68d587523b..6bab3d910d879 100644 --- a/pandas/tests/plotting/test_hist_method.py +++ b/pandas/tests/plotting/test_hist_method.py @@ -626,9 +626,9 @@ def test_hist_secondary_primary(self): assert ax.left_ax.get_yaxis().get_visible() assert ax.get_yaxis().get_visible() - @td.skip_if_no_mpl def test_hist_with_nans_and_weights(self): # GH 48884 + mpl_patches = pytest.importorskip("matplotlib.patches") df = DataFrame( [[np.nan, 0.2, 0.3], [0.4, np.nan, np.nan], [0.7, 0.8, 0.9]], columns=list("abc"), @@ -637,15 +637,15 @@ def test_hist_with_nans_and_weights(self): no_nan_df = DataFrame([[0.4, 0.2, 0.3], [0.7, 0.8, 0.9]], columns=list("abc")) no_nan_weights = np.array([[0.3, 0.25, 0.25], [0.45, 0.45, 0.45]]) - from matplotlib.patches import Rectangle - _, ax0 = mpl.pyplot.subplots() df.plot.hist(ax=ax0, weights=weights) - rects = [x for x in ax0.get_children() if isinstance(x, Rectangle)] + rects = [x for x in ax0.get_children() if isinstance(x, mpl_patches.Rectangle)] heights = [rect.get_height() for rect in rects] _, ax1 = mpl.pyplot.subplots() no_nan_df.plot.hist(ax=ax1, weights=no_nan_weights) - no_nan_rects = [x for x in ax1.get_children() if isinstance(x, Rectangle)] + no_nan_rects = [ + x for x in ax1.get_children() if isinstance(x, mpl_patches.Rectangle) + ] no_nan_heights = [rect.get_height() for rect in no_nan_rects] assert all(h0 == h1 for h0, h1 in zip(heights, no_nan_heights)) diff --git a/pandas/tests/series/test_api.py b/pandas/tests/series/test_api.py index bd33f499711db..be63d9500ce73 100644 --- a/pandas/tests/series/test_api.py +++ b/pandas/tests/series/test_api.py @@ -4,8 +4,6 @@ import numpy as np import pytest -from pandas.util._test_decorators import skip_if_no - import pandas as pd from pandas import ( DataFrame, @@ -166,9 +164,9 @@ def test_attrs(self): result = s + 1 assert result.attrs == {"version": 1} - @skip_if_no("jinja2") def test_inspect_getmembers(self): # GH38782 + pytest.importorskip("jinja2") ser = Series(dtype=object) msg = "Series._data is deprecated" with tm.assert_produces_warning( diff --git a/pandas/tests/test_downstream.py b/pandas/tests/test_downstream.py index 01efb01e63e1c..e81c2f9c086b0 100644 --- a/pandas/tests/test_downstream.py +++ b/pandas/tests/test_downstream.py @@ -2,7 +2,6 @@ Testing that we work in the downstream packages """ import array -import importlib import subprocess import sys @@ -28,16 +27,6 @@ from pandas.core.arrays.timedeltas import sequence_to_td64ns -def import_module(name): - # we *only* want to skip if the module is truly not available - # and NOT just an actual import error because of pandas changes - - try: - return importlib.import_module(name) - except ModuleNotFoundError: - pytest.skip(f"skipping as {name} not available") - - @pytest.fixture def df(): return DataFrame({"A": [1, 2, 3]}) @@ -49,10 +38,8 @@ def test_dask(df): olduse = pd.get_option("compute.use_numexpr") try: - toolz = import_module("toolz") # noqa: F841 - dask = import_module("dask") # noqa: F841 - - import dask.dataframe as dd + pytest.importorskip("toolz") + dd = pytest.importorskip("dask.dataframe") ddf = dd.from_pandas(df, npartitions=3) assert ddf.A is not None @@ -67,9 +54,8 @@ def test_dask_ufunc(): olduse = pd.get_option("compute.use_numexpr") try: - dask = import_module("dask") # noqa: F841 - import dask.array as da - import dask.dataframe as dd + da = pytest.importorskip("dask.array") + dd = pytest.importorskip("dask.dataframe") s = Series([1.5, 2.3, 3.7, 4.0]) ds = dd.from_pandas(s, npartitions=2) @@ -81,11 +67,10 @@ def test_dask_ufunc(): pd.set_option("compute.use_numexpr", olduse) -@td.skip_if_no("dask") def test_construct_dask_float_array_int_dtype_match_ndarray(): # GH#40110 make sure we treat a float-dtype dask array with the same # rules we would for an ndarray - import dask.dataframe as dd + dd = pytest.importorskip("dask.dataframe") arr = np.array([1, 2.5, 3]) darr = dd.from_array(arr) @@ -109,17 +94,15 @@ def test_construct_dask_float_array_int_dtype_match_ndarray(): def test_xarray(df): - xarray = import_module("xarray") # noqa: F841 + pytest.importorskip("xarray") assert df.to_xarray() is not None -@td.skip_if_no("cftime") -@td.skip_if_no("xarray", "0.21.0") def test_xarray_cftimeindex_nearest(): # https://github.com/pydata/xarray/issues/3751 - import cftime - import xarray + cftime = pytest.importorskip("cftime") + xarray = pytest.importorskip("xarray", minversion="0.21.0") times = xarray.cftime_range("0001", periods=2) key = cftime.DatetimeGregorian(2000, 1, 1) @@ -151,8 +134,7 @@ def test_oo_optimized_datetime_index_unpickle(): def test_statsmodels(): - statsmodels = import_module("statsmodels") # noqa: F841 - import statsmodels.formula.api as smf + smf = pytest.importorskip("statsmodels.formula.api") df = DataFrame( {"Lottery": range(5), "Literacy": range(5), "Pop1831": range(100, 105)} @@ -161,7 +143,7 @@ def test_statsmodels(): def test_scikit_learn(): - sklearn = import_module("sklearn") # noqa: F841 + pytest.importorskip("sklearn") from sklearn import ( datasets, svm, @@ -174,7 +156,7 @@ def test_scikit_learn(): def test_seaborn(): - seaborn = import_module("seaborn") + seaborn = pytest.importorskip("seaborn") tips = DataFrame( {"day": pd.date_range("2023", freq="D", periods=5), "total_bill": range(5)} ) @@ -184,15 +166,14 @@ def test_seaborn(): def test_pandas_gbq(): # Older versions import from non-public, non-existent pandas funcs pytest.importorskip("pandas_gbq", minversion="0.10.0") - pandas_gbq = import_module("pandas_gbq") # noqa: F841 def test_pandas_datareader(): - pandas_datareader = import_module("pandas_datareader") # noqa: F841 + pytest.importorskip("pandas_datareader") def test_pyarrow(df): - pyarrow = import_module("pyarrow") + pyarrow = pytest.importorskip("pyarrow") table = pyarrow.Table.from_pandas(df) result = table.to_pandas() tm.assert_frame_equal(result, df) @@ -200,7 +181,7 @@ def test_pyarrow(df): def test_yaml_dump(df): # GH#42748 - yaml = import_module("yaml") + yaml = pytest.importorskip("yaml") dumped = yaml.dump(df) @@ -256,9 +237,7 @@ def test_frame_setitem_dask_array_into_new_col(): olduse = pd.get_option("compute.use_numexpr") try: - dask = import_module("dask") # noqa: F841 - - import dask.array as da + da = pytest.importorskip("dask.array") dda = da.array([1, 2]) df = DataFrame({"a": ["a", "b"]}) @@ -353,3 +332,21 @@ def test_from_obscure_array(dtype, array_likes): result = idx_cls(arr) expected = idx_cls(data) tm.assert_index_equal(result, expected) + + +def test_xarray_coerce_unit(): + # GH44053 + xr = pytest.importorskip("xarray") + + arr = xr.DataArray([1, 2, 3]) + result = pd.to_datetime(arr, unit="ns") + expected = DatetimeIndex( + [ + "1970-01-01 00:00:00.000000001", + "1970-01-01 00:00:00.000000002", + "1970-01-01 00:00:00.000000003", + ], + dtype="datetime64[ns]", + freq=None, + ) + tm.assert_index_equal(result, expected) diff --git a/pandas/tests/tools/test_to_datetime.py b/pandas/tests/tools/test_to_datetime.py index e5dfae169453f..83b4949bc32cd 100644 --- a/pandas/tests/tools/test_to_datetime.py +++ b/pandas/tests/tools/test_to_datetime.py @@ -3540,25 +3540,6 @@ def test_empty_string_datetime_coerce__unit(): tm.assert_index_equal(expected, result) -@td.skip_if_no("xarray") -def test_xarray_coerce_unit(): - # GH44053 - import xarray as xr - - arr = xr.DataArray([1, 2, 3]) - result = to_datetime(arr, unit="ns") - expected = DatetimeIndex( - [ - "1970-01-01 00:00:00.000000001", - "1970-01-01 00:00:00.000000002", - "1970-01-01 00:00:00.000000003", - ], - dtype="datetime64[ns]", - freq=None, - ) - tm.assert_index_equal(result, expected) - - @pytest.mark.parametrize("cache", [True, False]) def test_to_datetime_monotonic_increasing_index(cache): # GH28238 diff --git a/pandas/tests/window/test_online.py b/pandas/tests/window/test_online.py index 5974de0ae4009..8c4fb1fe6872b 100644 --- a/pandas/tests/window/test_online.py +++ b/pandas/tests/window/test_online.py @@ -6,7 +6,6 @@ is_platform_mac, is_platform_windows, ) -import pandas.util._test_decorators as td from pandas import ( DataFrame, @@ -24,8 +23,9 @@ ), ] +pytest.importorskip("numba") + -@td.skip_if_no("numba") @pytest.mark.filterwarnings("ignore") # Filter warnings when parallel=True and the function can't be parallelized by Numba class TestEWM: diff --git a/pandas/util/_test_decorators.py b/pandas/util/_test_decorators.py index 59f30cd4e6e8f..03011a1ffe622 100644 --- a/pandas/util/_test_decorators.py +++ b/pandas/util/_test_decorators.py @@ -82,15 +82,6 @@ def safe_import(mod_name: str, min_version: str | None = None): return False -def _skip_if_no_mpl() -> bool: - mod = safe_import("matplotlib") - if mod: - mod.use("Agg") - return False - else: - return True - - def _skip_if_not_us_locale() -> bool: lang, _ = locale.getlocale() if lang != "en_US": @@ -136,9 +127,10 @@ def skip_if_no(package: str, min_version: str | None = None) -> pytest.MarkDecor evaluated during test collection. An attempt will be made to import the specified ``package`` and optionally ensure it meets the ``min_version`` - The mark can be used as either a decorator for a test function or to be + The mark can be used as either a decorator for a test class or to be applied to parameters in pytest.mark.parametrize calls or parametrized - fixtures. + fixtures. Use pytest.importorskip if an imported moduled is later needed + or for test functions. If the import and version check are unsuccessful, then the test function (or test case when used in conjunction with parametrization) will be @@ -165,10 +157,9 @@ def skip_if_no(package: str, min_version: str | None = None) -> pytest.MarkDecor ) -skip_if_no_mpl = pytest.mark.skipif( - _skip_if_no_mpl(), reason="Missing matplotlib dependency" +skip_if_mpl = pytest.mark.skipif( + bool(safe_import("matplotlib")), reason="matplotlib is present" ) -skip_if_mpl = pytest.mark.skipif(not _skip_if_no_mpl(), reason="matplotlib is present") skip_if_32bit = pytest.mark.skipif(not IS64, reason="skipping for 32 bit") skip_if_windows = pytest.mark.skipif(is_platform_windows(), reason="Running on Windows") skip_if_not_us_locale = pytest.mark.skipif(