| 399 | self._configured = False |
| 400 | |
| 401 | def parse_hookimpl_opts(self, plugin: _PluggyPlugin, name: str): |
| 402 | # pytest hooks are always prefixed with "pytest_", |
| 403 | # so we avoid accessing possibly non-readable attributes |
| 404 | # (see issue #1073). |
| 405 | if not name.startswith("pytest_"): |
| 406 | return |
| 407 | # Ignore names which can not be hooks. |
| 408 | if name == "pytest_plugins": |
| 409 | return |
| 410 | |
| 411 | method = getattr(plugin, name) |
| 412 | opts = super().parse_hookimpl_opts(plugin, name) |
| 413 | |
| 414 | # Consider only actual functions for hooks (#3775). |
| 415 | if not inspect.isroutine(method): |
| 416 | return |
| 417 | |
| 418 | # Collect unmarked hooks as long as they have the `pytest_' prefix. |
| 419 | if opts is None and name.startswith("pytest_"): |
| 420 | opts = {} |
| 421 | if opts is not None: |
| 422 | # TODO: DeprecationWarning, people should use hookimpl |
| 423 | # https://github.com/pytest-dev/pytest/issues/4562 |
| 424 | known_marks = {m.name for m in getattr(method, "pytestmark", [])} |
| 425 | |
| 426 | for name in ("tryfirst", "trylast", "optionalhook", "hookwrapper"): |
| 427 | opts.setdefault(name, hasattr(method, name) or name in known_marks) |
| 428 | return opts |
| 429 | |
| 430 | def parse_hookspec_opts(self, module_or_class, name: str): |
| 431 | opts = super().parse_hookspec_opts(module_or_class, name) |