summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/orm/unitofwork.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2006-10-19 07:02:04 +0000
committerMike Bayer <mike_mp@zzzcomputing.com>2006-10-19 07:02:04 +0000
commit2bef6699d35b80bf1e329878f8f6a46134b9dc3d (patch)
tree2d4e6903fd4fa1111737443d1c47dc98e49fd7ee /lib/sqlalchemy/orm/unitofwork.py
parent3f5f5ae4fa1dc9958da138a24845bd0e5e6a303c (diff)
downloadsqlalchemy-2bef6699d35b80bf1e329878f8f6a46134b9dc3d.tar.gz
progress on [ticket:329]
Diffstat (limited to 'lib/sqlalchemy/orm/unitofwork.py')
-rw-r--r--lib/sqlalchemy/orm/unitofwork.py413
1 files changed, 196 insertions, 217 deletions
diff --git a/lib/sqlalchemy/orm/unitofwork.py b/lib/sqlalchemy/orm/unitofwork.py
index 5c2e21c5f..c4fd92e36 100644
--- a/lib/sqlalchemy/orm/unitofwork.py
+++ b/lib/sqlalchemy/orm/unitofwork.py
@@ -14,12 +14,11 @@ an "identity map" pattern. The Unit of Work then maintains lists of objects tha
dirty, or deleted and provides the capability to flush all those changes at once.
"""
-from sqlalchemy import attributes, util, logging
+from sqlalchemy import attributes, util, logging, topological
import sqlalchemy
from sqlalchemy.exceptions import *
import StringIO
import weakref
-import topological
import sets
class UOWEventHandler(attributes.AttributeExtension):
@@ -203,8 +202,6 @@ class UOWTransaction(object):
self.mappers = util.Set()
self.dependencies = {}
self.tasks = {}
- self.__modified = False
- self.__is_executing = False
self.logger = logging.instance_logger(self)
self.echo = uow.echo
@@ -229,8 +226,7 @@ class UOWTransaction(object):
self.mappers.add(mapper)
task = self.get_task_by_mapper(mapper)
if postupdate:
- mod = task.append_postupdate(obj, post_update_cols)
- if mod: self._mark_modified()
+ task.append_postupdate(obj, post_update_cols)
return
# for a cyclical task, things need to be sorted out already,
@@ -239,8 +235,7 @@ class UOWTransaction(object):
if task.circular:
return
- mod = task.append(obj, listonly, isdelete=isdelete, **kwargs)
- if mod: self._mark_modified()
+ task.append(obj, listonly, isdelete=isdelete, **kwargs)
def unregister_object(self, obj):
#print "UNREGISTER", obj
@@ -248,12 +243,6 @@ class UOWTransaction(object):
task = self.get_task_by_mapper(mapper)
if obj in task.objects:
task.delete(obj)
- self._mark_modified()
-
- def _mark_modified(self):
- #if self.__is_executing:
- # raise "test assertion failed"
- self.__modified = True
def is_deleted(self, obj):
@@ -287,7 +276,6 @@ class UOWTransaction(object):
dependency = dependency.primary_mapper().base_mapper()
self.dependencies[(mapper, dependency)] = True
- self._mark_modified()
def register_processor(self, mapper, processor, mapperfrom):
"""called by mapper.PropertyLoader to register itself as a "processor", which
@@ -307,7 +295,6 @@ class UOWTransaction(object):
targettask = self.get_task_by_mapper(mapperfrom)
up = UOWDependencyProcessor(processor, targettask)
task.dependencies.add(up)
- self._mark_modified()
def execute(self):
# insure that we have a UOWTask for every mapper that will be involved
@@ -328,14 +315,7 @@ class UOWTransaction(object):
if not ret:
break
- # flip the execution flag on. in some test cases
- # we like to check this flag against any new objects being added, since everything
- # should be registered by now. there is a slight exception in the case of
- # post_update requests; this should be fixed.
- self.__is_executing = True
-
head = self._sort_dependencies()
- self.__modified = False
if self.echo:
if head is None:
self.logger.info("Task dump: None")
@@ -343,8 +323,6 @@ class UOWTransaction(object):
self.logger.info("Task dump:\n" + head.dump())
if head is not None:
head.execute(self)
- #if self.__modified and head is not None:
- # raise "Assertion failed ! new pre-execute dependency step should eliminate post-execute changes (except post_update stuff)."
self.logger.info("Execute Complete")
def post_exec(self):
@@ -391,182 +369,25 @@ class UOWTransaction(object):
mappers.add(base)
return mappers
-
-class UOWTaskElement(object):
- """an element within a UOWTask. corresponds to a single object instance
- to be saved, deleted, or just part of the transaction as a placeholder for
- further dependencies (i.e. 'listonly').
- in the case of self-referential mappers, may also store a list of childtasks,
- further UOWTasks containing objects dependent on this element's object instance."""
- def __init__(self, obj):
- self.obj = obj
- self.__listonly = True
- self.childtasks = []
- self.__isdelete = False
- self.__preprocessed = {}
- def _get_listonly(self):
- return self.__listonly
- def _set_listonly(self, value):
- """set_listonly is a one-way setter, will only go from True to False."""
- if not value and self.__listonly:
- self.__listonly = False
- self.clear_preprocessed()
- def _get_isdelete(self):
- return self.__isdelete
- def _set_isdelete(self, value):
- if self.__isdelete is not value:
- self.__isdelete = value
- self.clear_preprocessed()
- listonly = property(_get_listonly, _set_listonly)
- isdelete = property(_get_isdelete, _set_isdelete)
-
- def mark_preprocessed(self, processor):
- """marks this element as "preprocessed" by a particular UOWDependencyProcessor. preprocessing is the step
- which sweeps through all the relationships on all the objects in the flush transaction and adds other objects
- which are also affected, In some cases it can switch an object from "tosave" to "todelete". changes to the state
- of this UOWTaskElement will reset all "preprocessed" flags, causing it to be preprocessed again. When all UOWTaskElements
- have been fully preprocessed by all UOWDependencyProcessors, then the topological sort can be done."""
- self.__preprocessed[processor] = True
- def is_preprocessed(self, processor):
- return self.__preprocessed.get(processor, False)
- def clear_preprocessed(self):
- self.__preprocessed.clear()
- def __repr__(self):
- return "UOWTaskElement/%d: %s/%d %s" % (id(self), self.obj.__class__.__name__, id(self.obj), (self.listonly and 'listonly' or (self.isdelete and 'delete' or 'save')) )
-
-class UOWDependencyProcessor(object):
- """in between the saving and deleting of objects, process "dependent" data, such as filling in
- a foreign key on a child item from a new primary key, or deleting association rows before a
- delete. This object acts as a proxy to a DependencyProcessor."""
- def __init__(self, processor, targettask):
- self.processor = processor
- self.targettask = targettask
- 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))
-
- def preexecute(self, trans):
- """traverses all objects handled by this dependency processor and locates additional objects which should be
- part of the transaction, such as those affected deletes, orphans to be deleted, etc. Returns True if any
- objects were preprocessed, or False if no objects were preprocessed."""
- def getobj(elem):
- elem.mark_preprocessed(self)
- return elem.obj
-
- ret = False
- elements = [getobj(elem) for elem in self.targettask.polymorphic_tosave_elements if elem.obj is not None and not elem.is_preprocessed(self)]
- if len(elements):
- ret = True
- self.processor.preprocess_dependencies(self.targettask, elements, trans, delete=False)
-
- elements = [getobj(elem) for elem in self.targettask.polymorphic_todelete_elements if elem.obj is not None and not elem.is_preprocessed(self)]
- if len(elements):
- ret = True
- self.processor.preprocess_dependencies(self.targettask, elements, trans, delete=True)
- return ret
-
- def execute(self, trans, delete):
- if not delete:
- self.processor.process_dependencies(self.targettask, [elem.obj for elem in self.targettask.polymorphic_tosave_elements if elem.obj is not None], trans, delete=False)
- else:
- self.processor.process_dependencies(self.targettask, [elem.obj for elem in self.targettask.polymorphic_todelete_elements if elem.obj is not None], trans, delete=True)
-
- def get_object_dependencies(self, obj, trans, passive):
- return self.processor.get_object_dependencies(obj, trans, passive=passive)
-
- def whose_dependent_on_who(self, obj, o):
- return self.processor.whose_dependent_on_who(obj, o)
-
- def branch(self, task):
- return UOWDependencyProcessor(self.processor, task)
-
-class UOWExecutor(object):
- def execute(self, trans, task, isdelete=None):
- if isdelete is not True:
- self.execute_save_steps(trans, task)
- if isdelete is not False:
- self.execute_delete_steps(trans, task)
-
- def save_objects(self, trans, task):
- task._save_objects(trans)
-
- def delete_objects(self, trans, task):
- task._delete_objects(trans)
-
- def execute_dependency(self, trans, dep, isdelete):
- dep.execute(trans, isdelete)
-
- def execute_save_steps(self, trans, task):
- if task.circular is not None:
- self.execute_save_steps(trans, task.circular)
- else:
- self.save_objects(trans, task)
- self.execute_cyclical_dependencies(trans, task, False)
- self.execute_per_element_childtasks(trans, task, False)
- self.execute_dependencies(trans, task, False)
- self.execute_dependencies(trans, task, True)
- self.execute_childtasks(trans, task, False)
-
- def execute_delete_steps(self, trans, task):
- if task.circular is not None:
- self.execute_delete_steps(trans, task.circular)
- else:
- self.execute_cyclical_dependencies(trans, task, True)
- self.execute_childtasks(trans, task, True)
- self.execute_per_element_childtasks(trans, task, True)
- self.delete_objects(trans, task)
-
- def execute_dependencies(self, trans, task, isdelete=None):
- alltasks = list(task.polymorphic_tasks())
- if isdelete is not True:
- for task in alltasks:
- for dep in task.dependencies:
- self.execute_dependency(trans, dep, False)
- if isdelete is not False:
- alltasks.reverse()
- for task in alltasks:
- for dep in task.dependencies:
- self.execute_dependency(trans, dep, True)
-
- def execute_childtasks(self, trans, task, isdelete=None):
- for polytask in task.polymorphic_tasks():
- for child in polytask.childtasks:
- self.execute(trans, child, isdelete)
-
- def execute_cyclical_dependencies(self, trans, task, isdelete):
- for polytask in task.polymorphic_tasks():
- for dep in polytask.cyclical_dependencies:
- self.execute_dependency(trans, dep, isdelete)
-
- def execute_per_element_childtasks(self, trans, task, isdelete):
- for polytask in task.polymorphic_tasks():
- for element in polytask.tosave_elements + polytask.todelete_elements:
- self.execute_element_childtasks(trans, element, isdelete)
-
- def execute_element_childtasks(self, trans, element, isdelete):
- for child in element.childtasks:
- self.execute(trans, child, isdelete)
-
class UOWTask(object):
"""represents the full list of objects that are to be saved/deleted by a specific Mapper."""
def __init__(self, uowtransaction, mapper, circular_parent=None):
if not circular_parent:
uowtransaction.tasks[mapper] = self
-
+
# the transaction owning this UOWTask
self.uowtransaction = uowtransaction
-
+
# the Mapper which this UOWTask corresponds to
self.mapper = mapper
-
+
# 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.
self.objects = {} #util.OrderedDict()
-
+
# a list of UOWDependencyProcessors which are executed after saves and
# before deletes, to synchronize data to dependent objects
self.dependencies = util.Set()
@@ -575,7 +396,7 @@ class UOWTask(object):
# are to be executed after this UOWTask performs saves and post-save
# dependency processing, and before pre-delete processing and deletes
self.childtasks = []
-
+
# whether this UOWTask is circular, meaning it holds a second
# UOWTask that contains a special row-based dependency structure.
self.circular = None
@@ -583,16 +404,16 @@ class UOWTask(object):
# for a task thats part of that row-based dependency structure, points
# back to the "public facing" task.
self.circular_parent = circular_parent
-
+
# 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 = util.Set()
-
+
def is_empty(self):
return len(self.objects) == 0 and len(self.dependencies) == 0 and len(self.childtasks) == 0
-
+
def append(self, obj, listonly = False, childtask = None, isdelete = False):
"""appends an object to this task, to be either saved or deleted depending on the
'isdelete' attribute of this UOWTask. 'listonly' indicates that the object should
@@ -614,14 +435,14 @@ class UOWTask(object):
if isdelete:
rec.isdelete = True
return retval
-
+
def append_postupdate(self, obj, post_update_cols):
# postupdates are UPDATED immeditely (for now)
# convert post_update_cols list to a Set so that __hashcode__ is used to compare columns
# instead of __eq__
self.mapper.save_obj([obj], self.uowtransaction, postupdate=True, post_update_cols=util.Set(post_update_cols))
return True
-
+
def delete(self, obj):
try:
del self.objects[obj]
@@ -633,11 +454,11 @@ class UOWTask(object):
def _delete_objects(self, trans):
for task in self.polymorphic_tasks():
task.mapper.delete_obj(task.todelete_objects, trans)
-
+
def execute(self, trans):
"""executes this UOWTask. saves objects to be saved, processes all dependencies
that have been registered, and deletes objects to be deleted. """
-
+
UOWExecutor().execute(trans, self)
def polymorphic_tasks(self):
@@ -645,10 +466,10 @@ class UOWTask(object):
mappers are inheriting descendants of this UOWTask's mapper. UOWTasks are returned in order
of their hierarchy to each other, meaning if UOWTask B's mapper inherits from UOWTask A's
mapper, then UOWTask B will appear after UOWTask A in the iteration."""
-
+
# first us
yield self
-
+
# "circular dependency" tasks aren't polymorphic
if self.circular_parent is not None:
return
@@ -662,12 +483,12 @@ class UOWTask(object):
else:
for t in _tasks_by_mapper(m):
yield t
-
+
# main yield loop
for task in _tasks_by_mapper(self.mapper):
for t in task.polymorphic_tasks():
yield t
-
+
def contains_object(self, obj, polymorphic=False):
if polymorphic:
for task in self.polymorphic_tasks():
@@ -680,13 +501,13 @@ class UOWTask(object):
def is_inserted(self, obj):
return not hasattr(obj, '_instance_key')
-
+
def is_deleted(self, obj):
try:
return self.objects[obj].isdelete
except KeyError:
return False
-
+
def get_elements(self, polymorphic=False):
if polymorphic:
for task in self.polymorphic_tasks():
@@ -695,7 +516,7 @@ class UOWTask(object):
else:
for rec in self.objects.values():
yield rec
-
+
polymorphic_tosave_elements = property(lambda self: [rec for rec in self.get_elements(polymorphic=True) if not rec.isdelete])
polymorphic_todelete_elements = property(lambda self: [rec for rec in self.get_elements(polymorphic=True) if rec.isdelete])
tosave_elements = property(lambda self: [rec for rec in self.get_elements(polymorphic=False) if not rec.isdelete])
@@ -703,30 +524,30 @@ class UOWTask(object):
tosave_objects = property(lambda self:[rec.obj for rec in self.get_elements(polymorphic=False) if rec.obj is not None and not rec.listonly and rec.isdelete is False])
todelete_objects = property(lambda self:[rec.obj for rec in self.get_elements(polymorphic=False) if rec.obj is not None and not rec.listonly and rec.isdelete is True])
polymorphic_tosave_objects = property(lambda self:[rec.obj for rec in self.get_elements(polymorphic=True) if rec.obj is not None and not rec.listonly and rec.isdelete is False])
-
+
def _sort_circular_dependencies(self, trans, cycles):
"""for a single task, creates a hierarchical tree of "subtasks" which associate
specific dependency actions with individual objects. This is used for a
"cyclical" task, or a task where elements
of its object list contain dependencies on each other.
-
+
this is not the normal case; this logic only kicks in when something like
a hierarchical tree is being represented."""
allobjects = []
for task in cycles:
allobjects += [e.obj for e in task.get_elements(polymorphic=True)]
tuples = []
-
+
cycles = util.Set(cycles)
-
+
#print "BEGIN CIRC SORT-------"
#print "PRE-CIRC:"
#print list(cycles) #[0].dump()
-
+
# dependency processors that arent part of the cyclical thing
# get put here
extradeplist = []
-
+
# organizes a set of new UOWTasks that will be assembled into
# the final tree, for the purposes of holding new UOWDependencyProcessors
# which process small sub-sections of dependent parent/child operations
@@ -748,7 +569,7 @@ class UOWTask(object):
proctask = trans.get_task_by_mapper(dep.processor.mapper.primary_mapper().base_mapper(), True)
targettask = trans.get_task_by_mapper(dep.targettask.mapper.base_mapper(), True)
return targettask in cycles and (proctask is not None and proctask in cycles)
-
+
# organize all original UOWDependencyProcessors by their target task
deps_by_targettask = {}
for t in cycles:
@@ -761,14 +582,14 @@ class UOWTask(object):
l.append(dep)
object_to_original_task = {}
-
+
for t in cycles:
for task in t.polymorphic_tasks():
for taskelement in task.get_elements(polymorphic=False):
obj = taskelement.obj
object_to_original_task[obj] = task
#print "OBJ", repr(obj), "TASK", repr(task)
-
+
for dep in deps_by_targettask.get(task, []):
# is this dependency involved in one of the cycles ?
#print "DEP iterate", dep.processor.key, dep.processor.parent, dep.processor.mapper
@@ -778,16 +599,16 @@ class UOWTask(object):
#print "DEP", dep.processor.key
(processor, targettask) = (dep.processor, dep.targettask)
isdelete = taskelement.isdelete
-
+
# list of dependent objects from this object
childlist = dep.get_object_dependencies(obj, trans, passive=True)
if childlist is None:
continue
# the task corresponding to saving/deleting of those dependent objects
childtask = trans.get_task_by_mapper(processor.mapper.primary_mapper())
-
+
childlist = childlist.added_items() + childlist.unchanged_items() + childlist.deleted_items()
-
+
for o in childlist:
if o is None or not childtask.contains_object(o, polymorphic=True):
continue
@@ -804,7 +625,7 @@ class UOWTask(object):
get_dependency_task(whosdep[0], dep).append(whosdep[1], isdelete=isdelete)
else:
get_dependency_task(obj, dep).append(obj, isdelete=isdelete)
-
+
#print "TUPLES", tuples
head = DependencySorter(tuples, allobjects).sort()
if head is None:
@@ -823,7 +644,7 @@ class UOWTask(object):
nexttasks[originating_task] = t
parenttask.append(None, listonly=False, isdelete=originating_task.objects[node.item].isdelete, childtask=t)
t.append(node.item, originating_task.objects[node.item].listonly, isdelete=originating_task.objects[node.item].isdelete)
-
+
if dependencies.has_key(node.item):
for depprocessor, deptask in dependencies[node.item].iteritems():
t.cyclical_dependencies.add(depprocessor.branch(deptask))
@@ -848,8 +669,8 @@ class UOWTask(object):
import uowdumper
uowdumper.UOWDumper(self, buf)
return buf.getvalue()
-
-
+
+
def __repr__(self):
if self.mapper is not None:
if self.mapper.__class__.__name__ == 'Mapper':
@@ -859,6 +680,164 @@ class UOWTask(object):
else:
name = '(none)'
return ("UOWTask(%d) Mapper: '%s'" % (id(self), name))
+
+class UOWTaskElement(object):
+ """an element within a UOWTask. corresponds to a single object instance
+ to be saved, deleted, or just part of the transaction as a placeholder for
+ further dependencies (i.e. 'listonly').
+ in the case of self-referential mappers, may also store a list of childtasks,
+ further UOWTasks containing objects dependent on this element's object instance."""
+ def __init__(self, obj):
+ self.obj = obj
+ self.__listonly = True
+ self.childtasks = []
+ self.__isdelete = False
+ self.__preprocessed = {}
+ def _get_listonly(self):
+ return self.__listonly
+ def _set_listonly(self, value):
+ """set_listonly is a one-way setter, will only go from True to False."""
+ if not value and self.__listonly:
+ self.__listonly = False
+ self.clear_preprocessed()
+ def _get_isdelete(self):
+ return self.__isdelete
+ def _set_isdelete(self, value):
+ if self.__isdelete is not value:
+ self.__isdelete = value
+ self.clear_preprocessed()
+ listonly = property(_get_listonly, _set_listonly)
+ isdelete = property(_get_isdelete, _set_isdelete)
+
+ def mark_preprocessed(self, processor):
+ """marks this element as "preprocessed" by a particular UOWDependencyProcessor. preprocessing is the step
+ which sweeps through all the relationships on all the objects in the flush transaction and adds other objects
+ which are also affected, In some cases it can switch an object from "tosave" to "todelete". changes to the state
+ of this UOWTaskElement will reset all "preprocessed" flags, causing it to be preprocessed again. When all UOWTaskElements
+ have been fully preprocessed by all UOWDependencyProcessors, then the topological sort can be done."""
+ self.__preprocessed[processor] = True
+ def is_preprocessed(self, processor):
+ return self.__preprocessed.get(processor, False)
+ def clear_preprocessed(self):
+ self.__preprocessed.clear()
+ def __repr__(self):
+ return "UOWTaskElement/%d: %s/%d %s" % (id(self), self.obj.__class__.__name__, id(self.obj), (self.listonly and 'listonly' or (self.isdelete and 'delete' or 'save')) )
+
+class UOWDependencyProcessor(object):
+ """in between the saving and deleting of objects, process "dependent" data, such as filling in
+ a foreign key on a child item from a new primary key, or deleting association rows before a
+ delete. This object acts as a proxy to a DependencyProcessor."""
+ def __init__(self, processor, targettask):
+ self.processor = processor
+ self.targettask = targettask
+ 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))
+
+ def preexecute(self, trans):
+ """traverses all objects handled by this dependency processor and locates additional objects which should be
+ part of the transaction, such as those affected deletes, orphans to be deleted, etc. Returns True if any
+ objects were preprocessed, or False if no objects were preprocessed."""
+ def getobj(elem):
+ elem.mark_preprocessed(self)
+ return elem.obj
+
+ ret = False
+ elements = [getobj(elem) for elem in self.targettask.polymorphic_tosave_elements if elem.obj is not None and not elem.is_preprocessed(self)]
+ if len(elements):
+ ret = True
+ self.processor.preprocess_dependencies(self.targettask, elements, trans, delete=False)
+
+ elements = [getobj(elem) for elem in self.targettask.polymorphic_todelete_elements if elem.obj is not None and not elem.is_preprocessed(self)]
+ if len(elements):
+ ret = True
+ self.processor.preprocess_dependencies(self.targettask, elements, trans, delete=True)
+ return ret
+
+ def execute(self, trans, delete):
+ if not delete:
+ self.processor.process_dependencies(self.targettask, [elem.obj for elem in self.targettask.polymorphic_tosave_elements if elem.obj is not None], trans, delete=False)
+ else:
+ self.processor.process_dependencies(self.targettask, [elem.obj for elem in self.targettask.polymorphic_todelete_elements if elem.obj is not None], trans, delete=True)
+
+ def get_object_dependencies(self, obj, trans, passive):
+ return self.processor.get_object_dependencies(obj, trans, passive=passive)
+
+ def whose_dependent_on_who(self, obj, o):
+ return self.processor.whose_dependent_on_who(obj, o)
+
+ def branch(self, task):
+ return UOWDependencyProcessor(self.processor, task)
+
+class UOWExecutor(object):
+ """encapsulates the execution traversal of a UOWTransaction structure."""
+ def execute(self, trans, task, isdelete=None):
+ if isdelete is not True:
+ self.execute_save_steps(trans, task)
+ if isdelete is not False:
+ self.execute_delete_steps(trans, task)
+
+ def save_objects(self, trans, task):
+ task._save_objects(trans)
+
+ def delete_objects(self, trans, task):
+ task._delete_objects(trans)
+
+ def execute_dependency(self, trans, dep, isdelete):
+ dep.execute(trans, isdelete)
+
+ def execute_save_steps(self, trans, task):
+ if task.circular is not None:
+ self.execute_save_steps(trans, task.circular)
+ else:
+ self.save_objects(trans, task)
+ self.execute_cyclical_dependencies(trans, task, False)
+ self.execute_per_element_childtasks(trans, task, False)
+ self.execute_dependencies(trans, task, False)
+ self.execute_dependencies(trans, task, True)
+ self.execute_childtasks(trans, task, False)
+
+ def execute_delete_steps(self, trans, task):
+ if task.circular is not None:
+ self.execute_delete_steps(trans, task.circular)
+ else:
+ self.execute_cyclical_dependencies(trans, task, True)
+ self.execute_childtasks(trans, task, True)
+ self.execute_per_element_childtasks(trans, task, True)
+ self.delete_objects(trans, task)
+
+ def execute_dependencies(self, trans, task, isdelete=None):
+ alltasks = list(task.polymorphic_tasks())
+ if isdelete is not True:
+ for task in alltasks:
+ for dep in task.dependencies:
+ self.execute_dependency(trans, dep, False)
+ if isdelete is not False:
+ alltasks.reverse()
+ for task in alltasks:
+ for dep in task.dependencies:
+ self.execute_dependency(trans, dep, True)
+
+ def execute_childtasks(self, trans, task, isdelete=None):
+ for polytask in task.polymorphic_tasks():
+ for child in polytask.childtasks:
+ self.execute(trans, child, isdelete)
+
+ def execute_cyclical_dependencies(self, trans, task, isdelete):
+ for polytask in task.polymorphic_tasks():
+ for dep in polytask.cyclical_dependencies:
+ self.execute_dependency(trans, dep, isdelete)
+
+ def execute_per_element_childtasks(self, trans, task, isdelete):
+ for polytask in task.polymorphic_tasks():
+ for element in polytask.tosave_elements + polytask.todelete_elements:
+ self.execute_element_childtasks(trans, element, isdelete)
+
+ def execute_element_childtasks(self, trans, element, isdelete):
+ for child in element.childtasks:
+ self.execute(trans, child, isdelete)
+
class DependencySorter(topological.QueueDependencySorter):
pass