1818import threading
1919import uuid
2020import warnings
21- from asyncio import Task , create_task
21+ from asyncio import AbstractEventLoop , Task , create_task
2222from concurrent .futures import Future
2323from contextlib import (
2424 asynccontextmanager ,
@@ -2059,7 +2059,6 @@ async def run_async(
20592059 from textual .pilot import Pilot
20602060
20612061 app = self
2062-
20632062 auto_pilot_task : Task | None = None
20642063
20652064 if auto_pilot is None and constants .PRESS :
@@ -2092,27 +2091,29 @@ async def run_auto_pilot(
20922091 run_auto_pilot (auto_pilot , pilot ), name = repr (pilot )
20932092 )
20942093
2095- try :
2096- app ._loop = asyncio .get_running_loop ()
2097- app ._thread_id = threading .get_ident ()
2098-
2099- await app ._process_messages (
2100- ready_callback = None if auto_pilot is None else app_ready ,
2101- headless = headless ,
2102- inline = inline ,
2103- inline_no_clear = inline_no_clear ,
2104- mouse = mouse ,
2105- terminal_size = size ,
2106- )
2107- finally :
2094+ app ._loop = asyncio .get_running_loop ()
2095+ app ._thread_id = threading .get_ident ()
2096+ with app ._context ():
21082097 try :
2109- if auto_pilot_task is not None :
2110- await auto_pilot_task
2098+ await app ._process_messages (
2099+ ready_callback = None if auto_pilot is None else app_ready ,
2100+ headless = headless ,
2101+ inline = inline ,
2102+ inline_no_clear = inline_no_clear ,
2103+ mouse = mouse ,
2104+ terminal_size = size ,
2105+ )
21112106 finally :
21122107 try :
2113- await asyncio .shield (app ._shutdown ())
2114- except asyncio .CancelledError :
2115- pass
2108+ if auto_pilot_task is not None :
2109+ await auto_pilot_task
2110+ finally :
2111+ try :
2112+ await asyncio .shield (app ._shutdown ())
2113+ except asyncio .CancelledError :
2114+ pass
2115+ app ._loop = None
2116+ app ._thread_id = 0
21162117
21172118 return app .return_value
21182119
@@ -2125,6 +2126,7 @@ def run(
21252126 mouse : bool = True ,
21262127 size : tuple [int , int ] | None = None ,
21272128 auto_pilot : AutopilotCallbackType | None = None ,
2129+ loop_factory : Callable [[], AbstractEventLoop ] | None = None ,
21282130 ) -> ReturnType | None :
21292131 """Run the app.
21302132
@@ -2136,35 +2138,30 @@ def run(
21362138 size: Force terminal size to `(WIDTH, HEIGHT)`,
21372139 or None to auto-detect.
21382140 auto_pilot: An auto pilot coroutine.
2139-
2141+ loop_factory: Callable which returns a new asyncio Loop, or `None` to use default.
21402142 Returns:
21412143 App return value.
21422144 """
21432145
21442146 async def run_app () -> None :
21452147 """Run the app."""
2146- self ._loop = asyncio .get_running_loop ()
2147- self ._thread_id = threading .get_ident ()
2148- with self ._context ():
2149- try :
2150- await self .run_async (
2151- headless = headless ,
2152- inline = inline ,
2153- inline_no_clear = inline_no_clear ,
2154- mouse = mouse ,
2155- size = size ,
2156- auto_pilot = auto_pilot ,
2157- )
2158- finally :
2159- self ._loop = None
2160- self ._thread_id = 0
2148+ await self .run_async (
2149+ headless = headless ,
2150+ inline = inline ,
2151+ inline_no_clear = inline_no_clear ,
2152+ mouse = mouse ,
2153+ size = size ,
2154+ auto_pilot = auto_pilot ,
2155+ )
21612156
21622157 if _ASYNCIO_GET_EVENT_LOOP_IS_DEPRECATED :
21632158 # N.B. This doesn't work with Python<3.10, as we end up with 2 event loops:
2164- asyncio .run (run_app ())
2159+ asyncio .run (run_app (), loop_factory = loop_factory )
21652160 else :
21662161 # However, this works with Python<3.10:
2167- event_loop = asyncio .get_event_loop ()
2162+ event_loop = (
2163+ asyncio .get_event_loop () if loop_factory is None else loop_factory ()
2164+ )
21682165 event_loop .run_until_complete (run_app ())
21692166 return self .return_value
21702167
0 commit comments