|
1 | 1 | """ command line options, ini-file and conftest.py processing. """
|
2 | 2 | import argparse
|
| 3 | +import contextlib |
3 | 4 | import copy
|
4 | 5 | import enum
|
5 | 6 | import inspect
|
@@ -87,10 +88,15 @@ class ExitCode(enum.IntEnum):
|
87 | 88 |
|
88 | 89 | class ConftestImportFailure(Exception):
|
89 | 90 | def __init__(self, path, excinfo):
|
90 |
| - Exception.__init__(self, path, excinfo) |
| 91 | + super().__init__(path, excinfo) |
91 | 92 | self.path = path
|
92 | 93 | self.excinfo = excinfo # type: Tuple[Type[Exception], Exception, TracebackType]
|
93 | 94 |
|
| 95 | + def __str__(self): |
| 96 | + return "{}: {} (from {})".format( |
| 97 | + self.excinfo[0].__name__, self.excinfo[1], self.path |
| 98 | + ) |
| 99 | + |
94 | 100 |
|
95 | 101 | def main(args=None, plugins=None) -> Union[int, ExitCode]:
|
96 | 102 | """ return exit code, after performing an in-process test run.
|
@@ -280,19 +286,6 @@ def _prepareconfig(
|
280 | 286 | raise
|
281 | 287 |
|
282 | 288 |
|
283 |
| -def _fail_on_non_top_pytest_plugins(conftestpath, confcutdir): |
284 |
| - msg = ( |
285 |
| - "Defining 'pytest_plugins' in a non-top-level conftest is no longer supported:\n" |
286 |
| - "It affects the entire test suite instead of just below the conftest as expected.\n" |
287 |
| - " {}\n" |
288 |
| - "Please move it to a top level conftest file at the rootdir:\n" |
289 |
| - " {}\n" |
290 |
| - "For more information, visit:\n" |
291 |
| - " https://docs.pytest.org/en/latest/deprecations.html#pytest-plugins-in-non-top-level-conftest-files" |
292 |
| - ) |
293 |
| - fail(msg.format(conftestpath, confcutdir), pytrace=False) |
294 |
| - |
295 |
| - |
296 | 289 | class PytestPluginManager(PluginManager):
|
297 | 290 | """
|
298 | 291 | Overwrites :py:class:`pluggy.PluginManager <pluggy.PluginManager>` to add pytest-specific
|
@@ -511,34 +504,49 @@ def _importconftest(self, conftestpath):
|
511 | 504 | # Using Path().resolve() is better than py.path.realpath because
|
512 | 505 | # it resolves to the correct path/drive in case-insensitive file systems (#5792)
|
513 | 506 | key = Path(str(conftestpath)).resolve()
|
514 |
| - try: |
| 507 | + |
| 508 | + with contextlib.suppress(KeyError): |
515 | 509 | return self._conftestpath2mod[key]
|
516 |
| - except KeyError: |
517 |
| - pkgpath = conftestpath.pypkgpath() |
518 |
| - if pkgpath is None: |
519 |
| - _ensure_removed_sysmodule(conftestpath.purebasename) |
520 |
| - try: |
521 |
| - mod = conftestpath.pyimport() |
522 |
| - if ( |
523 |
| - hasattr(mod, "pytest_plugins") |
524 |
| - and self._configured |
525 |
| - and not self._using_pyargs |
526 |
| - ): |
527 |
| - _fail_on_non_top_pytest_plugins(conftestpath, self._confcutdir) |
528 |
| - except Exception: |
529 |
| - raise ConftestImportFailure(conftestpath, sys.exc_info()) |
530 |
| - |
531 |
| - self._conftest_plugins.add(mod) |
532 |
| - self._conftestpath2mod[key] = mod |
533 |
| - dirpath = conftestpath.dirpath() |
534 |
| - if dirpath in self._dirpath2confmods: |
535 |
| - for path, mods in self._dirpath2confmods.items(): |
536 |
| - if path and path.relto(dirpath) or path == dirpath: |
537 |
| - assert mod not in mods |
538 |
| - mods.append(mod) |
539 |
| - self.trace("loading conftestmodule {!r}".format(mod)) |
540 |
| - self.consider_conftest(mod) |
541 |
| - return mod |
| 510 | + |
| 511 | + pkgpath = conftestpath.pypkgpath() |
| 512 | + if pkgpath is None: |
| 513 | + _ensure_removed_sysmodule(conftestpath.purebasename) |
| 514 | + |
| 515 | + try: |
| 516 | + mod = conftestpath.pyimport() |
| 517 | + except Exception as e: |
| 518 | + raise ConftestImportFailure(conftestpath, sys.exc_info()) from e |
| 519 | + |
| 520 | + self._check_non_top_pytest_plugins(mod, conftestpath) |
| 521 | + |
| 522 | + self._conftest_plugins.add(mod) |
| 523 | + self._conftestpath2mod[key] = mod |
| 524 | + dirpath = conftestpath.dirpath() |
| 525 | + if dirpath in self._dirpath2confmods: |
| 526 | + for path, mods in self._dirpath2confmods.items(): |
| 527 | + if path and path.relto(dirpath) or path == dirpath: |
| 528 | + assert mod not in mods |
| 529 | + mods.append(mod) |
| 530 | + self.trace("loading conftestmodule {!r}".format(mod)) |
| 531 | + self.consider_conftest(mod) |
| 532 | + return mod |
| 533 | + |
| 534 | + def _check_non_top_pytest_plugins(self, mod, conftestpath): |
| 535 | + if ( |
| 536 | + hasattr(mod, "pytest_plugins") |
| 537 | + and self._configured |
| 538 | + and not self._using_pyargs |
| 539 | + ): |
| 540 | + msg = ( |
| 541 | + "Defining 'pytest_plugins' in a non-top-level conftest is no longer supported:\n" |
| 542 | + "It affects the entire test suite instead of just below the conftest as expected.\n" |
| 543 | + " {}\n" |
| 544 | + "Please move it to a top level conftest file at the rootdir:\n" |
| 545 | + " {}\n" |
| 546 | + "For more information, visit:\n" |
| 547 | + " https://docs.pytest.org/en/latest/deprecations.html#pytest-plugins-in-non-top-level-conftest-files" |
| 548 | + ) |
| 549 | + fail(msg.format(conftestpath, self._confcutdir), pytrace=False) |
542 | 550 |
|
543 | 551 | #
|
544 | 552 | # API for bootstrapping plugin loading
|
|
0 commit comments