"""Object related utilities, including introspection, etc."""fromfunctoolsimportreduce__all__=('Bunch','FallbackContext','getitem_property','mro_lookup')
[文档]classBunch:"""Object that enables you to modify attributes."""def__init__(self,**kwargs):self.__dict__.update(kwargs)
[文档]defmro_lookup(cls,attr,stop=None,monkey_patched=None):"""Return the first node by MRO order that defines an attribute. Arguments: cls (Any): Child class to traverse. attr (str): Name of attribute to find. stop (Set[Any]): A set of types that if reached will stop the search. monkey_patched (Sequence): Use one of the stop classes if the attributes module origin isn't in this list. Used to detect monkey patched attributes. Returns: Any: The attribute value, or :const:`None` if not found. """stop=set()ifnotstopelsestopmonkey_patched=[]ifnotmonkey_patchedelsemonkey_patchedfornodeincls.mro():ifnodeinstop:try:value=node.__dict__[attr]module_origin=value.__module__except(AttributeError,KeyError):passelse:ifmodule_originnotinmonkey_patched:returnnodereturnifattrinnode.__dict__:returnnode
[文档]classFallbackContext:"""Context workaround. The built-in ``@contextmanager`` utility does not work well when wrapping other contexts, as the traceback is wrong when the wrapped context raises. This solves this problem and can be used instead of ``@contextmanager`` in this example:: @contextmanager def connection_or_default_connection(connection=None): if connection: # user already has a connection, shouldn't close # after use yield connection else: # must've new connection, and also close the connection # after the block returns with create_new_connection() as connection: yield connection This wrapper can be used instead for the above like this:: def connection_or_default_connection(connection=None): return FallbackContext(connection, create_new_connection) """def__init__(self,provided,fallback,*fb_args,**fb_kwargs):self.provided=providedself.fallback=fallbackself.fb_args=fb_argsself.fb_kwargs=fb_kwargsself._context=Nonedef__enter__(self):ifself.providedisnotNone:returnself.providedcontext=self._context=self.fallback(*self.fb_args,**self.fb_kwargs).__enter__()returncontextdef__exit__(self,*exc_info):ifself._contextisnotNone:returnself._context.__exit__(*exc_info)
[文档]classgetitem_property:"""Attribute -> dict key descriptor. The target object must support ``__getitem__``, and optionally ``__setitem__``. Example: >>> from collections import defaultdict >>> class Me(dict): ... deep = defaultdict(dict) ... ... foo = _getitem_property('foo') ... deep_thing = _getitem_property('deep.thing') >>> me = Me() >>> me.foo None >>> me.foo = 10 >>> me.foo 10 >>> me['foo'] 10 >>> me.deep_thing = 42 >>> me.deep_thing 42 >>> me.deep defaultdict(<type 'dict'>, {'thing': 42}) """def__init__(self,keypath,doc=None):path,_,self.key=keypath.rpartition('.')self.path=path.split('.')ifpathelseNoneself.__doc__=docdef_path(self,obj):return(reduce(lambdad,k:d[k],[obj]+self.path)ifself.pathelseobj)def__get__(self,obj,type=None):ifobjisNone:returntypereturnself._path(obj).get(self.key)def__set__(self,obj,value):self._path(obj)[self.key]=value