[Sqlalchemy-commits] [1515] sqlalchemy/trunk/test: added has_key to RowProxy, + caching of key looku
Brought to you by:
zzzeek
From: <co...@sq...> - 2006-05-27 00:13:27
|
<!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>[1515] sqlalchemy/trunk/test: added has_key to RowProxy, + caching of key lookups</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1515</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-05-26 19:13:12 -0500 (Fri, 26 May 2006)</dd> </dl> <h3>Log Message</h3> <pre>added has_key to RowProxy, + caching of key lookups fix for mapper translate_row for deferred columns continuing with the "polymorph-tizing" of the unit of work, dependency processing accesses objects on each target task polymorphically</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemyenginebasepy">sqlalchemy/trunk/lib/sqlalchemy/engine/base.py</a></li> <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> <li><a href="#sqlalchemytrunktestinheritancepy">sqlalchemy/trunk/test/inheritance.py</a></li> <li><a href="#sqlalchemytrunktestselectpy">sqlalchemy/trunk/test/select.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemyenginebasepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/engine/base.py (1514 => 1515)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/engine/base.py 2006-05-26 23:35:56 UTC (rev 1514) +++ sqlalchemy/trunk/lib/sqlalchemy/engine/base.py 2006-05-27 00:13:12 UTC (rev 1515) </span><span class="lines">@@ -506,6 +506,7 @@ </span><span class="cx"> self.closed = False </span><span class="cx"> self.executioncontext = executioncontext </span><span class="cx"> self.echo = engine.echo=="debug" </span><ins>+ self.__key_cache = {} </ins><span class="cx"> if executioncontext: </span><span class="cx"> self.rowcount = executioncontext.get_rowcount(cursor) </span><span class="cx"> else: </span><span class="lines">@@ -534,19 +535,36 @@ </span><span class="cx"> self.closed = True </span><span class="cx"> if self.connection.should_close_with_result and self.dialect.supports_autoclose_results: </span><span class="cx"> self.connection.close() </span><del>- def _get_col(self, row, key): - if isinstance(key, sql.ColumnElement): - try: - rec = self.props[key._label.lower()] - except KeyError: </del><ins>+ + def _convert_key(self, key): + """given a key, which could be a ColumnElement, string, etc., matches it to the + appropriate key we got from the result set's metadata; then cache it locally for quick re-access.""" + try: + return self.__key_cache[key] + except KeyError: + if isinstance(key, sql.ColumnElement): </ins><span class="cx"> try: </span><del>- rec = self.props[key.key.lower()] </del><ins>+ rec = self.props[key._label.lower()] </ins><span class="cx"> except KeyError: </span><del>- rec = self.props[key.name.lower()] - elif isinstance(key, str): - rec = self.props[key.lower()] - else: - rec = self.props[key] </del><ins>+ try: + rec = self.props[key.key.lower()] + except KeyError: + rec = self.props[key.name.lower()] + elif isinstance(key, str): + rec = self.props[key.lower()] + else: + rec = self.props[key] + self.__key_cache[key] = rec + return rec + def _has_key(self, row, key): + try: + self._convert_key(key) + return True + except KeyError: + return False + + def _get_col(self, row, key): + rec = self._convert_key(key) </ins><span class="cx"> return rec[0].dialect_impl(self.dialect).convert_result_value(row[rec[1]], self.dialect) </span><span class="cx"> </span><span class="cx"> def __iter__(self): </span><span class="lines">@@ -605,6 +623,8 @@ </span><span class="cx"> return (other is self) or (other == tuple([self.__parent._get_col(self.__row, key) for key in range(0, len(self.__row))])) </span><span class="cx"> def __repr__(self): </span><span class="cx"> return repr(tuple([self.__parent._get_col(self.__row, key) for key in range(0, len(self.__row))])) </span><ins>+ def has_key(self, key): + return self.__parent._has_key(self.__row, key) </ins><span class="cx"> def __getitem__(self, key): </span><span class="cx"> return self.__parent._get_col(self.__row, key) </span><span class="cx"> def __getattr__(self, name): </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyormmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/orm/mapper.py (1514 => 1515)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/orm/mapper.py 2006-05-26 23:35:56 UTC (rev 1514) +++ sqlalchemy/trunk/lib/sqlalchemy/orm/mapper.py 2006-05-27 00:13:12 UTC (rev 1515) </span><span class="lines">@@ -822,7 +822,8 @@ </span><span class="cx"> newrow = util.DictDecorator(row) </span><span class="cx"> for c in tomapper.mapped_table.c: </span><span class="cx"> c2 = self.mapped_table.corresponding_column(c, keys_ok=True, raiseerr=True) </span><del>- newrow[c] = row[c2] </del><ins>+ if row.has_key(c2): + newrow[c] = row[c2] </ins><span class="cx"> return newrow </span><span class="cx"> </span><span class="cx"> def populate_instance(self, session, instance, row, identitykey, imap, isnew, frommapper=None): </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyormpropertiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/orm/properties.py (1514 => 1515)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/orm/properties.py 2006-05-26 23:35:56 UTC (rev 1514) +++ sqlalchemy/trunk/lib/sqlalchemy/orm/properties.py 2006-05-27 00:13:12 UTC (rev 1515) </span><span class="lines">@@ -581,6 +581,8 @@ </span><span class="cx"> class DecoratorDict(object): </span><span class="cx"> def __init__(self, row): </span><span class="cx"> self.row = row </span><ins>+ def has_key(self, key): + return map.has_key(key) or self.row.has_key(key) </ins><span class="cx"> def __getitem__(self, key): </span><span class="cx"> if map.has_key(key): </span><span class="cx"> key = map[key] </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyormunitofworkpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/orm/unitofwork.py (1514 => 1515)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/orm/unitofwork.py 2006-05-26 23:35:56 UTC (rev 1514) +++ sqlalchemy/trunk/lib/sqlalchemy/orm/unitofwork.py 2006-05-27 00:13:12 UTC (rev 1515) </span><span class="lines">@@ -506,12 +506,12 @@ </span><span class="cx"> return elem.obj </span><span class="cx"> </span><span class="cx"> ret = False </span><del>- elements = [getobj(elem) for elem in self.targettask.tosave_elements if elem.obj is not None and not elem.is_preprocessed(self)] </del><ins>+ elements = [getobj(elem) for elem in self.targettask.polymorphic_tosave_elements if elem.obj is not None and not elem.is_preprocessed(self)] </ins><span class="cx"> if len(elements): </span><span class="cx"> ret = True </span><span class="cx"> self.processor.preprocess_dependencies(self.targettask, elements, trans, delete=False) </span><span class="cx"> </span><del>- elements = [getobj(elem) for elem in self.targettask.todelete_elements if elem.obj is not None and not elem.is_preprocessed(self)] </del><ins>+ elements = [getobj(elem) for elem in self.targettask.polymorphic_todelete_elements if elem.obj is not None and not elem.is_preprocessed(self)] </ins><span class="cx"> if len(elements): </span><span class="cx"> ret = True </span><span class="cx"> self.processor.preprocess_dependencies(self.targettask, elements, trans, delete=True) </span><span class="lines">@@ -519,9 +519,9 @@ </span><span class="cx"> </span><span class="cx"> def execute(self, trans, delete): </span><span class="cx"> if not delete: </span><del>- self.processor.process_dependencies(self.targettask, [elem.obj for elem in self.targettask.tosave_elements if elem.obj is not None], trans, delete=False) </del><ins>+ 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) </ins><span class="cx"> else: </span><del>- self.processor.process_dependencies(self.targettask, [elem.obj for elem in self.targettask.todelete_elements if elem.obj is not None], trans, delete=True) </del><ins>+ 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) </ins><span class="cx"> </span><span class="cx"> def get_object_dependencies(self, obj, trans, passive): </span><span class="cx"> return self.processor.get_object_dependencies(obj, trans, passive=passive) </span><span class="lines">@@ -533,6 +533,7 @@ </span><span class="cx"> return UOWDependencyProcessor(self.processor, task) </span><span class="cx"> </span><span class="cx"> class UOWTask(object): </span><ins>+ """represents the full list of objects that are to be saved/deleted by a specific Mapper.""" </ins><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><span class="lines">@@ -603,6 +604,22 @@ </span><span class="cx"> child.execute(trans) </span><span class="cx"> for task in self.inheriting_tasks: </span><span class="cx"> task._execute_childtasks(trans) </span><ins>+ def _execute_cyclical_dependencies(self, trans, isdelete): + for dep in self.cyclical_dependencies: + dep.execute(trans, isdelete) + for task in self.inheriting_tasks: + task._execute_cyclical_dependencies(trans, isdelete) + def _execute_per_element_childtasks(self, trans, isdelete): + if isdelete: + for element in self.todelete_elements: + for task in element.childtasks: + task.execute(trans) + else: + for element in self.tosave_elements: + for task in element.childtasks: + task.execute(trans) + for task in self.inheriting_tasks: + task._execute_per_element_childtasks(trans, isdelete) </ins><span class="cx"> </span><span class="cx"> def execute(self, trans): </span><span class="cx"> """executes this UOWTask. saves objects to be saved, processes all dependencies </span><span class="lines">@@ -615,25 +632,28 @@ </span><span class="cx"> self.circular.execute(trans) </span><span class="cx"> return </span><span class="cx"> </span><del>- # TODO: apply the same recursive inheritance logic to the cyclical tasks/dependencies </del><span class="cx"> # TODO: add a visitation system to the UOW classes and have this execution called </span><span class="cx"> # from a separate executor object ? (would also handle dumping) </span><span class="cx"> </span><span class="cx"> self._save_objects(trans) </span><del>- for dep in self.cyclical_dependencies: - dep.execute(trans, False) - for element in self.tosave_elements: - for task in element.childtasks: - task.execute(trans) </del><ins>+ self._execute_cyclical_dependencies(trans, False) + self._execute_per_element_childtasks(trans, False) </ins><span class="cx"> self._execute_dependencies(trans) </span><del>- for dep in self.cyclical_dependencies: - dep.execute(trans, True) </del><ins>+ self._execute_cyclical_dependencies(trans, True) </ins><span class="cx"> self._execute_childtasks(trans) </span><del>- for element in self.todelete_elements: - for task in element.childtasks: - task.execute(trans) </del><ins>+ self._execute_per_element_childtasks(trans, True) </ins><span class="cx"> self._delete_objects(trans) </span><span class="cx"> </span><ins>+ def _polymorphic_elements(self): + for rec in self.objects.values(): + yield rec + for task in self.inheriting_tasks: + for rec in task._polymorphic_elements(): + yield rec + + polymorphic_tosave_elements = property(lambda self: [rec for rec in self._polymorphic_elements() if not rec.isdelete]) + polymorphic_todelete_elements = property(lambda self: [rec for rec in self._polymorphic_elements() if rec.isdelete]) + </ins><span class="cx"> tosave_elements = property(lambda self: [rec for rec in self.objects.values() if not rec.isdelete]) </span><span class="cx"> todelete_elements = property(lambda self:[rec for rec in self.objects.values() if rec.isdelete]) </span><span class="cx"> tosave_objects = property(lambda self:[rec.obj for rec in self.objects.values() if rec.obj is not None and not rec.listonly and rec.isdelete is False]) </span><span class="lines">@@ -808,9 +828,9 @@ </span><span class="cx"> </span><span class="cx"> def _dump_processor(proc, deletes): </span><span class="cx"> if deletes: </span><del>- val = [t for t in proc.targettask.objects.values() if t.isdelete] </del><ins>+ val = proc.targettask.polymorphic_todelete_elements </ins><span class="cx"> else: </span><del>- val = [t for t in proc.targettask.objects.values() if not t.isdelete] </del><ins>+ val = proc.targettask.polymorphic_tosave_elements </ins><span class="cx"> </span><span class="cx"> buf.write(_indent() + " |- %s attribute on %s (UOWDependencyProcessor(%d) processing %s)\n" % ( </span><span class="cx"> repr(proc.processor.key), </span></span></pre></div> <a id="sqlalchemytrunktestinheritancepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/inheritance.py (1514 => 1515)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/inheritance.py 2006-05-26 23:35:56 UTC (rev 1514) +++ sqlalchemy/trunk/test/inheritance.py 2006-05-27 00:13:12 UTC (rev 1515) </span><span class="lines">@@ -350,7 +350,8 @@ </span><span class="cx"> #contents.add_property('content_type', relation(content_types)) #adding this makes the inheritance stop working </span><span class="cx"> # shouldnt throw exception </span><span class="cx"> products = mapper(Product, product, inherits=contents) </span><del>- </del><ins>+ # TODO: assertion ?? + </ins><span class="cx"> def testbackref(self): </span><span class="cx"> """tests adding a property to the superclass mapper""" </span><span class="cx"> class ContentType(object): pass </span><span class="lines">@@ -364,6 +365,7 @@ </span><span class="cx"> }) </span><span class="cx"> p = Product() </span><span class="cx"> p.contenttype = ContentType() </span><ins>+ # TODO: assertion ?? </ins><span class="cx"> </span><span class="cx"> class InheritTest6(testbase.AssertMixin): </span><span class="cx"> """tests eager load/lazy load of child items off inheritance mappers, tests that </span></span></pre></div> <a id="sqlalchemytrunktestselectpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/select.py (1514 => 1515)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/select.py 2006-05-26 23:35:56 UTC (rev 1514) +++ sqlalchemy/trunk/test/select.py 2006-05-27 00:13:12 UTC (rev 1515) </span><span class="lines">@@ -3,6 +3,7 @@ </span><span class="cx"> from sqlalchemy.databases import sqlite, postgres, mysql, oracle </span><span class="cx"> from testbase import PersistTest </span><span class="cx"> import unittest, re </span><ins>+import testbase </ins><span class="cx"> </span><span class="cx"> # the select test now tests almost completely with TableClause/ColumnClause objects, </span><span class="cx"> # which are free-roaming table/column objects not attached to any database. </span><span class="lines">@@ -645,4 +646,4 @@ </span><span class="cx"> self.runtest(table4.insert(values=(2, 5, 'test')), "INSERT INTO remote_owner.remotetable (rem_id, datatype_id, value) VALUES (:rem_id, :datatype_id, :value)") </span><span class="cx"> </span><span class="cx"> if __name__ == "__main__": </span><del>- unittest.main() </del><ins>+ testbase.main() </ins></span></pre> </div> </div> </body> </html> |