summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r--lib/sqlalchemy/orm/attributes.py109
-rw-r--r--lib/sqlalchemy/orm/dynamic.py10
-rw-r--r--lib/sqlalchemy/orm/query.py6
-rw-r--r--lib/sqlalchemy/orm/state.py4
-rw-r--r--lib/sqlalchemy/orm/strategies.py24
-rw-r--r--lib/sqlalchemy/orm/unitofwork.py5
-rw-r--r--lib/sqlalchemy/util/langhelpers.py20
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()