diff options
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r-- | lib/sqlalchemy/orm/attributes.py | 109 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/dynamic.py | 10 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/query.py | 6 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/state.py | 4 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/strategies.py | 24 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/unitofwork.py | 5 | ||||
-rw-r--r-- | lib/sqlalchemy/util/langhelpers.py | 20 |
7 files changed, 98 insertions, 80 deletions
diff --git a/lib/sqlalchemy/orm/attributes.py b/lib/sqlalchemy/orm/attributes.py index 3b4f18b31..ec0b84a60 100644 --- a/lib/sqlalchemy/orm/attributes.py +++ b/lib/sqlalchemy/orm/attributes.py @@ -22,60 +22,77 @@ from sqlalchemy.orm import interfaces, collections, events, exc as orm_exc mapperutil = util.importlater("sqlalchemy.orm", "util") -PASSIVE_NO_RESULT = util.symbol('PASSIVE_NO_RESULT') -ATTR_WAS_SET = util.symbol('ATTR_WAS_SET') -ATTR_EMPTY = util.symbol('ATTR_EMPTY') -NO_VALUE = util.symbol('NO_VALUE') -NEVER_SET = util.symbol('NEVER_SET') - -PASSIVE_RETURN_NEVER_SET = util.symbol('PASSIVE_RETURN_NEVER_SET', -"""Symbol indicating that loader callables can be -fired off, but if no callable is applicable and no value is -present, the attribute should remain non-initialized. -NEVER_SET is returned in this case. +PASSIVE_NO_RESULT = util.symbol('PASSIVE_NO_RESULT', +"""Symbol returned by a loader callable or other attribute/history +retrieval operation when a value could not be determined, based +on loader callable flags. +""" +) + +ATTR_WAS_SET = util.symbol('ATTR_WAS_SET', +"""Symbol returned by a loader callable to indicate the +retrieved value, or values, were assigned to their attributes +on the target object. """) -PASSIVE_NO_INITIALIZE = util.symbol('PASSIVE_NO_INITIALIZE', -"""Symbol indicating that loader callables should - not be fired off, and a non-initialized attribute - should remain that way. +ATTR_EMPTY = util.symbol('ATTR_EMPTY', +"""Symbol used internally to indicate an attribute had no callable. """) -PASSIVE_NO_FETCH = util.symbol('PASSIVE_NO_FETCH', -"""Symbol indicating that loader callables should not emit SQL, - but a value can be fetched from the current session. - - Non-initialized attributes should be initialized to an empty value. +NO_VALUE = util.symbol('NO_VALUE', +"""Symbol which may be placed as the 'previous' value of an attribute, +indicating no value was loaded for an attribute when it was modified, +and flags indicated we were not to load it. +""" +) -""") +NEVER_SET = util.symbol('NEVER_SET', +"""Symbol which may be placed as the 'previous' value of an attribute +indicating that the attribute had not been assigned to previously. +""" +) -PASSIVE_NO_FETCH_RELATED = util.symbol('PASSIVE_NO_FETCH_RELATED', -"""Symbol indicating that loader callables should not emit SQL for - loading a related object, but can refresh the attributes of the local - instance in order to locate a related object in the current session. - - Non-initialized attributes should be initialized to an empty value. - - The unit of work uses this mode to check if history is present - on many-to-one attributes with minimal SQL emitted. +CALLABLES_OK = util.symbol("CALLABLES_OK", +"""Loader callables can be fired off if a value +is not present.""", canonical=1 +) -""") +SQL_OK = util.symbol("SQL_OK", +"""Loader callables can emit SQL at least on scalar value +attributes.""", canonical=2) -PASSIVE_ONLY_PERSISTENT = util.symbol('PASSIVE_ONLY_PERSISTENT', -"""Symbol indicating that loader callables should only fire off for - parent objects which are persistent (i.e., have a database - identity). +RELATED_OBJECT_OK = util.symbol("RELATED_OBJECT_OK", +"""callables can use SQL to load related objects as well +as scalar value attributes. +""", canonical=4 +) - Load operations for the "previous" value of an attribute make - use of this flag during change events. +INIT_OK = util.symbol("INIT_OK", +"""Attributes should be initialized with a blank +value (None or an empty collection) upon get, if no other +value can be obtained. +""", canonical=8 +) -""") +NON_PERSISTENT_OK = util.symbol("NON_PERSISTENT_OK", +"""callables can be emitted if the parent is not persistent.""", +canonical=16 +) -PASSIVE_OFF = util.symbol('PASSIVE_OFF', -"""Symbol indicating that loader callables should be executed - normally. -""") +# 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 + class QueryableAttribute(interfaces.PropComparator): @@ -443,7 +460,7 @@ class AttributeImpl(object): key = self.key if key not in state.committed_state or \ state.committed_state[key] is NEVER_SET: - if passive is PASSIVE_NO_INITIALIZE: + if not passive & CALLABLES_OK: return PASSIVE_NO_RESULT if key in state.callables: @@ -468,7 +485,7 @@ class AttributeImpl(object): elif value is not ATTR_EMPTY: return self.set_committed_value(state, dict_, value) - if passive is PASSIVE_RETURN_NEVER_SET: + if not passive & INIT_OK: return NEVER_SET else: # Return a new, empty value @@ -639,8 +656,8 @@ class ScalarObjectAttributeImpl(ScalarAttributeImpl): if self.key in dict_: return History.from_object_attribute(self, state, dict_[self.key]) else: - if passive is PASSIVE_OFF: - passive = PASSIVE_RETURN_NEVER_SET + if passive & INIT_OK: + passive ^= INIT_OK current = self.get(state, dict_, passive=passive) if current is PASSIVE_NO_RESULT: return HISTORY_BLANK diff --git a/lib/sqlalchemy/orm/dynamic.py b/lib/sqlalchemy/orm/dynamic.py index edf052870..1a3e52a36 100644 --- a/lib/sqlalchemy/orm/dynamic.py +++ b/lib/sqlalchemy/orm/dynamic.py @@ -57,7 +57,7 @@ class DynamicAttributeImpl(attributes.AttributeImpl): self.query_class = mixin_user_query(query_class) def get(self, state, dict_, passive=attributes.PASSIVE_OFF): - if passive is not attributes.PASSIVE_OFF: + if not passive & attributes.SQL_OK: return self._get_collection_history(state, attributes.PASSIVE_NO_INITIALIZE).added_items else: @@ -65,7 +65,7 @@ class DynamicAttributeImpl(attributes.AttributeImpl): def get_collection(self, state, dict_, user_data=None, passive=attributes.PASSIVE_NO_INITIALIZE): - if passive is not attributes.PASSIVE_OFF: + if not passive & attributes.SQL_OK: return self._get_collection_history(state, passive).added_items else: @@ -142,7 +142,7 @@ class DynamicAttributeImpl(attributes.AttributeImpl): c.deleted_items) def get_all_pending(self, state, dict_): - c = self._get_collection_history(state, True) + c = self._get_collection_history(state, attributes.PASSIVE_NO_INITIALIZE) return [ (attributes.instance_state(x), x) for x in @@ -155,7 +155,9 @@ class DynamicAttributeImpl(attributes.AttributeImpl): else: c = CollectionHistory(self, state) - if passive is attributes.PASSIVE_OFF: + # TODO: consider using a different flag here, possibly + # one local to dynamic + if passive & attributes.INIT_OK: return CollectionHistory(self, state, apply_to=c) else: return c diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index 66d7f6eb4..924679085 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -764,7 +764,7 @@ class Query(object): not mapper.always_refresh and \ self._lockmode is None: - instance = self._get_from_identity(self.session, key, False) + instance = self._get_from_identity(self.session, key, attributes.PASSIVE_OFF) if instance is not None: # reject calls for id in identity map but class # mismatch. @@ -2426,10 +2426,10 @@ class Query(object): # expired - ensure it still exists if state.expired: - if passive is attributes.PASSIVE_NO_FETCH: + if not passive & attributes.SQL_OK: # TODO: no coverage here return attributes.PASSIVE_NO_RESULT - elif passive is attributes.PASSIVE_NO_FETCH_RELATED: + elif not passive & attributes.RELATED_OBJECT_OK: # this mode is used within a flush and the instance's # expired state will be checked soon enough, if necessary return instance diff --git a/lib/sqlalchemy/orm/state.py b/lib/sqlalchemy/orm/state.py index 4803ecdc3..30a08faba 100644 --- a/lib/sqlalchemy/orm/state.py +++ b/lib/sqlalchemy/orm/state.py @@ -18,7 +18,7 @@ from sqlalchemy import util from sqlalchemy.orm import exc as orm_exc, attributes, interfaces,\ util as orm_util from sqlalchemy.orm.attributes import PASSIVE_OFF, PASSIVE_NO_RESULT, \ - PASSIVE_NO_FETCH, NEVER_SET, ATTR_WAS_SET, NO_VALUE + SQL_OK, NEVER_SET, ATTR_WAS_SET, NO_VALUE mapperlib = util.importlater("sqlalchemy.orm", "mapperlib") @@ -276,7 +276,7 @@ class InstanceState(object): """ - if passive is PASSIVE_NO_FETCH: + if not passive & SQL_OK: return PASSIVE_NO_RESULT toload = self.expired_attributes.\ diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py index 2e09ccbbe..703fc0160 100644 --- a/lib/sqlalchemy/orm/strategies.py +++ b/lib/sqlalchemy/orm/strategies.py @@ -213,7 +213,7 @@ class DeferredColumnLoader(LoaderStrategy): if not state.key: return attributes.ATTR_EMPTY - if passive is attributes.PASSIVE_NO_FETCH: + if not passive & attributes.SQL_OK: return attributes.PASSIVE_NO_RESULT localparent = state.manager.mapper @@ -464,13 +464,10 @@ class LazyLoader(AbstractRelationshipLoader): ident_key = None if ( - (passive is attributes.PASSIVE_NO_FETCH or \ - passive is attributes.PASSIVE_NO_FETCH_RELATED) and - not self.use_get - ) or ( - passive is attributes.PASSIVE_ONLY_PERSISTENT and - pending - ): + (not passive & attributes.SQL_OK and not self.use_get) + or + (not passive & attributes.NON_PERSISTENT_OK and pending) + ): return attributes.PASSIVE_NO_RESULT session = sessionlib._state_session(state) @@ -501,8 +498,8 @@ class LazyLoader(AbstractRelationshipLoader): instance = Query._get_from_identity(session, ident_key, passive) if instance is not None: return instance - elif passive is attributes.PASSIVE_NO_FETCH or \ - passive is attributes.PASSIVE_NO_FETCH_RELATED: + elif not passive & attributes.SQL_OK or \ + not passive & attributes.RELATED_OBJECT_OK: return attributes.PASSIVE_NO_RESULT return self._emit_lazyload(session, state, ident_key) @@ -517,17 +514,12 @@ class LazyLoader(AbstractRelationshipLoader): dict_ = state.dict - if passive is attributes.PASSIVE_NO_FETCH_RELATED: - attr_passive = attributes.PASSIVE_OFF - else: - attr_passive = passive - return [ get_attr( state, dict_, self._equated_columns[pk], - passive=attr_passive) + passive=passive) for pk in self.mapper.primary_key ] diff --git a/lib/sqlalchemy/orm/unitofwork.py b/lib/sqlalchemy/orm/unitofwork.py index 8fc5f139d..3523e7d06 100644 --- a/lib/sqlalchemy/orm/unitofwork.py +++ b/lib/sqlalchemy/orm/unitofwork.py @@ -166,8 +166,9 @@ class UOWTransaction(object): history, state_history, cached_passive = self.attributes[hashkey] # if the cached lookup was "passive" and now # we want non-passive, do a non-passive lookup and re-cache - if cached_passive is not attributes.PASSIVE_OFF \ - and passive is attributes.PASSIVE_OFF: + + if not cached_passive & attributes.SQL_OK \ + and passive & attributes.SQL_OK: impl = state.manager[key].impl history = impl.get_history(state, state.dict, attributes.PASSIVE_OFF) diff --git a/lib/sqlalchemy/util/langhelpers.py b/lib/sqlalchemy/util/langhelpers.py index b6c89b11a..d266c9664 100644 --- a/lib/sqlalchemy/util/langhelpers.py +++ b/lib/sqlalchemy/util/langhelpers.py @@ -783,15 +783,21 @@ class classproperty(property): return desc.fget(cls) -class _symbol(object): - def __init__(self, name, doc=None): +class _symbol(int): + def __new__(self, name, doc=None, canonical=None): """Construct a new named symbol.""" assert isinstance(name, str) - self.name = name + if canonical is None: + canonical = hash(name) + v = int.__new__(_symbol, canonical) + v.name = name if doc: - self.__doc__ = doc + v.__doc__ = doc + return v + def __reduce__(self): - return symbol, (self.name,) + return symbol, (self.name, "x", int(self)) + def __repr__(self): return "<symbol '%s>" % self.name @@ -822,12 +828,12 @@ class symbol(object): symbols = {} _lock = threading.Lock() - def __new__(cls, name, doc=None): + def __new__(cls, name, doc=None, canonical=None): cls._lock.acquire() try: sym = cls.symbols.get(name) if sym is None: - cls.symbols[name] = sym = _symbol(name, doc) + cls.symbols[name] = sym = _symbol(name, doc, canonical) return sym finally: symbol._lock.release() |