Uploaded Test files

This commit is contained in:
Batuhan Berk Başoğlu 2020-11-12 11:05:57 -05:00
parent f584ad9d97
commit 2e81cb7d99
16627 changed files with 2065359 additions and 102444 deletions

View file

@ -0,0 +1,29 @@
"""pure-Python sugar wrappers for core 0MQ objects."""
# Copyright (C) PyZMQ Developers
# Distributed under the terms of the Modified BSD License.
from zmq.sugar import (
constants, context, frame, poll, socket, tracker, version
)
from zmq import error
__all__ = ['constants']
for submod in (
constants, context, error, frame, poll, socket, tracker, version
):
__all__.extend(submod.__all__)
from zmq.error import *
from zmq.sugar.context import *
from zmq.sugar.tracker import *
from zmq.sugar.socket import *
from zmq.sugar.constants import *
from zmq.sugar.frame import *
from zmq.sugar.poll import *
from zmq.sugar.version import *
# deprecated:
from zmq.sugar.stopwatch import Stopwatch
__all__.append('Stopwatch')

View file

@ -0,0 +1,66 @@
# coding: utf-8
"""Mixin for mapping set/getattr to self.set/get"""
# Copyright (C) PyZMQ Developers
# Distributed under the terms of the Modified BSD License.
import errno
from . import constants
class AttributeSetter(object):
def __setattr__(self, key, value):
"""set zmq options by attribute"""
if key in self.__dict__:
object.__setattr__(self, key, value)
return
# regular setattr only allowed for class-defined attributes
for obj in self.__class__.mro():
if key in obj.__dict__:
object.__setattr__(self, key, value)
return
upper_key = key.upper()
try:
opt = getattr(constants, upper_key)
except AttributeError:
raise AttributeError("%s has no such option: %s" % (
self.__class__.__name__, upper_key)
)
else:
self._set_attr_opt(upper_key, opt, value)
def _set_attr_opt(self, name, opt, value):
"""override if setattr should do something other than call self.set"""
self.set(opt, value)
def __getattr__(self, key):
"""get zmq options by attribute"""
upper_key = key.upper()
try:
opt = getattr(constants, upper_key)
except AttributeError:
raise AttributeError("%s has no such option: %s" % (
self.__class__.__name__, upper_key)
)
else:
from zmq import ZMQError
try:
return self._get_attr_opt(upper_key, opt)
except ZMQError as e:
# EINVAL will be raised on access for write-only attributes.
# Turn that into an AttributeError
# necessary for mocking
if e.errno == errno.EINVAL:
raise AttributeError("{} attribute is write-only".format(key))
else:
raise
def _get_attr_opt(self, name, opt):
"""override if getattr should do something other than call self.get"""
return self.get(opt)
__all__ = ['AttributeSetter']

View file

@ -0,0 +1,108 @@
"""0MQ Constants."""
# Copyright (c) PyZMQ Developers.
# Distributed under the terms of the Modified BSD License.
from zmq.backend import constants
from zmq.backend import has
from zmq.utils.constant_names import (
base_names,
switched_sockopt_names,
int_sockopt_names,
int64_sockopt_names,
bytes_sockopt_names,
fd_sockopt_names,
ctx_opt_names,
msg_opt_names,
)
#-----------------------------------------------------------------------------
# Python module level constants
#-----------------------------------------------------------------------------
__all__ = [
'int_sockopts',
'int64_sockopts',
'bytes_sockopts',
'ctx_opts',
'ctx_opt_names',
'DRAFT_API',
]
if constants.VERSION < 40200:
DRAFT_API = False
else:
DRAFT_API = bool(has('draft') and constants.DRAFT_API)
int_sockopts = set()
int64_sockopts = set()
bytes_sockopts = set()
fd_sockopts = set()
ctx_opts = set()
msg_opts = set()
if constants.VERSION < 30000:
int64_sockopt_names.extend(switched_sockopt_names)
else:
int_sockopt_names.extend(switched_sockopt_names)
_UNDEFINED = -9999
def _add_constant(name, container=None):
"""add a constant to be defined
optionally add it to one of the sets for use in get/setopt checkers
"""
c = getattr(constants, name, _UNDEFINED)
if c == _UNDEFINED:
return
globals()[name] = c
__all__.append(name)
if container is not None:
container.add(c)
return c
for name in base_names:
_add_constant(name)
for name in int_sockopt_names:
_add_constant(name, int_sockopts)
for name in int64_sockopt_names:
_add_constant(name, int64_sockopts)
for name in bytes_sockopt_names:
_add_constant(name, bytes_sockopts)
for name in fd_sockopt_names:
_add_constant(name, fd_sockopts)
for name in ctx_opt_names:
_add_constant(name, ctx_opts)
for name in msg_opt_names:
_add_constant(name, msg_opts)
# ensure some aliases are always defined
aliases = [
('DONTWAIT', 'NOBLOCK'),
('XREQ', 'DEALER'),
('XREP', 'ROUTER'),
]
for group in aliases:
undefined = set()
found = None
for name in group:
value = getattr(constants, name, -1)
if value != -1:
found = value
else:
undefined.add(name)
if found is not None:
for name in undefined:
globals()[name] = found
__all__.append(name)

View file

@ -0,0 +1,282 @@
# coding: utf-8
"""Python bindings for 0MQ."""
# Copyright (C) PyZMQ Developers
# Distributed under the terms of the Modified BSD License.
import atexit
import os
from threading import Lock
from weakref import WeakSet
from zmq.backend import Context as ContextBase
from . import constants
from .attrsettr import AttributeSetter
from .constants import ENOTSUP, LINGER, ctx_opt_names
from .socket import Socket
from zmq.error import ZMQError
# notice when exiting, to avoid triggering term on exit
_exiting = False
def _notice_atexit():
global _exiting
_exiting = True
atexit.register(_notice_atexit)
class Context(ContextBase, AttributeSetter):
"""Create a zmq Context
A zmq Context creates sockets via its ``ctx.socket`` method.
"""
sockopts = None
_instance = None
_instance_lock = Lock()
_instance_pid = None
_shadow = False
_sockets = None
def __init__(self, io_threads=1, **kwargs):
super(Context, self).__init__(io_threads=io_threads, **kwargs)
if kwargs.get('shadow', False):
self._shadow = True
else:
self._shadow = False
self.sockopts = {}
self._sockets = WeakSet()
def __del__(self):
"""deleting a Context should terminate it, without trying non-threadsafe destroy"""
# Calling locals() here conceals issue #1167 on Windows CPython 3.5.4.
locals()
if not self._shadow and not _exiting:
self.term()
def __enter__(self):
return self
def __exit__(self, *args, **kwargs):
self.term()
def __copy__(self, memo=None):
"""Copying a Context creates a shadow copy"""
return self.__class__.shadow(self.underlying)
__deepcopy__ = __copy__
@classmethod
def shadow(cls, address):
"""Shadow an existing libzmq context
address is the integer address of the libzmq context
or an FFI pointer to it.
.. versionadded:: 14.1
"""
from zmq.utils.interop import cast_int_addr
address = cast_int_addr(address)
return cls(shadow=address)
@classmethod
def shadow_pyczmq(cls, ctx):
"""Shadow an existing pyczmq context
ctx is the FFI `zctx_t *` pointer
.. versionadded:: 14.1
"""
from pyczmq import zctx
from zmq.utils.interop import cast_int_addr
underlying = zctx.underlying(ctx)
address = cast_int_addr(underlying)
return cls(shadow=address)
# static method copied from tornado IOLoop.instance
@classmethod
def instance(cls, io_threads=1):
"""Returns a global Context instance.
Most single-threaded applications have a single, global Context.
Use this method instead of passing around Context instances
throughout your code.
A common pattern for classes that depend on Contexts is to use
a default argument to enable programs with multiple Contexts
but not require the argument for simpler applications::
class MyClass(object):
def __init__(self, context=None):
self.context = context or Context.instance()
.. versionchanged:: 18.1
When called in a subprocess after forking,
a new global instance is created instead of inheriting
a Context that won't work from the parent process.
"""
if (
cls._instance is None
or cls._instance_pid != os.getpid()
or cls._instance.closed
):
with cls._instance_lock:
if (
cls._instance is None
or cls._instance_pid != os.getpid()
or cls._instance.closed
):
cls._instance = cls(io_threads=io_threads)
cls._instance_pid = os.getpid()
return cls._instance
def term(self):
"""Close or terminate the context.
Context termination is performed in the following steps:
- Any blocking operations currently in progress on sockets open within context shall
raise :class:`zmq.ContextTerminated`.
With the exception of socket.close(), any further operations on sockets open within this context
shall raise :class:`zmq.ContextTerminated`.
- After interrupting all blocking calls, term shall block until the following conditions are satisfied:
- All sockets open within context have been closed.
- For each socket within context, all messages sent on the socket have either been
physically transferred to a network peer,
or the socket's linger period set with the zmq.LINGER socket option has expired.
For further details regarding socket linger behaviour refer to libzmq documentation for ZMQ_LINGER.
This can be called to close the context by hand. If this is not called,
the context will automatically be closed when it is garbage collected.
"""
return super(Context, self).term()
#-------------------------------------------------------------------------
# Hooks for ctxopt completion
#-------------------------------------------------------------------------
def __dir__(self):
keys = dir(self.__class__)
for collection in (
ctx_opt_names,
):
keys.extend(collection)
return keys
#-------------------------------------------------------------------------
# Creating Sockets
#-------------------------------------------------------------------------
def _add_socket(self, socket):
self._sockets.add(socket)
def _rm_socket(self, socket):
if self._sockets:
self._sockets.discard(socket)
def destroy(self, linger=None):
"""Close all sockets associated with this context and then terminate
the context.
.. warning::
destroy involves calling ``zmq_close()``, which is **NOT** threadsafe.
If there are active sockets in other threads, this must not be called.
Parameters
----------
linger : int, optional
If specified, set LINGER on sockets prior to closing them.
"""
if self.closed:
return
sockets = self._sockets
self._sockets = WeakSet()
for s in sockets:
if s and not s.closed:
if linger is not None:
s.setsockopt(LINGER, linger)
s.close()
self.term()
@property
def _socket_class(self):
return Socket
def socket(self, socket_type, **kwargs):
"""Create a Socket associated with this Context.
Parameters
----------
socket_type : int
The socket type, which can be any of the 0MQ socket types:
REQ, REP, PUB, SUB, PAIR, DEALER, ROUTER, PULL, PUSH, etc.
kwargs:
will be passed to the __init__ method of the socket class.
"""
if self.closed:
raise ZMQError(ENOTSUP)
s = self._socket_class(self, socket_type, **kwargs)
for opt, value in self.sockopts.items():
try:
s.setsockopt(opt, value)
except ZMQError:
# ignore ZMQErrors, which are likely for socket options
# that do not apply to a particular socket type, e.g.
# SUBSCRIBE for non-SUB sockets.
pass
self._add_socket(s)
return s
def setsockopt(self, opt, value):
"""set default socket options for new sockets created by this Context
.. versionadded:: 13.0
"""
self.sockopts[opt] = value
def getsockopt(self, opt):
"""get default socket options for new sockets created by this Context
.. versionadded:: 13.0
"""
return self.sockopts[opt]
def _set_attr_opt(self, name, opt, value):
"""set default sockopts as attributes"""
if name in constants.ctx_opt_names:
return self.set(opt, value)
else:
self.sockopts[opt] = value
def _get_attr_opt(self, name, opt):
"""get default sockopts as attributes"""
if name in constants.ctx_opt_names:
return self.get(opt)
else:
if opt not in self.sockopts:
raise AttributeError(name)
else:
return self.sockopts[opt]
def __delattr__(self, key):
"""delete default sockopts as attributes"""
key = key.upper()
try:
opt = getattr(constants, key)
except AttributeError:
raise AttributeError("no such socket option: %s" % key)
else:
if opt not in self.sockopts:
raise AttributeError(key)
else:
del self.sockopts[opt]
__all__ = ['Context']

