Improve pluggy-related typing
This commit is contained in:
parent
39f9306357
commit
f43a8db618
|
@ -37,6 +37,7 @@ from typing import Type
|
|||
from typing import TYPE_CHECKING
|
||||
from typing import Union
|
||||
|
||||
import pluggy
|
||||
from pluggy import HookimplMarker
|
||||
from pluggy import HookimplOpts
|
||||
from pluggy import HookspecMarker
|
||||
|
@ -46,6 +47,7 @@ from pluggy import PluginManager
|
|||
import _pytest._code
|
||||
import _pytest.deprecated
|
||||
import _pytest.hookspec
|
||||
from .compat import PathAwareHookProxy
|
||||
from .exceptions import PrintHelp as PrintHelp
|
||||
from .exceptions import UsageError as UsageError
|
||||
from .findpaths import determine_setup
|
||||
|
@ -1005,10 +1007,8 @@ class Config:
|
|||
# Deprecated alias. Was never public. Can be removed in a few releases.
|
||||
self._store = self.stash
|
||||
|
||||
from .compat import PathAwareHookProxy
|
||||
|
||||
self.trace = self.pluginmanager.trace.root.get("config")
|
||||
self.hook = PathAwareHookProxy(self.pluginmanager.hook)
|
||||
self.hook: pluggy.HookRelay = PathAwareHookProxy(self.pluginmanager.hook) # type: ignore[assignment]
|
||||
self._inicache: Dict[str, Any] = {}
|
||||
self._override_ini: Sequence[str] = ()
|
||||
self._opt2dest: Dict[str, str] = {}
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import functools
|
||||
import warnings
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
from typing import Mapping
|
||||
|
||||
import pluggy
|
||||
|
||||
from ..compat import LEGACY_PATH
|
||||
from ..compat import legacy_path
|
||||
from ..deprecated import HOOK_LEGACY_PATH_ARG
|
||||
from _pytest.nodes import _check_path
|
||||
|
||||
# hookname: (Path, LEGACY_PATH)
|
||||
imply_paths_hooks = {
|
||||
imply_paths_hooks: Mapping[str, tuple[str, str]] = {
|
||||
"pytest_ignore_collect": ("collection_path", "path"),
|
||||
"pytest_collect_file": ("file_path", "path"),
|
||||
"pytest_pycollect_makemodule": ("module_path", "path"),
|
||||
|
@ -18,6 +21,14 @@ imply_paths_hooks = {
|
|||
}
|
||||
|
||||
|
||||
def _check_path(path: Path, fspath: LEGACY_PATH) -> None:
|
||||
if Path(fspath) != path:
|
||||
raise ValueError(
|
||||
f"Path({fspath!r}) != {path!r}\n"
|
||||
"if both path and fspath are given they need to be equal"
|
||||
)
|
||||
|
||||
|
||||
class PathAwareHookProxy:
|
||||
"""
|
||||
this helper wraps around hook callers
|
||||
|
@ -27,24 +38,24 @@ class PathAwareHookProxy:
|
|||
this may have to be changed later depending on bugs
|
||||
"""
|
||||
|
||||
def __init__(self, hook_caller):
|
||||
self.__hook_caller = hook_caller
|
||||
def __init__(self, hook_relay: pluggy.HookRelay) -> None:
|
||||
self._hook_relay = hook_relay
|
||||
|
||||
def __dir__(self):
|
||||
return dir(self.__hook_caller)
|
||||
def __dir__(self) -> list[str]:
|
||||
return dir(self._hook_relay)
|
||||
|
||||
def __getattr__(self, key, _wraps=functools.wraps):
|
||||
hook = getattr(self.__hook_caller, key)
|
||||
def __getattr__(self, key: str) -> pluggy.HookCaller:
|
||||
hook: pluggy.HookCaller = getattr(self._hook_relay, key)
|
||||
if key not in imply_paths_hooks:
|
||||
self.__dict__[key] = hook
|
||||
return hook
|
||||
else:
|
||||
path_var, fspath_var = imply_paths_hooks[key]
|
||||
|
||||
@_wraps(hook)
|
||||
@functools.wraps(hook)
|
||||
def fixed_hook(**kw):
|
||||
path_value: Optional[Path] = kw.pop(path_var, None)
|
||||
fspath_value: Optional[LEGACY_PATH] = kw.pop(fspath_var, None)
|
||||
path_value: Path | None = kw.pop(path_var, None)
|
||||
fspath_value: LEGACY_PATH | None = kw.pop(fspath_var, None)
|
||||
if fspath_value is not None:
|
||||
warnings.warn(
|
||||
HOOK_LEGACY_PATH_ARG.format(
|
||||
|
@ -65,6 +76,8 @@ class PathAwareHookProxy:
|
|||
kw[fspath_var] = fspath_value
|
||||
return hook(**kw)
|
||||
|
||||
fixed_hook.name = hook.name # type: ignore[attr-defined]
|
||||
fixed_hook.spec = hook.spec # type: ignore[attr-defined]
|
||||
fixed_hook.__name__ = key
|
||||
self.__dict__[key] = fixed_hook
|
||||
return fixed_hook
|
||||
return fixed_hook # type: ignore[return-value]
|
||||
|
|
|
@ -7,6 +7,7 @@ import importlib
|
|||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import AbstractSet
|
||||
from typing import Callable
|
||||
from typing import Dict
|
||||
from typing import final
|
||||
|
@ -22,6 +23,8 @@ from typing import Type
|
|||
from typing import TYPE_CHECKING
|
||||
from typing import Union
|
||||
|
||||
import pluggy
|
||||
|
||||
import _pytest._code
|
||||
from _pytest import nodes
|
||||
from _pytest.config import Config
|
||||
|
@ -31,6 +34,7 @@ from _pytest.config import hookimpl
|
|||
from _pytest.config import PytestPluginManager
|
||||
from _pytest.config import UsageError
|
||||
from _pytest.config.argparsing import Parser
|
||||
from _pytest.config.compat import PathAwareHookProxy
|
||||
from _pytest.fixtures import FixtureManager
|
||||
from _pytest.outcomes import exit
|
||||
from _pytest.pathlib import absolutepath
|
||||
|
@ -429,11 +433,15 @@ def pytest_collection_modifyitems(items: List[nodes.Item], config: Config) -> No
|
|||
|
||||
|
||||
class FSHookProxy:
|
||||
def __init__(self, pm: PytestPluginManager, remove_mods) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
pm: PytestPluginManager,
|
||||
remove_mods: AbstractSet[object],
|
||||
) -> None:
|
||||
self.pm = pm
|
||||
self.remove_mods = remove_mods
|
||||
|
||||
def __getattr__(self, name: str):
|
||||
def __getattr__(self, name: str) -> pluggy.HookCaller:
|
||||
x = self.pm.subset_hook_caller(name, remove_plugins=self.remove_mods)
|
||||
self.__dict__[name] = x
|
||||
return x
|
||||
|
@ -546,7 +554,7 @@ class Session(nodes.FSCollector):
|
|||
path_ = path if isinstance(path, Path) else Path(path)
|
||||
return path_ in self._initialpaths
|
||||
|
||||
def gethookproxy(self, fspath: "os.PathLike[str]"):
|
||||
def gethookproxy(self, fspath: "os.PathLike[str]") -> pluggy.HookRelay:
|
||||
# Optimization: Path(Path(...)) is much slower than isinstance.
|
||||
path = fspath if isinstance(fspath, Path) else Path(fspath)
|
||||
pm = self.config.pluginmanager
|
||||
|
@ -563,11 +571,10 @@ class Session(nodes.FSCollector):
|
|||
)
|
||||
my_conftestmodules = pm._getconftestmodules(path)
|
||||
remove_mods = pm._conftest_plugins.difference(my_conftestmodules)
|
||||
proxy: pluggy.HookRelay
|
||||
if remove_mods:
|
||||
# One or more conftests are not in use at this fspath.
|
||||
from .config.compat import PathAwareHookProxy
|
||||
|
||||
proxy = PathAwareHookProxy(FSHookProxy(pm, remove_mods))
|
||||
# One or more conftests are not in use at this path.
|
||||
proxy = PathAwareHookProxy(FSHookProxy(pm, remove_mods)) # type: ignore[arg-type,assignment]
|
||||
else:
|
||||
# All plugins are active for this fspath.
|
||||
proxy = self.config.hook
|
||||
|
|
|
@ -19,6 +19,8 @@ from typing import TYPE_CHECKING
|
|||
from typing import TypeVar
|
||||
from typing import Union
|
||||
|
||||
import pluggy
|
||||
|
||||
import _pytest._code
|
||||
from _pytest._code import getfslineno
|
||||
from _pytest._code.code import ExceptionInfo
|
||||
|
@ -27,6 +29,7 @@ from _pytest._code.code import Traceback
|
|||
from _pytest.compat import LEGACY_PATH
|
||||
from _pytest.config import Config
|
||||
from _pytest.config import ConftestImportFailure
|
||||
from _pytest.config.compat import _check_path
|
||||
from _pytest.deprecated import FSCOLLECTOR_GETHOOKPROXY_ISINITPATH
|
||||
from _pytest.deprecated import NODE_CTOR_FSPATH_ARG
|
||||
from _pytest.mark.structures import Mark
|
||||
|
@ -94,14 +97,6 @@ def iterparentnodeids(nodeid: str) -> Iterator[str]:
|
|||
yield nodeid
|
||||
|
||||
|
||||
def _check_path(path: Path, fspath: LEGACY_PATH) -> None:
|
||||
if Path(fspath) != path:
|
||||
raise ValueError(
|
||||
f"Path({fspath!r}) != {path!r}\n"
|
||||
"if both path and fspath are given they need to be equal"
|
||||
)
|
||||
|
||||
|
||||
def _imply_path(
|
||||
node_type: Type["Node"],
|
||||
path: Optional[Path],
|
||||
|
@ -264,7 +259,7 @@ class Node(metaclass=NodeMeta):
|
|||
return cls._create(parent=parent, **kw)
|
||||
|
||||
@property
|
||||
def ihook(self):
|
||||
def ihook(self) -> pluggy.HookRelay:
|
||||
"""fspath-sensitive hook proxy used to call pytest hooks."""
|
||||
return self.session.gethookproxy(self.path)
|
||||
|
||||
|
|
Loading…
Reference in New Issue