"""functools.py - Tools for working with functions and callable objects"""# Python module wrapper for _functools C module# to allow utilities written in Python to be added# to the functools module.# Written by Nick Coghlan <ncoghlan at gmail.com># and Raymond Hettinger <python at rcn.com># Copyright (C) 2006-2010 Python Software Foundation.# See C source code for _functools credits/copyright__all__=['update_wrapper','wraps','WRAPPER_ASSIGNMENTS','WRAPPER_UPDATES','total_ordering','cmp_to_key','lru_cache','reduce','partial']from_functoolsimportpartial,reducefromcollectionsimportOrderedDict,namedtupletry:from_threadimportallocate_lockasLockexcept:from_dummy_threadimportallocate_lockasLock# update_wrapper() and wraps() are tools to help write# wrapper functions that can handle naive introspectionWRAPPER_ASSIGNMENTS=('__module__','__name__','__doc__','__annotations__')WRAPPER_UPDATES=('__dict__',)defupdate_wrapper(wrapper,wrapped,assigned=WRAPPER_ASSIGNMENTS,updated=WRAPPER_UPDATES):"""Update a wrapper function to look like the wrapped function wrapper is the function to be updated wrapped is the original function assigned is a tuple naming the attributes assigned directly from the wrapped function to the wrapper function (defaults to functools.WRAPPER_ASSIGNMENTS) updated is a tuple naming the attributes of the wrapper that are updated with the corresponding attribute from the wrapped function (defaults to functools.WRAPPER_UPDATES) """wrapper.__wrapped__=wrappedforattrinassigned:try:value=getattr(wrapped,attr)exceptAttributeError:passelse:setattr(wrapper,attr,value)forattrinupdated:getattr(wrapper,attr).update(getattr(wrapped,attr,{}))# Return the wrapper so this can be used as a decorator via partial()returnwrapperdefwraps(wrapped,assigned=WRAPPER_ASSIGNMENTS,updated=WRAPPER_UPDATES):"""Decorator factory to apply update_wrapper() to a wrapper function Returns a decorator that invokes update_wrapper() with the decorated function as the wrapper argument and the arguments to wraps() as the remaining arguments. Default arguments are as for update_wrapper(). This is a convenience function to simplify applying partial() to update_wrapper(). """returnpartial(update_wrapper,wrapped=wrapped,assigned=assigned,updated=updated)deftotal_ordering(cls):"""Class decorator that fills in missing ordering methods"""convert={'__lt__':[('__gt__',lambdaself,other:not(self<otherorself==other)),('__le__',lambdaself,other:self<otherorself==other),('__ge__',lambdaself,other:notself<other)],'__le__':[('__ge__',lambdaself,other:notself<=otherorself==other),('__lt__',lambdaself,other:self<=otherandnotself==other),('__gt__',lambdaself,other:notself<=other)],'__gt__':[('__lt__',lambdaself,other:not(self>otherorself==other)),('__ge__',lambdaself,other:self>otherorself==other),('__le__',lambdaself,other:notself>other)],'__ge__':[('__le__',lambdaself,other:(notself>=other)orself==other),('__gt__',lambdaself,other:self>=otherandnotself==other),('__lt__',lambdaself,other:notself>=other)]}# Find user-defined comparisons (not those inherited from object).roots=[opforopinconvertifgetattr(cls,op,None)isnotgetattr(object,op,None)]ifnotroots:raiseValueError('must define at least one ordering operation: < > <= >=')root=max(roots)# prefer __lt__ to __le__ to __gt__ to __ge__foropname,opfuncinconvert[root]:ifopnamenotinroots:opfunc.__name__=opnameopfunc.__doc__=getattr(int,opname).__doc__setattr(cls,opname,opfunc)returnclsdefcmp_to_key(mycmp):"""Convert a cmp= function into a key= function"""classK(object):def__init__(self,obj,*args):self.obj=objdef__lt__(self,other):returnmycmp(self.obj,other.obj)<0def__gt__(self,other):returnmycmp(self.obj,other.obj)>0def__eq__(self,other):returnmycmp(self.obj,other.obj)==0def__le__(self,other):returnmycmp(self.obj,other.obj)<=0def__ge__(self,other):returnmycmp(self.obj,other.obj)>=0def__ne__(self,other):returnmycmp(self.obj,other.obj)!=0def__hash__(self):raiseTypeError('hash not implemented')returnK_CacheInfo=namedtuple("CacheInfo","hits misses maxsize currsize")deflru_cache(maxsize=100):"""Least-recently-used cache decorator. If *maxsize* is set to None, the LRU features are disabled and the cache can grow without bound. Arguments to the cached function must be hashable. View the cache statistics named tuple (hits, misses, maxsize, currsize) with f.cache_info(). Clear the cache and statistics with f.cache_clear(). Access the underlying function with f.__wrapped__. See: http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used """# Users should only access the lru_cache through its public API:# cache_info, cache_clear, and f.__wrapped__# The internals of the lru_cache are encapsulated for thread safety and# to allow the implementation to change (including a possible C version).defdecorating_function(user_function,tuple=tuple,sorted=sorted,len=len,KeyError=KeyError):hits=misses=0kwd_mark=object()# separates positional and keyword argslock=Lock()# needed because ordereddicts aren't threadsafeifmaxsizeisNone:cache=dict()# simple cache without ordering or size limit@wraps(user_function)defwrapper(*args,**kwds):nonlocalhits,misseskey=argsifkwds:key+=(kwd_mark,)+tuple(sorted(kwds.items()))try:result=cache[key]hits+=1exceptKeyError:result=user_function(*args,**kwds)cache[key]=resultmisses+=1returnresultelse:cache=OrderedDict()# ordered least recent to most recentcache_popitem=cache.popitemcache_renew=cache.move_to_end@wraps(user_function)defwrapper(*args,**kwds):nonlocalhits,misseskey=argsifkwds:key+=(kwd_mark,)+tuple(sorted(kwds.items()))try:withlock:result=cache[key]cache_renew(key)# record recent use of this keyhits+=1exceptKeyError:result=user_function(*args,**kwds)withlock:cache[key]=result# record recent use of this keymisses+=1iflen(cache)>maxsize:cache_popitem(0)# purge least recently used cache entryreturnresultdefcache_info():"""Report cache statistics"""withlock:return_CacheInfo(hits,misses,maxsize,len(cache))defcache_clear():"""Clear the cache and cache statistics"""nonlocalhits,misseswithlock:cache.clear()hits=misses=0wrapper.cache_info=cache_infowrapper.cache_clear=cache_clearreturnwrapperreturndecorating_function