From 91be856742d90ff1ecceb787a9a99e64bbc1c9cc Mon Sep 17 00:00:00 2001 From: Matthew Roeschke <10647082+mroeschke@users.noreply.github.com> Date: Wed, 2 Aug 2023 11:14:23 -0700 Subject: [PATCH 1/6] TST: Use more pytest.importorskip --- pandas/tests/io/formats/test_to_excel.py | 6 +- pandas/tests/io/test_html.py | 12 ++-- pandas/tests/io/test_orc.py | 15 ++--- pandas/tests/io/test_parquet.py | 15 +++-- pandas/tests/io/test_pickle.py | 2 +- pandas/tests/io/test_s3.py | 6 +- pandas/tests/plotting/test_hist_method.py | 10 ++-- pandas/tests/series/test_api.py | 4 +- pandas/tests/test_downstream.py | 69 +++++++++++------------ pandas/tests/tools/test_to_datetime.py | 19 ------- pandas/util/_test_decorators.py | 14 +---- 11 files changed, 67 insertions(+), 105 deletions(-) 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/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/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..dad85ce67a6de 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") 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/util/_test_decorators.py b/pandas/util/_test_decorators.py index 59f30cd4e6e8f..a1d4a9598171f 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": @@ -165,10 +156,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( + not 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( From 20f8a4a077921935eeaf0be65776d822ed3848c8 Mon Sep 17 00:00:00 2001 From: Matthew Roeschke <10647082+mroeschke@users.noreply.github.com> Date: Wed, 2 Aug 2023 11:27:13 -0700 Subject: [PATCH 2/6] Fix xml --- pandas/tests/io/test_common.py | 5 +- pandas/tests/io/test_fsspec.py | 23 ++++---- pandas/tests/io/xml/test_to_xml.py | 50 ++++++++---------- pandas/tests/io/xml/test_xml.py | 84 ++++++++++++++---------------- 4 files changed, 77 insertions(+), 85 deletions(-) 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/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) From 50a8bf36f973e5381174f462754d3811449185d2 Mon Sep 17 00:00:00 2001 From: Matthew Roeschke <10647082+mroeschke@users.noreply.github.com> Date: Wed, 2 Aug 2023 12:12:47 -0700 Subject: [PATCH 3/6] Final cleanup --- pandas/tests/arrays/interval/test_interval.py | 23 +++------- pandas/tests/arrays/string_/test_string.py | 8 +--- pandas/tests/frame/test_api.py | 7 +--- pandas/tests/frame/test_ufunc.py | 10 +---- pandas/tests/generic/test_to_xarray.py | 6 +-- pandas/tests/groupby/aggregate/test_numba.py | 42 +++++++++++-------- pandas/tests/groupby/test_numba.py | 5 +-- pandas/tests/groupby/test_timegrouper.py | 5 +-- pandas/tests/groupby/transform/test_numba.py | 36 +++++++++------- pandas/tests/io/excel/test_readers.py | 3 +- pandas/tests/io/formats/test_to_string.py | 4 +- pandas/tests/io/parser/test_network.py | 8 ++-- pandas/tests/io/parser/test_upcast.py | 4 +- pandas/tests/window/test_online.py | 4 +- pandas/util/_test_decorators.py | 5 ++- 15 files changed, 77 insertions(+), 93 deletions(-) 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/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/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 a1d4a9598171f..fc8535c64e91e 100644 --- a/pandas/util/_test_decorators.py +++ b/pandas/util/_test_decorators.py @@ -127,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 From 78b9c8ba055555c01838adef6630e58b95326815 Mon Sep 17 00:00:00 2001 From: Matthew Roeschke <10647082+mroeschke@users.noreply.github.com> Date: Wed, 2 Aug 2023 14:46:27 -0700 Subject: [PATCH 4/6] Fix condition --- pandas/util/_test_decorators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/util/_test_decorators.py b/pandas/util/_test_decorators.py index fc8535c64e91e..03011a1ffe622 100644 --- a/pandas/util/_test_decorators.py +++ b/pandas/util/_test_decorators.py @@ -158,7 +158,7 @@ def skip_if_no(package: str, min_version: str | None = None) -> pytest.MarkDecor skip_if_mpl = pytest.mark.skipif( - not bool(safe_import("matplotlib")), reason="matplotlib is present" + bool(safe_import("matplotlib")), 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") From 69091296fd45390d17f3da1589718d9cdf4ea77d Mon Sep 17 00:00:00 2001 From: Matthew Roeschke <10647082+mroeschke@users.noreply.github.com> Date: Wed, 2 Aug 2023 15:09:32 -0700 Subject: [PATCH 5/6] Fix downstream --- pandas/tests/test_downstream.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/tests/test_downstream.py b/pandas/tests/test_downstream.py index dad85ce67a6de..e81c2f9c086b0 100644 --- a/pandas/tests/test_downstream.py +++ b/pandas/tests/test_downstream.py @@ -39,7 +39,7 @@ def test_dask(df): try: pytest.importorskip("toolz") - dd = pytest.importorskip("dask") + dd = pytest.importorskip("dask.dataframe") ddf = dd.from_pandas(df, npartitions=3) assert ddf.A is not None From 36950e04476940cded64d6e0d95a3c9af7544d8b Mon Sep 17 00:00:00 2001 From: Matthew Roeschke <10647082+mroeschke@users.noreply.github.com> Date: Wed, 2 Aug 2023 16:09:42 -0700 Subject: [PATCH 6/6] cleanup after matplotlib --- .../tests/io/formats/style/test_matplotlib.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) 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"])