Update functools from 3.13.9 (#6205)

* Update `functools.py` from 3.13.9

* mark/unmark tests
This commit is contained in:
Shahar Naveh
2025-10-22 14:26:19 +03:00
committed by GitHub
parent b15e537692
commit 5d9e62390c
2 changed files with 416 additions and 110 deletions

91
Lib/functools.py vendored
View File

@@ -19,8 +19,9 @@ from collections import namedtuple
# import types, weakref # Deferred to single_dispatch()
from reprlib import recursive_repr
from _thread import RLock
from types import GenericAlias
# Avoid importing types, so we can speedup import time
GenericAlias = type(list[int])
################################################################################
### update_wrapper() and wraps() decorator
@@ -236,14 +237,16 @@ _initial_missing = object()
def reduce(function, sequence, initial=_initial_missing):
"""
reduce(function, iterable[, initial]) -> value
reduce(function, iterable[, initial], /) -> value
Apply a function of two arguments cumulatively to the items of a sequence
or iterable, from left to right, so as to reduce the iterable to a single
value. For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates
((((1+2)+3)+4)+5). If initial is present, it is placed before the items
of the iterable in the calculation, and serves as a default when the
iterable is empty.
Apply a function of two arguments cumulatively to the items of an iterable, from left to right.
This effectively reduces the iterable to a single value. If initial is present,
it is placed before the items of the iterable in the calculation, and serves as
a default when the iterable is empty.
For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])
calculates ((((1 + 2) + 3) + 4) + 5).
"""
it = iter(sequence)
@@ -284,7 +287,7 @@ class partial:
if not callable(func):
raise TypeError("the first argument must be callable")
if hasattr(func, "func"):
if isinstance(func, partial):
args = func.args + args
keywords = {**func.keywords, **keywords}
func = func.func
@@ -302,13 +305,23 @@ class partial:
@recursive_repr()
def __repr__(self):
qualname = type(self).__qualname__
cls = type(self)
qualname = cls.__qualname__
module = cls.__module__
args = [repr(self.func)]
args.extend(repr(x) for x in self.args)
args.extend(f"{k}={v!r}" for (k, v) in self.keywords.items())
if type(self).__module__ == "functools":
return f"functools.{qualname}({', '.join(args)})"
return f"{qualname}({', '.join(args)})"
return f"{module}.{qualname}({', '.join(args)})"
def __get__(self, obj, objtype=None):
if obj is None:
return self
import warnings
warnings.warn('functools.partial will be a method descriptor in '
'future Python versions; wrap it in staticmethod() '
'if you want to preserve the old behavior',
FutureWarning, 2)
return self
def __reduce__(self):
return type(self), (self.func,), (self.func, self.args,
@@ -338,6 +351,9 @@ class partial:
self.args = args
self.keywords = kwds
__class_getitem__ = classmethod(GenericAlias)
try:
from _functools import partial
except ImportError:
@@ -372,28 +388,26 @@ class partialmethod(object):
self.keywords = keywords
def __repr__(self):
args = ", ".join(map(repr, self.args))
keywords = ", ".join("{}={!r}".format(k, v)
for k, v in self.keywords.items())
format_string = "{module}.{cls}({func}, {args}, {keywords})"
return format_string.format(module=self.__class__.__module__,
cls=self.__class__.__qualname__,
func=self.func,
args=args,
keywords=keywords)
cls = type(self)
module = cls.__module__
qualname = cls.__qualname__
args = [repr(self.func)]
args.extend(map(repr, self.args))
args.extend(f"{k}={v!r}" for k, v in self.keywords.items())
return f"{module}.{qualname}({', '.join(args)})"
def _make_unbound_method(self):
def _method(cls_or_self, /, *args, **keywords):
keywords = {**self.keywords, **keywords}
return self.func(cls_or_self, *self.args, *args, **keywords)
_method.__isabstractmethod__ = self.__isabstractmethod__
_method._partialmethod = self
_method.__partialmethod__ = self
return _method
def __get__(self, obj, cls=None):
get = getattr(self.func, "__get__", None)
result = None
if get is not None:
if get is not None and not isinstance(self.func, partial):
new_func = get(obj, cls)
if new_func is not self.func:
# Assume __get__ returning something new indicates the
@@ -423,6 +437,17 @@ def _unwrap_partial(func):
func = func.func
return func
def _unwrap_partialmethod(func):
prev = None
while func is not prev:
prev = func
while isinstance(getattr(func, "__partialmethod__", None), partialmethod):
func = func.__partialmethod__
while isinstance(func, partialmethod):
func = getattr(func, 'func')
func = _unwrap_partial(func)
return func
################################################################################
### LRU Cache function decorator
################################################################################
@@ -483,8 +508,9 @@ def lru_cache(maxsize=128, typed=False):
can grow without bound.
If *typed* is True, arguments of different types will be cached separately.
For example, f(3.0) and f(3) will be treated as distinct calls with
distinct results.
For example, f(decimal.Decimal("3.0")) and f(3.0) will be treated as
distinct calls with distinct results. Some types such as str and int may
be cached separately even when typed is false.
Arguments to the cached function must be hashable.
@@ -660,7 +686,7 @@ def cache(user_function, /):
def _c3_merge(sequences):
"""Merges MROs in *sequences* to a single MRO using the C3 algorithm.
Adapted from https://www.python.org/download/releases/2.3/mro/.
Adapted from https://docs.python.org/3/howto/mro.html.
"""
result = []
@@ -905,7 +931,6 @@ def singledispatch(func):
if not args:
raise TypeError(f'{funcname} requires at least '
'1 positional argument')
return dispatch(args[0].__class__)(*args, **kw)
funcname = getattr(func, '__name__', 'singledispatch function')
@@ -941,13 +966,18 @@ class singledispatchmethod:
return self.dispatcher.register(cls, func=method)
def __get__(self, obj, cls=None):
dispatch = self.dispatcher.dispatch
funcname = getattr(self.func, '__name__', 'singledispatchmethod method')
def _method(*args, **kwargs):
method = self.dispatcher.dispatch(args[0].__class__)
return method.__get__(obj, cls)(*args, **kwargs)
if not args:
raise TypeError(f'{funcname} requires at least '
'1 positional argument')
return dispatch(args[0].__class__).__get__(obj, cls)(*args, **kwargs)
_method.__isabstractmethod__ = self.__isabstractmethod__
_method.register = self.register
update_wrapper(_method, self.func)
return _method
@property
@@ -966,6 +996,7 @@ class cached_property:
self.func = func
self.attrname = None
self.__doc__ = func.__doc__
self.__module__ = func.__module__
def __set_name__(self, owner, name):
if self.attrname is None:

View File

@@ -31,10 +31,6 @@ c_functools = import_helper.import_fresh_module('functools',
decimal = import_helper.import_fresh_module('decimal', fresh=['_decimal'])
_partial_types = [py_functools.partial]
if c_functools:
_partial_types.append(c_functools.partial)
@contextlib.contextmanager
def replaced_module(name, replacement):
@@ -189,6 +185,19 @@ class TestPartial:
flat = partial(signature, 'asdf', bar=True)
self.assertEqual(signature(nested), signature(flat))
def test_nested_optimization_bug(self):
partial = self.partial
class Builder:
def __call__(self, tag, *children, **attrib):
return (tag, children, attrib)
def __getattr__(self, tag):
return partial(self, tag)
B = Builder()
m = B.m
assert m(1, 2, a=2) == ('m', (1, 2), dict(a=2))
def test_nested_partial_with_attribute(self):
# see issue 25137
partial = self.partial
@@ -207,10 +216,7 @@ class TestPartial:
kwargs = {'a': object(), 'b': object()}
kwargs_reprs = ['a={a!r}, b={b!r}'.format_map(kwargs),
'b={b!r}, a={a!r}'.format_map(kwargs)]
if self.partial in _partial_types:
name = 'functools.partial'
else:
name = self.partial.__name__
name = f"{self.partial.__module__}.{self.partial.__qualname__}"
f = self.partial(capture)
self.assertEqual(f'{name}({capture!r})', repr(f))
@@ -229,10 +235,7 @@ class TestPartial:
for kwargs_repr in kwargs_reprs])
def test_recursive_repr(self):
if self.partial in _partial_types:
name = 'functools.partial'
else:
name = self.partial.__name__
name = f"{self.partial.__module__}.{self.partial.__qualname__}"
f = self.partial(capture)
f.__setstate__((f, (), {}, {}))
@@ -344,8 +347,10 @@ class TestPartial:
f.__setstate__((f, (), {}, {}))
try:
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
with self.assertRaises(RecursionError):
pickle.dumps(f, proto)
# gh-117008: Small limit since pickle uses C stack memory
with support.infinite_recursion(100):
with self.assertRaises(RecursionError):
pickle.dumps(f, proto)
finally:
f.__setstate__((capture, (), {}, {}))
@@ -390,6 +395,30 @@ class TestPartial:
f = self.partial(object)
self.assertRaises(TypeError, f.__setstate__, BadSequence())
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_partial_as_method(self):
class A:
meth = self.partial(capture, 1, a=2)
cmeth = classmethod(self.partial(capture, 1, a=2))
smeth = staticmethod(self.partial(capture, 1, a=2))
a = A()
self.assertEqual(A.meth(3, b=4), ((1, 3), {'a': 2, 'b': 4}))
self.assertEqual(A.cmeth(3, b=4), ((1, A, 3), {'a': 2, 'b': 4}))
self.assertEqual(A.smeth(3, b=4), ((1, 3), {'a': 2, 'b': 4}))
with self.assertWarns(FutureWarning) as w:
self.assertEqual(a.meth(3, b=4), ((1, 3), {'a': 2, 'b': 4}))
self.assertEqual(w.filename, __file__)
self.assertEqual(a.cmeth(3, b=4), ((1, A, 3), {'a': 2, 'b': 4}))
self.assertEqual(a.smeth(3, b=4), ((1, 3), {'a': 2, 'b': 4}))
def test_partial_genericalias(self):
alias = self.partial[int]
self.assertIs(alias.__origin__, self.partial)
self.assertEqual(alias.__args__, (int,))
self.assertEqual(alias.__parameters__, ())
@unittest.skipUnless(c_functools, 'requires the C _functools module')
class TestPartialC(TestPartial, unittest.TestCase):
if c_functools:
@@ -436,6 +465,14 @@ class TestPartialC(TestPartial, unittest.TestCase):
self.assertIn('astr', r)
self.assertIn("['sth']", r)
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_repr(self):
return super().test_repr()
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_recursive_repr(self):
return super().test_recursive_repr()
class TestPartialPy(TestPartial, unittest.TestCase):
module = py_functools
@@ -454,14 +491,6 @@ class TestPartialCSubclass(TestPartialC):
if c_functools:
partial = CPartialSubclass
# TODO: RUSTPYTHON
def test_pickle(self):
TestPartial.test_pickle(self)
# TODO: RUSTPYTHON
def test_recursive_pickle(self):
TestPartial.test_recursive_pickle(self)
# partial subclasses are not optimized for nested calls
test_nested_optimization = None
@@ -572,6 +601,14 @@ class TestPartialMethod(unittest.TestCase):
method = functools.partialmethod(func=capture, a=1)
def test_repr(self):
self.assertEqual(repr(vars(self.A)['nothing']),
'functools.partialmethod({})'.format(capture))
self.assertEqual(repr(vars(self.A)['positional']),
'functools.partialmethod({}, 1)'.format(capture))
self.assertEqual(repr(vars(self.A)['keywords']),
'functools.partialmethod({}, a=2)'.format(capture))
self.assertEqual(repr(vars(self.A)['spec_keywords']),
'functools.partialmethod({}, self=1, func=2)'.format(capture))
self.assertEqual(repr(vars(self.A)['both']),
'functools.partialmethod({}, 3, b=4)'.format(capture))
@@ -620,9 +657,7 @@ class TestUpdateWrapper(unittest.TestCase):
def _default_update(self):
# XXX: RUSTPYTHON; f[T] is not supported yet
# def f[T](a:'This is a new annotation'):
def f(a:'This is a new annotation'):
def f[T](a:'This is a new annotation'):
"""This is a test"""
pass
f.attr = 'This is also a test'
@@ -632,8 +667,6 @@ class TestUpdateWrapper(unittest.TestCase):
functools.update_wrapper(wrapper, f)
return wrapper, f
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_default_update(self):
wrapper, f = self._default_update()
self.check_wrapper(wrapper, f)
@@ -717,6 +750,14 @@ class TestUpdateWrapper(unittest.TestCase):
self.assertTrue(wrapper.__doc__.startswith('max('))
self.assertEqual(wrapper.__annotations__, {})
def test_update_type_wrapper(self):
def wrapper(*args): pass
functools.update_wrapper(wrapper, type)
self.assertEqual(wrapper.__name__, 'type')
self.assertEqual(wrapper.__annotations__, {})
self.assertEqual(wrapper.__type_params__, ())
class TestWraps(TestUpdateWrapper):
@@ -951,9 +992,16 @@ class TestCmpToKey:
self.assertRaises(TypeError, hash, k)
self.assertNotIsInstance(k, collections.abc.Hashable)
@unittest.skipIf(support.MISSING_C_DOCSTRINGS,
"Signature information for builtins requires docstrings")
def test_cmp_to_signature(self):
self.assertEqual(str(Signature.from_callable(self.cmp_to_key)),
'(mycmp)')
sig = Signature.from_callable(self.cmp_to_key)
self.assertEqual(str(sig), '(mycmp)')
def mycmp(x, y):
return y - x
sig = Signature.from_callable(self.cmp_to_key(mycmp))
self.assertEqual(str(sig), '(obj)')
@unittest.skipUnless(c_functools, 'requires the C _functools module')
@@ -961,46 +1009,6 @@ class TestCmpToKeyC(TestCmpToKey, unittest.TestCase):
if c_functools:
cmp_to_key = c_functools.cmp_to_key
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_bad_cmp(self):
super().test_bad_cmp()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_cmp_to_key(self):
super().test_cmp_to_key()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_cmp_to_key_arguments(self):
super().test_cmp_to_key_arguments()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_hash(self):
super().test_hash()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_obj_field(self):
super().test_obj_field()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_sort_int(self):
super().test_sort_int()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_sort_int_str(self):
super().test_sort_int_str()
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_cmp_to_signature(self):
super().test_cmp_to_signature()
@support.cpython_only
def test_disallow_instantiation(self):
# Ensure that the type disallows instantiation (bpo-43916)
@@ -1008,6 +1016,38 @@ class TestCmpToKeyC(TestCmpToKey, unittest.TestCase):
self, type(c_functools.cmp_to_key(None))
)
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_bad_cmp(self):
return super().test_bad_cmp()
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_cmp_to_key(self):
return super().test_cmp_to_key()
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_cmp_to_key_arguments(self):
return super().test_cmp_to_key_arguments()
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_cmp_to_signature(self):
return super().test_cmp_to_signature()
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_hash(self):
return super().test_hash()
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_obj_field(self):
return super().test_obj_field()
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_sort_int(self):
return super().test_sort_int()
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_sort_int_str(self):
return super().test_sort_int_str()
class TestCmpToKeyPy(TestCmpToKey, unittest.TestCase):
cmp_to_key = staticmethod(py_functools.cmp_to_key)
@@ -1340,6 +1380,16 @@ class TestCache:
self.module._CacheInfo(hits=0, misses=0, maxsize=None, currsize=0))
class TestCachePy(TestCache, unittest.TestCase):
module = py_functools
@unittest.skipUnless(c_functools, 'requires the C _functools module')
class TestCacheC(TestCache, unittest.TestCase):
if c_functools:
module = c_functools
class TestLRU:
def test_lru(self):
@@ -1773,8 +1823,7 @@ class TestLRU:
time.sleep(.01)
return 3 * x
def test(i, x):
with self.subTest(thread=i):
self.assertEqual(f(x), 3 * x, i)
self.assertEqual(f(x), 3 * x, i)
threads = [threading.Thread(target=test, args=(i, v))
for i, v in enumerate([1, 2, 2, 3, 2])]
with threading_helper.start_threads(threads):
@@ -1878,6 +1927,7 @@ class TestLRU:
return 1
self.assertEqual(f.cache_parameters(), {'maxsize': 1000, "typed": True})
@support.suppress_immortalization()
def test_lru_cache_weakrefable(self):
@self.module.lru_cache
def test_function(x):
@@ -1908,12 +1958,33 @@ class TestLRU:
self.assertIsNone(ref())
def test_common_signatures(self):
def orig(): ...
def orig(a, /, b, c=True): ...
lru = self.module.lru_cache(1)(orig)
self.assertEqual(str(Signature.from_callable(lru)), '(a, /, b, c=True)')
self.assertEqual(str(Signature.from_callable(lru.cache_info)), '()')
self.assertEqual(str(Signature.from_callable(lru.cache_clear)), '()')
@support.skip_on_s390x
@unittest.skipIf(support.is_wasi, "WASI has limited C stack")
def test_lru_recursion(self):
@self.module.lru_cache
def fib(n):
if n <= 1:
return n
return fib(n-1) + fib(n-2)
if not support.Py_DEBUG:
depth = support.get_c_recursion_limit()*2//7
with support.infinite_recursion():
fib(depth)
if self.module == c_functools:
fib.cache_clear()
with support.infinite_recursion():
with self.assertRaises(RecursionError):
fib(10000)
@py_functools.lru_cache()
def py_cached_func(x, y):
@@ -2524,6 +2595,74 @@ class TestSingleDispatch(unittest.TestCase):
self.assertTrue(A.t(''))
self.assertEqual(A.t(0.0), 0.0)
def test_slotted_class(self):
class Slot:
__slots__ = ('a', 'b')
@functools.singledispatchmethod
def go(self, item, arg):
pass
@go.register
def _(self, item: int, arg):
return item + arg
s = Slot()
self.assertEqual(s.go(1, 1), 2)
def test_classmethod_slotted_class(self):
class Slot:
__slots__ = ('a', 'b')
@functools.singledispatchmethod
@classmethod
def go(cls, item, arg):
pass
@go.register
@classmethod
def _(cls, item: int, arg):
return item + arg
s = Slot()
self.assertEqual(s.go(1, 1), 2)
self.assertEqual(Slot.go(1, 1), 2)
def test_staticmethod_slotted_class(self):
class A:
__slots__ = ['a']
@functools.singledispatchmethod
@staticmethod
def t(arg):
return arg
@t.register(int)
@staticmethod
def _(arg):
return isinstance(arg, int)
@t.register(str)
@staticmethod
def _(arg):
return isinstance(arg, str)
a = A()
self.assertTrue(A.t(0))
self.assertTrue(A.t(''))
self.assertEqual(A.t(0.0), 0.0)
self.assertTrue(a.t(0))
self.assertTrue(a.t(''))
self.assertEqual(a.t(0.0), 0.0)
def test_assignment_behavior(self):
# see gh-106448
class A:
@functools.singledispatchmethod
def t(arg):
return arg
a = A()
a.t.foo = 'bar'
a2 = A()
with self.assertRaises(AttributeError):
a2.t.foo
def test_classmethod_register(self):
class A:
def __init__(self, arg):
@@ -2659,6 +2798,7 @@ class TestSingleDispatch(unittest.TestCase):
"""My function docstring"""
return str(arg)
prefix = A.__qualname__ + '.'
for meth in (
A.func,
A().func,
@@ -2668,7 +2808,11 @@ class TestSingleDispatch(unittest.TestCase):
A().static_func
):
with self.subTest(meth=meth):
self.assertEqual(meth.__doc__, 'My function docstring')
self.assertEqual(meth.__qualname__, prefix + meth.__name__)
self.assertEqual(meth.__doc__,
('My function docstring'
if support.HAVE_PY_DOCSTRINGS
else None))
self.assertEqual(meth.__annotations__['arg'], int)
self.assertEqual(A.func.__name__, 'func')
@@ -2757,7 +2901,10 @@ class TestSingleDispatch(unittest.TestCase):
WithSingleDispatch().decorated_classmethod
):
with self.subTest(meth=meth):
self.assertEqual(meth.__doc__, 'My function docstring')
self.assertEqual(meth.__doc__,
('My function docstring'
if support.HAVE_PY_DOCSTRINGS
else None))
self.assertEqual(meth.__annotations__['arg'], int)
self.assertEqual(
@@ -2829,11 +2976,26 @@ class TestSingleDispatch(unittest.TestCase):
def test_invalid_positional_argument(self):
@functools.singledispatch
def f(*args):
def f(*args, **kwargs):
pass
msg = 'f requires at least 1 positional argument'
with self.assertRaisesRegex(TypeError, msg):
f()
msg = 'f requires at least 1 positional argument'
with self.assertRaisesRegex(TypeError, msg):
f(a=1)
def test_invalid_positional_argument_singledispatchmethod(self):
class A:
@functools.singledispatchmethod
def t(self, *args, **kwargs):
pass
msg = 't requires at least 1 positional argument'
with self.assertRaisesRegex(TypeError, msg):
A().t()
msg = 't requires at least 1 positional argument'
with self.assertRaisesRegex(TypeError, msg):
A().t(a=1)
def test_union(self):
@functools.singledispatch
@@ -2957,6 +3119,115 @@ class TestSingleDispatch(unittest.TestCase):
self.assertEqual(f(""), "default")
self.assertEqual(f(b""), "default")
def test_method_equal_instances(self):
# gh-127750: Reference to self was cached
class A:
def __eq__(self, other):
return True
def __hash__(self):
return 1
@functools.singledispatchmethod
def t(self, arg):
return self
a = A()
b = A()
self.assertIs(a.t(1), a)
self.assertIs(b.t(2), b)
def test_method_bad_hash(self):
class A:
def __eq__(self, other):
raise AssertionError
def __hash__(self):
raise AssertionError
@functools.singledispatchmethod
def t(self, arg):
pass
# Should not raise
A().t(1)
hash(A().t)
A().t == A().t
def test_method_no_reference_loops(self):
# gh-127750: Created a strong reference to self
class A:
@functools.singledispatchmethod
def t(self, arg):
return weakref.ref(self)
a = A()
r = a.t(1)
self.assertIsNotNone(r())
del a # delete a after a.t
if not support.check_impl_detail(cpython=True):
support.gc_collect()
self.assertIsNone(r())
a = A()
t = a.t
del a # delete a before a.t
support.gc_collect()
r = t(1)
self.assertIsNotNone(r())
del t
if not support.check_impl_detail(cpython=True):
support.gc_collect()
self.assertIsNone(r())
def test_signatures(self):
@functools.singledispatch
def func(item, arg: int) -> str:
return str(item)
@func.register
def _(item: int, arg: bytes) -> str:
return str(item)
self.assertEqual(str(Signature.from_callable(func)),
'(item, arg: int) -> str')
def test_method_signatures(self):
class A:
def m(self, item, arg: int) -> str:
return str(item)
@classmethod
def cm(cls, item, arg: int) -> str:
return str(item)
@functools.singledispatchmethod
def func(self, item, arg: int) -> str:
return str(item)
@func.register
def _(self, item, arg: bytes) -> str:
return str(item)
@functools.singledispatchmethod
@classmethod
def cls_func(cls, item, arg: int) -> str:
return str(arg)
@func.register
@classmethod
def _(cls, item, arg: bytes) -> str:
return str(item)
@functools.singledispatchmethod
@staticmethod
def static_func(item, arg: int) -> str:
return str(arg)
@func.register
@staticmethod
def _(item, arg: bytes) -> str:
return str(item)
self.assertEqual(str(Signature.from_callable(A.func)),
'(self, item, arg: int) -> str')
self.assertEqual(str(Signature.from_callable(A().func)),
'(self, item, arg: int) -> str')
self.assertEqual(str(Signature.from_callable(A.cls_func)),
'(cls, item, arg: int) -> str')
self.assertEqual(str(Signature.from_callable(A.static_func)),
'(item, arg: int) -> str')
class CachedCostItem:
_cost = 1
@@ -3007,8 +3278,7 @@ class TestCachedProperty(unittest.TestCase):
self.assertEqual(item.get_cost(), 4)
self.assertEqual(item.cached_cost, 3)
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_object_with_slots(self):
item = CachedCostItemWithSlots()
with self.assertRaisesRegex(
@@ -3032,8 +3302,7 @@ class TestCachedProperty(unittest.TestCase):
):
MyClass.prop
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_reuse_different_names(self):
"""Disallow this case because decorated function a would not be cached."""
with self.assertRaises(TypeError) as ctx:
@@ -3089,7 +3358,13 @@ class TestCachedProperty(unittest.TestCase):
self.assertIsInstance(CachedCostItem.cost, py_functools.cached_property)
def test_doc(self):
self.assertEqual(CachedCostItem.cost.__doc__, "The cost of the item.")
self.assertEqual(CachedCostItem.cost.__doc__,
("The cost of the item."
if support.HAVE_PY_DOCSTRINGS
else None))
def test_module(self):
self.assertEqual(CachedCostItem.cost.__module__, CachedCostItem.__module__)
def test_subclass_with___set__(self):
"""Caching still works for a subclass defining __set__."""