From 91fc20876a96efd5137025bd266dd9206161d61c Mon Sep 17 00:00:00 2001 From: Glyphack Date: Thu, 20 Jun 2024 15:16:47 +0200 Subject: [PATCH] refactor: removed PytestWrapper class --- src/_pytest/compat.py | 28 ++++++---------------------- src/_pytest/fixtures.py | 2 -- testing/test_compat.py | 16 +++++++++------- 3 files changed, 15 insertions(+), 31 deletions(-) diff --git a/src/_pytest/compat.py b/src/_pytest/compat.py index 02256cfc7..25e66f65c 100644 --- a/src/_pytest/compat.py +++ b/src/_pytest/compat.py @@ -3,7 +3,6 @@ from __future__ import annotations -import dataclasses import enum import functools import inspect @@ -210,30 +209,15 @@ def ascii_escaped(val: bytes | str) -> str: return ret.translate(_non_printable_ascii_translate_table) -# TODO: remove and replace with FixtureFunctionDefinition -@dataclasses.dataclass -class _PytestWrapper: - """Dummy wrapper around a function object for internal use only. - - Used to correctly unwrap the underlying function object when we are - creating fixtures, because we wrap the function object ourselves with a - decorator to issue warnings when the fixture function is called directly. - """ - - obj: Any - - def get_real_func(obj): """Get the real function object of the (possibly) wrapped object by - functools.wraps or functools.partial.""" + functools.wraps or functools.partial or pytest.fixture""" + from _pytest.fixtures import FixtureFunctionDefinition + start_obj = obj - for i in range(100): - # __pytest_wrapped__ is set by @pytest.fixture when wrapping the fixture function - # to trigger a warning if it gets called directly instead of by pytest: we don't - # want to unwrap further than this otherwise we lose useful wrappings like @mock.patch (#3774) - new_obj = getattr(obj, "__pytest_wrapped__", None) - if isinstance(new_obj, _PytestWrapper): - obj = new_obj.obj + for _ in range(100): + if isinstance(obj, FixtureFunctionDefinition): + obj = obj.get_real_func() break new_obj = getattr(obj, "__wrapped__", None) if new_obj is None: diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 71aea5eee..500e163be 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -42,7 +42,6 @@ from _pytest._code import Source from _pytest._code.code import FormattedExcinfo from _pytest._code.code import TerminalRepr 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 getfuncargnames @@ -1204,7 +1203,6 @@ class FixtureFunctionDefinition: # Using isinstance on every object in code might execute code that is not intended to be executed. # Like lazy loaded classes. self._pytestfixturefunction = fixture_function_marker - self.__pytest_wrapped__ = _PytestWrapper(function) self.fixture_function_marker = fixture_function_marker self.fixture_function = function self.instance = instance diff --git a/testing/test_compat.py b/testing/test_compat.py index 73ac1bad8..9c8a2f9d4 100644 --- a/testing/test_compat.py +++ b/testing/test_compat.py @@ -7,7 +7,6 @@ import sys from typing import TYPE_CHECKING from typing import Union -from _pytest.compat import _PytestWrapper from _pytest.compat import assert_never from _pytest.compat import get_real_func from _pytest.compat import is_generator @@ -52,8 +51,8 @@ def test_real_func_loop_limit() -> None: with pytest.raises( ValueError, match=( - "could not find real function of \n" - "stopped at " + "could not find real function of \n" + "stopped at " ), ): get_real_func(evil) @@ -78,10 +77,13 @@ def test_get_real_func() -> None: wrapped_func2 = decorator(decorator(wrapped_func)) assert get_real_func(wrapped_func2) is func - # special case for __pytest_wrapped__ attribute: used to obtain the function up until the point - # a function was wrapped by pytest itself - wrapped_func2.__pytest_wrapped__ = _PytestWrapper(wrapped_func) - assert get_real_func(wrapped_func2) is wrapped_func + # obtain the function up until the point a function was wrapped by pytest itself + @pytest.fixture + def wrapped_func3(): + pass + + wrapped_func4 = decorator(wrapped_func3) + assert get_real_func(wrapped_func4) is wrapped_func3.get_real_func() def test_get_real_func_partial() -> None: