[Sqlalchemy-commits] [1518] sqlalchemy/trunk/lib/sqlalchemy/orm: unitofwork more Set oriented now
Brought to you by:
zzzeek
From: <co...@sq...> - 2006-05-27 05:45:30
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head><style type="text/css"><!-- #msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; } #msg dt { float: left; width: 6em; font-weight: bold; } #msg dt:after { content:':';} #msg dl, #msg dt, #msg ul, #msg li { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; } #msg dl a { font-weight: bold} #msg dl a:link { color:#fc3; } #msg dl a:active { color:#ff0; } #msg dl a:visited { color:#cc6; } h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; } #msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; } #msg ul, pre { overflow: auto; } #patch { width: 100%; } #patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;} #patch .propset h4, #patch .binary h4 {margin:0;} #patch pre {padding:0;line-height:1.2em;margin:0;} #patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;} #patch .propset .diff, #patch .binary .diff {padding:10px 0;} #patch span {display:block;padding:0 10px;} #patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;} #patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;} #patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;} #patch .lines, .info {color:#888;background:#fff;} --></style> <title>[1518] sqlalchemy/trunk/lib/sqlalchemy/orm: unitofwork more Set oriented now</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1518</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-27 00:45:21 -0500 (Sat, 27 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>unitofwork more Set oriented now MapperProperty now has "localparent" and "parent" attributes, which in the case of inheritance represent the mapper the property is attached to, and the original mapper it was created on. the unitofwork now keeps the dependency processors derived from those properties unique so inheritance structures dont register redundant dependency processors.</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemyormmapperpy">sqlalchemy/trunk/lib/sqlalchemy/orm/mapper.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemyormpropertiespy">sqlalchemy/trunk/lib/sqlalchemy/orm/properties.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemyormunitofworkpy">sqlalchemy/trunk/lib/sqlalchemy/orm/unitofwork.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemyormmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/orm/mapper.py (1517 => 1518)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/orm/mapper.py 2006-05-27 01:19:56 UTC (rev 1517) +++ sqlalchemy/trunk/lib/sqlalchemy/orm/mapper.py 2006-05-27 05:45:21 UTC (rev 1518) </span><span class="lines">@@ -932,6 +932,7 @@ </span><span class="cx"> """called when the MapperProperty is first attached to a new parent Mapper.""" </span><span class="cx"> self.key = key </span><span class="cx"> self.parent = parent </span><ins>+ self.localparent = parent </ins><span class="cx"> self.do_init(key, parent) </span><span class="cx"> def adapt(self, newparent): </span><span class="cx"> """adapts this MapperProperty to a new parent, assuming the new parent is an inheriting </span><span class="lines">@@ -939,7 +940,8 @@ </span><span class="cx"> False if this MapperProperty cannot be adapted to the new parent (the case for this is, </span><span class="cx"> the parent mapper has a polymorphic select, and this property represents a column that is not </span><span class="cx"> represented in the new mapper's mapped table)""" </span><del>- self.parent = newparent </del><ins>+ #self.parent = newparent + self.localparent = newparent </ins><span class="cx"> return True </span><span class="cx"> def do_init(self, key, parent): </span><span class="cx"> """template method for subclasses""" </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyormpropertiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/orm/properties.py (1517 => 1518)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/orm/properties.py 2006-05-27 01:19:56 UTC (rev 1517) +++ sqlalchemy/trunk/lib/sqlalchemy/orm/properties.py 2006-05-27 05:45:21 UTC (rev 1518) </span><span class="lines">@@ -38,8 +38,6 @@ </span><span class="cx"> else: </span><span class="cx"> statement.append_column(c) </span><span class="cx"> def do_init(self, key, parent): </span><del>- self.key = key - self.parent = parent </del><span class="cx"> # establish a SmartProperty property manager on the object for this key </span><span class="cx"> if parent._is_primary_mapper(): </span><span class="cx"> #print "regiser col on class %s key %s" % (parent.class_.__name__, key) </span><span class="lines">@@ -60,14 +58,12 @@ </span><span class="cx"> def copy(self): </span><span class="cx"> return DeferredColumnProperty(*self.columns) </span><span class="cx"> def do_init(self, key, parent): </span><del>- self.key = key - self.parent = parent </del><span class="cx"> # establish a SmartProperty property manager on the object for this key, </span><span class="cx"> # containing a callable to load in the attribute </span><span class="cx"> if self.is_primary(): </span><span class="cx"> sessionlib.global_attributes.register_attribute(parent.class_, key, uselist=False, callable_=lambda i:self.setup_loader(i)) </span><span class="cx"> def setup_loader(self, instance): </span><del>- if not self.parent.is_assigned(instance): </del><ins>+ if not self.localparent.is_assigned(instance): </ins><span class="cx"> return mapper.object_mapper(instance).props[self.key].setup_loader(instance) </span><span class="cx"> def lazyload(): </span><span class="cx"> session = sessionlib.object_session(instance) </span><span class="lines">@@ -85,7 +81,7 @@ </span><span class="cx"> </span><span class="cx"> try: </span><span class="cx"> if self.group is not None: </span><del>- groupcols = [p for p in self.parent.props.values() if isinstance(p, DeferredColumnProperty) and p.group==self.group] </del><ins>+ groupcols = [p for p in self.localparent.props.values() if isinstance(p, DeferredColumnProperty) and p.group==self.group] </ins><span class="cx"> row = connection.execute(sql.select([g.columns[0] for g in groupcols], clause, use_labels=True), None).fetchone() </span><span class="cx"> for prop in groupcols: </span><span class="cx"> if prop is self: </span><span class="lines">@@ -193,8 +189,6 @@ </span><span class="cx"> self.association = mapper.class_mapper(self.association) </span><span class="cx"> </span><span class="cx"> self.target = self.mapper.mapped_table </span><del>- self.key = key - self.parent = parent </del><span class="cx"> </span><span class="cx"> if self.secondaryjoin is not None and self.secondary is None: </span><span class="cx"> raise exceptions.ArgumentError("Property '" + self.key + "' specified with secondary join condition but no secondary argument") </span><span class="lines">@@ -253,7 +247,6 @@ </span><span class="cx"> </span><span class="cx"> def _get_direction(self): </span><span class="cx"> """determines our 'direction', i.e. do we represent one to many, many to many, etc.""" </span><del>- #print self.key, repr(self.parent.mapped_table.name), repr(self.parent.primarytable.name), repr(self.foreignkey.table.name), repr(self.target), repr(self.foreigntable.name) </del><span class="cx"> </span><span class="cx"> if self.secondaryjoin is not None: </span><span class="cx"> return sync.MANYTOMANY </span><span class="lines">@@ -323,7 +316,6 @@ </span><span class="cx"> </span><span class="cx"> self.syncrules = sync.ClauseSynchronizer(self.parent, self.mapper, self.direction) </span><span class="cx"> if self.direction == sync.MANYTOMANY: </span><del>- #print "COMPILING p/c", self.parent, self.mapper </del><span class="cx"> self.syncrules.compile(self.primaryjoin, parent_tables, [self.secondary], False) </span><span class="cx"> self.syncrules.compile(self.secondaryjoin, target_tables, [self.secondary], True) </span><span class="cx"> else: </span><span class="lines">@@ -342,7 +334,7 @@ </span><span class="cx"> sessionlib.global_attributes.register_attribute(class_, key, uselist = self.uselist, callable_=lambda i: self.setup_loader(i), extension=self.attributeext, cascade=self.cascade, trackparent=True) </span><span class="cx"> </span><span class="cx"> def setup_loader(self, instance): </span><del>- if not self.parent.is_assigned(instance): </del><ins>+ if not self.localparent.is_assigned(instance): </ins><span class="cx"> return mapper.object_mapper(instance).props[self.key].setup_loader(instance) </span><span class="cx"> def lazyload(): </span><span class="cx"> params = {} </span><span class="lines">@@ -476,7 +468,7 @@ </span><span class="cx"> if isinstance(prop, EagerLoader): </span><span class="cx"> eagerprops.append(prop) </span><span class="cx"> if len(eagerprops): </span><del>- recursion_stack[self.parent.mapped_table] = True </del><ins>+ recursion_stack[self.localparent.mapped_table] = True </ins><span class="cx"> self.mapper = self.mapper.copy() </span><span class="cx"> try: </span><span class="cx"> for prop in eagerprops: </span><span class="lines">@@ -495,7 +487,7 @@ </span><span class="cx"> p.eagerprimary.accept_visitor(self.aliasizer) </span><span class="cx"> #print "new eagertqarget", p.eagertarget.name, (p.secondary and p.secondary.name or "none"), p.parent.mapped_table.name </span><span class="cx"> finally: </span><del>- del recursion_stack[self.parent.mapped_table] </del><ins>+ del recursion_stack[self.localparent.mapped_table] </ins><span class="cx"> </span><span class="cx"> self._row_decorator = self._create_decorator_row() </span><span class="cx"> </span><span class="lines">@@ -522,7 +514,7 @@ </span><span class="cx"> if hasattr(statement, '_outerjoin'): </span><span class="cx"> towrap = statement._outerjoin </span><span class="cx"> else: </span><del>- towrap = self.parent.mapped_table </del><ins>+ towrap = self.localparent.mapped_table </ins><span class="cx"> </span><span class="cx"> # print "hello, towrap", str(towrap) </span><span class="cx"> if self.secondaryjoin is not None: </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyormunitofworkpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/orm/unitofwork.py (1517 => 1518)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/orm/unitofwork.py 2006-05-27 01:19:56 UTC (rev 1517) +++ sqlalchemy/trunk/lib/sqlalchemy/orm/unitofwork.py 2006-05-27 05:45:21 UTC (rev 1518) </span><span class="lines">@@ -21,7 +21,7 @@ </span><span class="cx"> import StringIO </span><span class="cx"> import weakref </span><span class="cx"> import topological </span><del>-from sets import * </del><ins>+import sets </ins><span class="cx"> </span><span class="cx"> # a global indicating if all flush() operations should have their plan </span><span class="cx"> # printed to standard output. also can be affected by creating an engine </span><span class="lines">@@ -218,19 +218,19 @@ </span><span class="cx"> flush_context = UOWTransaction(self, session) </span><span class="cx"> </span><span class="cx"> if objects is not None: </span><del>- objset = util.HashSet(iter=objects) </del><ins>+ objset = sets.Set(objects) </ins><span class="cx"> else: </span><span class="cx"> objset = None </span><span class="cx"> </span><span class="cx"> for obj in [n for n in self.new] + [d for d in self.dirty]: </span><del>- if objset is not None and not objset.contains(obj): </del><ins>+ if objset is not None and not obj in objset: </ins><span class="cx"> continue </span><span class="cx"> if self.deleted.contains(obj): </span><span class="cx"> continue </span><span class="cx"> flush_context.register_object(obj) </span><span class="cx"> </span><span class="cx"> for obj in self.deleted: </span><del>- if objset is not None and not objset.contains(obj): </del><ins>+ if objset is not None and not obj in objset: </ins><span class="cx"> continue </span><span class="cx"> flush_context.register_object(obj, isdelete=True) </span><span class="cx"> </span><span class="lines">@@ -265,7 +265,7 @@ </span><span class="cx"> self.uow = uow </span><span class="cx"> self.session = session </span><span class="cx"> # unique list of all the mappers we come across </span><del>- self.mappers = util.HashSet() </del><ins>+ self.mappers = sets.Set() </ins><span class="cx"> self.dependencies = {} </span><span class="cx"> self.tasks = {} </span><span class="cx"> self.__modified = False </span><span class="lines">@@ -287,7 +287,7 @@ </span><span class="cx"> self.uow._validate_obj(obj) </span><span class="cx"> </span><span class="cx"> mapper = object_mapper(obj) </span><del>- self.mappers.append(mapper) </del><ins>+ self.mappers.add(mapper) </ins><span class="cx"> task = self.get_task_by_mapper(mapper) </span><span class="cx"> </span><span class="cx"> if postupdate: </span><span class="lines">@@ -356,7 +356,7 @@ </span><span class="cx"> task = self.get_task_by_mapper(mapper) </span><span class="cx"> targettask = self.get_task_by_mapper(mapperfrom) </span><span class="cx"> up = UOWDependencyProcessor(processor, targettask) </span><del>- task.dependencies.append(up) </del><ins>+ task.dependencies.add(up) </ins><span class="cx"> self._mark_modified() </span><span class="cx"> </span><span class="cx"> def execute(self, echo=False): </span><span class="lines">@@ -368,7 +368,7 @@ </span><span class="cx"> while True: </span><span class="cx"> ret = False </span><span class="cx"> for task in self.tasks.values(): </span><del>- for up in task.dependencies: </del><ins>+ for up in list(task.dependencies): </ins><span class="cx"> if up.preexecute(self): </span><span class="cx"> ret = True </span><span class="cx"> if not ret: </span><span class="lines">@@ -423,9 +423,9 @@ </span><span class="cx"> task.childtasks.append(t) </span><span class="cx"> return task </span><span class="cx"> </span><del>- mappers = util.HashSet() </del><ins>+ mappers = sets.Set() </ins><span class="cx"> for task in self.tasks.values(): </span><del>- mappers.append(task.mapper) </del><ins>+ mappers.add(task.mapper) </ins><span class="cx"> </span><span class="cx"> def inheriting_tasks(task): </span><span class="cx"> if task.mapper not in mappers: </span><span class="lines">@@ -436,12 +436,12 @@ </span><span class="cx"> continue </span><span class="cx"> inheriting_tasks(inherit_task) </span><span class="cx"> task.inheriting_tasks.append(inherit_task) </span><del>- del mappers[mapper] </del><ins>+ mappers.remove(mapper) </ins><span class="cx"> </span><span class="cx"> for task in self.tasks.values(): </span><span class="cx"> inheriting_tasks(task) </span><span class="cx"> </span><del>- head = DependencySorter(self.dependencies, mappers).sort(allow_all_cycles=True) </del><ins>+ head = DependencySorter(self.dependencies, list(mappers)).sort(allow_all_cycles=True) </ins><span class="cx"> #print str(head) </span><span class="cx"> task = sort_hier(head) </span><span class="cx"> return task </span><span class="lines">@@ -496,6 +496,11 @@ </span><span class="cx"> def __init__(self, processor, targettask): </span><span class="cx"> self.processor = processor </span><span class="cx"> self.targettask = targettask </span><ins>+ + def __eq__(self, other): + return other.processor is self.processor and other.targettask is self.targettask + def __hash__(self): + return hash((self.processor, self.targettask)) </ins><span class="cx"> </span><span class="cx"> def preexecute(self, trans): </span><span class="cx"> """traverses all objects handled by this dependency processor and locates additional objects which should be </span><span class="lines">@@ -537,14 +542,42 @@ </span><span class="cx"> def __init__(self, uowtransaction, mapper): </span><span class="cx"> if uowtransaction is not None: </span><span class="cx"> uowtransaction.tasks[mapper] = self </span><ins>+ + # the transaction owning this UOWTask </ins><span class="cx"> self.uowtransaction = uowtransaction </span><ins>+ + # the Mapper which this UOWTask corresponds to </ins><span class="cx"> self.mapper = mapper </span><ins>+ + # a dictionary mapping object instances to a corresponding UOWTaskElement. + # Each UOWTaskElement represents one instance which is to be saved or + # deleted by this UOWTask's Mapper. + # in the case of the row-based "circular sort", the UOWTaskElement may + # also reference further UOWTasks which are dependent on that UOWTaskElement. </ins><span class="cx"> self.objects = util.OrderedDict() </span><del>- self.dependencies = [] - self.cyclical_dependencies = [] - self.circular = None </del><ins>+ + # a list of UOWDependencyProcessors which are executed after saves and + # before deletes, to synchronize data to dependent objects + self.dependencies = sets.Set() + + # a list of UOWTasks that are dependent on this UOWTask, which + # are to be executed after this UOWTask performs saves and post-save + # dependency processing, and before pre-delete processing and deletes </ins><span class="cx"> self.childtasks = [] </span><ins>+ + # a list of UOWTasks that correspond to Mappers which are inheriting + # mappers of this UOWTask's Mapper </ins><span class="cx"> self.inheriting_tasks = [] </span><ins>+ + # whether this UOWTask is circular, meaning it holds a second + # UOWTask that contains a special row-based dependency structure + self.circular = None + + # a list of UOWDependencyProcessors are derived from the main + # set of dependencies, referencing sub-UOWTasks attached to this + # one which represent portions of the total list of objects. + # this is used for the row-based "circular sort" + self.cyclical_dependencies = sets.Set() </ins><span class="cx"> </span><span class="cx"> def is_empty(self): </span><span class="cx"> return len(self.objects) == 0 and len(self.dependencies) == 0 and len(self.childtasks) == 0 </span><span class="lines">@@ -569,8 +602,6 @@ </span><span class="cx"> rec.childtasks.append(childtask) </span><span class="cx"> if isdelete: </span><span class="cx"> rec.isdelete = True </span><del>- #if not childtask: - # rec.preprocessed = False </del><span class="cx"> return retval </span><span class="cx"> </span><span class="cx"> def append_postupdate(self, obj): </span><span class="lines">@@ -666,16 +697,13 @@ </span><span class="cx"> of its object list contain dependencies on each other. </span><span class="cx"> </span><span class="cx"> this is not the normal case; this logic only kicks in when something like </span><del>- a hierarchical tree is being represented. - - """ - </del><ins>+ a hierarchical tree is being represented.""" </ins><span class="cx"> allobjects = [] </span><span class="cx"> for task in cycles: </span><span class="cx"> allobjects += task.objects.keys() </span><span class="cx"> tuples = [] </span><span class="cx"> </span><del>- cycles = Set(cycles) </del><ins>+ cycles = sets.Set(cycles) </ins><span class="cx"> </span><span class="cx"> #print "BEGIN CIRC SORT-------" </span><span class="cx"> #print "PRE-CIRC:" </span><span class="lines">@@ -733,11 +761,6 @@ </span><span class="cx"> # the task corresponding to the processor's objects </span><span class="cx"> childtask = trans.get_task_by_mapper(processor.mapper) </span><span class="cx"> </span><del>-# if isdelete: -# childlist = childlist.unchanged_items() + childlist.deleted_items() -# else: -# childlist = childlist.added_items() - </del><span class="cx"> childlist = childlist.added_items() + childlist.unchanged_items() + childlist.deleted_items() </span><span class="cx"> </span><span class="cx"> for o in childlist: </span><span class="lines">@@ -785,14 +808,12 @@ </span><span class="cx"> else: </span><span class="cx"> t.append(node.item, original_task.objects[node.item].listonly, isdelete=original_task.objects[node.item].isdelete) </span><span class="cx"> parenttask.append(None, listonly=False, isdelete=original_task.objects[node.item].isdelete, childtask=t) </span><del>- #else: - # parenttask.append(None, listonly=False, isdelete=original_task.objects[node.item].isdelete, childtask=t) </del><span class="cx"> if dependencies.has_key(node.item): </span><span class="cx"> for depprocessor, deptask in dependencies[node.item].iteritems(): </span><span class="cx"> if can_add_to_parent: </span><del>- parenttask.cyclical_dependencies.append(depprocessor.branch(deptask)) </del><ins>+ parenttask.cyclical_dependencies.add(depprocessor.branch(deptask)) </ins><span class="cx"> else: </span><del>- t.cyclical_dependencies.append(depprocessor.branch(deptask)) </del><ins>+ t.cyclical_dependencies.add(depprocessor.branch(deptask)) </ins><span class="cx"> return t </span><span class="cx"> </span><span class="cx"> # this is the new "circular" UOWTask which will execute in place of "self" </span><span class="lines">@@ -800,7 +821,7 @@ </span><span class="cx"> </span><span class="cx"> # stick the non-circular dependencies and child tasks onto the new </span><span class="cx"> # circular UOWTask </span><del>- t.dependencies += [d for d in extradeplist] </del><ins>+ [t.dependencies.add(d) for d in extradeplist] </ins><span class="cx"> t.childtasks = self.childtasks </span><span class="cx"> make_task_tree(head, t) </span><span class="cx"> #print t.dump() </span></span></pre> </div> </div> </body> </html> |