socket: Add unit-tests, fixes, docs
This commit is contained in:
parent
5b5bbe55e1
commit
e8b8cea305
|
@ -4,6 +4,14 @@
|
||||||
##
|
##
|
||||||
|
|
||||||
|
|
||||||
|
"""Greensocket (non-blocking) for Tulip.
|
||||||
|
|
||||||
|
Use ``greentulip.socket`` in the same way as you would use stdlib's
|
||||||
|
``socket.socket`` in ``greentulip.task`` tasks or coroutines invoked
|
||||||
|
from them.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
import tulip
|
import tulip
|
||||||
|
|
||||||
from socket import *
|
from socket import *
|
||||||
|
@ -36,34 +44,73 @@ class socket:
|
||||||
def proto(self):
|
def proto(self):
|
||||||
return self._sock.proto
|
return self._sock.proto
|
||||||
|
|
||||||
def setblocking(flag):
|
def _proxy(attr):
|
||||||
assert not flag, 'greenlet.socket does not support blocking mode'
|
def proxy(self, *args, **kwargs):
|
||||||
|
meth = getattr(self._sock, attr)
|
||||||
|
return meth(*args, **kwargs)
|
||||||
|
|
||||||
|
proxy.__name__ = attr
|
||||||
|
proxy.__qualname__ = attr
|
||||||
|
proxy.__doc__ = getattr(getattr(std_socket, attr), '__doc__', None)
|
||||||
|
return proxy
|
||||||
|
|
||||||
|
def _copydoc(func):
|
||||||
|
func.__doc__ = getattr(getattr(std_socket, func.__name__), '__doc__', None)
|
||||||
|
return func
|
||||||
|
|
||||||
|
@_copydoc
|
||||||
|
def setblocking(self, flag):
|
||||||
|
if flag:
|
||||||
|
raise error('greentulip.socket does not support blocking mode')
|
||||||
|
|
||||||
|
@_copydoc
|
||||||
def recv(self, nbytes):
|
def recv(self, nbytes):
|
||||||
fut = tulip.get_event_loop().sock_recv(self._sock, nbytes)
|
fut = tulip.get_event_loop().sock_recv(self._sock, nbytes)
|
||||||
return yield_from(fut)
|
yield_from(fut)
|
||||||
|
return fut.result()
|
||||||
|
|
||||||
|
@_copydoc
|
||||||
def connect(self, addr):
|
def connect(self, addr):
|
||||||
fut = tulip.get_event_loop().sock_connect(self._sock, addr)
|
fut = tulip.get_event_loop().sock_connect(self._sock, addr)
|
||||||
return yield_from(fut)
|
yield_from(fut)
|
||||||
|
return fut.result()
|
||||||
|
|
||||||
|
@_copydoc
|
||||||
def sendall(self, data, flags=0):
|
def sendall(self, data, flags=0):
|
||||||
assert not flags
|
|
||||||
fut = tulip.get_event_loop().sock_sendall(self._sock, data)
|
|
||||||
return yield_from(fut)
|
|
||||||
|
|
||||||
def send(self, data, flags=0):
|
|
||||||
assert not flags
|
assert not flags
|
||||||
fut = tulip.get_event_loop().sock_sendall(self._sock, data)
|
fut = tulip.get_event_loop().sock_sendall(self._sock, data)
|
||||||
yield_from(fut)
|
yield_from(fut)
|
||||||
|
return fut.result()
|
||||||
|
|
||||||
|
@_copydoc
|
||||||
|
def send(self, data, flags=0):
|
||||||
|
self.sendall(data, flags)
|
||||||
return len(data)
|
return len(data)
|
||||||
|
|
||||||
|
@_copydoc
|
||||||
def accept(self):
|
def accept(self):
|
||||||
fut = tulip.get_event_loop().sock_accept(self._sock)
|
fut = tulip.get_event_loop().sock_accept(self._sock)
|
||||||
return yield_from(fut)
|
yield_from(fut)
|
||||||
|
sock, addr = fut.result()
|
||||||
|
return self.__class__.from_socket(sock), addr
|
||||||
|
|
||||||
def close(self):
|
@_copydoc
|
||||||
return self._sock.close()
|
def makefile(self, *args, **kwargs):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
bind = _proxy('bind')
|
||||||
|
listen = _proxy('listen')
|
||||||
|
getsockname = _proxy('getsockname')
|
||||||
|
getpeername = _proxy('getpeername')
|
||||||
|
gettimeout = _proxy('gettimeout')
|
||||||
|
getsockopt = _proxy('getsockopt')
|
||||||
|
setsockopt = _proxy('setsockopt')
|
||||||
|
fileno = _proxy('fileno')
|
||||||
|
detach = _proxy('detach')
|
||||||
|
close = _proxy('close')
|
||||||
|
shutdown = _proxy('shutdown')
|
||||||
|
|
||||||
|
del _copydoc, _proxy
|
||||||
|
|
||||||
|
|
||||||
def create_connection(address:tuple, timeout=None):
|
def create_connection(address:tuple, timeout=None):
|
||||||
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
##
|
||||||
|
# Copyright (c) 2013 Yury Selivanov
|
||||||
|
# License: Apache 2.0
|
||||||
|
##
|
||||||
|
|
||||||
|
|
||||||
|
import greentulip
|
||||||
|
import greentulip.socket as greensocket
|
||||||
|
|
||||||
|
import tulip
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
class SocketTests(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
tulip.set_event_loop_policy(greentulip.GreenEventLoopPolicy())
|
||||||
|
self.loop = tulip.new_event_loop()
|
||||||
|
tulip.set_event_loop(self.loop)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.loop.close()
|
||||||
|
tulip.set_event_loop_policy(None)
|
||||||
|
|
||||||
|
def test_socket_docs(self):
|
||||||
|
self.assertTrue('accept connections' in greensocket.socket.listen.__doc__)
|
||||||
|
self.assertTrue('Receive' in greensocket.socket.recv.__doc__)
|
||||||
|
|
||||||
|
def test_socket_setblocking(self):
|
||||||
|
sock = greensocket.socket()
|
||||||
|
self.assertEquals(sock.gettimeout(), 0)
|
||||||
|
with self.assertRaisesRegex(greensocket.error, 'does not support blocking mode'):
|
||||||
|
sock.setblocking(True)
|
||||||
|
|
||||||
|
def test_socket_echo(self):
|
||||||
|
import socket as std_socket
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
|
||||||
|
check = 0
|
||||||
|
ev = threading.Event()
|
||||||
|
|
||||||
|
def server(sock_factory):
|
||||||
|
socket = sock_factory()
|
||||||
|
socket.bind(('127.0.0.1', 0))
|
||||||
|
|
||||||
|
assert socket.fileno() is not None
|
||||||
|
|
||||||
|
nonlocal addr
|
||||||
|
addr = socket.getsockname()
|
||||||
|
socket.listen(1)
|
||||||
|
|
||||||
|
ev.set()
|
||||||
|
|
||||||
|
sock, client_addrs = socket.accept()
|
||||||
|
assert isinstance(sock, sock_factory)
|
||||||
|
|
||||||
|
data = b''
|
||||||
|
while not data.endswith(b'\r'):
|
||||||
|
data += sock.recv(1024)
|
||||||
|
|
||||||
|
sock.sendall(data)
|
||||||
|
|
||||||
|
ev.wait()
|
||||||
|
ev.clear()
|
||||||
|
|
||||||
|
sock.close()
|
||||||
|
socket.close()
|
||||||
|
|
||||||
|
def client(sock_factory):
|
||||||
|
ev.wait()
|
||||||
|
ev.clear()
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
assert addr
|
||||||
|
sock = sock_factory()
|
||||||
|
sock.connect(addr)
|
||||||
|
|
||||||
|
data = b'hello greenlets\r'
|
||||||
|
sock.sendall(data)
|
||||||
|
|
||||||
|
rep = b''
|
||||||
|
while not rep.endswith(b'\r'):
|
||||||
|
rep += sock.recv(1024)
|
||||||
|
|
||||||
|
self.assertEqual(data, rep)
|
||||||
|
ev.set()
|
||||||
|
|
||||||
|
nonlocal check
|
||||||
|
check += 1
|
||||||
|
|
||||||
|
sock.close()
|
||||||
|
|
||||||
|
addr = None
|
||||||
|
ev.clear()
|
||||||
|
thread = threading.Thread(target=client, args=(std_socket.socket,))
|
||||||
|
thread.setDaemon(True)
|
||||||
|
thread.start()
|
||||||
|
self.loop.run_until_complete(greentulip.task(server)(greensocket.socket))
|
||||||
|
thread.join(1)
|
||||||
|
self.assertEqual(check, 1)
|
||||||
|
|
||||||
|
addr = None
|
||||||
|
ev.clear()
|
||||||
|
thread = threading.Thread(target=server, args=(std_socket.socket,))
|
||||||
|
thread.setDaemon(True)
|
||||||
|
thread.start()
|
||||||
|
self.loop.run_until_complete(greentulip.task(client)(greensocket.socket))
|
||||||
|
thread.join(1)
|
||||||
|
self.assertEqual(check, 2)
|
Loading…
Reference in New Issue