blob: 7c63fb904b3bdd90b56d6c9b000d913d9653de0a [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:
Victor Stinnere19558a2016-03-23 00:28:08 +0100162 warnings.warn("unclosed %r" % self, ResourceWarning,
163 source=self)
Victor Stinner978a9af2015-01-29 17:50:58 +0100164 self.close()
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700165
166 def __enter__(self):
167 return self
168
169 def __exit__(self, t, v, tb):
170 self.close()
171
Guido van Rossuma8d630a2013-11-01 14:20:55 -0700172
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700173# Replacement for subprocess.Popen using overlapped pipe handles
Guido van Rossuma8d630a2013-11-01 14:20:55 -0700174
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700175
176class Popen(subprocess.Popen):
177 """Replacement for subprocess.Popen using overlapped pipe handles.
178
179 The stdin, stdout, stderr are None or instances of PipeHandle.
180 """
181 def __init__(self, args, stdin=None, stdout=None, stderr=None, **kwds):
Guido van Rossum59691282013-10-30 14:52:03 -0700182 assert not kwds.get('universal_newlines')
183 assert kwds.get('bufsize', 0) == 0
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700184 stdin_rfd = stdout_wfd = stderr_wfd = None
185 stdin_wh = stdout_rh = stderr_rh = None
186 if stdin == PIPE:
Guido van Rossum59691282013-10-30 14:52:03 -0700187 stdin_rh, stdin_wh = pipe(overlapped=(False, True), duplex=True)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700188 stdin_rfd = msvcrt.open_osfhandle(stdin_rh, os.O_RDONLY)
Guido van Rossum59691282013-10-30 14:52:03 -0700189 else:
190 stdin_rfd = stdin
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700191 if stdout == PIPE:
192 stdout_rh, stdout_wh = pipe(overlapped=(True, False))
193 stdout_wfd = msvcrt.open_osfhandle(stdout_wh, 0)
Guido van Rossum59691282013-10-30 14:52:03 -0700194 else:
195 stdout_wfd = stdout
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700196 if stderr == PIPE:
197 stderr_rh, stderr_wh = pipe(overlapped=(True, False))
198 stderr_wfd = msvcrt.open_osfhandle(stderr_wh, 0)
Guido van Rossum59691282013-10-30 14:52:03 -0700199 elif stderr == STDOUT:
200 stderr_wfd = stdout_wfd
201 else:
202 stderr_wfd = stderr
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700203 try:
Guido van Rossum59691282013-10-30 14:52:03 -0700204 super().__init__(args, stdin=stdin_rfd, stdout=stdout_wfd,
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700205 stderr=stderr_wfd, **kwds)
206 except:
207 for h in (stdin_wh, stdout_rh, stderr_rh):
Guido van Rossum59691282013-10-30 14:52:03 -0700208 if h is not None:
209 _winapi.CloseHandle(h)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700210 raise
211 else:
212 if stdin_wh is not None:
213 self.stdin = PipeHandle(stdin_wh)
214 if stdout_rh is not None:
215 self.stdout = PipeHandle(stdout_rh)
216 if stderr_rh is not None:
217 self.stderr = PipeHandle(stderr_rh)
218 finally:
219 if stdin == PIPE:
220 os.close(stdin_rfd)
221 if stdout == PIPE:
222 os.close(stdout_wfd)
223 if stderr == PIPE:
224 os.close(stderr_wfd)