Skip to content

Commit 3f5cb6f

Browse files
committed
Fix cursor cloning issue with deepcopy and regex mongodb#179
1 parent 1615430 commit 3f5cb6f

File tree

2 files changed

+26
-5
lines changed

2 files changed

+26
-5
lines changed

pymongo/cursor.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -841,22 +841,33 @@ def __deepcopy__(self, memo):
841841
return self.__clone(deepcopy=True)
842842

843843
def __deepcopy(self, x, memo=None):
844-
"""Deepcopy helper for the data dictionary.
844+
"""Deepcopy helper for the data dictionary or list.
845845
846846
Regular expressions cannot be deep copied but as they are immutable we
847847
don't have to copy them when cloning.
848848
"""
849-
y = {}
849+
if not hasattr(x, 'items'):
850+
y, is_list, iterator = [], True, enumerate(x)
851+
else:
852+
y, is_list, iterator = {}, False, x.iteritems()
853+
850854
if memo is None:
851855
memo = {}
852856
val_id = id(x)
853857
if val_id in memo:
854858
return memo.get(val_id)
855859
memo[val_id] = y
856-
for key, value in x.iteritems():
857-
if isinstance(value, dict) and not isinstance(value, SON):
860+
861+
for key, value in iterator:
862+
if isinstance(value, (dict, list)) and not isinstance(value, SON):
858863
value = self.__deepcopy(value, memo)
859864
elif not isinstance(value, RE_TYPE):
860865
value = copy.deepcopy(value, memo)
861-
y[copy.deepcopy(key, memo)] = value
866+
867+
if is_list:
868+
y.append(value)
869+
else:
870+
if not isinstance(key, RE_TYPE):
871+
key = copy.deepcopy(key, memo)
872+
y[key] = value
862873
return y

test/test_cursor.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,16 @@ class MyClass(dict):
529529
self.assertTrue(isinstance(cursor2._Cursor__hint, SON))
530530
self.assertEqual(cursor._Cursor__hint, cursor2._Cursor__hint)
531531

532+
def test_deepcopy_cursor_littered_with_regexes(self):
533+
534+
cursor = self.db.test.find({"x": re.compile("^hmmm.*"),
535+
"y": [re.compile("^hmm.*")],
536+
"z": {"a": [re.compile("^hm.*")]},
537+
re.compile("^key.*"): {"a": [re.compile("^hm.*")]}})
538+
539+
cursor2 = copy.deepcopy(cursor)
540+
self.assertEqual(cursor._Cursor__spec, cursor2._Cursor__spec)
541+
532542
def test_add_remove_option(self):
533543
cursor = self.db.test.find()
534544
self.assertEqual(0, cursor._Cursor__query_options())

0 commit comments

Comments
 (0)