From 46d2ccbfd4b7a2f75da65c330adcbabbd974365f Mon Sep 17 00:00:00 2001 From: Glyphack Date: Wed, 19 Jun 2024 13:36:28 +0200 Subject: [PATCH] (fixture) create a new class to represent fixtures --- src/_pytest/fixtures.py | 75 ++++++++++++++++++++++++++++++++++------- 1 file changed, 63 insertions(+), 12 deletions(-) diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 383084e07..58721c7d2 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -45,7 +45,8 @@ from _pytest._io import TerminalWriter from _pytest.compat import _PytestWrapper from _pytest.compat import assert_never from _pytest.compat import get_real_func -from _pytest.compat import get_real_method + +# from _pytest.compat import get_real_method from _pytest.compat import getfuncargnames from _pytest.compat import getimfunc from _pytest.compat import getlocation @@ -1186,7 +1187,7 @@ class FixtureFunctionMarker: def __post_init__(self, _ispytest: bool) -> None: check_ispytest(_ispytest) - def __call__(self, function: FixtureFunction) -> FixtureFunction: + def __call__(self, function: FixtureFunction) -> "FixtureFunctionDefinition": if inspect.isclass(function): raise ValueError("class fixtures not supported (maybe in the future)") @@ -1198,7 +1199,9 @@ class FixtureFunctionMarker: if hasattr(function, "pytestmark"): warnings.warn(MARKED_FIXTURE, stacklevel=2) - function = wrap_function_to_error_out_if_called_directly(function, self) + fixture_definition = FixtureFunctionDefinition(function, self) + + # function = wrap_function_to_error_out_if_called_directly(function, self) name = self.name or function.__name__ if name == "request": @@ -1209,13 +1212,53 @@ class FixtureFunctionMarker: ) # Type ignored because https://github.com/python/mypy/issues/2087. - function._pytestfixturefunction = self # type: ignore[attr-defined] - return function + # function._pytestfixturefunction = self # type: ignore[attr-defined] + # return function + return fixture_definition + + def __repr__(self): + return "fixture" + + +class FixtureFunctionDefinition: + def __init__( + self, + function: Callable[..., object], + fixture_function_marker: FixtureFunctionMarker, + instance: Optional[type] = None, + ): + self.name = fixture_function_marker.name or function.__name__ + self._pytestfixturefunction = fixture_function_marker + self.__pytest_wrapped__ = _PytestWrapper(function) + self.fixture_function = function + self.fixture_function_marker = fixture_function_marker + self.scope = fixture_function_marker.scope + self.params = fixture_function_marker.params + self.autouse = fixture_function_marker.autouse + self.ids = fixture_function_marker.ids + self.fixture_function = function + self.instance = instance + + def __repr__(self) -> str: + return f"fixture {self.fixture_function}" + + def __get__(self, instance, owner=None): + return FixtureFunctionDefinition( + self.fixture_function, self.fixture_function_marker, instance + ) + + def __call__(self, *args: Any, **kwds: Any) -> Any: + return self.get_real_func(*args, **kwds) + + def get_real_func(self): + if self.instance is not None: + return self.fixture_function.__get__(self.instance) + return self.fixture_function @overload def fixture( - fixture_function: FixtureFunction, + fixture_function: Callable[..., object], *, scope: "Union[_ScopeName, Callable[[str, Config], _ScopeName]]" = ..., params: Optional[Iterable[object]] = ..., @@ -1224,7 +1267,7 @@ def fixture( Union[Sequence[Optional[object]], Callable[[Any], Optional[object]]] ] = ..., name: Optional[str] = ..., -) -> FixtureFunction: ... +) -> FixtureFunctionDefinition: ... @overload @@ -1238,7 +1281,7 @@ def fixture( Union[Sequence[Optional[object]], Callable[[Any], Optional[object]]] ] = ..., name: Optional[str] = None, -) -> FixtureFunctionMarker: ... +) -> FixtureFunctionDefinition: ... def fixture( @@ -1251,7 +1294,7 @@ def fixture( Union[Sequence[Optional[object]], Callable[[Any], Optional[object]]] ] = None, name: Optional[str] = None, -) -> Union[FixtureFunctionMarker, FixtureFunction]: +) -> Union[FixtureFunctionMarker, FixtureFunctionDefinition]: """Decorator to mark a fixture factory function. This decorator can be used, with or without parameters, to define a @@ -1321,7 +1364,7 @@ def fixture( def yield_fixture( fixture_function=None, *args, - scope="function", + scope: _ScopeName = "function", params=None, autouse=False, ids=None, @@ -1644,6 +1687,13 @@ class FixtureManager: ] = None, autouse: bool = False, ) -> None: + if name == "fixt2": + print(name) + print(func) + print(nodeid) + print(scope) + print(ids) + print(autouse) """Register a fixture :param name: @@ -1735,7 +1785,7 @@ class FixtureManager: self._holderobjseen.add(holderobj) for name in dir(holderobj): # The attribute can be an arbitrary descriptor, so the attribute - # access below can raise. safe_getatt() ignores such exceptions. + # access below can raise. safe_getattr() ignores such exceptions. obj = safe_getattr(holderobj, name, None) marker = getfixturemarker(obj) if not isinstance(marker, FixtureFunctionMarker): @@ -1750,7 +1800,8 @@ class FixtureManager: # to issue a warning if called directly, so here we unwrap it in # order to not emit the warning when pytest itself calls the # fixture function. - func = get_real_method(obj, holderobj) + # func = get_real_method(obj, holderobj) + func = obj.get_real_func() self._register_fixture( name=name,