From 2aa00c49d7a1a783ff50832f2de7760bfaaccf6d Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Thu, 21 Nov 2013 13:30:32 -0500 Subject: [PATCH] - Fixed bug which prevented the ``serializer`` extension from working correctly with table or column names that contain non-ASCII characters. [ticket:2869] --- doc/build/changelog/changelog_08.rst | 9 +++++++++ lib/sqlalchemy/ext/serializer.py | 8 ++++---- lib/sqlalchemy/sql/selectable.py | 2 +- lib/sqlalchemy/testing/assertions.py | 2 +- test/ext/test_serializer.py | 24 +++++++++++++++++++++--- 5 files changed, 36 insertions(+), 9 deletions(-) diff --git a/doc/build/changelog/changelog_08.rst b/doc/build/changelog/changelog_08.rst index 3a5844bbb4..dde98e82f5 100644 --- a/doc/build/changelog/changelog_08.rst +++ b/doc/build/changelog/changelog_08.rst @@ -11,6 +11,15 @@ .. changelog:: :version: 0.8.4 + .. change:: + :tags: bug, ext + :tickets: 2869 + :versions: 0.9.0b2 + + Fixed bug which prevented the ``serializer`` extension from working + correctly with table or column names that contain non-ASCII + characters. + .. change:: :tags: bug, orm :tickets: 2818 diff --git a/lib/sqlalchemy/ext/serializer.py b/lib/sqlalchemy/ext/serializer.py index 8abd1fdf3c..672e077ad1 100644 --- a/lib/sqlalchemy/ext/serializer.py +++ b/lib/sqlalchemy/ext/serializer.py @@ -58,7 +58,7 @@ from ..orm.interfaces import MapperProperty from ..orm.attributes import QueryableAttribute from .. import Table, Column from ..engine import Engine -from ..util import pickle, byte_buffer, b64encode, b64decode +from ..util import pickle, byte_buffer, b64encode, b64decode, text_type import re @@ -80,9 +80,9 @@ def Serializer(*args, **kw): id = "mapperprop:" + b64encode(pickle.dumps(obj.parent.class_)) + \ ":" + obj.key elif isinstance(obj, Table): - id = "table:" + str(obj) + id = "table:" + text_type(obj.key) elif isinstance(obj, Column) and isinstance(obj.table, Table): - id = "column:" + str(obj.table) + ":" + obj.key + id = "column:" + text_type(obj.table.key) + ":" + text_type(obj.key) elif isinstance(obj, Session): id = "session:" elif isinstance(obj, Engine): @@ -112,7 +112,7 @@ def Deserializer(file, metadata=None, scoped_session=None, engine=None): return None def persistent_load(id): - m = our_ids.match(str(id)) + m = our_ids.match(text_type(id)) if not m: return None else: diff --git a/lib/sqlalchemy/sql/selectable.py b/lib/sqlalchemy/sql/selectable.py index 0468401104..708103fc69 100644 --- a/lib/sqlalchemy/sql/selectable.py +++ b/lib/sqlalchemy/sql/selectable.py @@ -246,7 +246,7 @@ class FromClause(Selectable): :param column: the target :class:`.ColumnElement` to be matched :param require_embedded: only return corresponding columns for - the given :class:`.ColumnElement`, if the given :class:`.ColumnElement` + the given :class:`.ColumnElement`, if the given :class:`.ColumnElement` is actually present within a sub-element of this :class:`.FromClause`. Normally the column will match if it merely shares a common ancestor with one of the exported diff --git a/lib/sqlalchemy/testing/assertions.py b/lib/sqlalchemy/testing/assertions.py index 8a859c0b1d..ebd1b424d4 100644 --- a/lib/sqlalchemy/testing/assertions.py +++ b/lib/sqlalchemy/testing/assertions.py @@ -235,7 +235,7 @@ class AssertsCompiledSQL(object): if util.py3k: param_str = param_str.encode('utf-8').decode('ascii', 'ignore') - print("\nSQL String:\n" + util.text_type(c) + param_str) + print("\nSQL String:\n" + util.text_type(c).encode('utf-8') + param_str) cc = re.sub(r'[\n\t]', '', util.text_type(c)) diff --git a/test/ext/test_serializer.py b/test/ext/test_serializer.py index 1950bd2d17..ffeac55c1e 100644 --- a/test/ext/test_serializer.py +++ b/test/ext/test_serializer.py @@ -1,13 +1,15 @@ +# coding: utf-8 from sqlalchemy.ext import serializer from sqlalchemy import testing from sqlalchemy import Integer, String, ForeignKey, select, \ - desc, func, util + desc, func, util, MetaData from sqlalchemy.testing.schema import Table from sqlalchemy.testing.schema import Column from sqlalchemy.orm import relationship, sessionmaker, scoped_session, \ class_mapper, mapper, joinedload, configure_mappers, aliased -from sqlalchemy.testing import eq_ +from sqlalchemy.testing import eq_, AssertsCompiledSQL +from sqlalchemy.util import u, ue from sqlalchemy.testing import fixtures @@ -19,7 +21,7 @@ class Address(fixtures.ComparableEntity): users = addresses = Session = None -class SerializeTest(fixtures.MappedTest): +class SerializeTest(AssertsCompiledSQL, fixtures.MappedTest): run_setup_mappers = 'once' run_inserts = 'once' @@ -171,6 +173,22 @@ class SerializeTest(fixtures.MappedTest): x = serializer.loads(ser, users.metadata) eq_(str(r), str(x)) + def test_unicode(self): + m = MetaData() + t = Table(ue('\u6e2c\u8a66'), m, + Column(ue('\u6e2c\u8a66_id'), Integer)) + + expr = select([t]).where(t.c[ue('\u6e2c\u8a66_id')] == 5) + + expr2 = serializer.loads(serializer.dumps(expr, -1), m) + + self.assert_compile( + expr2, + ue('SELECT "\u6e2c\u8a66"."\u6e2c\u8a66_id" FROM "\u6e2c\u8a66" ' + 'WHERE "\u6e2c\u8a66"."\u6e2c\u8a66_id" = :\u6e2c\u8a66_id_1'), + dialect="default" + ) + if __name__ == '__main__': testing.main() -- 2.47.3