Skip to content

Commit b1df8ae

Browse files
committed
Fixed to_process.run_sync() sometimes failing to initialize on Windows
Fixes #696.
1 parent 675930b commit b1df8ae

File tree

3 files changed

+40
-12
lines changed

3 files changed

+40
-12
lines changed

docs/versionhistory.rst

+3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ This library adheres to `Semantic Versioning 2.0 <http://semver.org/>`_.
77

88
- Added support for the ``from_uri()``, ``full_match()``, ``parser`` methods/properties
99
in ``anyio.Path``, newly added in Python 3.13
10+
- Fixed ``to_process.run_sync()`` failing to initialize on Windows if the script pointed
11+
to by ``__main__.__file__`` did not exist
12+
(`#696 <https://github.com/agronholm/anyio/issues/696>`_)
1013

1114
**4.4.0**
1215

src/anyio/to_process.py

+18-12
Original file line numberDiff line numberDiff line change
@@ -223,18 +223,24 @@ def process_worker() -> None:
223223
main_module_path: str | None
224224
sys.path, main_module_path = args
225225
del sys.modules["__main__"]
226-
if main_module_path:
227-
# Load the parent's main module but as __mp_main__ instead of
228-
# __main__ (like multiprocessing does) to avoid infinite recursion
229-
try:
230-
spec = spec_from_file_location("__mp_main__", main_module_path)
231-
if spec and spec.loader:
232-
main = module_from_spec(spec)
233-
spec.loader.exec_module(main)
234-
sys.modules["__main__"] = main
235-
except BaseException as exc:
236-
exception = exc
237-
226+
with open("/tmp/output", "w") as f:
227+
print("main module path:", main_module_path, file=f)
228+
if main_module_path:
229+
# Load the parent's main module but as __mp_main__ instead of
230+
# __main__ (like multiprocessing does) to avoid infinite recursion
231+
try:
232+
spec = spec_from_file_location(
233+
"__mp_main__", main_module_path
234+
)
235+
print(f"{spec=}", file=f)
236+
if spec and spec.loader:
237+
print(f"{spec.loader=}", file=f)
238+
main = module_from_spec(spec)
239+
spec.loader.exec_module(main)
240+
sys.modules["__main__"] = main
241+
print("loaded", main_module_path, file=f)
242+
except BaseException as exc:
243+
exception = exc
238244
try:
239245
if exception is not None:
240246
status = b"EXCEPTION"

tests/test_to_process.py

+19
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
from __future__ import annotations
22

33
import os
4+
import platform
45
import sys
56
import time
67
from functools import partial
8+
from pathlib import Path
79
from unittest.mock import Mock
810

911
import pytest
12+
from pytest import MonkeyPatch
1013

1114
from anyio import (
1215
CancelScope,
@@ -113,3 +116,19 @@ async def test_exec_while_pruning() -> None:
113116
assert idle_workers[0][0] is real_worker
114117
finally:
115118
workers.discard(fake_idle_process)
119+
120+
121+
@pytest.mark.skipif(
122+
platform.system() != "Windows", reason="This test fails only on Windows"
123+
)
124+
async def test_nonexistent_main_module(
125+
monkeypatch: MonkeyPatch, tmp_path: Path
126+
) -> None:
127+
"""
128+
Test that worker process creation won't fail if the detected path to the `__main__`
129+
module doesn't exist. Regression test for #696.
130+
"""
131+
132+
script_path = tmp_path / "nonexistent"
133+
monkeypatch.setattr("__main__.__file__", str(script_path))
134+
await to_process.run_sync(os.getpid)

0 commit comments

Comments
 (0)