|
13 | 13 | import warnings
|
14 | 14 | from functools import lru_cache
|
15 | 15 | from pathlib import Path
|
| 16 | +from types import FunctionType |
16 | 17 | from types import TracebackType
|
17 | 18 | from typing import Any
|
18 | 19 | from typing import Callable
|
|
35 | 36 | from pluggy import HookimplMarker
|
36 | 37 | from pluggy import HookspecMarker
|
37 | 38 | from pluggy import PluginManager
|
| 39 | +from typing_extensions import Literal |
38 | 40 |
|
39 | 41 | import _pytest._code
|
40 | 42 | import _pytest.deprecated
|
@@ -330,6 +332,32 @@ def _prepareconfig(
|
330 | 332 | raise
|
331 | 333 |
|
332 | 334 |
|
| 335 | +def _get_legacy_hook_marks( |
| 336 | + method: FunctionType, |
| 337 | + hook_type: Literal["spec", "impl"], |
| 338 | + opt_names: Tuple[str, ...], |
| 339 | +) -> Dict[str, bool]: |
| 340 | + known_marks = {m.name for m in getattr(method, "pytestmark", [])} |
| 341 | + must_warn = False |
| 342 | + opts = {} |
| 343 | + for opt_name in opt_names: |
| 344 | + if hasattr(method, opt_name) or opt_name in known_marks: |
| 345 | + opts[opt_name] = True |
| 346 | + must_warn = True |
| 347 | + else: |
| 348 | + opts[opt_name] = False |
| 349 | + if must_warn: |
| 350 | + |
| 351 | + hook_opts = ", ".join(f"{name}=True" for name, val in opts.items() if val) |
| 352 | + message = _pytest.deprecated.HOOK_LEGACY_MARKING.format( |
| 353 | + type=hook_type, |
| 354 | + fullname=method.__qualname__, |
| 355 | + hook_opts=hook_opts, |
| 356 | + ) |
| 357 | + warn_explicit_for(method, message) |
| 358 | + return opts |
| 359 | + |
| 360 | + |
333 | 361 | @final
|
334 | 362 | class PytestPluginManager(PluginManager):
|
335 | 363 | """A :py:class:`pluggy.PluginManager <pluggy.PluginManager>` with
|
@@ -402,39 +430,20 @@ def parse_hookimpl_opts(self, plugin: _PluggyPlugin, name: str):
|
402 | 430 | if not inspect.isroutine(method):
|
403 | 431 | return
|
404 | 432 | # Collect unmarked hooks as long as they have the `pytest_' prefix.
|
405 |
| - |
406 |
| - known_marks = {m.name for m in getattr(method, "pytestmark", [])} |
407 |
| - opts = { |
408 |
| - name: hasattr(method, name) or name in known_marks |
409 |
| - for name in ("tryfirst", "trylast", "optionalhook", "hookwrapper") |
410 |
| - } |
411 |
| - if any(opts.values()): |
412 |
| - message = _pytest.deprecated.HOOK_LEGACY_MARKING.format( |
413 |
| - type="spec", fullname=method.__qualname__ |
414 |
| - ) |
415 |
| - warn_explicit_for(method, message) |
416 |
| - |
417 |
| - return opts |
| 433 | + return _get_legacy_hook_marks( |
| 434 | + method, "impl", ("tryfirst", "trylast", "optionalhook", "hookwrapper") |
| 435 | + ) |
418 | 436 |
|
419 | 437 | def parse_hookspec_opts(self, module_or_class, name: str):
|
420 | 438 | opts = super().parse_hookspec_opts(module_or_class, name)
|
421 | 439 | if opts is None:
|
422 | 440 | method = getattr(module_or_class, name)
|
423 | 441 | if name.startswith("pytest_"):
|
424 |
| - |
425 |
| - known_marks = {m.name for m in getattr(method, "pytestmark", [])} |
426 |
| - opts = { |
427 |
| - "firstresult": hasattr(method, "firstresult") |
428 |
| - or "firstresult" in known_marks, |
429 |
| - "historic": hasattr(method, "historic") |
430 |
| - or "historic" in known_marks, |
431 |
| - } |
432 |
| - # hook from xdist, fixing in ... |
433 |
| - if any(opts.values()) and module_or_class.__name__ != "xdist.newhooks": |
434 |
| - message = _pytest.deprecated.HOOK_LEGACY_MARKING.format( |
435 |
| - type="spec", fullname=method.__qualname__ |
436 |
| - ) |
437 |
| - warn_explicit_for(method, message) |
| 442 | + opts = _get_legacy_hook_marks( |
| 443 | + method, |
| 444 | + "spec", |
| 445 | + ("firstresult", "historic"), |
| 446 | + ) |
438 | 447 | return opts
|
439 | 448 |
|
440 | 449 | def register(
|
|
0 commit comments