adding funcutils docstrings, slight argument name tweak

This commit is contained in:
Mahmoud Hashemi 2015-03-29 14:06:40 -07:00
parent 0370400ace
commit 2f0b31d6d4
2 changed files with 104 additions and 59 deletions

View File

@ -150,7 +150,7 @@ def tokenize_format_str(fstr, resolve_pos=True):
class BaseFormatField(object):
"""\
A class representing a reference to an argument inside of a
new-style format string. For instance, in "{greeting}, world!",
new-style format string. For instance, in `"{greeting}, world!"`,
there is a field called "greeting".
These fields can have a lot of options applied to them. See the

View File

@ -5,27 +5,49 @@ from types import MethodType
from itertools import chain
__all__ = ['partial', 'CachedInstancePartial', 'InstancePartial']
def mro_items(obj_type):
# handle slots?
return chain.from_iterable([ct.__dict__.iteritems()
for ct in obj_type.__mro__])
def dir_dict(obj):
# separate function for handling descriptors on types?
ret = {}
for k in dir(obj):
ret[k] = getattr(obj, k)
return ret
__all__ = ['partial', 'CachedInstancePartial', 'InstancePartial',
'dir_dict', 'mro_items']
_func_type = type(lambda: None)
def mro_items(type_obj):
"""\
Takes a type and returns an iterator over all class variables
throughout the type hierarchy (respecting the MRO).
>>> sorted([k for k, v in mro_items(int) if not (callable(v) or isinstance(v, type(int.__int__)))])
['__class__', '__doc__', '__doc__', 'denominator', 'imag', 'numerator', 'real']
"""
# TODO: handle slots?
return chain.from_iterable([ct.__dict__.iteritems()
for ct in type_obj.__mro__])
def dir_dict(obj, raise_exc=False):
"""\
Return a dictionary of attribute names to values for a given
object. Unlike ``obj.__dict__``, this function returns all
attributes on the object, including ones on parent classes.
"""
# TODO: separate function for handling descriptors on types?
ret = {}
for k in dir(obj):
try:
ret[k] = getattr(obj, k)
except:
if raise_exc:
raise
return ret
def copy_function(orig, copy_dict=True):
"""\
Returns a shallow copy of the function, including code object,
globals, closure, etc. If ``copy_dict`` is set to ``True``, then
the function's nonstandard attributes are also copied.
"""
# TODO: Python 3 compat
ret = _func_type(orig.func_code,
orig.func_globals,
name=orig.func_name,
@ -37,11 +59,36 @@ def copy_function(orig, copy_dict=True):
class InstancePartial(functools.partial):
"""\
:class:`functools.partial` is a huge convenience for anyone working
with Python's great first-class functions. It allows developers to
curry arguments and incrementally create simpler callables for a
variety of use cases.
Unfortunately there's one big gap in its usefulness:
methods. Partials just don't get bound as methods and
automatically handed a reference to ``self``. The
``InstancePartial`` class remedies this by inheriting from
:class:`functools.partial` and implementing the necessary
descriptor protocol. There are no other differences in
implementation or usage. :class:`CachedInstancePartial`, below,
has the same ability, but is slightly more efficient.
"""
def __get__(self, obj, obj_type):
return MethodType(self, obj, obj_type)
class CachedInstancePartial(functools.partial):
"""\
The CachedInstancePartial is virtually the same as
:class:`InstancePartial`, adding support for method-usage to
:class:`functools.partial`, except that upon first access, it
caches the bound method on the associated object, speeding it up
for future accesses, and bringing the method call overhead to
about the same as non-``partial`` methods.
See the :class:`InstancePartial` docstring for more info.
"""
def __init__(self, func, *a, **kw):
self.__name__ = None
self.__doc__ = func.__doc__
@ -65,50 +112,48 @@ class CachedInstancePartial(functools.partial):
partial = CachedInstancePartial
'''
class FunctionDef(object):
"""
general blocker: accept a bunch of fine-grained arguments, or just
accept a whole ArgSpec? or a whole signature?
"""
def __init__(self, name, code, doc=None):
pass
'''
#class FunctionDef(object):
# """
# general blocker: accept a bunch of fine-grained arguments, or just
# accept a whole ArgSpec? or a whole signature?
# """
# def __init__(self, name, code, doc=None):
# pass
# tests
def _partial_main():
class Greeter(object):
def __init__(self, greeting):
self.greeting = greeting
def greet(self, excitement='.'):
return self.greeting.capitalize() + excitement
partial_greet = InstancePartial(greet, excitement='!')
cached_partial_greet = CachedInstancePartial(greet, excitement='...')
def native_greet(self):
return self.greet(';')
class SubGreeter(Greeter):
pass
g = SubGreeter('hello')
print g.greet()
print g.native_greet()
print g.partial_greet()
print g.cached_partial_greet()
print CachedInstancePartial(g.greet, excitement='s')()
def callee():
return 1
callee_copy = copy_function(callee)
assert callee is not callee_copy
assert callee() == callee_copy()
import pdb;pdb.set_trace()
if __name__ == '__main__':
def _partial_main():
class Greeter(object):
def __init__(self, greeting):
self.greeting = greeting
def greet(self, excitement='.'):
return self.greeting.capitalize() + excitement
partial_greet = InstancePartial(greet, excitement='!')
cached_partial_greet = CachedInstancePartial(greet, excitement='...')
def native_greet(self):
return self.greet(';')
class SubGreeter(Greeter):
pass
g = SubGreeter('hello')
print g.greet()
print g.native_greet()
print g.partial_greet()
print g.cached_partial_greet()
print CachedInstancePartial(g.greet, excitement='s')()
def callee():
return 1
callee_copy = copy_function(callee)
assert callee is not callee_copy
assert callee() == callee_copy()
import pdb;pdb.set_trace()
_partial_main()