Yury Selivanov wrote:
> On Mon, Jun 21, 2021 at 7:20 PM Mark Gordon msg...@gmail.com wrote:
> > Yeah, it would indeed inherit the copy. We could, theoretically, make
> > asyncio.Task accept context objects and not copy them, but what would
> > that
> > give us?
> > My main thinking was to just be similar to the closest synchronous analog
> > I'm aware of, contextvars.Context.run. I would think an explanation of why
> > the Context object API exists as it does, letting you manipulate and run in
> > contexts directly, would equally motivate the async analogs. Maybe the
> > exception would be if this API exists purely just to support async tasks
> > (then maybe it should be private?).
> > At any rate, the issue attached to the pull requests gives one example of
> > seeking to do asyncio tests with providing fixture data through an existing
> > context object. I could also imagine a use case of wanting to track the
> > number of database requests made within a logical request that may span
> > multiple tasks. Having the subtasks inherit the same context could help
> > with this.
> > To track things like database requests just put a mutable object in the
> context somewhere at the top level, referenced by a well-known contextvar
> in your code. That single object will be part of all contexts derived from
> the top one throughout the application lifecycle.

Using mutable objects in contexts seems strange as it works against their copy 
semantics. I agree it will work in this use case.

> > *Ultimately, contextvars enable implicit flow
> > of information from outer code to nested code and not vice versa. *
> > Just to clarify, are you stating an established position of the python
> > community or is this your personal view of how context vars should be used?
> > I'm stating this as the original contextvars PEP author and implementer. I

Oh very cool, thanks for taking the time to look at this.

> don't see how the reverse flow is possible to implement within all
> restrictions of the design space. To propagate information from nested
> calls to outer calls just use mutable objects, as I outlined above.
> > Additionally (obviously) it will also be running in a separate task.
> > There's no way around that, unfortunately. Even if we add some kind of
> > helper to run coroutines in a context, there still we be a task object
> > that
> > iterates the coroutine.
> > I was just pointing out that the stated work-around requires creating an
> > additional task to run the called coroutine rather than running directly in
> > the calling task.
> > Yes, and I'm saying that running a coroutine "directly on the stack" within
> some context is not technically possible, without wrapping it into a Task.

Is there something wrong with the solution of changing the context associated 
with a task before execution, yielding, run the coroutine, then swap it back 
and yield again when the coroutine exits? This is the hacky solution that 
appears to work for me on python 3.8 if I force use of _PyTask (and the linked 
PR extends this idea to work for the CTask implementation).

import asyncio.tasks
asyncio.tasks.Task = asyncio.tasks._PyTask
import asyncio
import contextvars

var = contextvars.ContextVar('var', default=123)

async def run_in_context(context, coro):
    task = asyncio.current_task()
    prev_context = task._context
    task._context = context
    await asyncio.sleep(0)

    try:
        return await coro
    finally:
        task._context = prev_context  
        await asyncio.sleep(0)

async def foo(x):
    old_val = var.get()
    var.set(x)
    return old_val

async def main():
    var.set(5)

    context = contextvars.Context()
    print(await run_in_context(context, foo(555))) # 123
    print(var.get()) # 5
    print(await run_in_context(context, foo(999))) # 555
    print(var.get()) # 5
    print(context.run(var.get)) # 999

asyncio.run(main())


> > I guess we can add a keyword argument to asyncio.create_task() for that.
> > It
> > is an open question if the task factory would just use the passed context
> > object or would copy it first. I'm leaning towards the latter.
> > My vote would be for not a copy as mentioned above.
> > Having a asyncio.run_in_context(context, coro()) API is more important as
> > this feature is currently completely missing. So happy to table this if we
> > can't decide on if/what semantics this task kwarg API change should have.
> > Is asyncio.run_in_context() a version of asyncio.run() or a shortcut for
> Context.run(asyncio.create_task, coro)?

Yeah I mean it to be more or less the same except without creating a new task 
(which maybe is impossible?).

> Yury
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/PGOFDKXV3GSV3IWRW7Y72MMYVXYLALK3/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to