Skip to content

Commit e7a8dbe

Browse files
committed
Add workaround for send_handle bug on Windows
See python/cpython#82369
1 parent f395f56 commit e7a8dbe

File tree

2 files changed

+81
-5
lines changed

2 files changed

+81
-5
lines changed

src/cutadapt/_handles.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
"""
2+
Wrappers around multiprocessing.reduction.send_handle/recv_handle
3+
that
4+
- fix a bug in CPython’s standard library preventing send_handle from
5+
working on Windows
6+
- and translate between Windows handles and Unix file descriptors as necessary.
7+
"""
8+
9+
import multiprocessing
10+
import os
11+
12+
try:
13+
import msvcrt
14+
except ImportError:
15+
msvcrt = None
16+
try:
17+
import _winapi
18+
except ImportError:
19+
_winapi = None
20+
21+
22+
class _PatchedDupHandle:
23+
"""Used by _patched_send_handle"""
24+
25+
def __init__(self, handle, access, pid=None, options=0):
26+
if pid is None:
27+
pid = os.getpid()
28+
proc = _winapi.OpenProcess(_winapi.PROCESS_DUP_HANDLE, False, pid)
29+
try:
30+
self._handle = _winapi.DuplicateHandle(
31+
_winapi.GetCurrentProcess(), handle, proc, access, False, options
32+
)
33+
finally:
34+
_winapi.CloseHandle(proc)
35+
self._access = access
36+
self._pid = pid
37+
38+
def detach(self):
39+
if self._pid == os.getpid():
40+
return self._handle
41+
proc = _winapi.OpenProcess(_winapi.PROCESS_DUP_HANDLE, False, self._pid)
42+
try:
43+
return _winapi.DuplicateHandle(
44+
proc,
45+
self._handle,
46+
_winapi.GetCurrentProcess(),
47+
self._access,
48+
False,
49+
_winapi.DUPLICATE_CLOSE_SOURCE,
50+
)
51+
finally:
52+
_winapi.CloseHandle(proc)
53+
54+
55+
def _patched_send_handle(conn, handle, destination_pid):
56+
"""
57+
A patched version of multiprocessing.reduction.send_handle that works around
58+
bug https://github.com/python/cpython/issues/82369
59+
Adapted from code posted by Cameron Kennedy (m3rc1fulcameron) in that issue.
60+
"""
61+
dh = _PatchedDupHandle(handle, 0, destination_pid, _winapi.DUPLICATE_SAME_ACCESS)
62+
conn.send(dh)
63+
64+
65+
def send_handle(conn, fd, destination_pid):
66+
if _winapi is None:
67+
return multiprocessing.reduction.send_handle(conn, fd, destination_pid)
68+
else:
69+
handle = msvcrt.get_osfhandle(fd)
70+
return _patched_send_handle(conn, handle, destination_pid)
71+
72+
73+
def recv_handle(conn):
74+
handle = multiprocessing.reduction.recv_handle(conn)
75+
if msvcrt:
76+
return msvcrt.open_osfhandle(handle, os.O_RDONLY | os.O_BINARY)
77+
else:
78+
return handle

src/cutadapt/runners.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import dnaio
1313

14+
from ._handles import recv_handle, send_handle
1415
from cutadapt.files import (
1516
InputFiles,
1617
OutputFiles,
@@ -85,8 +86,7 @@ def run(self):
8586
with ExitStack() as stack:
8687
try:
8788
fds = [
88-
multiprocessing.reduction.recv_handle(self._input_fds_pipe)
89-
for _ in range(self._n_files)
89+
recv_handle(self._input_fds_pipe) for _ in range(self._n_files)
9090
]
9191
self._input_fds_pipe.close()
9292
raw_files = [
@@ -326,9 +326,7 @@ def __init__(
326326
)
327327
else:
328328
with open(path, "rb") as f:
329-
multiprocessing.reduction.send_handle(
330-
input_fds_pipe_w, f.fileno(), pid
331-
)
329+
send_handle(input_fds_pipe_w, f.fileno(), pid)
332330
input_fds_pipe_w.close()
333331

334332
file_format: Optional[FileFormat] = self._try_receive(file_format_connection_r)

0 commit comments

Comments
 (0)