Commit 18d7c7d4 authored by Jason Madden's avatar Jason Madden

Support more objects that implement the buffer protocal by asking for the...

Support more objects that implement the buffer protocal by asking for the len() of their memoryview, not the len() of themselves.

Also potentially reduces the number of calls to memoryview so may be slightly more efficient.

Fixes #466.
parent de32caa8
......@@ -25,6 +25,9 @@ Unreleased
- In non-monkey-patched environments under Python 2.7.9 or above or
Python 3, using a gevent SSL socket could cause the greenlet to
block. See :issue:`597` by David Ford.
- ``gevent.socket.socket.sendall`` supports arbitrary objects that
implement the buffer protocol (such as ctypes structurs), just like
native sockets. Reported in :issue:`466` by tzickel.
1.1a1 (Jun 29, 2015)
====================
......
......@@ -95,11 +95,12 @@ class SocketAdapter(object):
def sendall(self, data):
fileno = self.fileno()
bytes_total = len(data)
data_memory = _get_memory(data)
bytes_total = len(data_memory)
bytes_written = 0
while True:
try:
bytes_written += _write(fileno, _get_memory(data, bytes_written))
bytes_written += _write(fileno, data_memory[bytes_written:])
except (IOError, OSError) as ex:
code = ex.args[0]
if code not in ignored_errors:
......
......@@ -20,14 +20,19 @@ _fileobject = __socket__._fileobject
if sys.version_info[:2] < (2, 7):
_get_memory = buffer
else:
def _get_memory(string, offset):
def _get_memory(data):
try:
return memoryview(string)[offset:]
mv = memoryview(data)
if mv.shape:
return mv
# No shape, probably working with a ctypes object,
# or something else exotic that supports the buffer interface
return mv.tobytes()
except TypeError:
# fixes "python2.7 array.array doesn't support memoryview used in
# gevent.socket.send" issue
# (http://code.google.com/p/gevent/issues/detail?id=94)
return buffer(string, offset)
return buffer(data)
class _closedsocket(object):
......@@ -287,17 +292,18 @@ class socket(object):
data = data.encode()
# this sendall is also reused by gevent.ssl.SSLSocket subclass,
# so it should not call self._sock methods directly
data_memory = _get_memory(data)
if self.timeout is None:
data_sent = 0
while data_sent < len(data):
data_sent += self.send(_get_memory(data, data_sent), flags)
while data_sent < len(data_memory):
data_sent += self.send(data_memory[data_sent:], flags)
else:
timeleft = self.timeout
end = time.time() + timeleft
data_sent = 0
while True:
data_sent += self.send(_get_memory(data, data_sent), flags, timeout=timeleft)
if data_sent >= len(data):
data_sent += self.send(data_memory[data_sent:], flags, timeout=timeleft)
if data_sent >= len(data_memory):
return
timeleft = end - time.time()
if timeleft <= 0:
......
......@@ -23,8 +23,13 @@ __dns__ = _socketcommon.__dns__
SocketIO = __socket__.SocketIO
def _get_memory(string, offset):
return memoryview(string)[offset:]
def _get_memory(data):
mv = memoryview(data)
if mv.shape:
return mv
# No shape, probably working with a ctypes object,
# or something else exotic that supports the buffer interface
return mv.tobytes()
timeout_default = object()
......@@ -320,17 +325,18 @@ class socket(object):
raise
def sendall(self, data, flags=0):
data_memory = _get_memory(data)
if self.timeout is None:
data_sent = 0
while data_sent < len(data):
data_sent += self.send(_get_memory(data, data_sent), flags)
while data_sent < len(data_memory):
data_sent += self.send(data_memory[data_sent:], flags)
else:
timeleft = self.timeout
end = time.time() + timeleft
data_sent = 0
while True:
data_sent += self.send(_get_memory(data, data_sent), flags, timeout=timeleft)
if data_sent >= len(data):
data_sent += self.send(data_memory[data_sent:], flags, timeout=timeleft)
if data_sent >= len(data_memory):
break
timeleft = end - time.time()
if timeleft <= 0:
......
# See issue #466
import ctypes
class AnStructure(ctypes.Structure):
_fields_ = [("x", ctypes.c_int)]
def _send(socket):
for meth in ('sendall', 'send'):
anStructure = AnStructure()
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.connect(('127.0.0.1', 12345))
getattr(sock, meth)(anStructure)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.connect(('127.0.0.1', 12345))
sock.settimeout(1.0)
getattr(sock, meth)(anStructure)
def TestSendBuiltinSocket():
import socket
_send(socket)
def TestSendGeventSocket():
import gevent.socket
_send(gevent.socket)
if __name__ == '__main__':
TestSendBuiltinSocket()
TestSendGeventSocket()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment