blob: 870cd13abe6cf2ff33425a7d93bbca78b1c75f06 [file] [log] [blame]
Guido van Rossum27b7c7e2013-10-17 13:40:50 -07001"""
2Various Windows specific bits and pieces
3"""
4
5import sys
6
7if sys.platform != 'win32': # pragma: no cover
8 raise ImportError('win32 only')
9
Victor Stinner29ad0112015-01-15 00:04:21 +010010import _winapi
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070011import itertools
12import msvcrt
13import os
Victor Stinner29ad0112015-01-15 00:04:21 +010014import socket
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070015import subprocess
16import tempfile
Victor Stinner978a9af2015-01-29 17:50:58 +010017import warnings
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070018
19
20__all__ = ['socketpair', 'pipe', 'Popen', 'PIPE', 'PipeHandle']
21
Guido van Rossuma8d630a2013-11-01 14:20:55 -070022
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070023# Constants/globals
Guido van Rossuma8d630a2013-11-01 14:20:55 -070024
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070025
26BUFSIZE = 8192
27PIPE = subprocess.PIPE
Guido van Rossum59691282013-10-30 14:52:03 -070028STDOUT = subprocess.STDOUT
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070029_mmap_counter = itertools.count()
30
Guido van Rossuma8d630a2013-11-01 14:20:55 -070031
Victor Stinnerf67f4602014-10-14 22:56:25 +020032if hasattr(socket, 'socketpair'):
33 # Since Python 3.5, socket.socketpair() is now also available on Windows
34 socketpair = socket.socketpair
35else:
36 # Replacement for socket.socketpair()
37 def socketpair(family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0):
38 """A socket pair usable as a self-pipe, for Windows.
Guido van Rossuma8d630a2013-11-01 14:20:55 -070039
Victor Stinner15cc6782015-01-09 00:09:10 +010040 Origin: https://gist.github.com/4325783, by Geert Jansen.
41 Public domain.
Victor Stinnerf67f4602014-10-14 22:56:25 +020042 """
43 if family == socket.AF_INET:
44 host = '127.0.0.1'
45 elif family == socket.AF_INET6:
46 host = '::1'
47 else:
Victor Stinner15cc6782015-01-09 00:09:10 +010048 raise ValueError("Only AF_INET and AF_INET6 socket address "
49 "families are supported")
Victor Stinnerf67f4602014-10-14 22:56:25 +020050 if type != socket.SOCK_STREAM:
51 raise ValueError("Only SOCK_STREAM socket type is supported")
52 if proto != 0:
53 raise ValueError("Only protocol zero is supported")
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070054
Victor Stinnerf67f4602014-10-14 22:56:25 +020055 # We create a connected TCP socket. Note the trick with setblocking(0)
56 # that prevents us from having to create a thread.
57 lsock = socket.socket(family, type, proto)
Victor Stinnera9fa2662014-06-04 00:12:28 +020058 try:
Victor Stinnerf67f4602014-10-14 22:56:25 +020059 lsock.bind((host, 0))
60 lsock.listen(1)
61 # On IPv6, ignore flow_info and scope_id
62 addr, port = lsock.getsockname()[:2]
63 csock = socket.socket(family, type, proto)
Victor Stinnera9fa2662014-06-04 00:12:28 +020064 try:
Victor Stinnerf67f4602014-10-14 22:56:25 +020065 csock.setblocking(False)
66 try:
67 csock.connect((addr, port))
68 except (BlockingIOError, InterruptedError):
69 pass
70 csock.setblocking(True)
71 ssock, _ = lsock.accept()
72 except:
73 csock.close()
74 raise
75 finally:
76 lsock.close()
77 return (ssock, csock)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070078
Guido van Rossuma8d630a2013-11-01 14:20:55 -070079
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070080# Replacement for os.pipe() using handles instead of fds
Guido van Rossuma8d630a2013-11-01 14:20:55 -070081
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070082
83def pipe(*, duplex=False, overlapped=(True, True), bufsize=BUFSIZE):
84 """Like os.pipe() but with overlapped support and using handles not fds."""
85 address = tempfile.mktemp(prefix=r'\\.\pipe\python-pipe-%d-%d-' %
86 (os.getpid(), next(_mmap_counter)))
87
88 if duplex:
89 openmode = _winapi.PIPE_ACCESS_DUPLEX
90 access = _winapi.GENERIC_READ | _winapi.GENERIC_WRITE
91 obsize, ibsize = bufsize, bufsize
92 else:
93 openmode = _winapi.PIPE_ACCESS_INBOUND
94 access = _winapi.GENERIC_WRITE
95 obsize, ibsize = 0, bufsize
96
97 openmode |= _winapi.FILE_FLAG_FIRST_PIPE_INSTANCE
98
99 if overlapped[0]:
100 openmode |= _winapi.FILE_FLAG_OVERLAPPED
101
102 if overlapped[1]:
103 flags_and_attribs = _winapi.FILE_FLAG_OVERLAPPED
104 else:
105 flags_and_attribs = 0
106
107 h1 = h2 = None
108 try:
109 h1 = _winapi.CreateNamedPipe(
110 address, openmode, _winapi.PIPE_WAIT,
111 1, obsize, ibsize, _winapi.NMPWAIT_WAIT_FOREVER, _winapi.NULL)
112
113 h2 = _winapi.CreateFile(
114 address, access, 0, _winapi.NULL, _winapi.OPEN_EXISTING,
115 flags_and_attribs, _winapi.NULL)
116
117 ov = _winapi.ConnectNamedPipe(h1, overlapped=True)
118 ov.GetOverlappedResult(True)
119 return h1, h2
120 except:
121 if h1 is not None:
122 _winapi.CloseHandle(h1)
123 if h2 is not None:
124 _winapi.CloseHandle(h2)
125 raise
126
Guido van Rossuma8d630a2013-11-01 14:20:55 -0700127
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700128# Wrapper for a pipe handle
Guido van Rossuma8d630a2013-11-01 14:20:55 -0700129
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700130
131class PipeHandle:
132 """Wrapper for an overlapped pipe handle which is vaguely file-object like.
133
134 The IOCP event loop can use these instead of socket objects.
135 """
136 def __init__(self, handle):
137 self._handle = handle
138
Victor Stinner1b9763d2014-12-18 23:47:27 +0100139 def __repr__(self):
Victor Stinner29ad0112015-01-15 00:04:21 +0100140 if self._handle is not None:
Victor Stinner1b9763d2014-12-18 23:47:27 +0100141 handle = 'handle=%r' % self._handle
142 else:
143 handle = 'closed'
144 return '<%s %s>' % (self.__class__.__name__, handle)
145
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700146 @property
147 def handle(self):
148 return self._handle
149
150 def fileno(self):
Victor Stinner2a3f38f2015-01-26 15:03:44 +0100151 if self._handle is None:
152 raise ValueError("I/O operatioon on closed pipe")
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700153 return self._handle
154
155 def close(self, *, CloseHandle=_winapi.CloseHandle):
Victor Stinner29ad0112015-01-15 00:04:21 +0100156 if self._handle is not None:
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700157 CloseHandle(self._handle)
Victor Stinner29ad0112015-01-15 00:04:21 +0100158 self._handle = None
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700159
Victor Stinner978a9af2015-01-29 17:50:58 +0100160 def __del__(self):
161 if self._handle is not None:
162 warnings.warn("unclosed %r" % self, ResourceWarning)
163 self.close()
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700164
165 def __enter__(self):
166 return self
167
168 def __exit__(self, t, v, tb):
169 self.close()
170
Guido van Rossuma8d630a2013-11-01 14:20:55 -0700171
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700172# Replacement for subprocess.Popen using overlapped pipe handles
Guido van Rossuma8d630a2013-11-01 14:20:55 -0700173
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700174
175class Popen(subprocess.Popen):
176 """Replacement for subprocess.Popen using overlapped pipe handles.
177
178 The stdin, stdout, stderr are None or instances of PipeHandle.
179 """
180 def __init__(self, args, stdin=None, stdout=None, stderr=None, **kwds):
Guido van Rossum59691282013-10-30 14:52:03 -0700181 assert not kwds.get('universal_newlines')
182 assert kwds.get('bufsize', 0) == 0
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700183 stdin_rfd = stdout_wfd = stderr_wfd = None
184 stdin_wh = stdout_rh = stderr_rh = None
185 if stdin == PIPE:
Guido van Rossum59691282013-10-30 14:52:03 -0700186 stdin_rh, stdin_wh = pipe(overlapped=(False, True), duplex=True)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700187 stdin_rfd = msvcrt.open_osfhandle(stdin_rh, os.O_RDONLY)
Guido van Rossum59691282013-10-30 14:52:03 -0700188 else:
189 stdin_rfd = stdin
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700190 if stdout == PIPE:
191 stdout_rh, stdout_wh = pipe(overlapped=(True, False))
192 stdout_wfd = msvcrt.open_osfhandle(stdout_wh, 0)
Guido van Rossum59691282013-10-30 14:52:03 -0700193 else:
194 stdout_wfd = stdout
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700195 if stderr == PIPE:
196 stderr_rh, stderr_wh = pipe(overlapped=(True, False))
197 stderr_wfd = msvcrt.open_osfhandle(stderr_wh, 0)
Guido van Rossum59691282013-10-30 14:52:03 -0700198 elif stderr == STDOUT:
199 stderr_wfd = stdout_wfd
200 else:
201 stderr_wfd = stderr
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700202 try:
Guido van Rossum59691282013-10-30 14:52:03 -0700203 super().__init__(args, stdin=stdin_rfd, stdout=stdout_wfd,
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700204 stderr=stderr_wfd, **kwds)
205 except:
206 for h in (stdin_wh, stdout_rh, stderr_rh):
Guido van Rossum59691282013-10-30 14:52:03 -0700207 if h is not None:
208 _winapi.CloseHandle(h)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700209 raise
210 else:
211 if stdin_wh is not None:
212 self.stdin = PipeHandle(stdin_wh)
213 if stdout_rh is not None:
214 self.stdout = PipeHandle(stdout_rh)
215 if stderr_rh is not None:
216 self.stderr = PipeHandle(stderr_rh)
217 finally:
218 if stdin == PIPE:
219 os.close(stdin_rfd)
220 if stdout == PIPE:
221 os.close(stdout_wfd)
222 if stderr == PIPE:
223 os.close(stderr_wfd)