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 ...@@ -25,6 +25,9 @@ Unreleased
- In non-monkey-patched environments under Python 2.7.9 or above or - 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 Python 3, using a gevent SSL socket could cause the greenlet to
block. See :issue:`597` by David Ford. 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) 1.1a1 (Jun 29, 2015)
==================== ====================
......
...@@ -95,11 +95,12 @@ class SocketAdapter(object): ...@@ -95,11 +95,12 @@ class SocketAdapter(object):
def sendall(self, data): def sendall(self, data):
fileno = self.fileno() fileno = self.fileno()
bytes_total = len(data) data_memory = _get_memory(data)
bytes_total = len(data_memory)
bytes_written = 0 bytes_written = 0
while True: while True:
try: try:
bytes_written += _write(fileno, _get_memory(data, bytes_written)) bytes_written += _write(fileno, data_memory[bytes_written:])
except (IOError, OSError) as ex: except (IOError, OSError) as ex:
code = ex.args[0] code = ex.args[0]
if code not in ignored_errors: if code not in ignored_errors:
......
...@@ -20,14 +20,19 @@ _fileobject = __socket__._fileobject ...@@ -20,14 +20,19 @@ _fileobject = __socket__._fileobject
if sys.version_info[:2] < (2, 7): if sys.version_info[:2] < (2, 7):
_get_memory = buffer _get_memory = buffer
else: else:
def _get_memory(string, offset): def _get_memory(data):
try: 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: except TypeError:
# fixes "python2.7 array.array doesn't support memoryview used in # fixes "python2.7 array.array doesn't support memoryview used in
# gevent.socket.send" issue # gevent.socket.send" issue
# (http://code.google.com/p/gevent/issues/detail?id=94) # (http://code.google.com/p/gevent/issues/detail?id=94)
return buffer(string, offset) return buffer(data)
class _closedsocket(object): class _closedsocket(object):
...@@ -287,17 +292,18 @@ class socket(object): ...@@ -287,17 +292,18 @@ class socket(object):
data = data.encode() data = data.encode()
# this sendall is also reused by gevent.ssl.SSLSocket subclass, # this sendall is also reused by gevent.ssl.SSLSocket subclass,
# so it should not call self._sock methods directly # so it should not call self._sock methods directly
data_memory = _get_memory(data)
if self.timeout is None: if self.timeout is None:
data_sent = 0 data_sent = 0
while data_sent < len(data): while data_sent < len(data_memory):
data_sent += self.send(_get_memory(data, data_sent), flags) data_sent += self.send(data_memory[data_sent:], flags)
else: else:
timeleft = self.timeout timeleft = self.timeout
end = time.time() + timeleft end = time.time() + timeleft
data_sent = 0 data_sent = 0
while True: while True:
data_sent += self.send(_get_memory(data, data_sent), flags, timeout=timeleft) data_sent += self.send(data_memory[data_sent:], flags, timeout=timeleft)
if data_sent >= len(data): if data_sent >= len(data_memory):
return return
timeleft = end - time.time() timeleft = end - time.time()
if timeleft <= 0: if timeleft <= 0:
......
...@@ -23,8 +23,13 @@ __dns__ = _socketcommon.__dns__ ...@@ -23,8 +23,13 @@ __dns__ = _socketcommon.__dns__
SocketIO = __socket__.SocketIO SocketIO = __socket__.SocketIO
def _get_memory(string, offset): def _get_memory(data):
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()
timeout_default = object() timeout_default = object()
...@@ -320,17 +325,18 @@ class socket(object): ...@@ -320,17 +325,18 @@ class socket(object):
raise raise
def sendall(self, data, flags=0): def sendall(self, data, flags=0):
data_memory = _get_memory(data)
if self.timeout is None: if self.timeout is None:
data_sent = 0 data_sent = 0
while data_sent < len(data): while data_sent < len(data_memory):
data_sent += self.send(_get_memory(data, data_sent), flags) data_sent += self.send(data_memory[data_sent:], flags)
else: else:
timeleft = self.timeout timeleft = self.timeout
end = time.time() + timeleft end = time.time() + timeleft
data_sent = 0 data_sent = 0
while True: while True:
data_sent += self.send(_get_memory(data, data_sent), flags, timeout=timeleft) data_sent += self.send(data_memory[data_sent:], flags, timeout=timeleft)
if data_sent >= len(data): if data_sent >= len(data_memory):
break break
timeleft = end - time.time() timeleft = end - time.time()
if timeleft <= 0: 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