From dc2080f3be9a75830074989078006752cf150ac5 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Thu, 19 Oct 2023 18:10:41 +0100 Subject: [PATCH 01/10] gh-11523: add importorskip(exc=...) kwarg Fixes #11523 --- src/_pytest/outcomes.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/_pytest/outcomes.py b/src/_pytest/outcomes.py index a8984c5b9..491c6b229 100644 --- a/src/_pytest/outcomes.py +++ b/src/_pytest/outcomes.py @@ -249,7 +249,8 @@ def xfail(reason: str = "") -> NoReturn: def importorskip( - modname: str, minversion: Optional[str] = None, reason: Optional[str] = None + modname: str, minversion: Optional[str] = None, reason: Optional[str] = None, + exc: type[ImportError] = ImportError, ) -> Any: """Import and return the requested module ``modname``, or skip the current test if the module cannot be imported. @@ -282,9 +283,9 @@ def importorskip( warnings.simplefilter("ignore") try: __import__(modname) - except ImportError as exc: + except exc as e: if reason is None: - reason = f"could not import {modname!r}: {exc}" + reason = f"could not import {modname!r}: {e}" raise Skipped(reason, allow_module_level=True) from None mod = sys.modules[modname] if minversion is None: From f34091f0ec02eef62b8976dc301770a3539d1875 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 19 Oct 2023 17:11:49 +0000 Subject: [PATCH 02/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/_pytest/outcomes.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/_pytest/outcomes.py b/src/_pytest/outcomes.py index 491c6b229..ce7db08a4 100644 --- a/src/_pytest/outcomes.py +++ b/src/_pytest/outcomes.py @@ -249,7 +249,9 @@ def xfail(reason: str = "") -> NoReturn: def importorskip( - modname: str, minversion: Optional[str] = None, reason: Optional[str] = None, + modname: str, + minversion: Optional[str] = None, + reason: Optional[str] = None, exc: type[ImportError] = ImportError, ) -> Any: """Import and return the requested module ``modname``, or skip the From 5ba3351b52876a43a58673400b61fd3b3d7b6a02 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Thu, 19 Oct 2023 18:17:27 +0100 Subject: [PATCH 03/10] add tests --- testing/test_skipping.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/testing/test_skipping.py b/testing/test_skipping.py index b7e448df3..6f632f650 100644 --- a/testing/test_skipping.py +++ b/testing/test_skipping.py @@ -1428,6 +1428,26 @@ def test_importorskip() -> None: pytest.importorskip("doesnotexist") +def test_importorskip_module_not_found() -> None: + with pytest.raises( + pytest.skip.Exception, + match="^could not import 'doesnotexist': No module named .*", + ): + pytest.importorskip("doesnotexist", exc=ModuleNotFoundError) + + +def test_importorskip_module_not_found_raises_on_import_error(monkeypatch, tmp_path) -> None: + on_path = tmp_path / "on_path" + on_path.mkdir() + + (on_path / "doesnotexist.py").write_bytes(b"1 / 0") + + monkeypatch.syspath_prepend(some_dir) + + with pytest.raises(ImportError): + pytest.importorskip("doesnotexist", exc=ModuleNotFoundError) + + def test_relpath_rootdir(pytester: Pytester) -> None: pytester.makepyfile( **{ From 5e6dc297c2a9debbb9b994082217408c315754b1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 19 Oct 2023 17:18:55 +0000 Subject: [PATCH 04/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- testing/test_skipping.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/testing/test_skipping.py b/testing/test_skipping.py index 6f632f650..d8ffdbf54 100644 --- a/testing/test_skipping.py +++ b/testing/test_skipping.py @@ -1436,7 +1436,9 @@ def test_importorskip_module_not_found() -> None: pytest.importorskip("doesnotexist", exc=ModuleNotFoundError) -def test_importorskip_module_not_found_raises_on_import_error(monkeypatch, tmp_path) -> None: +def test_importorskip_module_not_found_raises_on_import_error( + monkeypatch, tmp_path +) -> None: on_path = tmp_path / "on_path" on_path.mkdir() From e74a6da6991d673b8c59329fb7ca9da638cc2b43 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Thu, 19 Oct 2023 18:22:56 +0100 Subject: [PATCH 05/10] use old style type annotation --- src/_pytest/outcomes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_pytest/outcomes.py b/src/_pytest/outcomes.py index ce7db08a4..93067b6c2 100644 --- a/src/_pytest/outcomes.py +++ b/src/_pytest/outcomes.py @@ -252,7 +252,7 @@ def importorskip( modname: str, minversion: Optional[str] = None, reason: Optional[str] = None, - exc: type[ImportError] = ImportError, + exc: Type[ImportError] = ImportError, ) -> Any: """Import and return the requested module ``modname``, or skip the current test if the module cannot be imported. From 6e40458fc1cfece2fe7ddaa353478a07287fc433 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Thu, 19 Oct 2023 18:25:43 +0100 Subject: [PATCH 06/10] make exc kwonly --- src/_pytest/outcomes.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/_pytest/outcomes.py b/src/_pytest/outcomes.py index 93067b6c2..ddcdbcacc 100644 --- a/src/_pytest/outcomes.py +++ b/src/_pytest/outcomes.py @@ -252,6 +252,7 @@ def importorskip( modname: str, minversion: Optional[str] = None, reason: Optional[str] = None, + *, exc: Type[ImportError] = ImportError, ) -> Any: """Import and return the requested module ``modname``, or skip the From ff12e083c596d871f10af7199958e10df82aee1c Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Thu, 19 Oct 2023 18:25:56 +0100 Subject: [PATCH 07/10] doc exc param to importorskip --- src/_pytest/outcomes.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/_pytest/outcomes.py b/src/_pytest/outcomes.py index ddcdbcacc..25aabf2db 100644 --- a/src/_pytest/outcomes.py +++ b/src/_pytest/outcomes.py @@ -266,6 +266,9 @@ def importorskip( :param reason: If given, this reason is shown as the message when the module cannot be imported. + :param exc: + Either ImportError or ModuleNotFoundError, the exception to catch + when importing. :returns: The imported module. This should be assigned to its canonical name. From 4821a41967c4ad527ee659cc826fd8fc7248e392 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Thu, 19 Oct 2023 18:29:11 +0100 Subject: [PATCH 08/10] add type annotations --- testing/test_skipping.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/testing/test_skipping.py b/testing/test_skipping.py index d8ffdbf54..cf4883844 100644 --- a/testing/test_skipping.py +++ b/testing/test_skipping.py @@ -1,7 +1,9 @@ import sys import textwrap +from pathlib import Path import pytest +from _pytest.monkeypatch import MonkeyPatch from _pytest.pytester import Pytester from _pytest.runner import runtestprotocol from _pytest.skipping import evaluate_skip_marks @@ -1437,7 +1439,7 @@ def test_importorskip_module_not_found() -> None: def test_importorskip_module_not_found_raises_on_import_error( - monkeypatch, tmp_path + monkeypatch: MonkeyPatch, tmp_path: Path ) -> None: on_path = tmp_path / "on_path" on_path.mkdir() From db62b80e8d17114c54ce88cef1a1f634a9146dfd Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Thu, 19 Oct 2023 18:29:24 +0100 Subject: [PATCH 09/10] fix some_dir NameError --- testing/test_skipping.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/test_skipping.py b/testing/test_skipping.py index cf4883844..27ba75553 100644 --- a/testing/test_skipping.py +++ b/testing/test_skipping.py @@ -1446,7 +1446,7 @@ def test_importorskip_module_not_found_raises_on_import_error( (on_path / "doesnotexist.py").write_bytes(b"1 / 0") - monkeypatch.syspath_prepend(some_dir) + monkeypatch.syspath_prepend(on_path) with pytest.raises(ImportError): pytest.importorskip("doesnotexist", exc=ModuleNotFoundError) From 662fc9c01292176cafb044a3766897a09d090fd6 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Thu, 19 Oct 2023 18:31:45 +0100 Subject: [PATCH 10/10] docs --- doc/en/how-to/skipping.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/doc/en/how-to/skipping.rst b/doc/en/how-to/skipping.rst index 1fc6f5e08..2a1c3f23e 100644 --- a/doc/en/how-to/skipping.rst +++ b/doc/en/how-to/skipping.rst @@ -197,6 +197,14 @@ the test. You can also skip based on the version number of a library: The version will be read from the specified module's ``__version__`` attribute. +Sometimes importing a module can fail due to an exception, if you want to +only skip if the module does not exist pass ModuleNotFoundError as the +``exc`` kwarg: + +.. code-block:: python + + docutils = pytest.importorskip("docutils", exc=ModuleNotFoundError) + Summary ~~~~~~~