Skip to content
Merged
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def readme():
return f.read()

setup(name='zeroless',
version='0.4.0',
version='0.5.0',
description='ZeroMQ for Pythonistas™',
long_description=readme(),
classifiers=[
Expand Down
61 changes: 60 additions & 1 deletion tests/test_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,84 @@ def test_port_under_range(self):
with pytest.raises(ValueError):
client.connect_local(port=1023)

with pytest.raises(ValueError):
client.disconnect_local(port=1023)

with pytest.raises(ValueError):
Server(port=1023)

def test_port_on_range(self):
client = Client()
client.connect_local(port=1024)
client.disconnect_local(port=1024)
client.connect_local(port=7000)
client.disconnect_local(port=7000)
client.connect_local(port=65535)
client.disconnect_local(port=65535)

def test_port_after_range(self):
client = Client()
with pytest.raises(ValueError):
client.connect_local(port=65536)

with pytest.raises(ValueError):
client.disconnect_local(port=65536)

with pytest.raises(ValueError):
Server(port=65536)

def test_connection_after_pattern_was_established(self):
client = Client()
listen_for_push = client.pull()

client.connect_local(port=1024)

with pytest.raises(ValueError):
client.connect_local(port=1024)

client.disconnect_local(port=1024)

with pytest.raises(ValueError):
client.disconnect_local(port=1024)

def test_there_was_no_connection_to_disconnect(self):
client = Client()
client.connect_local(port=1024)

with pytest.raises(ValueError):
client.disconnect_local(port=1025)

client.disconnect_local(port=1024)

with pytest.raises(ValueError):
client.disconnect_local(port=1024)

def test_connection_already_exist(self):
client = Client()
client.connect_local(port=1024)

with pytest.raises(ValueError):
client.connect_local(port=1024)

client.disconnect_local(port=1024)
client.connect_local(port=1024)

def test_disconnect_all(self):
client = Client()
client.connect_local(port=1024)
client.connect_local(port=1025)
client.connect_local(port=1026)
client.connect_local(port=1027)

client.disconnect_all()

client.connect_local(port=1024)
client.connect_local(port=1025)
client.connect_local(port=1026)
client.connect_local(port=1027)

def test_port_already_used(self):
server1 = Server(port=65000).pull()
listen_for_push = Server(port=65000).pull()

with pytest.raises(ValueError):
Server(port=65000).pull()
Expand Down
78 changes: 69 additions & 9 deletions zeroless/zeroless.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import logging

from time import sleep
from copy import deepcopy
from warnings import warn
from functools import partial

Expand All @@ -34,6 +35,26 @@ def _connect_zmq_sock(sock, ip, port):
log.info('Connecting to {0} on port {1}'.format(ip, port))
sock.connect('tcp://' + ip + ':' + str(port))

def _disconnect_zmq_sock(sock, ip, port):
log.info('Disconnecting from {0} on port {1}'.format(ip, port))

try:
sock.disconnect('tcp://' + ip + ':' + str(port))
except zmq.ZMQError:
error = 'There was no connection to {0} on port {1}'.format(ip, port)
log.exception(error)
raise ValueError(error)

def _bind_zmq_sock(sock, port):
log.info('Binding on port {0}'.format(port))

try:
sock.bind('tcp://*:' + str(port))
except zmq.ZMQError:
error = 'Port {0} is already in use'.format(port)
log.exception(error)
raise ValueError(error)

def _recv(sock):
while True:
frames = sock.recv_multipart()
Expand Down Expand Up @@ -222,6 +243,12 @@ def connect(self, ip, port):
_check_valid_port_range(port)

address = (ip, port)

if address in self._addresses:
error = 'Already connected to {0} on port {1}'.format(ip, port)
log.exception(error)
raise ValueError(error)

self._addresses.append(address)

if self._is_ready:
Expand All @@ -232,13 +259,53 @@ def connect(self, ip, port):

def connect_local(self, port):
"""
Connects to a server from localhost at the specified port.
Connects to a server in localhost at the specified port.

:param port: port number from 1024 up to 65535
:type port: int
"""
self.connect('127.0.0.1', port)

def disconnect(self, ip, port):
"""
Disconnects from a server at the specified ip and port.

:param ip: an IP address
:type ip: str or unicode
:param port: port number from 1024 up to 65535
:type port: int
"""
_check_valid_port_range(port)
address = (ip, port)

try:
self._addresses.remove(address)
except ValueError:
error = 'There was no connection to {0} on port {1}'.format(ip, port)
log.exception(error)
raise ValueError(error)

if self._is_ready:
_disconnect_zmq_sock(self._sock, ip, port)

def disconnect_local(self, port):
"""
Disconnects from a server in localhost at the specified port.

:param port: port number from 1024 up to 65535
:type port: int
"""
self.disconnect('127.0.0.1', port)

def disconnect_all(self):
"""
Disconnects from all connected servers.
"""
addresses = deepcopy(self._addresses)

for ip, port in addresses:
self.disconnect(ip, port)

class Server(Sock):
"""
A server that clients can connect to.
Expand All @@ -263,11 +330,4 @@ def _setup(self, sock):
warning += 'is not an option'
warn(warning)

log.info('Binding on port {0}'.format(self._port))

try:
sock.bind('tcp://*:' + str(self._port))
except zmq.ZMQError:
error = 'Port {0} is already in use'.format(self._port)
log.exception(error)
raise ValueError(error)
_bind_zmq_sock(sock, self._port)