From bc84b6233eaa1e7a6302b51f8ab8950534ff1813 Mon Sep 17 00:00:00 2001 From: Daniele Varrazzo Date: Sun, 13 May 2018 23:51:21 +0100 Subject: [PATCH 1/3] Allow non-ascii chars in namedtuple fields They can be valid chars in Python 3. Or maybe not? In which case Python will throw an exception, but that's fine. Fix regression introduced fixing #211 --- NEWS | 2 ++ lib/extras.py | 9 ++++++--- tests/test_extras_dictcursor.py | 9 ++++++++- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index 09b19e5f..a264c907 100644 --- a/NEWS +++ b/NEWS @@ -17,6 +17,8 @@ Other changes: What's new in psycopg 2.7.5 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +- Allow non-ascii chars in namedtuple fields (regression introduced fixing + :ticket':`#211`). - Fixed building on Solaris 11 and derivatives such as SmartOS and illumos (:ticket:`#677`). - Maybe fixed building on MSYS2 (as reported in :ticket:`#658`). diff --git a/lib/extras.py b/lib/extras.py index 1f85d532..9c26ccba 100644 --- a/lib/extras.py +++ b/lib/extras.py @@ -363,12 +363,15 @@ class NamedTupleCursor(_cursor): return def _make_nt(self): + # ascii except alnum and underscore + nochars = ' !"#$%&\'()*+,-./:;<=>?@[\\]^`{|}~' + re_clean = _re.compile('[' + _re.escape(nochars) + ']') + def f(s): - # NOTE: Python 3 actually allows unicode chars in fields - s = _re.sub('[^a-zA-Z0-9_]', '_', s) + s = re_clean.sub('_', s) # Python identifier cannot start with numbers, namedtuple fields # cannot start with underscore. So... - if _re.match('^[0-9_]', s): + if s[0] == '_' or '0' <= s[0] <= '9': s = 'f' + s return s diff --git a/tests/test_extras_dictcursor.py b/tests/test_extras_dictcursor.py index 99bdeee6..2a46fbaf 100755 --- a/tests/test_extras_dictcursor.py +++ b/tests/test_extras_dictcursor.py @@ -19,7 +19,7 @@ from datetime import timedelta import psycopg2 import psycopg2.extras import unittest -from .testutils import ConnectingTestCase, skip_before_postgres +from .testutils import ConnectingTestCase, skip_before_postgres, skip_before_python class ExtrasDictCursorTests(ConnectingTestCase): @@ -357,6 +357,13 @@ class NamedTupleCursorTest(ConnectingTestCase): self.assertEqual(rv.f_column_, 2) self.assertEqual(rv.f3, 3) + @skip_before_python(3) + def test_nonascii_name(self): + curs = self.conn.cursor() + curs.execute('select 1 as \xe5h\xe9') + rv = curs.fetchone() + self.assertEqual(getattr(rv, '\xe5h\xe9'), 1) + def test_minimal_generation(self): # Instrument the class to verify it gets called the minimum number of times. from psycopg2.extras import NamedTupleCursor From eb570488a40ce67fb1df3ed548bda1c57d585084 Mon Sep 17 00:00:00 2001 From: Daniele Varrazzo Date: Mon, 14 May 2018 02:41:32 +0100 Subject: [PATCH 2/3] Test databases from newest to oldest This way we can spot when a feature was not supported yet by the first test failing. --- scripts/travis_test.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts/travis_test.sh b/scripts/travis_test.sh index 0320654a..342e24cc 100755 --- a/scripts/travis_test.sh +++ b/scripts/travis_test.sh @@ -56,15 +56,15 @@ fi # Unsupported postgres versions that we still support # Images built by https://github.com/psycopg/psycopg2-wheels/tree/build-dinosaurs if [[ -n "$TEST_PAST" ]]; then - run_test 7.4 - run_test 8.0 - run_test 8.1 - run_test 8.2 - run_test 8.3 - run_test 8.4 - run_test 9.0 - run_test 9.1 run_test 9.2 + run_test 9.1 + run_test 9.0 + run_test 8.4 + run_test 8.3 + run_test 8.2 + run_test 8.1 + run_test 8.0 + run_test 7.4 fi # Postgres built from master From dd7e5c906fc37c50852619d68d6a347ab5928371 Mon Sep 17 00:00:00 2001 From: Daniele Varrazzo Date: Mon, 14 May 2018 03:11:11 +0100 Subject: [PATCH 3/3] Skipped test on db version not supporting unicode identifiers --- tests/test_extras_dictcursor.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_extras_dictcursor.py b/tests/test_extras_dictcursor.py index 2a46fbaf..d43980af 100755 --- a/tests/test_extras_dictcursor.py +++ b/tests/test_extras_dictcursor.py @@ -358,6 +358,7 @@ class NamedTupleCursorTest(ConnectingTestCase): self.assertEqual(rv.f3, 3) @skip_before_python(3) + @skip_before_postgres(8) def test_nonascii_name(self): curs = self.conn.cursor() curs.execute('select 1 as \xe5h\xe9')