diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-01-28 23:43:14 -0500 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-01-28 23:43:14 -0500 |
commit | d4c908ded1e9a7923312f3b335835e7e40b6690e (patch) | |
tree | 8737395d3494ad31270c1dddb1ade580ceaf8a47 | |
parent | d8d03011b8dafe48b71e9eb3bc756d05da54a7bf (diff) | |
download | sqlalchemy-d4c908ded1e9a7923312f3b335835e7e40b6690e.tar.gz |
- 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]
-rw-r--r-- | doc/build/changelog/changelog_09.rst | 12 | ||||
-rw-r--r-- | lib/sqlalchemy/engine/result.py | 21 | ||||
-rw-r--r-- | test/sql/test_query.py | 26 |
3 files changed, 55 insertions, 4 deletions
diff --git a/doc/build/changelog/changelog_09.rst b/doc/build/changelog/changelog_09.rst index d0cf14f6e..b6bc07847 100644 --- a/doc/build/changelog/changelog_09.rst +++ b/doc/build/changelog/changelog_09.rst @@ -15,6 +15,18 @@ :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 f9e0ca0d2..6c98dae18 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 40c63b179..8cbd01c66 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, |