Skip to content

Commit a96f142

Browse files
committed
Fix bug in mutable thread local related with no_dereference ctx manager added in 0.28.0
1 parent 5e0a678 commit a96f142

File tree

2 files changed

+49
-3
lines changed

2 files changed

+49
-3
lines changed

mongoengine/context_managers.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,12 @@
2020
)
2121

2222

23-
thread_locals = threading.local()
24-
thread_locals.no_dereferencing_class = {}
23+
class MyThreadLocals(threading.local):
24+
def __init__(self):
25+
self.no_dereferencing_class = {}
26+
27+
28+
thread_locals = MyThreadLocals()
2529

2630

2731
def no_dereferencing_active_for_class(cls):

tests/test_context_managers.py

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import random
2+
import time
13
import unittest
4+
from threading import Thread
25

36
import pytest
47
from bson import DBRef
@@ -18,6 +21,31 @@
1821
from tests.utils import MongoDBTestCase
1922

2023

24+
class TestableThread(Thread):
25+
"""
26+
Wrapper around `threading.Thread` that propagates exceptions.
27+
28+
REF: https://gist.github.com/sbrugman/59b3535ebcd5aa0e2598293cfa58b6ab
29+
"""
30+
31+
def __init__(self, *args, **kwargs):
32+
super().__init__(*args, **kwargs)
33+
self.exc = None
34+
35+
def run(self):
36+
try:
37+
super().run()
38+
except BaseException as e:
39+
self.exc = e
40+
# finally:
41+
# del self._target, self._args, self._kwargs
42+
43+
def join(self, timeout=None):
44+
super().join(timeout)
45+
if self.exc:
46+
raise self.exc
47+
48+
2149
class TestContextManagers(MongoDBTestCase):
2250
def test_set_write_concern(self):
2351
class User(Document):
@@ -172,13 +200,27 @@ class Group(Document):
172200
group = Group.objects.first()
173201
assert isinstance(group.ref, DBRef)
174202

175-
# make sure its still off here
203+
# make sure it's still off here
176204
group = Group.objects.first()
177205
assert isinstance(group.ref, DBRef)
178206

179207
group = Group.objects.first()
180208
assert isinstance(group.ref, User)
181209

210+
def run_in_thread(id):
211+
time.sleep(random.uniform(0.1, 0.5)) # Force desync of threads
212+
if id % 2 == 0:
213+
with no_dereference(Group):
214+
group = Group.objects.first()
215+
assert isinstance(group.ref, DBRef)
216+
else:
217+
group = Group.objects.first()
218+
assert isinstance(group.ref, User)
219+
220+
threads = [TestableThread(target=run_in_thread, args=(id,)) for id in range(10)]
221+
_ = [th.start() for th in threads]
222+
_ = [th.join() for th in threads]
223+
182224
def test_no_dereference_context_manager_dbref(self):
183225
"""Ensure that DBRef items in ListFields aren't dereferenced"""
184226

0 commit comments

Comments
 (0)