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
+ + history.
+
+ Only applies to object-based attributes.
+
+ This is an inlining of existing functionality
+ which roughly correponds to:
+
+ get_state_history(
+ state,
+ key,
+ passive=PASSIVE_NO_INITIALIZE).sum()
+
+ """
raise NotImplementedError()
def initialize(self, state, dict_):
if self.key in state.committed_state:
original = state.committed_state[self.key]
- if original not in (NEVER_SET, None) and \
+ if original not in (NEVER_SET, PASSIVE_NO_RESULT, None) and \
original is not current:
- return [current, original]
- return [current]
- else:
- return []
+ if current is not None:
+ return [
+ (instance_state(current), current),
+ (instance_state(original), original)
+ ]
+ else:
+ return [(instance_state(original), original)]
+
+ if current is not None:
+ return [(instance_state(current), current)]
+ return []
def set(self, state, dict_, value, initiator, passive=PASSIVE_OFF):
"""Set a value on the given InstanceState.
return History.from_collection(self, state, current)
def get_all_pending(self, state, dict_):
- # this is basically an inline
- # of self.get_history().sum()
-
if self.key not in dict_:
return []
else:
current = self.get_collection(state, dict_, current)
if self.key not in state.committed_state:
- return list(current)
+ return [(instance_state(o), o) for o in current]
original = state.committed_state[self.key]
if original is NO_VALUE:
- return list(current)
+ return [(instance_state(o), o) for o in current]
else:
- # TODO: use the dict() of state, obj here
- current_set = util.IdentitySet(current)
- original_set = util.IdentitySet(original)
-
- # ensure ordering is maintained
+ current_states = [(instance_state(c), c) for c in current]
+ original_states = [(instance_state(c), c) for c in original]
+
+ current_set = dict(current_states)
+ original_set = dict(original_states)
+
return \
- [x for x in current if x not in original_set] + \
- [x for x in current if x in original_set] + \
- [x for x in original if x not in current_set]
+ [(s, o) for s, o in current_states if s not in original_set] + \
+ [(s, o) for s, o in current_states if s in original_set] + \
+ [(s, o) for s, o in original_states if s not in current_set]
def fire_append_event(self, state, dict_, value, initiator):
for fn in self.dispatch.on_append:
def get_state_history(state, key, **kwargs):
return state.get_history(key, **kwargs)
-def get_all_pending(state, dict_, key):
- """Return a list of all objects currently in memory
- involving the given key on the given state.
-
- This should be equivalent to::
-
- get_state_history(
- state,
- key,
- passive=PASSIVE_NO_INITIALIZE).sum()
-
- TODO: we'd like to more closely merge the "history" tuple
- generation with "get_all_pending()", making the presence
- of the "History" object optional.
-
- """
-
- return state.manager[key].impl.get_all_pending(state, dict_)
-
def has_parent(cls, obj, key, optimistic=False):
"""TODO"""
passive = attributes.PASSIVE_OFF
if type_ == 'save-update':
- instances = state.manager[self.key].impl.get_all_pending(state, dict_)
+ tuples = state.manager[self.key].impl.\
+ get_all_pending(state, dict_)
else:
- instances = state.value_as_iterable(dict_, self.key,
- passive=passive)
+ tuples = state.value_as_iterable(dict_, self.key,
+ passive=passive)
+
skip_pending = type_ == 'refresh-expire' and 'delete-orphan' \
not in self.cascade
- if instances:
- for c in instances:
- if c is not None and \
- c is not attributes.PASSIVE_NO_RESULT:
-
- instance_state = attributes.instance_state(c)
- if instance_state in visited_states:
- continue
-
- instance_dict = attributes.instance_dict(c)
-
- if halt_on and halt_on(instance_state):
- continue
-
- if skip_pending and not instance_state.key:
- continue
-
- instance_mapper = instance_state.manager.mapper
-
- if not instance_mapper.isa(self.mapper.class_manager.mapper):
- raise AssertionError("Attribute '%s' on class '%s' "
- "doesn't handle objects "
- "of type '%s'" % (
- self.key,
- self.parent.class_,
- c.__class__
- ))
-
- visited_states.add(instance_state)
-
- # cascade using the mapper local to this
- # object, so that its individual properties are located
- yield c, instance_mapper, instance_state, instance_dict
+ for instance_state, c in tuples:
+ if instance_state in visited_states:
+ continue
+
+ instance_dict = attributes.instance_dict(c)
+
+ if halt_on and halt_on(instance_state):
+ continue
+
+ if skip_pending and not instance_state.key:
+ continue
+
+ instance_mapper = instance_state.manager.mapper
+
+ if not instance_mapper.isa(self.mapper.class_manager.mapper):
+ raise AssertionError("Attribute '%s' on class '%s' "
+ "doesn't handle objects "
+ "of type '%s'" % (
+ self.key,
+ self.parent.class_,
+ c.__class__
+ ))
+
+ visited_states.add(instance_state)
+
+ yield c, instance_mapper, instance_state, instance_dict
def _add_reverse_property(self, key):
return self.pending[key]
def value_as_iterable(self, dict_, key, passive=PASSIVE_OFF):
- """return an InstanceState attribute as a list,
- regardless of it being a scalar or collection-based
- attribute.
+ """Return a list of tuples (state, obj) for the given
+ key.
- returns None if passive is not PASSIVE_OFF and the getter returns
- PASSIVE_NO_RESULT.
+ returns an empty list if the value is None/empty/PASSIVE_NO_RESULT
"""
- impl = self.get_impl(key)
+ impl = self.manager[key].impl
x = impl.get(self, dict_, passive=passive)
- if x is PASSIVE_NO_RESULT:
- return None
+ if x is PASSIVE_NO_RESULT or x is None:
+ return []
elif hasattr(impl, 'get_collection'):
- return impl.get_collection(self, dict_, x, passive=passive)
+ return [
+ (attributes.instance_state(o), o) for o in
+ impl.get_collection(self, dict_, x, passive=passive)
+ ]
else:
- return [x]
+ return [(attributes.instance_state(x), x)]
def __getstate__(self):
d = {'instance':self.obj()}