Creates a subprocess with Job Object support for tree termination. Spawns via anyio's open_process; event loops without async subprocess support (notably the SelectorEventLoop) raise NotImplementedError, in which case the spawn falls back to a Popen-backed FallbackProcess. Either way th
(
command: str,
args: list[str],
env: dict[str, str] | None = None,
errlog: TextIO | None = sys.stderr,
cwd: Path | str | None = None,
)
| 111 | |
| 112 | |
| 113 | async def create_windows_process( |
| 114 | command: str, |
| 115 | args: list[str], |
| 116 | env: dict[str, str] | None = None, |
| 117 | errlog: TextIO | None = sys.stderr, |
| 118 | cwd: Path | str | None = None, |
| 119 | ) -> Process | FallbackProcess: |
| 120 | """Creates a subprocess with Job Object support for tree termination. |
| 121 | |
| 122 | Spawns via anyio's open_process; event loops without async subprocess |
| 123 | support (notably the SelectorEventLoop) raise NotImplementedError, in which |
| 124 | case the spawn falls back to a Popen-backed FallbackProcess. Either way the |
| 125 | process is then assigned to a Job Object so its children can be terminated |
| 126 | with it; children spawned before the assignment completes are not captured |
| 127 | (see the inline note below). |
| 128 | |
| 129 | Returns: |
| 130 | Process | FallbackProcess: The spawned process with async stdin/stdout streams. |
| 131 | """ |
| 132 | try: |
| 133 | process = await anyio.open_process( |
| 134 | [command, *args], |
| 135 | env=env, |
| 136 | # Ensure we don't create console windows for each process |
| 137 | creationflags=getattr(subprocess, "CREATE_NO_WINDOW", 0), |
| 138 | stderr=errlog, |
| 139 | cwd=cwd, |
| 140 | ) |
| 141 | except NotImplementedError: |
| 142 | # Windows event loops without async subprocess support (SelectorEventLoop) |
| 143 | process = await _create_windows_fallback_process(command, args, env, errlog, cwd) |
| 144 | |
| 145 | # Children spawned before the assignment completes land outside the job |
| 146 | # (membership is inherited at CreateProcess, never acquired retroactively); |
| 147 | # if that ever bites, the fix is a CREATE_SUSPENDED spawn -> assign -> resume. |
| 148 | job = _create_job_object() |
| 149 | _maybe_assign_process_to_job(process, job) |
| 150 | return process |
| 151 | |
| 152 | |
| 153 | async def _create_windows_fallback_process( |
no test coverage detected