GeezerVison is showing github.com — gh-109276: libregrtest: WASM use filename for JSON · python/cpython@359d425
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: Lib/test/libregrtest/main.py +1-1Lines changed: 1 addition & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -406,7 +406,7 @@ def create_run_tests(self, tests: TestTuple): | 406 | 406 | python_cmd=self.python_cmd, | 407 | 407 | randomize=self.randomize, | 408 | 408 | random_seed=self.random_seed, | 409 | | - json_fd=None, | | 409 | + json_file=None, | 410 | 410 | ) | 411 | 411 |
| 412 | 412 | def _run_tests(self, selected: TestTuple, tests: TestList | None) -> int: | |
Collapse file Copy file name to clipboardExpand all lines: Lib/test/libregrtest/run_workers.py +22-13Lines changed: 22 additions & 13 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 17 | 17 | from .logger import Logger | 18 | 18 | from .result import TestResult, State | 19 | 19 | from .results import TestResults | 20 | | -from .runtests import RunTests | | 20 | +from .runtests import RunTests, JsonFileType, JSON_FILE_USE_FILENAME | 21 | 21 | from .single import PROGRESS_MIN_TIME | 22 | 22 | from .utils import ( | 23 | 23 | StrPath, StrJSON, TestName, MS_WINDOWS, | @@ -155,10 +155,11 @@ def mp_result_error( | 155 | 155 | ) -> MultiprocessResult: | 156 | 156 | return MultiprocessResult(test_result, stdout, err_msg) | 157 | 157 |
| 158 | | - def _run_process(self, runtests: RunTests, output_fd: int, json_fd: int, | | 158 | + def _run_process(self, runtests: RunTests, output_fd: int, | | 159 | + json_file: JsonFileType, | 159 | 160 | tmp_dir: StrPath | None = None) -> int: | 160 | 161 | try: | 161 | | - popen = create_worker_process(runtests, output_fd, json_fd, | | 162 | + popen = create_worker_process(runtests, output_fd, json_file, | 162 | 163 | tmp_dir) | 163 | 164 |
| 164 | 165 | self._killed = False | @@ -226,21 +227,29 @@ def _runtest(self, test_name: TestName) -> MultiprocessResult: | 226 | 227 | match_tests = None | 227 | 228 | err_msg = None | 228 | 229 |
| | 230 | + stdout_file = tempfile.TemporaryFile('w+', encoding=encoding) | | 231 | + if JSON_FILE_USE_FILENAME: | | 232 | + json_tmpfile = tempfile.NamedTemporaryFile('w+', encoding='utf8') | | 233 | + else: | | 234 | + json_tmpfile = tempfile.TemporaryFile('w+', encoding='utf8') | | 235 | + | 229 | 236 | # gh-94026: Write stdout+stderr to a tempfile as workaround for | 230 | 237 | # non-blocking pipes on Emscripten with NodeJS. | 231 | | - with (tempfile.TemporaryFile('w+', encoding=encoding) as stdout_file, | 232 | | - tempfile.TemporaryFile('w+', encoding='utf8') as json_file): | | 238 | + with (stdout_file, json_tmpfile): | 233 | 239 | stdout_fd = stdout_file.fileno() | 234 | | - json_fd = json_file.fileno() | 235 | | - if MS_WINDOWS: | 236 | | - json_fd = msvcrt.get_osfhandle(json_fd) | | 240 | + if JSON_FILE_USE_FILENAME: | | 241 | + json_file = json_tmpfile.name | | 242 | + else: | | 243 | + json_file = json_tmpfile.fileno() | | 244 | + if MS_WINDOWS: | | 245 | + json_file = msvcrt.get_osfhandle(json_file) | 237 | 246 |
| 238 | 247 | kwargs = {} | 239 | 248 | if match_tests: | 240 | 249 | kwargs['match_tests'] = match_tests | 241 | 250 | worker_runtests = self.runtests.copy( | 242 | 251 | tests=tests, | 243 | | - json_fd=json_fd, | | 252 | + json_file=json_file, | 244 | 253 | **kwargs) | 245 | 254 |
| 246 | 255 | # gh-93353: Check for leaked temporary files in the parent process, | @@ -254,13 +263,13 @@ def _runtest(self, test_name: TestName) -> MultiprocessResult: | 254 | 263 | tmp_dir = os.path.abspath(tmp_dir) | 255 | 264 | try: | 256 | 265 | retcode = self._run_process(worker_runtests, | 257 | | - stdout_fd, json_fd, tmp_dir) | | 266 | + stdout_fd, json_file, tmp_dir) | 258 | 267 | finally: | 259 | 268 | tmp_files = os.listdir(tmp_dir) | 260 | 269 | os_helper.rmtree(tmp_dir) | 261 | 270 | else: | 262 | 271 | retcode = self._run_process(worker_runtests, | 263 | | - stdout_fd, json_fd) | | 272 | + stdout_fd, json_file) | 264 | 273 | tmp_files = () | 265 | 274 | stdout_file.seek(0) | 266 | 275 |
| @@ -275,8 +284,8 @@ def _runtest(self, test_name: TestName) -> MultiprocessResult: | 275 | 284 |
| 276 | 285 | try: | 277 | 286 | # deserialize run_tests_worker() output | 278 | | - json_file.seek(0) | 279 | | - worker_json: StrJSON = json_file.read() | | 287 | + json_tmpfile.seek(0) | | 288 | + worker_json: StrJSON = json_tmpfile.read() | 280 | 289 | if worker_json: | 281 | 290 | result = TestResult.from_json(worker_json) | 282 | 291 | else: | |
Collapse file Copy file name to clipboardExpand all lines: Lib/test/libregrtest/runtests.py +14-3Lines changed: 14 additions & 3 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 6 | 6 | StrPath, StrJSON, TestTuple, FilterTuple, FilterDict) | 7 | 7 |
| 8 | 8 |
| | 9 | +if support.is_emscripten or support.is_wasi: | | 10 | + # On Emscripten/WASI, it's a filename. Passing a file descriptor to a | | 11 | + # worker process fails with "OSError: [Errno 8] Bad file descriptor" in the | | 12 | + # worker process. | | 13 | + JsonFileType = StrPath | | 14 | + JSON_FILE_USE_FILENAME = True | | 15 | +else: | | 16 | + # On Unix, it's a file descriptor. | | 17 | + # On Windows, it's a handle. | | 18 | + JsonFileType = int | | 19 | + JSON_FILE_USE_FILENAME = False | | 20 | + | | 21 | + | 9 | 22 | @dataclasses.dataclass(slots=True, frozen=True) | 10 | 23 | class HuntRefleak: | 11 | 24 | warmups: int | @@ -38,9 +51,7 @@ class RunTests: | 38 | 51 | python_cmd: tuple[str] | None | 39 | 52 | randomize: bool | 40 | 53 | random_seed: int | None | 41 | | - # On Unix, it's a file descriptor. | 42 | | - # On Windows, it's a handle. | 43 | | - json_fd: int | None | | 54 | + json_file: JsonFileType | None | 44 | 55 |
| 45 | 56 | def copy(self, **override): | 46 | 57 | state = dataclasses.asdict(self) | |
Collapse file Copy file name to clipboardExpand all lines: Lib/test/libregrtest/utils.py +1-1Lines changed: 1 addition & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -387,7 +387,7 @@ def get_work_dir(parent_dir: StrPath, worker: bool = False) -> StrPath: | 387 | 387 | # testing (see the -j option). | 388 | 388 | # Emscripten and WASI have stubbed getpid(), Emscripten has only | 389 | 389 | # milisecond clock resolution. Use randint() instead. | 390 | | - if sys.platform in {"emscripten", "wasi"}: | | 390 | + if support.is_emscripten or support.is_wasi: | 391 | 391 | nounce = random.randint(0, 1_000_000) | 392 | 392 | else: | 393 | 393 | nounce = os.getpid() | |
Collapse file Copy file name to clipboardExpand all lines: Lib/test/libregrtest/worker.py +21-12Lines changed: 21 additions & 12 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 7 | 7 | from test.support import os_helper | 8 | 8 |
| 9 | 9 | from .setup import setup_process, setup_test_dir | 10 | | -from .runtests import RunTests | | 10 | +from .runtests import RunTests, JsonFileType, JSON_FILE_USE_FILENAME | 11 | 11 | from .single import run_single_test | 12 | 12 | from .utils import ( | 13 | 13 | StrPath, StrJSON, FilterTuple, MS_WINDOWS, | | 18 | 18 |
| 19 | 19 |
| 20 | 20 | def create_worker_process(runtests: RunTests, | 21 | | - output_fd: int, json_fd: int, | | 21 | + output_fd: int, json_file: JsonFileType, | 22 | 22 | tmp_dir: StrPath | None = None) -> subprocess.Popen: | 23 | 23 | python_cmd = runtests.python_cmd | 24 | 24 | worker_json = runtests.as_json() | @@ -55,33 +55,42 @@ def create_worker_process(runtests: RunTests, | 55 | 55 | close_fds=True, | 56 | 56 | cwd=work_dir, | 57 | 57 | ) | 58 | | - if not MS_WINDOWS: | 59 | | - kwargs['pass_fds'] = [json_fd] | 60 | | - else: | | 58 | + if JSON_FILE_USE_FILENAME: | | 59 | + # Nothing to do to pass the JSON filename to the worker process | | 60 | + pass | | 61 | + elif MS_WINDOWS: | | 62 | + # Pass the JSON handle to the worker process | 61 | 63 | startupinfo = subprocess.STARTUPINFO() | 62 | | - startupinfo.lpAttributeList = {"handle_list": [json_fd]} | | 64 | + startupinfo.lpAttributeList = {"handle_list": [json_file]} | 63 | 65 | kwargs['startupinfo'] = startupinfo | | 66 | + else: | | 67 | + # Pass the JSON file descriptor to the worker process | | 68 | + kwargs['pass_fds'] = [json_file] | 64 | 69 | if USE_PROCESS_GROUP: | 65 | 70 | kwargs['start_new_session'] = True | 66 | 71 |
| 67 | 72 | if MS_WINDOWS: | 68 | | - os.set_handle_inheritable(json_fd, True) | | 73 | + os.set_handle_inheritable(json_file, True) | 69 | 74 | try: | 70 | 75 | return subprocess.Popen(cmd, **kwargs) | 71 | 76 | finally: | 72 | 77 | if MS_WINDOWS: | 73 | | - os.set_handle_inheritable(json_fd, False) | | 78 | + os.set_handle_inheritable(json_file, False) | 74 | 79 |
| 75 | 80 |
| 76 | 81 | def worker_process(worker_json: StrJSON) -> NoReturn: | 77 | 82 | runtests = RunTests.from_json(worker_json) | 78 | 83 | test_name = runtests.tests[0] | 79 | 84 | match_tests: FilterTuple | None = runtests.match_tests | 80 | | - json_fd: int = runtests.json_fd | | 85 | + # On Unix, it's a file descriptor. | | 86 | + # On Windows, it's a handle. | | 87 | + # On Emscripten/WASI, it's a filename. | | 88 | + json_file: JsonFileType = runtests.json_file | 81 | 89 |
| 82 | 90 | if MS_WINDOWS: | 83 | 91 | import msvcrt | 84 | | - json_fd = msvcrt.open_osfhandle(json_fd, os.O_WRONLY) | | 92 | + # Create a file descriptor from the handle | | 93 | + json_file = msvcrt.open_osfhandle(json_file, os.O_WRONLY) | 85 | 94 |
| 86 | 95 |
| 87 | 96 | setup_test_dir(runtests.test_dir) | @@ -96,8 +105,8 @@ def worker_process(worker_json: StrJSON) -> NoReturn: | 96 | 105 |
| 97 | 106 | result = run_single_test(test_name, runtests) | 98 | 107 |
| 99 | | - with open(json_fd, 'w', encoding='utf-8') as json_file: | 100 | | - result.write_json_into(json_file) | | 108 | + with open(json_file, 'w', encoding='utf-8') as fp: | | 109 | + result.write_json_into(fp) | 101 | 110 |
| 102 | 111 | sys.exit(0) | 103 | 112 |
| |
|