sqlalchemy-commits Mailing List for SQLAlchemy (Page 367)
Brought to you by:
zzzeek
You can subscribe to this list here.
2006 |
Jan
|
Feb
(74) |
Mar
(167) |
Apr
(127) |
May
(190) |
Jun
(119) |
Jul
(77) |
Aug
(82) |
Sep
(84) |
Oct
(153) |
Nov
(45) |
Dec
(54) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2007 |
Jan
(109) |
Feb
(80) |
Mar
(110) |
Apr
(106) |
May
(92) |
Jun
(147) |
Jul
(288) |
Aug
(307) |
Sep
(108) |
Oct
(156) |
Nov
(147) |
Dec
(134) |
2008 |
Jan
(126) |
Feb
(91) |
Mar
(184) |
Apr
(208) |
May
(212) |
Jun
(54) |
Jul
(106) |
Aug
(80) |
Sep
(58) |
Oct
(80) |
Nov
(119) |
Dec
(220) |
2009 |
Jan
(202) |
Feb
(50) |
Mar
(70) |
Apr
(46) |
May
(80) |
Jun
(61) |
Jul
(146) |
Aug
(81) |
Sep
(71) |
Oct
(74) |
Nov
(66) |
Dec
(82) |
2010 |
Jan
(112) |
Feb
(169) |
Mar
(235) |
Apr
(77) |
May
(22) |
Jun
(31) |
Jul
(46) |
Aug
(46) |
Sep
(70) |
Oct
(36) |
Nov
(37) |
Dec
(79) |
2011 |
Jan
(46) |
Feb
(54) |
Mar
(65) |
Apr
(73) |
May
(31) |
Jun
(46) |
Jul
(40) |
Aug
(36) |
Sep
(44) |
Oct
(33) |
Nov
(19) |
Dec
(10) |
2012 |
Jan
(60) |
Feb
(37) |
Mar
(35) |
Apr
(28) |
May
(27) |
Jun
(50) |
Jul
(33) |
Aug
(88) |
Sep
(64) |
Oct
(74) |
Nov
(62) |
Dec
(41) |
2013 |
Jan
(30) |
Feb
(37) |
Mar
(39) |
Apr
(52) |
May
(40) |
Jun
(85) |
Jul
(74) |
Aug
(76) |
Sep
(26) |
Oct
(76) |
Nov
(63) |
Dec
(65) |
2014 |
Jan
(68) |
Feb
(82) |
Mar
(87) |
Apr
(24) |
May
(66) |
Jun
(34) |
Jul
(86) |
Aug
(75) |
Sep
(70) |
Oct
(41) |
Nov
(23) |
Dec
(53) |
2015 |
Jan
(40) |
Feb
(39) |
Mar
(69) |
Apr
(64) |
May
(40) |
Jun
(43) |
Jul
(20) |
Aug
(48) |
Sep
(38) |
Oct
(28) |
Nov
(34) |
Dec
(44) |
2016 |
Jan
(82) |
Feb
(49) |
Mar
(25) |
Apr
(21) |
May
(19) |
Jun
(46) |
Jul
(38) |
Aug
(21) |
Sep
(33) |
Oct
(44) |
Nov
(26) |
Dec
(10) |
2017 |
Jan
(52) |
Feb
(18) |
Mar
(61) |
Apr
(43) |
May
(57) |
Jun
(36) |
Jul
(37) |
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
From: <co...@sq...> - 2006-04-18 18:24:24
|
<!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>[1285] sqlalchemy/trunk/lib/sqlalchemy/mapping: mapper will verify class inheritance scheme; also will not re-init inherited property, as the improved attribute system seems to handle inheritance OK and allows the property to keep its correct initialization on the parent</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1285</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-18 13:24:07 -0500 (Tue, 18 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>mapper will verify class inheritance scheme; also will not re-init inherited property, as the improved attribute system seems to handle inheritance OK and allows the property to keep its correct initialization on the parent exceptions import in query</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemymappingmapperpy">sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymappingquerypy">sqlalchemy/trunk/lib/sqlalchemy/mapping/query.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemymappingmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py (1284 => 1285)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py 2006-04-18 04:37:59 UTC (rev 1284) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py 2006-04-18 18:24:07 UTC (rev 1285) </span><span class="lines">@@ -88,6 +88,8 @@ </span><span class="cx"> self.table = table </span><span class="cx"> </span><span class="cx"> if inherits is not None: </span><ins>+ if self.class_.__mro__[1] != inherits.class_: + raise ArgumentError("Class '%s' does not inherit from '%s'" % (self.class_.__name__, inherits.class_.__name__)) </ins><span class="cx"> self.primarytable = inherits.primarytable </span><span class="cx"> # inherit_condition is optional. </span><span class="cx"> if not table is inherits.noninherited_table: </span><span class="lines">@@ -214,7 +216,7 @@ </span><span class="cx"> if not self.props.has_key(key): </span><span class="cx"> self.props[key] = prop.copy() </span><span class="cx"> self.props[key].parent = self </span><del>- self.props[key].key = None # force re-init </del><ins>+ # self.props[key].key = None # force re-init </ins><span class="cx"> l = [(key, prop) for key, prop in self.props.iteritems()] </span><span class="cx"> for key, prop in l: </span><span class="cx"> if getattr(prop, 'key', None) is None: </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingquerypy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/query.py (1284 => 1285)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/query.py 2006-04-18 04:37:59 UTC (rev 1284) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/query.py 2006-04-18 18:24:07 UTC (rev 1285) </span><span class="lines">@@ -9,6 +9,7 @@ </span><span class="cx"> import sqlalchemy.sql as sql </span><span class="cx"> import sqlalchemy.util as util </span><span class="cx"> import mapper </span><ins>+from sqlalchemy.exceptions import * </ins><span class="cx"> </span><span class="cx"> class Query(object): </span><span class="cx"> """encapsulates the object-fetching operations provided by Mappers.""" </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-17 23:31:21
|
<!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>[1282] sqlalchemy/trunk/lib/sqlalchemy/engine.py: added cgi parser for url key/value connect strings, towards [ticket:157]</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1282</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-17 18:31:12 -0500 (Mon, 17 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>added cgi parser for url key/value connect strings, towards [ticket:157]</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemyenginepy">sqlalchemy/trunk/lib/sqlalchemy/engine.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemyenginepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/engine.py (1281 => 1282)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/engine.py 2006-04-17 23:19:57 UTC (rev 1281) +++ sqlalchemy/trunk/lib/sqlalchemy/engine.py 2006-04-17 23:31:12 UTC (rev 1282) </span><span class="lines">@@ -30,6 +30,7 @@ </span><span class="cx"> import sqlalchemy.databases </span><span class="cx"> import sqlalchemy.types as types </span><span class="cx"> import StringIO, sys, re </span><ins>+from cgi import parse_qsl </ins><span class="cx"> </span><span class="cx"> __all__ = ['create_engine', 'engine_descriptors'] </span><span class="cx"> </span><span class="lines">@@ -71,13 +72,10 @@ </span><span class="cx"> have full ANSI support instead of using this feature. </span><span class="cx"> </span><span class="cx"> """ </span><del>- m = re.match(r'(\w+)://(.*)', name) </del><ins>+ m = re.match(r'(\w+)://(.*)', name) </ins><span class="cx"> if m is not None: </span><span class="cx"> (name, args) = m.group(1, 2) </span><del>- opts = {} - def assign(m): - opts[m.group(1)] = m.group(2) - re.sub(r'([^&]+)=([^&]*)', assign, args) </del><ins>+ opts = dict( parse_qsl( args ) ) </ins><span class="cx"> module = getattr(__import__('sqlalchemy.databases.%s' % name).databases, name) </span><span class="cx"> return module.engine(opts, **kwargs) </span><span class="cx"> </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-17 23:20:11
|
<!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>[1281] sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py: fix for [ticket:158] regarding translate row</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1281</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-17 18:19:57 -0500 (Mon, 17 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>fix for [ticket:158] regarding translate row</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemymappingmapperpy">sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemymappingmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py (1280 => 1281)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py 2006-04-17 01:15:19 UTC (rev 1280) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py 2006-04-17 23:19:57 UTC (rev 1281) </span><span class="lines">@@ -747,9 +747,9 @@ </span><span class="cx"> examples.""" </span><span class="cx"> newrow = util.DictDecorator(row) </span><span class="cx"> for c in self.table.c: </span><del>- newrow[c.key] = row[c] </del><ins>+ newrow[c.name] = row[c] </ins><span class="cx"> for c in tomapper.table.c: </span><del>- newrow[c] = newrow[c.key] </del><ins>+ newrow[c] = newrow[c.name] </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> </div> </body> </html> |
From: <co...@sq...> - 2006-04-17 01:15:34
|
<!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>[1280] sqlalchemy/trunk/test: added _get_from_obj to TypeClause</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1280</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-16 20:15:19 -0500 (Sun, 16 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>added _get_from_obj to TypeClause</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemysqlpy">sqlalchemy/trunk/lib/sqlalchemy/sql.py</a></li> <li><a href="#sqlalchemytrunktestselectpy">sqlalchemy/trunk/test/select.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemysqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/sql.py (1279 => 1280)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-04-16 23:47:26 UTC (rev 1279) +++ sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-04-17 01:15:19 UTC (rev 1280) </span><span class="lines">@@ -702,7 +702,8 @@ </span><span class="cx"> self.type = type </span><span class="cx"> def accept_visitor(self, visitor): </span><span class="cx"> visitor.visit_typeclause(self) </span><del>- </del><ins>+ def _get_from_objects(self): + return [] </ins><span class="cx"> class TextClause(ClauseElement): </span><span class="cx"> """represents literal a SQL text fragment. public constructor is the </span><span class="cx"> text() function. </span></span></pre></div> <a id="sqlalchemytrunktestselectpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/select.py (1279 => 1280)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/select.py 2006-04-16 23:47:26 UTC (rev 1279) +++ sqlalchemy/trunk/test/select.py 2006-04-17 01:15:19 UTC (rev 1280) </span><span class="lines">@@ -549,6 +549,9 @@ </span><span class="cx"> self.assertEqual(str(cast(1234, TEXT, engine=engine)), 'CAST(%s AS %s)' %(literal, expected_results[3])) </span><span class="cx"> self.assertEqual(str(cast('test', String(20), engine=engine)), 'CAST(%s AS %s)' %(literal, expected_results[4])) </span><span class="cx"> </span><ins>+ sel = select([tbl, cast(tbl.c.v1, Numeric)], engine=engine) + self.assertEqual(str(sel), "SELECT casttest.id, casttest.v1, casttest.v2, casttest.ts, CAST(casttest.v1 AS NUMERIC(10, 2)) \nFROM casttest") + </ins><span class="cx"> # first test with Postgres engine </span><span class="cx"> check_results(postgres.engine({}), ['NUMERIC(10, 2)', 'NUMERIC(12, 9)', 'DATE', 'TEXT', 'VARCHAR(20)'], '%(literal)s') </span><span class="cx"> </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-16 23:47:36
|
<!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>[1279] sqlalchemy/trunk/test: a new batching algorithm for the topological sort</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1279</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-16 18:47:26 -0500 (Sun, 16 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>a new batching algorithm for the topological sort</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemymappingtopologicalpy">sqlalchemy/trunk/lib/sqlalchemy/mapping/topological.py</a></li> <li><a href="#sqlalchemytrunktestdependencypy">sqlalchemy/trunk/test/dependency.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemymappingtopologicalpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/topological.py (1278 => 1279)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/topological.py 2006-04-14 18:11:54 UTC (rev 1278) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/topological.py 2006-04-16 23:47:26 UTC (rev 1279) </span><span class="lines">@@ -57,6 +57,16 @@ </span><span class="cx"> return "%s (idself=%s)" % (str(self.item), repr(id(self))) </span><span class="cx"> def __repr__(self): </span><span class="cx"> return self.describe() </span><ins>+ 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) </ins><span class="cx"> </span><span class="cx"> def __init__(self, tuples, allitems): </span><span class="cx"> self.tuples = tuples </span><span class="lines">@@ -138,21 +148,46 @@ </span><span class="cx"> if len(childnode.edges) == 0: </span><span class="cx"> queue.append(childnode) </span><span class="cx"> </span><del>- #print repr(output) - head = None - node = None - # put the sorted list into a "tree". this is not much of a - # "tree" at the moment as its more of a linked list. it would be nice - # to group non-dependent nodes into sibling nodes, which allows better batching - # of SQL statements, but this algorithm has proved tricky - for o in output: - if head is None: - head = o </del><ins>+ 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 </ins><span class="cx"> else: </span><del>- node.children.append(o) - node = o - return head - </del><ins>+ for c in child.children: + x = search_dep(parent, c) + if x is True: + return True + else: + return False + return sort() + + </ins><span class="cx"> def _add_edge(self, edges, edge): </span><span class="cx"> (parentnode, childnode) = edge </span><span class="cx"> edges[parentnode][childnode] = True </span></span></pre></div> <a id="sqlalchemytrunktestdependencypy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/dependency.py (1278 => 1279)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/dependency.py 2006-04-14 18:11:54 UTC (rev 1278) +++ sqlalchemy/trunk/test/dependency.py 2006-04-16 23:47:26 UTC (rev 1279) </span><span class="lines">@@ -24,10 +24,10 @@ </span><span class="cx"> </span><span class="cx"> print "\n" + str(head) </span><span class="cx"> def findnode(t, n, parent=False): </span><del>- if n.item is t[0]: </del><ins>+ if n.item is t[0] or (n.cycles is not None and t[0] in [c.item for c in n.cycles]): </ins><span class="cx"> parent=True </span><span class="cx"> elif n.item is t[1]: </span><del>- if not parent and t[0] not in [c.item for c in n.cycles]: </del><ins>+ if not parent and (n.cycles is None or t[0] not in [c.item for c in n.cycles]): </ins><span class="cx"> self.assert_(False, "Node " + str(t[1]) + " not a child of " +str(t[0])) </span><span class="cx"> else: </span><span class="cx"> return </span><span class="lines">@@ -148,6 +148,7 @@ </span><span class="cx"> self._assert_sort(tuples, allitems) </span><span class="cx"> </span><span class="cx"> def testcircular(self): </span><ins>+ #print "TESTCIRCULAR" </ins><span class="cx"> node1 = thingy('node1') </span><span class="cx"> node2 = thingy('node2') </span><span class="cx"> node3 = thingy('node3') </span><span class="lines">@@ -162,6 +163,7 @@ </span><span class="cx"> (node4, node1) </span><span class="cx"> ] </span><span class="cx"> self._assert_sort(tuples, [node1,node2,node3,node4,node5], allow_all_cycles=True) </span><ins>+ #print "TESTCIRCULAR DONE" </ins><span class="cx"> </span><span class="cx"> </span><span class="cx"> if __name__ == "__main__": </span></span></pre> </div> </div> </body> </html> |
<!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>[1278] sqlalchemy/trunk: had to take out the "treeification" of the dependency sort as it doenst really work , added test conditions to the dependency test + the original test that failed</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1278</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-14 13:11:54 -0500 (Fri, 14 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>had to take out the "treeification" of the dependency sort as it doenst really work , added test conditions to the dependency test + the original test that failed</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkCHANGES">sqlalchemy/trunk/CHANGES</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymappingtopologicalpy">sqlalchemy/trunk/lib/sqlalchemy/mapping/topological.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymappingunitofworkpy">sqlalchemy/trunk/lib/sqlalchemy/mapping/unitofwork.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemysqlpy">sqlalchemy/trunk/lib/sqlalchemy/sql.py</a></li> <li><a href="#sqlalchemytrunktestdependencypy">sqlalchemy/trunk/test/dependency.py</a></li> </ul> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemytrunktestrelationshipspy">sqlalchemy/trunk/test/relationships.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkCHANGES"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/CHANGES (1277 => 1278)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/CHANGES 2006-04-12 19:41:39 UTC (rev 1277) +++ sqlalchemy/trunk/CHANGES 2006-04-14 18:11:54 UTC (rev 1278) </span><span class="lines">@@ -1,3 +1,6 @@ </span><ins>+next +- some fixes to topological sort algorithm + </ins><span class="cx"> 0.1.6 </span><span class="cx"> - support for MS-SQL added courtesy Rick Morrison, Runar Petursson </span><span class="cx"> - the latest SQLSoup from J. Ellis </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingtopologicalpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/topological.py (1277 => 1278)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/topological.py 2006-04-12 19:41:39 UTC (rev 1277) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/topological.py 2006-04-14 18:11:54 UTC (rev 1278) </span><span class="lines">@@ -141,15 +141,16 @@ </span><span class="cx"> #print repr(output) </span><span class="cx"> head = None </span><span class="cx"> node = None </span><ins>+ # put the sorted list into a "tree". this is not much of a + # "tree" at the moment as its more of a linked list. it would be nice + # to group non-dependent nodes into sibling nodes, which allows better batching + # of SQL statements, but this algorithm has proved tricky </ins><span class="cx"> for o in output: </span><span class="cx"> if head is None: </span><span class="cx"> head = o </span><del>- node = o </del><span class="cx"> else: </span><del>- for x in node.children: - if x.dependencies.has_key(o): - node = x </del><span class="cx"> node.children.append(o) </span><ins>+ node = o </ins><span class="cx"> return head </span><span class="cx"> </span><span class="cx"> def _add_edge(self, edges, edge): </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingunitofworkpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/unitofwork.py (1277 => 1278)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/unitofwork.py 2006-04-12 19:41:39 UTC (rev 1277) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/unitofwork.py 2006-04-14 18:11:54 UTC (rev 1278) </span><span class="lines">@@ -422,7 +422,6 @@ </span><span class="cx"> mappers = util.HashSet() </span><span class="cx"> for task in self.tasks.values(): </span><span class="cx"> mappers.append(task.mapper) </span><del>- </del><span class="cx"> head = DependencySorter(self.dependencies, mappers).sort(allow_all_cycles=True) </span><span class="cx"> #print str(head) </span><span class="cx"> task = sort_hier(head) </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemysqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/sql.py (1277 => 1278)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-04-12 19:41:39 UTC (rev 1277) +++ sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-04-14 18:11:54 UTC (rev 1278) </span><span class="lines">@@ -917,7 +917,6 @@ </span><span class="cx"> def __init__(self, left, right, onclause=None, isouter = False): </span><span class="cx"> self.left = left </span><span class="cx"> self.right = right </span><del>- </del><span class="cx"> # TODO: if no onclause, do NATURAL JOIN </span><span class="cx"> if onclause is None: </span><span class="cx"> self.onclause = self._match_primaries(left, right) </span><span class="lines">@@ -925,6 +924,8 @@ </span><span class="cx"> self.onclause = onclause </span><span class="cx"> self.isouter = isouter </span><span class="cx"> </span><ins>+ name = property(lambda self: "Join on %s, %s" % (self.left.name, self.right.name)) + </ins><span class="cx"> def _locate_oid_column(self): </span><span class="cx"> return self.left.oid_column </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemytrunktestdependencypy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/dependency.py (1277 => 1278)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/dependency.py 2006-04-12 19:41:39 UTC (rev 1277) +++ sqlalchemy/trunk/test/dependency.py 2006-04-14 18:11:54 UTC (rev 1278) </span><span class="lines">@@ -17,6 +17,26 @@ </span><span class="cx"> return repr(self) </span><span class="cx"> </span><span class="cx"> class DependencySortTest(PersistTest): </span><ins>+ + def _assert_sort(self, tuples, allnodes, **kwargs): + + head = DependencySorter(tuples, allnodes).sort(**kwargs) + + print "\n" + str(head) + def findnode(t, n, parent=False): + if n.item is t[0]: + parent=True + elif n.item is t[1]: + if not parent and t[0] not in [c.item for c in n.cycles]: + self.assert_(False, "Node " + str(t[1]) + " not a child of " +str(t[0])) + else: + return + for c in n.children: + findnode(t, c, parent) + + for t in tuples: + findnode(t, head) + </ins><span class="cx"> def testsort(self): </span><span class="cx"> rootnode = thingy('root') </span><span class="cx"> node2 = thingy('node2') </span><span class="lines">@@ -27,6 +47,7 @@ </span><span class="cx"> subnode3 = thingy('subnode3') </span><span class="cx"> subnode4 = thingy('subnode4') </span><span class="cx"> subsubnode1 = thingy('subsubnode1') </span><ins>+ allnodes = [rootnode, node2,node3,node4,subnode1,subnode2,subnode3,subnode4,subsubnode1] </ins><span class="cx"> tuples = [ </span><span class="cx"> (subnode3, subsubnode1), </span><span class="cx"> (node2, subnode1), </span><span class="lines">@@ -37,9 +58,9 @@ </span><span class="cx"> (node4, subnode3), </span><span class="cx"> (node4, subnode4) </span><span class="cx"> ] </span><del>- head = DependencySorter(tuples, []).sort() - print "\n" + str(head) </del><span class="cx"> </span><ins>+ self._assert_sort(tuples, allnodes) + </ins><span class="cx"> def testsort2(self): </span><span class="cx"> node1 = thingy('node1') </span><span class="cx"> node2 = thingy('node2') </span><span class="lines">@@ -55,8 +76,7 @@ </span><span class="cx"> (node5, node6), </span><span class="cx"> (node6, node2) </span><span class="cx"> ] </span><del>- head = DependencySorter(tuples, [node7]).sort() - print "\n" + str(head) </del><ins>+ self._assert_sort(tuples, [node1,node2,node3,node4,node5,node6,node7]) </ins><span class="cx"> </span><span class="cx"> def testsort3(self): </span><span class="cx"> ['Mapper|Keyword|keywords,Mapper|IKAssociation|itemkeywords', 'Mapper|Item|items,Mapper|IKAssociation|itemkeywords'] </span><span class="lines">@@ -68,15 +88,10 @@ </span><span class="cx"> (node3, node2), </span><span class="cx"> (node1,node3) </span><span class="cx"> ] </span><del>- head1 = DependencySorter(tuples, [node1, node2, node3]).sort() - head2 = DependencySorter(tuples, [node3, node1, node2]).sort() - head3 = DependencySorter(tuples, [node3, node2, node1]).sort() </del><ins>+ self._assert_sort(tuples, [node1, node2, node3]) + self._assert_sort(tuples, [node3, node1, node2]) + self._assert_sort(tuples, [node3, node2, node1]) </ins><span class="cx"> </span><del>- # TODO: figure out a "node == node2" function - #self.assert_(str(head1) == str(head2) == str(head3)) - print "\n" + str(head1) - print "\n" + str(head2) - print "\n" + str(head3) </del><span class="cx"> </span><span class="cx"> def testsort4(self): </span><span class="cx"> node1 = thingy('keywords') </span><span class="lines">@@ -89,8 +104,7 @@ </span><span class="cx"> (node1, node3), </span><span class="cx"> (node3, node2) </span><span class="cx"> ] </span><del>- head = DependencySorter(tuples, []).sort() - print "\n" + str(head) </del><ins>+ self._assert_sort(tuples, [node1,node2,node3,node4]) </ins><span class="cx"> </span><span class="cx"> def testsort5(self): </span><span class="cx"> # this one, depenending on the weather, </span><span class="lines">@@ -117,9 +131,22 @@ </span><span class="cx"> node3, </span><span class="cx"> node4 </span><span class="cx"> ] </span><del>- head = DependencySorter(tuples, allitems).sort() - print "\n" + str(head) </del><ins>+ self._assert_sort(tuples, allitems) </ins><span class="cx"> </span><ins>+ def testsort6(self): + #('tbl_c', 'tbl_d'), ('tbl_a', 'tbl_c'), ('tbl_b', 'tbl_d') + nodea = thingy('tbl_a') + nodeb = thingy('tbl_b') + nodec = thingy('tbl_c') + noded = thingy('tbl_d') + tuples = [ + (nodec, noded), + (nodea, nodec), + (nodeb, noded) + ] + allitems = [nodea,nodeb,nodec,noded] + self._assert_sort(tuples, allitems) + </ins><span class="cx"> def testcircular(self): </span><span class="cx"> node1 = thingy('node1') </span><span class="cx"> node2 = thingy('node2') </span><span class="lines">@@ -134,8 +161,7 @@ </span><span class="cx"> (node3, node1), </span><span class="cx"> (node4, node1) </span><span class="cx"> ] </span><del>- head = DependencySorter(tuples, []).sort(allow_all_cycles=True) - print "\n" + str(head) </del><ins>+ self._assert_sort(tuples, [node1,node2,node3,node4,node5], allow_all_cycles=True) </ins><span class="cx"> </span><span class="cx"> </span><span class="cx"> if __name__ == "__main__": </span></span></pre></div> <a id="sqlalchemytrunktestrelationshipspy"></a> <div class="addfile"><h4>Added: sqlalchemy/trunk/test/relationships.py (1277 => 1278)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/relationships.py 2006-04-12 19:41:39 UTC (rev 1277) +++ sqlalchemy/trunk/test/relationships.py 2006-04-14 18:11:54 UTC (rev 1278) </span><span class="lines">@@ -0,0 +1,99 @@ </span><ins>+"""Test complex relationships""" + +import testbase +import unittest, sys, datetime + +db = testbase.db +#db.echo_uow=True + +from sqlalchemy import * + + +class RelationTest(testbase.PersistTest): + """this is essentially an extension of the "dependency.py" topological sort test. this exposes + a particular issue that doesnt always occur with the straight dependency tests, due to the nature + of the sort being different based on random conditions""" + def setUpAll(self): + testbase.db.tables.clear() + global tbl_a + global tbl_b + global tbl_c + global tbl_d + tbl_a = Table("tbl_a", db, + Column("id", Integer, primary_key=True), + Column("name", String), + ) + tbl_b = Table("tbl_b", db, + Column("id", Integer, primary_key=True), + Column("name", String), + ) + tbl_c = Table("tbl_c", db, + Column("id", Integer, primary_key=True), + Column("tbl_a_id", Integer, ForeignKey("tbl_a.id"), nullable=False), + Column("name", String), + ) + tbl_d = Table("tbl_d", db, + Column("id", Integer, primary_key=True), + Column("tbl_c_id", Integer, ForeignKey("tbl_c.id"), nullable=False), + Column("tbl_b_id", Integer, ForeignKey("tbl_b.id")), + Column("name", String), + ) + def setUp(self): + tbl_a.create() + tbl_b.create() + tbl_c.create() + tbl_d.create() + + objectstore.clear() + clear_mappers() + + class A(object): + pass + class B(object): + pass + class C(object): + pass + class D(object): + pass + + D.mapper = mapper(D, tbl_d) + C.mapper = mapper(C, tbl_c, properties=dict( + d_rows=relation(D, private=True, backref="c_row"), + )) + B.mapper = mapper(B, tbl_b) + A.mapper = mapper(A, tbl_a, properties=dict( + c_rows=relation(C, private=True, backref="a_row"), + )) + D.mapper.add_property("b_row", relation(B)) + + global a + global c + a = A(); a.name = "a1" + b = B(); b.name = "b1" + c = C(); c.name = "c1"; c.a_row = a + # we must have more than one d row or it won't fail + d = D(); d.name = "d1"; d.b_row = b; d.c_row = c + d = D(); d.name = "d2"; d.b_row = b; d.c_row = c + d = D(); d.name = "d3"; d.b_row = b; d.c_row = c + + def tearDown(self): + tbl_d.drop() + tbl_c.drop() + tbl_b.drop() + tbl_a.drop() + + def testDeleteRootTable(self): + session = objectstore.get_session() + session.commit() + session.delete(a) # works as expected + session.commit() + + def testDeleteMiddleTable(self): + session = objectstore.get_session() + session.commit() + session.delete(c) # fails + session.commit() + + +if __name__ == "__main__": + testbase.main() </ins></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-12 19:41:53
|
<!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>[1277] sqlalchemy/tags/rel_0_1_6/: 0.1.6 </title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1277</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-12 14:41:39 -0500 (Wed, 12 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>0.1.6 </pre> <h3>Added Paths</h3> <ul> <li>sqlalchemy/tags/rel_0_1_6/</li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytagsrel_0_1_6fromrev1276sqlalchemytrunk"></a> <div class="copfile"><h4>Copied: sqlalchemy/tags/rel_0_1_6 (from rev 1276, sqlalchemy/trunk) ( => )</h4> <pre class="diff"><span> <span class="info"> </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-12 19:41:00
|
<!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>[1276] sqlalchemy/trunk/doc/build/content/document_base.myt: 0.1.6...</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1276</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-12 14:40:43 -0500 (Wed, 12 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>0.1.6...</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkdocbuildcontentdocument_basemyt">sqlalchemy/trunk/doc/build/content/document_base.myt</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkdocbuildcontentdocument_basemyt"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/content/document_base.myt (1275 => 1276)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/document_base.myt 2006-04-11 22:15:49 UTC (rev 1275) +++ sqlalchemy/trunk/doc/build/content/document_base.myt 2006-04-12 19:40:43 UTC (rev 1276) </span><span class="lines">@@ -24,7 +24,7 @@ </span><span class="cx"> onepage='documentation' </span><span class="cx"> index='index' </span><span class="cx"> title='SQLAlchemy Documentation' </span><del>- version = '0.1.5' </del><ins>+ version = '0.1.6' </ins><span class="cx"> </%attr> </span><span class="cx"> </span><span class="cx"> <%method title> </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-11 22:15:59
|
<!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>[1275] sqlalchemy/trunk: 0.1.6 prep</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1275</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-11 17:15:49 -0500 (Tue, 11 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>0.1.6 prep</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkCHANGES">sqlalchemy/trunk/CHANGES</a></li> <li><a href="#sqlalchemytrunksetuppy">sqlalchemy/trunk/setup.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkCHANGES"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/CHANGES (1274 => 1275)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/CHANGES 2006-04-11 16:59:18 UTC (rev 1274) +++ sqlalchemy/trunk/CHANGES 2006-04-11 22:15:49 UTC (rev 1275) </span><span class="lines">@@ -1,4 +1,20 @@ </span><span class="cx"> 0.1.6 </span><ins>+- support for MS-SQL added courtesy Rick Morrison, Runar Petursson +- the latest SQLSoup from J. Ellis +- ActiveMapper has preliminary support for inheritance (Jeff Watkins) +- added a "mods" system which allows pluggable modules that modify/augment +core functionality, using the function "install_mods(*modnames)". +- added the first "mod", SelectResults, which modifies mapper selects to +return generators that turn ranges into LIMIT/OFFSET queries (Jonas Borgström) +- factored out querying capabilities of Mapper into a separate Query object +which is Session-centric. this improves the performance of mapper.using(session) +and makes other things possible. +- objectstore/Session refactored, the official way to save objects is now +via the flush() method. The begin/commit functionality of Session is factored +into LegacySession which is still established as the default behavior, until +the 0.2 series. +- types system is bound to an engine at query compile time, not schema +construction time. this simplifies the types system as well as the ProxyEngine. </ins><span class="cx"> - added 'version_id' keyword argument to mapper. this keyword should reference a </span><span class="cx"> Column object with type Integer, preferably non-nullable, which will be used on </span><span class="cx"> the mapped table to track version numbers. this number is incremented on each </span><span class="lines">@@ -14,6 +30,10 @@ </span><span class="cx"> map for an otherwise equilvalent object. </span><span class="cx"> - overhaul to the attributes system. code has been clarified, and also fixed to </span><span class="cx"> support proper polymorphic behavior on object attributes. </span><ins>+- added "for_update" flag to Select objects +- some fixes for backrefs +- fix for postgres1 DateTime type +- documentation pages mostly switched over to Markdown syntax </ins><span class="cx"> </span><span class="cx"> 0.1.5 </span><span class="cx"> - added SQLSession concept to SQLEngine. this object keeps track of retrieving a </span></span></pre></div> <a id="sqlalchemytrunksetuppy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/setup.py (1274 => 1275)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/setup.py 2006-04-11 16:59:18 UTC (rev 1274) +++ sqlalchemy/trunk/setup.py 2006-04-11 22:15:49 UTC (rev 1275) </span><span class="lines">@@ -3,7 +3,7 @@ </span><span class="cx"> from setuptools import setup, find_packages </span><span class="cx"> </span><span class="cx"> setup(name = "SQLAlchemy", </span><del>- version = "0.1.5", </del><ins>+ version = "0.1.6", </ins><span class="cx"> description = "Database Abstraction Library", </span><span class="cx"> author = "Mike Bayer", </span><span class="cx"> author_email = "mi...@zz...", </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-11 16:59:29
|
<!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>[1274] sqlalchemy/trunk/test/inheritance.py: added a failing unittest for inheriting mappers with add_property</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1274</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-11 11:59:18 -0500 (Tue, 11 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>added a failing unittest for inheriting mappers with add_property</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunktestinheritancepy">sqlalchemy/trunk/test/inheritance.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunktestinheritancepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/inheritance.py (1273 => 1274)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/inheritance.py 2006-04-11 16:58:15 UTC (rev 1273) +++ sqlalchemy/trunk/test/inheritance.py 2006-04-11 16:59:18 UTC (rev 1274) </span><span class="lines">@@ -374,7 +374,7 @@ </span><span class="cx"> def testbasic(self): </span><span class="cx"> class ContentType(object): pass </span><span class="cx"> class Content(object): pass </span><del>- class Product(object): pass </del><ins>+ class Product(Content): pass </ins><span class="cx"> </span><span class="cx"> content_types = mapper(ContentType, content_type) </span><span class="cx"> contents = mapper(Content, content, properties={ </span><span class="lines">@@ -383,7 +383,22 @@ </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><ins>+ + def testbackref(self): + class ContentType(object): pass + class Content(object): pass + class Product(Content): pass + + # this test fails currently + contents = mapper(Content, content) + products = mapper(Product, product, inherits=contents) + content_types = mapper(ContentType, content_type, properties={ + 'content':relation(contents, backref='contenttype') + }) + p = Product() + p.contenttype = ContentType() </ins><span class="cx"> </span><ins>+ </ins><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 class="cx"> LazyLoader constructs the right query condition.""" </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-11 16:58:38
|
<!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>[1273] sqlalchemy/trunk/lib/sqlalchemy/databases/postgres.py: added patch for mxDateTime support, [ticket:5], courtesy jk...@ka...</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1273</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-11 11:58:15 -0500 (Tue, 11 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>added patch for mxDateTime support, [ticket:5], courtesy jk...@ka...</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemydatabasespostgrespy">sqlalchemy/trunk/lib/sqlalchemy/databases/postgres.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemydatabasespostgrespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/databases/postgres.py (1272 => 1273)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/databases/postgres.py 2006-04-10 05:44:03 UTC (rev 1272) +++ sqlalchemy/trunk/lib/sqlalchemy/databases/postgres.py 2006-04-11 16:58:15 UTC (rev 1273) </span><span class="lines">@@ -4,7 +4,7 @@ </span><span class="cx"> # This module is part of SQLAlchemy and is released under </span><span class="cx"> # the MIT License: http://www.opensource.org/licenses/mit-license.php </span><span class="cx"> </span><del>-import sys, StringIO, string, types, re </del><ins>+import datetime, sys, StringIO, string, types, re </ins><span class="cx"> </span><span class="cx"> import sqlalchemy.util as util </span><span class="cx"> import sqlalchemy.sql as sql </span><span class="lines">@@ -17,6 +17,11 @@ </span><span class="cx"> import information_schema as ischema </span><span class="cx"> </span><span class="cx"> try: </span><ins>+ import mx.DateTime.DateTime as mxDateTime +except: + mxDateTime = None + +try: </ins><span class="cx"> import psycopg2 as psycopg </span><span class="cx"> #import psycopg2.psycopg1 as psycopg </span><span class="cx"> except: </span><span class="lines">@@ -43,15 +48,27 @@ </span><span class="cx"> return "TIMESTAMP" </span><span class="cx"> class PG1DateTime(sqltypes.DateTime): </span><span class="cx"> def convert_bind_param(self, value, engine): </span><del>- # TODO: perform appropriate postgres1 conversion between Python DateTime/MXDateTime - # this one doesnt seem to work with the "emulation" mode </del><span class="cx"> if value is not None: </span><ins>+ if isinstance(value, datetime.datetime): + seconds = float(str(value.second) + "." + + str(value.microsecond)) + mx_datetime = mxDateTime(value.year, value.month, value.day, + value.hour, value.minute, + seconds) + return psycopg.TimestampFromMx(mx_datetime) </ins><span class="cx"> return psycopg.TimestampFromMx(value) </span><span class="cx"> else: </span><span class="cx"> return None </span><span class="cx"> def convert_result_value(self, value, engine): </span><del>- # TODO: perform appropriate postgres1 conversion between Python DateTime/MXDateTime - return value </del><ins>+ if value is None: + return None + second_parts = str(value.second).split(".") + seconds = int(second_parts[0]) + microseconds = int(second_parts[1]) + return datetime.datetime(value.year, value.month, value.day, + value.hour, value.minute, seconds, + microseconds) + </ins><span class="cx"> def get_col_spec(self): </span><span class="cx"> return "TIMESTAMP" </span><span class="cx"> class PG2Date(sqltypes.Date): </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-10 13:08: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>[1272] sqlalchemy/trunk/lib/sqlalchemy/ext/sqlsoup.py: latest from j.</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1272</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-10 00:44:03 -0500 (Mon, 10 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>latest from j. ellis...</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemyextsqlsouppy">sqlalchemy/trunk/lib/sqlalchemy/ext/sqlsoup.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemyextsqlsouppy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/ext/sqlsoup.py (1271 => 1272)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/ext/sqlsoup.py 2006-04-10 00:58:15 UTC (rev 1271) +++ sqlalchemy/trunk/lib/sqlalchemy/ext/sqlsoup.py 2006-04-10 05:44:03 UTC (rev 1272) </span><span class="lines">@@ -1,72 +1,182 @@ </span><del>-from sqlalchemy import * - -class NoSuchTableError(SQLAlchemyError): pass - -# metaclass is necessary to expose class methods with getattr, e.g. -# we want to pass db.users.select through to users._mapper.select -class TableClassType(type): - def insert(cls, **kwargs): - o = cls() - o.__dict__.update(kwargs) - return o - def __getattr__(cls, attr): - if attr == '_mapper': - # called during mapper init - raise AttributeError() - return getattr(cls._mapper, attr) - -def class_for_table(table): - klass = TableClassType('Class_' + table.name.capitalize(), (object,), {}) - def __repr__(self): - import locale - encoding = locale.getdefaultlocale()[1] - L = [] - for k in self.__class__.c.keys(): - value = getattr(self, k, '') - if isinstance(value, unicode): - value = value.encode(encoding) - L.append("%s=%r" % (k, value)) - return '%s(%s)' % (self.__class__.__name__, ','.join(L)) - klass.__repr__ = __repr__ - klass._mapper = mapper(klass, table) - return klass - -class SqlSoup: - def __init__(self, *args, **kwargs): - """ - args may either be an SQLEngine or a set of arguments suitable - for passing to create_engine - """ - from sqlalchemy.engine import SQLEngine - # meh, sometimes having method overloading instead of kwargs would be easier - if isinstance(args[0], SQLEngine): - engine = args.pop(0) - if args or kwargs: - raise ArgumentError('Extra arguments not allowed when engine is given') - else: - engine = create_engine(*args, **kwargs) - self._engine = engine - self._cache = {} - def delete(self, *args, **kwargs): - objectstore.delete(*args, **kwargs) - def commit(self): - objectstore.get_session().commit() - def rollback(self): - objectstore.clear() - def _reset(self): - # for debugging - self._cache = {} - self.rollback() - def __getattr__(self, attr): - try: - t = self._cache[attr] - except KeyError: - table = Table(attr, self._engine, autoload=True) - if table.columns: - t = class_for_table(table) - else: - t = None - self._cache[attr] = t - if not t: - raise NoSuchTableError('%s does not exist' % attr) - return t </del><ins>+from sqlalchemy import * + +""" +SqlSoup provides a convenient way to access database tables without having +to declare table or mapper classes ahead of time. + +Suppose we have a database with users, books, and loans tables +(corresponding to the PyWebOff dataset, if you're curious). +For testing purposes, we can create this db as follows: + +>>> from sqlalchemy import create_engine +>>> e = create_engine('sqlite://filename=:memory:') +>>> for sql in _testsql: e.execute(sql) +... + +Creating a SqlSoup gateway is just like creating an SqlAlchemy engine: +>>> from sqlalchemy.ext.sqlsoup import SqlSoup +>>> soup = SqlSoup('sqlite://filename=:memory:') + +or, you can re-use an existing engine: +>>> soup = SqlSoup(e) + +Loading objects is as easy as this: +>>> users = soup.users.select() +>>> users.sort() +>>> users +[Class_Users(name='Bhargan Basepair',email='bas...@ex...',password='basepair',classname=None,admin=1), Class_Users(name='Joe Student',email='st...@ex...',password='student',classname=None,admin=0)] + +Of course, letting the database do the sort is better (".c" is short for ".columns"): +>>> soup.users.select(order_by=[soup.users.c.name]) +[Class_Users(name='Bhargan Basepair',email='bas...@ex...',password='basepair',classname=None,admin=1), + Class_Users(name='Joe Student',email='st...@ex...',password='student',classname=None,admin=0)] + +Field access is intuitive: +>>> users[0].email +u'bas...@ex...' + +Of course, you don't want to load all users very often. The common case is to +select by a key or other field: +>>> soup.users.selectone_by(name='Bhargan Basepair') +Class_Users(name='Bhargan Basepair',email='bas...@ex...',password='basepair',classname=None,admin=1) + +All the SqlAlchemy mapper select variants (select, select_by, selectone, selectone_by, selectfirst, selectfirst_by) +are available. See the SqlAlchemy documentation for details: +http://www.sqlalchemy.org/docs/sqlconstruction.myt + +Modifying objects is intuitive: +>>> user = _ +>>> user.email = 'bas...@ex...' +>>> soup.commit() + +(SqlSoup leverages the sophisticated SqlAlchemy unit-of-work code, so +multiple updates to a single object will be turned into a single UPDATE +statement when you commit.) + +Finally, insert and delete. Let's insert a new loan, then delete it: +>>> soup.loans.insert(book_id=soup.books.selectfirst().id, user_name=user.name) +Class_Loans(book_id=1,user_name='Bhargan Basepair',loan_date=None) +>>> soup.commit() + +>>> loan = soup.loans.selectone_by(book_id=1, user_name='Bhargan Basepair') +>>> soup.delete(loan) +>>> soup.commit() +""" + +_testsql = """ +CREATE TABLE books ( + id integer PRIMARY KEY, -- auto-SERIAL in sqlite + title text NOT NULL, + published_year char(4) NOT NULL, + authors text NOT NULL +); + +CREATE TABLE users ( + name varchar(32) PRIMARY KEY, + email varchar(128) NOT NULL, + password varchar(128) NOT NULL, + classname text, + admin int NOT NULL -- 0 = false +); + +CREATE TABLE loans ( + book_id int PRIMARY KEY REFERENCES books(id), + user_name varchar(32) references users(name) + ON DELETE SET NULL ON UPDATE CASCADE, + loan_date date NOT NULL DEFAULT current_timestamp +); + +insert into users(name, email, password, admin) +values('Bhargan Basepair', 'bas...@ex...', 'basepair', 1); +insert into users(name, email, password, admin) +values('Joe Student', 'st...@ex...', 'student', 0); + +insert into books(title, published_year, authors) +values('Mustards I Have Known', '1989', 'Jones'); +insert into books(title, published_year, authors) +values('Regional Variation in Moss', '1971', 'Flim and Flam'); + +insert into loans(book_id, user_name) +values ( + (select min(id) from books), + (select name from users where name like 'Joe%')) +; +""".split(';') + +__all__ = ['NoSuchTableError', 'SqlSoup'] + +class NoSuchTableError(SQLAlchemyError): pass + +# metaclass is necessary to expose class methods with getattr, e.g. +# we want to pass db.users.select through to users._mapper.select +class TableClassType(type): + def insert(cls, **kwargs): + o = cls() + o.__dict__.update(kwargs) + return o + def __getattr__(cls, attr): + if attr == '_mapper': + # called during mapper init + raise AttributeError() + return getattr(cls._mapper, attr) + +def class_for_table(table): + klass = TableClassType('Class_' + table.name.capitalize(), (object,), {}) + def __repr__(self): + import locale + encoding = locale.getdefaultlocale()[1] + L = [] + for k in self.__class__.c.keys(): + value = getattr(self, k, '') + if isinstance(value, unicode): + value = value.encode(encoding) + L.append("%s=%r" % (k, value)) + return '%s(%s)' % (self.__class__.__name__, ','.join(L)) + klass.__repr__ = __repr__ + klass._mapper = mapper(klass, table) + return klass + +class SqlSoup: + def __init__(self, *args, **kwargs): + """ + args may either be an SQLEngine or a set of arguments suitable + for passing to create_engine + """ + from sqlalchemy.engine import SQLEngine + # meh, sometimes having method overloading instead of kwargs would be easier + if isinstance(args[0], SQLEngine): + args = list(args) + engine = args.pop(0) + if args or kwargs: + raise ArgumentError('Extra arguments not allowed when engine is given') + else: + engine = create_engine(*args, **kwargs) + self._engine = engine + self._cache = {} + def delete(self, *args, **kwargs): + objectstore.delete(*args, **kwargs) + def commit(self): + objectstore.get_session().commit() + def rollback(self): + objectstore.clear() + def _reset(self): + # for debugging + self._cache = {} + self.rollback() + def __getattr__(self, attr): + try: + t = self._cache[attr] + except KeyError: + table = Table(attr, self._engine, autoload=True) + if table.columns: + t = class_for_table(table) + else: + t = None + self._cache[attr] = t + if not t: + raise NoSuchTableError('%s does not exist' % attr) + return t + +if __name__ == '__main__': + import doctest + doctest.testmod() </ins></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-08 17:12:08
|
<!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>[1270] sqlalchemy/trunk/lib/sqlalchemy/ext/activemapper.py: Added preliminary support for inheritance.</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1270</dd> <dt>Author</dt> <dd>jeff</dd> <dt>Date</dt> <dd>2006-04-08 12:11:53 -0500 (Sat, 08 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>Added preliminary support for inheritance.</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemyextactivemapperpy">sqlalchemy/trunk/lib/sqlalchemy/ext/activemapper.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemyextactivemapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/ext/activemapper.py (1269 => 1270)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/ext/activemapper.py 2006-04-07 06:34:37 UTC (rev 1269) +++ sqlalchemy/trunk/lib/sqlalchemy/ext/activemapper.py 2006-04-08 17:11:53 UTC (rev 1270) </span><span class="lines">@@ -1,4 +1,4 @@ </span><del>-from sqlalchemy import objectstore, create_engine, assign_mapper, relation, mapper </del><ins>+from sqlalchemy import objectstore, create_engine, assign_mapper, relation, mapper, join </ins><span class="cx"> from sqlalchemy import and_, or_ </span><span class="cx"> from sqlalchemy import Table, Column, ForeignKey </span><span class="cx"> from sqlalchemy.ext.proxy import ProxyEngine </span><span class="lines">@@ -25,8 +25,6 @@ </span><span class="cx"> self.colname = colname </span><span class="cx"> self.foreign_key = foreign_key </span><span class="cx"> self.primary_key = primary_key </span><del>-# self.unique = kwargs.pop( 'unique', None ) -# self.index = kwargs.pop( 'indexed', None ) </del><span class="cx"> self.kwargs = kwargs </span><span class="cx"> self.args = args </span><span class="cx"> </span><span class="lines">@@ -84,7 +82,8 @@ </span><span class="cx"> private=reldesc.private, </span><span class="cx"> lazy=reldesc.lazy, </span><span class="cx"> uselist=reldesc.uselist) </span><del>- assign_mapper(klass, klass.table, properties=relations) </del><ins>+ assign_mapper(klass, klass.table, properties=relations, + inherits=getattr(klass, "_base_mapper", None)) </ins><span class="cx"> if was_deferred: __deferred_classes__.remove(klass) </span><span class="cx"> </span><span class="cx"> if not was_deferred: </span><span class="lines">@@ -128,17 +127,18 @@ </span><span class="cx"> primary_key=value.primary_key, </span><span class="cx"> *value.args, **value.kwargs) </span><span class="cx"> columns.append(col) </span><del>-# if value.indexed: -# # create a Index object for the column -# index= Index( "%s_idx" % (value.colname or name), -# col, unique= value.unique ) </del><span class="cx"> continue </span><span class="cx"> </span><span class="cx"> if isinstance(value, relationship): </span><span class="cx"> relations[name] = value </span><span class="cx"> assert _engine is not None, "No engine specified" </span><span class="cx"> cls.table = Table(table_name, _engine, *columns) </span><del>- assign_mapper(cls, cls.table) </del><ins>+ # check for inheritence + if hasattr( bases[0], "mapping" ): + cls._base_mapper= bases[0].mapper + assign_mapper(cls, cls.table, inherits=cls._base_mapper) + else: + assign_mapper(cls, cls.table) </ins><span class="cx"> cls.relations = relations </span><span class="cx"> ActiveMapperMeta.classes[clsname] = cls </span><span class="cx"> </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-07 06:34:47
|
<!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>[1269] sqlalchemy/branches/schema/: new branch to decouple SchemaItem from SQLEngine via Schema object</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1269</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-07 01:34:37 -0500 (Fri, 07 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>new branch to decouple SchemaItem from SQLEngine via Schema object</pre> <h3>Added Paths</h3> <ul> <li>sqlalchemy/branches/schema/</li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemybranchesschemafromrev1268sqlalchemytrunk"></a> <div class="copfile"><h4>Copied: sqlalchemy/branches/schema (from rev 1268, sqlalchemy/trunk) ( => )</h4> <pre class="diff"><span> <span class="info"> </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-07 00:26:13
|
<!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>[1268] sqlalchemy/trunk/lib/sqlalchemy/mods/__init__.py: install_mod can take strings or module items</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1268</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-06 19:25:58 -0500 (Thu, 06 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>install_mod can take strings or module items</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemymods__init__py">sqlalchemy/trunk/lib/sqlalchemy/mods/__init__.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemymods__init__py"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mods/__init__.py (1267 => 1268)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mods/__init__.py 2006-04-07 00:22:55 UTC (rev 1267) +++ sqlalchemy/trunk/lib/sqlalchemy/mods/__init__.py 2006-04-07 00:25:58 UTC (rev 1268) </span><span class="lines">@@ -1,4 +1,7 @@ </span><span class="cx"> def install_mods(*mods): </span><span class="cx"> for mod in mods: </span><del>- _mod = getattr(__import__('sqlalchemy.mods.%s' % mod).mods, mod) - _mod.install_plugin() </del><span class="cx">\ No newline at end of file </span><ins>+ if isinstance(mod, str): + _mod = getattr(__import__('sqlalchemy.mods.%s' % mod).mods, mod) + _mod.install_plugin() + else: + mod.install_plugin() </ins><span class="cx">\ No newline at end of file </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-07 00:23:07
|
<!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>[1267] sqlalchemy/trunk/lib/sqlalchemy/engine.py: pool argument adjusts for DBProxy/Pool</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1267</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-06 19:22:55 -0500 (Thu, 06 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>pool argument adjusts for DBProxy/Pool</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemyenginepy">sqlalchemy/trunk/lib/sqlalchemy/engine.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemyenginepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/engine.py (1266 => 1267)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/engine.py 2006-04-07 00:11:13 UTC (rev 1266) +++ sqlalchemy/trunk/lib/sqlalchemy/engine.py 2006-04-07 00:22:55 UTC (rev 1267) </span><span class="lines">@@ -47,9 +47,10 @@ </span><span class="cx"> In both cases, **kwargs represents options to be sent to the SQLEngine itself. A possibly </span><span class="cx"> partial listing of those options is as follows: </span><span class="cx"> </span><del>- pool=None : an instance of sqlalchemy.pool.DBProxy to be used as the underlying source - for connections (DBProxy is described in the previous section). If None, a default DBProxy - will be created using the engine's own database module with the given arguments. </del><ins>+ pool=None : an instance of sqlalchemy.pool.DBProxy or sqlalchemy.pool.Pool to be used as the + underlying source for connections (DBProxy/Pool is described in the previous section). If None, + a default DBProxy will be created using the engine's own database module with the given + arguments. </ins><span class="cx"> </span><span class="cx"> echo=False : if True, the SQLEngine will log all statements as well as a repr() of their </span><span class="cx"> parameter lists to the engines logger, which defaults to sys.stdout. A SQLEngine instances' </span><span class="lines">@@ -240,6 +241,8 @@ </span><span class="cx"> params['echo'] = echo_pool </span><span class="cx"> params['use_threadlocal'] = True </span><span class="cx"> self._pool = sqlalchemy.pool.manage(self.dbapi(), **params).get_pool(*cargs, **cparams) </span><ins>+ elif isinstance(pool, sqlalchemy.pool.DBProxy): + self._pool = pool.get_pool(*cargs, **cparams) </ins><span class="cx"> else: </span><span class="cx"> self._pool = pool </span><span class="cx"> self.default_ordering=default_ordering </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-07 00:11:26
|
<!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>[1266] sqlalchemy/trunk/lib/sqlalchemy: a little spring cleaning for the util package, etc</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1266</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-06 19:11:13 -0500 (Thu, 06 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>a little spring cleaning for the util package, etc</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemymappingquerypy">sqlalchemy/trunk/lib/sqlalchemy/mapping/query.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymappingsyncpy">sqlalchemy/trunk/lib/sqlalchemy/mapping/sync.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymappingutilpy">sqlalchemy/trunk/lib/sqlalchemy/mapping/util.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemyschemapy">sqlalchemy/trunk/lib/sqlalchemy/schema.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemysqlpy">sqlalchemy/trunk/lib/sqlalchemy/sql.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemyutilpy">sqlalchemy/trunk/lib/sqlalchemy/util.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemymappingquerypy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/query.py (1265 => 1266)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/query.py 2006-04-06 23:46:02 UTC (rev 1265) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/query.py 2006-04-07 00:11:13 UTC (rev 1266) </span><span class="lines">@@ -1,4 +1,10 @@ </span><ins>+# mapper/query.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 </ins><span class="cx"> </span><ins>+ </ins><span class="cx"> import objectstore </span><span class="cx"> import sqlalchemy.sql as sql </span><span class="cx"> import sqlalchemy.util as util </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingsyncpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/sync.py (1265 => 1266)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/sync.py 2006-04-06 23:46:02 UTC (rev 1265) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/sync.py 2006-04-07 00:11:13 UTC (rev 1266) </span><span class="lines">@@ -1,3 +1,11 @@ </span><ins>+# mapper/sync.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 + + + </ins><span class="cx"> import sqlalchemy.sql as sql </span><span class="cx"> import sqlalchemy.schema as schema </span><span class="cx"> from sqlalchemy.exceptions import * </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingutilpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/util.py (1265 => 1266)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/util.py 2006-04-06 23:46:02 UTC (rev 1265) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/util.py 2006-04-07 00:11:13 UTC (rev 1266) </span><span class="lines">@@ -1,3 +1,10 @@ </span><ins>+# mapper/util.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 + + </ins><span class="cx"> import sqlalchemy.sql as sql </span><span class="cx"> </span><span class="cx"> class TableFinder(sql.ClauseVisitor): </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyschemapy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/schema.py (1265 => 1266)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/schema.py 2006-04-06 23:46:02 UTC (rev 1265) +++ sqlalchemy/trunk/lib/sqlalchemy/schema.py 2006-04-07 00:11:13 UTC (rev 1266) </span><span class="lines">@@ -317,8 +317,8 @@ </span><span class="cx"> if len(kwargs): </span><span class="cx"> raise ArgumentError("Unknown arguments passed to Column: " + repr(kwargs.keys())) </span><span class="cx"> </span><del>- primary_key = AttrProp('_primary_key') - foreign_key = AttrProp('_foreign_key') </del><ins>+ primary_key = SimpleProperty('_primary_key') + foreign_key = SimpleProperty('_foreign_key') </ins><span class="cx"> original = property(lambda s: s._orig or s) </span><span class="cx"> parent = property(lambda s:s._parent or s) </span><span class="cx"> engine = property(lambda s: s.table.engine) </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemysqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/sql.py (1265 => 1266)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-04-06 23:46:02 UTC (rev 1265) +++ sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-04-07 00:11:13 UTC (rev 1266) </span><span class="lines">@@ -247,6 +247,7 @@ </span><span class="cx"> return isinstance(col, ColumnElement) </span><span class="cx"> </span><span class="cx"> class AbstractEngine(object): </span><ins>+ """represents a 'thing that can produce Compiler objects an execute them'.""" </ins><span class="cx"> def execute_compiled(self, compiled, parameters, echo=None, **kwargs): </span><span class="cx"> raise NotImplementedError() </span><span class="cx"> def compiler(self, statement, parameters, **kwargs): </span><span class="lines">@@ -343,7 +344,7 @@ </span><span class="cx"> self.after_compile() </span><span class="cx"> </span><span class="cx"> def execute(self, *multiparams, **params): </span><del>- """executes this compiled object using the underlying SQLEngine""" </del><ins>+ """executes this compiled object using the AbstractEngine it is bound to.""" </ins><span class="cx"> if len(multiparams): </span><span class="cx"> params = multiparams </span><span class="cx"> </span><span class="lines">@@ -1350,8 +1351,6 @@ </span><span class="cx"> else: </span><span class="cx"> setattr(self, attribute, condition) </span><span class="cx"> </span><del>- _hash_recursion = util.RecursionStack() - </del><span class="cx"> def clear_from(self, from_obj): </span><span class="cx"> self._froms[from_obj] = FromClause(from_name = None) </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyutilpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/util.py (1265 => 1266)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/util.py 2006-04-06 23:46:02 UTC (rev 1265) +++ sqlalchemy/trunk/lib/sqlalchemy/util.py 2006-04-07 00:11:13 UTC (rev 1266) </span><span class="lines">@@ -4,7 +4,6 @@ </span><span class="cx"> # This module is part of SQLAlchemy and is released under </span><span class="cx"> # the MIT License: http://www.opensource.org/licenses/mit-license.php </span><span class="cx"> </span><del>-__all__ = ['OrderedProperties', 'OrderedDict', 'generic_repr', 'HashSet', 'AttrProp'] </del><span class="cx"> import thread, threading, weakref, UserList, time, string, inspect, sys </span><span class="cx"> from exceptions import * </span><span class="cx"> import __builtin__ </span><span class="lines">@@ -36,9 +35,9 @@ </span><span class="cx"> i -= 1 </span><span class="cx"> raise StopIteration() </span><span class="cx"> return rev() </span><del>- -class AttrProp(object): - """a quick way to stick a property accessor on an object""" </del><ins>+ +class SimpleProperty(object): + """a "default" property accessor.""" </ins><span class="cx"> def __init__(self, key): </span><span class="cx"> self.key = key </span><span class="cx"> def __set__(self, obj, value): </span><span class="lines">@@ -50,21 +49,7 @@ </span><span class="cx"> return self </span><span class="cx"> else: </span><span class="cx"> return getattr(obj, self.key) </span><del>- -def generic_repr(obj, exclude=None): - L = ['%s=%s' % (a, repr(getattr(obj, a))) for a in dir(obj) if not callable(getattr(obj, a)) and not a.startswith('_') and (exclude is None or not exclude.has_key(a))] - return '%s(%s)' % (obj.__class__.__name__, ','.join(L)) </del><span class="cx"> </span><del>-def hash_key(obj): - if obj is None: - return 'None' - elif isinstance(obj, list): - return repr([hash_key(o) for o in obj]) - elif hasattr(obj, 'hash_key'): - return obj.hash_key() - else: - return repr(obj) - </del><span class="cx"> class Logger(object): </span><span class="cx"> """defines various forms of logging""" </span><span class="cx"> def __init__(self, logger=None, usethreads=False, usetimestamp=True, origin=None): </span><span class="lines">@@ -130,30 +115,6 @@ </span><span class="cx"> def clear(self): </span><span class="cx"> self.__dict__.clear() </span><span class="cx"> self.__dict__['_list'] = [] </span><del>- -class RecursionStack(object): - """a thread-local stack used to detect recursive object traversals.""" - def __init__(self): - self.stacks = {} - def _get_stack(self): - try: - stack = self.stacks[thread.get_ident()] - except KeyError: - stack = {} - self.stacks[thread.get_ident()] = stack - return stack - def push(self, obj): - s = self._get_stack() - if s.has_key(obj): - return True - else: - s[obj] = True - return False - def pop(self, obj): - stack = self._get_stack() - del stack[obj] - if len(stack) == 0: - del self.stacks[thread.get_ident()] </del><span class="cx"> </span><span class="cx"> class OrderedDict(dict): </span><span class="cx"> """A Dictionary that keeps its own internal ordering""" </span><span class="lines">@@ -163,52 +124,40 @@ </span><span class="cx"> if values is not None: </span><span class="cx"> for val in values: </span><span class="cx"> self.update(val) </span><del>- </del><span class="cx"> def keys(self): </span><span class="cx"> return list(self._list) </span><del>- </del><span class="cx"> def clear(self): </span><span class="cx"> self._list = [] </span><span class="cx"> dict.clear(self) </span><del>- </del><span class="cx"> def update(self, dict): </span><span class="cx"> for key in dict.keys(): </span><span class="cx"> self.__setitem__(key, dict[key]) </span><del>- </del><span class="cx"> def setdefault(self, key, value): </span><span class="cx"> if not self.has_key(key): </span><span class="cx"> self.__setitem__(key, value) </span><span class="cx"> return value </span><span class="cx"> else: </span><span class="cx"> return self.__getitem__(key) </span><del>- </del><span class="cx"> def values(self): </span><span class="cx"> return map(lambda key: self[key], self._list) </span><del>- </del><span class="cx"> def __iter__(self): </span><span class="cx"> return iter(self._list) </span><del>- </del><span class="cx"> def itervalues(self): </span><span class="cx"> return iter([self[key] for key in self._list]) </span><del>- </del><span class="cx"> def iterkeys(self): </span><span class="cx"> return self.__iter__() </span><del>- </del><span class="cx"> def iteritems(self): </span><span class="cx"> return iter([(key, self[key]) for key in self.keys()]) </span><del>- </del><span class="cx"> def __delitem__(self, key): </span><span class="cx"> try: </span><span class="cx"> del self._list[self._list.index(key)] </span><span class="cx"> except ValueError: </span><span class="cx"> raise KeyError(key) </span><span class="cx"> dict.__delitem__(self, key) </span><del>- </del><span class="cx"> def __setitem__(self, key, object): </span><span class="cx"> if not self.has_key(key): </span><span class="cx"> self._list.append(key) </span><span class="cx"> dict.__setitem__(self, key, object) </span><del>- </del><span class="cx"> def __getitem__(self, key): </span><span class="cx"> return dict.__getitem__(self, key) </span><span class="cx"> </span><span class="lines">@@ -230,6 +179,7 @@ </span><span class="cx"> self._tdict["%d_%s" % (thread.get_ident(), key)] = value </span><span class="cx"> </span><span class="cx"> class DictDecorator(dict): </span><ins>+ """a Dictionary that delegates items not found to a second wrapped dictionary.""" </ins><span class="cx"> def __init__(self, decorate): </span><span class="cx"> self.decorate = decorate </span><span class="cx"> def __getitem__(self, key): </span><span class="lines">@@ -239,8 +189,9 @@ </span><span class="cx"> return self.decorate[key] </span><span class="cx"> def __repr__(self): </span><span class="cx"> return dict.__repr__(self) + repr(self.decorate) </span><ins>+ </ins><span class="cx"> class HashSet(object): </span><del>- """implements a Set.""" </del><ins>+ """implements a Set, including ordering capability""" </ins><span class="cx"> def __init__(self, iter=None, ordered=False): </span><span class="cx"> if ordered: </span><span class="cx"> self.map = OrderedDict() </span><span class="lines">@@ -474,6 +425,12 @@ </span><span class="cx"> </span><span class="cx"> </span><span class="cx"> def constructor_args(instance, **kwargs): </span><ins>+ """given an object instance and keyword arguments, inspects the + argument signature of the instance's __init__ method and returns + a tuple of list and keyword arguments, suitable for creating a new + instance of the class. The returned arguments are drawn from the + given keyword dictionary, or if not found are drawn from the + corresponding attributes of the original instance.""" </ins><span class="cx"> classobj = instance.__class__ </span><span class="cx"> </span><span class="cx"> argspec = inspect.getargspec(classobj.__init__.im_func) </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-06 23:46:15
|
<!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>[1265] sqlalchemy/trunk/lib/sqlalchemy: split up Session into Session/LegacySession, added some new constructor args</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1265</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-06 18:46:02 -0500 (Thu, 06 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>split up Session into Session/LegacySession, added some new constructor args created AbstractEngine class which provides base for SQLEngine and will also provide base for ConnectionProxy, so SQL binding can be to an engine or specific connection resource ClauseElements get using() method which can take AbstractEngines for execution made more separation between SchemaItems and bound engine</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemyextproxypy">sqlalchemy/trunk/lib/sqlalchemy/ext/proxy.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymappingobjectstorepy">sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymappingquerypy">sqlalchemy/trunk/lib/sqlalchemy/mapping/query.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemyschemapy">sqlalchemy/trunk/lib/sqlalchemy/schema.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemysqlpy">sqlalchemy/trunk/lib/sqlalchemy/sql.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemyextproxypy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/ext/proxy.py (1264 => 1265)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/ext/proxy.py 2006-04-06 21:12:00 UTC (rev 1264) +++ sqlalchemy/trunk/lib/sqlalchemy/ext/proxy.py 2006-04-06 23:46:02 UTC (rev 1265) </span><span class="lines">@@ -24,7 +24,15 @@ </span><span class="cx"> </span><span class="cx"> def reflecttable(self, table): </span><span class="cx"> return self.get_engine().reflecttable(table) </span><del>- </del><ins>+ def execute_compiled(self, *args, **kwargs): + return self.get_engine().execute_compiled(*args, **kwargs) + def compiler(self, *args, **kwargs): + return self.get_engine().compiler(*args, **kwargs) + def schemagenerator(self, *args, **kwargs): + return self.get_engine().schemagenerator(*args, **kwargs) + def schemadropper(self, *args, **kwargs): + return self.get_engine().schemadropper(*args, **kwargs) + </ins><span class="cx"> def hash_key(self): </span><span class="cx"> return "%s(%s)" % (self.__class__.__name__, id(self)) </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingobjectstorepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py (1264 => 1265)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py 2006-04-06 21:12:00 UTC (rev 1264) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py 2006-04-06 23:46:02 UTC (rev 1265) </span><span class="lines">@@ -17,7 +17,7 @@ </span><span class="cx"> class Session(object): </span><span class="cx"> """Maintains a UnitOfWork instance, including transaction state.""" </span><span class="cx"> </span><del>- def __init__(self, nest_on=None, hash_key=None): </del><ins>+ def __init__(self, hash_key=None, new_imap=True, import_session=None): </ins><span class="cx"> """Initialize the objectstore with a UnitOfWork registry. If called </span><span class="cx"> with no arguments, creates a single UnitOfWork for all operations. </span><span class="cx"> </span><span class="lines">@@ -26,31 +26,23 @@ </span><span class="cx"> hash_key - the hash_key used to identify objects against this session, which </span><span class="cx"> defaults to the id of the Session instance. </span><span class="cx"> """ </span><del>- self.uow = unitofwork.UnitOfWork() - self.parent_uow = None - self.begin_count = 0 - self.nest_on = util.to_list(nest_on) - self.__pushed_count = 0 </del><ins>+ if import_session is not None: + self.uow = unitofwork.UnitOfWork(identity_map=import_session.uow.identity_map) + elif new_imap is False: + self.uow = unitofwork.UnitOfWork(identity_map=objectstore.get_session().uow.identity_map) + else: + self.uow = unitofwork.UnitOfWork() + + self.binds = {} </ins><span class="cx"> if hash_key is None: </span><span class="cx"> self.hash_key = id(self) </span><span class="cx"> else: </span><span class="cx"> self.hash_key = hash_key </span><span class="cx"> _sessions[self.hash_key] = self </span><span class="cx"> </span><del>- def was_pushed(self): - if self.nest_on is None: - return - self.__pushed_count += 1 - if self.__pushed_count == 1: - for n in self.nest_on: - n.push_session() - def was_popped(self): - if self.nest_on is None or self.__pushed_count == 0: - return - self.__pushed_count -= 1 - if self.__pushed_count == 0: - for n in self.nest_on: - n.pop_session() </del><ins>+ def bind_table(self, table, bindto): + self.binds[table] = bindto + </ins><span class="cx"> def get_id_key(ident, class_, entity_name=None): </span><span class="cx"> """returns an identity-map key for use in storing/retrieving an item from the identity </span><span class="cx"> map, given a tuple of the object's primary key values. </span><span class="lines">@@ -81,79 +73,12 @@ </span><span class="cx"> """ </span><span class="cx"> return (class_, tuple([row[column] for column in primary_key]), entity_name) </span><span class="cx"> get_row_key = staticmethod(get_row_key) </span><del>- - class SessionTrans(object): - """returned by Session.begin(), denotes a transactionalized UnitOfWork instance. - call commit() on this to commit the transaction.""" - def __init__(self, parent, uow, isactive): - self.__parent = parent - self.__isactive = isactive - self.__uow = uow - isactive = property(lambda s:s.__isactive, doc="True if this SessionTrans is the 'active' transaction marker, else its a no-op.") - parent = property(lambda s:s.__parent, doc="returns the parent Session of this SessionTrans object.") - uow = property(lambda s:s.__uow, doc="returns the parent UnitOfWork corresponding to this transaction.") - def begin(self): - """calls begin() on the underlying Session object, returning a new no-op SessionTrans object.""" - if self.parent.uow is not self.uow: - raise InvalidRequestError("This SessionTrans is no longer valid") - return self.parent.begin() - def commit(self): - """commits the transaction noted by this SessionTrans object.""" - self.__parent._trans_commit(self) - self.__isactive = False - def rollback(self): - """rolls back the current UnitOfWork transaction, in the case that begin() - has been called. The changes logged since the begin() call are discarded.""" - self.__parent._trans_rollback(self) - self.__isactive = False - - def begin(self): - """begins a new UnitOfWork transaction and returns a tranasaction-holding - object. commit() or rollback() should be called on the returned object. - commit() on the Session will do nothing while a transaction is pending, and further - calls to begin() will return no-op transactional objects.""" - if self.parent_uow is not None: - return Session.SessionTrans(self, self.uow, False) - self.parent_uow = self.uow - self.uow = unitofwork.UnitOfWork(identity_map = self.uow.identity_map) - return Session.SessionTrans(self, self.uow, True) </del><span class="cx"> </span><span class="cx"> def engines(self, mapper): </span><span class="cx"> return [t.engine for t in mapper.tables] </span><span class="cx"> </span><del>- def _trans_commit(self, trans): - if trans.uow is self.uow and trans.isactive: - try: - self._commit_uow() - finally: - self.uow = self.parent_uow - self.parent_uow = None - def _trans_rollback(self, trans): - if trans.uow is self.uow: - self.uow = self.parent_uow - self.parent_uow = None - - def _commit_uow(self, *obj): - self.was_pushed() - try: - self.uow.flush(self, *obj) - finally: - self.was_popped() - - def commit(self, *objects): - """commits the current UnitOfWork transaction. called with - no arguments, this is only used - for "implicit" transactions when there was no begin(). - if individual objects are submitted, then only those objects are committed, and the - begin/commit cycle is not affected.""" - # if an object list is given, commit just those but dont - # change begin/commit status - if len(objects): - self._commit_uow(*objects) - self.uow.flush(self, *objects) - return - if self.parent_uow is None: - self._commit_uow() </del><ins>+ def flush(self, *obj): + self.uow.flush(self, *obj) </ins><span class="cx"> </span><span class="cx"> def refresh(self, *obj): </span><span class="cx"> """reloads the attributes for the given objects from the database, clears </span><span class="lines">@@ -221,6 +146,95 @@ </span><span class="cx"> u.register_new(instance) </span><span class="cx"> return instance </span><span class="cx"> </span><ins>+class LegacySession(Session): + def __init__(self, nest_on=None, hash_key=None, **kwargs): + super(LegacySession, self).__init__(**kwargs) + self.parent_uow = None + self.begin_count = 0 + self.nest_on = util.to_list(nest_on) + self.__pushed_count = 0 + def was_pushed(self): + if self.nest_on is None: + return + self.__pushed_count += 1 + if self.__pushed_count == 1: + for n in self.nest_on: + n.push_session() + def was_popped(self): + if self.nest_on is None or self.__pushed_count == 0: + return + self.__pushed_count -= 1 + if self.__pushed_count == 0: + for n in self.nest_on: + n.pop_session() + class SessionTrans(object): + """returned by Session.begin(), denotes a transactionalized UnitOfWork instance. + call commit() on this to commit the transaction.""" + def __init__(self, parent, uow, isactive): + self.__parent = parent + self.__isactive = isactive + self.__uow = uow + isactive = property(lambda s:s.__isactive, doc="True if this SessionTrans is the 'active' transaction marker, else its a no-op.") + parent = property(lambda s:s.__parent, doc="returns the parent Session of this SessionTrans object.") + uow = property(lambda s:s.__uow, doc="returns the parent UnitOfWork corresponding to this transaction.") + def begin(self): + """calls begin() on the underlying Session object, returning a new no-op SessionTrans object.""" + if self.parent.uow is not self.uow: + raise InvalidRequestError("This SessionTrans is no longer valid") + return self.parent.begin() + def commit(self): + """commits the transaction noted by this SessionTrans object.""" + self.__parent._trans_commit(self) + self.__isactive = False + def rollback(self): + """rolls back the current UnitOfWork transaction, in the case that begin() + has been called. The changes logged since the begin() call are discarded.""" + self.__parent._trans_rollback(self) + self.__isactive = False + def begin(self): + """begins a new UnitOfWork transaction and returns a tranasaction-holding + object. commit() or rollback() should be called on the returned object. + commit() on the Session will do nothing while a transaction is pending, and further + calls to begin() will return no-op transactional objects.""" + if self.parent_uow is not None: + return Session.SessionTrans(self, self.uow, False) + self.parent_uow = self.uow + self.uow = unitofwork.UnitOfWork(identity_map = self.uow.identity_map) + return Session.SessionTrans(self, self.uow, True) + def commit(self, *objects): + """commits the current UnitOfWork transaction. called with + no arguments, this is only used + for "implicit" transactions when there was no begin(). + if individual objects are submitted, then only those objects are committed, and the + begin/commit cycle is not affected.""" + # if an object list is given, commit just those but dont + # change begin/commit status + if len(objects): + self._commit_uow(*objects) + self.uow.flush(self, *objects) + return + if self.parent_uow is None: + self._commit_uow() + def _trans_commit(self, trans): + if trans.uow is self.uow and trans.isactive: + try: + self._commit_uow() + finally: + self.uow = self.parent_uow + self.parent_uow = None + def _trans_rollback(self, trans): + if trans.uow is self.uow: + self.uow = self.parent_uow + self.parent_uow = None + def _commit_uow(self, *obj): + self.was_pushed() + try: + self.uow.flush(self, *obj) + finally: + self.was_popped() + +Session = LegacySession + </ins><span class="cx"> def get_id_key(ident, class_, entity_name=None): </span><span class="cx"> return Session.get_id_key(ident, class_, entity_name) </span><span class="cx"> </span><span class="lines">@@ -228,19 +242,22 @@ </span><span class="cx"> return Session.get_row_key(row, class_, primary_key, entity_name) </span><span class="cx"> </span><span class="cx"> def begin(): </span><del>- """begins a new UnitOfWork transaction. the next commit will affect only - objects that are created, modified, or deleted following the begin statement.""" </del><ins>+ """deprecated. use s = Session(new_imap=False).""" </ins><span class="cx"> return get_session().begin() </span><span class="cx"> </span><span class="cx"> def commit(*obj): </span><del>- """commits the current UnitOfWork transaction. if a transaction was begun - via begin(), commits only those objects that were created, modified, or deleted - since that begin statement. otherwise commits all objects that have been </del><ins>+ """deprecated; use flush(*obj)""" + get_session().flush(*obj) + +def flush(*obj): + """flushes the current UnitOfWork transaction. if a transaction was begun + via begin(), flushes only those objects that were created, modified, or deleted + since that begin statement. otherwise flushes all objects that have been </ins><span class="cx"> changed. </span><del>- </del><ins>+ </ins><span class="cx"> if individual objects are submitted, then only those objects are committed, and the </span><span class="cx"> begin/commit cycle is not affected.""" </span><del>- get_session().commit(*obj) </del><ins>+ get_session().flush(*obj) </ins><span class="cx"> </span><span class="cx"> def clear(): </span><span class="cx"> """removes all current UnitOfWorks and IdentityMaps for this thread and </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingquerypy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/query.py (1264 => 1265)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/query.py 2006-04-06 21:12:00 UTC (rev 1264) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/query.py 2006-04-06 23:46:02 UTC (rev 1265) </span><span class="lines">@@ -10,6 +10,7 @@ </span><span class="cx"> self.mapper = mapper </span><span class="cx"> self.always_refresh = kwargs.pop('always_refresh', self.mapper.always_refresh) </span><span class="cx"> self.order_by = kwargs.pop('order_by', self.mapper.order_by) </span><ins>+ self.extension = kwargs.pop('extension', self.mapper.extension) </ins><span class="cx"> self._session = kwargs.pop('session', None) </span><span class="cx"> if not hasattr(mapper, '_get_clause'): </span><span class="cx"> _get_clause = sql.and_() </span><span class="lines">@@ -66,7 +67,7 @@ </span><span class="cx"> </span><span class="cx"> e.g. result = usermapper.select_by(user_name = 'fred') </span><span class="cx"> """ </span><del>- ret = self.mapper.extension.select_by(self, *args, **params) </del><ins>+ ret = self.extension.select_by(self, *args, **params) </ins><span class="cx"> if ret is not mapper.EXT_PASS: </span><span class="cx"> return ret </span><span class="cx"> return self.select_whereclause(self._by_clause(*args, **params)) </span><span class="lines">@@ -116,7 +117,7 @@ </span><span class="cx"> in this case, the developer must insure that an adequate set of columns exists in the </span><span class="cx"> rowset with which to build new object instances.""" </span><span class="cx"> </span><del>- ret = self.mapper.extension.select(self, arg=arg, **kwargs) </del><ins>+ ret = self.extension.select(self, arg=arg, **kwargs) </ins><span class="cx"> if ret is not mapper.EXT_PASS: </span><span class="cx"> return ret </span><span class="cx"> elif arg is not None and isinstance(arg, sql.Selectable): </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyschemapy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/schema.py (1264 => 1265)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/schema.py 2006-04-06 21:12:00 UTC (rev 1264) +++ sqlalchemy/trunk/lib/sqlalchemy/schema.py 2006-04-06 23:46:02 UTC (rev 1265) </span><span class="lines">@@ -23,8 +23,17 @@ </span><span class="cx"> __all__ = ['SchemaItem', 'Table', 'Column', 'ForeignKey', 'Sequence', 'Index', </span><span class="cx"> 'SchemaEngine', 'SchemaVisitor', 'PassiveDefault', 'ColumnDefault'] </span><span class="cx"> </span><ins>+class SchemaMeta(type): + """provides universal constructor arguments for all SchemaItems""" + def __call__(self, *args, **kwargs): + engine = kwargs.pop('engine', None) + obj = type.__call__(self, *args, **kwargs) + obj._engine = engine + return obj + </ins><span class="cx"> class SchemaItem(object): </span><span class="cx"> """base class for items that define a database schema.""" </span><ins>+ __metaclass__ = SchemaMeta </ins><span class="cx"> def _init_items(self, *args): </span><span class="cx"> for item in args: </span><span class="cx"> if item is not None: </span><span class="lines">@@ -34,7 +43,20 @@ </span><span class="cx"> raise NotImplementedError() </span><span class="cx"> def __repr__(self): </span><span class="cx"> return "%s()" % self.__class__.__name__ </span><del>- </del><ins>+ +class EngineMixin(object): + """a mixin for SchemaItems that provides an "engine" accessor.""" + def _derived_engine(self): + """subclasses override this method to return an AbstractEngine + bound to a parent item""" + return None + def _get_engine(self): + if self._engine is not None: + return self._engine + else: + return self._derived_engine() + engine = property(_get_engine) + </ins><span class="cx"> def _get_table_key(engine, name, schema): </span><span class="cx"> if schema is not None and schema == engine.get_default_schema_name(): </span><span class="cx"> schema = None </span><span class="lines">@@ -43,14 +65,12 @@ </span><span class="cx"> else: </span><span class="cx"> return schema + "." + name </span><span class="cx"> </span><del>-class TableSingleton(type): </del><ins>+class TableSingleton(SchemaMeta): </ins><span class="cx"> """a metaclass used by the Table object to provide singleton behavior.""" </span><span class="cx"> def __call__(self, name, engine=None, *args, **kwargs): </span><span class="cx"> try: </span><del>- if not isinstance(engine, SchemaEngine): </del><ins>+ if engine is not None and not isinstance(engine, SchemaEngine): </ins><span class="cx"> args = [engine] + list(args) </span><del>- engine = None - if engine is None: </del><span class="cx"> engine = default_engine </span><span class="cx"> name = str(name) # in case of incoming unicode </span><span class="cx"> schema = kwargs.get('schema', None) </span><span class="lines">@@ -58,6 +78,10 @@ </span><span class="cx"> redefine = kwargs.pop('redefine', False) </span><span class="cx"> mustexist = kwargs.pop('mustexist', False) </span><span class="cx"> useexisting = kwargs.pop('useexisting', False) </span><ins>+ if not engine: + table = type.__call__(self, name, engine, **kwargs) + table._init_items(*args) + return table </ins><span class="cx"> key = _get_table_key(engine, name, schema) </span><span class="cx"> table = engine.tables[key] </span><span class="cx"> if len(args): </span><span class="lines">@@ -440,15 +464,14 @@ </span><span class="cx"> self.parent.foreign_key = self </span><span class="cx"> self.parent.table.foreign_keys.append(self) </span><span class="cx"> </span><del>-class DefaultGenerator(SchemaItem): </del><ins>+class DefaultGenerator(SchemaItem, EngineMixin): </ins><span class="cx"> """Base class for column "default" values.""" </span><del>- def __init__(self, for_update=False, engine=None): </del><ins>+ def __init__(self, for_update=False): </ins><span class="cx"> self.for_update = for_update </span><del>- self.engine = engine </del><ins>+ def _derived_engine(self): + return self.column.table.engine </ins><span class="cx"> def _set_parent(self, column): </span><span class="cx"> self.column = column </span><del>- if self.engine is None: - self.engine = column.table.engine </del><span class="cx"> if self.for_update: </span><span class="cx"> self.column.onupdate = self </span><span class="cx"> else: </span><span class="lines">@@ -509,7 +532,7 @@ </span><span class="cx"> return visitor.visit_sequence(self) </span><span class="cx"> </span><span class="cx"> </span><del>-class Index(SchemaItem): </del><ins>+class Index(SchemaItem, EngineMixin): </ins><span class="cx"> """Represents an index of columns from a database table </span><span class="cx"> """ </span><span class="cx"> def __init__(self, name, *columns, **kw): </span><span class="lines">@@ -530,7 +553,8 @@ </span><span class="cx"> self.unique = kw.pop('unique', False) </span><span class="cx"> self._init_items(*columns) </span><span class="cx"> </span><del>- engine = property(lambda s:s.table.engine) </del><ins>+ def _derived_engine(self): + return self.table.engine </ins><span class="cx"> def _init_items(self, *args): </span><span class="cx"> for column in args: </span><span class="cx"> self.append_column(column) </span><span class="lines">@@ -570,18 +594,21 @@ </span><span class="cx"> for c in self.columns]), </span><span class="cx"> (self.unique and ', unique=True') or '') </span><span class="cx"> </span><del>-class SchemaEngine(object): </del><ins>+class SchemaEngine(sql.AbstractEngine): </ins><span class="cx"> """a factory object used to create implementations for schema objects. This object </span><span class="cx"> is the ultimate base class for the engine.SQLEngine class.""" </span><span class="cx"> </span><span class="cx"> def __init__(self): </span><span class="cx"> # a dictionary that stores Table objects keyed off their name (and possibly schema name) </span><span class="cx"> self.tables = {} </span><del>- </del><span class="cx"> def reflecttable(self, table): </span><span class="cx"> """given a table, will query the database and populate its Column and ForeignKey </span><span class="cx"> objects.""" </span><span class="cx"> raise NotImplementedError() </span><ins>+ def schemagenerator(self, **params): + raise NotImplementedError() + def schemadropper(self, **params): + raise NotImplementedError() </ins><span class="cx"> </span><span class="cx"> class SchemaVisitor(sql.ClauseVisitor): </span><span class="cx"> """defines the visiting for SchemaItem objects""" </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemysqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/sql.py (1264 => 1265)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-04-06 21:12:00 UTC (rev 1264) +++ sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-04-06 23:46:02 UTC (rev 1265) </span><span class="lines">@@ -246,6 +246,12 @@ </span><span class="cx"> def is_column(col): </span><span class="cx"> return isinstance(col, ColumnElement) </span><span class="cx"> </span><ins>+class AbstractEngine(object): + def execute_compiled(self, compiled, parameters, echo=None, **kwargs): + raise NotImplementedError() + def compiler(self, statement, parameters, **kwargs): + raise NotImplementedError() + </ins><span class="cx"> class ClauseParameters(util.OrderedDict): </span><span class="cx"> """represents a dictionary/iterator of bind parameter key names/values. Includes parameters compiled with a Compiled object as well as additional arguments passed to the Compiled object's get_params() method. Parameter values will be converted as per the TypeEngine objects present in the bind parameter objects. The non-converted value can be retrieved via the get_original method. For Compiled objects that compile positional parameters, the values() iteration of the object will return the parameter values in the correct order.""" </span><span class="cx"> def __init__(self, engine=None): </span><span class="lines">@@ -340,8 +346,11 @@ </span><span class="cx"> """executes this compiled object using the underlying SQLEngine""" </span><span class="cx"> if len(multiparams): </span><span class="cx"> params = multiparams </span><del>- - return self.engine.execute_compiled(self, params) </del><ins>+ + e = self.engine + if e is None: + raise InvalidRequestError("This Compiled object is not bound to any engine.") + return e.execute_compiled(self, params) </ins><span class="cx"> </span><span class="cx"> def scalar(self, *multiparams, **params): </span><span class="cx"> """executes this compiled object via the execute() method, then </span><span class="lines">@@ -356,7 +365,26 @@ </span><span class="cx"> return row[0] </span><span class="cx"> else: </span><span class="cx"> return None </span><del>- </del><ins>+ +class Executor(object): + """handles the compilation/execution of a ClauseElement within the context of a particular AbtractEngine. This + AbstractEngine will usually be a SQLEngine or ConnectionProxy.""" + def __init__(self, clauseelement, abstractengine=None): + self.engine=abstractengine + self.clauseelement = clauseelement + def execute(self, *multiparams, **params): + return self.compile(*multiparams, **params).execute(*multiparams, **params) + def scalar(self, *multiparams, **params): + return self.compile(*multiparams, **params).scalar(*multiparams, **params) + def compile(self, *multiparams, **params): + if len(multiparams): + bindparams = multiparams[0] + else: + bindparams = params + compiler = self.engine.compiler(self.clauseelement, bindparams) + compiler.compile() + return compiler + </ins><span class="cx"> class ClauseElement(object): </span><span class="cx"> """base class for elements of a programmatically constructed SQL expression.""" </span><span class="cx"> def _get_from_objects(self): </span><span class="lines">@@ -415,10 +443,12 @@ </span><span class="cx"> </span><span class="cx"> engine = property(lambda s: s._find_engine(), doc="attempts to locate a SQLEngine within this ClauseElement structure, or returns None if none found.") </span><span class="cx"> </span><del>- </del><ins>+ def using(self, abstractengine): + return Executor(self, abstractengine) + </ins><span class="cx"> def compile(self, engine = None, parameters = None, typemap=None, compiler=None): </span><span class="cx"> """compiles this SQL expression using its underlying SQLEngine to produce </span><del>- a Compiled object. If no engine can be found, an ansisql engine is used. </del><ins>+ a Compiled object. If no engine can be found, an ANSICompiler is used with no engine. </ins><span class="cx"> bindparams is a dictionary representing the default bind parameters to be used with </span><span class="cx"> the statement. """ </span><span class="cx"> </span><span class="lines">@@ -430,7 +460,7 @@ </span><span class="cx"> </span><span class="cx"> if compiler is None: </span><span class="cx"> import sqlalchemy.ansisql as ansisql </span><del>- compiler = ansisql.ANSICompiler(self, parameters=parameters, typemap=typemap) </del><ins>+ compiler = ansisql.ANSICompiler(self, parameters=parameters) </ins><span class="cx"> compiler.compile() </span><span class="cx"> return compiler </span><span class="cx"> </span><span class="lines">@@ -438,30 +468,10 @@ </span><span class="cx"> return str(self.compile()) </span><span class="cx"> </span><span class="cx"> def execute(self, *multiparams, **params): </span><del>- """compiles and executes this SQL expression using its underlying SQLEngine. the - given **params are used as bind parameters when compiling and executing the - expression. the DBAPI cursor object is returned.""" - e = self.engine - if len(multiparams): - bindparams = multiparams[0] - else: - bindparams = params - c = self.compile(e, parameters=bindparams) - return c.execute(*multiparams, **params) </del><ins>+ return self.using(self.engine).execute(*multiparams, **params) </ins><span class="cx"> </span><span class="cx"> def scalar(self, *multiparams, **params): </span><del>- """executes this SQL expression via the execute() method, then - returns the first column of the first row. Useful for executing functions, - sequences, rowcounts, etc.""" - # we are still going off the assumption that fetching only the first row - # in a result set is not performance-wise any different than specifying limit=1 - # else we'd have to construct a copy of the select() object with the limit - # installed (else if we change the existing select, not threadsafe) - row = self.execute(*multiparams, **params).fetchone() - if row is not None: - return row[0] - else: - return None </del><ins>+ return self.using(self.engine).scalar(*multiparams, **params) </ins><span class="cx"> </span><span class="cx"> def __and__(self, other): </span><span class="cx"> return and_(self, other) </span></span></pre> </div> </div> </body> </html> |
<!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>[1264] sqlalchemy/trunk/test: mapper's querying facilities migrated to new query.Query() object, which can receive session-specific context via the mapper.using() statement.</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1264</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-06 16:12:00 -0500 (Thu, 06 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>mapper's querying facilities migrated to new query.Query() object, which can receive session-specific context via the mapper.using() statement. reuslting object instances will be bound to this session, but query execution still handled by the SQLEngines implicit in the mapper's Table objects. session now propigates to the unitofwork UOWTransaction object, as well as mapper's save_obj/delete_obj via the UOWTransaction it receives. UOWTransaction explicitly calls the Session for the engine corresponding to each Mapper in the flush operation, although the Session does not yet affect the choice of engines used, and mapper save/delete is still using the Table's implicit SQLEngine. changed internal unitofwork commit() method to be called flush(). removed all references to 'engine' from mapper module, including adding insert/update specific SQLEngine methods such as last_inserted_ids, last_inserted_params, etc. to the returned ResultProxy so that Mapper need not know which SQLEngine was used for the execute. changes to unit tests, SelectResults to support the new Query object.</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkdocbuildcontentdocstringsmyt">sqlalchemy/trunk/doc/build/content/docstrings.myt</a></li> <li><a href="#sqlalchemytrunklibsqlalchemy__init__py">sqlalchemy/trunk/lib/sqlalchemy/__init__.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemyenginepy">sqlalchemy/trunk/lib/sqlalchemy/engine.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymappingmapperpy">sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymappingobjectstorepy">sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymappingpropertiespy">sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymappingunitofworkpy">sqlalchemy/trunk/lib/sqlalchemy/mapping/unitofwork.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymodsselectresultspy">sqlalchemy/trunk/lib/sqlalchemy/mods/selectresults.py</a></li> <li><a href="#sqlalchemytrunktestmapperpy">sqlalchemy/trunk/test/mapper.py</a></li> <li><a href="#sqlalchemytrunktestobjectstorepy">sqlalchemy/trunk/test/objectstore.py</a></li> </ul> <h3>Added Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemymappingquerypy">sqlalchemy/trunk/lib/sqlalchemy/mapping/query.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkdocbuildcontentdocstringsmyt"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/doc/build/content/docstrings.myt (1263 => 1264)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/doc/build/content/docstrings.myt 2006-04-06 19:25:58 UTC (rev 1263) +++ sqlalchemy/trunk/doc/build/content/docstrings.myt 2006-04-06 21:12:00 UTC (rev 1264) </span><span class="lines">@@ -17,6 +17,7 @@ </span><span class="cx"> <& pydoc.myt:obj_doc, obj=sql, classes=[sql.ClauseParameters, sql.Compiled, sql.ClauseElement, sql.TableClause, sql.ColumnClause] &> </span><span class="cx"> <& pydoc.myt:obj_doc, obj=pool, classes=[pool.DBProxy, pool.Pool, pool.QueuePool, pool.SingletonThreadPool] &> </span><span class="cx"> <& pydoc.myt:obj_doc, obj=mapping, classes=[mapping.Mapper, mapping.MapperExtension] &> </span><ins>+<& pydoc.myt:obj_doc, obj=mapping.query, classes=[mapping.query.Query] &> </ins><span class="cx"> <& pydoc.myt:obj_doc, obj=mapping.objectstore, classes=[mapping.objectstore.Session, mapping.objectstore.Session.SessionTrans] &> </span><span class="cx"> <& pydoc.myt:obj_doc, obj=exceptions &> </span><span class="cx"> <& pydoc.myt:obj_doc, obj=proxy &> </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemy__init__py"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/__init__.py (1263 => 1264)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/__init__.py 2006-04-06 19:25:58 UTC (rev 1263) +++ sqlalchemy/trunk/lib/sqlalchemy/__init__.py 2006-04-06 21:12:00 UTC (rev 1264) </span><span class="lines">@@ -9,8 +9,9 @@ </span><span class="cx"> from sql import * </span><span class="cx"> from schema import * </span><span class="cx"> from exceptions import * </span><del>-import mapping as mapperlib -from mapping import * </del><ins>+import sqlalchemy.sql +import sqlalchemy.mapping as mapping +from sqlalchemy.mapping import * </ins><span class="cx"> import sqlalchemy.schema </span><span class="cx"> import sqlalchemy.ext.proxy </span><span class="cx"> sqlalchemy.schema.default_engine = sqlalchemy.ext.proxy.ProxyEngine() </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyenginepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/engine.py (1263 => 1264)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/engine.py 2006-04-06 19:25:58 UTC (rev 1263) +++ sqlalchemy/trunk/lib/sqlalchemy/engine.py 2006-04-06 21:12:00 UTC (rev 1264) </span><span class="lines">@@ -817,6 +817,17 @@ </span><span class="cx"> raise StopIteration </span><span class="cx"> else: </span><span class="cx"> yield row </span><ins>+ + def last_inserted_ids(self): + return self.engine.last_inserted_ids() + def last_updated_params(self): + return self.engine.last_updated_params() + def last_inserted_params(self): + return self.engine.last_inserted_params() + def lastrow_has_defaults(self): + return self.engine.lastrow_has_defaults() + def supports_sane_rowcount(self): + return self.engine.supports_sane_rowcount() </ins><span class="cx"> </span><span class="cx"> def fetchall(self): </span><span class="cx"> """fetches all rows, just like DBAPI cursor.fetchall().""" </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py (1263 => 1264)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py 2006-04-06 19:25:58 UTC (rev 1263) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py 2006-04-06 21:12:00 UTC (rev 1264) </span><span class="lines">@@ -7,11 +7,11 @@ </span><span class="cx"> </span><span class="cx"> import sqlalchemy.sql as sql </span><span class="cx"> import sqlalchemy.schema as schema </span><del>-import sqlalchemy.engine as engine </del><span class="cx"> import sqlalchemy.util as util </span><span class="cx"> import util as mapperutil </span><span class="cx"> import sync </span><span class="cx"> from sqlalchemy.exceptions import * </span><ins>+import query </ins><span class="cx"> import objectstore </span><span class="cx"> import sys </span><span class="cx"> import weakref </span><span class="lines">@@ -205,10 +205,6 @@ </span><span class="cx"> proplist = self.columntoproperty.setdefault(column.original, []) </span><span class="cx"> proplist.append(prop) </span><span class="cx"> </span><del>- self._get_clause = sql.and_() - for primary_key in self.pks_by_table[self.table]: - self._get_clause.clauses.append(primary_key == sql.bindparam("pk_"+primary_key.key)) - </del><span class="cx"> if not mapper_registry.has_key(self.class_key) or self.is_primary or (inherits is not None and inherits._is_primary_mapper()): </span><span class="cx"> objectstore.global_attributes.reset_class_managed(self.class_) </span><span class="cx"> self._init_class() </span><span class="lines">@@ -229,9 +225,75 @@ </span><span class="cx"> #print "mapper %s, columntoproperty:" % (self.class_.__name__) </span><span class="cx"> #for key, value in self.columntoproperty.iteritems(): </span><span class="cx"> # print key.table.name, key.key, [(v.key, v) for v in value] </span><del>- - engines = property(lambda s: [t.engine for t in s.tables]) </del><span class="cx"> </span><ins>+ def _get_query(self): + try: + if self._query.mapper is not self: + self._query = query.Query(self) + return self._query + except AttributeError: + self._query = query.Query(self) + return self._query + query = property(_get_query, doc=\ + """returns an instance of sqlalchemy.mapping.query.Query, which implements all the query-constructing + methods such as get(), select(), select_by(), etc. The default Query object uses the global thread-local + Session from the objectstore package. To get a Query object for a specific Session, call the + using(session) method.""") + + def get(self, *ident, **kwargs): + """calls get() on this mapper's default Query object.""" + return self.query.get(*ident, **kwargs) + + def _get(self, key, ident=None, reload=False): + return self.query._get(key, ident=ident, reload=reload) + + def get_by(self, *args, **params): + """calls get_by() on this mapper's default Query object.""" + return self.query.get_by(*args, **params) + + def select_by(self, *args, **params): + """calls select_by() on this mapper's default Query object.""" + return self.query.select_by(*args, **params) + + def selectfirst_by(self, *args, **params): + """calls selectfirst_by() on this mapper's default Query object.""" + return self.query.selectfirst_by(*args, **params) + + def selectone_by(self, *args, **params): + """calls selectone_by() on this mapper's default Query object.""" + return self.query.selectone_by(*args, **params) + + def count_by(self, *args, **params): + """calls count_by() on this mapper's default Query object.""" + return self.query.count_by(*args, **params) + + def selectfirst(self, *args, **params): + """calls selectfirst() on this mapper's default Query object.""" + return self.query.selectfirst(*args, **params) + + def selectone(self, *args, **params): + """calls selectone() on this mapper's default Query object.""" + return self.query.selectone(*args, **params) + + def select(self, arg=None, **kwargs): + """calls select() on this mapper's default Query object.""" + return self.query.select(arg=arg, **kwargs) + + def select_whereclause(self, whereclause=None, params=None, **kwargs): + """calls select_whereclause() on this mapper's default Query object.""" + return self.query.select_whereclause(whereclause=whereclause, params=params, **kwargs) + + def count(self, whereclause=None, params=None, **kwargs): + """calls count() on this mapper's default Query object.""" + return self.query.count(whereclause=whereclause, params=params, **kwargs) + + def select_statement(self, statement, **params): + """calls select_statement() on this mapper's default Query object.""" + return self.query.select_statement(statement, **params) + + def select_text(self, text, **params): + return self.query.select_text(text, **params) + </ins><span class="cx"> def add_property(self, key, prop): </span><span class="cx"> """adds an additional property to this mapper. this is the same as if it were </span><span class="cx"> specified within the 'properties' argument to the constructor. if the named </span><span class="lines">@@ -293,12 +355,18 @@ </span><span class="cx"> mapper_registry[self.class_key] = self </span><span class="cx"> if self.entity_name is None: </span><span class="cx"> self.class_.c = self.c </span><ins>+ + def has_eager(self): + """returns True if one of the properties attached to this Mapper is eager loading""" + return getattr(self, '_has_eager', False) </ins><span class="cx"> </span><span class="cx"> def set_property(self, key, prop): </span><span class="cx"> self.props[key] = prop </span><span class="cx"> prop.init(key, self) </span><span class="cx"> </span><span class="cx"> def instances(self, cursor, *mappers, **kwargs): </span><ins>+ """given a cursor (ResultProxy) from an SQLEngine, returns a list of object instances + corresponding to the rows in the cursor.""" </ins><span class="cx"> limit = kwargs.get('limit', None) </span><span class="cx"> offset = kwargs.get('offset', None) </span><span class="cx"> session = kwargs.get('session', None) </span><span class="lines">@@ -330,38 +398,7 @@ </span><span class="cx"> if mappers: </span><span class="cx"> result = [result] + otherresults </span><span class="cx"> return result </span><del>- - def get(self, *ident, **kwargs): - """returns an instance of the object based on the given identifier, or None - if not found. The *ident argument is a - list of primary key columns in the order of the table def's primary key columns.""" - key = objectstore.get_id_key(ident, self.class_, self.entity_name) - #print "key: " + repr(key) + " ident: " + repr(ident) - return self._get(key, ident, **kwargs) </del><span class="cx"> </span><del>- def _get(self, key, ident=None, reload=False, session=None): - if not reload and not self.always_refresh: - try: - if session is None: - session = objectstore.get_session() - return session._get(key) - except KeyError: - pass - - if ident is None: - ident = key[1] - i = 0 - params = {} - for primary_key in self.pks_by_table[self.table]: - params["pk_"+primary_key.key] = ident[i] - i += 1 - try: - statement = self._compile(self._get_clause) - return self._select_statement(statement, params=params, populate_existing=reload, session=session)[0] - except IndexError: - return None - - </del><span class="cx"> def identity_key(self, *primary_key): </span><span class="cx"> """returns the instance key for the given identity value. this is a global tracking object used by the objectstore, and is usually available off a mapped object as instance._instance_key.""" </span><span class="cx"> return objectstore.get_id_key(tuple(primary_key), self.class_, self.entity_name) </span><span class="lines">@@ -377,7 +414,7 @@ </span><span class="cx"> def compile(self, whereclause = None, **options): </span><span class="cx"> """works like select, except returns the SQL statement object without </span><span class="cx"> compiling or executing it""" </span><del>- return self._compile(whereclause, **options) </del><ins>+ return self.query._compile(whereclause, **options) </ins><span class="cx"> </span><span class="cx"> def copy(self, **kwargs): </span><span class="cx"> mapper = Mapper.__new__(Mapper) </span><span class="lines">@@ -387,22 +424,11 @@ </span><span class="cx"> return mapper </span><span class="cx"> </span><span class="cx"> def using(self, session): </span><del>- """returns a proxying object to this mapper, which will execute methods on the mapper - within the context of the given session. The session is placed as the "current" session - via the push_session/pop_session methods in the objectstore module.""" </del><ins>+ """returns a new Query object with the given Session.""" </ins><span class="cx"> if objectstore.get_session() is session: </span><del>- return self - mapper = self - class Proxy(object): - def __getattr__(self, key): - def callit(*args, **kwargs): - objectstore.push_session(session) - try: - return getattr(mapper, key)(*args, **kwargs) - finally: - objectstore.pop_session() - return callit - return Proxy() </del><ins>+ return self.query + else: + return query.Query(self, session=session) </ins><span class="cx"> </span><span class="cx"> def options(self, *options, **kwargs): </span><span class="cx"> """uses this mapper as a prototype for a new mapper with different behavior. </span><span class="lines">@@ -418,169 +444,12 @@ </span><span class="cx"> self._options[optkey] = mapper </span><span class="cx"> return mapper </span><span class="cx"> </span><del>- def get_by(self, *args, **params): - """returns a single object instance based on the given key/value criterion. - this is either the first value in the result list, or None if the list is - empty. - - the keys are mapped to property or column names mapped by this mapper's Table, and the values - are coerced into a WHERE clause separated by AND operators. If the local property/column - names dont contain the key, a search will be performed against this mapper's immediate - list of relations as well, forming the appropriate join conditions if a matching property - is located. - - e.g. u = usermapper.get_by(user_name = 'fred') - """ - x = self.select_whereclause(self._by_clause(*args, **params), limit=1) - if x: - return x[0] - else: - return None - - def select_by(self, *args, **params): - """returns an array of object instances based on the given clauses and key/value criterion. - - *args is a list of zero or more ClauseElements which will be connected by AND operators. - **params is a set of zero or more key/value parameters which are converted into ClauseElements. - the keys are mapped to property or column names mapped by this mapper's Table, and the values - are coerced into a WHERE clause separated by AND operators. If the local property/column - names dont contain the key, a search will be performed against this mapper's immediate - list of relations as well, forming the appropriate join conditions if a matching property - is located. - - e.g. result = usermapper.select_by(user_name = 'fred') - """ - ret = self.extension.select_by(self, *args, **params) - if ret is not EXT_PASS: - return ret - return self.select_whereclause(self._by_clause(*args, **params)) - - def selectfirst_by(self, *args, **params): - """works like select_by(), but only returns the first result by itself, or None if no - objects returned. Synonymous with get_by()""" - return self.get_by(*args, **params) - - def selectone_by(self, *args, **params): - """works like selectfirst_by(), but throws an error if not exactly one result was returned.""" - ret = mapper.select_whereclause(self._by_clause(*args, **params), limit=2) - if len(ret) == 1: - return ret[0] - raise InvalidRequestError('Multiple rows returned for selectone_by') - - def count_by(self, *args, **params): - """returns the count of instances based on the given clauses and key/value criterion. - The criterion is constructed in the same way as the select_by() method.""" - return self.count(self._by_clause(*args, **params)) - - def _by_clause(self, *args, **params): - clause = None - for arg in args: - if clause is None: - clause = arg - else: - clause &= arg - for key, value in params.iteritems(): - if value is False: - continue - c = self._get_criterion(key, value) - if c is None: - raise InvalidRequestError("Cant find criterion for property '"+ key + "'") - if clause is None: - clause = c - else: - clause &= c - return clause - - def _get_criterion(self, key, value): - """used by select_by to match a key/value pair against - local properties, column names, or a matching property in this mapper's - list of relations.""" - if self.props.has_key(key): - return self.props[key].columns[0] == value - elif self.table.c.has_key(key): - return self.table.c[key] == value - else: - for prop in self.props.values(): - c = prop.get_criterion(key, value) - if c is not None: - return c - else: - return None - </del><span class="cx"> def __getattr__(self, key): </span><del>- if (key.startswith('select_by_')): - key = key[10:] - def foo(arg): - return self.select_by(**{key:arg}) - return foo - elif (key.startswith('get_by_')): - key = key[7:] - def foo(arg): - return self.get_by(**{key:arg}) - return foo </del><ins>+ if (key.startswith('select_by_') or key.startswith('get_by_')): + return getattr(self.query, key) </ins><span class="cx"> else: </span><span class="cx"> raise AttributeError(key) </span><del>- - def selectfirst(self, *args, **params): - """works like select(), but only returns the first result by itself, or None if no - objects returned.""" - params['limit'] = 1 - ret = self.select_whereclause(*args, **params) - if ret: - return ret[0] - else: - return None </del><span class="cx"> </span><del>- def selectone(self, *args, **params): - """works like selectfirst(), but throws an error if not exactly one result was returned.""" - ret = list(self.select(*args, **params)[0:2]) - if len(ret) == 1: - return ret[0] - raise InvalidRequestError('Multiple rows returned for selectone') - - def select(self, arg=None, **kwargs): - """selects instances of the object from the database. - - arg can be any ClauseElement, which will form the criterion with which to - load the objects. - - For more advanced usage, arg can also be a Select statement object, which - will be executed and its resulting rowset used to build new object instances. - in this case, the developer must insure that an adequate set of columns exists in the - rowset with which to build new object instances.""" - - ret = self.extension.select(self, arg=arg, **kwargs) - if ret is not EXT_PASS: - return ret - elif arg is not None and isinstance(arg, sql.Selectable): - return self.select_statement(arg, **kwargs) - else: - return self.select_whereclause(whereclause=arg, **kwargs) - - def select_whereclause(self, whereclause=None, params=None, session=None, **kwargs): - statement = self._compile(whereclause, **kwargs) - return self._select_statement(statement, params=params, session=session) - - def count(self, whereclause=None, params=None, **kwargs): - s = self.table.count(whereclause) - if params is not None: - return s.scalar(**params) - else: - return s.scalar() - - def select_statement(self, statement, **params): - return self._select_statement(statement, params=params) - - def select_text(self, text, **params): - t = sql.text(text, engine=self.primarytable.engine) - return self.instances(t.execute(**params)) - - def _select_statement(self, statement, params=None, **kwargs): - statement.use_labels = True - if params is None: - params = {} - return self.instances(statement.execute(**params), **kwargs) - </del><span class="cx"> def _getpropbycolumn(self, column, raiseerror=True): </span><span class="cx"> try: </span><span class="cx"> prop = self.columntoproperty[column.original] </span><span class="lines">@@ -604,7 +473,6 @@ </span><span class="cx"> </span><span class="cx"> def _setattrbycolumn(self, obj, column, value): </span><span class="cx"> self.columntoproperty[column.original][0].setattr(obj, value) </span><del>- </del><span class="cx"> </span><span class="cx"> def save_obj(self, objects, uow, postupdate=False): </span><span class="cx"> """called by a UnitOfWork object to save objects, which involves either an INSERT or </span><span class="lines">@@ -714,17 +582,17 @@ </span><span class="cx"> for rec in update: </span><span class="cx"> (obj, params) = rec </span><span class="cx"> c = statement.execute(params) </span><del>- self._postfetch(table, obj, table.engine.last_updated_params()) </del><ins>+ self._postfetch(table, obj, c, c.last_updated_params()) </ins><span class="cx"> self.extension.after_update(self, obj) </span><span class="cx"> rows += c.cursor.rowcount </span><del>- if table.engine.supports_sane_rowcount() and rows != len(update): </del><ins>+ if c.supports_sane_rowcount() and rows != len(update): </ins><span class="cx"> raise CommitError("ConcurrencyError - updated rowcount %d does not match number of objects updated %d" % (rows, len(update))) </span><span class="cx"> if len(insert): </span><span class="cx"> statement = table.insert() </span><span class="cx"> for rec in insert: </span><span class="cx"> (obj, params) = rec </span><del>- statement.execute(**params) - primary_key = table.engine.last_inserted_ids() </del><ins>+ c = statement.execute(**params) + primary_key = c.last_inserted_ids() </ins><span class="cx"> if primary_key is not None: </span><span class="cx"> i = 0 </span><span class="cx"> for col in self.pks_by_table[table]: </span><span class="lines">@@ -732,16 +600,16 @@ </span><span class="cx"> if self._getattrbycolumn(obj, col) is None: </span><span class="cx"> self._setattrbycolumn(obj, col, primary_key[i]) </span><span class="cx"> i+=1 </span><del>- self._postfetch(table, obj, table.engine.last_inserted_params()) </del><ins>+ self._postfetch(table, obj, c, c.last_inserted_params()) </ins><span class="cx"> if self._synchronizer is not None: </span><span class="cx"> self._synchronizer.execute(obj, obj) </span><span class="cx"> self.extension.after_insert(self, obj) </span><span class="cx"> </span><del>- def _postfetch(self, table, obj, params): - """after an INSERT or UPDATE, asks the engine if PassiveDefaults fired off on the database side </del><ins>+ def _postfetch(self, table, obj, resultproxy, params): + """after an INSERT or UPDATE, asks the returned result if PassiveDefaults fired off on the database side </ins><span class="cx"> which need to be post-fetched, *or* if pre-exec defaults like ColumnDefaults were fired off </span><span class="cx"> and should be populated into the instance. this is only for non-primary key columns.""" </span><del>- if table.engine.lastrow_has_defaults(): </del><ins>+ if resultproxy.lastrow_has_defaults(): </ins><span class="cx"> clause = sql.and_() </span><span class="cx"> for p in self.pks_by_table[table]: </span><span class="cx"> clause.clauses.append(p == self._getattrbycolumn(obj, p)) </span><span class="lines">@@ -785,7 +653,7 @@ </span><span class="cx"> clause.clauses.append(self.version_id_col == sql.bindparam(self.version_id_col.key)) </span><span class="cx"> statement = table.delete(clause) </span><span class="cx"> c = statement.execute(*delete) </span><del>- if table.engine.supports_sane_rowcount() and c.rowcount != len(delete): </del><ins>+ if c.supports_sane_rowcount() and c.rowcount != len(delete): </ins><span class="cx"> raise CommitError("ConcurrencyError - updated rowcount %d does not match number of objects updated %d" % (c.cursor.rowcount, len(delete))) </span><span class="cx"> </span><span class="cx"> def _has_pks(self, table): </span><span class="lines">@@ -811,53 +679,7 @@ </span><span class="cx"> for prop in self.props.values(): </span><span class="cx"> prop.register_deleted(obj, uow) </span><span class="cx"> </span><del>- def _should_nest(self, **kwargs): - """returns True if the given statement options indicate that we should "nest" the - generated query as a subquery inside of a larger eager-loading query. this is used - with keywords like distinct, limit and offset and the mapper defines eager loads.""" - return ( - getattr(self, '_has_eager', False) - and (kwargs.has_key('limit') or kwargs.has_key('offset') or kwargs.get('distinct', False)) - ) </del><span class="cx"> </span><del>- def _compile(self, whereclause = None, **kwargs): - order_by = kwargs.pop('order_by', False) - if order_by is False: - order_by = self.order_by - if order_by is False: - if self.table.default_order_by() is not None: - order_by = self.table.default_order_by() - - if self._should_nest(**kwargs): - s2 = sql.select(self.table.primary_key, whereclause, use_labels=True, from_obj=[self.table], **kwargs) -# raise "ok first thing", str(s2) - if not kwargs.get('distinct', False) and order_by: - s2.order_by(*util.to_list(order_by)) - s3 = s2.alias('rowcount') - crit = [] - for i in range(0, len(self.table.primary_key)): - crit.append(s3.primary_key[i] == self.table.primary_key[i]) - statement = sql.select([], sql.and_(*crit), from_obj=[self.table], use_labels=True) - # raise "OK statement", str(statement) - if order_by: - statement.order_by(*util.to_list(order_by)) - else: - statement = sql.select([], whereclause, from_obj=[self.table], use_labels=True, **kwargs) - if order_by: - statement.order_by(*util.to_list(order_by)) - # for a DISTINCT query, you need the columns explicitly specified in order - # to use it in "order_by". insure they are in the column criterion (particularly oid). - # TODO: this should be done at the SQL level not the mapper level - if kwargs.get('distinct', False) and order_by: - statement.append_column(*util.to_list(order_by)) - # plugin point - - - # give all the attached properties a chance to modify the query - for key, value in self.props.iteritems(): - value.setup(key, statement, **kwargs) - return statement - </del><span class="cx"> def _identity_key(self, row): </span><span class="cx"> return objectstore.get_row_key(row, self.class_, self.pks_by_table[self.table], self.entity_name) </span><span class="cx"> </span><span class="lines">@@ -1003,16 +825,18 @@ </span><span class="cx"> def chain(self, ext): </span><span class="cx"> self.next = ext </span><span class="cx"> return self </span><del>- def select_by(self, mapper, *args, **kwargs): </del><ins>+ def select_by(self, query, *args, **kwargs): + """overrides the select_by method of the Query object""" </ins><span class="cx"> if self.next is None: </span><span class="cx"> return EXT_PASS </span><span class="cx"> else: </span><del>- return self.next.select_by(mapper, *args, **kwargs) - def select(self, mapper, *args, **kwargs): </del><ins>+ return self.next.select_by(query, *args, **kwargs) + def select(self, query, *args, **kwargs): + """overrides the select method of the Query object""" </ins><span class="cx"> if self.next is None: </span><span class="cx"> return EXT_PASS </span><span class="cx"> else: </span><del>- return self.next.select(mapper, *args, **kwargs) </del><ins>+ return self.next.select(query, *args, **kwargs) </ins><span class="cx"> def create_instance(self, mapper, row, imap, class_): </span><span class="cx"> """called when a new object instance is about to be created from a row. </span><span class="cx"> the method can choose to create the instance itself, or it can return </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingobjectstorepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py (1263 => 1264)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py 2006-04-06 19:25:58 UTC (rev 1263) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/objectstore.py 2006-04-06 21:12:00 UTC (rev 1264) </span><span class="lines">@@ -118,6 +118,9 @@ </span><span class="cx"> self.uow = unitofwork.UnitOfWork(identity_map = self.uow.identity_map) </span><span class="cx"> return Session.SessionTrans(self, self.uow, True) </span><span class="cx"> </span><ins>+ def engines(self, mapper): + return [t.engine for t in mapper.tables] + </ins><span class="cx"> def _trans_commit(self, trans): </span><span class="cx"> if trans.uow is self.uow and trans.isactive: </span><span class="cx"> try: </span><span class="lines">@@ -133,7 +136,7 @@ </span><span class="cx"> def _commit_uow(self, *obj): </span><span class="cx"> self.was_pushed() </span><span class="cx"> try: </span><del>- self.uow.commit(*obj) </del><ins>+ self.uow.flush(self, *obj) </ins><span class="cx"> finally: </span><span class="cx"> self.was_popped() </span><span class="cx"> </span><span class="lines">@@ -147,7 +150,7 @@ </span><span class="cx"> # change begin/commit status </span><span class="cx"> if len(objects): </span><span class="cx"> self._commit_uow(*objects) </span><del>- self.uow.commit(*objects) </del><ins>+ self.uow.flush(self, *objects) </ins><span class="cx"> return </span><span class="cx"> if self.parent_uow is None: </span><span class="cx"> self._commit_uow() </span><span class="lines">@@ -283,13 +286,13 @@ </span><span class="cx"> return get_session().import_instance(instance) </span><span class="cx"> </span><span class="cx"> def mapper(*args, **params): </span><del>- return sqlalchemy.mapperlib.mapper(*args, **params) </del><ins>+ return sqlalchemy.mapping.mapper(*args, **params) </ins><span class="cx"> </span><span class="cx"> def object_mapper(obj): </span><del>- return sqlalchemy.mapperlib.object_mapper(obj) </del><ins>+ return sqlalchemy.mapping.object_mapper(obj) </ins><span class="cx"> </span><span class="cx"> def class_mapper(class_): </span><del>- return sqlalchemy.mapperlib.class_mapper(class_) </del><ins>+ return sqlalchemy.mapping.class_mapper(class_) </ins><span class="cx"> </span><span class="cx"> global_attributes = unitofwork.global_attributes </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingpropertiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py (1263 => 1264)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py 2006-04-06 19:25:58 UTC (rev 1263) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py 2006-04-06 21:12:00 UTC (rev 1264) </span><span class="lines">@@ -582,7 +582,7 @@ </span><span class="cx"> (self.lazywhere, self.lazybinds) = create_lazy_clause(self.parent.noninherited_table, self.primaryjoin, self.secondaryjoin, self.foreignkey) </span><span class="cx"> # determine if our "lazywhere" clause is the same as the mapper's </span><span class="cx"> # get() clause. then we can just use mapper.get() </span><del>- self.use_get = not self.uselist and self.mapper._get_clause.compare(self.lazywhere) </del><ins>+ self.use_get = not self.uselist and self.mapper.query._get_clause.compare(self.lazywhere) </ins><span class="cx"> </span><span class="cx"> def _set_class_attribute(self, class_, key): </span><span class="cx"> # establish a class-level lazy loader on our class </span><span class="lines">@@ -609,14 +609,14 @@ </span><span class="cx"> ident = [] </span><span class="cx"> for primary_key in self.mapper.pks_by_table[self.mapper.table]: </span><span class="cx"> ident.append(params[primary_key._label]) </span><del>- return self.mapper.get(session=session, *ident) </del><ins>+ return self.mapper.using(session).get(*ident) </ins><span class="cx"> elif self.order_by is not False: </span><span class="cx"> order_by = self.order_by </span><span class="cx"> elif self.secondary is not None and self.secondary.default_order_by() is not None: </span><span class="cx"> order_by = self.secondary.default_order_by() </span><span class="cx"> else: </span><span class="cx"> order_by = False </span><del>- result = self.mapper.select_whereclause(self.lazywhere, order_by=order_by, params=params, session=session) </del><ins>+ result = self.mapper.using(session).select_whereclause(self.lazywhere, order_by=order_by, params=params) </ins><span class="cx"> else: </span><span class="cx"> result = [] </span><span class="cx"> if self.uselist: </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingquerypy"></a> <div class="addfile"><h4>Added: sqlalchemy/trunk/lib/sqlalchemy/mapping/query.py (1263 => 1264)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/query.py 2006-04-06 19:25:58 UTC (rev 1263) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/query.py 2006-04-06 21:12:00 UTC (rev 1264) </span><span class="lines">@@ -0,0 +1,267 @@ </span><ins>+ +import objectstore +import sqlalchemy.sql as sql +import sqlalchemy.util as util +import mapper + +class Query(object): + """encapsulates the object-fetching operations provided by Mappers.""" + def __init__(self, mapper, **kwargs): + self.mapper = mapper + self.always_refresh = kwargs.pop('always_refresh', self.mapper.always_refresh) + self.order_by = kwargs.pop('order_by', self.mapper.order_by) + self._session = kwargs.pop('session', None) + if not hasattr(mapper, '_get_clause'): + _get_clause = sql.and_() + for primary_key in self.mapper.pks_by_table[self.table]: + _get_clause.clauses.append(primary_key == sql.bindparam("pk_"+primary_key.key)) + self.mapper._get_clause = _get_clause + self._get_clause = self.mapper._get_clause + def _get_session(self): + if self._session is None: + return objectstore.get_session() + else: + return self._session + table = property(lambda s:s.mapper.table) + props = property(lambda s:s.mapper.props) + session = property(_get_session) + + def get(self, *ident, **kwargs): + """returns an instance of the object based on the given identifier, or None + if not found. The *ident argument is a + list of primary key columns in the order of the table def's primary key columns.""" + key = self.mapper.identity_key(*ident) + #print "key: " + repr(key) + " ident: " + repr(ident) + return self._get(key, ident, **kwargs) + + def get_by(self, *args, **params): + """returns a single object instance based on the given key/value criterion. + this is either the first value in the result list, or None if the list is + empty. + + the keys are mapped to property or column names mapped by this mapper's Table, and the values + are coerced into a WHERE clause separated by AND operators. If the local property/column + names dont contain the key, a search will be performed against this mapper's immediate + list of relations as well, forming the appropriate join conditions if a matching property + is located. + + e.g. u = usermapper.get_by(user_name = 'fred') + """ + x = self.select_whereclause(self._by_clause(*args, **params), limit=1) + if x: + return x[0] + else: + return None + + def select_by(self, *args, **params): + """returns an array of object instances based on the given clauses and key/value criterion. + + *args is a list of zero or more ClauseElements which will be connected by AND operators. + **params is a set of zero or more key/value parameters which are converted into ClauseElements. + the keys are mapped to property or column names mapped by this mapper's Table, and the values + are coerced into a WHERE clause separated by AND operators. If the local property/column + names dont contain the key, a search will be performed against this mapper's immediate + list of relations as well, forming the appropriate join conditions if a matching property + is located. + + e.g. result = usermapper.select_by(user_name = 'fred') + """ + ret = self.mapper.extension.select_by(self, *args, **params) + if ret is not mapper.EXT_PASS: + return ret + return self.select_whereclause(self._by_clause(*args, **params)) + + def selectfirst_by(self, *args, **params): + """works like select_by(), but only returns the first result by itself, or None if no + objects returned. Synonymous with get_by()""" + return self.get_by(*args, **params) + + def selectone_by(self, *args, **params): + """works like selectfirst_by(), but throws an error if not exactly one result was returned.""" + ret = self.select_whereclause(self._by_clause(*args, **params), limit=2) + if len(ret) == 1: + return ret[0] + raise InvalidRequestError('Multiple rows returned for selectone_by') + + def count_by(self, *args, **params): + """returns the count of instances based on the given clauses and key/value criterion. + The criterion is constructed in the same way as the select_by() method.""" + return self.count(self._by_clause(*args, **params)) + + def selectfirst(self, *args, **params): + """works like select(), but only returns the first result by itself, or None if no + objects returned.""" + params['limit'] = 1 + ret = self.select_whereclause(*args, **params) + if ret: + return ret[0] + else: + return None + + def selectone(self, *args, **params): + """works like selectfirst(), but throws an error if not exactly one result was returned.""" + ret = list(self.select(*args, **params)[0:2]) + if len(ret) == 1: + return ret[0] + raise InvalidRequestError('Multiple rows returned for selectone') + + def select(self, arg=None, **kwargs): + """selects instances of the object from the database. + + arg can be any ClauseElement, which will form the criterion with which to + load the objects. + + For more advanced usage, arg can also be a Select statement object, which + will be executed and its resulting rowset used to build new object instances. + in this case, the developer must insure that an adequate set of columns exists in the + rowset with which to build new object instances.""" + + ret = self.mapper.extension.select(self, arg=arg, **kwargs) + if ret is not mapper.EXT_PASS: + return ret + elif arg is not None and isinstance(arg, sql.Selectable): + return self.select_statement(arg, **kwargs) + else: + return self.select_whereclause(whereclause=arg, **kwargs) + + def select_whereclause(self, whereclause=None, params=None, **kwargs): + statement = self._compile(whereclause, **kwargs) + return self._select_statement(statement, params=params) + + def count(self, whereclause=None, params=None, **kwargs): + s = self.table.count(whereclause) + if params is not None: + return s.scalar(**params) + else: + return s.scalar() + + def select_statement(self, statement, **params): + return self._select_statement(statement, params=params) + + def select_text(self, text, **params): + t = sql.text(text, engine=self.mapper.primarytable.engine) + return self.instances(t.execute(**params)) + + def __getattr__(self, key): + if (key.startswith('select_by_')): + key = key[10:] + def foo(arg): + return self.select_by(**{key:arg}) + return foo + elif (key.startswith('get_by_')): + key = key[7:] + def foo(arg): + return self.get_by(**{key:arg}) + return foo + else: + raise AttributeError(key) + + def instances(self, *args, **kwargs): + return self.mapper.instances(session=self.session, *args, **kwargs) + + def _by_clause(self, *args, **params): + clause = None + for arg in args: + if clause is None: + clause = arg + else: + clause &= arg + for key, value in params.iteritems(): + if value is False: + continue + c = self._get_criterion(key, value) + if c is None: + raise InvalidRequestError("Cant find criterion for property '"+ key + "'") + if clause is None: + clause = c + else: + clause &= c + return clause + + def _get(self, key, ident=None, reload=False): + if not reload and not self.always_refresh: + try: + return self.session._get(key) + except KeyError: + pass + + if ident is None: + ident = key[1] + i = 0 + params = {} + for primary_key in self.mapper.pks_by_table[self.table]: + params["pk_"+primary_key.key] = ident[i] + i += 1 + try: + statement = self._compile(self._get_clause) + return self._select_statement(statement, params=params, populate_existing=reload)[0] + except IndexError: + return None + + def _select_statement(self, statement, params=None, **kwargs): + statement.use_labels = True + if params is None: + params = {} + return self.instances(statement.execute(**params), **kwargs) + + def _should_nest(self, **kwargs): + """returns True if the given statement options indicate that we should "nest" the + generated query as a subquery inside of a larger eager-loading query. this is used + with keywords like distinct, limit and offset and the mapper defines eager loads.""" + return ( + self.mapper.has_eager() + and (kwargs.has_key('limit') or kwargs.has_key('offset') or kwargs.get('distinct', False)) + ) + + def _compile(self, whereclause = None, **kwargs): + order_by = kwargs.pop('order_by', False) + if order_by is False: + order_by = self.order_by + if order_by is False: + if self.table.default_order_by() is not None: + order_by = self.table.default_order_by() + + if self._should_nest(**kwargs): + s2 = sql.select(self.table.primary_key, whereclause, use_labels=True, from_obj=[self.table], **kwargs) +# raise "ok first thing", str(s2) + if not kwargs.get('distinct', False) and order_by: + s2.order_by(*util.to_list(order_by)) + s3 = s2.alias('rowcount') + crit = [] + for i in range(0, len(self.table.primary_key)): + crit.append(s3.primary_key[i] == self.table.primary_key[i]) + statement = sql.select([], sql.and_(*crit), from_obj=[self.table], use_labels=True) + # raise "OK statement", str(statement) + if order_by: + statement.order_by(*util.to_list(order_by)) + else: + statement = sql.select([], whereclause, from_obj=[self.table], use_labels=True, **kwargs) + if order_by: + statement.order_by(*util.to_list(order_by)) + # for a DISTINCT query, you need the columns explicitly specified in order + # to use it in "order_by". insure they are in the column criterion (particularly oid). + # TODO: this should be done at the SQL level not the mapper level + if kwargs.get('distinct', False) and order_by: + statement.append_column(*util.to_list(order_by)) + # plugin point + + # give all the attached properties a chance to modify the query + for key, value in self.mapper.props.iteritems(): + value.setup(key, statement, **kwargs) + return statement + + def _get_criterion(self, key, value): + """used by select_by to match a key/value pair against + local properties, column names, or a matching property in this mapper's + list of relations.""" + if self.props.has_key(key): + return self.props[key].columns[0] == value + elif self.table.c.has_key(key): + return self.table.c[key] == value + else: + for prop in self.props.values(): + c = prop.get_criterion(key, value) + if c is not None: + return c + else: + return None </ins></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingunitofworkpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/unitofwork.py (1263 => 1264)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/unitofwork.py 2006-04-06 19:25:58 UTC (rev 1263) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/unitofwork.py 2006-04-06 21:12:00 UTC (rev 1264) </span><span class="lines">@@ -5,13 +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"> """the internals for the Unit Of Work system. includes hooks into the attributes package </span><del>-enabling the routing of change events to Unit Of Work objects, as well as the commit mechanism </del><ins>+enabling the routing of change events to Unit Of Work objects, as well as the flush() mechanism </ins><span class="cx"> which creates a dependency structure that executes change operations. </span><span class="cx"> </span><span class="cx"> a Unit of Work is essentially a system of maintaining a graph of in-memory objects and their </span><span class="cx"> modified state. Objects are maintained as unique against their primary key identity using </span><span class="cx"> an "identity map" pattern. The Unit of Work then maintains lists of objects that are new, </span><del>-dirty, or deleted and provides the capability to commit all those changes at once. </del><ins>+dirty, or deleted and provides the capability to flush all those changes at once. </ins><span class="cx"> """ </span><span class="cx"> </span><span class="cx"> from sqlalchemy import attributes </span><span class="lines">@@ -23,7 +23,7 @@ </span><span class="cx"> import topological </span><span class="cx"> from sets import * </span><span class="cx"> </span><del>-# a global indicating if all commit() operations should have their plan </del><ins>+# a global indicating if all flush() operations should have their plan </ins><span class="cx"> # printed to standard output. also can be affected by creating an engine </span><span class="cx"> # with the "echo_uow=True" keyword argument. </span><span class="cx"> LOG = False </span><span class="lines">@@ -73,7 +73,7 @@ </span><span class="cx"> return UOWListElement(obj, key, list_, **kwargs) </span><span class="cx"> </span><span class="cx"> class UnitOfWork(object): </span><del>- """main UOW object which stores lists of dirty/new/deleted objects, as well as 'modified_lists' for list attributes. provides top-level "commit" functionality as well as the transaction boundaries with the SQLEngine(s) involved in a write operation.""" </del><ins>+ """main UOW object which stores lists of dirty/new/deleted objects, as well as 'modified_lists' for list attributes. provides top-level "flush" functionality as well as the transaction boundaries with the SQLEngine(s) involved in a write operation.""" </ins><span class="cx"> def __init__(self, identity_map=None): </span><span class="cx"> if identity_map is not None: </span><span class="cx"> self.identity_map = identity_map </span><span class="lines">@@ -141,7 +141,7 @@ </span><span class="cx"> self.attributes.remove(obj) </span><span class="cx"> </span><span class="cx"> def _validate_obj(self, obj): </span><del>- """validates that dirty/delete/commit operations can occur upon the given object, by checking </del><ins>+ """validates that dirty/delete/flush operations can occur upon the given object, by checking </ins><span class="cx"> if it has an instance key and that the instance key is present in the identity map.""" </span><span class="cx"> if hasattr(obj, '_instance_key') and not self.identity_map.has_key(obj._instance_key): </span><span class="cx"> raise InvalidRequestError("Detected a mapped object not present in the current thread's Identity Map: '%s'. Use objectstore.import_instance() to place deserialized instances or instances from other threads" % repr(obj._instance_key)) </span><span class="lines">@@ -203,8 +203,8 @@ </span><span class="cx"> except KeyError: </span><span class="cx"> pass </span><span class="cx"> </span><del>- def commit(self, *objects): - commit_context = UOWTransaction(self) </del><ins>+ def flush(self, session, *objects): + flush_context = UOWTransaction(self, session) </ins><span class="cx"> </span><span class="cx"> if len(objects): </span><span class="cx"> objset = util.HashSet(iter=objects) </span><span class="lines">@@ -216,29 +216,29 @@ </span><span class="cx"> continue </span><span class="cx"> if self.deleted.contains(obj): </span><span class="cx"> continue </span><del>- commit_context.register_object(obj) </del><ins>+ flush_context.register_object(obj) </ins><span class="cx"> for item in self.modified_lists: </span><span class="cx"> obj = item.obj </span><span class="cx"> if objset is not None and not objset.contains(obj): </span><span class="cx"> continue </span><span class="cx"> if self.deleted.contains(obj): </span><span class="cx"> continue </span><del>- commit_context.register_object(obj, listonly = True) - commit_context.register_saved_history(item) </del><ins>+ flush_context.register_object(obj, listonly = True) + flush_context.register_saved_history(item) </ins><span class="cx"> </span><span class="cx"> # for o in item.added_items() + item.deleted_items(): </span><span class="cx"> # if self.deleted.contains(o): </span><span class="cx"> # cont... [truncated message content] |
From: <co...@sq...> - 2006-04-06 19:26:09
|
<!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>[1263] sqlalchemy/trunk/lib/sqlalchemy/util.py: the __iter__ method on historyarraylist seemed to get broked...hmmmm</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1263</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-06 14:25:58 -0500 (Thu, 06 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>the __iter__ method on historyarraylist seemed to get broked...hmmmm</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemyutilpy">sqlalchemy/trunk/lib/sqlalchemy/util.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemyutilpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/util.py (1262 => 1263)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/util.py 2006-04-06 19:01:14 UTC (rev 1262) +++ sqlalchemy/trunk/lib/sqlalchemy/util.py 2006-04-06 19:25:58 UTC (rev 1263) </span><span class="lines">@@ -294,8 +294,8 @@ </span><span class="cx"> else: </span><span class="cx"> self.data = [] </span><span class="cx"> self.readonly=readonly </span><del>-# def __iter__(self): -# return iter([k for k in self.records if self.records[k] is not False]) </del><ins>+ def __iter__(self): + return iter(self.data) </ins><span class="cx"> def __getattr__(self, attr): </span><span class="cx"> """proxies unknown HistoryArraySet methods and attributes to the underlying </span><span class="cx"> data array. this allows custom list classes to be used.""" </span><span class="lines">@@ -397,7 +397,7 @@ </span><span class="cx"> del self.records[item] </span><span class="cx"> self.data.remove(item) </span><span class="cx"> def has_item(self, item): </span><del>- return self.records.has_key(item) </del><ins>+ return self.records.has_key(item) and self.records[item] is not False </ins><span class="cx"> def __setitem__(self, i, item): </span><span class="cx"> if self._setrecord(item): </span><span class="cx"> self.data[i] = item </span></span></pre> </div> </div> </body> </html> |
<!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>[1262] sqlalchemy/trunk/test: added explicit "session" argument to get(), select_whereclause in mapper, as well as throughout the call-chain for those.</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1262</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-06 14:01:14 -0500 (Thu, 06 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>added explicit "session" argument to get(), select_whereclause in mapper, as well as throughout the call-chain for those. lazy loader honors the "session" of the parent object, + added simple unit test</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunkexamplesadjacencytreebyroot_treepy">sqlalchemy/trunk/examples/adjacencytree/byroot_tree.py</a></li> <li><a href="#sqlalchemytrunkexamplespolymorphpolymorph2py">sqlalchemy/trunk/examples/polymorph/polymorph2.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymappingmapperpy">sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemymappingpropertiespy">sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemyutilpy">sqlalchemy/trunk/lib/sqlalchemy/util.py</a></li> <li><a href="#sqlalchemytrunktestmapperpy">sqlalchemy/trunk/test/mapper.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunkexamplesadjacencytreebyroot_treepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/examples/adjacencytree/byroot_tree.py (1261 => 1262)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/examples/adjacencytree/byroot_tree.py 2006-04-06 14:54:16 UTC (rev 1261) +++ sqlalchemy/trunk/examples/adjacencytree/byroot_tree.py 2006-04-06 19:01:14 UTC (rev 1262) </span><span class="lines">@@ -45,6 +45,7 @@ </span><span class="cx"> def __iter__(self): </span><span class="cx"> return iter(self.values()) </span><span class="cx"> </span><ins>+ </ins><span class="cx"> class TreeNode(object): </span><span class="cx"> """a hierarchical Tree class, which adds the concept of a "root node". The root is </span><span class="cx"> the topmost node in a tree, or in other words a node whose parent ID is NULL. </span></span></pre></div> <a id="sqlalchemytrunkexamplespolymorphpolymorph2py"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/examples/polymorph/polymorph2.py (1261 => 1262)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/examples/polymorph/polymorph2.py 2006-04-06 14:54:16 UTC (rev 1261) +++ sqlalchemy/trunk/examples/polymorph/polymorph2.py 2006-04-06 19:01:14 UTC (rev 1262) </span><span class="lines">@@ -86,12 +86,12 @@ </span><span class="cx"> else: </span><span class="cx"> return Person() </span><span class="cx"> </span><del>- def populate_instance(self, mapper, instance, row, identitykey, imap, isnew): </del><ins>+ def populate_instance(self, mapper, session, instance, row, identitykey, imap, isnew): </ins><span class="cx"> if row[person_join.c.type] =='engineer': </span><del>- Engineer.mapper.populate_instance(instance, row, identitykey, imap, isnew, frommapper=mapper) </del><ins>+ Engineer.mapper.populate_instance(session, instance, row, identitykey, imap, isnew, frommapper=mapper) </ins><span class="cx"> return False </span><span class="cx"> elif row[person_join.c.type] =='manager': </span><del>- Manager.mapper.populate_instance(instance, row, identitykey, imap, isnew, frommapper=mapper) </del><ins>+ Manager.mapper.populate_instance(session, instance, row, identitykey, imap, isnew, frommapper=mapper) </ins><span class="cx"> return False </span><span class="cx"> else: </span><span class="cx"> return sqlalchemy.mapping.EXT_PASS </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py (1261 => 1262)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py 2006-04-06 14:54:16 UTC (rev 1261) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/mapper.py 2006-04-06 19:01:14 UTC (rev 1262) </span><span class="lines">@@ -301,6 +301,9 @@ </span><span class="cx"> def instances(self, cursor, *mappers, **kwargs): </span><span class="cx"> limit = kwargs.get('limit', None) </span><span class="cx"> offset = kwargs.get('offset', None) </span><ins>+ session = kwargs.get('session', None) + if session is None: + session = objectstore.get_session() </ins><span class="cx"> populate_existing = kwargs.get('populate_existing', False) </span><span class="cx"> </span><span class="cx"> result = util.HistoryArraySet() </span><span class="lines">@@ -314,32 +317,34 @@ </span><span class="cx"> row = cursor.fetchone() </span><span class="cx"> if row is None: </span><span class="cx"> break </span><del>- self._instance(row, imap, result, populate_existing=populate_existing) </del><ins>+ self._instance(session, row, imap, result, populate_existing=populate_existing) </ins><span class="cx"> i = 0 </span><span class="cx"> for m in mappers: </span><del>- m._instance(row, imap, otherresults[i]) </del><ins>+ m._instance(session, row, imap, otherresults[i]) </ins><span class="cx"> i+=1 </span><span class="cx"> </span><span class="cx"> # store new stuff in the identity map </span><span class="cx"> for value in imap.values(): </span><del>- objectstore.get_session().register_clean(value) </del><ins>+ session.register_clean(value) </ins><span class="cx"> </span><span class="cx"> if mappers: </span><span class="cx"> result = [result] + otherresults </span><span class="cx"> return result </span><span class="cx"> </span><del>- def get(self, *ident): </del><ins>+ def get(self, *ident, **kwargs): </ins><span class="cx"> """returns an instance of the object based on the given identifier, or None </span><span class="cx"> if not found. The *ident argument is a </span><span class="cx"> list of primary key columns in the order of the table def's primary key columns.""" </span><span class="cx"> key = objectstore.get_id_key(ident, self.class_, self.entity_name) </span><span class="cx"> #print "key: " + repr(key) + " ident: " + repr(ident) </span><del>- return self._get(key, ident) </del><ins>+ return self._get(key, ident, **kwargs) </ins><span class="cx"> </span><del>- def _get(self, key, ident=None, reload=False): </del><ins>+ def _get(self, key, ident=None, reload=False, session=None): </ins><span class="cx"> if not reload and not self.always_refresh: </span><span class="cx"> try: </span><del>- return objectstore.get_session()._get(key) </del><ins>+ if session is None: + session = objectstore.get_session() + return session._get(key) </ins><span class="cx"> except KeyError: </span><span class="cx"> pass </span><span class="cx"> </span><span class="lines">@@ -352,7 +357,7 @@ </span><span class="cx"> i += 1 </span><span class="cx"> try: </span><span class="cx"> statement = self._compile(self._get_clause) </span><del>- return self._select_statement(statement, params=params, populate_existing=reload)[0] </del><ins>+ return self._select_statement(statement, params=params, populate_existing=reload, session=session)[0] </ins><span class="cx"> except IndexError: </span><span class="cx"> return None </span><span class="cx"> </span><span class="lines">@@ -552,9 +557,9 @@ </span><span class="cx"> else: </span><span class="cx"> return self.select_whereclause(whereclause=arg, **kwargs) </span><span class="cx"> </span><del>- def select_whereclause(self, whereclause=None, params=None, **kwargs): </del><ins>+ def select_whereclause(self, whereclause=None, params=None, session=None, **kwargs): </ins><span class="cx"> statement = self._compile(whereclause, **kwargs) </span><del>- return self._select_statement(statement, params=params) </del><ins>+ return self._select_statement(statement, params=params, session=session) </ins><span class="cx"> </span><span class="cx"> def count(self, whereclause=None, params=None, **kwargs): </span><span class="cx"> s = self.table.count(whereclause) </span><span class="lines">@@ -856,7 +861,7 @@ </span><span class="cx"> def _identity_key(self, row): </span><span class="cx"> return objectstore.get_row_key(row, self.class_, self.pks_by_table[self.table], self.entity_name) </span><span class="cx"> </span><del>- def _instance(self, row, imap, result = None, populate_existing = False): </del><ins>+ def _instance(self, session, row, imap, result = None, populate_existing = False): </ins><span class="cx"> """pulls an object instance from the given row and appends it to the given result </span><span class="cx"> list. if the instance already exists in the given identity map, its not added. in </span><span class="cx"> either case, executes all the property loaders on the instance to also process extra </span><span class="lines">@@ -865,18 +870,20 @@ </span><span class="cx"> # including modifying any of its related items lists, as its already </span><span class="cx"> # been exposed to being modified by the application. </span><span class="cx"> </span><ins>+ if session is None: + session = objectstore.get_session() + </ins><span class="cx"> populate_existing = populate_existing or self.always_refresh </span><span class="cx"> identitykey = self._identity_key(row) </span><del>- sess = objectstore.get_session() - if sess.has_key(identitykey): - instance = sess._get(identitykey) </del><ins>+ if session.has_key(identitykey): + instance = session._get(identitykey) </ins><span class="cx"> </span><span class="cx"> isnew = False </span><del>- if populate_existing or sess.is_expired(instance, unexpire=True): </del><ins>+ if populate_existing or session.is_expired(instance, unexpire=True): </ins><span class="cx"> if not imap.has_key(identitykey): </span><span class="cx"> imap[identitykey] = instance </span><span class="cx"> for prop in self.props.values(): </span><del>- prop.execute(instance, row, identitykey, imap, True) </del><ins>+ prop.execute(session, instance, row, identitykey, imap, True) </ins><span class="cx"> if self.extension.append_result(self, row, imap, result, instance, isnew, populate_existing=populate_existing) is EXT_PASS: </span><span class="cx"> if result is not None: </span><span class="cx"> result.append_nohistory(instance) </span><span class="lines">@@ -893,7 +900,7 @@ </span><span class="cx"> # plugin point </span><span class="cx"> instance = self.extension.create_instance(self, row, imap, self.class_) </span><span class="cx"> if instance is EXT_PASS: </span><del>- instance = self.class_(_mapper_nohistory=True, _sa_entity_name=self.entity_name) </del><ins>+ instance = self.class_(_mapper_nohistory=True, _sa_entity_name=self.entity_name, _sa_session=session) </ins><span class="cx"> imap[identitykey] = instance </span><span class="cx"> isnew = True </span><span class="cx"> else: </span><span class="lines">@@ -904,8 +911,8 @@ </span><span class="cx"> </span><span class="cx"> # call further mapper properties on the row, to pull further </span><span class="cx"> # instances from the row and possibly populate this item. </span><del>- if self.extension.populate_instance(self, instance, row, identitykey, imap, isnew) is EXT_PASS: - self.populate_instance(instance, row, identitykey, imap, isnew) </del><ins>+ if self.extension.populate_instance(self, session, instance, row, identitykey, imap, isnew) is EXT_PASS: + self.populate_instance(session, instance, row, identitykey, imap, isnew) </ins><span class="cx"> if self.extension.append_result(self, row, imap, result, instance, isnew, populate_existing=populate_existing) is EXT_PASS: </span><span class="cx"> if result is not None: </span><span class="cx"> result.append_nohistory(instance) </span><span class="lines">@@ -923,17 +930,17 @@ </span><span class="cx"> newrow[c] = newrow[c.key] </span><span class="cx"> return newrow </span><span class="cx"> </span><del>- def populate_instance(self, instance, row, identitykey, imap, isnew, frommapper=None): </del><ins>+ def populate_instance(self, session, instance, row, identitykey, imap, isnew, frommapper=None): </ins><span class="cx"> if frommapper is not None: </span><span class="cx"> row = frommapper.translate_row(self, row) </span><span class="cx"> </span><span class="cx"> for prop in self.props.values(): </span><del>- prop.execute(instance, row, identitykey, imap, isnew) </del><ins>+ prop.execute(session, instance, row, identitykey, imap, isnew) </ins><span class="cx"> </span><span class="cx"> class MapperProperty(object): </span><span class="cx"> """an element attached to a Mapper that describes and assists in the loading and saving </span><span class="cx"> of an attribute on an object instance.""" </span><del>- def execute(self, instance, row, identitykey, imap, isnew): </del><ins>+ def execute(self, session, instance, row, identitykey, imap, isnew): </ins><span class="cx"> """called when the mapper receives a row. instance is the parent instance </span><span class="cx"> corresponding to the row. """ </span><span class="cx"> raise NotImplementedError() </span><span class="lines">@@ -1054,7 +1061,7 @@ </span><span class="cx"> return EXT_PASS </span><span class="cx"> else: </span><span class="cx"> return self.next.append_result(mapper, row, imap, result, instance, isnew, populate_existing) </span><del>- def populate_instance(self, mapper, instance, row, identitykey, imap, isnew): </del><ins>+ def populate_instance(self, mapper, session, instance, row, identitykey, imap, isnew): </ins><span class="cx"> """called right before the mapper, after creating an instance from a row, passes the row </span><span class="cx"> to its MapperProperty objects which are responsible for populating the object's attributes. </span><span class="cx"> If this method returns True, it is assumed that the mapper should do the appending, else </span><span class="lines">@@ -1062,14 +1069,14 @@ </span><span class="cx"> </span><span class="cx"> Essentially, this method is used to have a different mapper populate the object: </span><span class="cx"> </span><del>- def populate_instance(self, mapper, instance, row, identitykey, imap, isnew): - othermapper.populate_instance(instance, row, identitykey, imap, isnew, frommapper=mapper) </del><ins>+ def populate_instance(self, mapper, session, instance, row, identitykey, imap, isnew): + othermapper.populate_instance(session, instance, row, identitykey, imap, isnew, frommapper=mapper) </ins><span class="cx"> return True </span><span class="cx"> """ </span><span class="cx"> if self.next is None: </span><span class="cx"> return EXT_PASS </span><span class="cx"> else: </span><del>- return self.next.populate_instance(mapper, instance, row, identitykey, imap, isnew) </del><ins>+ return self.next.populate_instance(mapper, session, instance, row, identitykey, imap, isnew) </ins><span class="cx"> def before_insert(self, mapper, instance): </span><span class="cx"> """called before an object instance is INSERTed into its table. </span><span class="cx"> </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemymappingpropertiespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py (1261 => 1262)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py 2006-04-06 14:54:16 UTC (rev 1261) +++ sqlalchemy/trunk/lib/sqlalchemy/mapping/properties.py 2006-04-06 19:01:14 UTC (rev 1262) </span><span class="lines">@@ -46,7 +46,7 @@ </span><span class="cx"> if parent._is_primary_mapper(): </span><span class="cx"> #print "regiser col on class %s key %s" % (parent.class_.__name__, key) </span><span class="cx"> objectstore.uow().register_attribute(parent.class_, key, uselist = False) </span><del>- def execute(self, instance, row, identitykey, imap, isnew): </del><ins>+ def execute(self, session, instance, row, identitykey, imap, isnew): </ins><span class="cx"> if isnew: </span><span class="cx"> #print "POPULATING OBJ", instance.__class__.__name__, "COL", self.columns[0]._label, "WITH DATA", row[self.columns[0]], "ROW IS A", row.__class__.__name__, "COL ID", id(self.columns[0]) </span><span class="cx"> instance.__dict__[self.key] = row[self.columns[0]] </span><span class="lines">@@ -95,7 +95,7 @@ </span><span class="cx"> return lazyload </span><span class="cx"> def setup(self, key, statement, **options): </span><span class="cx"> pass </span><del>- def execute(self, instance, row, identitykey, imap, isnew): </del><ins>+ def execute(self, session, instance, row, identitykey, imap, isnew): </ins><span class="cx"> if isnew: </span><span class="cx"> if not self.is_primary(): </span><span class="cx"> objectstore.global_attributes.create_history(instance, self.key, False, callable_=self.setup_loader(instance)) </span><span class="lines">@@ -533,7 +533,7 @@ </span><span class="cx"> self._synchronize(obj, child, None, True) </span><span class="cx"> uowcommit.register_object(child, isdelete=self.private) </span><span class="cx"> </span><del>- def execute(self, instance, row, identitykey, imap, isnew): </del><ins>+ def execute(self, session, instance, row, identitykey, imap, isnew): </ins><span class="cx"> if self.is_primary(): </span><span class="cx"> return </span><span class="cx"> #print "PLAIN PROPLOADER EXEC NON-PRIAMRY", repr(id(self)), repr(self.mapper.class_), self.key </span><span class="lines">@@ -595,6 +595,7 @@ </span><span class="cx"> def lazyload(): </span><span class="cx"> params = {} </span><span class="cx"> allparams = True </span><ins>+ session = objectstore.get_session(instance) </ins><span class="cx"> #print "setting up loader, lazywhere", str(self.lazywhere) </span><span class="cx"> for col, bind in self.lazybinds.iteritems(): </span><span class="cx"> params[bind.key] = self.parent._getattrbycolumn(instance, col) </span><span class="lines">@@ -608,14 +609,14 @@ </span><span class="cx"> ident = [] </span><span class="cx"> for primary_key in self.mapper.pks_by_table[self.mapper.table]: </span><span class="cx"> ident.append(params[primary_key._label]) </span><del>- return self.mapper.get(*ident) </del><ins>+ return self.mapper.get(session=session, *ident) </ins><span class="cx"> elif self.order_by is not False: </span><span class="cx"> order_by = self.order_by </span><span class="cx"> elif self.secondary is not None and self.secondary.default_order_by() is not None: </span><span class="cx"> order_by = self.secondary.default_order_by() </span><span class="cx"> else: </span><span class="cx"> order_by = False </span><del>- result = self.mapper.select_whereclause(self.lazywhere, order_by=order_by, params=params) </del><ins>+ result = self.mapper.select_whereclause(self.lazywhere, order_by=order_by, params=params, session=session) </ins><span class="cx"> else: </span><span class="cx"> result = [] </span><span class="cx"> if self.uselist: </span><span class="lines">@@ -627,7 +628,7 @@ </span><span class="cx"> return None </span><span class="cx"> return lazyload </span><span class="cx"> </span><del>- def execute(self, instance, row, identitykey, imap, isnew): </del><ins>+ def execute(self, session, instance, row, identitykey, imap, isnew): </ins><span class="cx"> if isnew: </span><span class="cx"> # new object instance being loaded from a result row </span><span class="cx"> if not self.is_primary(): </span><span class="lines">@@ -780,7 +781,7 @@ </span><span class="cx"> value.setup(key, statement, eagertable=self.eagertarget) </span><span class="cx"> </span><span class="cx"> </span><del>- def execute(self, instance, row, identitykey, imap, isnew): </del><ins>+ def execute(self, session, instance, row, identitykey, imap, isnew): </ins><span class="cx"> """receive a row. tell our mapper to look for a new object instance in the row, and attach </span><span class="cx"> it to a list on the parent instance.""" </span><span class="cx"> </span><span class="lines">@@ -791,11 +792,11 @@ </span><span class="cx"> </span><span class="cx"> if not self.uselist: </span><span class="cx"> if isnew: </span><del>- h.setattr_clean(self._instance(row, imap)) </del><ins>+ h.setattr_clean(self._instance(session, row, imap)) </ins><span class="cx"> else: </span><span class="cx"> # call _instance on the row, even though the object has been created, </span><span class="cx"> # so that we further descend into properties </span><del>- self._instance(row, imap) </del><ins>+ self._instance(session, row, imap) </ins><span class="cx"> </span><span class="cx"> return </span><span class="cx"> elif isnew: </span><span class="lines">@@ -803,7 +804,7 @@ </span><span class="cx"> else: </span><span class="cx"> result_list = getattr(instance, self.key) </span><span class="cx"> </span><del>- self._instance(row, imap, result_list) </del><ins>+ self._instance(session, row, imap, result_list) </ins><span class="cx"> </span><span class="cx"> def _create_decorator_row(self): </span><span class="cx"> class DecoratorDict(object): </span><span class="lines">@@ -823,7 +824,7 @@ </span><span class="cx"> map[parent.name] = c </span><span class="cx"> return DecoratorDict </span><span class="cx"> </span><del>- def _instance(self, row, imap, result_list=None): </del><ins>+ def _instance(self, session, row, imap, result_list=None): </ins><span class="cx"> """gets an instance from a row, via this EagerLoader's mapper.""" </span><span class="cx"> # since the EagerLoader makes an Alias of its mapper's table, </span><span class="cx"> # we translate the actual result columns back to what they </span><span class="lines">@@ -833,7 +834,7 @@ </span><span class="cx"> # (which is what mappers use) as well as its "label" (which might be what </span><span class="cx"> # user-defined code is using) </span><span class="cx"> row = self._row_decorator(row) </span><del>- return self.mapper._instance(row, imap, result_list) </del><ins>+ return self.mapper._instance(session, row, imap, result_list) </ins><span class="cx"> </span><span class="cx"> class GenericOption(MapperOption): </span><span class="cx"> """a mapper option that can handle dotted property names, </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyutilpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/util.py (1261 => 1262)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/util.py 2006-04-06 14:54:16 UTC (rev 1261) +++ sqlalchemy/trunk/lib/sqlalchemy/util.py 2006-04-06 19:01:14 UTC (rev 1262) </span><span class="lines">@@ -294,6 +294,8 @@ </span><span class="cx"> else: </span><span class="cx"> self.data = [] </span><span class="cx"> self.readonly=readonly </span><ins>+# def __iter__(self): +# return iter([k for k in self.records if self.records[k] is not False]) </ins><span class="cx"> def __getattr__(self, attr): </span><span class="cx"> """proxies unknown HistoryArraySet methods and attributes to the underlying </span><span class="cx"> data array. this allows custom list classes to be used.""" </span></span></pre></div> <a id="sqlalchemytrunktestmapperpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/mapper.py (1261 => 1262)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/mapper.py 2006-04-06 14:54:16 UTC (rev 1261) +++ sqlalchemy/trunk/test/mapper.py 2006-04-06 19:01:14 UTC (rev 1262) </span><span class="lines">@@ -124,6 +124,13 @@ </span><span class="cx"> objectstore.refresh(u) </span><span class="cx"> self.assert_sql_count(db, go, 1) </span><span class="cx"> </span><ins>+ def testsessionpropigation(self): + sess = objectstore.Session() + m = mapper(User, users, properties={'addresses':relation(mapper(Address, addresses), lazy=True)}) + u = m.get(7, session=sess) + assert objectstore.get_session(u) is sess + assert objectstore.get_session(u.addresses[0]) is sess + </ins><span class="cx"> def testexpire(self): </span><span class="cx"> m = mapper(User, users, properties={'addresses':relation(mapper(Address, addresses), lazy=False)}) </span><span class="cx"> u = m.get(7) </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-06 14:54:31
|
<!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>[1261] sqlalchemy/trunk/lib/sqlalchemy/attributes.py: this assertion not really needed, esp.</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1261</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-06 09:54:16 -0500 (Thu, 06 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>this assertion not really needed, esp. if a PickleType is used to take in a list</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemyattributespy">sqlalchemy/trunk/lib/sqlalchemy/attributes.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemyattributespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/attributes.py (1260 => 1261)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/attributes.py 2006-04-06 14:32:57 UTC (rev 1260) +++ sqlalchemy/trunk/lib/sqlalchemy/attributes.py 2006-04-06 14:54:16 UTC (rev 1261) </span><span class="lines">@@ -109,8 +109,8 @@ </span><span class="cx"> def getattr(self, **kwargs): </span><span class="cx"> return self.obj.__dict__[self.key] </span><span class="cx"> def setattr(self, value, **kwargs): </span><del>- if isinstance(value, list): - raise InvalidRequestError("assigning a list to scalar property '%s' on '%s' instance %d" % (self.key, self.obj.__class__.__name__, id(self.obj))) </del><ins>+ #if isinstance(value, list): + # raise InvalidRequestError("assigning a list to scalar property '%s' on '%s' instance %d" % (self.key, self.obj.__class__.__name__, id(self.obj))) </ins><span class="cx"> orig = self.obj.__dict__.get(self.key, None) </span><span class="cx"> if orig is value: </span><span class="cx"> return </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-06 14:33:16
|
<!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>[1260] sqlalchemy/trunk/test/attributes.py: added pickle test</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1260</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-06 09:32:57 -0500 (Thu, 06 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>added pickle test</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunktestattributespy">sqlalchemy/trunk/test/attributes.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunktestattributespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/attributes.py (1259 => 1260)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/attributes.py 2006-04-06 14:27:03 UTC (rev 1259) +++ sqlalchemy/trunk/test/attributes.py 2006-04-06 14:32:57 UTC (rev 1260) </span><span class="lines">@@ -2,8 +2,10 @@ </span><span class="cx"> import sqlalchemy.util as util </span><span class="cx"> import sqlalchemy.attributes as attributes </span><span class="cx"> import unittest, sys, os </span><ins>+import pickle </ins><span class="cx"> </span><span class="cx"> </span><ins>+class MyTest(object):pass </ins><span class="cx"> </span><span class="cx"> class AttributesTest(PersistTest): </span><span class="cx"> """tests for the attributes.py module, which deals with tracking attribute changes on an object.""" </span><span class="lines">@@ -36,6 +38,15 @@ </span><span class="cx"> print repr(u.__dict__) </span><span class="cx"> self.assert_(u.user_id == 7 and u.user_name == 'john' and u.email_address == 'la...@12...') </span><span class="cx"> </span><ins>+ def testpickleness(self): + manager = attributes.AttributeManager() + manager.register_attribute(MyTest, 'user_id', uselist = False) + manager.register_attribute(MyTest, 'user_name', uselist = False) + manager.register_attribute(MyTest, 'email_address', uselist = False) + x = MyTest() + x.user_id=7 + pickle.dumps(x) + </ins><span class="cx"> def testlist(self): </span><span class="cx"> class User(object):pass </span><span class="cx"> class Address(object):pass </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-06 14:27:26
|
<!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>[1259] sqlalchemy/trunk/lib/sqlalchemy/attributes.py: the ultimate "hands off" approach to the object's dictionary of managed attributes</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1259</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-06 09:27:03 -0500 (Thu, 06 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>the ultimate "hands off" approach to the object's dictionary of managed attributes</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemyattributespy">sqlalchemy/trunk/lib/sqlalchemy/attributes.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemyattributespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/attributes.py (1258 => 1259)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/attributes.py 2006-04-06 01:15:46 UTC (rev 1258) +++ sqlalchemy/trunk/lib/sqlalchemy/attributes.py 2006-04-06 14:27:03 UTC (rev 1259) </span><span class="lines">@@ -78,9 +78,10 @@ </span><span class="cx"> which occurs through the SmartProperty property object ultimately calls upon </span><span class="cx"> ManagedAttribute objects associated with the instance via this dictionary.""" </span><span class="cx"> def __init__(self, obj, key): </span><del>- self.__obj = weakref.ref(obj) </del><ins>+ #self.__obj = weakref.ref(obj) + self.obj = obj </ins><span class="cx"> self.key = key </span><del>- obj = property(lambda s:s.__obj()) </del><ins>+ #obj = property(lambda s:s.__obj()) </ins><span class="cx"> def history(self, **kwargs): </span><span class="cx"> return self </span><span class="cx"> def plain_init(self, *args, **kwargs): </span><span class="lines">@@ -493,6 +494,27 @@ </span><span class="cx"> the new object instance as an argument to create the new ManagedAttribute. </span><span class="cx"> Extra keyword arguments can be sent which </span><span class="cx"> will be passed along to newly created ManagedAttribute.""" </span><del>- class_._attribute_manager = self </del><ins>+ if not hasattr(class_, '_attribute_manager'): + class_._attribute_manager = self + class_._managed_attributes = ObjectAttributeGateway() </ins><span class="cx"> setattr(class_, key, self.create_prop(class_, key, uselist, callable_, **kwargs)) </span><span class="cx"> </span><ins>+managed_attributes = weakref.WeakKeyDictionary() + +class ObjectAttributeGateway(object): + """handles the dictionary of ManagedAttributes for instances. this level of indirection + is to prevent circular references upon objects, as well as keeping them Pickle-compatible.""" + def __set__(self, obj, value): + managed_attributes[obj] = value + def __delete__(self, obj): + try: + del managed_attributes[obj] + except KeyError: + raise AttributeError() + def __get__(self, obj, owner): + if obj is None: + return self + try: + return managed_attributes[obj] + except KeyError: + raise AttributeError() </ins><span class="cx">\ No newline at end of file </span></span></pre> </div> </div> </body> </html> |
From: <co...@sq...> - 2006-04-06 01:16:08
|
<!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>[1258] sqlalchemy/trunk/test: moves the binding of a TypeEngine object from "schema/statement creation" time into "compilation" time</title> </head> <body> <div id="msg"> <dl> <dt>Revision</dt> <dd>1258</dd> <dt>Author</dt> <dd>zzzeek</dd> <dt>Date</dt> <dd>2006-04-05 20:15:46 -0500 (Wed, 05 Apr 2006)</dd> </dl> <h3>Log Message</h3> <pre>moves the binding of a TypeEngine object from "schema/statement creation" time into "compilation" time</pre> <h3>Modified Paths</h3> <ul> <li><a href="#sqlalchemytrunklibsqlalchemyansisqlpy">sqlalchemy/trunk/lib/sqlalchemy/ansisql.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemydatabasesfirebirdpy">sqlalchemy/trunk/lib/sqlalchemy/databases/firebird.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemydatabasesmssqlpy">sqlalchemy/trunk/lib/sqlalchemy/databases/mssql.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemydatabasesmysqlpy">sqlalchemy/trunk/lib/sqlalchemy/databases/mysql.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemydatabasesoraclepy">sqlalchemy/trunk/lib/sqlalchemy/databases/oracle.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemydatabasespostgrespy">sqlalchemy/trunk/lib/sqlalchemy/databases/postgres.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemydatabasessqlitepy">sqlalchemy/trunk/lib/sqlalchemy/databases/sqlite.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemyenginepy">sqlalchemy/trunk/lib/sqlalchemy/engine.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemyextproxypy">sqlalchemy/trunk/lib/sqlalchemy/ext/proxy.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemyschemapy">sqlalchemy/trunk/lib/sqlalchemy/schema.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemysqlpy">sqlalchemy/trunk/lib/sqlalchemy/sql.py</a></li> <li><a href="#sqlalchemytrunklibsqlalchemytypespy">sqlalchemy/trunk/lib/sqlalchemy/types.py</a></li> <li><a href="#sqlalchemytrunktestproxy_enginepy">sqlalchemy/trunk/test/proxy_engine.py</a></li> </ul> </div> <div id="patch"> <h3>Diff</h3> <a id="sqlalchemytrunklibsqlalchemyansisqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/ansisql.py (1257 => 1258)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/ansisql.py 2006-04-04 21:40:18 UTC (rev 1257) +++ sqlalchemy/trunk/lib/sqlalchemy/ansisql.py 2006-04-06 01:15:46 UTC (rev 1258) </span><span class="lines">@@ -189,7 +189,10 @@ </span><span class="cx"> </span><span class="cx"> def visit_index(self, index): </span><span class="cx"> self.strings[index] = index.name </span><del>- </del><ins>+ + def visit_typeclause(self, typeclause): + self.strings[typeclause] = typeclause.type.engine_impl(self.engine).get_col_spec() + </ins><span class="cx"> def visit_textclause(self, textclause): </span><span class="cx"> if textclause.parens and len(textclause.text): </span><span class="cx"> self.strings[textclause] = "(" + textclause.text + ")" </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemydatabasesfirebirdpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/databases/firebird.py (1257 => 1258)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/databases/firebird.py 2006-04-04 21:40:18 UTC (rev 1257) +++ sqlalchemy/trunk/lib/sqlalchemy/databases/firebird.py 2006-04-06 01:15:46 UTC (rev 1258) </span><span class="lines">@@ -238,7 +238,7 @@ </span><span class="cx"> class FBSchemaGenerator(ansisql.ANSISchemaGenerator): </span><span class="cx"> def get_column_specification(self, column, override_pk=False, **kwargs): </span><span class="cx"> colspec = column.name </span><del>- colspec += " " + column.type.get_col_spec() </del><ins>+ colspec += " " + column.type.engine_impl(self.engine).get_col_spec() </ins><span class="cx"> default = self.get_column_default_string(column) </span><span class="cx"> if default is not None: </span><span class="cx"> colspec += " DEFAULT " + default </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemydatabasesmssqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/databases/mssql.py (1257 => 1258)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/databases/mssql.py 2006-04-04 21:40:18 UTC (rev 1257) +++ sqlalchemy/trunk/lib/sqlalchemy/databases/mssql.py 2006-04-06 01:15:46 UTC (rev 1258) </span><span class="lines">@@ -460,7 +460,7 @@ </span><span class="cx"> </span><span class="cx"> class MSSQLSchemaGenerator(ansisql.ANSISchemaGenerator): </span><span class="cx"> def get_column_specification(self, column, override_pk=False, first_pk=False): </span><del>- colspec = column.name + " " + column.type.get_col_spec() </del><ins>+ colspec = column.name + " " + column.type.engine_impl(self.engine).get_col_spec() </ins><span class="cx"> </span><span class="cx"> # install a IDENTITY Sequence if we have an implicit IDENTITY column </span><span class="cx"> if column.primary_key and isinstance(column.type, types.Integer): </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemydatabasesmysqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/databases/mysql.py (1257 => 1258)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/databases/mysql.py 2006-04-04 21:40:18 UTC (rev 1257) +++ sqlalchemy/trunk/lib/sqlalchemy/databases/mysql.py 2006-04-06 01:15:46 UTC (rev 1258) </span><span class="lines">@@ -263,7 +263,7 @@ </span><span class="cx"> </span><span class="cx"> class MySQLSchemaGenerator(ansisql.ANSISchemaGenerator): </span><span class="cx"> def get_column_specification(self, column, override_pk=False, first_pk=False): </span><del>- colspec = column.name + " " + column.type.get_col_spec() </del><ins>+ colspec = column.name + " " + column.type.engine_impl(self.engine).get_col_spec() </ins><span class="cx"> default = self.get_column_default_string(column) </span><span class="cx"> if default is not None: </span><span class="cx"> colspec += " DEFAULT " + default </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemydatabasesoraclepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/databases/oracle.py (1257 => 1258)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/databases/oracle.py 2006-04-04 21:40:18 UTC (rev 1257) +++ sqlalchemy/trunk/lib/sqlalchemy/databases/oracle.py 2006-04-06 01:15:46 UTC (rev 1258) </span><span class="lines">@@ -306,7 +306,7 @@ </span><span class="cx"> class OracleSchemaGenerator(ansisql.ANSISchemaGenerator): </span><span class="cx"> def get_column_specification(self, column, override_pk=False, **kwargs): </span><span class="cx"> colspec = column.name </span><del>- colspec += " " + column.type.get_col_spec() </del><ins>+ colspec += " " + column.type.engine_impl(self.engine).get_col_spec() </ins><span class="cx"> default = self.get_column_default_string(column) </span><span class="cx"> if default is not None: </span><span class="cx"> colspec += " DEFAULT " + default </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemydatabasespostgrespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/databases/postgres.py (1257 => 1258)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/databases/postgres.py 2006-04-04 21:40:18 UTC (rev 1257) +++ sqlalchemy/trunk/lib/sqlalchemy/databases/postgres.py 2006-04-06 01:15:46 UTC (rev 1258) </span><span class="lines">@@ -305,7 +305,7 @@ </span><span class="cx"> if column.primary_key and isinstance(column.type, types.Integer) and (column.default is None or (isinstance(column.default, schema.Sequence) and column.default.optional)): </span><span class="cx"> colspec += " SERIAL" </span><span class="cx"> else: </span><del>- colspec += " " + column.type.get_col_spec() </del><ins>+ colspec += " " + column.type.engine_impl(self.engine).get_col_spec() </ins><span class="cx"> default = self.get_column_default_string(column) </span><span class="cx"> if default is not None: </span><span class="cx"> colspec += " DEFAULT " + default </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemydatabasessqlitepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/databases/sqlite.py (1257 => 1258)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/databases/sqlite.py 2006-04-04 21:40:18 UTC (rev 1257) +++ sqlalchemy/trunk/lib/sqlalchemy/databases/sqlite.py 2006-04-06 01:15:46 UTC (rev 1258) </span><span class="lines">@@ -241,7 +241,7 @@ </span><span class="cx"> </span><span class="cx"> class SQLiteSchemaGenerator(ansisql.ANSISchemaGenerator): </span><span class="cx"> def get_column_specification(self, column, override_pk=False, **kwargs): </span><del>- colspec = column.name + " " + column.type.get_col_spec() </del><ins>+ colspec = column.name + " " + column.type.engine_impl(self.engine).get_col_spec() </ins><span class="cx"> default = self.get_column_default_string(column) </span><span class="cx"> if default is not None: </span><span class="cx"> colspec += " DEFAULT " + default </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyenginepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/engine.py (1257 => 1258)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/engine.py 2006-04-04 21:40:18 UTC (rev 1257) +++ sqlalchemy/trunk/lib/sqlalchemy/engine.py 2006-04-06 01:15:46 UTC (rev 1258) </span><span class="lines">@@ -319,7 +319,7 @@ </span><span class="cx"> self.positional = True </span><span class="cx"> else: </span><span class="cx"> raise DBAPIError("Unsupported paramstyle '%s'" % self._paramstyle) </span><del>- </del><ins>+ </ins><span class="cx"> def type_descriptor(self, typeobj): </span><span class="cx"> """provides a database-specific TypeEngine object, given the generic object </span><span class="cx"> which comes from the types module. Subclasses will usually use the adapt_type() </span><span class="lines">@@ -808,7 +808,7 @@ </span><span class="cx"> rec = self.props[key.lower()] </span><span class="cx"> else: </span><span class="cx"> rec = self.props[key] </span><del>- return rec[0].convert_result_value(row[rec[1]], self.engine) </del><ins>+ return rec[0].engine_impl(self.engine).convert_result_value(row[rec[1]], self.engine) </ins><span class="cx"> </span><span class="cx"> def __iter__(self): </span><span class="cx"> while True: </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyextproxypy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/ext/proxy.py (1257 => 1258)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/ext/proxy.py 2006-04-04 21:40:18 UTC (rev 1257) +++ sqlalchemy/trunk/lib/sqlalchemy/ext/proxy.py 2006-04-06 01:15:46 UTC (rev 1258) </span><span class="lines">@@ -36,11 +36,6 @@ </span><span class="cx"> return None </span><span class="cx"> return e.oid_column_name() </span><span class="cx"> </span><del>- def type_descriptor(self, typeobj): - """Proxy point: return a ProxyTypeEngine - """ - return ProxyTypeEngine(self, typeobj) - </del><span class="cx"> def __getattr__(self, attr): </span><span class="cx"> # call get_engine() to give subclasses a chance to change </span><span class="cx"> # connection establishment behavior </span><span class="lines">@@ -116,37 +111,3 @@ </span><span class="cx"> self.storage.engine = engine </span><span class="cx"> </span><span class="cx"> </span><del>-class ProxyType(object): - """ProxyType base class; used by ProxyTypeEngine to construct proxying - types - """ - def __init__(self, engine, typeobj): - self._engine = engine - self.typeobj = typeobj - - def __getattribute__(self, attr): - if attr.startswith('__') and attr.endswith('__'): - return object.__getattribute__(self, attr) - - engine = object.__getattribute__(self, '_engine').engine - typeobj = object.__getattribute__(self, 'typeobj') - return getattr(engine.type_descriptor(typeobj), attr) - - def __repr__(self): - return '<Proxy %s>' % (object.__getattribute__(self, 'typeobj')) - -class ProxyTypeEngine(object): - """Proxy type engine; creates dynamic proxy type subclass that is instance - of actual type, but proxies engine-dependant operations through the proxy - engine. - """ - def __new__(cls, engine, typeobj): - """Create a new subclass of ProxyType and typeobj - so that internal isinstance() calls will get the expected result. - """ - if isinstance(typeobj, type): - typeclass = typeobj - else: - typeclass = typeobj.__class__ - typed = type('ProxyTypeHelper', (ProxyType, typeclass), {}) - return typed(engine, typeobj) </del></span></pre></div> <a id="sqlalchemytrunklibsqlalchemyschemapy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/schema.py (1257 => 1258)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/schema.py 2006-04-04 21:40:18 UTC (rev 1257) +++ sqlalchemy/trunk/lib/sqlalchemy/schema.py 2006-04-06 01:15:46 UTC (rev 1258) </span><span class="lines">@@ -163,7 +163,6 @@ </span><span class="cx"> if column.primary_key: </span><span class="cx"> self.primary_key.append(column) </span><span class="cx"> column.table = self </span><del>- column.type = self.engine.type_descriptor(column.type) </del><span class="cx"> </span><span class="cx"> def append_index(self, index): </span><span class="cx"> self.indexes[index.name] = index </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemysqlpy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/sql.py (1257 => 1258)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-04-04 21:40:18 UTC (rev 1257) +++ sqlalchemy/trunk/lib/sqlalchemy/sql.py 2006-04-06 01:15:46 UTC (rev 1258) </span><span class="lines">@@ -139,17 +139,11 @@ </span><span class="cx"> or </span><span class="cx"> cast(table.c.timestamp, DATE) </span><span class="cx"> """ </span><del>- engine = kwargs.get('engine', None) - if engine is None: - engine = getattr(clause, 'engine', None) - if engine is not None: - totype_desc = engine.type_descriptor(totype) - # handle non-column clauses (e.g. cast(1234, TEXT) - if not hasattr(clause, 'label'): - clause = literal(clause) - return Function('CAST', clause.label(totype_desc.get_col_spec()), type=totype, **kwargs) - else: - raise InvalidRequestError("No engine available, cannot generate cast for " + str(clause) + " to type " + str(totype)) </del><ins>+ # handle non-column clauses (e.g. cast(1234, TEXT) + if not hasattr(clause, 'label'): + clause = literal(clause) + totype = sqltypes.to_instance(totype) + return Function('CAST', CompoundClause("AS", clause, TypeClause(totype)), type=totype, **kwargs) </ins><span class="cx"> </span><span class="cx"> def exists(*args, **params): </span><span class="cx"> params['correlate'] = True </span><span class="lines">@@ -295,7 +289,8 @@ </span><span class="cx"> def visit_clauselist(self, list):pass </span><span class="cx"> def visit_function(self, func):pass </span><span class="cx"> def visit_label(self, label):pass </span><del>- </del><ins>+ def visit_typeclause(self, typeclause):pass + </ins><span class="cx"> class Compiled(ClauseVisitor): </span><span class="cx"> """represents a compiled SQL expression. the __str__ method of the Compiled object </span><span class="cx"> should produce the actual text of the statement. Compiled objects are specific to the </span><span class="lines">@@ -671,13 +666,7 @@ </span><span class="cx"> self.key = key </span><span class="cx"> self.value = value </span><span class="cx"> self.shortname = shortname </span><del>- self.type = type or sqltypes.NULLTYPE - def _get_convert_type(self, engine): - try: - return self._converted_type - except AttributeError: - self._converted_type = engine.type_descriptor(self.type) - return self._converted_type </del><ins>+ self.type = sqltypes.to_instance(type) </ins><span class="cx"> def accept_visitor(self, visitor): </span><span class="cx"> visitor.visit_bindparam(self) </span><span class="cx"> def _get_from_objects(self): </span><span class="lines">@@ -685,7 +674,7 @@ </span><span class="cx"> def copy_container(self): </span><span class="cx"> return BindParamClause(self.key, self.value, self.shortname, self.type) </span><span class="cx"> def typeprocess(self, value, engine): </span><del>- return self._get_convert_type(engine).convert_bind_param(value, engine) </del><ins>+ return self.type.engine_impl(engine).convert_bind_param(value, engine) </ins><span class="cx"> def compare(self, other): </span><span class="cx"> """compares this BindParamClause to the given clause. </span><span class="cx"> </span><span class="lines">@@ -695,7 +684,14 @@ </span><span class="cx"> def _make_proxy(self, selectable, name = None): </span><span class="cx"> return self </span><span class="cx"> # return self.obj._make_proxy(selectable, name=self.name) </span><del>- </del><ins>+ +class TypeClause(ClauseElement): + """handles a type keyword in a SQL statement""" + def __init__(self, type): + self.type = type + def accept_visitor(self, visitor): + visitor.visit_typeclause(self) + </ins><span class="cx"> class TextClause(ClauseElement): </span><span class="cx"> """represents literal a SQL text fragment. public constructor is the </span><span class="cx"> text() function. </span><span class="lines">@@ -714,7 +710,7 @@ </span><span class="cx"> self.typemap = typemap </span><span class="cx"> if typemap is not None: </span><span class="cx"> for key in typemap.keys(): </span><del>- typemap[key] = engine.type_descriptor(typemap[key]) </del><ins>+ typemap[key] = sqltypes.to_instance(typemap[key]) </ins><span class="cx"> def repl(m): </span><span class="cx"> self.bindparams[m.group(1)] = bindparam(m.group(1)) </span><span class="cx"> return ":%s" % m.group(1) </span><span class="lines">@@ -820,11 +816,9 @@ </span><span class="cx"> """describes a SQL function. extends ClauseList to provide comparison operators.""" </span><span class="cx"> def __init__(self, name, *clauses, **kwargs): </span><span class="cx"> self.name = name </span><del>- self.type = kwargs.get('type', sqltypes.NULLTYPE) </del><ins>+ self.type = sqltypes.to_instance(kwargs.get('type', None)) </ins><span class="cx"> self.packagenames = kwargs.get('packagenames', None) or [] </span><span class="cx"> self._engine = kwargs.get('engine', None) </span><del>- if self._engine is not None: - self.type = self._engine.type_descriptor(self.type) </del><span class="cx"> ClauseList.__init__(self, parens=True, *clauses) </span><span class="cx"> key = property(lambda self:self.name) </span><span class="cx"> def append(self, clause): </span><span class="lines">@@ -873,7 +867,7 @@ </span><span class="cx"> self.left = left </span><span class="cx"> self.right = right </span><span class="cx"> self.operator = operator </span><del>- self.type = type </del><ins>+ self.type = sqltypes.to_instance(type) </ins><span class="cx"> self.parens = False </span><span class="cx"> if isinstance(self.left, BinaryClause): </span><span class="cx"> self.left.parens = True </span><span class="lines">@@ -1028,7 +1022,7 @@ </span><span class="cx"> while isinstance(obj, Label): </span><span class="cx"> obj = obj.obj </span><span class="cx"> self.obj = obj </span><del>- self.type = type or sqltypes.NullTypeEngine() </del><ins>+ self.type = sqltypes.to_instance(type) </ins><span class="cx"> obj.parens=True </span><span class="cx"> key = property(lambda s: s.name) </span><span class="cx"> </span><span class="lines">@@ -1049,7 +1043,7 @@ </span><span class="cx"> def __init__(self, text, selectable=None, type=None): </span><span class="cx"> self.key = self.name = self.text = text </span><span class="cx"> self.table = selectable </span><del>- self.type = type or sqltypes.NullTypeEngine() </del><ins>+ self.type = sqltypes.to_instance(type) </ins><span class="cx"> self.__label = None </span><span class="cx"> def _get_label(self): </span><span class="cx"> if self.__label is None: </span></span></pre></div> <a id="sqlalchemytrunklibsqlalchemytypespy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/lib/sqlalchemy/types.py (1257 => 1258)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/lib/sqlalchemy/types.py 2006-04-04 21:40:18 UTC (rev 1257) +++ sqlalchemy/trunk/lib/sqlalchemy/types.py 2006-04-06 01:15:46 UTC (rev 1258) </span><span class="lines">@@ -16,11 +16,22 @@ </span><span class="cx"> import cPickle as pickle </span><span class="cx"> except: </span><span class="cx"> import pickle </span><del>- </del><ins>+ </ins><span class="cx"> class TypeEngine(object): </span><del>- basetypes = [] </del><span class="cx"> def __init__(self, *args, **kwargs): </span><span class="cx"> pass </span><ins>+ def _get_impl_dict(self): + try: + return self._impl_dict + except AttributeError: + self._impl_dict = {} + return self._impl_dict + impl_dict = property(_get_impl_dict) + def engine_impl(self, engine): + try: + return self.impl_dict[engine] + except: + return self.impl_dict.setdefault(engine, engine.type_descriptor(self)) </ins><span class="cx"> def _get_impl(self): </span><span class="cx"> if hasattr(self, '_impl'): </span><span class="cx"> return self._impl </span><span class="lines">@@ -41,7 +52,14 @@ </span><span class="cx"> return {} </span><span class="cx"> def adapt_args(self): </span><span class="cx"> return self </span><del>- </del><ins>+ +def to_instance(typeobj): + if typeobj is None: + return NULLTYPE + elif isinstance(typeobj, type): + return typeobj() + else: + return typeobj </ins><span class="cx"> def adapt_type(typeobj, colspecs): </span><span class="cx"> if isinstance(typeobj, type): </span><span class="cx"> typeobj = typeobj() </span></span></pre></div> <a id="sqlalchemytrunktestproxy_enginepy"></a> <div class="modfile"><h4>Modified: sqlalchemy/trunk/test/proxy_engine.py (1257 => 1258)</h4> <pre class="diff"><span> <span class="info">--- sqlalchemy/trunk/test/proxy_engine.py 2006-04-04 21:40:18 UTC (rev 1257) +++ sqlalchemy/trunk/test/proxy_engine.py 2006-04-06 01:15:46 UTC (rev 1258) </span><span class="lines">@@ -194,7 +194,7 @@ </span><span class="cx"> return 'a' </span><span class="cx"> </span><span class="cx"> def type_descriptor(self, typeobj): </span><del>- if typeobj == types.Integer: </del><ins>+ if isinstance(typeobj, types.Integer): </ins><span class="cx"> return TypeEngineX2() </span><span class="cx"> else: </span><span class="cx"> return TypeEngineSTR() </span><span class="lines">@@ -224,16 +224,16 @@ </span><span class="cx"> engine = ProxyEngine() </span><span class="cx"> engine.storage.engine = EngineA() </span><span class="cx"> </span><del>- a = engine.type_descriptor(sqltypes.Integer) </del><ins>+ a = sqltypes.Integer().engine_impl(engine) </ins><span class="cx"> assert a.convert_bind_param(12, engine) == 24 </span><span class="cx"> assert a.convert_bind_param([1,2,3], engine) == [1, 2, 3, 1, 2, 3] </span><span class="cx"> </span><del>- a2 = engine.type_descriptor(sqltypes.String) </del><ins>+ a2 = sqltypes.String().engine_impl(engine) </ins><span class="cx"> assert a2.convert_bind_param(12, engine) == "'12'" </span><span class="cx"> assert a2.convert_bind_param([1,2,3], engine) == "'[1, 2, 3]'" </span><span class="cx"> </span><span class="cx"> engine.storage.engine = EngineB() </span><del>- b = engine.type_descriptor(sqltypes.Integer) </del><ins>+ b = sqltypes.Integer().engine_impl(engine) </ins><span class="cx"> assert b.convert_bind_param(12, engine) == 'monkey' </span><span class="cx"> assert b.convert_bind_param([1,2,3], engine) == 'monkey' </span><span class="cx"> </span></span></pre> </div> </div> </body> </html> |