]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
support accept for chains of joineddispatchers
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 29 Jan 2025 15:10:09 +0000 (10:10 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 29 Jan 2025 15:11:14 +0000 (10:11 -0500)
Fixed issue where creating an :class:`.Engine` using multiple calls to
:meth:`.Engine.execution_options` where a subsequent call involved certain
options such as ``isolation_level`` would lead to an internal error
involving event registration.

Fixes: #12289
Change-Id: Iec5fbc0eb0c5a92dda1ea762872ae992ca816685
(cherry picked from commit fc3623990eeeb415fb076ddc96a0c7974beb2050)

doc/build/changelog/unreleased_20/12289.rst [new file with mode: 0644]
lib/sqlalchemy/event/base.py
test/base/test_events.py
test/engine/test_execute.py

diff --git a/doc/build/changelog/unreleased_20/12289.rst b/doc/build/changelog/unreleased_20/12289.rst
new file mode 100644 (file)
index 0000000..7ac111c
--- /dev/null
@@ -0,0 +1,8 @@
+.. change::
+    :tags: bug, engine
+    :tickets: 12289
+
+    Fixed issue where creating an :class:`.Engine` using multiple calls to
+    :meth:`.Engine.execution_options` where a subsequent call involved certain
+    options such as ``isolation_level`` would lead to an internal error
+    involving event registration.
index a73e86bd2a205155bef4a91cbb3ec2625ab390b7..66dc12996bc04e36fec189c783ddde18cdd3b959 100644 (file)
@@ -380,9 +380,11 @@ class Events(_HasEventsDispatch[_ET]):
             return all(isinstance(target.dispatch, t) for t in types)
 
         def dispatch_parent_is(t: Type[Any]) -> bool:
-            return isinstance(
-                cast("_JoinedDispatcher[_ET]", target.dispatch).parent, t
-            )
+            parent = cast("_JoinedDispatcher[_ET]", target.dispatch).parent
+            while isinstance(parent, _JoinedDispatcher):
+                parent = cast("_JoinedDispatcher[_ET]", parent).parent
+
+            return isinstance(parent, t)
 
         # Mapper, ClassManager, Session override this to
         # also accept classes, scoped_sessions, sessionmakers, etc.
index 7a387e8440dd9783748e53343c39cda03be826c9..ccb53f2bb37af021b0e324d92453683425897bf3 100644 (file)
@@ -978,6 +978,9 @@ class JoinTest(TearDownLocalEventsFixture, fixtures.TestBase):
             def __init__(self, parent):
                 self.dispatch = self.dispatch._join(parent.dispatch)
 
+            def create(self):
+                return TargetElement(self)
+
             def run_event(self, arg):
                 list(self.dispatch.event_one)
                 self.dispatch.event_one(self, arg)
@@ -1044,6 +1047,38 @@ class JoinTest(TearDownLocalEventsFixture, fixtures.TestBase):
             [call(element, 1), call(element, 2), call(element, 3)],
         )
 
+    def test_join_twice(self):
+        """test #12289"""
+
+        l1 = Mock()
+        l2 = Mock()
+
+        first_target_element = self.TargetFactory().create()
+        second_target_element = first_target_element.create()
+
+        event.listen(second_target_element, "event_one", l2)
+        event.listen(first_target_element, "event_one", l1)
+
+        second_target_element.run_event(1)
+        eq_(
+            l1.mock_calls,
+            [call(second_target_element, 1)],
+        )
+        eq_(
+            l2.mock_calls,
+            [call(second_target_element, 1)],
+        )
+
+        first_target_element.run_event(2)
+        eq_(
+            l1.mock_calls,
+            [call(second_target_element, 1), call(first_target_element, 2)],
+        )
+        eq_(
+            l2.mock_calls,
+            [call(second_target_element, 1)],
+        )
+
     def test_parent_class_child_instance_apply_after(self):
         l1 = Mock()
         l2 = Mock()
index 61c422bb56ae8d71093e59567efb846463407603..3291fa304781c961544bcdcbc7a36534ee11b359 100644 (file)
@@ -1782,6 +1782,38 @@ class EngineEventsTest(fixtures.TestBase):
         eq_(canary.be2.call_count, 1)
         eq_(canary.be3.call_count, 2)
 
+    @testing.requires.ad_hoc_engines
+    def test_option_engine_registration_issue_one(self):
+        """test #12289"""
+
+        e1 = create_engine(testing.db.url)
+        e2 = e1.execution_options(foo="bar")
+        e3 = e2.execution_options(isolation_level="AUTOCOMMIT")
+
+        eq_(
+            e3._execution_options,
+            {"foo": "bar", "isolation_level": "AUTOCOMMIT"},
+        )
+
+    @testing.requires.ad_hoc_engines
+    def test_option_engine_registration_issue_two(self):
+        """test #12289"""
+
+        e1 = create_engine(testing.db.url)
+        e2 = e1.execution_options(foo="bar")
+
+        @event.listens_for(e2, "engine_connect")
+        def r1(*arg, **kw):
+            pass
+
+        e3 = e2.execution_options(bat="hoho")
+
+        @event.listens_for(e3, "engine_connect")
+        def r2(*arg, **kw):
+            pass
+
+        eq_(e3._execution_options, {"foo": "bar", "bat": "hoho"})
+
     def test_emit_sql_in_autobegin(self, testing_engine):
         e1 = testing_engine(config.db_url)