From: Mike Bayer Date: Tue, 9 Nov 2010 16:40:23 +0000 (-0500) Subject: - Implemented sequence check capability for the C X-Git-Tag: rel_0_7b1~282 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a41c50ad63f688cce99fdb9920c4f7c24ef0c866;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - Implemented sequence check capability for the C version of RowProxy, as well as 2.7 style "collections.Sequence" registration for RowProxy. [ticket:1871] --- diff --git a/CHANGES b/CHANGES index 65715de54b..1b4f7cd348 100644 --- a/CHANGES +++ b/CHANGES @@ -19,6 +19,12 @@ CHANGES is an outer join, thus allowing primary rows without a referenced child row to be correctly returned in results. [ticket:1954] + +- engine + - Implemented sequence check capability for the C + version of RowProxy, as well as 2.7 style + "collections.Sequence" registration for RowProxy. + [ticket:1871] - mysql - Fixed error handling for Jython + zxjdbc, such that diff --git a/lib/sqlalchemy/cextension/resultproxy.c b/lib/sqlalchemy/cextension/resultproxy.c index 7404b9ed20..73e1273457 100644 --- a/lib/sqlalchemy/cextension/resultproxy.c +++ b/lib/sqlalchemy/cextension/resultproxy.c @@ -326,6 +326,12 @@ BaseRowProxy_subscript(BaseRowProxy *self, PyObject *key) } } +static PyObject * +BaseRowProxy_getitem(PyObject *self, Py_ssize_t i) +{ + return BaseRowProxy_subscript((BaseRowProxy*)self, PyInt_FromSsize_t(i)); +} + static PyObject * BaseRowProxy_getattro(BaseRowProxy *self, PyObject *name) { @@ -506,7 +512,7 @@ static PySequenceMethods BaseRowProxy_as_sequence = { (lenfunc)BaseRowProxy_length, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ - 0, /* sq_item */ + (ssizeargfunc)BaseRowProxy_getitem, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py index ef402ceea4..10b2b67827 100644 --- a/lib/sqlalchemy/engine/base.py +++ b/lib/sqlalchemy/engine/base.py @@ -2090,6 +2090,14 @@ class RowProxy(BaseRowProxy): def itervalues(self): return iter(self) +try: + # Register RowProxy with Sequence, + # so sequence protocol is implemented + import collections + collections.Sequence.register(RowProxy) +except ImportError: + pass + class ResultMetaData(object): """Handle cursor.description, applying additional info from an execution diff --git a/lib/sqlalchemy/test/requires.py b/lib/sqlalchemy/test/requires.py index 14c548f122..5b68a18e17 100644 --- a/lib/sqlalchemy/test/requires.py +++ b/lib/sqlalchemy/test/requires.py @@ -252,6 +252,12 @@ def sane_rowcount(fn): skip_if(lambda: not testing.db.dialect.supports_sane_rowcount) ) +def cextensions(fn): + return _chain_decorators_on( + fn, + skip_if(lambda: not _has_cextensions(), "C extensions not installed") + ) + def dbapi_lastrowid(fn): return _chain_decorators_on( fn, @@ -279,7 +285,14 @@ def python2(fn): "Python version 2.xx is required." ) ) - + +def _has_cextensions(): + try: + from sqlalchemy import cresultproxy, cprocessors + return True + except ImportError: + return False + def _has_sqlite(): from sqlalchemy import create_engine try: diff --git a/test/aaa_profiling/test_resultset.py b/test/aaa_profiling/test_resultset.py index d71b8dab84..bd9d3ae50f 100644 --- a/test/aaa_profiling/test_resultset.py +++ b/test/aaa_profiling/test_resultset.py @@ -30,13 +30,13 @@ class ResultSetTest(TestBase, AssertsExecutionResults): metadata.drop_all() @profiling.function_call_count(14416, versions={'2.4': 13214, - '2.6+cextension': 409}) + '2.6+cextension': 409, '2.7+cextension':438}) def test_string(self): [tuple(row) for row in t.select().execute().fetchall()] # sqlite3 returns native unicode. so shouldn't be an increase here. @profiling.function_call_count(14396, versions={'2.4': 13214, - '2.6+cextension': 409}) + '2.6+cextension': 409, '2.7+cextension':409}) def test_unicode(self): [tuple(row) for row in t2.select().execute().fetchall()] diff --git a/test/engine/test_execute.py b/test/engine/test_execute.py index ee5953c749..bbec402b20 100644 --- a/test/engine/test_execute.py +++ b/test/engine/test_execute.py @@ -289,6 +289,37 @@ class ResultProxyTest(TestBase): assert_raises(AssertionError, t.delete().execute) finally: engine.dialect.execution_ctx_cls = execution_ctx_cls + + def test_rowproxy_is_sequence(self): + import collections + from sqlalchemy.engine import RowProxy + + row = RowProxy(object(), ['value'], [None], {'key' + : (None, 0), 0: (None, 0)}) + assert isinstance(row, collections.Sequence) + + @testing.requires.cextensions + def test_row_c_sequence_check(self): + import csv + import collections + from StringIO import StringIO + + metadata = MetaData() + metadata.bind = 'sqlite://' + users = Table('users', metadata, + Column('id', Integer, primary_key=True), + Column('name', String(40)), + ) + users.create() + + users.insert().execute(name='Test') + row = users.select().execute().fetchone() + + s = StringIO() + writer = csv.writer(s) + # csv performs PySequenceCheck call + writer.writerow(row) + assert s.getvalue().strip() == '1,Test' class ProxyConnectionTest(TestBase):