View file

@ -0,0 +1,88 @@
# coding: utf-8
"""0MQ Frame pure Python methods."""
# Copyright (C) PyZMQ Developers
# Distributed under the terms of the Modified BSD License.
from .attrsettr import AttributeSetter
from zmq.backend import Frame as FrameBase
import zmq
def _draft(v, feature):
zmq.error._check_version(v, feature)
if not zmq.DRAFT_API:
raise RuntimeError("libzmq and pyzmq must be built with draft support for %s" % feature)
class Frame(FrameBase, AttributeSetter):
"""Frame(data=None, track=False, copy=None, copy_threshold=zmq.COPY_THRESHOLD)
A zmq message Frame class for non-copy send/recvs.
This class is only needed if you want to do non-copying send and recvs.
When you pass a string to this class, like ``Frame(s)``, the
ref-count of `s` is increased by two: once because the Frame saves `s` as
an instance attribute and another because a ZMQ message is created that
points to the buffer of `s`. This second ref-count increase makes sure
that `s` lives until all messages that use it have been sent. Once 0MQ
sends all the messages and it doesn't need the buffer of s, 0MQ will call
``Py_DECREF(s)``.
Parameters
----------
data : object, optional
any object that provides the buffer interface will be used to
construct the 0MQ message data.
track : bool [default: False]
whether a MessageTracker_ should be created to track this object.
Tracking a message has a cost at creation, because it creates a threadsafe
Event object.
copy : bool [default: use copy_threshold]
Whether to create a copy of the data to pass to libzmq
or share the memory with libzmq.
If unspecified, copy_threshold is used.
copy_threshold: int [default: zmq.COPY_THRESHOLD]
If copy is unspecified, messages smaller than this many bytes
will be copied and messages larger than this will be shared with libzmq.
"""
def __getitem__(self, key):
# map Frame['User-Id'] to Frame.get('User-Id')
return self.get(key)
@property
def group(self):
"""The RADIO-DISH group of the message.
Requires libzmq >= 4.2 and pyzmq built with draft APIs enabled.
.. versionadded:: 17
"""
_draft((4,2), "RADIO-DISH")
return self.get('group')
@group.setter
def group(self, group):
_draft((4,2), "RADIO-DISH")
self.set('group', group)
@property
def routing_id(self):
"""The CLIENT-SERVER routing id of the message.
Requires libzmq >= 4.2 and pyzmq built with draft APIs enabled.
.. versionadded:: 17
"""
_draft((4,2), "CLIENT-SERVER")
return self.get('routing_id')
@routing_id.setter
def routing_id(self, routing_id):
_draft((4,2), "CLIENT-SERVER")
self.set('routing_id', routing_id)
# keep deprecated alias
Message = Frame
__all__ = ['Frame', 'Message']

View file

