check that tests that are partial staticmethods are supported (#5701)
check that tests that are partial staticmethods are supported
This commit is contained in:
commit
28c6c5bb71
|
@ -0,0 +1 @@
|
||||||
|
Fix collection of ``staticmethod`` objects defined with ``functools.partial``.
|
|
@ -80,7 +80,7 @@ def num_mock_patch_args(function):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def getfuncargnames(function, is_method=False, cls=None):
|
def getfuncargnames(function, *, name: str = "", is_method=False, cls=None):
|
||||||
"""Returns the names of a function's mandatory arguments.
|
"""Returns the names of a function's mandatory arguments.
|
||||||
|
|
||||||
This should return the names of all function arguments that:
|
This should return the names of all function arguments that:
|
||||||
|
@ -93,11 +93,12 @@ def getfuncargnames(function, is_method=False, cls=None):
|
||||||
be treated as a bound method even though it's not unless, only in
|
be treated as a bound method even though it's not unless, only in
|
||||||
the case of cls, the function is a static method.
|
the case of cls, the function is a static method.
|
||||||
|
|
||||||
|
The name parameter should be the original name in which the function was collected.
|
||||||
|
|
||||||
@RonnyPfannschmidt: This function should be refactored when we
|
@RonnyPfannschmidt: This function should be refactored when we
|
||||||
revisit fixtures. The fixture mechanism should ask the node for
|
revisit fixtures. The fixture mechanism should ask the node for
|
||||||
the fixture names, and not try to obtain directly from the
|
the fixture names, and not try to obtain directly from the
|
||||||
function object well after collection has occurred.
|
function object well after collection has occurred.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# The parameters attribute of a Signature object contains an
|
# The parameters attribute of a Signature object contains an
|
||||||
# ordered mapping of parameter names to Parameter instances. This
|
# ordered mapping of parameter names to Parameter instances. This
|
||||||
|
@ -120,11 +121,14 @@ def getfuncargnames(function, is_method=False, cls=None):
|
||||||
)
|
)
|
||||||
and p.default is Parameter.empty
|
and p.default is Parameter.empty
|
||||||
)
|
)
|
||||||
|
if not name:
|
||||||
|
name = function.__name__
|
||||||
|
|
||||||
# If this function should be treated as a bound method even though
|
# If this function should be treated as a bound method even though
|
||||||
# it's passed as an unbound method or function, remove the first
|
# it's passed as an unbound method or function, remove the first
|
||||||
# parameter name.
|
# parameter name.
|
||||||
if is_method or (
|
if is_method or (
|
||||||
cls and not isinstance(cls.__dict__.get(function.__name__, None), staticmethod)
|
cls and not isinstance(cls.__dict__.get(name, None), staticmethod)
|
||||||
):
|
):
|
||||||
arg_names = arg_names[1:]
|
arg_names = arg_names[1:]
|
||||||
# Remove any names that will be replaced with mocks.
|
# Remove any names that will be replaced with mocks.
|
||||||
|
@ -247,7 +251,7 @@ def get_real_method(obj, holder):
|
||||||
try:
|
try:
|
||||||
is_method = hasattr(obj, "__func__")
|
is_method = hasattr(obj, "__func__")
|
||||||
obj = get_real_func(obj)
|
obj = get_real_func(obj)
|
||||||
except Exception:
|
except Exception: # pragma: no cover
|
||||||
return obj
|
return obj
|
||||||
if is_method and hasattr(obj, "__get__") and callable(obj.__get__):
|
if is_method and hasattr(obj, "__get__") and callable(obj.__get__):
|
||||||
obj = obj.__get__(holder)
|
obj = obj.__get__(holder)
|
||||||
|
|
|
@ -828,7 +828,7 @@ class FixtureDef:
|
||||||
where=baseid,
|
where=baseid,
|
||||||
)
|
)
|
||||||
self.params = params
|
self.params = params
|
||||||
self.argnames = getfuncargnames(func, is_method=unittest)
|
self.argnames = getfuncargnames(func, name=argname, is_method=unittest)
|
||||||
self.unittest = unittest
|
self.unittest = unittest
|
||||||
self.ids = ids
|
self.ids = ids
|
||||||
self._finalizers = []
|
self._finalizers = []
|
||||||
|
@ -1143,7 +1143,7 @@ class FixtureManager:
|
||||||
|
|
||||||
def getfixtureinfo(self, node, func, cls, funcargs=True):
|
def getfixtureinfo(self, node, func, cls, funcargs=True):
|
||||||
if funcargs and not getattr(node, "nofuncargs", False):
|
if funcargs and not getattr(node, "nofuncargs", False):
|
||||||
argnames = getfuncargnames(func, cls=cls)
|
argnames = getfuncargnames(func, name=node.name, cls=cls)
|
||||||
else:
|
else:
|
||||||
argnames = ()
|
argnames = ()
|
||||||
|
|
||||||
|
|
|
@ -1143,52 +1143,6 @@ def test_unorderable_types(testdir):
|
||||||
assert result.ret == ExitCode.NO_TESTS_COLLECTED
|
assert result.ret == ExitCode.NO_TESTS_COLLECTED
|
||||||
|
|
||||||
|
|
||||||
def test_collect_functools_partial(testdir):
|
|
||||||
"""
|
|
||||||
Test that collection of functools.partial object works, and arguments
|
|
||||||
to the wrapped functions are dealt with correctly (see #811).
|
|
||||||
"""
|
|
||||||
testdir.makepyfile(
|
|
||||||
"""
|
|
||||||
import functools
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def fix1():
|
|
||||||
return 'fix1'
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def fix2():
|
|
||||||
return 'fix2'
|
|
||||||
|
|
||||||
def check1(i, fix1):
|
|
||||||
assert i == 2
|
|
||||||
assert fix1 == 'fix1'
|
|
||||||
|
|
||||||
def check2(fix1, i):
|
|
||||||
assert i == 2
|
|
||||||
assert fix1 == 'fix1'
|
|
||||||
|
|
||||||
def check3(fix1, i, fix2):
|
|
||||||
assert i == 2
|
|
||||||
assert fix1 == 'fix1'
|
|
||||||
assert fix2 == 'fix2'
|
|
||||||
|
|
||||||
test_ok_1 = functools.partial(check1, i=2)
|
|
||||||
test_ok_2 = functools.partial(check1, i=2, fix1='fix1')
|
|
||||||
test_ok_3 = functools.partial(check1, 2)
|
|
||||||
test_ok_4 = functools.partial(check2, i=2)
|
|
||||||
test_ok_5 = functools.partial(check3, i=2)
|
|
||||||
test_ok_6 = functools.partial(check3, i=2, fix1='fix1')
|
|
||||||
|
|
||||||
test_fail_1 = functools.partial(check2, 2)
|
|
||||||
test_fail_2 = functools.partial(check3, 2)
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
result = testdir.inline_run()
|
|
||||||
result.assertoutcome(passed=6, failed=2)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.filterwarnings("default")
|
@pytest.mark.filterwarnings("default")
|
||||||
def test_dont_collect_non_function_callable(testdir):
|
def test_dont_collect_non_function_callable(testdir):
|
||||||
"""Test for issue https://github.com/pytest-dev/pytest/issues/331
|
"""Test for issue https://github.com/pytest-dev/pytest/issues/331
|
||||||
|
|
|
@ -10,7 +10,9 @@ from _pytest.pytester import get_public_names
|
||||||
from _pytest.warnings import SHOW_PYTEST_WARNINGS_ARG
|
from _pytest.warnings import SHOW_PYTEST_WARNINGS_ARG
|
||||||
|
|
||||||
|
|
||||||
def test_getfuncargnames():
|
def test_getfuncargnames_functions():
|
||||||
|
"""Test getfuncargnames for normal functions"""
|
||||||
|
|
||||||
def f():
|
def f():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -31,18 +33,56 @@ def test_getfuncargnames():
|
||||||
|
|
||||||
assert fixtures.getfuncargnames(j) == ("arg1", "arg2")
|
assert fixtures.getfuncargnames(j) == ("arg1", "arg2")
|
||||||
|
|
||||||
|
|
||||||
|
def test_getfuncargnames_methods():
|
||||||
|
"""Test getfuncargnames for normal methods"""
|
||||||
|
|
||||||
class A:
|
class A:
|
||||||
def f(self, arg1, arg2="hello"):
|
def f(self, arg1, arg2="hello"):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
assert fixtures.getfuncargnames(A().f) == ("arg1",)
|
||||||
|
|
||||||
|
|
||||||
|
def test_getfuncargnames_staticmethod():
|
||||||
|
"""Test getfuncargnames for staticmethods"""
|
||||||
|
|
||||||
|
class A:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def static(arg1, arg2):
|
def static(arg1, arg2, x=1):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
assert fixtures.getfuncargnames(A().f) == ("arg1",)
|
|
||||||
assert fixtures.getfuncargnames(A.static, cls=A) == ("arg1", "arg2")
|
assert fixtures.getfuncargnames(A.static, cls=A) == ("arg1", "arg2")
|
||||||
|
|
||||||
|
|
||||||
|
def test_getfuncargnames_partial():
|
||||||
|
"""Check getfuncargnames for methods defined with functools.partial (#5701)"""
|
||||||
|
import functools
|
||||||
|
|
||||||
|
def check(arg1, arg2, i):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class T:
|
||||||
|
test_ok = functools.partial(check, i=2)
|
||||||
|
|
||||||
|
values = fixtures.getfuncargnames(T().test_ok, name="test_ok")
|
||||||
|
assert values == ("arg1", "arg2")
|
||||||
|
|
||||||
|
|
||||||
|
def test_getfuncargnames_staticmethod_partial():
|
||||||
|
"""Check getfuncargnames for staticmethods defined with functools.partial (#5701)"""
|
||||||
|
import functools
|
||||||
|
|
||||||
|
def check(arg1, arg2, i):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class T:
|
||||||
|
test_ok = staticmethod(functools.partial(check, i=2))
|
||||||
|
|
||||||
|
values = fixtures.getfuncargnames(T().test_ok, name="test_ok")
|
||||||
|
assert values == ("arg1", "arg2")
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.pytester_example_path("fixtures/fill_fixtures")
|
@pytest.mark.pytester_example_path("fixtures/fill_fixtures")
|
||||||
class TestFillFixtures:
|
class TestFillFixtures:
|
||||||
def test_fillfuncargs_exposed(self):
|
def test_fillfuncargs_exposed(self):
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import sys
|
import sys
|
||||||
|
from functools import partial
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -72,6 +73,16 @@ def test_get_real_func():
|
||||||
assert get_real_func(wrapped_func2) is wrapped_func
|
assert get_real_func(wrapped_func2) is wrapped_func
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_real_func_partial():
|
||||||
|
"""Test get_real_func handles partial instances correctly"""
|
||||||
|
|
||||||
|
def foo(x):
|
||||||
|
return x
|
||||||
|
|
||||||
|
assert get_real_func(foo) is foo
|
||||||
|
assert get_real_func(partial(foo)) is foo
|
||||||
|
|
||||||
|
|
||||||
def test_is_generator_asyncio(testdir):
|
def test_is_generator_asyncio(testdir):
|
||||||
testdir.makepyfile(
|
testdir.makepyfile(
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue