pyre-gevent 0.2.3.dev3__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. {pyre-gevent-0.2.3.dev3/pyre_gevent.egg-info → pyre-gevent-0.3.0}/PKG-INFO +5 -6
  2. pyre-gevent-0.3.0/pyre_gevent/__init__.py +8 -0
  3. {pyre-gevent-0.2.3.dev3 → pyre-gevent-0.3.0}/pyre_gevent/pyre.py +1 -1
  4. {pyre-gevent-0.2.3.dev3 → pyre-gevent-0.3.0}/pyre_gevent/pyre_event.py +2 -4
  5. {pyre-gevent-0.2.3.dev3 → pyre-gevent-0.3.0}/pyre_gevent/pyre_node.py +4 -4
  6. {pyre-gevent-0.2.3.dev3 → pyre-gevent-0.3.0}/pyre_gevent/zbeacon.py +149 -79
  7. {pyre-gevent-0.2.3.dev3 → pyre-gevent-0.3.0}/pyre_gevent/zre_msg.py +3 -5
  8. {pyre-gevent-0.2.3.dev3 → pyre-gevent-0.3.0/pyre_gevent.egg-info}/PKG-INFO +5 -6
  9. {pyre-gevent-0.2.3.dev3 → pyre-gevent-0.3.0}/pyre_gevent.egg-info/SOURCES.txt +6 -1
  10. {pyre-gevent-0.2.3.dev3 → pyre-gevent-0.3.0}/pyre_gevent.egg-info/requires.txt +2 -2
  11. {pyre-gevent-0.2.3.dev3 → pyre-gevent-0.3.0}/setup.py +2 -2
  12. pyre-gevent-0.3.0/tests/test_network_disconnect.py +81 -0
  13. pyre-gevent-0.3.0/tests/test_pyre.py +178 -0
  14. pyre-gevent-0.3.0/tests/test_zactor.py +38 -0
  15. pyre-gevent-0.3.0/tests/test_zbeacon.py +82 -0
  16. pyre-gevent-0.3.0/tests/test_zhelper.py +32 -0
  17. pyre-gevent-0.2.3.dev3/pyre_gevent/__init__.py +0 -8
  18. {pyre-gevent-0.2.3.dev3 → pyre-gevent-0.3.0}/AUTHORS +0 -0
  19. {pyre-gevent-0.2.3.dev3 → pyre-gevent-0.3.0}/LICENSE +0 -0
  20. {pyre-gevent-0.2.3.dev3 → pyre-gevent-0.3.0}/MANIFEST.in +0 -0
  21. {pyre-gevent-0.2.3.dev3 → pyre-gevent-0.3.0}/README.md +0 -0
  22. {pyre-gevent-0.2.3.dev3 → pyre-gevent-0.3.0}/pyre_gevent/pyre_group.py +0 -0
  23. {pyre-gevent-0.2.3.dev3 → pyre-gevent-0.3.0}/pyre_gevent/pyre_peer.py +0 -0
  24. {pyre-gevent-0.2.3.dev3 → pyre-gevent-0.3.0}/pyre_gevent/zactor.py +0 -0
  25. {pyre-gevent-0.2.3.dev3 → pyre-gevent-0.3.0}/pyre_gevent/zhelper.py +0 -0
  26. {pyre-gevent-0.2.3.dev3 → pyre-gevent-0.3.0}/pyre_gevent/zsocket.py +0 -0
  27. {pyre-gevent-0.2.3.dev3 → pyre-gevent-0.3.0}/pyre_gevent.egg-info/dependency_links.txt +0 -0
  28. {pyre-gevent-0.2.3.dev3 → pyre-gevent-0.3.0}/pyre_gevent.egg-info/top_level.txt +0 -0
  29. {pyre-gevent-0.2.3.dev3 → pyre-gevent-0.3.0}/setup.cfg +0 -0
@@ -1,16 +1,15 @@
1
- Metadata-Version: 1.2
1
+ Metadata-Version: 2.1
2
2
  Name: pyre-gevent
3
- Version: 0.2.3.dev3
3
+ Version: 0.3.0
4
4
  Summary: Python ZRE implementation (green version)
5
5
  Home-page: http://www.github.com/tangb/pyre-gevent/
6
6
  Author: Arnaud Loonstra
7
7
  Author-email: arnaud@sphaero.org
8
8
  Maintainer: Tanguy Bonneau
9
9
  Maintainer-email: tanguy.bonneau@gmail.com
10
- License: UNKNOWN
11
- Description: UNKNOWN
12
- Platform: UNKNOWN
13
10
  Requires: pyzmq
14
11
  Requires: ipaddress
15
12
  Requires: netaddr
16
- Requires: netifaces
13
+ Requires: netifaces2
14
+ License-File: LICENSE
15
+ License-File: AUTHORS
@@ -0,0 +1,8 @@
1
+ import re
2
+
3
+ __all__ = ["pyre", "zbeacon", "zhelper"]
4
+ __version__ = "0.3.0"
5
+ __version_info__ = tuple(int(v) for v in re.sub("[a-zA-Z]", "", __version__).split("."))
6
+
7
+ from .pyre import Pyre
8
+ from .pyre_event import PyreEvent
@@ -39,7 +39,7 @@ class Pyre(object):
39
39
  """
40
40
  super(Pyre, self).__init__(*args, **kwargs)
41
41
  ctx = kwargs.get('ctx')
42
- if ctx == None:
42
+ if ctx is None:
43
43
  ctx = zmq.Context()
44
44
  self._ctx = ctx
45
45
  self._uuid = None
@@ -27,7 +27,7 @@ class PyreEvent(object):
27
27
  if self.type == "ENTER":
28
28
  self.headers = json.loads(incoming.pop(0).decode('utf-8'))
29
29
  self.peer_addr = incoming.pop(0).decode('utf-8')
30
- elif self.type == "JOIN" or self.type == "LEAVE":
30
+ elif self.type in ["JOIN", "LEAVE"]:
31
31
  self.group = incoming.pop(0).decode('utf-8')
32
32
  elif self.type == "WHISPER":
33
33
  self.msg = incoming
@@ -44,9 +44,7 @@ class PyreEvent(object):
44
44
  Returns:
45
45
  str: Header value
46
46
  """