@ -0,0 +1,162 @@
"""0MQ polling related functions and classes."""
# Copyright (C) PyZMQ Developers
# Distributed under the terms of the Modified BSD License.
import zmq
from zmq.backend import zmq_poll
from .constants import POLLIN, POLLOUT, POLLERR
#-----------------------------------------------------------------------------
# Polling related methods
#-----------------------------------------------------------------------------
class Poller(object):
"""A stateful poll interface that mirrors Python's built-in poll."""
sockets = None
_map = {}
def __init__(self):
self.sockets = []
self._map = {}
def __contains__(self, socket):
return socket in self._map
def register(self, socket, flags=POLLIN|POLLOUT):
"""p.register(socket, flags=POLLIN|POLLOUT)
Register a 0MQ socket or native fd for I/O monitoring.
register(s,0) is equivalent to unregister(s).
Parameters
----------
socket : zmq.Socket or native socket
A zmq.Socket or any Python object having a ``fileno()``
method that returns a valid file descriptor.
flags : int
The events to watch for. Can be POLLIN, POLLOUT or POLLIN|POLLOUT.
If `flags=0`, socket will be unregistered.
"""
if flags:
if socket in self._map:
idx = self._map[socket]
self.sockets[idx] = (socket, flags)
else:
idx = len(self.sockets)
self.sockets.append((socket, flags))
self._map[socket] = idx
elif socket in self._map:
# uregister sockets registered with no events
self.unregister(socket)
else:
# ignore new sockets with no events
pass
def modify(self, socket, flags=POLLIN|POLLOUT):
"""Modify the flags for an already registered 0MQ socket or native fd."""
self.register(socket, flags)
def unregister(self, socket):
"""Remove a 0MQ socket or native fd for I/O monitoring.
Parameters
----------
socket : Socket
The socket instance to stop polling.
"""
idx = self._map.pop(socket)
self.sockets.pop(idx)
# shift indices after deletion
for socket, flags in self.sockets[idx:]:
self._map[socket] -= 1
def poll(self, timeout=None):
"""Poll the registered 0MQ or native fds for I/O.
If there are currently events ready to be processed, this function will return immediately.
Otherwise, this function will return as soon the first event is available or after timeout
milliseconds have elapsed.
Parameters
----------
timeout : float, int
The timeout in milliseconds. If None, no `timeout` (infinite). This
is in milliseconds to be compatible with ``select.poll()``.
Returns
-------
events : list of tuples
The list of events that are ready to be processed.
This is a list of tuples of the form ``(socket, event_mask)``, where the 0MQ Socket
or integer fd is the first element, and the poll event mask (POLLIN, POLLOUT) is the second.
It is common to call ``events = dict(poller.poll())``,
which turns the list of tuples into a mapping of ``socket : event_mask``.
"""
if timeout is None or timeout < 0:
timeout = -1
elif isinstance(timeout, float):
timeout = int(timeout)
return zmq_poll(self.sockets, timeout=timeout)
def select(rlist, wlist, xlist, timeout=None):
"""select(rlist, wlist, xlist, timeout=None) -> (rlist, wlist, xlist)
Return the result of poll as a lists of sockets ready for r/w/exception.
This has the same interface as Python's built-in ``select.select()`` function.
Parameters
----------
timeout : float, int, optional
The timeout in seconds. If None, no timeout (infinite). This is in seconds to be
compatible with ``select.select()``.
rlist : list of sockets/FDs
sockets/FDs to be polled for read events
wlist : list of sockets/FDs
sockets/FDs to be polled for write events
xlist : list of sockets/FDs
sockets/FDs to be polled for error events
Returns
-------
(rlist, wlist, xlist) : tuple of lists of sockets (length 3)
Lists correspond to sockets available for read/write/error events respectively.
"""
if timeout is None:
timeout = -1
# Convert from sec -> us for zmq_poll.
# zmq_poll accepts 3.x style timeout in ms
timeout = int(timeout*1000.0)
if timeout < 0:
timeout = -1
sockets = []
for s in set(rlist + wlist + xlist):
flags = 0
if s in rlist:
flags |= POLLIN
if s in wlist:
flags |= POLLOUT
if s in xlist:
flags |= POLLERR
sockets.append((s, flags))
return_sockets = zmq_poll(sockets, timeout)
rlist, wlist, xlist = [], [], []
for s, flags in return_sockets:
if flags & POLLIN:
rlist.append(s)
if flags & POLLOUT:
wlist.append(s)
if flags & POLLERR:
xlist.append(s)
return rlist, wlist, xlist
#-----------------------------------------------------------------------------
# Symbols to export
#-----------------------------------------------------------------------------
__all__ = [ 'Poller', 'select' ]

View file

@ -0,0 +1,774 @@
# coding: utf-8
"""0MQ Socket pure Python methods."""
# Copyright (C) PyZMQ Developers
# Distributed under the terms of the Modified BSD License.
import errno
import random
import sys
import warnings
import zmq
from zmq.backend import Socket as SocketBase
from .poll import Poller
from . import constants
from .attrsettr import AttributeSetter
from zmq.error import ZMQError, ZMQBindError
from zmq.utils import jsonapi
from zmq.utils.strtypes import bytes, unicode, basestring
from .constants import (
SNDMORE, ENOTSUP, POLLIN,
int64_sockopt_names,
int_sockopt_names,
bytes_sockopt_names,
fd_sockopt_names,
)
try:
import cPickle
pickle = cPickle
except:
cPickle = None
import pickle
try:
DEFAULT_PROTOCOL = pickle.DEFAULT_PROTOCOL
except AttributeError:
DEFAULT_PROTOCOL = pickle.HIGHEST_PROTOCOL
class Socket(SocketBase, AttributeSetter):
"""The ZMQ socket object
To create a Socket, first create a Context::
ctx = zmq.Context.instance()
then call ``ctx.socket(socket_type)``::
s = ctx.socket(zmq.ROUTER)
"""
_shadow = False
_monitor_socket = None
def __init__(self, *a, **kw):
super(Socket, self).__init__(*a, **kw)
if 'shadow' in kw:
self._shadow = True
else:
self._shadow = False
def __del__(self):
if not self._shadow:
self.close()
# socket as context manager:
def __enter__(self):
"""Sockets are context managers
.. versionadded:: 14.4
"""
return self
def __exit__(self, *args, **kwargs):
self.close()
#-------------------------------------------------------------------------
# Socket creation
#-------------------------------------------------------------------------
def __copy__(self, memo=None):
"""Copying a Socket creates a shadow copy"""
return self.__class__.shadow(self.underlying)
__deepcopy__ = __copy__
@classmethod
def shadow(cls, address):
"""Shadow an existing libzmq socket
address is the integer address of the libzmq socket
or an FFI pointer to it.
.. versionadded:: 14.1
"""
from zmq.utils.interop import cast_int_addr
address = cast_int_addr(address)
return cls(shadow=address)
def close(self, linger=None):
"""
Close the socket.
If linger is specified, LINGER sockopt will be set prior to closing.
Note: closing a zmq Socket may not close the underlying sockets
if there are undelivered messages.
Only after all messages are delivered or discarded by reaching the socket's LINGER timeout
(default: forever)
will the underlying sockets be closed.
This can be called to close the socket by hand. If this is not
called, the socket will automatically be closed when it is
garbage collected.
"""
if self.context:
self.context._rm_socket(self)
super(Socket, self).close(linger=linger)
#-------------------------------------------------------------------------
# Deprecated aliases
#-------------------------------------------------------------------------
@property
def socket_type(self):
warnings.warn("Socket.socket_type is deprecated, use Socket.type",
DeprecationWarning
)
return self.type
#-------------------------------------------------------------------------
# Hooks for sockopt completion
#-------------------------------------------------------------------------
def __dir__(self):
keys = dir(self.__class__)
for collection in (
bytes_sockopt_names,
int_sockopt_names,
int64_sockopt_names,
fd_sockopt_names,
):
keys.extend(collection)
return keys
#-------------------------------------------------------------------------
# Getting/Setting options
#-------------------------------------------------------------------------
setsockopt = SocketBase.set
getsockopt = SocketBase.get
def __setattr__(self, key, value):
"""Override to allow setting zmq.[UN]SUBSCRIBE even though we have a subscribe method"""
if key in self.__dict__:
object.__setattr__(self, key, value)
return
_key = key.lower()
if _key in ('subscribe', 'unsubscribe'):
if isinstance(value, unicode):
value = value.encode('utf8')
if _key == 'subscribe':
self.set(zmq.SUBSCRIBE, value)
else:
self.set(zmq.UNSUBSCRIBE, value)
return
super(Socket, self).__setattr__(key, value)
def fileno(self):
"""Return edge-triggered file descriptor for this socket.
This is a read-only edge-triggered file descriptor for both read and write events on this socket.
It is important that all available events be consumed when an event is detected,
otherwise the read event will not trigger again.
.. versionadded:: 17.0
"""
return self.FD
def subscribe(self, topic):
"""Subscribe to a topic
Only for SUB sockets.
.. versionadded:: 15.3
"""
if isinstance(topic, unicode):
topic = topic.encode('utf8')
self.set(zmq.SUBSCRIBE, topic)
def unsubscribe(self, topic):
"""Unsubscribe from a topic
Only for SUB sockets.
.. versionadded:: 15.3
"""
if isinstance(topic, unicode):
topic = topic.encode('utf8')
self.set(zmq.UNSUBSCRIBE, topic)
def set_string(self, option, optval, encoding='utf-8'):
"""Set socket options with a unicode object.
This is simply a wrapper for setsockopt to protect from encoding ambiguity.
See the 0MQ documentation for details on specific options.
Parameters
----------
option : int
The name of the option to set. Can be any of: SUBSCRIBE,
UNSUBSCRIBE, IDENTITY
optval : unicode string (unicode on py2, str on py3)
The value of the option to set.
encoding : str
The encoding to be used, default is utf8
"""
if not isinstance(optval, unicode):
raise TypeError("unicode strings only")
return self.set(option, optval.encode(encoding))
setsockopt_unicode = setsockopt_string = set_string
def get_string(self, option, encoding='utf-8'):
"""Get the value of a socket option.
See the 0MQ documentation for details on specific options.
Parameters
----------
option : int
The option to retrieve.
Returns
-------
optval : unicode string (unicode on py2, str on py3)
The value of the option as a unicode string.
"""
if option not in constants.bytes_sockopts:
raise TypeError("option %i will not return a string to be decoded"%option)
return self.getsockopt(option).decode(encoding)
getsockopt_unicode = getsockopt_string = get_string
def bind_to_random_port(self, addr, min_port=49152, max_port=65536, max_tries=100):
"""Bind this socket to a random port in a range.
If the port range is unspecified, the system will choose the port.
Parameters
----------
addr : str
The address string without the port to pass to ``Socket.bind()``.
min_port : int, optional
The minimum port in the range of ports to try (inclusive).
max_port : int, optional
The maximum port in the range of ports to try (exclusive).
max_tries : int, optional
The maximum number of bind attempts to make.
Returns
-------
port : int
The port the socket was bound to.
Raises
------
ZMQBindError
if `max_tries` reached before successful bind
"""
if hasattr(constants, 'LAST_ENDPOINT') and min_port == 49152 and max_port == 65536:
# if LAST_ENDPOINT is supported, and min_port / max_port weren't specified,
# we can bind to port 0 and let the OS do the work
self.bind("%s:*" % addr)
url = self.last_endpoint.decode('ascii', 'replace')
_, port_s = url.rsplit(':', 1)
return int(port_s)
for i in range(max_tries):
try:
port = random.randrange(min_port, max_port)
self.bind('%s:%s' % (addr, port))
except ZMQError as exception:
en = exception.errno
if en == zmq.EADDRINUSE:
continue
elif sys.platform == 'win32' and en == errno.EACCES:
continue
else:
raise
else:
return port
raise ZMQBindError("Could not bind socket to random port.")
def get_hwm(self):
"""Get the High Water Mark.
On libzmq 3, this gets SNDHWM if available, otherwise RCVHWM
"""
major = zmq.zmq_version_info()[0]
if major >= 3:
# return sndhwm, fallback on rcvhwm
try:
return self.getsockopt(zmq.SNDHWM)
except zmq.ZMQError:
pass
return self.getsockopt(zmq.RCVHWM)
else:
return self.getsockopt(zmq.HWM)
def set_hwm(self, value):
"""Set the High Water Mark.
On libzmq 3, this sets both SNDHWM and RCVHWM
.. warning::
New values only take effect for subsequent socket
bind/connects.
"""
major = zmq.zmq_version_info()[0]
if major >= 3:
raised = None
try:
self.sndhwm = value
except Exception as e:
raised = e
try:
self.rcvhwm = value
except Exception as e:
raised = e
if raised:
raise raised
else:
return self.setsockopt(zmq.HWM, value)
hwm = property(get_hwm, set_hwm,
"""Property for High Water Mark.
Setting hwm sets both SNDHWM and RCVHWM as appropriate.
It gets SNDHWM if available, otherwise RCVHWM.
"""
)
#-------------------------------------------------------------------------
# Sending and receiving messages
#-------------------------------------------------------------------------
def send(self, data, flags=0, copy=True, track=False, routing_id=None, group=None):
"""Send a single zmq message frame on this socket.
This queues the message to be sent by the IO thread at a later time.
With flags=NOBLOCK, this raises :class:`ZMQError` if the queue is full;
otherwise, this waits until space is available.
See :class:`Poller` for more general non-blocking I/O.
Parameters
----------
data : bytes, Frame, memoryview
The content of the message. This can be any object that provides
the Python buffer API (i.e. `memoryview(data)` can be called).
flags : int
0, NOBLOCK, SNDMORE, or NOBLOCK|SNDMORE.
copy : bool
Should the message be sent in a copying or non-copying manner.
track : bool
Should the message be tracked for notification that ZMQ has
finished with it? (ignored if copy=True)
routing_id : int
For use with SERVER sockets
group : str
For use with RADIO sockets
Returns
-------
None : if `copy` or not track
None if message was sent, raises an exception otherwise.
MessageTracker : if track and not copy
a MessageTracker object, whose `pending` property will
be True until the send is completed.
Raises
------
TypeError
If a unicode object is passed
ValueError
If `track=True`, but an untracked Frame is passed.
ZMQError
If the send does not succeed for any reason (including
if NOBLOCK is set and the outgoing queue is full).
.. versionchanged:: 17.0
DRAFT support for routing_id and group arguments.
"""
if routing_id is not None:
if not isinstance(data, zmq.Frame):
data = zmq.Frame(data, track=track, copy=copy or None,
copy_threshold=self.copy_threshold)
data.routing_id = routing_id
if group is not None:
if not isinstance(data, zmq.Frame):
data = zmq.Frame(data, track=track, copy=copy or None,
copy_threshold=self.copy_threshold)
data.group = group
return super(Socket, self).send(data, flags=flags, copy=copy, track=track)
def send_multipart(self, msg_parts, flags=0, copy=True, track=False, **kwargs):
"""Send a sequence of buffers as a multipart message.
The zmq.SNDMORE flag is added to all msg parts before the last.
Parameters
----------
msg_parts : iterable
A sequence of objects to send as a multipart message. Each element
can be any sendable object (Frame, bytes, buffer-providers)
flags : int, optional
Any valid flags for :func:`Socket.send`.
SNDMORE is added automatically for frames before the last.
copy : bool, optional
Should the frame(s) be sent in a copying or non-copying manner.
If copy=False, frames smaller than self.copy_threshold bytes
will be copied anyway.
track : bool, optional
Should the frame(s) be tracked for notification that ZMQ has
finished with it (ignored if copy=True).
Returns
-------
None : if copy or not track
MessageTracker : if track and not copy
a MessageTracker object, whose `pending` property will
be True until the last send is completed.
"""
# typecheck parts before sending:
for i,msg in enumerate(msg_parts):
if isinstance(msg, (zmq.Frame, bytes, memoryview)):
continue
try:
memoryview(msg)
except Exception:
rmsg = repr(msg)
if len(rmsg) > 32:
rmsg = rmsg[:32] + '...'
raise TypeError(
"Frame %i (%s) does not support the buffer interface." % (
i, rmsg,
))
for msg in msg_parts[:-1]:
self.send(msg, SNDMORE|flags, copy=copy, track=track)
# Send the last part without the extra SNDMORE flag.
return self.send(msg_parts[-1], flags, copy=copy, track=track)
def recv_multipart(self, flags=0, copy=True, track=False):
"""Receive a multipart message as a list of bytes or Frame objects
Parameters
----------
flags : int, optional
Any valid flags for :func:`Socket.recv`.
copy : bool, optional
Should the message frame(s) be received in a copying or non-copying manner?
If False a Frame object is returned for each part, if True a copy of
the bytes is made for each frame.
track : bool, optional
Should the message frame(s) be tracked for notification that ZMQ has
finished with it? (ignored if copy=True)
Returns
-------
msg_parts : list
A list of frames in the multipart message; either Frames or bytes,
depending on `copy`.
Raises
------
ZMQError
for any of the reasons :func:`~Socket.recv` might fail
"""
parts = [self.recv(flags, copy=copy, track=track)]
# have first part already, only loop while more to receive
while self.getsockopt(zmq.RCVMORE):
part = self.recv(flags, copy=copy, track=track)
parts.append(part)
return parts
def _deserialize(self, recvd, load):
"""Deserialize a received message
Override in subclass (e.g. Futures) if recvd is not the raw bytes.
The default implementation expects bytes and returns the deserialized message immediately.
Parameters
----------
load: callable
Callable that deserializes bytes
recvd:
The object returned by self.recv
"""
return load(recvd)
def send_serialized(self, msg, serialize, flags=0, copy=True, **kwargs):
"""Send a message with a custom serialization function.
.. versionadded:: 17
Parameters
----------
msg : The message to be sent. Can be any object serializable by `serialize`.
serialize : callable
The serialization function to use.
serialize(msg) should return an iterable of sendable message frames
(e.g. bytes objects), which will be passed to send_multipart.
flags : int, optional
Any valid flags for :func:`Socket.send`.
copy : bool, optional
Whether to copy the frames.
"""
frames = serialize(msg)
return self.send_multipart(frames, flags=flags, copy=copy, **kwargs)
def recv_serialized(self, deserialize, flags=0, copy=True):
"""Receive a message with a custom deserialization function.
.. versionadded:: 17
Parameters
----------
deserialize : callable
The deserialization function to use.
deserialize will be called with one argument: the list of frames
returned by recv_multipart() and can return any object.
flags : int, optional
Any valid flags for :func:`Socket.recv`.
copy : bool, optional
Whether to recv bytes or Frame objects.
Returns
-------
obj : object
The object returned by the deserialization function.
Raises
------
ZMQError
for any of the reasons :func:`~Socket.recv` might fail
"""
frames = self.recv_multipart(flags=flags, copy=copy)
return self._deserialize(frames, deserialize)
def send_string(self, u, flags=0, copy=True, encoding='utf-8', **kwargs):
"""Send a Python unicode string as a message with an encoding.
0MQ communicates with raw bytes, so you must encode/decode
text (unicode on py2, str on py3) around 0MQ.
Parameters
----------
u : Python unicode string (unicode on py2, str on py3)
The unicode string to send.
flags : int, optional
Any valid flags for :func:`Socket.send`.
encoding : str [default: 'utf-8']
The encoding to be used
"""
if not isinstance(u, basestring):
raise TypeError("unicode/str objects only")
return self.send(u.encode(encoding), flags=flags, copy=copy, **kwargs)
send_unicode = send_string
def recv_string(self, flags=0, encoding='utf-8'):
"""Receive a unicode string, as sent by send_string.
Parameters
----------
flags : int
Any valid flags for :func:`Socket.recv`.
encoding : str [default: 'utf-8']
The encoding to be used
Returns
-------
s : unicode string (unicode on py2, str on py3)
The Python unicode string that arrives as encoded bytes.
Raises
------
ZMQError
for any of the reasons :func:`~Socket.recv` might fail
"""
msg = self.recv(flags=flags)
return self._deserialize(msg, lambda buf: buf.decode(encoding))
recv_unicode = recv_string
def send_pyobj(self, obj, flags=0, protocol=DEFAULT_PROTOCOL, **kwargs):
"""Send a Python object as a message using pickle to serialize.
Parameters
----------
obj : Python object
The Python object to send.
flags : int
Any valid flags for :func:`Socket.send`.
protocol : int
The pickle protocol number to use. The default is pickle.DEFAULT_PROTOCOL
where defined, and pickle.HIGHEST_PROTOCOL elsewhere.
"""
msg = pickle.dumps(obj, protocol)
return self.send(msg, flags=flags, **kwargs)
def recv_pyobj(self, flags=0):
"""Receive a Python object as a message using pickle to serialize.
Parameters
----------
flags : int
Any valid flags for :func:`Socket.recv`.
Returns
-------
obj : Python object
The Python object that arrives as a message.
Raises
------
ZMQError
for any of the reasons :func:`~Socket.recv` might fail
"""
msg = self.recv(flags)
return self._deserialize(msg, pickle.loads)
def send_json(self, obj, flags=0, **kwargs):
"""Send a Python object as a message using json to serialize.
Keyword arguments are passed on to json.dumps
Parameters
----------
obj : Python object
The Python object to send
flags : int
Any valid flags for :func:`Socket.send`
"""
send_kwargs = {}
for key in ('routing_id', 'group'):
if key in kwargs:
send_kwargs[key] = kwargs.pop(key)
msg = jsonapi.dumps(obj, **kwargs)
return self.send(msg, flags=flags, **send_kwargs)
def recv_json(self, flags=0, **kwargs):
"""Receive a Python object as a message using json to serialize.
Keyword arguments are passed on to json.loads
Parameters
----------
flags : int
Any valid flags for :func:`Socket.recv`.
Returns
-------
obj : Python object
The Python object that arrives as a message.
Raises
------
ZMQError
for any of the reasons :func:`~Socket.recv` might fail
"""
msg = self.recv(flags)
return self._deserialize(msg, lambda buf: jsonapi.loads(buf, **kwargs))
_poller_class = Poller
def poll(self, timeout=None, flags=POLLIN):
"""Poll the socket for events.
See :class:`Poller` to wait for multiple sockets at once.
Parameters
----------
timeout : int [default: None]
The timeout (in milliseconds) to wait for an event. If unspecified
(or specified None), will wait forever for an event.
flags : int [default: POLLIN]
POLLIN, POLLOUT, or POLLIN|POLLOUT. The event flags to poll for.
Returns
-------
event_mask : int
The poll event mask (POLLIN, POLLOUT),
0 if the timeout was reached without an event.
"""
if self.closed:
raise ZMQError(ENOTSUP)
p = self._poller_class()
p.register(self, flags)
evts = dict(p.poll(timeout))
# return 0 if no events, otherwise return event bitfield
return evts.get(self, 0)
def get_monitor_socket(self, events=None, addr=None):
"""Return a connected PAIR socket ready to receive the event notifications.
.. versionadded:: libzmq-4.0
.. versionadded:: 14.0
Parameters
----------
events : int [default: ZMQ_EVENT_ALL]
The bitmask defining which events are wanted.
addr : string [default: None]
The optional endpoint for the monitoring sockets.
Returns
-------
socket : (PAIR)
The socket is already connected and ready to receive messages.
"""
# safe-guard, method only available on libzmq >= 4
if zmq.zmq_version_info() < (4,):
raise NotImplementedError("get_monitor_socket requires libzmq >= 4, have %s" % zmq.zmq_version())
# if already monitoring, return existing socket
if self._monitor_socket:
if self._monitor_socket.closed:
self._monitor_socket = None
else:
return self._monitor_socket
if addr is None:
# create endpoint name from internal fd
addr = "inproc://monitor.s-%d" % self.FD
if events is None:
# use all events
events = zmq.EVENT_ALL
# attach monitoring socket
self.monitor(addr, events)
# create new PAIR socket and connect it
self._monitor_socket = self.context.socket(zmq.PAIR)
self._monitor_socket.connect(addr)
return self._monitor_socket
def disable_monitor(self):
"""Shutdown the PAIR socket (created using get_monitor_socket)
that is serving socket events.
.. versionadded:: 14.4
"""
self._monitor_socket = None
self.monitor(None, 0)
__all__ = ['Socket']

View file

@ -0,0 +1,31 @@
"""Deprecated Stopwatch implementation"""
# Copyright (c) PyZMQ Development Team.
# Distributed under the terms of the Modified BSD License.
class Stopwatch(object):
"""Deprecated zmq.Stopwatch implementation
You can use Python's builtin timers (time.monotonic, etc.).
"""
def __init__(self):
import warnings
warnings.warn("zmq.Stopwatch is deprecated. Use stdlib time.monotonic and friends instead",
DeprecationWarning, stacklevel=2,
)
self._start = 0
import time
try:
self._monotonic = time.monotonic
except AttributeError:
self._monotonic = time.time
def start(self):
"""Start the counter"""
self._start = self._monotonic()
def stop(self):
"""Return time since start in microseconds"""
stop = self._monotonic()
return int(1e6 * (stop - self._start))

View file

@ -0,0 +1,122 @@
"""Tracker for zero-copy messages with 0MQ."""
# Copyright (C) PyZMQ Developers
# Distributed under the terms of the Modified BSD License.
import time
try:
# below 3.3
from threading import _Event as Event
except (ImportError, AttributeError):
# python throws ImportError, cython throws AttributeError
from threading import Event
from zmq.error import NotDone
from zmq.backend import Frame
class MessageTracker(object):
"""MessageTracker(*towatch)
A class for tracking if 0MQ is done using one or more messages.
When you send a 0MQ message, it is not sent immediately. The 0MQ IO thread
sends the message at some later time. Often you want to know when 0MQ has
actually sent the message though. This is complicated by the fact that
a single 0MQ message can be sent multiple times using different sockets.
This class allows you to track all of the 0MQ usages of a message.
Parameters
----------
towatch : Event, MessageTracker, Message instances.
This objects to track. This class can track the low-level
Events used by the Message class, other MessageTrackers or
actual Messages.
"""
events = None
peers = None
def __init__(self, *towatch):
"""MessageTracker(*towatch)
Create a message tracker to track a set of mesages.
Parameters
----------
*towatch : tuple of Event, MessageTracker, Message instances.
This list of objects to track. This class can track the low-level
Events used by the Message class, other MessageTrackers or
actual Messages.
"""
self.events = set()
self.peers = set()
for obj in towatch:
if isinstance(obj, Event):
self.events.add(obj)
elif isinstance(obj, MessageTracker):
self.peers.add(obj)
elif isinstance(obj, Frame):
if not obj.tracker:
raise ValueError("Not a tracked message")
self.peers.add(obj.tracker)
else:
raise TypeError("Require Events or Message Frames, not %s"%type(obj))
@property
def done(self):
"""Is 0MQ completely done with the message(s) being tracked?"""
for evt in self.events:
if not evt.is_set():
return False
for pm in self.peers:
if not pm.done:
return False
return True
def wait(self, timeout=-1):
"""mt.wait(timeout=-1)
Wait for 0MQ to be done with the message or until `timeout`.
Parameters
----------
timeout : float [default: -1, wait forever]
Maximum time in (s) to wait before raising NotDone.
Returns
-------
None
if done before `timeout`
Raises
------
NotDone
if `timeout` reached before I am done.
"""
tic = time.time()
if timeout is False or timeout < 0:
remaining = 3600*24*7 # a week
else:
remaining = timeout
done = False
for evt in self.events:
if remaining < 0:
raise NotDone
evt.wait(timeout=remaining)
if not evt.is_set():
raise NotDone
toc = time.time()
remaining -= (toc-tic)
tic = toc
for peer in self.peers:
if remaining < 0:
raise NotDone
peer.wait(timeout=remaining)
toc = time.time()
remaining -= (toc-tic)
tic = toc
_FINISHED_TRACKER = MessageTracker()
__all__ = ['MessageTracker', '_FINISHED_TRACKER']

View file

@ -0,0 +1,48 @@
"""PyZMQ and 0MQ version functions."""
# Copyright (C) PyZMQ Developers
# Distributed under the terms of the Modified BSD License.
from zmq.backend import zmq_version_info
VERSION_MAJOR = 19
VERSION_MINOR = 0
VERSION_PATCH = 2
VERSION_EXTRA = ""
__version__ = '%i.%i.%i' % (VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH)
if VERSION_EXTRA:
__version__ = "%s.%s" % (__version__, VERSION_EXTRA)
version_info = (VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, float('inf'))
else:
version_info = (VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH)
__revision__ = ''
def pyzmq_version():
"""return the version of pyzmq as a string"""
if __revision__:
return '@'.join([__version__,__revision__[:6]])
else:
return __version__
def pyzmq_version_info():
"""return the pyzmq version as a tuple of at least three numbers
If pyzmq is a development version, `inf` will be appended after the third integer.
"""
return version_info
def zmq_version():
"""return the version of libzmq as a string"""
return "%i.%i.%i" % zmq_version_info()
__all__ = ['zmq_version', 'zmq_version_info',
'pyzmq_version','pyzmq_version_info',
'__version__', '__revision__'
]