Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 62 additions & 31 deletions Lib/functools.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@
# 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
Expand Down Expand Up @@ -236,14 +237,16 @@ def __ge__(self, other):

def reduce(function, sequence, initial=_initial_missing):
"""
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.
reduce(function, iterable[, initial], /) -> value

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)
Expand Down Expand Up @@ -284,7 +287,7 @@ def __new__(cls, func, /, *args, **keywords):
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
Expand All @@ -302,13 +305,23 @@ def __call__(self, /, *args, **keywords):

@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,
Expand Down Expand Up @@ -338,6 +351,9 @@ def __setstate__(self, state):
self.args = args
self.keywords = kwds

__class_getitem__ = classmethod(GenericAlias)


try:
from _functools import partial
except ImportError:
Expand Down Expand Up @@ -372,28 +388,26 @@ def __init__(self, func, /, *args, **keywords):
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
Expand Down Expand Up @@ -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
################################################################################
Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -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 = []
Expand Down Expand Up @@ -905,7 +931,6 @@ def wrapper(*args, **kw):
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')
Expand Down Expand Up @@ -941,13 +966,18 @@ def register(self, cls, method=None):
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
Expand All @@ -966,6 +996,7 @@ def __init__(self, func):
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:
Expand Down
Loading
Loading