[Sqlalchemy-commits] [2009] sqlalchemy/trunk: progress on [ticket:329]
Brought to you by:
zzzeek
From: <co...@sq...> - 2006-10-19 06:03:55
|
<!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>[2009] sqlalchemy/trunk: progress on [ticket:329]</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>2009</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-10-19 02:02:04 -0500 (Thu, 19 Oct 2006)</dd> </dl> <h3>Log Message</h3> <pre>progress on [ticket:329]</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkCHANGES">sqlalchemy/trunk/CHANGES</a></li> <li><a href="#sqlalchemytrunklibsqlalchemyorm__init__py">sqlalchemy/trunk/lib/sqlalchemy/orm/__init__.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemyorminterfacespy">sqlalchemy/trunk/lib/sqlalchemy/orm/interfaces.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemyormmapperpy">sqlalchemy/trunk/lib/sqlalchemy/orm/mapper.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemyormquerypy">sqlalchemy/trunk/lib/sqlalchemy/orm/query.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemyormsessionpy">sqlalchemy/trunk/lib/sqlalchemy/orm/session.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemyormstrategiespy">sqlalchemy/trunk/lib/sqlalchemy/orm/strategies.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemyormunitofworkpy">sqlalchemy/trunk/lib/sqlalchemy/orm/unitofwork.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemysql_utilpy">sqlalchemy/trunk/lib/sqlalchemy/sql_util.py</a></li> <li><a href="#sqlalchemytrunktestormmapperpy">sqlalchemy/trunk/test/orm/mapper.py</a></li> </ul> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemytopologicalpy">sqlalchemy/trunk/lib/sqlalchemy/topological.py</a></li> </ul> <h3>Removed Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemyormtopologicalpy">sqlalchemy/trunk/lib/sqlalchemy/orm/topological.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkCHANGES"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/CHANGES (2008 => 2009)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/CHANGES 2006-10-19 05:03:01 UTC (rev 2008) +++ sqlalchemy/trunk/CHANGES 2006-10-19 07:02:04 UTC (rev 2009) </span><span class="lines">@@ -36,7 +36,7 @@ </span><span class="cx"> methods, methods that are no longer needed. slightly more constrained </span><span class="cx"> useage, greater emphasis on explicitness </span><span class="cx"> - the "primary_key" attribute of Table and other selectables becomes </span><del>- a setlike ColumnCollection object; is no longer ordered or numerically </del><ins>+ a setlike ColumnCollection object; is ordered but not numerically </ins><span class="cx"> indexed. a comparison clause between two pks that are derived from the </span><span class="cx"> same underlying tables (i.e. such as two Alias objects) can be generated </span><span class="cx"> via table1.primary_key==table2.primary_key </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyorm__init__py"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/orm/__init__.py (2008 => 2009)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/orm/__init__.py 2006-10-19 05:03:01 UTC (rev 2008) +++ sqlalchemy/trunk/lib/sqlalchemy/orm/__init__.py 2006-10-19 07:02:04 UTC (rev 2009) </span><span class="lines">@@ -19,7 +19,7 @@ </span><span class="cx"> </span><span class="cx"> __all__ = ['relation', 'backref', 'eagerload', 'lazyload', 'noload', 'deferred', 'defer', 'undefer', </span><span class="cx"> 'mapper', 'clear_mappers', 'clear_mapper', 'sql', 'class_mapper', 'object_mapper', 'MapperExtension', 'Query', </span><del>- 'cascade_mappers', 'polymorphic_union', 'create_session', 'synonym', 'EXT_PASS' </del><ins>+ 'cascade_mappers', 'polymorphic_union', 'create_session', 'synonym', 'contains_eager', 'EXT_PASS' </ins><span class="cx"> ] </span><span class="cx"> </span><span class="cx"> def relation(*args, **kwargs): </span><span class="lines">@@ -75,6 +75,9 @@ </span><span class="cx"> into a non-load.""" </span><span class="cx"> return strategies.EagerLazyOption(name, lazy=None) </span><span class="cx"> </span><ins>+def contains_eager(key, decorator=None): + return strategies.RowDecorateOption(key, decorator=decorator) + </ins><span class="cx"> def defer(name): </span><span class="cx"> """returns a MapperOption that will convert the column property of the given </span><span class="cx"> name into a deferred load. Used with mapper.options()""" </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyorminterfacespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/orm/interfaces.py (2008 => 2009)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/orm/interfaces.py 2006-10-19 05:03:01 UTC (rev 2008) +++ sqlalchemy/trunk/lib/sqlalchemy/orm/interfaces.py 2006-10-19 07:02:04 UTC (rev 2009) </span><span class="lines">@@ -72,6 +72,7 @@ </span><span class="cx"> self._all_strategies[cls] = strategy </span><span class="cx"> return strategy </span><span class="cx"> def setup(self, querycontext, **kwargs): </span><ins>+ print "SP SETUP, KEY", self.key, " STRAT IS ", self._get_context_strategy(querycontext) </ins><span class="cx"> self._get_context_strategy(querycontext).setup_query(querycontext, **kwargs) </span><span class="cx"> def execute(self, selectcontext, instance, row, identitykey, isnew): </span><span class="cx"> self._get_context_strategy(selectcontext).process_row(selectcontext, instance, row, identitykey, isnew) </span><span class="lines">@@ -92,33 +93,51 @@ </span><span class="cx"> self.attributes = {} </span><span class="cx"> self.recursion_stack = util.Set() </span><span class="cx"> for opt in options: </span><del>- opt.process_context(self) </del><ins>+ self.accept_option(opt) + def accept_option(self, opt): + pass </ins><span class="cx"> </span><span class="cx"> class MapperOption(object): </span><span class="cx"> """describes a modification to an OperationContext.""" </span><del>- def process_context(self, context): </del><ins>+ def process_query_context(self, context): </ins><span class="cx"> pass </span><del>- -class StrategizedOption(MapperOption): - """a MapperOption that affects which LoaderStrategy will be used for an operation - by a StrategizedProperty.""" </del><ins>+ def process_selection_context(self, context): + pass + +class PropertyOption(MapperOption): + """a MapperOption that is applied to a property off the mapper + or one of its child mappers, identified by a dot-separated key.""" </ins><span class="cx"> def __init__(self, key): </span><span class="cx"> self.key = key </span><del>- def get_strategy_class(self): - raise NotImplementedError() - def process_context(self, context): </del><ins>+ def process_query_property(self, context, property): + pass + def process_selection_property(self, context, property): + pass + def process_query_context(self, context): + self.process_query_property(context, self._get_property(context)) + def process_selection_context(self, context): + self.process_selection_property(context, self._get_property(context)) + def _get_property(self, context): </ins><span class="cx"> try: </span><del>- key = self.__key </del><ins>+ prop = self.__prop </ins><span class="cx"> except AttributeError: </span><span class="cx"> mapper = context.mapper </span><span class="cx"> for token in self.key.split('.'): </span><span class="cx"> prop = mapper.props[token] </span><span class="cx"> mapper = getattr(prop, 'mapper', None) </span><del>- self.__key = (LoaderStrategy, prop) - key = self.__key - context.attributes[key] = self.get_strategy_class() - </del><ins>+ self.__prop = prop + return prop </ins><span class="cx"> </span><ins>+class StrategizedOption(PropertyOption): + """a MapperOption that affects which LoaderStrategy will be used for an operation + by a StrategizedProperty.""" + def process_query_property(self, context, property): + print "HI " + self.key + " " + property.key + context.attributes[(LoaderStrategy, property)] = self.get_strategy_class() + def get_strategy_class(self): + raise NotImplementedError() + + </ins><span class="cx"> class LoaderStrategy(object): </span><span class="cx"> """describes the loading behavior of a StrategizedProperty object. The LoaderStrategy </span><span class="cx"> interacts with the querying process in three ways: </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyormmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/orm/mapper.py (2008 => 2009)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/orm/mapper.py 2006-10-19 05:03:01 UTC (rev 2008) +++ sqlalchemy/trunk/lib/sqlalchemy/orm/mapper.py 2006-10-19 07:02:04 UTC (rev 2009) </span><span class="lines">@@ -13,7 +13,7 @@ </span><span class="cx"> import session as sessionlib </span><span class="cx"> import weakref </span><span class="cx"> </span><del>-__all__ = ['Mapper', 'MapperExtension', 'class_mapper', 'object_mapper', 'EXT_PASS', 'SelectionContext'] </del><ins>+__all__ = ['Mapper', 'MapperExtension', 'class_mapper', 'object_mapper', 'EXT_PASS'] </ins><span class="cx"> </span><span class="cx"> # a dictionary mapping classes to their primary mappers </span><span class="cx"> mapper_registry = weakref.WeakKeyDictionary() </span><span class="lines">@@ -686,20 +686,22 @@ </span><span class="cx"> return mapper_registry[self.class_key] </span><span class="cx"> </span><span class="cx"> def is_assigned(self, instance): </span><del>- """returns True if this mapper handles the given instance. this is dependent - not only on class assignment but the optional "entity_name" parameter as well.""" </del><ins>+ """return True if this mapper handles the given instance. + + this is dependent not only on class assignment but the optional "entity_name" parameter as well.""" </ins><span class="cx"> return instance.__class__ is self.class_ and getattr(instance, '_entity_name', None) == self.entity_name </span><span class="cx"> </span><span class="cx"> def _assign_entity_name(self, instance): </span><del>- """assigns this Mapper's entity name to the given instance. subsequent Mapper lookups for this - instance will return the primary mapper corresponding to this Mapper's class and entity name.""" </del><ins>+ """assign this Mapper's entity name to the given instance. + + subsequent Mapper lookups for this instance will return the primary + mapper corresponding to this Mapper's class and entity name.""" </ins><span class="cx"> instance._entity_name = self.entity_name </span><span class="cx"> </span><span class="cx"> def get_session(self): </span><del>- """returns the contextual session provided by the mapper extension chain </del><ins>+ """return the contextual session provided by the mapper extension chain, if any. </ins><span class="cx"> </span><del>- raises InvalidRequestError if a session cannot be retrieved from the - extension chain </del><ins>+ raises InvalidRequestError if a session cannot be retrieved from the extension chain </ins><span class="cx"> """ </span><span class="cx"> self.compile() </span><span class="cx"> s = self.extension.get_session() </span><span class="lines">@@ -708,52 +710,50 @@ </span><span class="cx"> return s </span><span class="cx"> </span><span class="cx"> def has_eager(self): </span><del>- """returns True if one of the properties attached to this Mapper is eager loading""" </del><ins>+ """return True if one of the properties attached to this Mapper is eager loading""" </ins><span class="cx"> return getattr(self, '_has_eager', False) </span><del>- </del><span class="cx"> </span><span class="cx"> def instances(self, cursor, session, *mappers, **kwargs): </span><del>- """given a cursor (ResultProxy) from an SQLEngine, returns a list of object instances - corresponding to the rows in the cursor.""" - self.__log_debug("instances()") - self.compile() </del><ins>+ """return a list of mapped instances corresponding to the rows in a given ResultProxy.""" + return querylib.Query(self, session).instances(cursor, *mappers, **kwargs) + + def identity_key_from_row(self, row): + """return an identity-map key for use in storing/retrieving an item from the identity map. + + row - a sqlalchemy.dbengine.RowProxy instance or other map corresponding result-set + column names to their values within a row. + """ + return (self.class_, tuple([row[column] for column in self.pks_by_table[self.mapped_table]]), self.entity_name) </ins><span class="cx"> </span><del>- context = SelectionContext(self, session, **kwargs) </del><ins>+ def identity_key_from_primary_key(self, primary_key): + """return an identity-map key for use in storing/retrieving an item from an identity map. </ins><span class="cx"> </span><del>- result = util.UniqueAppender([]) - if mappers: - otherresults = [] - for m in mappers: - otherresults.append(util.UniqueAppender([])) - - for row in cursor.fetchall(): - self._instance(context, row, result) - i = 0 - for m in mappers: - m._instance(context, row, otherresults[i]) - i+=1 - - # store new stuff in the identity map - for value in context.identity_map.values(): - session._register_persistent(value) - - if mappers: - return [result.data] + [o.data for o in otherresults] - else: - return result.data </del><ins>+ primary_key - a list of values indicating the identifier. + """ + return (self.class_, tuple(util.to_list(primary_key)), self.entity_name) + + def identity_key_from_instance(self, instance): + """return the identity key for the given instance, based on its primary key attributes. </ins><span class="cx"> </span><del>- def identity_key(self, primary_key): - """returns the instance key for the given identity value. this is a global tracking object used by the Session, and is usually available off a mapped object as instance._instance_key.""" - return sessionlib.get_id_key(util.to_list(primary_key), self.class_, self.entity_name) </del><ins>+ this value is typically also found on the instance itself under the attribute name '_instance_key'. + """ + return self.identity_key_from_primary_key(self.primary_key_from_instance(instance)) </ins><span class="cx"> </span><ins>+ def primary_key_from_instance(self, instance): + """return the list of primary key values for the given instance.""" + return [self._getattrbycolumn(instance, column) for column in self.pks_by_table[self.mapped_table]] + </ins><span class="cx"> def instance_key(self, instance): </span><del>- """returns the instance key for the given instance. this is a global tracking object used by the Session, and is usually available off a mapped object as instance._instance_key.""" - return self.identity_key(self.identity(instance)) </del><ins>+ """deprecated. a synonym for identity_key_from_instance.""" + return self.identity_key_from_instance(instance) </ins><span class="cx"> </span><ins>+ def identity_key(self, primary_key): + """deprecated. a synonym for identity_key_from_primary_key.""" + return self.identity_key_from_primary_key(primary_key) + </ins><span class="cx"> def identity(self, instance): </span><del>- """returns the identity (list of primary key values) for the given instance. The list of values can be fed directly into the get() method as mapper.get(*key).""" - return [self._getattrbycolumn(instance, column) for column in self.pks_by_table[self.mapped_table]] - </del><ins>+ """deprecated. a synoynm for primary_key_from_instance.""" + return self.primary_key_from_instance(instance) </ins><span class="cx"> </span><span class="cx"> def _getpropbycolumn(self, column, raiseerror=True): </span><span class="cx"> try: </span><span class="lines">@@ -1090,8 +1090,6 @@ </span><span class="cx"> for prop in self.__props.values(): </span><span class="cx"> prop.cascade_callable(type, object, callable_, recursive) </span><span class="cx"> </span><del>- def _row_identity_key(self, row): - return sessionlib.get_row_key(row, self.class_, self.pks_by_table[self.mapped_table], self.entity_name) </del><span class="cx"> </span><span class="cx"> def get_select_mapper(self): </span><span class="cx"> """return the mapper used for issuing selects. </span><span class="lines">@@ -1117,7 +1115,7 @@ </span><span class="cx"> # been exposed to being modified by the application. </span><span class="cx"> </span><span class="cx"> populate_existing = context.populate_existing or self.always_refresh </span><del>- identitykey = self._row_identity_key(row) </del><ins>+ identitykey = self.identity_key_from_row(row) </ins><span class="cx"> if context.session.has_key(identitykey): </span><span class="cx"> instance = context.session._get(identitykey) </span><span class="cx"> self.__log_debug("_instance(): using existing instance %s identity %s" % (mapperutil.instance_str(instance), str(identitykey))) </span><span class="lines">@@ -1202,36 +1200,6 @@ </span><span class="cx"> </span><span class="cx"> Mapper.logger = logging.class_logger(Mapper) </span><span class="cx"> </span><del>-class SelectionContext(OperationContext): - """created within the mapper.instances() method to store and share - state among all the Mappers and MapperProperty objects used in a load operation. - - SelectionContext contains these attributes: - - mapper - the Mapper which originated the instances() call. - - session - the Session that is relevant to the instances call. - - identity_map - a dictionary which stores newly created instances that have - not yet been added as persistent to the Session. - - attributes - a dictionary to store arbitrary data; eager loaders use it to - store additional result lists - - populate_existing - indicates if its OK to overwrite the attributes of instances - that were already in the Session - - version_check - indicates if mappers that have version_id columns should verify - that instances existing already within the Session should have this attribute compared - to the freshly loaded value - - """ - def __init__(self, mapper, session, **kwargs): - self.populate_existing = kwargs.pop('populate_existing', False) - self.version_check = kwargs.pop('version_check', False) - self.session = session - self.identity_map = {} - super(SelectionContext, self).__init__(mapper, kwargs.pop('with_options', []), **kwargs) </del><span class="cx"> </span><span class="cx"> class MapperExtension(object): </span><span class="cx"> """base implementation for an object that provides overriding behavior to various </span><span class="lines">@@ -1378,6 +1346,8 @@ </span><span class="cx"> def has_mapper(object): </span><span class="cx"> """returns True if the given object has a mapper association""" </span><span class="cx"> return hasattr(object, '_entity_name') </span><ins>+ + </ins><span class="cx"> </span><span class="cx"> def object_mapper(object, raiseerror=True): </span><span class="cx"> """given an object, returns the primary Mapper associated with the object instance""" </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyormquerypy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/orm/query.py (2008 => 2009)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/orm/query.py 2006-10-19 05:03:01 UTC (rev 2008) +++ sqlalchemy/trunk/lib/sqlalchemy/orm/query.py 2006-10-19 07:02:04 UTC (rev 2009) </span><span class="lines">@@ -5,11 +5,13 @@ </span><span class="cx"> # the MIT License: http://www.opensource.org/licenses/mit-license.php </span><span class="cx"> </span><span class="cx"> import session as sessionlib </span><del>-from sqlalchemy import sql, util, exceptions, sql_util </del><ins>+from sqlalchemy import sql, util, exceptions, sql_util, logging </ins><span class="cx"> </span><span class="cx"> import mapper </span><span class="cx"> from interfaces import OperationContext </span><span class="cx"> </span><ins>+__all__ = ['Query', 'QueryContext', 'SelectionContext'] + </ins><span class="cx"> class Query(object): </span><span class="cx"> """encapsulates the object-fetching operations provided by Mappers.""" </span><span class="cx"> def __init__(self, class_or_mapper, session=None, entity_name=None, lockmode=None, with_options=None, **kwargs): </span><span class="lines">@@ -244,7 +246,7 @@ </span><span class="cx"> </span><span class="cx"> def select_text(self, text, **params): </span><span class="cx"> t = sql.text(text) </span><del>- return self.instances(t, params=params) </del><ins>+ return self.execute(t, params=params) </ins><span class="cx"> </span><span class="cx"> def options(self, *args, **kwargs): </span><span class="cx"> """returns a new Query object using the given MapperOptions.""" </span><span class="lines">@@ -268,13 +270,44 @@ </span><span class="cx"> else: </span><span class="cx"> raise AttributeError(key) </span><span class="cx"> </span><del>- def instances(self, clauseelement, params=None, *args, **kwargs): </del><ins>+ def execute(self, clauseelement, params=None, *args, **kwargs): </ins><span class="cx"> result = self.session.execute(self.mapper, clauseelement, params=params) </span><span class="cx"> try: </span><span class="cx"> return self.mapper.instances(result, self.session, with_options=self.with_options, **kwargs) </span><span class="cx"> finally: </span><span class="cx"> result.close() </span><ins>+ + def instances(self, cursor, *mappers, **kwargs): + """return a list of mapped instances corresponding to the rows in a given ResultProxy.""" + self.__log_debug("instances()") + + session = self.session </ins><span class="cx"> </span><ins>+ context = SelectionContext(self.mapper, session, **kwargs) + + result = util.UniqueAppender([]) + if mappers: + otherresults = [] + for m in mappers: + otherresults.append(util.UniqueAppender([])) + + for row in cursor.fetchall(): + self.mapper._instance(context, row, result) + i = 0 + for m in mappers: + m._instance(context, row, otherresults[i]) + i+=1 + + # store new stuff in the identity map + for value in context.identity_map.values(): + session._register_persistent(value) + + if mappers: + return [result.data] + [o.data for o in otherresults] + else: + return result.data + + </ins><span class="cx"> def _get(self, key, ident=None, reload=False, lockmode=None): </span><span class="cx"> lockmode = lockmode or self.lockmode </span><span class="cx"> if not reload and not self.always_refresh and lockmode is None: </span><span class="lines">@@ -308,7 +341,7 @@ </span><span class="cx"> statement.use_labels = True </span><span class="cx"> if params is None: </span><span class="cx"> params = {} </span><del>- return self.instances(statement, params=params, **kwargs) </del><ins>+ return self.execute(statement, params=params, **kwargs) </ins><span class="cx"> </span><span class="cx"> def _should_nest(self, querycontext): </span><span class="cx"> """return True if the given statement options indicate that we should "nest" the </span><span class="lines">@@ -397,6 +430,11 @@ </span><span class="cx"> </span><span class="cx"> return statement </span><span class="cx"> </span><ins>+ def __log_debug(self, msg): + self.logger.debug(msg) + +Query.logger = logging.class_logger(Query) + </ins><span class="cx"> class QueryContext(OperationContext): </span><span class="cx"> """created within the Query.compile() method to store and share </span><span class="cx"> state among all the Mappers and MapperProperty objects used in a query construction.""" </span><span class="lines">@@ -412,4 +450,40 @@ </span><span class="cx"> super(QueryContext, self).__init__(query.mapper, query.with_options, **kwargs) </span><span class="cx"> def select_args(self): </span><span class="cx"> return {'limit':self.limit, 'offset':self.offset, 'distinct':self.distinct} </span><ins>+ def accept_option(self, opt): + opt.process_query_context(self) + + +class SelectionContext(OperationContext): + """created within the query.instances() method to store and share + state among all the Mappers and MapperProperty objects used in a load operation. + + SelectionContext contains these attributes: + + mapper - the Mapper which originated the instances() call. + + session - the Session that is relevant to the instances call. + + identity_map - a dictionary which stores newly created instances that have + not yet been added as persistent to the Session. + + attributes - a dictionary to store arbitrary data; eager loaders use it to + store additional result lists + + populate_existing - indicates if its OK to overwrite the attributes of instances + that were already in the Session + + version_check - indicates if mappers that have version_id columns should verify + that instances existing already within the Session should have this attribute compared + to the freshly loaded value + + """ + def __init__(self, mapper, session, **kwargs): + self.populate_existing = kwargs.pop('populate_existing', False) + self.version_check = kwargs.pop('version_check', False) + self.session = session + self.identity_map = {} + super(SelectionContext, self).__init__(mapper, kwargs.pop('with_options', []), **kwargs) + def accept_option(self, opt): + opt.process_selection_context(self) </ins><span class="cx"> </span><span class="cx">\ No newline at end of file </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyormsessionpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/orm/session.py (2008 => 2009)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/orm/session.py 2006-10-19 05:03:01 UTC (rev 2008) +++ sqlalchemy/trunk/lib/sqlalchemy/orm/session.py 2006-10-19 07:02:04 UTC (rev 2009) </span><span class="lines">@@ -140,15 +140,17 @@ </span><span class="cx"> self.uow.echo = echo </span><span class="cx"> </span><span class="cx"> def mapper(self, class_, entity_name=None): </span><del>- """given an Class, returns the primary Mapper responsible for persisting it""" </del><ins>+ """given an Class, return the primary Mapper responsible for persisting it""" </ins><span class="cx"> return class_mapper(class_, entity_name = entity_name) </span><span class="cx"> def bind_mapper(self, mapper, bindto): </span><del>- """binds the given Mapper to the given Engine or Connection. All subsequent operations involving this - Mapper will use the given bindto.""" </del><ins>+ """bind the given Mapper to the given Engine or Connection. + + All subsequent operations involving this Mapper will use the given bindto.""" </ins><span class="cx"> self.binds[mapper] = bindto </span><span class="cx"> def bind_table(self, table, bindto): </span><del>- """binds the given Table to the given Engine or Connection. All subsequent operations involving this - Table will use the given bindto.""" </del><ins>+ """bind the given Table to the given Engine or Connection. + + All subsequent operations involving this Table will use the given bindto.""" </ins><span class="cx"> self.binds[table] = bindto </span><span class="cx"> def get_bind(self, mapper): </span><span class="cx"> """return the Engine or Connection which is used to execute statements on behalf of the given Mapper. </span><span class="lines">@@ -198,36 +200,6 @@ </span><span class="cx"> </span><span class="cx"> sql = property(_sql) </span><span class="cx"> </span><del>- - def get_id_key(ident, class_, entity_name=None): - """return an identity-map key for use in storing/retrieving an item from the identity map. - - ident - a tuple of primary key values corresponding to the object to be stored. these - values should be in the same order as the primary keys of the table - - class_ - a reference to the object's class - - entity_name - optional string name to further qualify the class - """ - return (class_, tuple(ident), entity_name) - get_id_key = staticmethod(get_id_key) - - def get_row_key(row, class_, primary_key, entity_name=None): - """return an identity-map key for use in storing/retrieving an item from the identity map. - - row - a sqlalchemy.dbengine.RowProxy instance or other map corresponding result-set - column names to their values within a row. - - class_ - a reference to the object's class - - primary_key - a list of column objects that will target the primary key values - in the given row. - - entity_name - optional string name to further qualify the class - """ - return (class_, tuple([row[column] for column in primary_key]), entity_name) - get_row_key = staticmethod(get_row_key) - </del><span class="cx"> def flush(self, objects=None): </span><span class="cx"> """flush all the object modifications present in this session to the database. </span><span class="cx"> </span><span class="lines">@@ -265,8 +237,12 @@ </span><span class="cx"> raise exceptions.InvalidRequestError("Could not refresh instance '%s'" % repr(obj)) </span><span class="cx"> </span><span class="cx"> def expire(self, obj): </span><del>- """invalidate the data in the given object and sets them to refresh themselves - the next time they are requested.""" </del><ins>+ """mark the given object as expired. + + this will add an instrumentation to all mapped attributes on the instance such that when + an attribute is next accessed, the session will reload all attributes on the instance + from the database. + """ </ins><span class="cx"> self._validate_persistent(obj) </span><span class="cx"> def exp(): </span><span class="cx"> if self.query(obj.__class__)._get(obj._instance_key, reload=True) is None: </span><span class="lines">@@ -274,6 +250,7 @@ </span><span class="cx"> attribute_manager.trigger_history(obj, exp) </span><span class="cx"> </span><span class="cx"> def is_expired(self, obj, unexpire=False): </span><ins>+ """return True if the given object has been marked as expired.""" </ins><span class="cx"> ret = attribute_manager.has_trigger(obj) </span><span class="cx"> if ret and unexpire: </span><span class="cx"> attribute_manager.untrigger_history(obj) </span><span class="lines">@@ -290,9 +267,11 @@ </span><span class="cx"> </span><span class="cx"> def save(self, object, entity_name=None): </span><span class="cx"> """ </span><del>- Adds a transient (unsaved) instance to this Session. This operation cascades the "save_or_update" - method to associated instances if the relation is mapped with cascade="save-update". </del><ins>+ Add a transient (unsaved) instance to this Session. </ins><span class="cx"> </span><ins>+ This operation cascades the "save_or_update" method to associated instances if the + relation is mapped with cascade="save-update". + </ins><span class="cx"> The 'entity_name' keyword argument will further qualify the specific Mapper used to handle this </span><span class="cx"> instance. </span><span class="cx"> """ </span><span class="lines">@@ -300,15 +279,21 @@ </span><span class="cx"> object_mapper(object).cascade_callable('save-update', object, lambda c, e:self._save_or_update_impl(c, e)) </span><span class="cx"> </span><span class="cx"> def update(self, object, entity_name=None): </span><del>- """Brings the given detached (saved) instance into this Session. - If there is a persistent instance with the same identifier (i.e. a saved instance already associated with this - Session), an exception is thrown. </del><ins>+ """Bring the given detached (saved) instance into this Session. + + If there is a persistent instance with the same identifier already associated + with this Session, an exception is thrown. + </ins><span class="cx"> This operation cascades the "save_or_update" method to associated instances if the relation is mapped </span><span class="cx"> with cascade="save-update".""" </span><span class="cx"> self._update_impl(object, entity_name=entity_name) </span><span class="cx"> object_mapper(object).cascade_callable('save-update', object, lambda c, e:self._save_or_update_impl(c, e)) </span><span class="cx"> </span><span class="cx"> def save_or_update(self, object, entity_name=None): </span><ins>+ """save or update the given object into this Session. + + The presence of an '_instance_key' attribute on the instance determines whether to + save() or update() the instance.""" </ins><span class="cx"> self._save_or_update_impl(object, entity_name=entity_name) </span><span class="cx"> object_mapper(object).cascade_callable('save-update', object, lambda c, e:self._save_or_update_impl(c, e)) </span><span class="cx"> </span><span class="lines">@@ -320,11 +305,16 @@ </span><span class="cx"> self._update_impl(object, entity_name=entity_name) </span><span class="cx"> </span><span class="cx"> def delete(self, object, entity_name=None): </span><del>- #self.uow.register_deleted(object) </del><ins>+ """mark the given instance as deleted. + + the delete operation occurs upon flush().""" </ins><span class="cx"> for c in [object] + list(object_mapper(object).cascade_iterator('delete', object)): </span><span class="cx"> self.uow.register_deleted(c) </span><span class="cx"> </span><span class="cx"> def merge(self, object, entity_name=None): </span><ins>+ """merge the object into a newly loaded or existing instance from this Session. + + note: this method is currently not completely implemented.""" </ins><span class="cx"> instance = None </span><span class="cx"> for obj in [object] + list(object_mapper(object).cascade_iterator('merge', object)): </span><span class="cx"> key = getattr(obj, '_instance_key', None) </span><span class="lines">@@ -430,12 +420,6 @@ </span><span class="cx"> """deprecated; a synynom for merge()""" </span><span class="cx"> return self.merge(*args, **kwargs) </span><span class="cx"> </span><del>-def get_id_key(ident, class_, entity_name=None): - return Session.get_id_key(ident, class_, entity_name) - -def get_row_key(row, class_, primary_key, entity_name=None): - return Session.get_row_key(row, class_, primary_key, entity_name) - </del><span class="cx"> def object_mapper(obj): </span><span class="cx"> return sqlalchemy.orm.object_mapper(obj) </span><span class="cx"> </span><span class="lines">@@ -453,6 +437,7 @@ </span><span class="cx"> _sessions = weakref.WeakValueDictionary() </span><span class="cx"> </span><span class="cx"> def object_session(obj): </span><ins>+ """return the Session to which the given object is bound, or None if none.""" </ins><span class="cx"> hashkey = getattr(obj, '_sa_session_id', None) </span><span class="cx"> if hashkey is not None: </span><span class="cx"> return _sessions.get(hashkey) </span><span class="lines">@@ -460,9 +445,3 @@ </span><span class="cx"> </span><span class="cx"> unitofwork.object_session = object_session </span><span class="cx"> </span><del>- -def get_session(obj=None): - """deprecated""" - if obj is not None: - return object_session(obj) - raise exceptions.InvalidRequestError("get_session() is deprecated, and does not return the thread-local session anymore. Use the SessionContext.mapper_extension or import sqlalchemy.mod.threadlocal to establish a default thread-local context.") </del></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyormstrategiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/orm/strategies.py (2008 => 2009)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/orm/strategies.py 2006-10-19 05:03:01 UTC (rev 2008) +++ sqlalchemy/trunk/lib/sqlalchemy/orm/strategies.py 2006-10-19 07:02:04 UTC (rev 2009) </span><span class="lines">@@ -458,10 +458,24 @@ </span><span class="cx"> return </span><span class="cx"> </span><span class="cx"> try: </span><del>- clauses = self.clauses_by_lead_mapper[selectcontext.mapper] - decorated_row = clauses._decorate_row(row) </del><ins>+ # decorate the row according to the stored AliasedClauses for this eager load, + # or look for a user-defined decorator in the SelectContext (which was set up by the contains_eager() option) + if selectcontext.attributes.has_key((EagerLoader, self)): + # custom row decoration function, placed in the selectcontext by the + # contains_eager() mapper option + decorator = selectcontext.attributes[(EagerLoader, self)] + if decorator is None: + decorated_row = row + else: + decorated_row = decorator(row) + print "OK! ROW IS", decorated_row + else: + # AliasedClauses, keyed to the lead mapper used in the query + clauses = self.clauses_by_lead_mapper[selectcontext.mapper] + decorated_row = clauses._decorate_row(row) + print "OK! DECORATED ROW IS", decorated_row </ins><span class="cx"> # check for identity key </span><del>- identity_key = self.mapper._row_identity_key(decorated_row) </del><ins>+ identity_key = self.mapper.identity_key_from_row(decorated_row) </ins><span class="cx"> except KeyError: </span><span class="cx"> # else degrade to a lazy loader </span><span class="cx"> self.logger.debug("degrade to lazy loader on %s" % mapperutil.attribute_str(instance, self.key)) </span><span class="lines">@@ -513,5 +527,11 @@ </span><span class="cx"> elif self.lazy is None: </span><span class="cx"> return NoLoader </span><span class="cx"> </span><ins>+class RowDecorateOption(PropertyOption): + def __init__(self, key, decorator=None): + super(RowDecorateOption, self).__init__(key) + self.decorator = decorator + def process_selection_property(self, context, property): + context.attributes[(EagerLoader, property)] = self.decorator + </ins><span class="cx"> </span><del>- </del></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyormtopologicalpy"></a> <div class="delfile"><h4>Deleted: sqlalchemy/trunk/lib/sqlalchemy/orm/topological.py (2008 => 2009)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/orm/topological.py 2006-10-19 05:03:01 UTC (rev 2008) +++ sqlalchemy/trunk/lib/sqlalchemy/orm/topological.py 2006-10-19 07:02:04 UTC (rev 2009) </span><span class="lines">@@ -1,238 +0,0 @@ </span><del>-# topological.py -# Copyright (C) 2005,2006 Michael Bayer mi...@zz... -# -# This module is part of SQLAlchemy and is released under -# the MIT License: http://www.opensource.org/licenses/mit-license.php - -"""topological sorting algorithms. the key to the unit of work is to assemble a list -of dependencies amongst all the different mappers that have been defined for classes. -Related tables with foreign key constraints have a definite insert order, deletion order, -objects need dependent properties from parent objects set up before saved, etc. -These are all encoded as dependencies, in the form "mapper X is dependent on mapper Y", -meaning mapper Y's objects must be saved before those of mapper X, and mapper X's objects -must be deleted before those of mapper Y. - -The topological sort is an algorithm that receives this list of dependencies as a "partial -ordering", that is a list of pairs which might say, "X is dependent on Y", "Q is dependent -on Z", but does not necessarily tell you anything about Q being dependent on X. Therefore, -its not a straight sort where every element can be compared to another...only some of the -elements have any sorting preference, and then only towards just some of the other elements. -For a particular partial ordering, there can be many possible sorts that satisfy the -conditions. - -An intrinsic "gotcha" to this algorithm is that since there are many possible outcomes -to sorting a partial ordering, the algorithm can return any number of different results for the -same input; just running it on a different machine architecture, or just random differences -in the ordering of dictionaries, can change the result that is returned. While this result -is guaranteed to be true to the incoming partial ordering, if the partial ordering itself -does not properly represent the dependencies, code that works fine will suddenly break, then -work again, then break, etc. Most of the bugs I've chased down while developing the "unit of work" -have been of this nature - very tricky to reproduce and track down, particularly before I -realized this characteristic of the algorithm. -""" -import string, StringIO -from sets import * -import sqlalchemy.util as util -from sqlalchemy.exceptions import * - -class QueueDependencySorter(object): - """this is a topological sort from wikipedia. its very stable. it creates a straight-line - list of elements, then a second pass groups non-dependent actions together to build - more of a tree structure with siblings.""" - class Node: - """represents a node in a tree. stores an 'item' which represents the - dependent thing we are talking about. if node 'a' is an ancestor node of - node 'b', it means 'a's item is *not* dependent on that of 'b'.""" - def __init__(self, item): - self.item = item - self.edges = {} - self.dependencies = {} - self.children = [] - self.cycles = None - def __str__(self): - return self.safestr() - def safestr(self, indent=0): - return (' ' * indent * 2) + \ - str(self.item) + \ - (self.cycles is not None and (" (cycles: " + repr([x for x in self.cycles]) + ")") or "") + \ - "\n" + \ - string.join([n.safestr(indent + 1) for n in self.children], '') - - def describe(self): - return "%s" % (str(self.item)) - def __repr__(self): - return self.describe() - def is_dependent(self, child): - if self.cycles is not None: - for c in self.cycles: - if c.dependencies.has_key(child): - return True - if child.cycles is not None: - for c in child.cycles: - if self.dependencies.has_key(c): - return True - return self.dependencies.has_key(child) - - def __init__(self, tuples, allitems): - self.tuples = tuples - self.allitems = allitems - - def _dump_edges(self, edges): - s = StringIO.StringIO() - for key, value in edges.iteritems(): - for c in value.keys(): - s.write("%s->%s\n" % (repr(key), repr(c))) - return s.getvalue() - - def sort(self, allow_self_cycles=True, allow_all_cycles=False): - (tuples, allitems) = (self.tuples, self.allitems) - - #print "\n---------------------------------\n" - #print repr([t for t in tuples]) - #print repr([a for a in allitems]) - #print "\n---------------------------------\n" - - nodes = {} - edges = {} - for item in allitems + [t[0] for t in tuples] + [t[1] for t in tuples]: - if not nodes.has_key(item): - node = QueueDependencySorter.Node(item) - nodes[item] = node - edges[node] = {} - - for t in tuples: - if t[0] is t[1]: - if allow_self_cycles: - n = nodes[t[0]] - n.cycles = Set([n]) - continue - else: - raise FlushError("Self-referential dependency detected " + repr(t)) - childnode = nodes[t[1]] - parentnode = nodes[t[0]] - self._add_edge(edges, (parentnode, childnode)) - - queue = [] - for n in nodes.values(): - if len(n.edges) == 0: - queue.append(n) - cycles = {} - output = [] - while len(edges) > 0: - #print self._dump_edges(edges) - if len(queue) == 0: - # edges remain but no edgeless nodes to remove; this indicates - # a cycle - if allow_all_cycles: - cycle = self._find_cycle(edges) - lead = cycle[0][0] - lead.cycles = Set() - for edge in cycle: - n = self._remove_edge(edges, edge) - lead.cycles.add(edge[0]) - lead.cycles.add(edge[1]) - if n is not None: - queue.append(n) - if n is not lead: - n._cyclical = True - # loop through cycle - # remove edges from the edge dictionary - # install the cycled nodes in the "cycle" list of one of the nodes - continue - else: - # long cycles not allowed - raise FlushError("Circular dependency detected " + repr(edges) + repr(queue)) - node = queue.pop() - if not hasattr(node, '_cyclical'): - output.append(node) - nodeedges = edges.pop(node, None) - if nodeedges is None: - continue - for childnode in nodeedges.keys(): - del childnode.edges[node] - if len(childnode.edges) == 0: - queue.append(childnode) - - return self._create_batched_tree(output) - - - def _create_batched_tree(self, nodes): - """given a list of nodes from a topological sort, organizes the nodes into a tree structure, - with as many non-dependent nodes set as silbings to each other as possible.""" - def sort(index=None, l=None): - if index is None: - index = 0 - - if index >= len(nodes): - return None - - node = nodes[index] - l2 = [] - sort(index + 1, l2) - for n in l2: - if l is None or search_dep(node, n): - node.children.append(n) - else: - l.append(n) - if l is not None: - l.append(node) - return node - - def search_dep(parent, child): - if child is None: - return False - elif parent.is_dependent(child): - return True - else: - for c in child.children: - x = search_dep(parent, c) - if x is True: - return True - else: - return False - return sort() - - - def _add_edge(self, edges, edge): - (parentnode, childnode) = edge - edges[parentnode][childnode] = True - parentnode.dependencies[childnode] = True - childnode.edges[parentnode] = True - - def _remove_edge(self, edges, edge): - (parentnode, childnode) = edge - del edges[parentnode][childnode] - del childnode.edges[parentnode] - del parentnode.dependencies[childnode] - if len(childnode.edges) == 0: - return childnode - - def _find_cycle(self, edges): - """given a structure of edges, locates a cycle in the strucure and returns - as a list of tuples representing edges involved in the cycle.""" - seen = Set() - cycled_edges = [] - def traverse(d, parent=None): - for key in d.keys(): - if not edges.has_key(key): - continue - if key in seen: - if parent is not None: - cycled_edges.append((parent, key)) - return key - seen.add(key) - x = traverse(edges[key], parent=key) - if x is None: - seen.remove(key) - else: - if parent is not None: - cycled_edges.append((parent, key)) - return x - else: - return None - s = traverse(edges) - if s is None: - return None - else: - return cycled_edges - </del></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyormunitofworkpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/orm/unitofwork.py (2008 => 2009)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/orm/unitofwork.py 2006-10-19 05:03:01 UTC (rev 2008) +++ sqlalchemy/trunk/lib/sqlalchemy/orm/unitofwork.py 2006-10-19 07:02:04 UTC (rev 2009) </span><span class="lines">@@ -14,12 +14,11 @@ </span><span class="cx"> dirty, or deleted and provides the capability to flush all those changes at once. </span><span class="cx"> """ </span><span class="cx"> </span><del>-from sqlalchemy import attributes, util, logging </del><ins>+from sqlalchemy import attributes, util, logging, topological </ins><span class="cx"> import sqlalchemy </span><span class="cx"> from sqlalchemy.exceptions import * </span><span class="cx"> import StringIO </span><span class="cx"> import weakref </span><del>-import topological </del><span class="cx"> import sets </span><span class="cx"> </span><span class="cx"> class UOWEventHandler(attributes.AttributeExtension): </span><span class="lines">@@ -203,8 +202,6 @@ </span><span class="cx"> self.mappers = util.Set() </span><span class="cx"> self.dependencies = {} </span><span class="cx"> self.tasks = {} </span><del>- self.__modified = False - self.__is_executing = False </del><span class="cx"> self.logger = logging.instance_logger(self) </span><span class="cx"> self.echo = uow.echo </span><span class="cx"> </span><span class="lines">@@ -229,8 +226,7 @@ </span><span class="cx"> self.mappers.add(mapper) </span><span class="cx"> task = self.get_task_by_mapper(mapper) </span><span class="cx"> if postupdate: </span><del>- mod = task.append_postupdate(obj, post_update_cols) - if mod: self._mark_modified() </del><ins>+ task.append_postupdate(obj, post_update_cols) </ins><span class="cx"> return </span><span class="cx"> </span><span class="cx"> # for a cyclical task, things need to be sorted out already, </span><span class="lines">@@ -239,8 +235,7 @@ </span><span class="cx"> if task.circular: </span><span class="cx"> return </span><span class="cx"> </span><del>- mod = task.append(obj, listonly, isdelete=isdelete, **kwargs) - if mod: self._mark_modified() </del><ins>+ task.append(obj, listonly, isdelete=isdelete, **kwargs) </ins><span class="cx"> </span><span class="cx"> def unregister_object(self, obj): </span><span class="cx"> #print "UNREGISTER", obj </span><span class="lines">@@ -248,13 +243,7 @@ </span><span class="cx"> task = self.get_task_by_mapper(mapper) </span><span class="cx"> if obj in task.objects: </span><span class="cx"> task.delete(obj) </span><del>- self._mark_modified() </del><span class="cx"> </span><del>- def _mark_modified(self): - #if self.__is_executing: - # raise "test assertion failed" - self.__modified = True - </del><span class="cx"> </span><span class="cx"> def is_deleted(self, obj): </span><span class="cx"> mapper = object_mapper(obj) </span><span class="lines">@@ -287,7 +276,6 @@ </span><span class="cx"> dependency = dependency.primary_mapper().base_mapper() </span><span class="cx"> </span><span class="cx"> self.dependencies[(mapper, dependency)] = True </span><del>- self._mark_modified() </del><span class="cx"> </span><span class="cx"> def register_processor(self, mapper, processor, mapperfrom): </span><span class="cx"> """called by mapper.PropertyLoader to register itself as a "processor", which </span><span class="lines">@@ -307,7 +295,6 @@ </span><span class="cx"> targettask = self.get_task_by_mapper(mapperfrom) </span><span class="cx"> up = UOWDependencyProcessor(processor, targettask) </span><span class="cx"> task.dependencies.add(up) </span><del>- self._mark_modified() </del><span class="cx"> </span><span class="cx"> def execute(self): </span><span class="cx"> # insure that we have a UOWTask for every mapper that will be involved </span><span class="lines">@@ -328,14 +315,7 @@ </span><span class="cx"> if not ret: </span><span class="cx"> break </span><span class="cx"> </span><del>- # 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 - </del><span class="cx"> head = self._sort_dependencies() </span><del>- self.__modified = False </del><span class="cx"> if self.echo: </span><span class="cx"> if head is None: </span><span class="cx"> self.logger.info("Task dump: None") </span><span class="lines">@@ -343,8 +323,6 @@ </span><span class="cx"> self.logger.info("Task dump:\n" + head.dump()) </span><span class="cx"> if head is not None: </span><span class="cx"> head.execute(self) </span><del>- #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)." </del><span class="cx"> self.logger.info("Execute Complete") </span><span class="cx"> </span><span class="cx"> def post_exec(self):... [truncated message content] |