(fixture) create a new class to represent fixtures

This commit is contained in:
Glyphack 2024-06-19 13:36:28 +02:00
parent 7fd947167d
commit 46d2ccbfd4
1 changed files with 63 additions and 12 deletions

View File

@ -45,7 +45,8 @@ from _pytest._io import TerminalWriter
from _pytest.compat import _PytestWrapper from _pytest.compat import _PytestWrapper
from _pytest.compat import assert_never from _pytest.compat import assert_never
from _pytest.compat import get_real_func 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 getfuncargnames
from _pytest.compat import getimfunc from _pytest.compat import getimfunc
from _pytest.compat import getlocation from _pytest.compat import getlocation
@ -1186,7 +1187,7 @@ class FixtureFunctionMarker:
def __post_init__(self, _ispytest: bool) -> None: def __post_init__(self, _ispytest: bool) -> None:
check_ispytest(_ispytest) check_ispytest(_ispytest)
def __call__(self, function: FixtureFunction) -> FixtureFunction: def __call__(self, function: FixtureFunction) -> "FixtureFunctionDefinition":
if inspect.isclass(function): if inspect.isclass(function):
raise ValueError("class fixtures not supported (maybe in the future)") raise ValueError("class fixtures not supported (maybe in the future)")
@ -1198,7 +1199,9 @@ class FixtureFunctionMarker:
if hasattr(function, "pytestmark"): if hasattr(function, "pytestmark"):
warnings.warn(MARKED_FIXTURE, stacklevel=2) 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__ name = self.name or function.__name__
if name == "request": if name == "request":
@ -1209,13 +1212,53 @@ class FixtureFunctionMarker:
) )
# Type ignored because https://github.com/python/mypy/issues/2087. # Type ignored because https://github.com/python/mypy/issues/2087.
function._pytestfixturefunction = self # type: ignore[attr-defined] # function._pytestfixturefunction = self # type: ignore[attr-defined]
return function # 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 @overload
def fixture( def fixture(
fixture_function: FixtureFunction, fixture_function: Callable[..., object],
*, *,
scope: "Union[_ScopeName, Callable[[str, Config], _ScopeName]]" = ..., scope: "Union[_ScopeName, Callable[[str, Config], _ScopeName]]" = ...,
params: Optional[Iterable[object]] = ..., params: Optional[Iterable[object]] = ...,
@ -1224,7 +1267,7 @@ def fixture(
Union[Sequence[Optional[object]], Callable[[Any], Optional[object]]] Union[Sequence[Optional[object]], Callable[[Any], Optional[object]]]
] = ..., ] = ...,
name: Optional[str] = ..., name: Optional[str] = ...,
) -> FixtureFunction: ... ) -> FixtureFunctionDefinition: ...
@overload @overload
@ -1238,7 +1281,7 @@ def fixture(
Union[Sequence[Optional[object]], Callable[[Any], Optional[object]]] Union[Sequence[Optional[object]], Callable[[Any], Optional[object]]]
] = ..., ] = ...,
name: Optional[str] = None, name: Optional[str] = None,
) -> FixtureFunctionMarker: ... ) -> FixtureFunctionDefinition: ...
def fixture( def fixture(
@ -1251,7 +1294,7 @@ def fixture(
Union[Sequence[Optional[object]], Callable[[Any], Optional[object]]] Union[Sequence[Optional[object]], Callable[[Any], Optional[object]]]
] = None, ] = None,
name: Optional[str] = None, name: Optional[str] = None,
) -> Union[FixtureFunctionMarker, FixtureFunction]: ) -> Union[FixtureFunctionMarker, FixtureFunctionDefinition]:
"""Decorator to mark a fixture factory function. """Decorator to mark a fixture factory function.
This decorator can be used, with or without parameters, to define a This decorator can be used, with or without parameters, to define a
@ -1321,7 +1364,7 @@ def fixture(
def yield_fixture( def yield_fixture(
fixture_function=None, fixture_function=None,
*args, *args,
scope="function", scope: _ScopeName = "function",
params=None, params=None,
autouse=False, autouse=False,
ids=None, ids=None,
@ -1644,6 +1687,13 @@ class FixtureManager:
] = None, ] = None,
autouse: bool = False, autouse: bool = False,
) -> None: ) -> None:
if name == "fixt2":
print(name)
print(func)
print(nodeid)
print(scope)
print(ids)
print(autouse)
"""Register a fixture """Register a fixture
:param name: :param name:
@ -1735,7 +1785,7 @@ class FixtureManager:
self._holderobjseen.add(holderobj) self._holderobjseen.add(holderobj)
for name in dir(holderobj): for name in dir(holderobj):
# The attribute can be an arbitrary descriptor, so the attribute # 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) obj = safe_getattr(holderobj, name, None)
marker = getfixturemarker(obj) marker = getfixturemarker(obj)
if not isinstance(marker, FixtureFunctionMarker): if not isinstance(marker, FixtureFunctionMarker):
@ -1750,7 +1800,8 @@ class FixtureManager:
# to issue a warning if called directly, so here we unwrap it in # to issue a warning if called directly, so here we unwrap it in
# order to not emit the warning when pytest itself calls the # order to not emit the warning when pytest itself calls the
# fixture function. # fixture function.
func = get_real_method(obj, holderobj) # func = get_real_method(obj, holderobj)
func = obj.get_real_func()
self._register_fixture( self._register_fixture(
name=name, name=name,