From: Mike Bayer Date: Wed, 29 Jan 2014 04:43:14 +0000 (-0500) Subject: - Fixed 0.9 regression where the new sortable support for :class:`.RowProxy` X-Git-Tag: rel_0_9_2~19 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=d4c908ded1e9a7923312f3b335835e7e40b6690e;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - Fixed 0.9 regression where the new sortable support for :class:`.RowProxy` would lead to ``TypeError`` when compared to non-tuple types as it attempted to apply tuple() to the "other" object unconditionally. The full range of Python comparison operators have now been implemented on :class:`.RowProxy`, using an approach that guarantees a comparison system that is equivalent to that of a tuple, and the "other" object is only coerced if it's an instance of RowProxy. [ticket:2924] --- diff --git a/doc/build/changelog/changelog_09.rst b/doc/build/changelog/changelog_09.rst index d0cf14f6e1..b6bc07847e 100644 --- a/doc/build/changelog/changelog_09.rst +++ b/doc/build/changelog/changelog_09.rst @@ -14,6 +14,18 @@ .. changelog:: :version: 0.9.2 + .. change:: + :tags: bug, sql + :tickets: 2924, 2848 + + Fixed 0.9 regression where the new sortable support for :class:`.RowProxy` + would lead to ``TypeError`` when compared to non-tuple types as it attempted + to apply tuple() to the "other" object unconditionally. The + full range of Python comparison operators have now been implemented on + :class:`.RowProxy`, using an approach that guarantees a comparison + system that is equivalent to that of a tuple, and the "other" object + is only coerced if it's an instance of RowProxy. + .. change:: :tags: bug, orm :tickets: 2918 diff --git a/lib/sqlalchemy/engine/result.py b/lib/sqlalchemy/engine/result.py index f9e0ca0d29..6c98dae186 100644 --- a/lib/sqlalchemy/engine/result.py +++ b/lib/sqlalchemy/engine/result.py @@ -12,6 +12,7 @@ and :class:`.RowProxy.""" from .. import exc, util from ..sql import expression, sqltypes import collections +import operator # This reconstructor is necessary so that pickles with the C extension or # without use the same Binary format. @@ -125,14 +126,28 @@ class RowProxy(BaseRowProxy): __hash__ = None + def _op(self, other, op): + return op(tuple(self), tuple(other)) \ + if isinstance(other, RowProxy) \ + else op(tuple(self), other) + def __lt__(self, other): - return tuple(self) < tuple(other) + return self._op(other, operator.lt) + + def __le__(self, other): + return self._op(other, operator.le) + + def __ge__(self, other): + return self._op(other, operator.ge) + + def __gt__(self, other): + return self._op(other, operator.gt) def __eq__(self, other): - return other is self or tuple(other) == tuple(self) + return self._op(other, operator.eq) def __ne__(self, other): - return not self.__eq__(other) + return self._op(other, operator.ne) def __repr__(self): return repr(tuple(self)) diff --git a/test/sql/test_query.py b/test/sql/test_query.py index 40c63b1793..8cbd01c661 100644 --- a/test/sql/test_query.py +++ b/test/sql/test_query.py @@ -304,7 +304,7 @@ class QueryTest(fixtures.TestBase): def test_row_comparison(self): - users.insert().execute(user_id = 7, user_name = 'jack') + users.insert().execute(user_id = 7, user_name='jack') rp = users.select().execute().first() self.assert_(rp == rp) @@ -317,6 +317,30 @@ class QueryTest(fixtures.TestBase): self.assert_(not (rp != equal)) self.assert_(not (equal != equal)) + def endless(): + while True: + yield 1 + self.assert_(rp != endless()) + self.assert_(endless() != rp) + + # test that everything compares the same + # as it would against a tuple + import operator + for compare in [False, 8, endless(), 'xyz', (7, 'jack')]: + for op in [ + operator.eq, operator.ne, operator.gt, + operator.lt, operator.ge, operator.le + ]: + eq_( + op(equal, compare), + op(rp, compare) + ) + eq_( + op(compare, equal), + op(compare, rp) + ) + + @testing.provide_metadata def test_column_label_overlap_fallback(self): content = Table('content', self.metadata,