blob: 5f8327eba63aa3827fc3398e26500121f1e76729 [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
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070017
18
19__all__ = ['socketpair', 'pipe', 'Popen', 'PIPE', 'PipeHandle']
20
Guido van Rossuma8d630a2013-11-01 14:20:55 -070021
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070022# Constants/globals
Guido van Rossuma8d630a2013-11-01 14:20:55 -070023
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070024
25BUFSIZE = 8192
26PIPE = subprocess.PIPE
Guido van Rossum59691282013-10-30 14:52:03 -070027STDOUT = subprocess.STDOUT
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070028_mmap_counter = itertools.count()
29
Guido van Rossuma8d630a2013-11-01 14:20:55 -070030
Victor Stinnerf67f4602014-10-14 22:56:25 +020031if hasattr(socket, 'socketpair'):
32 # Since Python 3.5, socket.socketpair() is now also available on Windows
33 socketpair = socket.socketpair
34else:
35 # Replacement for socket.socketpair()
36 def socketpair(family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0):
37 """A socket pair usable as a self-pipe, for Windows.
Guido van Rossuma8d630a2013-11-01 14:20:55 -070038
Victor Stinner15cc6782015-01-09 00:09:10 +010039 Origin: https://gist.github.com/4325783, by Geert Jansen.
40 Public domain.
Victor Stinnerf67f4602014-10-14 22:56:25 +020041 """
42 if family == socket.AF_INET:
43 host = '127.0.0.1'
44 elif family == socket.AF_INET6:
45 host = '::1'
46 else:
Victor Stinner15cc6782015-01-09 00:09:10 +010047 raise ValueError("Only AF_INET and AF_INET6 socket address "
48 "families are supported")
Victor Stinnerf67f4602014-10-14 22:56:25 +020049 if type != socket.SOCK_STREAM:
50 raise ValueError("Only SOCK_STREAM socket type is supported")
51 if proto != 0:
52 raise ValueError("Only protocol zero is supported")
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070053
Victor Stinnerf67f4602014-10-14 22:56:25 +020054 # We create a connected TCP socket. Note the trick with setblocking(0)
55 # that prevents us from having to create a thread.
56 lsock = socket.socket(family, type, proto)
Victor Stinnera9fa2662014-06-04 00:12:28 +020057 try:
Victor Stinnerf67f4602014-10-14 22:56:25 +020058 lsock.bind((host, 0))
59 lsock.listen(1)
60 # On IPv6, ignore flow_info and scope_id
61 addr, port = lsock.getsockname()[:2]
62 csock = socket.socket(family, type, proto)
Victor Stinnera9fa2662014-06-04 00:12:28 +020063 try:
Victor Stinnerf67f4602014-10-14 22:56:25 +020064 csock.setblocking(False)
65 try:
66 csock.connect((addr, port))
67 except (BlockingIOError, InterruptedError):
68 pass
69 csock.setblocking(True)
70 ssock, _ = lsock.accept()
71 except:
72 csock.close()
73 raise
74 finally:
75 lsock.close()
76 return (ssock, csock)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070077
Guido van Rossuma8d630a2013-11-01 14:20:55 -070078
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070079# Replacement for os.pipe() using handles instead of fds
Guido van Rossuma8d630a2013-11-01 14:20:55 -070080
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070081
82def pipe(*, duplex=False, overlapped=(True, True), bufsize=BUFSIZE):
83 """Like os.pipe() but with overlapped support and using handles not fds."""
84 address = tempfile.mktemp(prefix=r'\\.\pipe\python-pipe-%d-%d-' %
85 (os.getpid(), next(_mmap_counter)))
86
87 if duplex:
88 openmode = _winapi.PIPE_ACCESS_DUPLEX
89 access = _winapi.GENERIC_READ | _winapi.GENERIC_WRITE
90 obsize, ibsize = bufsize, bufsize
91 else:
92 openmode = _winapi.PIPE_ACCESS_INBOUND
93 access = _winapi.GENERIC_WRITE
94 obsize, ibsize = 0, bufsize
95
96 openmode |= _winapi.FILE_FLAG_FIRST_PIPE_INSTANCE
97
98 if overlapped[0]:
99 openmode |= _winapi.FILE_FLAG_OVERLAPPED
100
101 if overlapped[1]:
102 flags_and_attribs = _winapi.FILE_FLAG_OVERLAPPED
103 else:
104 flags_and_attribs = 0
105
106 h1 = h2 = None
107 try:
108 h1 = _winapi.CreateNamedPipe(
109 address, openmode, _winapi.PIPE_WAIT,
110 1, obsize, ibsize, _winapi.NMPWAIT_WAIT_FOREVER, _winapi.NULL)
111
112 h2 = _winapi.CreateFile(
113 address, access, 0, _winapi.NULL, _winapi.OPEN_EXISTING,
114 flags_and_attribs, _winapi.NULL)
115
116 ov = _winapi.ConnectNamedPipe(h1, overlapped=True)
117 ov.GetOverlappedResult(True)
118 return h1, h2
119 except:
120 if h1 is not None:
121 _winapi.CloseHandle(h1)
122 if h2 is not None:
123 _winapi.CloseHandle(h2)
124 raise
125
Guido van Rossuma8d630a2013-11-01 14:20:55 -0700126
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700127# Wrapper for a pipe handle
Guido van Rossuma8d630a2013-11-01 14:20:55 -0700128
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700129
130class PipeHandle:
131 """Wrapper for an overlapped pipe handle which is vaguely file-object like.
132
133 The IOCP event loop can use these instead of socket objects.
134 """
135 def __init__(self, handle):
136 self._handle = handle
137
Victor Stinner1b9763d2014-12-18 23:47:27 +0100138 def __repr__(self):
Victor Stinner29ad0112015-01-15 00:04:21 +0100139 if self._handle is not None:
Victor Stinner1b9763d2014-12-18 23:47:27 +0100140 handle = 'handle=%r' % self._handle
141 else:
142 handle = 'closed'
143 return '<%s %s>' % (self.__class__.__name__, handle)
144
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700145 @property
146 def handle(self):
147 return self._handle
148
149 def fileno(self):
Victor Stinner2a3f38f2015-01-26 15:03:44 +0100150 if self._handle is None:
151 raise ValueError("I/O operatioon on closed pipe")
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700152 return self._handle
153
154 def close(self, *, CloseHandle=_winapi.CloseHandle):
Victor Stinner29ad0112015-01-15 00:04:21 +0100155 if self._handle is not None:
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700156 CloseHandle(self._handle)
Victor Stinner29ad0112015-01-15 00:04:21 +0100157 self._handle = None
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700158
159 __del__ = close
160
161 def __enter__(self):
162 return self
163
164 def __exit__(self, t, v, tb):
165 self.close()
166
Guido van Rossuma8d630a2013-11-01 14:20:55 -0700167
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700168# Replacement for subprocess.Popen using overlapped pipe handles
Guido van Rossuma8d630a2013-11-01 14:20:55 -0700169
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700170
171class Popen(subprocess.Popen):
172 """Replacement for subprocess.Popen using overlapped pipe handles.
173
174 The stdin, stdout, stderr are None or instances of PipeHandle.
175 """
176 def __init__(self, args, stdin=None, stdout=None, stderr=None, **kwds):
Guido van Rossum59691282013-10-30 14:52:03 -0700177 assert not kwds.get('universal_newlines')
178 assert kwds.get('bufsize', 0) == 0
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700179 stdin_rfd = stdout_wfd = stderr_wfd = None
180 stdin_wh = stdout_rh = stderr_rh = None
181 if stdin == PIPE:
Guido van Rossum59691282013-10-30 14:52:03 -0700182 stdin_rh, stdin_wh = pipe(overlapped=(False, True), duplex=True)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700183 stdin_rfd = msvcrt.open_osfhandle(stdin_rh, os.O_RDONLY)
Guido van Rossum59691282013-10-30 14:52:03 -0700184 else:
185 stdin_rfd = stdin
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700186 if stdout == PIPE:
187 stdout_rh, stdout_wh = pipe(overlapped=(True, False))
188 stdout_wfd = msvcrt.open_osfhandle(stdout_wh, 0)
Guido van Rossum59691282013-10-30 14:52:03 -0700189 else:
190 stdout_wfd = stdout
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700191 if stderr == PIPE:
192 stderr_rh, stderr_wh = pipe(overlapped=(True, False))
193 stderr_wfd = msvcrt.open_osfhandle(stderr_wh, 0)
Guido van Rossum59691282013-10-30 14:52:03 -0700194 elif stderr == STDOUT:
195 stderr_wfd = stdout_wfd
196 else:
197 stderr_wfd = stderr
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700198 try:
Guido van Rossum59691282013-10-30 14:52:03 -0700199 super().__init__(args, stdin=stdin_rfd, stdout=stdout_wfd,
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700200 stderr=stderr_wfd, **kwds)
201 except:
202 for h in (stdin_wh, stdout_rh, stderr_rh):
Guido van Rossum59691282013-10-30 14:52:03 -0700203 if h is not None:
204 _winapi.CloseHandle(h)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700205 raise
206 else:
207 if stdin_wh is not None:
208 self.stdin = PipeHandle(stdin_wh)
209 if stdout_rh is not None:
210 self.stdout = PipeHandle(stdout_rh)
211 if stderr_rh is not None:
212 self.stderr = PipeHandle(stderr_rh)
213 finally:
214 if stdin == PIPE:
215 os.close(stdin_rfd)
216 if stdout == PIPE:
217 os.close(stdout_wfd)
218 if stderr == PIPE:
219 os.close(stderr_wfd)