From 62743c3be1de686154c3c927f1d6df99e5203b39 Mon Sep 17 00:00:00 2001 From: Daniele Varrazzo Date: Mon, 6 Apr 2020 02:30:29 +1200 Subject: Fixed copy() on DictRow Close #1073. --- NEWS | 1 + lib/extras.py | 8 +++++++- tests/test_extras_dictcursor.py | 29 +++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 29aef12..4cab5cf 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,7 @@ What's new in psycopg 2.8.5 `~psycopg2.extras.LoggingConnection` (:ticket:`#1026`). - `~psycopg2.extensions.Column` objects in `cursor.description` can be sliced (:ticket:`#1034`). +- Fixed `~copy.copy()` of `~psycopg2.extras.DictCursor` rows (:ticket:`#1073`). What's new in psycopg 2.8.4 diff --git a/lib/extras.py b/lib/extras.py index 85c1a44..ccb8b3f 100644 --- a/lib/extras.py +++ b/lib/extras.py @@ -196,6 +196,10 @@ class DictRow(list): def __contains__(self, x): return x in self._index + def __reduce__(self): + # this is apparently useless, but it fixes #1073 + return super(DictRow, self).__reduce__() + def __getstate__(self): return self[:], self._index.copy() @@ -392,6 +396,7 @@ class NamedTupleCursor(_cursor): 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) @@ -410,7 +415,8 @@ class LoggingConnection(_connection): instance from the standard logging module. """ self._logobj = logobj - if _logging and isinstance(logobj, (_logging.Logger, _logging.LoggerAdapter)): + if _logging and isinstance( + logobj, (_logging.Logger, _logging.LoggerAdapter)): self.log = self._logtologger else: self.log = self._logtofile diff --git a/tests/test_extras_dictcursor.py b/tests/test_extras_dictcursor.py index d4bb12f..180d996 100755 --- a/tests/test_extras_dictcursor.py +++ b/tests/test_extras_dictcursor.py @@ -15,6 +15,7 @@ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public # License for more details. +import copy import time import pickle import unittest @@ -158,6 +159,20 @@ class ExtrasDictCursorTests(_DictCursorBase): self.assertEqual(r['b'], r1['b']) self.assertEqual(r._index, r1._index) + def test_copy(self): + curs = self.conn.cursor(cursor_factory=psycopg2.extras.DictCursor) + curs.execute("select 10 as foo, 'hi' as bar") + rv = curs.fetchone() + self.assertEqual(len(rv), 2) + + rv2 = copy.copy(rv) + self.assertEqual(len(rv2), 2) + self.assertEqual(len(rv), 2) + + rv3 = copy.deepcopy(rv) + self.assertEqual(len(rv3), 2) + self.assertEqual(len(rv), 2) + @skip_from_python(3) def test_iter_methods_2(self): curs = self.conn.cursor(cursor_factory=psycopg2.extras.DictCursor) @@ -267,6 +282,20 @@ class ExtrasDictCursorRealTests(_DictCursorBase): self.assertEqual(r['a'], r1['a']) self.assertEqual(r['b'], r1['b']) + def test_copy(self): + curs = self.conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) + curs.execute("select 10 as foo, 'hi' as bar") + rv = curs.fetchone() + self.assertEqual(len(rv), 2) + + rv2 = copy.copy(rv) + self.assertEqual(len(rv2), 2) + self.assertEqual(len(rv), 2) + + rv3 = copy.deepcopy(rv) + self.assertEqual(len(rv3), 2) + self.assertEqual(len(rv), 2) + def testDictCursorRealWithNamedCursorFetchOne(self): self._testWithNamedCursorReal(lambda curs: curs.fetchone()) -- cgit v1.2.1