47
- if self.headers and name in self.headers:
48
- return self.headers[name]
49
- return None
47
+ return self.headers[name] if self.headers and name in self.headers else None
50
48
 
51
49
  @property
52
50
  def peer_uuid(self):
@@ -301,7 +301,8 @@ class PyreNode(object):
301
301
  logger.warning("Unkown Node API command: {0}".format(command))
302
302
 
303
303
  def purge_peer(self, peer, endpoint):
304
- if (peer.get_endpoint() == endpoint):
304
+ if peer.get_endpoint() == endpoint:
305
+ self.remove_peer(peer)
305
306
  peer.disconnect()
306
307
  logger.debug("Purge peer: {0}{1}".format(peer,endpoint))
307
308
 
@@ -410,7 +411,7 @@ class PyreNode(object):
410
411
 
411
412
  # Ignore command if peer isn't ready
412
413
  if not peer or not peer.get_ready():
413
- logger.warning("Peer {0} isn't ready".format(peer))
414
+ logger.debug("Peer {0} isn't ready".format(peer))
414
415
  return
415
416
 
416
417
  if peer.messages_lost(zmsg):
@@ -530,8 +531,7 @@ class PyreNode(object):
530
531
  reap_at = time.time() + REAP_INTERVAL
531
532
  while not self._terminated:
532
533
  timeout = reap_at - time.time()
533
- if timeout < 0:
534
- timeout = 0
534
+ timeout = max(timeout, 0)
535
535
  items = dict(self.poller.poll(timeout * 1000))
536
536
 
537
537
  if self._pipe in items and items[self._pipe] == zmq.POLLIN:
@@ -1,4 +1,3 @@
1
-
2
1
  # ======================================================================
3
2
  # zbeacon - LAN discovery and presence
4
3
  #
@@ -38,26 +37,30 @@ import netaddr
38
37
  logger = logging.getLogger(__name__)
39
38
 
40
39
  INTERVAL_DFLT = 1.0
41
- BEACON_MAX = 255 # Max size of beacon data
42
- MULTICAST_GRP = '225.25.25.25'
40
+ BEACON_MAX = 255 # Max size of beacon data
41
+ MULTICAST_GRP = "225.25.25.25"
42
+ ENETDOWN = 50 # socket error, network is down
43
+ ENETUNREACH = 51 # socket error, network unreachable
43
44
 
44
45
 
45
46
  class ZBeacon(object):
46
47
 
47
48
  def __init__(self, ctx, pipe, *args, **kwargs):
48
- self.ctx = ctx # ZMQ context
49
- self.pipe = pipe # Actor command pipe
50
- self.udpsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
51
- # UDP socket for send/recv
52
- self.port_nbr = 0 # UDP port number we work on
53
- self.interval = INTERVAL_DFLT # Beacon broadcast interval
54
- self.ping_at = 0 # Next broadcast time
55
- self.transmit = None # Beacon transmit data
56
- self.filter = b"" # Beacon filter data
57
-
58
- self.terminated = False # Did caller ask us to quit?
59
- self.verbose = False # Verbose logging enabled?
60
- self.hostname = "" # Saved host name
49
+ self.ctx = ctx # ZMQ context
50
+ self.pipe = pipe # Actor command pipe
51
+ self.udpsock = socket.socket(
52
+ socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP
53
+ )
54
+ # UDP socket for send/recv
55
+ self.port_nbr = 0 # UDP port number we work on
56
+ self.interval = INTERVAL_DFLT # Beacon broadcast interval
57
+ self.ping_at = 0 # Next broadcast time
58
+ self.transmit = None # Beacon transmit data
59
+ self.filter = b"" # Beacon filter data
60
+
61
+ self.terminated = False # Did caller ask us to quit?
62
+ self.verbose = False # Verbose logging enabled?
63
+ self.hostname = "" # Saved host name
61
64
 
62
65
  self.address = None
63
66
  self.mac = None
@@ -71,23 +74,25 @@ class ZBeacon(object):
71
74
  self.udpsock.close()
72
75
 
73
76
  def prepare_udp(self, interface_name=None):
74
- self._prepare_socket(interface_name)
77
+ try:
78
+ self._prepare_socket(interface_name)
79
+ except ValueError:
80
+ logger.exception("Error preparing socket:")
81
+ return
75
82
  try:
76
83
  self.udpsock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
77
84
  self.udpsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
78
85
 
79
86
  # On some platforms we have to ask to reuse the port
80
87
  try:
81
- self.udpsock.setsockopt(socket.SOL_SOCKET,
82
- socket.SO_REUSEPORT, 1)
88
+ self.udpsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
83
89
 
84
90
  except AttributeError:
85
91
  pass
86
92
 
87
93
  if self.broadcast_address.is_multicast:
88
94
  # TTL
89
- self.udpsock.setsockopt(socket.IPPROTO_IP,
90
- socket.IP_MULTICAST_TTL, 2)
95
+ self.udpsock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
91
96
 
92
97
  # TODO: This should only be used if we do not have inproc method!
93
98
  self.udpsock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, 1)
@@ -115,48 +120,63 @@ class ZBeacon(object):
115
120
  self.udpsock.bind(("", self.port_nbr))
116
121
 
117
122
  group = socket.inet_aton("{0}".format(self.broadcast_address))
118
- mreq = struct.pack('4sl', group, socket.INADDR_ANY)
123
+ mreq = struct.pack("4sl", group, socket.INADDR_ANY)
119
124
 
120
- self.udpsock.setsockopt(socket.SOL_IP,
121
- socket.IP_ADD_MEMBERSHIP, mreq)
125
+ self.udpsock.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, mreq)
122
126
 
123
127
  else:
124
128
  # Platform specifics
125
129
  if platform.startswith("linux"):
126
130
  # on linux we bind to the broadcast address and send to
127
131
  # the broadcast address
128
- self.udpsock.bind((str(self.broadcast_address),
129
- self.port_nbr))
132
+ self.udpsock.bind((str(self.broadcast_address), self.port_nbr))
130
133
  else:
131
134
  self.udpsock.bind(("", self.port_nbr))
132
135
 
133
- logger.debug("Set up a broadcast beacon to {0}:{1}".format(self.broadcast_address, self.port_nbr))
136
+ logger.debug(
137
+ "Set up a broadcast beacon to {0}:{1}".format(
138
+ self.broadcast_address, self.port_nbr
139
+ )
140
+ )
134
141
  except socket.error:
135
- logger.exception("Initializing of {0} raised an exception".format(self.__class__.__name__))
142
+ logger.exception(
143
+ "Initializing of {0} raised an exception".format(
144
+ self.__class__.__name__
145
+ )
146
+ )
136
147
 
137
148
  def _prepare_socket(self, interface_name=None):
138
149
  netinf = zhelper.get_ifaddrs()
139
150
  logger.debug("Available interfaces: {0}".format(netinf))
140
151
 
141
- #get default gateway interface
152
+ # get default gateway interface
142
153
  default_interface_names = []
143
154
  netifaces_default_interface_names = []
144
155
  gateways = netifaces.gateways()
145
156
  logger.debug("Gateways: {0}".format(gateways))
146
157
  if netifaces.AF_INET in gateways:
147
158
  for address, interface, is_default in gateways[netifaces.AF_INET]:
148
- #fix for windows (netifaces.gateway() returns adapter name instead of interface name)
159
+ # fix for windows (netifaces.gateway() returns adapter name instead of interface name)
149
160
  if platform.startswith("win"):
150
161
  netifaces_default_interface_names.append(interface)
151
162
  for iface in netinf:
152
163
  for name, data in iface.items():
153
- if netifaces.AF_INET in data and data[netifaces.AF_INET]['adapter']==interface:
164
+ if (
165
+ netifaces.AF_INET in data
166
+ and data[netifaces.AF_INET]["adapter"] == interface
167
+ ):
154
168
  default_interface_names.append(name)
155
169
  else:
156
170
  default_interface_names.append(interface)
157
-
158
- logger.debug('Default interface names "{0}"'.format(list(default_interface_names)))
159
- logger.debug('Netifaces default interface names "{0}"'.format(list(netifaces_default_interface_names)))
171
+
172
+ logger.debug(
173
+ "Default interface names '{0}'".format(list(default_interface_names))
174
+ )
175
+ logger.debug(
176
+ "Netifaces default interface names '{0}'".format(
177
+ list(netifaces_default_interface_names)
178
+ )
179
+ )
160
180
 
161
181
  for iface in netinf:
162
182
  # Loop over the interfaces and their settings to try to find the broadcast address.
@@ -166,39 +186,53 @@ class ZBeacon(object):
166
186
  if interface_name and interface_name != name:
167
187
  continue
168
188
 
169
- #Interface of default route found, skip other ones
170
- #This trick allows to skip invalid interfaces like docker ones.
171
- if len(default_interface_names)>0 and name not in default_interface_names:
172
- logger.debug('Interface "{0}" is not interface of default route'.format(name))
189
+ # Interface of default route found, skip other ones
190
+ # This trick allows to skip invalid interfaces like docker ones.
191
+ if (
192
+ len(default_interface_names) > 0
193
+ and name not in default_interface_names
194
+ ):
195
+ logger.debug(
196
+ "Interface '{0}' is not interface of default route".format(name)
197
+ )
173
198
  continue
174
199
 
175
- logger.debug('Checking out interface "{0}".'.format(name))
200
+ logger.debug("Checking out interface '{0}'.".format(name))
176
201
 
177
- #Get addr and netmask infos
202
+ # Get addr and netmask infos
178
203
  data_2 = data.get(netifaces.AF_INET)
179
204
  if not data_2:
180
- logger.debug('No data_2 found for interface "{0}".'.format(name))
205
+ logger.debug("No data_2 found for interface '{0}'.".format(name))
181
206
  continue
182
207
 
183
- #get mac address infos
184
- data_17 = data.get(netifaces.AF_LINK)
208
+ # get mac address infos
209
+ data_17 = data.get(netifaces.AF_PACKET)
185
210
  if not data_17 and platform.startswith("win"):
186
- #last chance to get mac address on windows platform
187
- for netifaces_default_interface_name in netifaces_default_interface_names:
188
- ifaddresses = netifaces.ifaddresses(netifaces_default_interface_name)
189
- if netifaces.AF_LINK in ifaddresses and len(ifaddresses[netifaces.AF_LINK])>0:
190
- data_17 = ifaddresses[netifaces.AF_LINK][0]
211
+ # last chance to get mac address on windows platform
212
+ for (
213
+ netifaces_default_interface_name
214
+ ) in netifaces_default_interface_names:
215
+ ifaddresses = netifaces.ifaddresses(
216
+ netifaces_default_interface_name
217
+ )
218
+ if (
219
+ netifaces.AF_PACKET in ifaddresses
220
+ and len(ifaddresses[netifaces.AF_PACKET]) > 0
221
+ ):
222
+ data_17 = ifaddresses[netifaces.AF_PACKET][0]
191
223
  break
192
224
  if not data_17:
193
- logger.debug('No data_17 found for interface "{0}".'.format(name))
225
+ logger.debug("No data_17 found for interface '{0}'.".format(name))
194
226
  continue
195
227
 
196
228
  address_str = data_2.get("addr")
197
229
  netmask_str = data_2.get("netmask")
198
- mac_str = data_17.get("addr")
230
+ mac_str = data_17.get("addr")
199
231
 
200
232
  if not address_str or not netmask_str:
201
- logger.debug('Address or netmask not found for interface "{0}".'.format(name))
233
+ logger.debug(
234
+ "Address or netmask not found for interface '{0}'.".format(name)
235
+ )
202
236
  continue
203
237
 
204
238
  if isinstance(address_str, bytes):
@@ -210,10 +244,24 @@ class ZBeacon(object):
210
244
  if isinstance(mac_str, bytes):
211
245
  mac_str = mac_str.decode("utf8")
212
246
 
213
- #keep only private interface
247
+ # keep only private interface
214
248
  ip_address = netaddr.IPAddress(address_str)
215
- if ip_address and not ip_address.is_private():
216
- logger.debug('Interface "{0}" refers to public ip address, drop it.'.format(name))
249
+ # handle netaddr breaking changes
250
+ if netaddr.__version__.startswith("0."):
251
+ # is_private does not exist anymore on new IpAddress lib version
252
+ # pylint: disable=no-member
253
+ ip_is_private = ip_address.is_private()
254
+ else:
255
+ ip_is_private = (
256
+ ip_address.is_ipv4_private_use()
257
+ or ip_address.is_ipv6_unique_local()
258
+ )
259
+ if ip_address and not ip_is_private():
260
+ logger.debug(
261
+ "Interface '{0}' refers to public ip address, drop it.".format(
262
+ name
263
+ )
264
+ )
217
265
  continue
218
266
 
219
267
  interface_string = "{0}/{1}".format(address_str, netmask_str)
@@ -221,11 +269,11 @@ class ZBeacon(object):
221
269
  interface = ipaddress.ip_interface(u(interface_string))
222
270
 
223
271
  if interface.is_loopback:
224
- logger.debug('Interface "{0}" is a loopback device.'.format(name))
272
+ logger.debug("Interface {0} is a loopback device.".format(name))
225
273
  continue
226
274
 
227
275
  if interface.is_link_local:
228
- logger.debug('Interface "{0}" is a link-local device.'.format(name))
276
+ logger.debug("Interface {0} is a link-local device.".format(name))
229
277
  continue
230
278
 
231
279
  self.address = interface.ip
@@ -236,17 +284,17 @@ class ZBeacon(object):
236
284
 
237
285
  if self.address:
238
286
  break
239
-
287
+
240
288
  if self.address:
241
289
  break
242
290
 
243
291
  logger.debug("Finished scanning interfaces.")
244
292
 
245
293
  if not self.address:
246
- self.network_address = ipaddress.IPv4Address(u('127.0.0.1'))
294
+ self.network_address = ipaddress.IPv4Address(u("127.0.0.1"))
247
295
  self.broadcast_address = ipaddress.IPv4Address(u(MULTICAST_GRP))
248
- self.interface_name = 'loopback'
249
- self.address = u('127.0.0.1')
296
+ self.interface_name = "loopback"
297
+ self.address = u("127.0.0.1")
250
298
  self.mac = None
251
299
 
252
300
  logger.debug("Address: {0}".format(self.address))
@@ -263,9 +311,9 @@ class ZBeacon(object):
263
311
  def handle_pipe(self):
264
312
  # Get just the commands off the pipe
265
313
  request = self.pipe.recv_multipart()
266
- command = request.pop(0).decode('UTF-8')
314
+ command = request.pop(0).decode("UTF-8")
267
315
  if not command:
268
- return -1 # Interrupted
316
+ return -1 # Interrupted
269
317
 
270
318
  if self.verbose:
271
319
  logger.debug("zbeacon: API command={0}".format(command))
@@ -276,10 +324,13 @@ class ZBeacon(object):
276
324
  # unpacking code from https://bit.ly/2qN3ZUz
277
325
  interface_data = request.pop(0)
278
326
 
279
- (interface_name_len,), interface = struct.unpack('I', interface_data[:4]), interface_data[4:]
280
- interface = interface.decode('UTF-8')
327
+ (interface_name_len,), interface = (
328
+ struct.unpack("I", interface_data[:4]),
329
+ interface_data[4:],
330
+ )
331
+ interface = interface.decode("UTF-8")
281
332
 
282
- port = struct.unpack('I', request.pop(0))[0]
333
+ port = struct.unpack("I", request.pop(0))[0]
283
334
  self.configure(interface, port)
284
335
  elif command == "PUBLISH":
285
336
  self.transmit = request.pop(0)
@@ -312,8 +363,8 @@ class ZBeacon(object):
312
363
  is_valid = False
313
364
  if self.filter is not None:
314
365
  if len(self.filter) <= len(frame):
315
- match_data = frame[:len(self.filter)]
316
- if (match_data == self.filter):
366
+ match_data = frame[: len(self.filter)]
367
+ if match_data == self.filter:
317
368
  is_valid = True
318
369
 
319
370
  # If valid, discard our own broadcasts, which UDP echoes to us
@@ -328,13 +379,27 @@ class ZBeacon(object):
328
379
 
329
380
  def send_beacon(self):
330
381
  try:
331
- self.udpsock.sendto(self.transmit, (str(self.broadcast_address),
332
- self.port_nbr))
333
- except (OSError, socket.error):
334
- logger.debug("Network seems gone, restarting zbeacon udp connection")
335
- #restart socket
336
- time.sleep(1.0)
337
- self.prepare_udp()
382
+ self.udpsock.sendto(
383
+ self.transmit, (str(self.broadcast_address), self.port_nbr)
384
+ )
385
+
386
+ except OSError as e:
387
+
388
+ # network down, just wait, it could come back up again.
389
+ # socket call errors 50 and 51 relate to the network being
390
+ # down or unreachable, the recommended action to take is to
391
+ # try again so we don't terminate in these cases.
392
+ if e.errno in [ENETDOWN, ENETUNREACH]:
393
+ pass
394
+
395
+ # all other cases, we'll terminate
396
+ else:
397
+ logger.debug("Network seems gone, exiting zbeacon")
398
+ self.terminated = True
399
+
400
+ except socket.error:
401
+ logger.debug("Network seems gone, exiting zbeacon")
402
+ self.terminated = True
338
403
 
339
404
  def run(self):
340
405
  # Signal actor successfully initialized
@@ -354,7 +419,10 @@ class ZBeacon(object):
354
419
  items = dict(self.poller.poll(timeout * 1000))
355
420
  if self.pipe in items and items[self.pipe] == zmq.POLLIN:
356
421
  self.handle_pipe()
357
- if self.udpsock.fileno() in items and items[self.udpsock.fileno()] == zmq.POLLIN:
422
+ if (
423
+ self.udpsock.fileno() in items
424
+ and items[self.udpsock.fileno()] == zmq.POLLIN
425
+ ):
358
426
  self.handle_udp()
359
427
 
360
428
  if self.transmit and time.time() >= self.ping_at:
@@ -362,18 +430,20 @@ class ZBeacon(object):
362
430
  self.ping_at = time.time() + self.interval
363
431
 
364
432
 
365
- if __name__ == '__main__':
433
+ if __name__ == "__main__":
366
434
  import zmq
367
435
  import struct
368
436
  import time
437
+
369
438
  speaker = ZActor(zmq.Context(), ZBeacon)
370
439
  speaker.send_unicode("VERBOSE")
371
440
  speaker.send_unicode("CONFIGURE", zmq.SNDMORE)
372
441
  speaker.send(struct.pack("I", 9999))
373
442
  speaker.send_unicode("PUBLISH", zmq.SNDMORE)
374
443
  import uuid
375
- transmit = struct.pack('cccb16sH', b'Z', b'R', b'E',
376
- 1, uuid.uuid4().bytes,
377
- socket.htons(1300))
444
+
445
+ transmit = struct.pack(
446
+ "cccb16sH", b"Z", b"R", b"E", 1, uuid.uuid4().bytes, socket.htons(1300)
447
+ )
378
448
  speaker.send(transmit)
379
449
  speaker.destroy()
@@ -68,7 +68,7 @@ class ZreMsg(object):
68
68
  def recv(self, input_socket):
69
69
  # If we're reading from a ROUTER socket, get address
70
70
  frames = input_socket.recv_multipart()
71
- if input_socket.socket_type == zmq.ROUTER:
71
+ if input_socket.type == zmq.ROUTER:
72
72
  self.address = frames.pop(0)
73
73
  # we drop the first byte: TODO ref!
74
74
  try:
@@ -181,7 +181,7 @@ class ZreMsg(object):
181
181
  logger.debug("Message type {0} unknown".format(self.id))
182
182
 
183
183
  # If we're sending to a ROUTER, we send the address first
184
- if output_socket.socket_type == zmq.ROUTER:
184
+ if output_socket.type == zmq.ROUTER:
185
185
  output_socket.send(self.address.bytes, zmq.SNDMORE)
186
186
  # Now send the data frame
187
187
  if (self.content):
@@ -414,9 +414,7 @@ class ZreMsg(object):
414
414
  group_len = self._get_number4()
415
415
  #print("needle is at: %i"% self._needle )
416
416
  #print("grouplen: ", group_len)
417
- self.groups = []
418
- for x in range(group_len):
419
- self.groups.append(self._get_long_string())
417
+ self.groups = [self._get_long_string() for _ in range(group_len)]
420
418
  #print(self.groups)
421
419
  #print("post_group: needle is at: %i"% self._needle )
422
420
  self.status = self._get_number1()
@@ -1,16 +1,15 @@
1
- Metadata-Version: 1.2
1
+ Metadata-Version: 2.1
2
2
  Name: pyre-gevent
3
- Version: 0.2.3.dev3
3
+ Version: 0.3.0
4
4
  Summary: Python ZRE implementation (green version)
5
5
  Home-page: http://www.github.com/tangb/pyre-gevent/
6
6
  Author: Arnaud Loonstra
7
7
  Author-email: arnaud@sphaero.org
8
8
  Maintainer: Tanguy Bonneau
9
9
  Maintainer-email: tanguy.bonneau@gmail.com
10
- License: UNKNOWN
11
- Description: UNKNOWN
12
- Platform: UNKNOWN
13
10
  Requires: pyzmq
14
11
  Requires: ipaddress
15
12
  Requires: netaddr
16
- Requires: netifaces
13
+ Requires: netifaces2
14
+ License-File: LICENSE
15
+ License-File: AUTHORS
@@ -19,4 +19,9 @@ pyre_gevent.egg-info/PKG-INFO
19
19
  pyre_gevent.egg-info/SOURCES.txt
20
20
  pyre_gevent.egg-info/dependency_links.txt
21
21
  pyre_gevent.egg-info/requires.txt
22
- pyre_gevent.egg-info/top_level.txt
22
+ pyre_gevent.egg-info/top_level.txt
23
+ tests/test_network_disconnect.py
24
+ tests/test_pyre.py
25
+ tests/test_zactor.py
26
+ tests/test_zbeacon.py
27
+ tests/test_zhelper.py
@@ -1,4 +1,4 @@
1
- pyzmq
2
1
  ipaddress
3
2
  netaddr
4
- netifaces
3
+ netifaces2
4
+ pyzmq
@@ -37,6 +37,6 @@ setup(
37
37
  url='http://www.github.com/tangb/pyre-gevent/',
38
38
  packages=['pyre_gevent'],
39
39
  include_package_data=True,
40
- requires=['pyzmq', 'ipaddress', 'netaddr', 'netifaces'],
41
- install_requires=['pyzmq', 'ipaddress', 'netaddr', 'netifaces'],
40
+ requires=['pyzmq', 'ipaddress', 'netaddr', 'netifaces2'],
41
+ install_requires=['pyzmq', 'ipaddress', 'netaddr', 'netifaces2'],
42
42
  )
@@ -0,0 +1,81 @@
1
+ import os
2
+ import time
3
+ import logging
4
+ import subprocess
5
+ import pyre
6
+
7
+
8
+ # The test needs the device name of the active network interface.
9
+ # The device name can be found under `DEVICE` column after running `nmcli c`.
10
+ # The device name should be set to the variable bellow, or as the `DEVICE` env variable.
11
+ NMCLI_DEVICE: str = None
12
+
13
+ NMCLI_LINETERMINATOR = "\n"
14
+ NMCLI_DELIMITER = " "
15
+
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ def _network_device() -> str:
21
+ return NMCLI_DEVICE or os.environ.get("DEVICE", None)
22
+
23
+
24
+ def _network_device_is_enabled(device: str) -> bool:
25
+ result = subprocess.run(["nmcli", "c"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
26
+ assert result.returncode == 0, result.stderr.decode("ascii")
27
+ output = result.stdout.decode("ascii")
28
+ rows = [[val.strip() for val in row.split(NMCLI_DELIMITER) if val.strip()] for row in output.split(NMCLI_LINETERMINATOR) if row.strip()]
29
+ head, rows = rows[0], rows[1:]
30
+ devices = [dict(zip(head, row)) for row in rows]
31
+ return any(d["DEVICE"] == device for d in devices)
32
+
33
+
34
+ def _network_device_enable(device: str, enable: bool):
35
+ if _network_device_is_enabled(device) == enable:
36
+ return
37
+ cmd = 'connect' if enable else 'disconnect'
38
+ result = subprocess.run(["nmcli", "d", cmd, f"{device}"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
39
+ assert result.returncode == 0, result.stderr.decode("ascii")
40
+ time.sleep(3)
41
+ while _network_device_is_enabled(device) != enable:
42
+ pass
43
+ logger.debug("Device \"{device}\" was {cmd}ed")
44
+
45
+
46
+ def test_network_disconnect(group="best-group"):
47
+
48
+ device = _network_device()
49
+
50
+ if device is None:
51
+ logger.warn("Skipping test. Please specify an active network interface to use for testing.")
52
+ return
53
+
54
+ _network_device_enable(device, enable=True)
55
+
56
+ try:
57
+ pyre_node = pyre.Pyre(name="foo")
58
+ pyre_node.join(group)
59
+ pyre_node.start()
60
+ logger.debug("Pyre started")
61
+
62
+ _network_device_enable(device, enable=False)
63
+
64
+ pyre_node.leave(group)
65
+ logger.debug("Pyre left group")
66
+
67
+ pyre_node.stop()
68
+ logger.debug("Pyre stopped")
69
+
70
+ del pyre_node
71
+ logger.debug("Pyre destroyed")
72
+
73
+ except KeyboardInterrupt:
74
+ pass
75
+
76
+ finally:
77
+ _network_device_enable(device, enable=True)
78
+
79
+
80
+ if __name__ == '__main__':
81
+ test_network_disconnect()
@@ -0,0 +1,178 @@
1
+ import unittest
2
+ import pyre
3
+ import zmq
4
+ import time
5
+ import logging
6
+ import sys
7
+
8
+
9
+ if sys.version.startswith('3'):
10
+ unicode = str
11
+
12
+
13
+ class PyreTest(unittest.TestCase):
14
+ def setUp(self, *args, **kwargs):
15
+ ctx = zmq.Context()
16
+ self.node1 = pyre.Pyre("node1", ctx=ctx)
17
+ self.node1.set_header("X-TEST", "1")
18
+ self.node2 = pyre.Pyre("node2", ctx=ctx)
19
+ self.node2.set_header("X-TEST", "1")
20
+ self.node1.start()
21
+ self.node2.start()
22
+ # give time for nodes to exchange
23
+ time.sleep(1)
24
+ # end setUp
25
+
26
+ def tearDown(self):
27
+ self.node1.stop()
28
+ self.node2.stop()
29
+ # end tearDown
30
+
31
+ def test_get_name(self):
32
+ self.assertEqual("node1", self.node1.name())
33
+ self.assertEqual("node2", self.node2.name())
34
+ # end test_get_name
35
+
36
+ def test_get_peers(self):
37
+ id1 = self.node1.uuid()
38
+ peers = self.node2.peers()
39
+
40
+ self.assertIsInstance(peers, list)
41
+ self.assertIn(id1, peers)
42
+ # end test_get_peers
43
+
44
+ def test_get_peers_by_group(self):
45
+ id1 = self.node1.uuid()
46
+ self.node1.join("TEST")
47
+ # pyre works asynchronous so give some time to let changes disperse
48
+ time.sleep(0.5)
49
+
50
+ peers = self.node2.peers_by_group("TEST")
51
+ self.node1.leave("TEST")
52
+
53
+ self.assertIsInstance(peers, list)
54
+ self.assertIn(id1, peers)
55
+ # end test_get_peers
56
+
57
+ def test_get_peer_address(self):
58
+ id1 = self.node1.uuid()
59
+ id2 = self.node2.uuid()
60
+
61
+ self.assertIsInstance(self.node1.peer_address(id2), unicode)
62
+ self.assertIsInstance(self.node2.peer_address(id1), unicode)
63
+ # end test_get_peer_address
64
+
65
+ def test_peer_header_value(self):
66
+ id1 = self.node1.uuid()
67
+ id2 = self.node2.uuid()
68
+
69
+ self.assertEqual("1", self.node1.peer_header_value(id2, "X-TEST"))
70
+ self.assertEqual("1", self.node2.peer_header_value(id1, "X-TEST"))
71
+ # end test_get_peer_header_value
72
+
73
+ def test_get_own_groups(self):
74
+ self.node1.join("TEST")
75
+ self.node2.join("TEST")
76
+ # pyre works asynchronous so give some time to let changes disperse
77
+ time.sleep(0.5)
78
+
79
+ self.assertIn("TEST", self.node1.own_groups())
80
+ self.assertIn("TEST", self.node2.own_groups())
81
+ # end test_get_own_groups
82
+
83
+ def test_join_leave_msg(self):
84
+ msg = self.node1.recv()
85
+ self.assertEqual(msg[0], b'ENTER')
86
+ msg = self.node2.recv()
87
+ self.assertEqual(msg[0], b'ENTER')
88
+ self.node1.join("TEST")
89
+ self.node2.join("TEST")
90
+ msg = self.node1.recv()
91
+ self.assertEqual(msg[0], b'JOIN')
92
+ msg = self.node2.recv()
93
+ self.assertEqual(msg[0], b'JOIN')
94
+ self.node1.leave("TEST")
95
+ self.node2.leave("TEST")
96
+ msg = self.node1.recv()
97
+ self.assertEqual(msg[0], b'LEAVE')
98
+ msg = self.node2.recv()
99
+ self.assertEqual(msg[0], b'LEAVE')
100
+
101
+ def test_get_peer_groups(self):
102
+ self.node1.join("TEST")
103
+ self.node2.join("TEST")
104
+
105
+ # pyre works asynchronous so give some time to let changes disperse
106
+ time.sleep(0.5)
107
+
108
+ self.assertIn("TEST", self.node1.peer_groups())
109
+ self.assertIn("TEST", self.node2.peer_groups())
110
+ # end test_get_peer_groups
111
+
112
+ def test_whispers(self):
113
+ msg = self.node1.recv()
114
+ self.assertEqual(msg[0], b'ENTER')
115
+ msg = self.node2.recv()
116
+ self.assertEqual(msg[0], b'ENTER')
117
+ self.node1.whispers(self.node2.uuid(), "Hi")
118
+ msg = self.node2.recv()
119
+ self.assertEqual(b"WHISPER", msg[0])
120
+ self.assertEqual(self.node1.uuid().bytes, msg[1])
121
+ self.assertEqual(b"node1", msg[2])
122
+ self.assertEqual(b"Hi", msg[3])
123
+
124
+ def test_shouts(self):
125
+ msg = self.node1.recv()
126
+ self.assertEqual(msg[0], b'ENTER')
127
+ msg = self.node2.recv()
128
+ self.assertEqual(msg[0], b'ENTER')
129
+ self.node1.join("TEST")
130
+ self.node2.join("TEST")
131
+ msg = self.node1.recv()
132
+ self.assertEqual(msg[0], b'JOIN')
133
+ msg = self.node2.recv()
134
+ self.assertEqual(msg[0], b'JOIN')
135
+ self.node1.shouts("TEST", "Hi")
136
+ msg = self.node2.recv()
137
+ self.assertEqual(b"SHOUT", msg[0])
138
+ self.assertEqual(self.node1.uuid().bytes, msg[1])
139
+ self.assertEqual(b"node1", msg[2])
140
+ self.assertEqual(b"TEST", msg[3])
141
+ self.assertEqual(b"Hi", msg[4])
142
+
143
+ def test_shout(self):
144
+ msg = self.node1.recv()
145
+ self.assertEqual(msg[0], b'ENTER')
146
+ msg = self.node2.recv()
147
+ self.assertEqual(msg[0], b'ENTER')
148
+ self.node1.join("TEST")
149
+ self.node2.join("TEST")
150
+ msg = self.node1.recv()
151
+ self.assertEqual(msg[0], b'JOIN')
152
+ msg = self.node2.recv()
153
+ self.assertEqual(msg[0], b'JOIN')
154
+ self.node1.shout("TEST", b"Hi")
155
+ msg = self.node2.recv()
156
+ self.assertEqual(b"SHOUT", msg[0])
157
+ self.assertEqual(self.node1.uuid().bytes, msg[1])
158
+ self.assertEqual(b"node1", msg[2])
159
+ self.assertEqual(b"TEST", msg[3])
160
+ self.assertEqual(b"Hi", msg[4])
161
+
162
+ def test_zfinal(self):
163
+ global inst_count
164
+ inst_count = 1
165
+ self.assertTrue(True)
166
+ # end test_zfinal
167
+ # end PyreTest
168
+
169
+ if __name__ == '__main__':
170
+ inst_count = 0
171
+
172
+ logger = logging.getLogger("pyre")
173
+ logger.setLevel(logging.DEBUG)
174
+
175
+ try:
176
+ unittest.main()
177
+ except Exception as a:
178
+ print(a)
@@ -0,0 +1,38 @@
1
+ import unittest
2
+ import zmq
3
+ import logging
4
+ from pyre import zactor
5
+ from pyre.zactor import ZActor
6
+ from pyre.zbeacon import ZBeacon
7
+
8
+ class ZBeaconTest(unittest.TestCase):
9
+
10
+ def setUp(self, *args, **kwargs):
11
+ ctx = zmq.Context()
12
+ self.actor = ZActor(ctx, zactor.echo_actor, "Hello, World")
13
+ # end setUp
14
+
15
+ def tearDown(self):
16
+ self.actor.destroy()
17
+ # end tearDown
18
+
19
+ def test_echo(self):
20
+ self.actor.send_multipart((b"ECHO", b"This is a string"))
21
+ msg = self.actor.recv()
22
+ self.assertEqual(b"This is a string", msg)
23
+ # end ZActorTest
24
+
25
+
26
+ if __name__ == '__main__':
27
+ #print(logging.Logger.manager.loggerDict)
28
+ logger = logging.getLogger()
29
+ logger.setLevel(logging.DEBUG)
30
+ ch = logging.StreamHandler()
31
+ ch.setLevel(logging.DEBUG)
32
+ logger.addHandler(ch)
33
+ logging.getLogger("pyre.zactor").setLevel(logging.DEBUG)
34
+
35
+ try:
36
+ unittest.main()
37
+ except Exception as a:
38
+ print(a)
@@ -0,0 +1,82 @@
1
+ import unittest
2
+ #import pyre
3
+ import zmq
4
+ import time
5
+ import struct
6
+ import uuid
7
+ import logging
8
+ import socket
9
+ from pyre.zactor import ZActor
10
+ from pyre.zbeacon import ZBeacon
11
+
12
+ class ZBeaconTest(unittest.TestCase):
13
+
14
+ def setUp(self, *args, **kwargs):
15
+ ctx = zmq.Context()
16
+ ctx = zmq.Context()
17
+ # two beacon frames
18
+ self.transmit1 = struct.pack('cccb16sH', b'Z', b'R', b'E',
19
+ 1, uuid.uuid4().bytes,
20
+ socket.htons(9999))
21
+ self.transmit2 = struct.pack('cccb16sH', b'Z', b'R', b'E',
22
+ 1, uuid.uuid4().bytes,
23
+ socket.htons(9999))
24
+
25
+ self.node1 = ZActor(ctx, ZBeacon)
26
+ self.node1.send_unicode("VERBOSE")
27
+ self.node1.send_unicode("CONFIGURE", zmq.SNDMORE)
28
+ self.node1.send(struct.pack("I", 9999))
29
+ print("Hostname 1:", self.node1.recv_unicode())
30
+
31
+ self.node2 = ZActor(ctx, ZBeacon)
32
+ self.node2.send_unicode("VERBOSE")
33
+ self.node2.send_unicode("CONFIGURE", zmq.SNDMORE)
34
+ self.node2.send(struct.pack("I", 9999))
35
+ print("Hostname 2:", self.node2.recv_unicode())
36
+ # end setUp
37
+
38
+ def tearDown(self):
39
+ self.node1.destroy()
40
+ self.node2.destroy()
41
+ # end tearDown
42
+
43
+ def test_node1(self):
44
+ self.node1.send_unicode("PUBLISH", zmq.SNDMORE)
45
+ self.node1.send(self.transmit1)
46
+
47
+ def test_node2(self):
48
+ self.node2.send_unicode("PUBLISH", zmq.SNDMORE)
49
+ self.node2.send(self.transmit2)
50
+
51
+ def test_recv_beacon1(self):
52
+ self.node1.send_unicode("PUBLISH", zmq.SNDMORE)
53
+ self.node1.send(self.transmit1)
54
+ self.node2.send_unicode("PUBLISH", zmq.SNDMORE)
55
+ self.node2.send(self.transmit2)
56
+ req = self.node1.recv_multipart()
57
+ self.assertEqual(self.transmit2, req[1])
58
+
59
+ def test_recv_beacon1(self):
60
+ self.node1.send_unicode("PUBLISH", zmq.SNDMORE)
61
+ self.node1.send(self.transmit1)
62
+ self.node2.send_unicode("PUBLISH", zmq.SNDMORE)
63
+ self.node2.send(self.transmit2)
64
+ req = self.node2.recv_multipart()
65
+ self.assertEqual(self.transmit1, req[1])
66
+
67
+ # end ZBeaconTest
68
+
69
+ if __name__ == '__main__':
70
+
71
+ #print(logging.Logger.manager.loggerDict)
72
+ logger = logging.getLogger()
73
+ logger.setLevel(logging.DEBUG)
74
+ ch = logging.StreamHandler()
75
+ ch.setLevel(logging.DEBUG)
76
+ logger.addHandler(ch)
77
+ logging.getLogger("pyre.zbeacon2").setLevel(logging.DEBUG)
78
+
79
+ try:
80
+ unittest.main()
81
+ except Exception as a:
82
+ print(a)
@@ -0,0 +1,32 @@
1
+ import unittest
2
+ from pyre import zhelper
3
+ from pyre import zbeacon
4
+ import zmq
5
+ import time
6
+
7
+ class ZHelperTest(unittest.TestCase):
8
+
9
+ def test_get_ifaddrs(self):
10
+ ifs = zhelper.get_ifaddrs()
11
+ self.assertIsInstance(ifs, list)
12
+ self.assertIsInstance(ifs[0], dict)
13
+ # end test_get_ifaddrs
14
+
15
+ def test_get_ifaddrs_loopback(self):
16
+ ifs = zhelper.get_ifaddrs()
17
+ addrs = []
18
+ for iface in ifs:
19
+ #print(iface)
20
+ for i,v in iface.items():
21
+ #print(i,v)
22
+ # 2 is for ipv4
23
+ if v.get(2):
24
+ addrs.append(v[2]['addr'])
25
+ self.assertIn('127.0.0.1', addrs)
26
+ # end test_get_ifaddrs_loopback
27
+
28
+ # end ZHelperTest
29
+
30
+ if __name__ == '__main__':
31
+ unittest.main()
32
+
@@ -1,8 +0,0 @@
1
- import re
2
-
3
- __all__ = ['pyre', 'zbeacon', 'zhelper']
4
- __version__ = '0.2.3.dev3'
5
- __version_info__ = tuple(int(v) for v in re.sub('[a-zA-Z]', '', __version__).split('.'))
6
-
7
- from .pyre import Pyre
8
- from .pyre_event import PyreEvent
File without changes
File without changes
File without changes
File without changes