diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2012-07-18 14:13:18 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2012-07-18 14:13:18 -0400 |
commit | dff7c2ad2c913ed0ec5979ff9470dd5dd5813483 (patch) | |
tree | 6f1c6483f8bac9ac68820984ae9a3f8137e418b0 /lib/sqlalchemy/orm/attributes.py | |
parent | 643a9afa86328ca6038d00543700dbe0f51af5e6 (diff) | |
download | sqlalchemy-dff7c2ad2c913ed0ec5979ff9470dd5dd5813483.tar.gz |
- document the inspection system
Diffstat (limited to 'lib/sqlalchemy/orm/attributes.py')
-rw-r--r-- | lib/sqlalchemy/orm/attributes.py | 202 |
1 files changed, 108 insertions, 94 deletions
diff --git a/lib/sqlalchemy/orm/attributes.py b/lib/sqlalchemy/orm/attributes.py index 0bf9ea438..9a1c60aa7 100644 --- a/lib/sqlalchemy/orm/attributes.py +++ b/lib/sqlalchemy/orm/attributes.py @@ -52,7 +52,7 @@ indicating that the attribute had not been assigned to previously. """ ) -NO_CHANGE = util.symbol("NO_CHANGE", +NO_CHANGE = util.symbol("NO_CHANGE", """No callables or SQL should be emitted on attribute access and no state should change""", canonical=0 ) @@ -80,29 +80,42 @@ value can be obtained. ) NON_PERSISTENT_OK = util.symbol("NON_PERSISTENT_OK", -"""callables can be emitted if the parent is not persistent.""", +"""callables can be emitted if the parent is not persistent.""", canonical=16 ) - # pre-packaged sets of flags used as inputs -PASSIVE_OFF = RELATED_OBJECT_OK | \ - NON_PERSISTENT_OK | \ - INIT_OK | \ - CALLABLES_OK | \ - SQL_OK - -PASSIVE_RETURN_NEVER_SET = PASSIVE_OFF ^ INIT_OK -PASSIVE_NO_INITIALIZE = PASSIVE_RETURN_NEVER_SET ^ CALLABLES_OK -PASSIVE_NO_FETCH = PASSIVE_OFF ^ SQL_OK -PASSIVE_NO_FETCH_RELATED = PASSIVE_OFF ^ RELATED_OBJECT_OK -PASSIVE_ONLY_PERSISTENT = PASSIVE_OFF ^ NON_PERSISTENT_OK +PASSIVE_OFF = util.symbol("PASSIVE_OFF", + "Callables can be emitted in all cases.", + canonical=(RELATED_OBJECT_OK | NON_PERSISTENT_OK | + INIT_OK | CALLABLES_OK | SQL_OK) +) +PASSIVE_RETURN_NEVER_SET = util.symbol("PASSIVE_RETURN_NEVER_SET", + """PASSIVE_OFF ^ INIT_OK""", + canonical=PASSIVE_OFF ^ INIT_OK +) +PASSIVE_NO_INITIALIZE = util.symbol("PASSIVE_NO_INITIALIZE", + "PASSIVE_RETURN_NEVER_SET ^ CALLABLES_OK", + canonical=PASSIVE_RETURN_NEVER_SET ^ CALLABLES_OK +) +PASSIVE_NO_FETCH = util.symbol("PASSIVE_NO_FETCH", + "PASSIVE_OFF ^ SQL_OK", + canonical=PASSIVE_OFF ^ SQL_OK +) +PASSIVE_NO_FETCH_RELATED = util.symbol("PASSIVE_NO_FETCH_RELATED", + "PASSIVE_OFF ^ RELATED_OBJECT_OK", + canonical=PASSIVE_OFF ^ RELATED_OBJECT_OK +) +PASSIVE_ONLY_PERSISTENT = util.symbol("PASSIVE_ONLY_PERSISTENT", + "PASSIVE_OFF ^ NON_PERSISTENT_OK", + canonical=PASSIVE_OFF ^ NON_PERSISTENT_OK +) class QueryableAttribute(interfaces.PropComparator): """Base class for class-bound attributes. """ - def __init__(self, class_, key, impl=None, + def __init__(self, class_, key, impl=None, comparator=None, parententity=None, of_type=None): self.class_ = class_ @@ -115,7 +128,7 @@ class QueryableAttribute(interfaces.PropComparator): manager = manager_of_class(class_) # manager is None in the case of AliasedClass if manager: - # propagate existing event listeners from + # propagate existing event listeners from # immediate superclass for base in manager._bases: if key in base: @@ -166,8 +179,8 @@ class QueryableAttribute(interfaces.PropComparator): except AttributeError: raise AttributeError( 'Neither %r object nor %r object has an attribute %r' % ( - type(self).__name__, - type(self.comparator).__name__, + type(self).__name__, + type(self.comparator).__name__, key) ) @@ -187,7 +200,7 @@ class InstrumentedAttribute(QueryableAttribute): """Class bound instrumented attribute which adds descriptor methods.""" def __set__(self, instance, value): - self.impl.set(instance_state(instance), + self.impl.set(instance_state(instance), instance_dict(instance), value, None) def __delete__(self, instance): @@ -215,13 +228,13 @@ def create_proxied_attribute(descriptor): class Proxy(QueryableAttribute): """Presents the :class:`.QueryableAttribute` interface as a - proxy on top of a Python descriptor / :class:`.PropComparator` + proxy on top of a Python descriptor / :class:`.PropComparator` combination. """ - def __init__(self, class_, key, descriptor, - comparator, + def __init__(self, class_, key, descriptor, + comparator, adapter=None, doc=None, original_property=None): self.class_ = class_ @@ -272,8 +285,8 @@ def create_proxied_attribute(descriptor): except AttributeError: raise AttributeError( 'Neither %r object nor %r object has an attribute %r' % ( - type(descriptor).__name__, - type(self.comparator).__name__, + type(descriptor).__name__, + type(self.comparator).__name__, attribute) ) @@ -289,7 +302,7 @@ class AttributeImpl(object): def __init__(self, class_, key, callable_, dispatch, trackparent=False, extension=None, - compare_function=None, active_history=False, + compare_function=None, active_history=False, parent_token=None, expire_missing=True, **kwargs): """Construct an AttributeImpl. @@ -326,12 +339,12 @@ class AttributeImpl(object): parent_token Usually references the MapperProperty, used as a key for the hasparent() function to identify an "owning" attribute. - Allows multiple AttributeImpls to all match a single + Allows multiple AttributeImpls to all match a single owner attribute. expire_missing if False, don't add an "expiry" callable to this attribute - during state.expire_attributes(None), if no value is present + during state.expire_attributes(None), if no value is present for this key. """ @@ -370,7 +383,7 @@ class AttributeImpl(object): def hasparent(self, state, optimistic=False): - """Return the boolean value of a `hasparent` flag attached to + """Return the boolean value of a `hasparent` flag attached to the given state. The `optimistic` flag determines what the default return value @@ -414,8 +427,8 @@ class AttributeImpl(object): "state %s along attribute '%s', " "but the parent record " "has gone stale, can't be sure this " - "is the most recent parent." % - (orm_util.state_str(state), + "is the most recent parent." % + (orm_util.state_str(state), orm_util.state_str(parent_state), self.key)) @@ -445,8 +458,8 @@ class AttributeImpl(object): raise NotImplementedError() def get_all_pending(self, state, dict_): - """Return a list of tuples of (state, obj) - for all objects in this attribute's current state + """Return a list of tuples of (state, obj) + for all objects in this attribute's current state + history. Only applies to object-based attributes. @@ -455,8 +468,8 @@ class AttributeImpl(object): which roughly corresponds to: get_state_history( - state, - key, + state, + key, passive=PASSIVE_NO_INITIALIZE).sum() """ @@ -517,14 +530,14 @@ class AttributeImpl(object): self.set(state, dict_, value, initiator, passive=passive) def remove(self, state, dict_, value, initiator, passive=PASSIVE_OFF): - self.set(state, dict_, None, initiator, + self.set(state, dict_, None, initiator, passive=passive, check_old=value) def pop(self, state, dict_, value, initiator, passive=PASSIVE_OFF): - self.set(state, dict_, None, initiator, + self.set(state, dict_, None, initiator, passive=passive, check_old=value, pop=True) - def set(self, state, dict_, value, initiator, + def set(self, state, dict_, value, initiator, passive=PASSIVE_OFF, check_old=None, pop=False): raise NotImplementedError() @@ -571,7 +584,7 @@ class ScalarAttributeImpl(AttributeImpl): return History.from_scalar_attribute( self, state, dict_.get(self.key, NO_VALUE)) - def set(self, state, dict_, value, initiator, + def set(self, state, dict_, value, initiator, passive=PASSIVE_OFF, check_old=None, pop=False): if initiator and initiator.parent_token is self.parent_token: return @@ -582,7 +595,7 @@ class ScalarAttributeImpl(AttributeImpl): old = dict_.get(self.key, NO_VALUE) if self.dispatch.set: - value = self.fire_replace_event(state, dict_, + value = self.fire_replace_event(state, dict_, value, old, initiator) state._modified_event(dict_, self, old) dict_[self.key] = value @@ -604,7 +617,7 @@ class ScalarAttributeImpl(AttributeImpl): class ScalarObjectAttributeImpl(ScalarAttributeImpl): - """represents a scalar-holding InstrumentedAttribute, + """represents a scalar-holding InstrumentedAttribute, where the target object is also instrumented. Adds events to delete/set operations. @@ -650,7 +663,7 @@ class ScalarObjectAttributeImpl(ScalarAttributeImpl): else: return [] - def set(self, state, dict_, value, initiator, + def set(self, state, dict_, value, initiator, passive=PASSIVE_OFF, check_old=None, pop=False): """Set a value on the given InstanceState. @@ -729,12 +742,12 @@ class CollectionAttributeImpl(AttributeImpl): typecallable=None, trackparent=False, extension=None, copy_function=None, compare_function=None, **kwargs): super(CollectionAttributeImpl, self).__init__( - class_, - key, + class_, + key, callable_, dispatch, trackparent=trackparent, extension=extension, - compare_function=compare_function, + compare_function=compare_function, **kwargs) if copy_function is None: @@ -762,11 +775,11 @@ class CollectionAttributeImpl(AttributeImpl): if self.key in state.committed_state: original = state.committed_state[self.key] if original is not NO_VALUE: - current_states = [((c is not None) and - instance_state(c) or None, c) + current_states = [((c is not None) and + instance_state(c) or None, c) for c in current] - original_states = [((c is not None) and - instance_state(c) or None, c) + original_states = [((c is not None) and + instance_state(c) or None, c) for c in original] current_set = dict(current_states) @@ -853,13 +866,13 @@ class CollectionAttributeImpl(AttributeImpl): def pop(self, state, dict_, value, initiator, passive=PASSIVE_OFF): try: # TODO: better solution here would be to add - # a "popper" role to collections.py to complement + # a "popper" role to collections.py to complement # "remover". self.remove(state, dict_, value, initiator, passive=passive) except (ValueError, KeyError, IndexError): pass - def set(self, state, dict_, value, initiator, + def set(self, state, dict_, value, initiator, passive=PASSIVE_OFF, pop=False): """Set a value on the given object. @@ -938,7 +951,7 @@ class CollectionAttributeImpl(AttributeImpl): return user_data - def get_collection(self, state, dict_, + def get_collection(self, state, dict_, user_data=None, passive=PASSIVE_OFF): """Retrieve the CollectionAdapter associated with the given state. @@ -967,19 +980,19 @@ def backref_listeners(attribute, key, uselist): old_state, old_dict = instance_state(oldchild),\ instance_dict(oldchild) impl = old_state.manager[key].impl - impl.pop(old_state, - old_dict, - state.obj(), + impl.pop(old_state, + old_dict, + state.obj(), initiator, passive=PASSIVE_NO_FETCH) if child is not None: child_state, child_dict = instance_state(child),\ instance_dict(child) child_state.manager[key].impl.append( - child_state, - child_dict, - state.obj(), - initiator, + child_state, + child_dict, + state.obj(), + initiator, passive=PASSIVE_NO_FETCH) return child @@ -987,10 +1000,10 @@ def backref_listeners(attribute, key, uselist): child_state, child_dict = instance_state(child), \ instance_dict(child) child_state.manager[key].impl.append( - child_state, - child_dict, - state.obj(), - initiator, + child_state, + child_dict, + state.obj(), + initiator, passive=PASSIVE_NO_FETCH) return child @@ -999,29 +1012,29 @@ def backref_listeners(attribute, key, uselist): child_state, child_dict = instance_state(child),\ instance_dict(child) child_state.manager[key].impl.pop( - child_state, - child_dict, - state.obj(), + child_state, + child_dict, + state.obj(), initiator, passive=PASSIVE_NO_FETCH) if uselist: - event.listen(attribute, "append", - emit_backref_from_collection_append_event, + event.listen(attribute, "append", + emit_backref_from_collection_append_event, retval=True, raw=True) else: - event.listen(attribute, "set", - emit_backref_from_scalar_set_event, + event.listen(attribute, "set", + emit_backref_from_scalar_set_event, retval=True, raw=True) # TODO: need coverage in test/orm/ of remove event - event.listen(attribute, "remove", - emit_backref_from_collection_remove_event, + event.listen(attribute, "remove", + emit_backref_from_collection_remove_event, retval=True, raw=True) _NO_HISTORY = util.symbol('NO_HISTORY') _NO_STATE_SYMBOLS = frozenset([ - id(PASSIVE_NO_RESULT), - id(NO_VALUE), + id(PASSIVE_NO_RESULT), + id(NO_VALUE), id(NEVER_SET)]) class History(tuple): """A 3-tuple of added, unchanged and deleted values, @@ -1062,7 +1075,7 @@ class History(tuple): return not bool( (self.added or self.deleted) or self.unchanged and self.unchanged != [None] - ) + ) def sum(self): """Return a collection of added + unchanged + deleted.""" @@ -1114,7 +1127,7 @@ class History(tuple): elif attribute.is_equal(current, original) is True: return cls((), [current], ()) else: - # current convention on native scalars is to not + # current convention on native scalars is to not # include information # about missing previous value in "deleted", but # we do include None, which helps in some primary @@ -1140,11 +1153,11 @@ class History(tuple): elif current is original: return cls((), [current], ()) else: - # current convention on related objects is to not + # current convention on related objects is to not # include information # about missing previous value in "deleted", and # to also not include None - the dependency.py rules - # ignore the None in any case. + # ignore the None in any case. if id(original) in _NO_STATE_SYMBOLS or original is None: deleted = () else: @@ -1165,11 +1178,11 @@ class History(tuple): return cls((), list(current), ()) else: - current_states = [((c is not None) and instance_state(c) or None, c) - for c in current + current_states = [((c is not None) and instance_state(c) or None, c) + for c in current ] - original_states = [((c is not None) and instance_state(c) or None, c) - for c in original + original_states = [((c is not None) and instance_state(c) or None, c) + for c in original ] current_set = dict(current_states) @@ -1184,7 +1197,7 @@ class History(tuple): HISTORY_BLANK = History(None, None, None) def get_history(obj, key, passive=PASSIVE_OFF): - """Return a :class:`.History` record for the given object + """Return a :class:`.History` record for the given object and attribute key. :param obj: an object whose class is instrumented by the @@ -1192,10 +1205,11 @@ def get_history(obj, key, passive=PASSIVE_OFF): :param key: string attribute name. - :param passive: indicates if the attribute should be - loaded from the database if not already present (:attr:`.PASSIVE_NO_FETCH`), and - if the attribute should be not initialized to a blank value otherwise - (:attr:`.PASSIVE_NO_INITIALIZE`). Default is :attr:`PASSIVE_OFF`. + :param passive: indicates loading behavior for the attribute + if the value is not already present. This is a + bitflag attribute, which defaults to the symbol + :attr:`.PASSIVE_OFF` indicating all necessary SQL + should be emitted. """ if passive is True: @@ -1223,14 +1237,14 @@ def register_attribute(class_, key, **kw): comparator = kw.pop('comparator', None) parententity = kw.pop('parententity', None) doc = kw.pop('doc', None) - desc = register_descriptor(class_, key, + desc = register_descriptor(class_, key, comparator, parententity, doc=doc) register_attribute_impl(class_, key, **kw) return desc def register_attribute_impl(class_, key, - uselist=False, callable_=None, - useobject=False, + uselist=False, callable_=None, + useobject=False, impl_class=None, backref=None, **kw): manager = manager_of_class(class_) @@ -1262,7 +1276,7 @@ def register_attribute_impl(class_, key, manager.post_configure_attribute(key) return manager[key] -def register_descriptor(class_, key, comparator=None, +def register_descriptor(class_, key, comparator=None, parententity=None, doc=None): manager = manager_of_class(class_) @@ -1288,10 +1302,10 @@ def init_collection(obj, key): collection_adapter.append_without_event(elem) For an easier way to do the above, see - :func:`~sqlalchemy.orm.attributes.set_committed_value`. + :func:`~sqlalchemy.orm.attributes.set_committed_value`. obj is an instrumented object instance. An InstanceState - is accepted directly for backwards compatibility but + is accepted directly for backwards compatibility but this usage is deprecated. """ @@ -1309,7 +1323,7 @@ def init_state_collection(state, dict_, key): def set_committed_value(instance, key, value): """Set the value of an attribute with no history events. - Cancels any previous history present. The value should be + Cancels any previous history present. The value should be a scalar value for scalar-holding attributes, or an iterable for any collection-holding attribute. @@ -1366,7 +1380,7 @@ def del_attribute(instance, key): def flag_modified(instance, key): """Mark an attribute on an instance as 'modified'. - This sets the 'modified' flag on the instance and + This sets the 'modified' flag on the instance and establishes an unconditional change event for the given attribute. """ |