From 527592a0a52b8c6a09535e785dac63c36cf25ada Mon Sep 17 00:00:00 2001 From: Changaco Date: Tue, 4 Jun 2019 13:44:40 +0200 Subject: [PATCH 1/3] improve the NamedTupleCursor cache test --- tests/test_extras_dictcursor.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/test_extras_dictcursor.py b/tests/test_extras_dictcursor.py index c7f09d54..a4f45c29 100755 --- a/tests/test_extras_dictcursor.py +++ b/tests/test_extras_dictcursor.py @@ -615,17 +615,27 @@ class NamedTupleCursorTest(ConnectingTestCase): self.assertEqual(i + 1, curs.rownumber) def test_cache(self): + NamedTupleCursor._cached_make_nt.cache_clear() + curs = self.conn.cursor() curs.execute("select 10 as a, 20 as b") r1 = curs.fetchone() curs.execute("select 10 as a, 20 as c") r2 = curs.fetchone() + + # Get a new cursor to check that the cache works across multiple ones + curs = self.conn.cursor() curs.execute("select 10 as a, 30 as b") r3 = curs.fetchone() self.assert_(type(r1) is type(r3)) self.assert_(type(r1) is not type(r2)) + cache_info = NamedTupleCursor._cached_make_nt.cache_info() + self.assertEqual(cache_info.hits, 1) + self.assertEqual(cache_info.misses, 2) + self.assertEqual(cache_info.currsize, 2) + def test_max_cache(self): old_func = NamedTupleCursor._cached_make_nt NamedTupleCursor._cached_make_nt = \ From 842e383c0c0e61f03ab6aa822fdf14c61c43d394 Mon Sep 17 00:00:00 2001 From: Changaco Date: Tue, 4 Jun 2019 14:29:06 +0200 Subject: [PATCH 2/3] fix `NamedTupleCursor._cached_make_nt` --- lib/extras.py | 16 +++++++++++----- tests/test_extras_dictcursor.py | 2 +- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/extras.py b/lib/extras.py index 3fdaaf0a..257153ef 100644 --- a/lib/extras.py +++ b/lib/extras.py @@ -372,10 +372,11 @@ class NamedTupleCursor(_cursor): key = tuple(d[0] for d in self.description) if self.description else () return self._cached_make_nt(key) - def _do_make_nt(self, key): + @classmethod + def _do_make_nt(cls, key): fields = [] for s in key: - s = self._re_clean.sub('_', s) + s = cls._re_clean.sub('_', s) # Python identifier cannot start with numbers, namedtuple fields # cannot start with underscore. So... if s[0] == '_' or '0' <= s[0] <= '9': @@ -385,9 +386,14 @@ class NamedTupleCursor(_cursor): nt = namedtuple("Record", fields) return nt - # Exposed for testability, and if someone wants to monkeypatch to tweak - # the cache size. - _cached_make_nt = lru_cache(512)(_do_make_nt) + +@lru_cache(512) +def _cached_make_nt(cls, key): + return cls._do_make_nt(key) + +# Exposed for testability, and if someone wants to monkeypatch to tweak +# the cache size. +NamedTupleCursor._cached_make_nt = classmethod(_cached_make_nt) class LoggingConnection(_connection): diff --git a/tests/test_extras_dictcursor.py b/tests/test_extras_dictcursor.py index a4f45c29..5f205ca9 100755 --- a/tests/test_extras_dictcursor.py +++ b/tests/test_extras_dictcursor.py @@ -639,7 +639,7 @@ class NamedTupleCursorTest(ConnectingTestCase): def test_max_cache(self): old_func = NamedTupleCursor._cached_make_nt NamedTupleCursor._cached_make_nt = \ - lru_cache(8)(NamedTupleCursor._do_make_nt) + lru_cache(8)(NamedTupleCursor._cached_make_nt.__wrapped__) try: recs = [] curs = self.conn.cursor() From 0578c1ab922a8c4565b8af62cbbf98bfd7454ddf Mon Sep 17 00:00:00 2001 From: Daniele Varrazzo Date: Fri, 7 Jun 2019 18:18:48 +0100 Subject: [PATCH 3/3] Mention #928 fixed --- NEWS | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS b/NEWS index 4ff4e602..92518342 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,7 @@ What's new in psycopg 2.8.3 `~psycopg2.extras.ReplicationCursor.start_replication()` method and other facilities to send automatic replication keepalives at periodic intervals (:ticket:`#913`). +- Fixed namedtuples caching introduced in 2.8 (:ticket:`#928`). What's new in psycopg 2.8.2