Skip to content

Commit d89283b

Browse files
miss-islingtonbswckasvetlov
authored
[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 67e01a4) Co-authored-by: Bartosz Sławecki <[email protected]> Co-authored-by: Andrew Svetlov <[email protected]> --------- Co-authored-by: Bartosz Sławecki <[email protected]> Co-authored-by: Andrew Svetlov <[email protected]>
1 parent 1e01dcf commit d89283b

File tree

3 files changed

+42
-2
lines changed

3 files changed

+42
-2
lines changed

Lib/asyncio/__main__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import asyncio
33
import code
44
import concurrent.futures
5+
import contextvars
56
import inspect
67
import sys
78
import threading
@@ -17,6 +18,7 @@ def __init__(self, locals, loop):
1718
super().__init__(locals)
1819
self.compile.compiler.flags |= ast.PyCF_ALLOW_TOP_LEVEL_AWAIT
1920
self.loop = loop
21+
self.context = contextvars.copy_context()
2022

2123
def runcode(self, code):
2224
future = concurrent.futures.Future()
@@ -46,12 +48,12 @@ def callback():
4648
return
4749

4850
try:
49-
repl_future = self.loop.create_task(coro)
51+
repl_future = self.loop.create_task(coro, context=self.context)
5052
futures._chain_future(repl_future, future)
5153
except BaseException as exc:
5254
future.set_exception(exc)
5355

54-
loop.call_soon_threadsafe(callback)
56+
loop.call_soon_threadsafe(callback, context=self.context)
5557

5658
try:
5759
return future.result()

Lib/test/test_repl.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,5 +146,42 @@ def f():
146146
self.assertEqual(traceback_lines, expected_lines)
147147

148148

149+
class TestAsyncioREPLContextVars(unittest.TestCase):
150+
def test_toplevel_contextvars_sync(self):
151+
user_input = dedent("""\
152+
from contextvars import ContextVar
153+
var = ContextVar("var", default="failed")
154+
var.set("ok")
155+
""")
156+
p = spawn_repl("-m", "asyncio")
157+
p.stdin.write(user_input)
158+
user_input2 = dedent("""
159+
print(f"toplevel contextvar test: {var.get()}")
160+
""")
161+
p.stdin.write(user_input2)
162+
output = kill_python(p)
163+
self.assertEqual(p.returncode, 0)
164+
expected = "toplevel contextvar test: ok"
165+
self.assertIn(expected, output, expected)
166+
167+
def test_toplevel_contextvars_async(self):
168+
user_input = dedent("""\
169+
from contextvars import ContextVar
170+
var = ContextVar('var', default='failed')
171+
""")
172+
p = spawn_repl("-m", "asyncio")
173+
p.stdin.write(user_input+"\n")
174+
user_input2 = "async def set_var(): var.set('ok')\n"
175+
p.stdin.write(user_input2+"\n")
176+
user_input3 = "await set_var()\n"
177+
p.stdin.write(user_input3+"\n")
178+
user_input4 = "print(f'toplevel contextvar test: {var.get()}')\n"
179+
p.stdin.write(user_input4+"\n")
180+
output = kill_python(p)
181+
self.assertEqual(p.returncode, 0)
182+
expected = "toplevel contextvar test: ok"
183+
self.assertIn(expected, output, expected)
184+
185+
149186
if __name__ == "__main__":
150187
unittest.main()
Lines changed: 1 addition & 0 deletions
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)