mirror of
https://github.com/python/cpython.git
synced 2024-11-25 09:39:56 +01:00
fb2c3465f0
* asyncio: __del__() keep reference to warnings.warn The __del__() methods of asyncio classes now keep a strong reference to the warnings.warn() to be able to display the ResourceWarning warning in more cases. Ensure that the function remains available if instances are destroyed late during Python shutdown (while module symbols are cleared). * Rename warn parameter to _warn "_warn" name is a hint that it's not the regular warnings.warn() function.
174 lines
4.9 KiB
Python
174 lines
4.9 KiB
Python
"""Various Windows specific bits and pieces."""
|
|
|
|
import sys
|
|
|
|
if sys.platform != 'win32': # pragma: no cover
|
|
raise ImportError('win32 only')
|
|
|
|
import _winapi
|
|
import itertools
|
|
import msvcrt
|
|
import os
|
|
import subprocess
|
|
import tempfile
|
|
import warnings
|
|
|
|
|
|
__all__ = 'pipe', 'Popen', 'PIPE', 'PipeHandle'
|
|
|
|
|
|
# Constants/globals
|
|
|
|
|
|
BUFSIZE = 8192
|
|
PIPE = subprocess.PIPE
|
|
STDOUT = subprocess.STDOUT
|
|
_mmap_counter = itertools.count()
|
|
|
|
|
|
# Replacement for os.pipe() using handles instead of fds
|
|
|
|
|
|
def pipe(*, duplex=False, overlapped=(True, True), bufsize=BUFSIZE):
|
|
"""Like os.pipe() but with overlapped support and using handles not fds."""
|
|
address = tempfile.mktemp(
|
|
prefix=r'\\.\pipe\python-pipe-{:d}-{:d}-'.format(
|
|
os.getpid(), next(_mmap_counter)))
|
|
|
|
if duplex:
|
|
openmode = _winapi.PIPE_ACCESS_DUPLEX
|
|
access = _winapi.GENERIC_READ | _winapi.GENERIC_WRITE
|
|
obsize, ibsize = bufsize, bufsize
|
|
else:
|
|
openmode = _winapi.PIPE_ACCESS_INBOUND
|
|
access = _winapi.GENERIC_WRITE
|
|
obsize, ibsize = 0, bufsize
|
|
|
|
openmode |= _winapi.FILE_FLAG_FIRST_PIPE_INSTANCE
|
|
|
|
if overlapped[0]:
|
|
openmode |= _winapi.FILE_FLAG_OVERLAPPED
|
|
|
|
if overlapped[1]:
|
|
flags_and_attribs = _winapi.FILE_FLAG_OVERLAPPED
|
|
else:
|
|
flags_and_attribs = 0
|
|
|
|
h1 = h2 = None
|
|
try:
|
|
h1 = _winapi.CreateNamedPipe(
|
|
address, openmode, _winapi.PIPE_WAIT,
|
|
1, obsize, ibsize, _winapi.NMPWAIT_WAIT_FOREVER, _winapi.NULL)
|
|
|
|
h2 = _winapi.CreateFile(
|
|
address, access, 0, _winapi.NULL, _winapi.OPEN_EXISTING,
|
|
flags_and_attribs, _winapi.NULL)
|
|
|
|
ov = _winapi.ConnectNamedPipe(h1, overlapped=True)
|
|
ov.GetOverlappedResult(True)
|
|
return h1, h2
|
|
except:
|
|
if h1 is not None:
|
|
_winapi.CloseHandle(h1)
|
|
if h2 is not None:
|
|
_winapi.CloseHandle(h2)
|
|
raise
|
|
|
|
|
|
# Wrapper for a pipe handle
|
|
|
|
|
|
class PipeHandle:
|
|
"""Wrapper for an overlapped pipe handle which is vaguely file-object like.
|
|
|
|
The IOCP event loop can use these instead of socket objects.
|
|
"""
|
|
def __init__(self, handle):
|
|
self._handle = handle
|
|
|
|
def __repr__(self):
|
|
if self._handle is not None:
|
|
handle = f'handle={self._handle!r}'
|
|
else:
|
|
handle = 'closed'
|
|
return f'<{self.__class__.__name__} {handle}>'
|
|
|
|
@property
|
|
def handle(self):
|
|
return self._handle
|
|
|
|
def fileno(self):
|
|
if self._handle is None:
|
|
raise ValueError("I/O operation on closed pipe")
|
|
return self._handle
|
|
|
|
def close(self, *, CloseHandle=_winapi.CloseHandle):
|
|
if self._handle is not None:
|
|
CloseHandle(self._handle)
|
|
self._handle = None
|
|
|
|
def __del__(self, _warn=warnings.warn):
|
|
if self._handle is not None:
|
|
_warn(f"unclosed {self!r}", ResourceWarning, source=self)
|
|
self.close()
|
|
|
|
def __enter__(self):
|
|
return self
|
|
|
|
def __exit__(self, t, v, tb):
|
|
self.close()
|
|
|
|
|
|
# Replacement for subprocess.Popen using overlapped pipe handles
|
|
|
|
|
|
class Popen(subprocess.Popen):
|
|
"""Replacement for subprocess.Popen using overlapped pipe handles.
|
|
|
|
The stdin, stdout, stderr are None or instances of PipeHandle.
|
|
"""
|
|
def __init__(self, args, stdin=None, stdout=None, stderr=None, **kwds):
|
|
assert not kwds.get('universal_newlines')
|
|
assert kwds.get('bufsize', 0) == 0
|
|
stdin_rfd = stdout_wfd = stderr_wfd = None
|
|
stdin_wh = stdout_rh = stderr_rh = None
|
|
if stdin == PIPE:
|
|
stdin_rh, stdin_wh = pipe(overlapped=(False, True), duplex=True)
|
|
stdin_rfd = msvcrt.open_osfhandle(stdin_rh, os.O_RDONLY)
|
|
else:
|
|
stdin_rfd = stdin
|
|
if stdout == PIPE:
|
|
stdout_rh, stdout_wh = pipe(overlapped=(True, False))
|
|
stdout_wfd = msvcrt.open_osfhandle(stdout_wh, 0)
|
|
else:
|
|
stdout_wfd = stdout
|
|
if stderr == PIPE:
|
|
stderr_rh, stderr_wh = pipe(overlapped=(True, False))
|
|
stderr_wfd = msvcrt.open_osfhandle(stderr_wh, 0)
|
|
elif stderr == STDOUT:
|
|
stderr_wfd = stdout_wfd
|
|
else:
|
|
stderr_wfd = stderr
|
|
try:
|
|
super().__init__(args, stdin=stdin_rfd, stdout=stdout_wfd,
|
|
stderr=stderr_wfd, **kwds)
|
|
except:
|
|
for h in (stdin_wh, stdout_rh, stderr_rh):
|
|
if h is not None:
|
|
_winapi.CloseHandle(h)
|
|
raise
|
|
else:
|
|
if stdin_wh is not None:
|
|
self.stdin = PipeHandle(stdin_wh)
|
|
if stdout_rh is not None:
|
|
self.stdout = PipeHandle(stdout_rh)
|
|
if stderr_rh is not None:
|
|
self.stderr = PipeHandle(stderr_rh)
|
|
finally:
|
|
if stdin == PIPE:
|
|
os.close(stdin_rfd)
|
|
if stdout == PIPE:
|
|
os.close(stdout_wfd)
|
|
if stderr == PIPE:
|
|
os.close(stderr_wfd)
|