summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>2019-06-07 18:19:47 +0100
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>2019-06-07 18:19:47 +0100
commit2635f43788b1a777f8517e4a3d2707f29d065c35 (patch)
treebf76010fa3352b8a5186c7946d10cc0557775b9e
parent668d507c34989bd7df97802d853e69c3b79cd128 (diff)
parent0578c1ab922a8c4565b8af62cbbf98bfd7454ddf (diff)
downloadpsycopg2-2635f43788b1a777f8517e4a3d2707f29d065c35.tar.gz
Merge branch 'fix-namedtuple-cache'
-rw-r--r--NEWS1
-rw-r--r--lib/extras.py16
-rwxr-xr-xtests/test_extras_dictcursor.py12
3 files changed, 23 insertions, 6 deletions
diff --git a/NEWS b/NEWS
index 4ff4e60..9251834 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
diff --git a/lib/extras.py b/lib/extras.py
index 3fdaaf0..257153e 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 c7f09d5..5f205ca 100755
--- a/tests/test_extras_dictcursor.py
+++ b/tests/test_extras_dictcursor.py
@@ -615,21 +615,31 @@ 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 = \
- lru_cache(8)(NamedTupleCursor._do_make_nt)
+ lru_cache(8)(NamedTupleCursor._cached_make_nt.__wrapped__)
try:
recs = []
curs = self.conn.cursor()