def merge_trait_dict_events(a, b, dict_): ''' Merge TraitDictEvent 'b' into TraitDictEvent 'a'. ``dict_`` should be a mapping object that gives the current value of 'changed' items in the events. Assumes that 'b' is an event generated immediately after event 'a'. This entails, for example, that 'a.added.keys()' and 'b.added.keys()' are disjoint, since it doesn't make sense for the same name to be added twice in succession. ''' assert disjoint(set(a.added), set(a.changed), set(a.removed)) assert disjoint(set(b.added), set(b.changed), set(b.removed)) for k,v in b.added.iteritems(): if k in a.added: assert False elif k in a.changed: assert False elif k in a.removed: if v is a.removed[k]: del a.removed[k] else: a.changed[k] = a.removed.pop(k) else: a.added[k] = v for k,v in b.changed.iteritems(): if k in a.added: a.added[k] = dict_[k] elif k in a.changed: pass # 'a.added[k]' is already the correct value elif k in a.removed: assert False else: a.changed[k] = v for k,v in b.removed.iteritems(): if k in a.added: del a.added[k] elif k in a.changed: a.removed[k] = a.changed[k] del a.changed[k] elif k in a.removed: assert False else: a.removed[k] = v assert disjoint(set(a.added), set(a.changed), set(a.removed))
def merge_context_modified_events(a, b): ''' Merge ContextModified event 'b' into ContextModified event 'a'. Assumes that 'b' is an event generated immediately after event 'a'. This entails, for example, that 'a.added' and 'b.added' are disjoint, since it doesn't make sense for the same name to be added twice in succession. ''' assert disjoint(set(a.added), set(a.modified), set(a.removed)) assert disjoint(set(b.added), set(b.modified), set(b.removed)) for x in b.added: if x in a.added: assert False elif x in a.modified: assert False elif x in a.removed: a.removed.remove(x) a.added.append(x) else: a.added.append(x) for x in b.modified: if x in a.added: pass # 'a.added' is already correct elif x in a.modified: pass # 'a.modified' is already correct elif x in a.removed: assert False else: a.modified.append(x) for x in b.removed: if x in a.added: a.added.remove(x) elif x in a.modified: a.modified.remove(x) a.removed.append(x) elif x in a.removed: assert False else: a.removed.append(x) # 'changed' gives us little information, so this is the best we can do a.changed = list(set(a.changed + b.changed)) a.reset |= b.reset assert disjoint(set(a.added), set(a.modified), set(a.removed))
def _bind_conditional(self, names): # Conditional bindings shadow global names, but are precluded by # existing unconditional bindings self.conditional_locals |= set(names) - self.locals self.globals -= set(names) assert disjoint(self.globals, self.locals, self.conditional_locals)
def _bind(self, names): # Local bindings shadow global names and replace conditional locals self.locals |= set(names) self.globals -= set(names) self.conditional_locals -= set(names) assert disjoint(self.globals, self.locals, self.conditional_locals)
def _dict_is_modified( self, event ): """ Fires when 'context_data' fires 'dict_modified'. """ assert disjoint( event.added, event.changed, event.removed ) # How much can we optimize this while keeping it maintainable? # If the dictionary has changed, then we are dirty. (I think this is # the only place we have to do this...) self.dirty = True # These become attributes on a ContextModified event we create below. # Leave them as normal sets until the end to avoid excessive trait # validation. added = set() modified = set() removed = set() changed = set() # The following three loops duplicate code: the body of 'changed' is # simply the body of 'removed' followed by the body of 'added', # followed by some logic for group maintenance. It would be better to # abstract out the two shared chunks, but I keep waiting for a higher # level insight that will greatly simplify this logic and eliminate the # need to repeat code in the first place. for name, new in event.added.iteritems(): # Rebind renamed dynamic bindings if hashable( new ) and new in self._dynamic_bindings: self._rebind_dynamic( new, name ) # Add new data if self.is_valid_array( new ): added.update( self._add_context_items( ContextItem( name = name ) ) ) elif isinstance( new, ANumericContext ): added.update( self._add_sub_context( name, new ) ) changed.add( name ) else: changed.add( name ) for name, old in event.changed.iteritems(): new = self.context_data[ name ] # Short-circuit if possible if new is old: continue # Unbind dynamic bindings if hashable( old ) and old in self._dynamic_bindings: d = self._dynamic_bindings[ old ] self._unbind_dynamic( old, d['trait_name'], remove=False ) # Remove old data if name in self._context_items: removed.update( self._remove_context_items( self._context_items[ name ], delete = False ) ) elif isinstance( old, ANumericContext ): removed.update( self._remove_sub_context( name, old ) ) changed.add( name ) else: changed.add( name ) # Rebind dynamic bindings if hashable( new ) and new in self._dynamic_bindings: self._rebind_dynamic( new, name ) # Add new data if self.is_valid_array( new ): added.update( self._add_context_items( ContextItem( name = name ) ) ) elif isinstance( new, ANumericContext ): added.update( self._add_sub_context( name, new ) ) changed.add( name ) else: changed.add( name ) # Update an item's group if it changes shape if ( self.is_valid_array( new ) and self.is_valid_array( old ) and new.shape != old.shape ): context_groups = self._context_groups item = self._context_items[ name ] for group in item.groups: n, names = context_groups[ group ] names.remove( name ) if len( names ) == 0: del context_groups[ group ] group = new.shape item.groups = [ group ] context_groups.setdefault( group, ( group, [] ) )[1].append( name ) for name, old in event.removed.iteritems(): # Unbind dynamic bindings if hashable( old ) and old in self._dynamic_bindings: d = self._dynamic_bindings[ old ] self._unbind_dynamic( old, d['trait_name'], remove=False ) # Remove old data if name in self._context_items: removed.update( self._remove_context_items( self._context_items[ name ], delete = False ) ) elif isinstance( old, ANumericContext ): removed.update( self._remove_sub_context( name, old ) ) changed.add( name ) else: changed.add( name ) # Calculate context event modified |= added & removed added -= modified removed -= modified cm = ContextModified( added = list( added ), modified = list( modified ), removed = list( removed ), changed = list( changed ), ) # Fire events self.post_dict_modified( event ) self.post_context_modified( cm )