From: Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> Date: Mon, 28 Oct 2024 14:25:00 +0000 (+0100) Subject: [3.12] gh-124594: Create and reuse the same context for the entire asyncio REPL sessi... X-Git-Tag: v3.12.8~158 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=d89283b3e7591c0a6cac8f22efbb5862ba83c431;p=thirdparty%2FPython%2Fcpython.git [3.12] gh-124594: Create and reuse the same context for the entire asyncio REPL session (GH-124595) (#124849) * gh-124594: Create and reuse the same context for the entire asyncio REPL session (GH-124595) (cherry picked from commit 67e01a430f4ecfcb540d6a29b347966ff4e53454) Co-authored-by: Bartosz Sławecki Co-authored-by: Andrew Svetlov --------- Co-authored-by: Bartosz Sławecki Co-authored-by: Andrew Svetlov --- diff --git a/Lib/asyncio/__main__.py b/Lib/asyncio/__main__.py index 046558011513..29e528aeed75 100644 --- a/Lib/asyncio/__main__.py +++ b/Lib/asyncio/__main__.py @@ -2,6 +2,7 @@ import ast import asyncio import code import concurrent.futures +import contextvars import inspect import sys import threading @@ -17,6 +18,7 @@ class AsyncIOInteractiveConsole(code.InteractiveConsole): super().__init__(locals) self.compile.compiler.flags |= ast.PyCF_ALLOW_TOP_LEVEL_AWAIT self.loop = loop + self.context = contextvars.copy_context() def runcode(self, code): future = concurrent.futures.Future() @@ -46,12 +48,12 @@ class AsyncIOInteractiveConsole(code.InteractiveConsole): return try: - repl_future = self.loop.create_task(coro) + repl_future = self.loop.create_task(coro, context=self.context) futures._chain_future(repl_future, future) except BaseException as exc: future.set_exception(exc) - loop.call_soon_threadsafe(callback) + loop.call_soon_threadsafe(callback, context=self.context) try: return future.result() diff --git a/Lib/test/test_repl.py b/Lib/test/test_repl.py index ddb4aa68048d..f2f18a2581cb 100644 --- a/Lib/test/test_repl.py +++ b/Lib/test/test_repl.py @@ -146,5 +146,42 @@ class TestInteractiveModeSyntaxErrors(unittest.TestCase): self.assertEqual(traceback_lines, expected_lines) +class TestAsyncioREPLContextVars(unittest.TestCase): + def test_toplevel_contextvars_sync(self): + user_input = dedent("""\ + from contextvars import ContextVar + var = ContextVar("var", default="failed") + var.set("ok") + """) + p = spawn_repl("-m", "asyncio") + p.stdin.write(user_input) + user_input2 = dedent(""" + print(f"toplevel contextvar test: {var.get()}") + """) + p.stdin.write(user_input2) + output = kill_python(p) + self.assertEqual(p.returncode, 0) + expected = "toplevel contextvar test: ok" + self.assertIn(expected, output, expected) + + def test_toplevel_contextvars_async(self): + user_input = dedent("""\ + from contextvars import ContextVar + var = ContextVar('var', default='failed') + """) + p = spawn_repl("-m", "asyncio") + p.stdin.write(user_input+"\n") + user_input2 = "async def set_var(): var.set('ok')\n" + p.stdin.write(user_input2+"\n") + user_input3 = "await set_var()\n" + p.stdin.write(user_input3+"\n") + user_input4 = "print(f'toplevel contextvar test: {var.get()}')\n" + p.stdin.write(user_input4+"\n") + output = kill_python(p) + self.assertEqual(p.returncode, 0) + expected = "toplevel contextvar test: ok" + self.assertIn(expected, output, expected) + + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/Library/2024-09-26-13-43-39.gh-issue-124594.peYhsP.rst b/Misc/NEWS.d/next/Library/2024-09-26-13-43-39.gh-issue-124594.peYhsP.rst new file mode 100644 index 000000000000..ac48bd849307 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-09-26-13-43-39.gh-issue-124594.peYhsP.rst @@ -0,0 +1 @@ +All :mod:`asyncio` REPL prompts run in the same :class:`context `. Contributed by Bartosz Sławecki.