Skip to content

Commit

Permalink
Use _get_wrapped_function to unwrap fixtures
Browse files Browse the repository at this point in the history
  • Loading branch information
Glyphack committed Nov 23, 2024
1 parent f632237 commit 50a001e
Show file tree
Hide file tree
Showing 4 changed files with 15 additions and 27 deletions.
23 changes: 2 additions & 21 deletions src/_pytest/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,33 +211,14 @@ def ascii_escaped(val: bytes | str) -> str:

def get_real_func(obj):
"""Get the real function object of the (possibly) wrapped object by
:func:`functools.wraps`, or :func:`functools.partial`, or :func:`pytest.fixture`."""
from _pytest.fixtures import FixtureFunctionDefinition

obj = inspect.unwrap(obj, stop=lambda x: type(x) is FixtureFunctionDefinition)

if type(obj) == FixtureFunctionDefinition:
obj = obj._get_wrapped_function()
:func:`functools.wraps`, or :func:`functools.partial`."""
obj = inspect.unwrap(obj)

if isinstance(obj, functools.partial):
obj = obj.func
return obj


def get_real_method(obj, holder):
"""Attempt to obtain the real function object that might be wrapping
``obj``, while at the same time returning a bound method to ``holder`` if
the original object was a bound method."""
try:
is_method = hasattr(obj, "__func__")
obj = get_real_func(obj)
except Exception: # pragma: no cover
return obj
if is_method and hasattr(obj, "__get__") and callable(obj.__get__):
obj = obj.__get__(holder)
return obj


def getimfunc(func):
try:
return func.__func__
Expand Down
9 changes: 5 additions & 4 deletions src/_pytest/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
from _pytest._io import TerminalWriter
from _pytest.compat import assert_never
from _pytest.compat import get_real_func
from _pytest.compat import get_real_method
from _pytest.compat import getfuncargnames
from _pytest.compat import getimfunc
from _pytest.compat import getlocation
Expand Down Expand Up @@ -1757,7 +1756,9 @@ def parsefactories(
if type(obj_ub) is FixtureFunctionDefinition:
marker = obj_ub._fixture_function_marker
if marker.name:
name = marker.name
fixture_name = marker.name
else:
fixture_name = name

# OK we know it is a fixture -- now safe to look up on the _instance_.
try:
Expand All @@ -1766,10 +1767,10 @@ def parsefactories(
except AttributeError:
obj = obj_ub

func = get_real_method(obj, holderobj)
func = obj._get_wrapped_function()

self._register_fixture(
name=name,
name=fixture_name,
nodeid=nodeid,
func=func,
scope=marker.scope,
Expand Down
4 changes: 3 additions & 1 deletion testing/code/test_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,9 @@ def deco_fixture():
# Make sure the decorator is not a wrapped function
assert not str(Source(deco_fixture)).startswith("@functools.wraps(function)")
assert (
textwrap.indent(str(Source(get_real_func(deco_fixture))), " ") + "\n" == src
textwrap.indent(str(Source(deco_fixture._get_wrapped_function())), " ")
+ "\n"
== src
)


Expand Down
6 changes: 5 additions & 1 deletion testing/test_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,11 @@ def wrapped_func3():
pass # pragma: no cover

wrapped_func4 = decorator(wrapped_func3)
assert get_real_func(wrapped_func4) is wrapped_func3._get_wrapped_function()
assert (
# get_real_func does not unwrap function that is wrapped by fixture hence we need to call _get_wrapped_function
get_real_func(wrapped_func4)._get_wrapped_function()
is wrapped_func3._get_wrapped_function()
)


def test_get_real_func_partial() -> None:
Expand Down

0 comments on commit 50a001e

Please sign in to comment.