From 543262c4c83cfaa125394db82fc5c9653e92c076 Mon Sep 17 00:00:00 2001 From: shekhuverma Date: Fri, 19 Apr 2024 19:26:57 +0530 Subject: [PATCH] added exc_types parameter in importorskip --- changelog/11523.improvement.rst | 1 + src/_pytest/outcomes.py | 23 +++++++++++++++++++++-- testing/test_runner.py | 33 ++++++++++++--------------------- 3 files changed, 34 insertions(+), 23 deletions(-) create mode 100644 changelog/11523.improvement.rst diff --git a/changelog/11523.improvement.rst b/changelog/11523.improvement.rst new file mode 100644 index 000000000..91cd6ccc2 --- /dev/null +++ b/changelog/11523.improvement.rst @@ -0,0 +1 @@ +pytest.importorskip will now skip both ImportError and ModuleNotFoundError. diff --git a/src/_pytest/outcomes.py b/src/_pytest/outcomes.py index 980cfe6de..0e4e5f566 100644 --- a/src/_pytest/outcomes.py +++ b/src/_pytest/outcomes.py @@ -11,6 +11,8 @@ from typing import Protocol from typing import Type from typing import TypeVar +from .warning_types import PytestDeprecationWarning + class OutcomeException(BaseException): """OutcomeException and its subclass instances indicate and contain info @@ -192,7 +194,10 @@ 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: Optional[Type[ImportError]] = None, ) -> Any: """Import and return the requested module ``modname``, or skip the current test if the module cannot be imported. @@ -205,6 +210,8 @@ def importorskip( :param reason: If given, this reason is shown as the message when the module cannot be imported. + :param exc_type: + If given, modules are skipped if this exception is raised. :returns: The imported module. This should be assigned to its canonical name. @@ -223,12 +230,24 @@ def importorskip( # of existing directories with the same name we're trying to # import but without a __init__.py file. warnings.simplefilter("ignore") + + if exc_type is None: + exc_type = ModuleNotFoundError + else: + exc_type = ImportError + warnings.warn( + PytestDeprecationWarning( + "The Default behaviour will change to ImportError in future", + ) + ) + try: __import__(modname) - except ModuleNotFoundError as exc: + except exc_type as exc: if reason is None: reason = f"could not import {modname!r}: {exc}" raise Skipped(reason, allow_module_level=True) from None + mod = sys.modules[modname] if minversion is None: return mod diff --git a/testing/test_runner.py b/testing/test_runner.py index 02cfbb2c5..fd8d35187 100644 --- a/testing/test_runner.py +++ b/testing/test_runner.py @@ -1,5 +1,4 @@ # mypy: allow-untyped-defs -import builtins from functools import partial import inspect import os @@ -8,8 +7,6 @@ import sys import types from typing import Dict from typing import List -from typing import Mapping -from typing import Sequence from typing import Tuple from typing import Type @@ -22,6 +19,7 @@ from _pytest.config import ExitCode from _pytest.monkeypatch import MonkeyPatch from _pytest.outcomes import OutcomeException from _pytest.pytester import Pytester +from _pytest.warning_types import PytestDeprecationWarning import pytest @@ -765,25 +763,18 @@ def test_importorskip_imports_last_module_part() -> None: assert os.path == ospath -def test_importorskip_importError_Exception() -> None: - ## Mocking the import function to raise a importError - realimport = builtins.__import__ +def test_importorskip_importError_warning(pytester: Pytester) -> None: + """ + importorskip() will only skip modules by ImportError as well as ModuleNotFoundError + will give warning when using ImportError + #11523 + """ + fn = pytester.makepyfile("raise ImportError('some specific problem')") + pytester.syspathinsert() - def myimport( - name: str, - globals: Mapping[str, object] | None = None, - locals: Mapping[str, object] | None = None, - fromlist: Sequence[str] = (), - level: int = 0, - ) -> types.ModuleType: - raise ImportError - - builtins.__import__ = myimport - - with pytest.raises(ImportError): - pytest.importorskip("abcdefghi") - - builtins.__import__ = realimport + with pytest.raises(pytest.skip.Exception): + with pytest.warns(PytestDeprecationWarning): + pytest.importorskip(fn.stem, exc_type=ImportError) def test_importorskip_dev_module(monkeypatch) -> None: