diff --git a/pandas/tests/io/test_html.py b/pandas/tests/io/test_html.py
index 225503cddceee..bd6fc6f57c496 100644
--- a/pandas/tests/io/test_html.py
+++ b/pandas/tests/io/test_html.py
@@ -77,10 +77,8 @@ def test_same_ordering(datapath):
@pytest.mark.parametrize("flavor", [
- pytest.param('bs4', marks=pytest.mark.skipif(
- not td.safe_import('lxml'), reason='No bs4')),
- pytest.param('lxml', marks=pytest.mark.skipif(
- not td.safe_import('lxml'), reason='No lxml'))], scope="class")
+ pytest.param('bs4', marks=td.skip_if_no('lxml')),
+ pytest.param('lxml', marks=td.skip_if_no('lxml'))], scope="class")
class TestReadHtml:
@pytest.fixture(autouse=True)
diff --git a/pandas/util/_test_decorators.py b/pandas/util/_test_decorators.py
index 5fa56c8505358..4cc316ffdd7ab 100644
--- a/pandas/util/_test_decorators.py
+++ b/pandas/util/_test_decorators.py
@@ -24,7 +24,9 @@ def test_foo():
For more information, refer to the ``pytest`` documentation on ``skipif``.
"""
import locale
+from typing import Optional
+from _pytest.mark.structures import MarkDecorator
import pytest
from pandas.compat import is_platform_32bit, is_platform_windows
@@ -97,38 +99,45 @@ def _skip_if_no_scipy():
safe_import('scipy.signal'))
-def skip_if_no(package, min_version=None):
+def skip_if_no(
+ package: str,
+ min_version: Optional[str] = None
+) -> MarkDecorator:
"""
- Generic function to help skip test functions when required packages are not
+ Generic function to help skip tests when required packages are not
present on the testing system.
- Intended for use as a decorator, this function will wrap the decorated
- function with a pytest ``skip_if`` mark. During a pytest test suite
- execution, that mark will attempt to import the specified ``package`` and
- optionally ensure it meets the ``min_version``. If the import and version
- check are unsuccessful, then the decorated function will be skipped.
+ This function returns a pytest mark with a skip condition that will be
+ 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
+ applied to parameters in pytest.mark.parametrize calls or parametrized
+ fixtures.
+
+ If the import and version check are unsuccessful, then the test function
+ (or test case when used in conjunction with parametrization) will be
+ skipped.
Parameters
----------
package: str
- The name of the package required by the decorated function
+ The name of the required package.
min_version: str or None, default None
- Optional minimum version of the package required by the decorated
- function
+ Optional minimum version of the package.
Returns
-------
- decorated_func: function
- The decorated function wrapped within a pytest ``skip_if`` mark
+ _pytest.mark.structures.MarkDecorator
+ a pytest.mark.skipif to use as either a test decorator or a
+ parametrization mark.
"""
- def decorated_func(func):
- msg = "Could not import '{}'".format(package)
- if min_version:
- msg += " satisfying a min_version of {}".format(min_version)
- return pytest.mark.skipif(
- not safe_import(package, min_version=min_version), reason=msg
- )(func)
- return decorated_func
+ msg = "Could not import '{}'".format(package)
+ if min_version:
+ msg += " satisfying a min_version of {}".format(min_version)
+ return pytest.mark.skipif(
+ not safe_import(package, min_version=min_version), reason=msg
+ )
skip_if_no_mpl = pytest.mark.skipif(_skip_if_no_mpl(),