#33410: captureOnCommitCallbacks executes callbacks multiple times
-------------------------------------+-------------------------------------
Reporter: flaeppe | Owner: flaeppe
Type: Bug | Status: assigned
Component: Testing framework | Version: dev
Severity: Normal | Resolution:
Keywords: | Triage Stage:
captureOnCommitCallbacks | Unreviewed
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Description changed by flaeppe:
Old description:
> When recursively adding multiple on commit callbacks,
> `captureOnCommitCallbacks` executes some callbacks multiple times. Below
> is a test case to reproduce the error.
>
> {{{
> #!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"),
> ],
> )
> }}}
> }}}
New description:
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"),
],
)
}}}
}}}
--
--
Ticket URL: <https://code.djangoproject.com/ticket/33410#comment:2>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/065.c17b0cdfd8ddc15479aa2264840ee92a%40djangoproject.com.