From 2f0b31d6d448c3dfdb3ff8268b2585274c7d9556 Mon Sep 17 00:00:00 2001 From: Mahmoud Hashemi Date: Sun, 29 Mar 2015 14:06:40 -0700 Subject: [PATCH] adding funcutils docstrings, slight argument name tweak --- boltons/formatutils.py | 2 +- boltons/funcutils.py | 161 ++++++++++++++++++++++++++--------------- 2 files changed, 104 insertions(+), 59 deletions(-) diff --git a/boltons/formatutils.py b/boltons/formatutils.py index 2c8b484..9911a99 100644 --- a/boltons/formatutils.py +++ b/boltons/formatutils.py @@ -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 diff --git a/boltons/funcutils.py b/boltons/funcutils.py index 0a6da0f..9fd2f5e 100644 --- a/boltons/funcutils.py +++ b/boltons/funcutils.py @@ -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()