diff --git a/NEWS b/NEWS index 18588818..725a1ec1 100644 --- a/NEWS +++ b/NEWS @@ -37,6 +37,7 @@ What's new in psycopg 2.4.6 - Fixed pickling of FixedOffsetTimezone objects (ticket #135). - Release the GIL around PQgetResult calls after COPY (ticket #140). - Fixed empty strings handling in composite caster (ticket #141). + - Fixed pickling of RealDictRow objects. What's new in psycopg 2.4.5 diff --git a/lib/extras.py b/lib/extras.py index 22ae3f6e..0c4df569 100644 --- a/lib/extras.py +++ b/lib/extras.py @@ -245,6 +245,13 @@ class RealDictRow(dict): name = self._column_mapping[name] return dict.__setitem__(self, name, value) + def __getstate__(self): + return (self.copy(), self._column_mapping[:]) + + def __setstate__(self, data): + self.update(data[0]) + self._column_mapping = data[1] + class NamedTupleConnection(_connection): """A connection that uses `NamedTupleCursor` automatically.""" diff --git a/tests/test_extras_dictcursor.py b/tests/test_extras_dictcursor.py index 34e571d3..e365c10e 100755 --- a/tests/test_extras_dictcursor.py +++ b/tests/test_extras_dictcursor.py @@ -205,6 +205,18 @@ class ExtrasDictCursorTests(unittest.TestCase): for i, r in enumerate(curs): self.assertEqual(i + 1, curs.rownumber) + def testPickleRealDictRow(self): + import pickle + curs = self.conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) + curs.execute("select 10 as a, 20 as b") + r = curs.fetchone() + d = pickle.dumps(r) + r1 = pickle.loads(d) + self.assertEqual(r, r1) + self.assertEqual(r['a'], r1['a']) + self.assertEqual(r['b'], r1['b']) + self.assertEqual(r._column_mapping, r1._column_mapping) + class NamedTupleCursorTest(unittest.TestCase): def setUp(self):