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