Skip to content

Commit 35d9624

Browse files
miss-islingtonbswckasvetlov
authored
[3.13] gh-124594: Create and reuse the same context for the entire asyncio REPL session (GH-124595) (#124848)
gh-124594: Create and reuse the same context for the entire asyncio REPL session (GH-124595) (cherry picked from commit 67e01a4) Co-authored-by: Bartosz Sławecki <[email protected]> Co-authored-by: Andrew Svetlov <[email protected]>
1 parent 8cf646a commit 35d9624

File tree

3 files changed

+42
-2
lines changed

3 files changed

+42
-2
lines changed

Lib/asyncio/__main__.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import ast
22
import asyncio
33
import concurrent.futures
4+
import contextvars
45
import inspect
56
import os
67
import site
@@ -22,6 +23,7 @@ def __init__(self, locals, loop):
2223
self.compile.compiler.flags |= ast.PyCF_ALLOW_TOP_LEVEL_AWAIT
2324

2425
self.loop = loop
26+
self.context = contextvars.copy_context()
2527

2628
def runcode(self, code):
2729
global return_code
@@ -55,12 +57,12 @@ def callback():
5557
return
5658

5759
try:
58-
repl_future = self.loop.create_task(coro)
60+
repl_future = self.loop.create_task(coro, context=self.context)
5961
futures._chain_future(repl_future, future)
6062
except BaseException as exc:
6163
future.set_exception(exc)
6264

63-
loop.call_soon_threadsafe(callback)
65+
loop.call_soon_threadsafe(callback, context=self.context)
6466

6567
try:
6668
return future.result()

Lib/test/test_repl.py

+37
Original file line numberDiff line numberDiff line change
@@ -291,5 +291,42 @@ def f():
291291
self.assertEqual(traceback_lines, expected_lines)
292292

293293

294+
class TestAsyncioREPLContextVars(unittest.TestCase):
295+
def test_toplevel_contextvars_sync(self):
296+
user_input = dedent("""\
297+
from contextvars import ContextVar
298+
var = ContextVar("var", default="failed")
299+
var.set("ok")
300+
""")
301+
p = spawn_repl("-m", "asyncio")
302+
p.stdin.write(user_input)
303+
user_input2 = dedent("""
304+
print(f"toplevel contextvar test: {var.get()}")
305+
""")
306+
p.stdin.write(user_input2)
307+
output = kill_python(p)
308+
self.assertEqual(p.returncode, 0)
309+
expected = "toplevel contextvar test: ok"
310+
self.assertIn(expected, output, expected)
311+
312+
def test_toplevel_contextvars_async(self):
313+
user_input = dedent("""\
314+
from contextvars import ContextVar
315+
var = ContextVar('var', default='failed')
316+
""")
317+
p = spawn_repl("-m", "asyncio")
318+
p.stdin.write(user_input)
319+
user_input2 = "async def set_var(): var.set('ok')\n"
320+
p.stdin.write(user_input2)
321+
user_input3 = "await set_var()\n"
322+
p.stdin.write(user_input3)
323+
user_input4 = "print(f'toplevel contextvar test: {var.get()}')\n"
324+
p.stdin.write(user_input4)
325+
output = kill_python(p)
326+
self.assertEqual(p.returncode, 0)
327+
expected = "toplevel contextvar test: ok"
328+
self.assertIn(expected, output, expected)
329+
330+
294331
if __name__ == "__main__":
295332
unittest.main()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
All :mod:`asyncio` REPL prompts run in the same :class:`context <contextvars.Context>`. Contributed by Bartosz Sławecki.

0 commit comments

Comments
 (0)