88from pathlib import Path
99from typing import BinaryIO , TextIO , cast
1010
11+ import anyio
1112from anyio import to_thread
13+ from anyio .abc import Process
1214from anyio .streams .file import FileReadStream , FileWriteStream
1315
1416
@@ -113,13 +115,14 @@ async def create_windows_process(
113115 env : dict [str , str ] | None = None ,
114116 errlog : TextIO | None = sys .stderr ,
115117 cwd : Path | str | None = None ,
116- ) -> FallbackProcess :
118+ ) -> Process | FallbackProcess :
117119 """
118120 Creates a subprocess in a Windows-compatible way.
119121
120- On Windows, asyncio.create_subprocess_exec has incomplete support
121- (NotImplementedError when trying to open subprocesses).
122- Therefore, we fallback to subprocess.Popen and wrap it for async usage.
122+ Attempt to use anyio's open_process for async subprocess creation.
123+ In some cases this will throw NotImplementedError on Windows, e.g.
124+ when using the SelectorEventLoop which does not support async subprocesses.
125+ In that case, we fall back to using subprocess.Popen.
123126
124127 Args:
125128 command (str): The executable to run
@@ -131,6 +134,45 @@ async def create_windows_process(
131134 Returns:
132135 FallbackProcess: Async-compatible subprocess with stdin and stdout streams
133136 """
137+ try :
138+ # First try using anyio with Windows-specific flags to hide console window
139+ process = await anyio .open_process (
140+ [command , * args ],
141+ env = env ,
142+ # Ensure we don't create console windows for each process
143+ creationflags = subprocess .CREATE_NO_WINDOW # type: ignore
144+ if hasattr (subprocess , "CREATE_NO_WINDOW" )
145+ else 0 ,
146+ stderr = errlog ,
147+ cwd = cwd ,
148+ )
149+ return process
150+ except NotImplementedError :
151+ # Windows often doesn't support async subprocess creation, use fallback
152+ return await _create_windows_fallback_process (command , args , env , errlog , cwd )
153+ except Exception :
154+ # Try again without creation flags
155+ process = await anyio .open_process (
156+ [command , * args ],
157+ env = env ,
158+ stderr = errlog ,
159+ cwd = cwd ,
160+ )
161+ return process
162+
163+
164+ async def _create_windows_fallback_process (
165+ command : str ,
166+ args : list [str ],
167+ env : dict [str , str ] | None = None ,
168+ errlog : TextIO | None = sys .stderr ,
169+ cwd : Path | str | None = None ,
170+ ) -> FallbackProcess :
171+ """
172+ Create a subprocess using subprocess.Popen as a fallback when anyio fails.
173+
174+ This function wraps the sync subprocess.Popen in an async-compatible interface.
175+ """
134176 try :
135177 # Try launching with creationflags to avoid opening a new console window
136178 popen_obj = subprocess .Popen (
0 commit comments