Coverage for Lib/asyncio/windows_utils.py: 0%
116 statements
« prev ^ index » next coverage.py v7.14.3, created at 2026-06-24 03:28 +0000
« prev ^ index » next coverage.py v7.14.3, created at 2026-06-24 03:28 +0000
1"""Various Windows specific bits and pieces."""
3import sys
5if sys.platform != 'win32': # pragma: no cover
6 raise ImportError('win32 only')
8import _winapi
9import itertools
10import msvcrt
11import os
12import subprocess
13import warnings
16__all__ = 'pipe', 'Popen', 'PIPE', 'PipeHandle'
19# Constants/globals
22BUFSIZE = 8192
23PIPE = subprocess.PIPE
24STDOUT = subprocess.STDOUT
25_mmap_counter = itertools.count()
26_MAX_PIPE_ATTEMPTS = 20
29# Replacement for os.pipe() using handles instead of fds
32def pipe(*, duplex=False, overlapped=(True, True), bufsize=BUFSIZE):
33 """Like os.pipe() but with overlapped support and using handles not fds."""
34 if duplex:
35 openmode = _winapi.PIPE_ACCESS_DUPLEX
36 access = _winapi.GENERIC_READ | _winapi.GENERIC_WRITE
37 obsize, ibsize = bufsize, bufsize
38 else:
39 openmode = _winapi.PIPE_ACCESS_INBOUND
40 access = _winapi.GENERIC_WRITE
41 obsize, ibsize = 0, bufsize
43 openmode |= _winapi.FILE_FLAG_FIRST_PIPE_INSTANCE
45 if overlapped[0]:
46 openmode |= _winapi.FILE_FLAG_OVERLAPPED
48 if overlapped[1]:
49 flags_and_attribs = _winapi.FILE_FLAG_OVERLAPPED
50 else:
51 flags_and_attribs = 0
53 h1 = h2 = None
54 try:
55 for attempts in itertools.count():
56 address = r'\\.\pipe\python-pipe-{:d}-{:d}-{}'.format(
57 os.getpid(), next(_mmap_counter), os.urandom(8).hex())
58 try:
59 h1 = _winapi.CreateNamedPipe(
60 address, openmode, _winapi.PIPE_WAIT,
61 1, obsize, ibsize, _winapi.NMPWAIT_WAIT_FOREVER, _winapi.NULL)
62 break
63 except OSError as e:
64 if attempts >= _MAX_PIPE_ATTEMPTS:
65 raise
66 if e.winerror not in (_winapi.ERROR_PIPE_BUSY,
67 _winapi.ERROR_ACCESS_DENIED):
68 raise
70 h2 = _winapi.CreateFile(
71 address, access, 0, _winapi.NULL, _winapi.OPEN_EXISTING,
72 flags_and_attribs, _winapi.NULL)
74 ov = _winapi.ConnectNamedPipe(h1, overlapped=True)
75 ov.GetOverlappedResult(True)
76 return h1, h2
77 except:
78 if h1 is not None:
79 _winapi.CloseHandle(h1)
80 if h2 is not None:
81 _winapi.CloseHandle(h2)
82 raise
85# Wrapper for a pipe handle
88class PipeHandle:
89 """Wrapper for an overlapped pipe handle which is vaguely file-object like.
91 The IOCP event loop can use these instead of socket objects.
92 """
93 def __init__(self, handle):
94 self._handle = handle
96 def __repr__(self):
97 if self._handle is not None:
98 handle = f'handle={self._handle!r}'
99 else:
100 handle = 'closed'
101 return f'<{self.__class__.__name__} {handle}>'
103 @property
104 def handle(self):
105 return self._handle
107 def fileno(self):
108 if self._handle is None:
109 raise ValueError("I/O operation on closed pipe")
110 return self._handle
112 def close(self, *, CloseHandle=_winapi.CloseHandle):
113 if self._handle is not None:
114 handle = self._handle
115 self._handle = None
116 CloseHandle(handle)
118 def __del__(self, _warn=warnings.warn):
119 if self._handle is not None:
120 _warn(f"unclosed {self!r}", ResourceWarning, source=self)
121 self.close()
123 def __enter__(self):
124 return self
126 def __exit__(self, t, v, tb):
127 self.close()
130# Replacement for subprocess.Popen using overlapped pipe handles
133class Popen(subprocess.Popen):
134 """Replacement for subprocess.Popen using overlapped pipe handles.
136 The stdin, stdout, stderr are None or instances of PipeHandle.
137 """
138 def __init__(self, args, stdin=None, stdout=None, stderr=None, **kwds):
139 assert not kwds.get('universal_newlines')
140 assert kwds.get('bufsize', 0) == 0
141 stdin_rfd = stdout_wfd = stderr_wfd = None
142 stdin_wh = stdout_rh = stderr_rh = None
143 if stdin == PIPE:
144 stdin_rh, stdin_wh = pipe(overlapped=(False, True), duplex=True)
145 stdin_rfd = msvcrt.open_osfhandle(stdin_rh, os.O_RDONLY)
146 else:
147 stdin_rfd = stdin
148 if stdout == PIPE:
149 stdout_rh, stdout_wh = pipe(overlapped=(True, False))
150 stdout_wfd = msvcrt.open_osfhandle(stdout_wh, 0)
151 else:
152 stdout_wfd = stdout
153 if stderr == PIPE:
154 stderr_rh, stderr_wh = pipe(overlapped=(True, False))
155 stderr_wfd = msvcrt.open_osfhandle(stderr_wh, 0)
156 elif stderr == STDOUT:
157 stderr_wfd = stdout_wfd
158 else:
159 stderr_wfd = stderr
160 try:
161 super().__init__(args, stdin=stdin_rfd, stdout=stdout_wfd,
162 stderr=stderr_wfd, **kwds)
163 except:
164 for h in (stdin_wh, stdout_rh, stderr_rh):
165 if h is not None:
166 _winapi.CloseHandle(h)
167 raise
168 else:
169 if stdin_wh is not None:
170 self.stdin = PipeHandle(stdin_wh)
171 if stdout_rh is not None:
172 self.stdout = PipeHandle(stdout_rh)
173 if stderr_rh is not None:
174 self.stderr = PipeHandle(stderr_rh)
175 finally:
176 if stdin == PIPE:
177 os.close(stdin_rfd)
178 if stdout == PIPE:
179 os.close(stdout_wfd)
180 if stderr == PIPE:
181 os.close(stderr_wfd)