📄 collections.py
字号:
setattr(fn, '_sa_instrument_role', 'converter') return fn converter = classmethod(converter) def adds(cls, arg): """Mark the method as adding an entity to the collection. Adds "add to collection" handling to the method. The decorator argument indicates which method argument holds the SQLAlchemy-relevant value. Arguments can be specified positionally (i.e. integer) or by name:: @collection.adds(1) def push(self, item): ... @collection.adds('entity') def do_stuff(self, thing, entity=None): ... """ def decorator(fn): setattr(fn, '_sa_instrument_before', ('fire_append_event', arg)) return fn return decorator adds = classmethod(adds) def replaces(cls, arg): """Mark the method as replacing an entity in the collection. Adds "add to collection" and "remove from collection" handling to the method. The decorator argument indicates which method argument holds the SQLAlchemy-relevant value to be added, and return value, if any will be considered the value to remove. Arguments can be specified positionally (i.e. integer) or by name:: @collection.replaces(2) def __setitem__(self, index, item): ... """ def decorator(fn): setattr(fn, '_sa_instrument_before', ('fire_append_event', arg)) setattr(fn, '_sa_instrument_after', 'fire_remove_event') return fn return decorator replaces = classmethod(replaces) def removes(cls, arg): """Mark the method as removing an entity in the collection. Adds "remove from collection" handling to the method. The decorator argument indicates which method argument holds the SQLAlchemy-relevant value to be removed. Arguments can be specified positionally (i.e. integer) or by name:: @collection.removes(1) def zap(self, item): ... For methods where the value to remove is not known at call-time, use collection.removes_return. """ def decorator(fn): setattr(fn, '_sa_instrument_before', ('fire_remove_event', arg)) return fn return decorator removes = classmethod(removes) def removes_return(cls): """Mark the method as removing an entity in the collection. Adds "remove from collection" handling to the method. The return value of the method, if any, is considered the value to remove. The method arguments are not inspected:: @collection.removes_return() def pop(self): ... For methods where the value to remove is known at call-time, use collection.remove. """ def decorator(fn): setattr(fn, '_sa_instrument_after', 'fire_remove_event') return fn return decorator removes_return = classmethod(removes_return)# public instrumentation interface for 'internally instrumented'# implementationsdef collection_adapter(collection): """Fetch the CollectionAdapter for a collection.""" return getattr(collection, '_sa_adapter', None)def collection_iter(collection): """Iterate over an object supporting the @iterator or __iter__ protocols. If the collection is an ORM collection, it need not be attached to an object to be iterable. """ try: return getattr(collection, '_sa_iterator', getattr(collection, '__iter__'))() except AttributeError: raise TypeError("'%s' object is not iterable" % type(collection).__name__)class CollectionAdapter(object): """Bridges between the ORM and arbitrary Python collections. Proxies base-level collection operations (append, remove, iterate) to the underlying Python collection, and emits add/remove events for entities entering or leaving the collection. The ORM uses an CollectionAdapter exclusively for interaction with entity collections. """ def __init__(self, attr, owner_state, data): self.attr = attr self._data = weakref.ref(data) self.owner_state = owner_state self.link_to_self(data) data = property(lambda s: s._data(), doc="The entity collection being adapted.") def link_to_self(self, data): """Link a collection to this adapter, and fire a link event.""" setattr(data, '_sa_adapter', self) if hasattr(data, '_sa_on_link'): getattr(data, '_sa_on_link')(self) def unlink(self, data): """Unlink a collection from any adapter, and fire a link event.""" setattr(data, '_sa_adapter', None) if hasattr(data, '_sa_on_link'): getattr(data, '_sa_on_link')(None) def adapt_like_to_iterable(self, obj): """Converts collection-compatible objects to an iterable of values. Can be passed any type of object, and if the underlying collection determines that it can be adapted into a stream of values it can use, returns an iterable of values suitable for append()ing. This method may raise TypeError or any other suitable exception if adaptation fails. If a converter implementation is not supplied on the collection, a default duck-typing-based implementation is used. """ converter = getattr(self._data(), '_sa_converter', None) if converter is not None: return converter(obj) setting_type = sautil.duck_type_collection(obj) receiving_type = sautil.duck_type_collection(self._data()) if obj is None or setting_type != receiving_type: given = obj is None and 'None' or obj.__class__.__name__ if receiving_type is None: wanted = self._data().__class__.__name__ else: wanted = receiving_type.__name__ raise TypeError( "Incompatible collection type: %s is not %s-like" % ( given, wanted)) # If the object is an adapted collection, return the (iterable) adapter. if getattr(obj, '_sa_adapter', None) is not None: return getattr(obj, '_sa_adapter') elif setting_type == dict: return getattr(obj, 'itervalues', getattr(obj, 'values'))() else: return iter(obj) def append_with_event(self, item, initiator=None): """Add an entity to the collection, firing mutation events.""" getattr(self._data(), '_sa_appender')(item, _sa_initiator=initiator) def append_without_event(self, item): """Add or restore an entity to the collection, firing no events.""" getattr(self._data(), '_sa_appender')(item, _sa_initiator=False) def remove_with_event(self, item, initiator=None): """Remove an entity from the collection, firing mutation events.""" getattr(self._data(), '_sa_remover')(item, _sa_initiator=initiator) def remove_without_event(self, item): """Remove an entity from the collection, firing no events.""" getattr(self._data(), '_sa_remover')(item, _sa_initiator=False) def clear_with_event(self, initiator=None): """Empty the collection, firing a mutation event for each entity.""" for item in list(self): self.remove_with_event(item, initiator) def clear_without_event(self): """Empty the collection, firing no events.""" for item in list(self): self.remove_without_event(item) def __iter__(self): """Iterate over entities in the collection.""" return getattr(self._data(), '_sa_iterator')() def __len__(self): """Count entities in the collection.""" return len(list(getattr(self._data(), '_sa_iterator')())) def __nonzero__(self): return True def fire_append_event(self, item, initiator=None): """Notify that a entity has entered the collection. Initiator is the InstrumentedAttribute that initiated the membership mutation, and should be left as None unless you are passing along an initiator value from a chained operation. """ if initiator is not False and item is not None: self.attr.fire_append_event(self.owner_state, item, initiator) def fire_remove_event(self, item, initiator=None): """Notify that a entity has been removed from the collection. Initiator is the InstrumentedAttribute that initiated the membership mutation, and should be left as None unless you are passing along an initiator value from a chained operation. """ if initiator is not False and item is not None: self.attr.fire_remove_event(self.owner_state, item, initiator) def fire_pre_remove_event(self, initiator=None): """Notify that an entity is about to be removed from the collection. Only called if the entity cannot be removed after calling fire_remove_event(). """ self.attr.fire_pre_remove_event(self.owner_state, initiator=initiator) def __getstate__(self): return { 'key': self.attr.key, 'owner_state': self.owner_state, 'data': self.data } def __setstate__(self, d): self.attr = getattr(d['owner_state'].obj().__class__, d['key']).impl self.owner_state = d['owner_state'] self._data = weakref.ref(d['data'])__instrumentation_mutex = sautil.threading.Lock()def _prepare_instrumentation(factory): """Prepare a callable for future use as a collection class factory. Given a collection class factory (either a type or no-arg callable), return another factory that will produce compatible instances when called. This function is responsible for converting collection_class=list into the run-time behavior of collection_class=InstrumentedList. """ # Convert a builtin to 'Instrumented*' if factory in __canned_instrumentation: factory = __canned_instrumentation[factory] # Create a specimen cls = type(factory()) # Did factory callable return a builtin? if cls in __canned_instrumentation: # Wrap it so that it returns our 'Instrumented*' factory = __converting_factory(factory) cls = factory() # Instrument the class if needed. if __instrumentation_mutex.acquire(): try: if getattr(cls, '_sa_instrumented', None) != id(cls): _instrument_class(cls) finally: __instrumentation_mutex.release() return factorydef __converting_factory(original_factory): """Convert the type returned by collection factories on the fly. Given a collection factory that returns a builtin type (e.g. a list), return a wrapped function that converts that type to one of our instrumented types. """ def wrapper(): collection = original_factory() type_ = type(collection) if type_ in __canned_instrumentation: # return an instrumented type initialized from the factory's # collection return __canned_instrumentation[type_](collection) else: raise exceptions.InvalidRequestError( "Collection class factories must produce instances of a " "single class.") try: # often flawed but better than nothing wrapper.__name__ = "%sWrapper" % original_factory.__name__ wrapper.__doc__ = original_factory.__doc__ except: pass return wrapperdef _instrument_class(cls): """Modify methods in a class and install instrumentation.""" # FIXME: more formally document this as a decoratorless/Python 2.3 # option for specifying instrumentation. (likely doc'd here in code only, # not in online docs.) # # __instrumentation__ = { # 'rolename': 'methodname', # ... # 'methods': { # 'methodname': ('fire_{append,remove}_event', argspec, # 'fire_{append,remove}_event'),
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -