Skip to content

Commit 0e1e4fb

Browse files
remote/client: rework event loop handling in start_session()
Calling asyncio.get_event_loop() with no current event loop is deprecated since Python 3.10 and will be an error in some future Python release [1]. Using it in labgrid.remote.client.start_session() causes errors in IPython when using a RemotePlace: In [1]: from labgrid.resource.remote import RemotePlace ...: from labgrid import Target ...: ...: target = Target("example") ...: RemotePlace(target, name="example-place") [...] RuntimeError: There is no current event loop in thread 'MainThread'. For labgrid.remote.client.start_session() there is no reliable way of retrieving the thread's event loop without being called from an async context (which we cannot assume here). Instead of using asyncio.get_event_loop(), use a new helper function ensure_event_loop() that returns the first available loop instance from: - externally provided event loop - stashed event loop - OS thread's running event loop (when called from async code) - new event loop The returned loop is stashed for future calls. See also [2] for a similar approach. start_session() now accepts a new optional argument "loop" for providing an external event loop. [1] https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.get_event_loop [2] jupyter/jupyter_core#387 Signed-off-by: Bastian Krause <[email protected]>
1 parent da95c44 commit 0e1e4fb

File tree

1 file changed

+43
-3
lines changed

1 file changed

+43
-3
lines changed

labgrid/remote/client.py

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import argparse
55
import asyncio
66
import contextlib
7+
from contextvars import ContextVar
78
import enum
89
import os
910
import pathlib
@@ -1529,8 +1530,45 @@ def print_version(self):
15291530
print(labgrid_version())
15301531

15311532

1532-
def start_session(address, extra, debug=False):
1533-
loop = asyncio.get_event_loop()
1533+
_loop: ContextVar["asyncio.AbstractEventLoop | None"] = ContextVar("_loop", default=None)
1534+
1535+
1536+
def ensure_event_loop(external_loop=None):
1537+
"""Get the event loop for this thread, or create a new event loop."""
1538+
# get stashed loop
1539+
loop = _loop.get()
1540+
1541+
# ignore closed stashed loop
1542+
if loop and loop.is_closed():
1543+
loop = None
1544+
1545+
if external_loop:
1546+
# if a loop is stashed, expect it to be the same as the external one
1547+
if loop:
1548+
assert loop is external_loop
1549+
_loop.set(external_loop)
1550+
return external_loop
1551+
1552+
# return stashed loop
1553+
if loop:
1554+
return loop
1555+
1556+
try:
1557+
# if called from async code, try to get current's thread loop
1558+
loop = asyncio.get_running_loop()
1559+
except RuntimeError:
1560+
# no previous, external or running loop found, create a new one
1561+
loop = asyncio.new_event_loop()
1562+
asyncio.set_event_loop(loop)
1563+
1564+
# stash it
1565+
_loop.set(loop)
1566+
return loop
1567+
1568+
1569+
def start_session(address, extra, debug=False, loop=None):
1570+
loop = ensure_event_loop(loop)
1571+
15341572
if debug:
15351573
loop.set_debug(True)
15361574

@@ -2040,7 +2078,9 @@ def main():
20402078
coordinator_address = os.environ.get("LG_COORDINATOR", "127.0.0.1:20408")
20412079

20422080
logging.debug('Starting session with "%s"', coordinator_address)
2043-
session = start_session(coordinator_address, extra, args.debug)
2081+
loop = asyncio.new_event_loop()
2082+
asyncio.set_event_loop(loop)
2083+
session = start_session(coordinator_address, extra=extra, debug=args.debug, loop=loop)
20442084
logging.debug("Started session")
20452085

20462086
try:

0 commit comments

Comments
 (0)