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 functools import partial
|
||||
|
||||
try:
|
||||
import _overlapped
|
||||
import msvcrt
|
||||
except ImportError:
|
||||
_overlapped = None
|
||||
msvcrt = None
|
||||
|
||||
try:
|
||||
import errno
|
||||
except ImportError:
|
||||
|
|
@ -467,6 +474,39 @@ def _sendfile_use_send(self, file, offset=0, count=None):
|
|||
if total_sent > 0 and hasattr(file, 'seek'):
|
||||
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):
|
||||
if 'b' not in getattr(file, 'mode', 'b'):
|
||||
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.
|
||||
"""
|
||||
try:
|
||||
if sys.platform == "win32":
|
||||
return self._sendfile_use_transmitfile(file, offset, count)
|
||||
return self._sendfile_use_sendfile(file, offset, count)
|
||||
except _GiveupOnSendfile:
|
||||
return self._sendfile_use_send(file, offset, count)
|
||||
|
|
|
|||
|
|
@ -7055,6 +7055,15 @@ def meth_from_sock(self, sock):
|
|||
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')
|
||||
class LinuxKernelCryptoAPI(unittest.TestCase):
|
||||
# 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;
|
||||
}
|
||||
|
||||
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__,
|
||||
"ReadFile($self, handle, size, /)\n"
|
||||
"--\n"
|
||||
|
|
|
|||
|
|
@ -876,44 +876,13 @@ _overlapped_Overlapped_cancel_impl(OverlappedObject *self)
|
|||
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 *
|
||||
_overlapped_Overlapped_getresult_impl(OverlappedObject *self, BOOL wait)
|
||||
/*[clinic end generated code: output=8c9bd04d08994f6c input=aa5b03e9897ca074]*/
|
||||
check_getresult_error(OverlappedObject *self, DWORD transferred)
|
||||
{
|
||||
DWORD transferred = 0;
|
||||
BOOL ret;
|
||||
DWORD err;
|
||||
PyObject *addr;
|
||||
PyObject *addr = NULL;
|
||||
DWORD err = self->error;
|
||||
|
||||
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 = err = ret ? ERROR_SUCCESS : GetLastError();
|
||||
switch (err) {
|
||||
switch (self->error) {
|
||||
case ERROR_SUCCESS:
|
||||
case ERROR_MORE_DATA:
|
||||
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 *
|
||||
do_ReadFile(OverlappedObject *self, HANDLE handle,
|
||||
char *bufstart, DWORD buflen)
|
||||
|
|
@ -1950,6 +1995,7 @@ _overlapped_Overlapped_WSARecvFromInto_impl(OverlappedObject *self,
|
|||
|
||||
static PyMethodDef Overlapped_methods[] = {
|
||||
_OVERLAPPED_OVERLAPPED_GETRESULT_METHODDEF
|
||||
_OVERLAPPED_OVERLAPPED_GETRESULTEX_METHODDEF
|
||||
_OVERLAPPED_OVERLAPPED_CANCEL_METHODDEF
|
||||
_OVERLAPPED_OVERLAPPED_READFILE_METHODDEF
|
||||
_OVERLAPPED_OVERLAPPED_READFILEINTO_METHODDEF
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue