Commit 6f8dabc
## Summary
Fixes #3975 — a regression in 4.56.2 (from #3974) where parallel runs
intermittently fail with `Bad file descriptor` / `Input/output error`.
## Root cause
In `get_stream_file_no` (the pty path used when running under a tty),
the child fd is closed once the child has inherited it, and then closed
**again** on generator teardown:
```python
main_fd, child_fd = allocated_pty
try:
yield child_fd
os.close(child_fd) # (1) close once inherited
yield main_fd
finally:
for fd in (child_fd, main_fd): # (2) closes child_fd a *second* time
with suppress(OSError):
os.close(fd)
```
In a serial run the second `os.close(child_fd)` is a harmless suppressed
`EBADF`. But under parallel execution, between (1) and (2) the freed fd
**number** can be reused by a sibling run — so (2) closes *that* run's
descriptor instead, causing the intermittent `Bad file descriptor` /
`Input/output error`.
## Fix
Track whether the child fd was already closed and skip it in the
teardown loop, so each fd is closed exactly once. The master fd is still
released (no leak), and the early-teardown path (before the child fd is
inherited) still closes both.
## Tests
- Added `test_get_stream_file_no_closes_each_pty_fd_once`: it fails on
`main` (the child fd is closed twice: `[22, 22, 11]`) and passes here.
- The existing pty fd tests
(`test_local_subprocess_tty_closes_master_fd`,
`test_pty_closes_fds_when_termios_fails`) still pass; full
`tests/execute/local_subprocess/test_local_subprocess.py` is green (50
passed, 1 skipped). `ruff check` clean. Changelog fragment added.
*Disclosure: prepared with AI assistance; reviewed and verified
locally.*
---------
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 5458a28 commit 6f8dabc
3 files changed
Lines changed: 31 additions & 2 deletions
File tree
- docs/changelog
- src/tox/execute/local_sub_process
- tests/execute/local_subprocess
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
267 | 267 | | |
268 | 268 | | |
269 | 269 | | |
| 270 | + | |
270 | 271 | | |
271 | 272 | | |
272 | 273 | | |
| 274 | + | |
273 | 275 | | |
274 | 276 | | |
275 | | - | |
276 | | - | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
277 | 281 | | |
278 | 282 | | |
279 | 283 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
387 | 387 | | |
388 | 388 | | |
389 | 389 | | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
390 | 412 | | |
391 | 413 | | |
392 | 414 | | |
| |||
0 commit comments