11
11
from typing import Type
12
12
from typing import TypeVar
13
13
14
+ from .warning_types import PytestDeprecationWarning
15
+
14
16
15
17
class OutcomeException (BaseException ):
16
18
"""OutcomeException and its subclass instances indicate and contain info
@@ -192,7 +194,11 @@ def xfail(reason: str = "") -> NoReturn:
192
194
193
195
194
196
def importorskip (
195
- modname : str , minversion : Optional [str ] = None , reason : Optional [str ] = None
197
+ modname : str ,
198
+ minversion : Optional [str ] = None ,
199
+ reason : Optional [str ] = None ,
200
+ * ,
201
+ exc_type : Optional [Type [ImportError ]] = None ,
196
202
) -> Any :
197
203
"""Import and return the requested module ``modname``, or skip the
198
204
current test if the module cannot be imported.
@@ -205,30 +211,81 @@ def importorskip(
205
211
:param reason:
206
212
If given, this reason is shown as the message when the module cannot
207
213
be imported.
214
+ :param exc_type:
215
+ The exception that should be captured in order to skip modules.
216
+ Must be :py:class:`ImportError` or a subclass.
217
+
218
+ If the module can be imported but raises :class:`ImportError`, pytest will
219
+ issue a warning to the user, as often users expect the module not to be
220
+ found (which would raise :class:`ModuleNotFoundError` instead).
221
+
222
+ This warning can be suppressed by passing ``exc_type=ImportError`` explicitly.
223
+
224
+ See :ref:`import-or-skip-import-error` for details.
225
+
208
226
209
227
:returns:
210
228
The imported module. This should be assigned to its canonical name.
211
229
212
230
Example::
213
231
214
232
docutils = pytest.importorskip("docutils")
233
+
234
+ .. versionadded:: 8.2
235
+
236
+ The ``exc_type`` parameter.
215
237
"""
216
238
import warnings
217
239
218
240
__tracebackhide__ = True
219
241
compile (modname , "" , "eval" ) # to catch syntaxerrors
220
242
243
+ # Until pytest 9.1, we will warn the user if we catch ImportError (instead of ModuleNotFoundError),
244
+ # as this might be hiding an installation/environment problem, which is not usually what is intended
245
+ # when using importorskip() (#11523).
246
+ # In 9.1, to keep the function signature compatible, we just change the code below to:
247
+ # 1. Use `exc_type = ModuleNotFoundError` if `exc_type` is not given.
248
+ # 2. Remove `warn_on_import` and the warning handling.
249
+ if exc_type is None :
250
+ exc_type = ImportError
251
+ warn_on_import_error = True
252
+ else :
253
+ warn_on_import_error = False
254
+
255
+ skipped : Optional [Skipped ] = None
256
+ warning : Optional [Warning ] = None
257
+
221
258
with warnings .catch_warnings ():
222
259
# Make sure to ignore ImportWarnings that might happen because
223
260
# of existing directories with the same name we're trying to
224
261
# import but without a __init__.py file.
225
262
warnings .simplefilter ("ignore" )
263
+
226
264
try :
227
265
__import__ (modname )
228
- except ImportError as exc :
266
+ except exc_type as exc :
267
+ # Do not raise or issue warnings inside the catch_warnings() block.
229
268
if reason is None :
230
269
reason = f"could not import { modname !r} : { exc } "
231
- raise Skipped (reason , allow_module_level = True ) from None
270
+ skipped = Skipped (reason , allow_module_level = True )
271
+
272
+ if warn_on_import_error and not isinstance (exc , ModuleNotFoundError ):
273
+ lines = [
274
+ "" ,
275
+ f"Module '{ modname } ' was found, but when imported by pytest it raised:" ,
276
+ f" { exc !r} " ,
277
+ "In pytest 9.1 this warning will become an error by default." ,
278
+ "You can fix the underlying problem, or alternatively overwrite this behavior and silence this "
279
+ "warning by passing exc_type=ImportError explicitly." ,
280
+ "See https://docs.pytest.org/en/stable/deprecations.html#pytest-importorskip-default-behavior-regarding-importerror" ,
281
+ ]
282
+ warning = PytestDeprecationWarning ("\n " .join (lines ))
283
+
284
+ if warning :
285
+ warnings .warn (warning , stacklevel = 2 )
286
+ if skipped :
287
+ raise skipped
288
+
232
289
mod = sys .modules [modname ]
233
290
if minversion is None :
234
291
return mod
0 commit comments