summaryrefslogtreecommitdiffstats
path: root/Lib/functools.py
diff options
context:
space:
mode:
authorNick Coghlan <ncoghlan@gmail.com>2013-11-03 06:41:46 (GMT)
committerNick Coghlan <ncoghlan@gmail.com>2013-11-03 06:41:46 (GMT)
commitf4cb48a72b20b2d166d5303426ab4098243c35e1 (patch)
tree030f58e9648df6a8a92a4f28842dd2393f4b1079 /Lib/functools.py
parentb19ff4174183e1e519c99243cff0e3c30e9ac258 (diff)
downloadcpython-f4cb48a72b20b2d166d5303426ab4098243c35e1.zip
cpython-f4cb48a72b20b2d166d5303426ab4098243c35e1.tar.gz
cpython-f4cb48a72b20b2d166d5303426ab4098243c35e1.tar.bz2
Issue #4331: Added functools.partialmethod
Initial patch by Alon Horev
Diffstat (limited to 'Lib/functools.py')
-rw-r--r--Lib/functools.py78
1 files changed, 76 insertions, 2 deletions
diff --git a/Lib/functools.py b/Lib/functools.py
index 6a6974f..8989361 100644
--- a/Lib/functools.py
+++ b/Lib/functools.py
@@ -19,7 +19,7 @@ except ImportError:
pass
from abc import get_cache_token
from collections import namedtuple
-from types import MappingProxyType
+from types import MappingProxyType, MethodType
from weakref import WeakKeyDictionary
try:
from _thread import RLock
@@ -223,8 +223,9 @@ except ImportError:
### partial() argument application
################################################################################
+# Purely functional, no descriptor behaviour
def partial(func, *args, **keywords):
- """new function with partial application of the given arguments
+ """New function with partial application of the given arguments
and keywords.
"""
def newfunc(*fargs, **fkeywords):
@@ -241,6 +242,79 @@ try:
except ImportError:
pass
+# Descriptor version
+class partialmethod(object):
+ """Method descriptor with partial application of the given arguments
+ and keywords.
+
+ Supports wrapping existing descriptors and handles non-descriptor
+ callables as instance methods.
+ """
+
+ def __init__(self, func, *args, **keywords):
+ if not callable(func) and not hasattr(func, "__get__"):
+ raise TypeError("{!r} is not callable or a descriptor"
+ .format(func))
+
+ # func could be a descriptor like classmethod which isn't callable,
+ # so we can't inherit from partial (it verifies func is callable)
+ if isinstance(func, partialmethod):
+ # flattening is mandatory in order to place cls/self before all
+ # other arguments
+ # it's also more efficient since only one function will be called
+ self.func = func.func
+ self.args = func.args + args
+ self.keywords = func.keywords.copy()
+ self.keywords.update(keywords)
+ else:
+ self.func = func
+ self.args = args
+ 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__.__name__,
+ func=self.func,
+ args=args,
+ keywords=keywords)
+
+ def _make_unbound_method(self):
+ def _method(*args, **keywords):
+ call_keywords = self.keywords.copy()
+ call_keywords.update(keywords)
+ cls_or_self, *rest = args
+ call_args = (cls_or_self,) + self.args + tuple(rest)
+ return self.func(*call_args, **call_keywords)
+ _method.__isabstractmethod__ = self.__isabstractmethod__
+ return _method
+
+ def __get__(self, obj, cls):
+ get = getattr(self.func, "__get__", None)
+ result = None
+ if get is not None:
+ new_func = get(obj, cls)
+ if new_func is not self.func:
+ # Assume __get__ returning something new indicates the
+ # creation of an appropriate callable
+ result = partial(new_func, *self.args, **self.keywords)
+ try:
+ result.__self__ = new_func.__self__
+ except AttributeError:
+ pass
+ if result is None:
+ # If the underlying descriptor didn't do anything, treat this
+ # like an instance method
+ result = self._make_unbound_method().__get__(obj, cls)
+ return result
+
+ @property
+ def __isabstractmethod__(self):
+ return getattr(self.func, "__isabstractmethod__", False)
+
################################################################################
### LRU Cache function decorator