blob: 3b410976f9ddc8eaf80de5aa96275ef2f462eb9c [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
Victor Stinnerac577d72017-11-28 21:33:20 +010020__all__ = ['pipe', 'Popen', 'PIPE', 'PipeHandle']
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070021
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
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070032# Replacement for os.pipe() using handles instead of fds
Guido van Rossuma8d630a2013-11-01 14:20:55 -070033
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070034
35def pipe(*, duplex=False, overlapped=(True, True), bufsize=BUFSIZE):
36 """Like os.pipe() but with overlapped support and using handles not fds."""
37 address = tempfile.mktemp(prefix=r'\\.\pipe\python-pipe-%d-%d-' %
38 (os.getpid(), next(_mmap_counter)))
39
40 if duplex:
41 openmode = _winapi.PIPE_ACCESS_DUPLEX
42 access = _winapi.GENERIC_READ | _winapi.GENERIC_WRITE
43 obsize, ibsize = bufsize, bufsize
44 else:
45 openmode = _winapi.PIPE_ACCESS_INBOUND
46 access = _winapi.GENERIC_WRITE
47 obsize, ibsize = 0, bufsize
48
49 openmode |= _winapi.FILE_FLAG_FIRST_PIPE_INSTANCE
50
51 if overlapped[0]:
52 openmode |= _winapi.FILE_FLAG_OVERLAPPED
53
54 if overlapped[1]:
55 flags_and_attribs = _winapi.FILE_FLAG_OVERLAPPED
56 else:
57 flags_and_attribs = 0
58
59 h1 = h2 = None
60 try:
61 h1 = _winapi.CreateNamedPipe(
62 address, openmode, _winapi.PIPE_WAIT,
63 1, obsize, ibsize, _winapi.NMPWAIT_WAIT_FOREVER, _winapi.NULL)
64
65 h2 = _winapi.CreateFile(
66 address, access, 0, _winapi.NULL, _winapi.OPEN_EXISTING,
67 flags_and_attribs, _winapi.NULL)
68
69 ov = _winapi.ConnectNamedPipe(h1, overlapped=True)
70 ov.GetOverlappedResult(True)
71 return h1, h2
72 except:
73 if h1 is not None:
74 _winapi.CloseHandle(h1)
75 if h2 is not None:
76 _winapi.CloseHandle(h2)
77 raise
78
Guido van Rossuma8d630a2013-11-01 14:20:55 -070079
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070080# Wrapper for a pipe handle
Guido van Rossuma8d630a2013-11-01 14:20:55 -070081
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070082
83class PipeHandle:
84 """Wrapper for an overlapped pipe handle which is vaguely file-object like.
85
86 The IOCP event loop can use these instead of socket objects.
87 """
88 def __init__(self, handle):
89 self._handle = handle
90
Victor Stinner1b9763d2014-12-18 23:47:27 +010091 def __repr__(self):
Victor Stinner29ad0112015-01-15 00:04:21 +010092 if self._handle is not None:
Victor Stinner1b9763d2014-12-18 23:47:27 +010093 handle = 'handle=%r' % self._handle
94 else:
95 handle = 'closed'
96 return '<%s %s>' % (self.__class__.__name__, handle)
97
Guido van Rossum27b7c7e2013-10-17 13:40:50 -070098 @property
99 def handle(self):
100 return self._handle
101
102 def fileno(self):
Victor Stinner2a3f38f2015-01-26 15:03:44 +0100103 if self._handle is None:
Jim Fasarakis-Hilliard1e73dbb2017-03-26 23:59:08 +0300104 raise ValueError("I/O operation on closed pipe")
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700105 return self._handle
106
107 def close(self, *, CloseHandle=_winapi.CloseHandle):
Victor Stinner29ad0112015-01-15 00:04:21 +0100108 if self._handle is not None:
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700109 CloseHandle(self._handle)
Victor Stinner29ad0112015-01-15 00:04:21 +0100110 self._handle = None
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700111
Victor Stinner978a9af2015-01-29 17:50:58 +0100112 def __del__(self):
113 if self._handle is not None:
Victor Stinnere19558a2016-03-23 00:28:08 +0100114 warnings.warn("unclosed %r" % self, ResourceWarning,
115 source=self)
Victor Stinner978a9af2015-01-29 17:50:58 +0100116 self.close()
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700117
118 def __enter__(self):
119 return self
120
121 def __exit__(self, t, v, tb):
122 self.close()
123
Guido van Rossuma8d630a2013-11-01 14:20:55 -0700124
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700125# Replacement for subprocess.Popen using overlapped pipe handles
Guido van Rossuma8d630a2013-11-01 14:20:55 -0700126
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700127
128class Popen(subprocess.Popen):
129 """Replacement for subprocess.Popen using overlapped pipe handles.
130
131 The stdin, stdout, stderr are None or instances of PipeHandle.
132 """
133 def __init__(self, args, stdin=None, stdout=None, stderr=None, **kwds):
Guido van Rossum59691282013-10-30 14:52:03 -0700134 assert not kwds.get('universal_newlines')
135 assert kwds.get('bufsize', 0) == 0
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700136 stdin_rfd = stdout_wfd = stderr_wfd = None
137 stdin_wh = stdout_rh = stderr_rh = None
138 if stdin == PIPE:
Guido van Rossum59691282013-10-30 14:52:03 -0700139 stdin_rh, stdin_wh = pipe(overlapped=(False, True), duplex=True)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700140 stdin_rfd = msvcrt.open_osfhandle(stdin_rh, os.O_RDONLY)
Guido van Rossum59691282013-10-30 14:52:03 -0700141 else:
142 stdin_rfd = stdin
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700143 if stdout == PIPE:
144 stdout_rh, stdout_wh = pipe(overlapped=(True, False))
145 stdout_wfd = msvcrt.open_osfhandle(stdout_wh, 0)
Guido van Rossum59691282013-10-30 14:52:03 -0700146 else:
147 stdout_wfd = stdout
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700148 if stderr == PIPE:
149 stderr_rh, stderr_wh = pipe(overlapped=(True, False))
150 stderr_wfd = msvcrt.open_osfhandle(stderr_wh, 0)
Guido van Rossum59691282013-10-30 14:52:03 -0700151 elif stderr == STDOUT:
152 stderr_wfd = stdout_wfd
153 else:
154 stderr_wfd = stderr
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700155 try:
Guido van Rossum59691282013-10-30 14:52:03 -0700156 super().__init__(args, stdin=stdin_rfd, stdout=stdout_wfd,
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700157 stderr=stderr_wfd, **kwds)
158 except:
159 for h in (stdin_wh, stdout_rh, stderr_rh):
Guido van Rossum59691282013-10-30 14:52:03 -0700160 if h is not None:
161 _winapi.CloseHandle(h)
Guido van Rossum27b7c7e2013-10-17 13:40:50 -0700162 raise
163 else:
164 if stdin_wh is not None:
165 self.stdin = PipeHandle(stdin_wh)
166 if stdout_rh is not None:
167 self.stdout = PipeHandle(stdout_rh)
168 if stderr_rh is not None:
169 self.stderr = PipeHandle(stderr_rh)
170 finally:
171 if stdin == PIPE:
172 os.close(stdin_rfd)
173 if stdout == PIPE:
174 os.close(stdout_wfd)
175 if stderr == PIPE:
176 os.close(stderr_wfd)