id,summary,reporter,owner,description,type,status,component,version,severity,resolution,keywords,cc,stage,has_patch,needs_docs,needs_tests,needs_better_patch,easy,ui_ux 33410,captureOnCommitCallbacks executes callbacks multiple times.,Petter Friberg,Petter Friberg,"When recursively adding multiple on commit callbacks, `captureOnCommitCallbacks` executes some callbacks multiple times. Below is a test case to reproduce the error. Patch: https://github.com/django/django/pull/15285 {{{ #!div style=""font-size: 80%"" {{{#!python def test_execute_tree(self): """""" A visualisation of the callback tree tested. Each node is expected to be visited only once. The child count of a node symbolises the amount of on commit callbacks. root └── branch_1 ├── branch_2 │ ├── leaf_1 │ └── leaf_2 └── leaf_3 """""" ( branch_1_call_counter, branch_2_call_counter, leaf_1_call_counter, leaf_2_call_counter, leaf_3_call_counter, ) = [itertools.count(start=1) for _ in range(5)] def leaf_3(): next(leaf_3_call_counter) def leaf_2(): next(leaf_2_call_counter) def leaf_1(): next(leaf_1_call_counter) def branch_2(): next(branch_2_call_counter) transaction.on_commit(leaf_1) transaction.on_commit(leaf_2) def branch_1(): next(branch_1_call_counter) transaction.on_commit(branch_2) transaction.on_commit(leaf_3) with self.captureOnCommitCallbacks(execute=True) as callbacks: with transaction.atomic(): transaction.on_commit(branch_1) # Every counter shall only have been called once, starting at 1; next value then # has to be 2 self.assertEqual(next(branch_1_call_counter), 2) self.assertEqual(next(branch_2_call_counter), 2) self.assertEqual(next(leaf_1_call_counter), 2) self.assertEqual(next(leaf_2_call_counter), 2) self.assertEqual(next(leaf_3_call_counter), 2) # Make sure all calls can be seen and the execution order is correct self.assertEqual( [(id(callback), callback.__name__) for callback in callbacks], [ (id(branch_1), ""branch_1""), (id(branch_2), ""branch_2""), (id(leaf_3), ""leaf_3""), (id(leaf_1), ""leaf_1""), (id(leaf_2), ""leaf_2""), ], ) }}} }}}",Bug,closed,Testing framework,4.0,Release blocker,fixed,captureOnCommitCallbacks,Eugene Morozov Adam Johnson,Ready for checkin,1,0,0,0,0,0