Skip to content

Commit 5d9e623

Browse files
authored
Update functools from 3.13.9 (RustPython#6205)
* Update `functools.py` from 3.13.9 * mark/unmark tests
1 parent b15e537 commit 5d9e623

File tree

2 files changed

+410
-104
lines changed

2 files changed

+410
-104
lines changed

Lib/functools.py

Lines changed: 62 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@
1919
# import types, weakref # Deferred to single_dispatch()
2020
from reprlib import recursive_repr
2121
from _thread import RLock
22-
from types import GenericAlias
2322

23+
# Avoid importing types, so we can speedup import time
24+
GenericAlias = type(list[int])
2425

2526
################################################################################
2627
### update_wrapper() and wraps() decorator
@@ -236,14 +237,16 @@ def __ge__(self, other):
236237

237238
def reduce(function, sequence, initial=_initial_missing):
238239
"""
239-
reduce(function, iterable[, initial]) -> value
240-
241-
Apply a function of two arguments cumulatively to the items of a sequence
242-
or iterable, from left to right, so as to reduce the iterable to a single
243-
value. For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates
244-
((((1+2)+3)+4)+5). If initial is present, it is placed before the items
245-
of the iterable in the calculation, and serves as a default when the
246-
iterable is empty.
240+
reduce(function, iterable[, initial], /) -> value
241+
242+
Apply a function of two arguments cumulatively to the items of an iterable, from left to right.
243+
244+
This effectively reduces the iterable to a single value. If initial is present,
245+
it is placed before the items of the iterable in the calculation, and serves as
246+
a default when the iterable is empty.
247+
248+
For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])
249+
calculates ((((1 + 2) + 3) + 4) + 5).
247250
"""
248251

249252
it = iter(sequence)
@@ -284,7 +287,7 @@ def __new__(cls, func, /, *args, **keywords):
284287
if not callable(func):
285288
raise TypeError("the first argument must be callable")
286289

287-
if hasattr(func, "func"):
290+
if isinstance(func, partial):
288291
args = func.args + args
289292
keywords = {**func.keywords, **keywords}
290293
func = func.func
@@ -302,13 +305,23 @@ def __call__(self, /, *args, **keywords):
302305

303306
@recursive_repr()
304307
def __repr__(self):
305-
qualname = type(self).__qualname__
308+
cls = type(self)
309+
qualname = cls.__qualname__
310+
module = cls.__module__
306311
args = [repr(self.func)]
307312
args.extend(repr(x) for x in self.args)
308313
args.extend(f"{k}={v!r}" for (k, v) in self.keywords.items())
309-
if type(self).__module__ == "functools":
310-
return f"functools.{qualname}({', '.join(args)})"
311-
return f"{qualname}({', '.join(args)})"
314+
return f"{module}.{qualname}({', '.join(args)})"
315+
316+
def __get__(self, obj, objtype=None):
317+
if obj is None:
318+
return self
319+
import warnings
320+
warnings.warn('functools.partial will be a method descriptor in '
321+
'future Python versions; wrap it in staticmethod() '
322+
'if you want to preserve the old behavior',
323+
FutureWarning, 2)
324+
return self
312325

313326
def __reduce__(self):
314327
return type(self), (self.func,), (self.func, self.args,
@@ -338,6 +351,9 @@ def __setstate__(self, state):
338351
self.args = args
339352
self.keywords = kwds
340353

354+
__class_getitem__ = classmethod(GenericAlias)
355+
356+
341357
try:
342358
from _functools import partial
343359
except ImportError:
@@ -372,28 +388,26 @@ def __init__(self, func, /, *args, **keywords):
372388
self.keywords = keywords
373389

374390
def __repr__(self):
375-
args = ", ".join(map(repr, self.args))
376-
keywords = ", ".join("{}={!r}".format(k, v)
377-
for k, v in self.keywords.items())
378-
format_string = "{module}.{cls}({func}, {args}, {keywords})"
379-
return format_string.format(module=self.__class__.__module__,
380-
cls=self.__class__.__qualname__,
381-
func=self.func,
382-
args=args,
383-
keywords=keywords)
391+
cls = type(self)
392+
module = cls.__module__
393+
qualname = cls.__qualname__
394+
args = [repr(self.func)]
395+
args.extend(map(repr, self.args))
396+
args.extend(f"{k}={v!r}" for k, v in self.keywords.items())
397+
return f"{module}.{qualname}({', '.join(args)})"
384398

385399
def _make_unbound_method(self):
386400
def _method(cls_or_self, /, *args, **keywords):
387401
keywords = {**self.keywords, **keywords}
388402
return self.func(cls_or_self, *self.args, *args, **keywords)
389403
_method.__isabstractmethod__ = self.__isabstractmethod__
390-
_method._partialmethod = self
404+
_method.__partialmethod__ = self
391405
return _method
392406

393407
def __get__(self, obj, cls=None):
394408
get = getattr(self.func, "__get__", None)
395409
result = None
396-
if get is not None:
410+
if get is not None and not isinstance(self.func, partial):
397411
new_func = get(obj, cls)
398412
if new_func is not self.func:
399413
# Assume __get__ returning something new indicates the
@@ -423,6 +437,17 @@ def _unwrap_partial(func):
423437
func = func.func
424438
return func
425439

440+
def _unwrap_partialmethod(func):
441+
prev = None
442+
while func is not prev:
443+
prev = func
444+
while isinstance(getattr(func, "__partialmethod__", None), partialmethod):
445+
func = func.__partialmethod__
446+
while isinstance(func, partialmethod):
447+
func = getattr(func, 'func')
448+
func = _unwrap_partial(func)
449+
return func
450+
426451
################################################################################
427452
### LRU Cache function decorator
428453
################################################################################
@@ -483,8 +508,9 @@ def lru_cache(maxsize=128, typed=False):
483508
can grow without bound.
484509
485510
If *typed* is True, arguments of different types will be cached separately.
486-
For example, f(3.0) and f(3) will be treated as distinct calls with
487-
distinct results.
511+
For example, f(decimal.Decimal("3.0")) and f(3.0) will be treated as
512+
distinct calls with distinct results. Some types such as str and int may
513+
be cached separately even when typed is false.
488514
489515
Arguments to the cached function must be hashable.
490516
@@ -660,7 +686,7 @@ def cache(user_function, /):
660686
def _c3_merge(sequences):
661687
"""Merges MROs in *sequences* to a single MRO using the C3 algorithm.
662688
663-
Adapted from https://www.python.org/download/releases/2.3/mro/.
689+
Adapted from https://docs.python.org/3/howto/mro.html.
664690
665691
"""
666692
result = []
@@ -905,7 +931,6 @@ def wrapper(*args, **kw):
905931
if not args:
906932
raise TypeError(f'{funcname} requires at least '
907933
'1 positional argument')
908-
909934
return dispatch(args[0].__class__)(*args, **kw)
910935

911936
funcname = getattr(func, '__name__', 'singledispatch function')
@@ -941,13 +966,18 @@ def register(self, cls, method=None):
941966
return self.dispatcher.register(cls, func=method)
942967

943968
def __get__(self, obj, cls=None):
969+
dispatch = self.dispatcher.dispatch
970+
funcname = getattr(self.func, '__name__', 'singledispatchmethod method')
944971
def _method(*args, **kwargs):
945-
method = self.dispatcher.dispatch(args[0].__class__)
946-
return method.__get__(obj, cls)(*args, **kwargs)
972+
if not args:
973+
raise TypeError(f'{funcname} requires at least '
974+
'1 positional argument')
975+
return dispatch(args[0].__class__).__get__(obj, cls)(*args, **kwargs)
947976

948977
_method.__isabstractmethod__ = self.__isabstractmethod__
949978
_method.register = self.register
950979
update_wrapper(_method, self.func)
980+
951981
return _method
952982

953983
@property
@@ -966,6 +996,7 @@ def __init__(self, func):
966996
self.func = func
967997
self.attrname = None
968998
self.__doc__ = func.__doc__
999+
self.__module__ = func.__module__
9691000

9701001
def __set_name__(self, owner, name):
9711002
if self.attrname is None:

0 commit comments

Comments
 (0)