mirror of
https://github.com/python/cpython.git
synced 2025-12-08 06:10:17 +00:00
Merge b153f22afe into 3db7bf2d18
This commit is contained in:
commit
cdaadfbe39
5 changed files with 168 additions and 35 deletions
|
|
@ -58,6 +58,13 @@
|
||||||
from enum import IntEnum, IntFlag
|
from enum import IntEnum, IntFlag
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
|
try:
|
||||||
|
import _overlapped
|
||||||
|
import msvcrt
|
||||||
|
except ImportError:
|
||||||
|
_overlapped = None
|
||||||
|
msvcrt = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import errno
|
import errno
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|
@ -467,6 +474,39 @@ def _sendfile_use_send(self, file, offset=0, count=None):
|
||||||
if total_sent > 0 and hasattr(file, 'seek'):
|
if total_sent > 0 and hasattr(file, 'seek'):
|
||||||
file.seek(offset + total_sent)
|
file.seek(offset + total_sent)
|
||||||
|
|
||||||
|
if _overlapped and msvcrt:
|
||||||
|
def _sendfile_use_transmitfile(self, file, offset=0, count=None):
|
||||||
|
self._check_sendfile_params(file, offset, count)
|
||||||
|
timeout = self.gettimeout()
|
||||||
|
if timeout == 0:
|
||||||
|
raise ValueError("non-blocking sockets are not supported")
|
||||||
|
ov = _overlapped.Overlapped()
|
||||||
|
offset_low = offset & 0xffff_ffff
|
||||||
|
offset_high = (offset >> 32) & 0xffff_ffff
|
||||||
|
count = count or 0
|
||||||
|
try:
|
||||||
|
fileno = file.fileno()
|
||||||
|
except (AttributeError, io.UnsupportedOperation) as err:
|
||||||
|
raise _GiveupOnSendfile(err) # not a regular file
|
||||||
|
try:
|
||||||
|
os.fstat(fileno)
|
||||||
|
except OSError as err:
|
||||||
|
raise _GiveupOnSendfile(err) # not a regular file
|
||||||
|
ov.TransmitFile(self.fileno(), msvcrt.get_osfhandle(fileno),
|
||||||
|
offset_low, offset_high, count, 0, 0)
|
||||||
|
timeout_ms = _overlapped.INFINITE
|
||||||
|
if timeout is not None:
|
||||||
|
timeout_ms = int(timeout * 1000)
|
||||||
|
try:
|
||||||
|
sent = ov.getresultex(timeout_ms, False)
|
||||||
|
except WindowsError as e:
|
||||||
|
if e.winerror == 258:
|
||||||
|
raise TimeoutError('timed out')
|
||||||
|
raise
|
||||||
|
if sent > 0 and hasattr(file, 'seek'):
|
||||||
|
file.seek(offset + sent)
|
||||||
|
return sent
|
||||||
|
|
||||||
def _check_sendfile_params(self, file, offset, count):
|
def _check_sendfile_params(self, file, offset, count):
|
||||||
if 'b' not in getattr(file, 'mode', 'b'):
|
if 'b' not in getattr(file, 'mode', 'b'):
|
||||||
raise ValueError("file should be opened in binary mode")
|
raise ValueError("file should be opened in binary mode")
|
||||||
|
|
@ -499,6 +539,8 @@ def sendfile(self, file, offset=0, count=None):
|
||||||
Non-blocking sockets are not supported.
|
Non-blocking sockets are not supported.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
|
if sys.platform == "win32":
|
||||||
|
return self._sendfile_use_transmitfile(file, offset, count)
|
||||||
return self._sendfile_use_sendfile(file, offset, count)
|
return self._sendfile_use_sendfile(file, offset, count)
|
||||||
except _GiveupOnSendfile:
|
except _GiveupOnSendfile:
|
||||||
return self._sendfile_use_send(file, offset, count)
|
return self._sendfile_use_send(file, offset, count)
|
||||||
|
|
|
||||||
|
|
@ -7055,6 +7055,15 @@ def meth_from_sock(self, sock):
|
||||||
return getattr(sock, "_sendfile_use_sendfile")
|
return getattr(sock, "_sendfile_use_sendfile")
|
||||||
|
|
||||||
|
|
||||||
|
@unittest.skipUnless(sys.platform == "win32", "Windows only test.")
|
||||||
|
class SendfileUsingTransmitfileTest(SendfileUsingSendTest):
|
||||||
|
"""
|
||||||
|
Test the TransmitFile() implementation of socket.sendfile().
|
||||||
|
"""
|
||||||
|
def meth_from_sock(self, sock):
|
||||||
|
return getattr(sock, "_sendfile_use_transmitfile")
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(HAVE_SOCKET_ALG, 'AF_ALG required')
|
@unittest.skipUnless(HAVE_SOCKET_ALG, 'AF_ALG required')
|
||||||
class LinuxKernelCryptoAPI(unittest.TestCase):
|
class LinuxKernelCryptoAPI(unittest.TestCase):
|
||||||
# tests for AF_ALG
|
# tests for AF_ALG
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
Use :c:func:`!TransmitFile` on Windows to implement :func:`socket.sendfile`.
|
||||||
35
Modules/clinic/overlapped.c.h
generated
35
Modules/clinic/overlapped.c.h
generated
|
|
@ -561,6 +561,41 @@ exit:
|
||||||
return return_value;
|
return return_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(_overlapped_Overlapped_getresultex__doc__,
|
||||||
|
"getresultex($self, milliseconds, alertable, /)\n"
|
||||||
|
"--\n"
|
||||||
|
"\n");
|
||||||
|
|
||||||
|
#define _OVERLAPPED_OVERLAPPED_GETRESULTEX_METHODDEF \
|
||||||
|
{"getresultex", _PyCFunction_CAST(_overlapped_Overlapped_getresultex), METH_FASTCALL, _overlapped_Overlapped_getresultex__doc__},
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_overlapped_Overlapped_getresultex_impl(OverlappedObject *self,
|
||||||
|
DWORD milliseconds, BOOL alertable);
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_overlapped_Overlapped_getresultex(OverlappedObject *self, PyObject *const *args, Py_ssize_t nargs)
|
||||||
|
{
|
||||||
|
PyObject *return_value = NULL;
|
||||||
|
DWORD milliseconds;
|
||||||
|
BOOL alertable;
|
||||||
|
|
||||||
|
if (!_PyArg_CheckPositional("getresultex", nargs, 2, 2)) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (!_PyLong_UnsignedLong_Converter(args[0], &milliseconds)) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
alertable = PyLong_AsInt(args[1]);
|
||||||
|
if (alertable == -1 && PyErr_Occurred()) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
return_value = _overlapped_Overlapped_getresultex_impl(self, milliseconds, alertable);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return return_value;
|
||||||
|
}
|
||||||
|
|
||||||
PyDoc_STRVAR(_overlapped_Overlapped_ReadFile__doc__,
|
PyDoc_STRVAR(_overlapped_Overlapped_ReadFile__doc__,
|
||||||
"ReadFile($self, handle, size, /)\n"
|
"ReadFile($self, handle, size, /)\n"
|
||||||
"--\n"
|
"--\n"
|
||||||
|
|
|
||||||
|
|
@ -876,44 +876,13 @@ _overlapped_Overlapped_cancel_impl(OverlappedObject *self)
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*[clinic input]
|
|
||||||
_overlapped.Overlapped.getresult
|
|
||||||
|
|
||||||
wait: BOOL(c_default='FALSE') = False
|
|
||||||
/
|
|
||||||
|
|
||||||
Retrieve result of operation.
|
|
||||||
|
|
||||||
If wait is true then it blocks until the operation is finished. If wait
|
|
||||||
is false and the operation is still pending then an error is raised.
|
|
||||||
[clinic start generated code]*/
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
_overlapped_Overlapped_getresult_impl(OverlappedObject *self, BOOL wait)
|
check_getresult_error(OverlappedObject *self, DWORD transferred)
|
||||||
/*[clinic end generated code: output=8c9bd04d08994f6c input=aa5b03e9897ca074]*/
|
|
||||||
{
|
{
|
||||||
DWORD transferred = 0;
|
PyObject *addr = NULL;
|
||||||
BOOL ret;
|
DWORD err = self->error;
|
||||||
DWORD err;
|
|
||||||
PyObject *addr;
|
|
||||||
|
|
||||||
if (self->type == TYPE_NONE) {
|
switch (self->error) {
|
||||||
PyErr_SetString(PyExc_ValueError, "operation not yet attempted");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self->type == TYPE_NOT_STARTED) {
|
|
||||||
PyErr_SetString(PyExc_ValueError, "operation failed to start");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
Py_BEGIN_ALLOW_THREADS
|
|
||||||
ret = GetOverlappedResult(self->handle, &self->overlapped, &transferred,
|
|
||||||
wait);
|
|
||||||
Py_END_ALLOW_THREADS
|
|
||||||
|
|
||||||
self->error = err = ret ? ERROR_SUCCESS : GetLastError();
|
|
||||||
switch (err) {
|
|
||||||
case ERROR_SUCCESS:
|
case ERROR_SUCCESS:
|
||||||
case ERROR_MORE_DATA:
|
case ERROR_MORE_DATA:
|
||||||
break;
|
break;
|
||||||
|
|
@ -1005,6 +974,82 @@ _overlapped_Overlapped_getresult_impl(OverlappedObject *self, BOOL wait)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*[clinic input]
|
||||||
|
_overlapped.Overlapped.getresult
|
||||||
|
|
||||||
|
wait: BOOL(c_default='FALSE') = False
|
||||||
|
/
|
||||||
|
|
||||||
|
Retrieve result of operation.
|
||||||
|
|
||||||
|
If wait is true then it blocks until the operation is finished. If wait
|
||||||
|
is false and the operation is still pending then an error is raised.
|
||||||
|
[clinic start generated code]*/
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_overlapped_Overlapped_getresult_impl(OverlappedObject *self, BOOL wait)
|
||||||
|
/*[clinic end generated code: output=8c9bd04d08994f6c input=aa5b03e9897ca074]*/
|
||||||
|
{
|
||||||
|
DWORD transferred = 0;
|
||||||
|
BOOL ret;
|
||||||
|
|
||||||
|
if (self->type == TYPE_NONE) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "operation not yet attempted");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->type == TYPE_NOT_STARTED) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "operation failed to start");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
ret = GetOverlappedResult(self->handle, &self->overlapped, &transferred,
|
||||||
|
wait);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
|
||||||
|
self->error = ret ? ERROR_SUCCESS : GetLastError();
|
||||||
|
|
||||||
|
return check_getresult_error(self, transferred);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*[clinic input]
|
||||||
|
_overlapped.Overlapped.getresultex
|
||||||
|
|
||||||
|
milliseconds: DWORD
|
||||||
|
alertable: BOOL
|
||||||
|
/
|
||||||
|
|
||||||
|
[clinic start generated code]*/
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
_overlapped_Overlapped_getresultex_impl(OverlappedObject *self,
|
||||||
|
DWORD milliseconds, BOOL alertable)
|
||||||
|
/*[clinic end generated code: output=ce0eb6ffb9618e54 input=ef4f4cab49ac1d80]*/
|
||||||
|
{
|
||||||
|
DWORD transferred = 0;
|
||||||
|
BOOL ret;
|
||||||
|
|
||||||
|
if (self->type == TYPE_NONE) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "operation not yet attempted");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->type == TYPE_NOT_STARTED) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "operation failed to start");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_BEGIN_ALLOW_THREADS
|
||||||
|
ret = GetOverlappedResultEx(self->handle, &self->overlapped, &transferred,
|
||||||
|
milliseconds, alertable);
|
||||||
|
Py_END_ALLOW_THREADS
|
||||||
|
|
||||||
|
self->error = ret ? ERROR_SUCCESS : GetLastError();
|
||||||
|
|
||||||
|
return check_getresult_error(self, transferred);
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
do_ReadFile(OverlappedObject *self, HANDLE handle,
|
do_ReadFile(OverlappedObject *self, HANDLE handle,
|
||||||
char *bufstart, DWORD buflen)
|
char *bufstart, DWORD buflen)
|
||||||
|
|
@ -1950,6 +1995,7 @@ _overlapped_Overlapped_WSARecvFromInto_impl(OverlappedObject *self,
|
||||||
|
|
||||||
static PyMethodDef Overlapped_methods[] = {
|
static PyMethodDef Overlapped_methods[] = {
|
||||||
_OVERLAPPED_OVERLAPPED_GETRESULT_METHODDEF
|
_OVERLAPPED_OVERLAPPED_GETRESULT_METHODDEF
|
||||||
|
_OVERLAPPED_OVERLAPPED_GETRESULTEX_METHODDEF
|
||||||
_OVERLAPPED_OVERLAPPED_CANCEL_METHODDEF
|
_OVERLAPPED_OVERLAPPED_CANCEL_METHODDEF
|
||||||
_OVERLAPPED_OVERLAPPED_READFILE_METHODDEF
|
_OVERLAPPED_OVERLAPPED_READFILE_METHODDEF
|
||||||
_OVERLAPPED_OVERLAPPED_READFILEINTO_METHODDEF
|
_OVERLAPPED_OVERLAPPED_READFILEINTO_METHODDEF
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue