#36714: Async signals lose ContextVar state due to use of asyncio.gather
----------------------------------+------------------------------------
     Reporter:  Mykhailo Havelia  |                    Owner:  (none)
         Type:  Bug               |                   Status:  new
    Component:  HTTP handling     |                  Version:  dev
     Severity:  Normal            |               Resolution:
     Keywords:  asyncio, signals  |             Triage Stage:  Accepted
    Has patch:  0                 |      Needs documentation:  0
  Needs tests:  0                 |  Patch needs improvement:  0
Easy pickings:  0                 |                    UI/UX:  0
----------------------------------+------------------------------------
Comment (by Mykhailo Havelia):

 Replying to [comment:12 Carlton Gibson]:
 > sync_to_async already propagates the parent context.
 
[https://github.com/django/asgiref/blob/2138f0317d79cedd065571447ae0a7571989550e/tests/test_sync_contextvars.py#L37-L55
 See tests]. Is it not the case that the `asyncio.TaskGroup()` call in
 `_gather` needs to take the context parameter? 🤔
 >
 > I think this is going to be easier to look at in a PR than a series of
 comments on the issue here.

 I’ve already attached the link to the MR above:
 https://github.com/Arfey/django/pull/3/files. Did you have a chance to
 look at it? 🙂

 `sync_to_async` does support modifying contextvars, but what we need is
 the ability to share the same context between tasks running in parallel.
 There’s a test included in my MR that demonstrates exactly what behavior
 we have to achieve


 {{{
 async def test_asend_correct_contextvars_sharing_mix_receivers(self):
     handler1 = self.CtxSyncHandler(self.ctx_var)
     handler2 = self.CtxAsyncHandler(self.ctx_var)
     signal = dispatch.Signal()
     signal.connect(handler1)
     signal.connect(handler2)

     # set custom value outer signal
     self.ctx_var.set(1)

     await signal.asend(self.__class__)

     self.assertEqual(len(handler1.values), 1)
     self.assertEqual(len(handler2.values), 1)
     self.assertEqual(
         sorted([*handler1.values, *handler2.values]),
         [2, 3]
     )
     self.assertEqual(self.ctx_var.get(), 3)
 }}}

 Right now we don't end up with all 3. We only get 2, because `handler1`
 and `handler2` don't share context with each other. The reason is that
 `sync_to_async` copies the context (see:
 
https://github.com/django/asgiref/blob/2138f0317d79cedd065571447ae0a7571989550e/asgiref/sync.py#L483),
 but in our case we need them to share it.
-- 
Ticket URL: <https://code.djangoproject.com/ticket/36714#comment:13>
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 visit 
https://groups.google.com/d/msgid/django-updates/0107019a6d7ed122-85a90716-85ef-4f01-a896-25d70e937454-000000%40eu-central-1.amazonses.com.

Reply via email to