From a96f142d2a32bb0ce2317dc40de8d2cbb9e64873 Mon Sep 17 00:00:00 2001 From: Bastien Gerard Date: Mon, 4 Mar 2024 14:35:12 +0100 Subject: [PATCH 1/3] Fix bug in mutable thread local related with no_dereference ctx manager added in 0.28.0 --- mongoengine/context_managers.py | 8 ++++-- tests/test_context_managers.py | 44 ++++++++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/mongoengine/context_managers.py b/mongoengine/context_managers.py index a4c90d476..f16753eea 100644 --- a/mongoengine/context_managers.py +++ b/mongoengine/context_managers.py @@ -20,8 +20,12 @@ ) -thread_locals = threading.local() -thread_locals.no_dereferencing_class = {} +class MyThreadLocals(threading.local): + def __init__(self): + self.no_dereferencing_class = {} + + +thread_locals = MyThreadLocals() def no_dereferencing_active_for_class(cls): diff --git a/tests/test_context_managers.py b/tests/test_context_managers.py index ac9ded729..6d1b76120 100644 --- a/tests/test_context_managers.py +++ b/tests/test_context_managers.py @@ -1,4 +1,7 @@ +import random +import time import unittest +from threading import Thread import pytest from bson import DBRef @@ -18,6 +21,31 @@ from tests.utils import MongoDBTestCase +class TestableThread(Thread): + """ + Wrapper around `threading.Thread` that propagates exceptions. + + REF: https://gist.github.com/sbrugman/59b3535ebcd5aa0e2598293cfa58b6ab + """ + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.exc = None + + def run(self): + try: + super().run() + except BaseException as e: + self.exc = e + # finally: + # del self._target, self._args, self._kwargs + + def join(self, timeout=None): + super().join(timeout) + if self.exc: + raise self.exc + + class TestContextManagers(MongoDBTestCase): def test_set_write_concern(self): class User(Document): @@ -172,13 +200,27 @@ class Group(Document): group = Group.objects.first() assert isinstance(group.ref, DBRef) - # make sure its still off here + # make sure it's still off here group = Group.objects.first() assert isinstance(group.ref, DBRef) group = Group.objects.first() assert isinstance(group.ref, User) + def run_in_thread(id): + time.sleep(random.uniform(0.1, 0.5)) # Force desync of threads + if id % 2 == 0: + with no_dereference(Group): + group = Group.objects.first() + assert isinstance(group.ref, DBRef) + else: + group = Group.objects.first() + assert isinstance(group.ref, User) + + threads = [TestableThread(target=run_in_thread, args=(id,)) for id in range(10)] + _ = [th.start() for th in threads] + _ = [th.join() for th in threads] + def test_no_dereference_context_manager_dbref(self): """Ensure that DBRef items in ListFields aren't dereferenced""" From b12372375f034efb88bbc315db4cfd94feeac48f Mon Sep 17 00:00:00 2001 From: Bastien Gerard Date: Mon, 4 Mar 2024 14:38:08 +0100 Subject: [PATCH 2/3] bump version and update changelog for 0.28.1 --- docs/changelog.rst | 4 ++++ mongoengine/__init__.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 0c767b576..81e1cf59e 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -8,6 +8,10 @@ Development =========== - (Fill this out as you fix issues and develop your features). +Changes in 0.28.1 +================= +- Fix bug related with recent updates to no_dereference context manager #2799 + Changes in 0.28.0 ================= - Fix for uuidRepresentation not read when provided in URI #2741 diff --git a/mongoengine/__init__.py b/mongoengine/__init__.py index e0bf7d76d..334ad0d08 100644 --- a/mongoengine/__init__.py +++ b/mongoengine/__init__.py @@ -29,7 +29,7 @@ ) -VERSION = (0, 28, 0) +VERSION = (0, 28, 1) def get_version(): From 7b230270b1d568363a0797c0f81c4075cc3280c6 Mon Sep 17 00:00:00 2001 From: Bastien Gerard Date: Mon, 4 Mar 2024 19:08:23 +0100 Subject: [PATCH 3/3] remove commented code --- tests/test_context_managers.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/test_context_managers.py b/tests/test_context_managers.py index 6d1b76120..69c8931dd 100644 --- a/tests/test_context_managers.py +++ b/tests/test_context_managers.py @@ -37,8 +37,6 @@ def run(self): super().run() except BaseException as e: self.exc = e - # finally: - # del self._target, self._args, self._kwargs def join(self, timeout=None): super().join(timeout)