Pipe inheritance broken on Windows since 3.13
Open
Nobody has claimed this yet.
3.13
3.14
OS-windows
type-bug
- Dominant language
- Python
- Stars
- 77.2k
- Forks
- 35.9k
- PR merge metrics
- PR metrics pending
Description
Bug report
Bug description:
Passing an inheritable pipe file descriptor to a child process is somehow broken on Windows since Python 3.13. Opening the file descriptor in the child process crashes with [WinError 6] the handle is invalid.
> py -3.12-64 .\main.py
INFO:root:S: Starting
INFO:root:Duration for 10000 requests: 0.37447249999968335 s
INFO:root:S: EOF Quitting
> py -3.13-64 .\main.py
INFO:root:S: Starting
Traceback (most recent call last):
File "D:\temp\test-pipe-main\sub.py", line 31, in <module>
main()
~~~~^^
File "D:\temp\test-pipe-main\sub.py", line 14, in main
reply_write_fobj = os.fdopen(reply_write_fd, "wb", buffering=0)
File "<frozen os>", line 1068, in fdopen
OSError: [WinError 6] Das Handle ist ungültig
Minimal example
import logging
import os
import pickle
import sys
import time
def main():
logging.basicConfig(level=logging.INFO)
reply_read_fd, reply_write_fd = os.pipe()
request_read_fd, request_write_fd = os.pipe()
child_fds = [reply_write_fd, request_read_fd]
for fd in child_fds:
os.set_inheritable(fd, True)
reply_read_fobj = os.fdopen(reply_read_fd, "rb", buffering=0)
request_write_fobj = os.fdopen(request_write_fd, "wb", buffering=0)
child_pid = os.spawnl(
os.P_NOWAIT,
sys.executable,
os.path.basename(sys.executable),
"sub.py",
*(str(fd) for fd in child_fds),
)
for fd in child_fds:
os.close(fd)
number_requests = 10000
start = time.perf_counter()
for i in range(number_requests):
request = f"test{i}"
pickle.dump(request, request_write_fobj)
logging.debug(f"C: Sent {request}")
reply = pickle.load(reply_read_fobj)
logging.debug(f"C: Got {reply}")
duration = time.perf_counter() - start
logging.info(f"Duration for {number_requests} requests: {duration} s")
request_write_fobj.close()
os.waitpid(child_pid, 0)
time.sleep(1)
if __name__ == "__main__":
main()
import logging
import os
import pickle
import sys
def main():
logging.basicConfig(level=logging.INFO)
reply_write_fd = int(sys.argv[1])
request_read_fd = int(sys.argv[2])
logging.info("S: Starting")
reply_write_fobj = os.fdopen(reply_write_fd, "wb", buffering=0)
request_read_fobj = os.fdopen(request_read_fd, "rb", buffering=0)
logging.debug("S: Sent event")
while True:
try:
request: str = pickle.load(request_read_fobj)
except EOFError:
logging.info("S: EOF Quitting")
return
logging.debug(f"S: Handle {request}")
reply = "test"
pickle.dump(reply, reply_write_fobj)
logging.debug("S: Sent event")
if __name__ == "__main__":
main()
CPython versions tested on:
3.12, 3.13
Operating systems tested on:
Windows
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by running the provided main.py and sub.py on Windows with Python 3.12 and 3.13, focusing on os.pipe, os.set_inheritable, os.spawnl, and os.fdopen. Trace the Windows process and pipe-inheritance path to identify why the child receives an invalid handle, then verify the fix with a regression test covering the minimal example.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- operating-systems
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100