first commit

This commit is contained in:
Ayxan
2022-05-23 00:16:32 +04:00
commit d660f2a4ca
24786 changed files with 4428337 additions and 0 deletions

View File

@ -0,0 +1,33 @@
"""CFFI backend (for PyPy)"""
# Copyright (C) PyZMQ Developers
# Distributed under the terms of the Modified BSD License.
from zmq.backend.cffi import _poll, context, devices, error, message, socket, utils
from ._cffi import ffi
from ._cffi import lib as C
def zmq_version_info():
"""Get libzmq version as tuple of ints"""
major = ffi.new('int*')
minor = ffi.new('int*')
patch = ffi.new('int*')
C.zmq_version(major, minor, patch)
return (int(major[0]), int(minor[0]), int(patch[0]))
__all__ = ["zmq_version_info"]
for submod in (error, message, context, socket, _poll, devices, utils):
__all__.extend(submod.__all__)
from ._poll import *
from .context import *
from .devices import *
from .error import *
from .message import *
from .socket import *
from .utils import *

View File

@ -0,0 +1,90 @@
void zmq_version(int *major, int *minor, int *patch);
void* zmq_socket(void *context, int type);
int zmq_close(void *socket);
int zmq_bind(void *socket, const char *endpoint);
int zmq_connect(void *socket, const char *endpoint);
int zmq_errno(void);
const char * zmq_strerror(int errnum);
int zmq_device(int device, void *frontend, void *backend);
int zmq_unbind(void *socket, const char *endpoint);
int zmq_disconnect(void *socket, const char *endpoint);
void* zmq_ctx_new();
int zmq_ctx_destroy(void *context);
int zmq_ctx_get(void *context, int opt);
int zmq_ctx_set(void *context, int opt, int optval);
int zmq_proxy(void *frontend, void *backend, void *capture);
int zmq_proxy_steerable(void *frontend,
void *backend,
void *capture,
void *control);
int zmq_socket_monitor(void *socket, const char *addr, int events);
int zmq_curve_keypair (char *z85_public_key, char *z85_secret_key);
int zmq_curve_public (char *z85_public_key, char *z85_secret_key);
int zmq_has (const char *capability);
typedef struct { ...; } zmq_msg_t;
typedef ... zmq_free_fn;
int zmq_msg_init(zmq_msg_t *msg);
int zmq_msg_init_size(zmq_msg_t *msg, size_t size);
int zmq_msg_init_data(zmq_msg_t *msg,
void *data,
size_t size,
zmq_free_fn *ffn,
void *hint);
size_t zmq_msg_size(zmq_msg_t *msg);
void *zmq_msg_data(zmq_msg_t *msg);
int zmq_msg_close(zmq_msg_t *msg);
int zmq_msg_copy(zmq_msg_t *dst, zmq_msg_t *src);
int zmq_msg_send(zmq_msg_t *msg, void *socket, int flags);
int zmq_msg_recv(zmq_msg_t *msg, void *socket, int flags);
int zmq_getsockopt(void *socket,
int option_name,
void *option_value,
size_t *option_len);
int zmq_setsockopt(void *socket,
int option_name,
const void *option_value,
size_t option_len);
typedef int... ZMQ_FD_T;
typedef struct
{
void *socket;
ZMQ_FD_T fd;
short events;
short revents;
} zmq_pollitem_t;
int zmq_poll(zmq_pollitem_t *items, int nitems, long timeout);
// miscellany
void * memcpy(void *restrict s1, const void *restrict s2, size_t n);
void * malloc(size_t sz);
void free(void *p);
int get_ipc_path_max_len(void);
typedef struct _zhint {
void *sock;
void *mutex;
size_t id;
} zhint;
typedef ... mutex_t;
mutex_t* mutex_allocate();
int zmq_wrap_msg_init_data(zmq_msg_t *msg,
void *data,
size_t size,
void *hint);

View File

@ -0,0 +1,92 @@
"""zmq poll function"""
# Copyright (C) PyZMQ Developers
# Distributed under the terms of the Modified BSD License.
try:
from time import monotonic
except ImportError:
from time import clock as monotonic
import warnings
from zmq.error import InterruptedSystemCall, _check_rc
from ._cffi import ffi
from ._cffi import lib as C
def _make_zmq_pollitem(socket, flags):
zmq_socket = socket._zmq_socket
zmq_pollitem = ffi.new('zmq_pollitem_t*')
zmq_pollitem.socket = zmq_socket
zmq_pollitem.fd = 0
zmq_pollitem.events = flags
zmq_pollitem.revents = 0
return zmq_pollitem[0]
def _make_zmq_pollitem_fromfd(socket_fd, flags):
zmq_pollitem = ffi.new('zmq_pollitem_t*')
zmq_pollitem.socket = ffi.NULL
zmq_pollitem.fd = socket_fd
zmq_pollitem.events = flags
zmq_pollitem.revents = 0
return zmq_pollitem[0]
def zmq_poll(sockets, timeout):
cffi_pollitem_list = []
low_level_to_socket_obj = {}
from zmq import Socket
for item in sockets:
if isinstance(item[0], Socket):
low_level_to_socket_obj[item[0]._zmq_socket] = item
cffi_pollitem_list.append(_make_zmq_pollitem(item[0], item[1]))
else:
if not isinstance(item[0], int):
# not an FD, get it from fileno()
item = (item[0].fileno(), item[1])
low_level_to_socket_obj[item[0]] = item
cffi_pollitem_list.append(_make_zmq_pollitem_fromfd(item[0], item[1]))
items = ffi.new('zmq_pollitem_t[]', cffi_pollitem_list)
list_length = ffi.cast('int', len(cffi_pollitem_list))
while True:
c_timeout = ffi.cast('long', timeout)
start = monotonic()
rc = C.zmq_poll(items, list_length, c_timeout)
try:
_check_rc(rc)
except InterruptedSystemCall:
if timeout > 0:
ms_passed = int(1000 * (monotonic() - start))
if ms_passed < 0:
# don't allow negative ms_passed,
# which can happen on old Python versions without time.monotonic.
warnings.warn(
"Negative elapsed time for interrupted poll: %s."
" Did the clock change?" % ms_passed,
RuntimeWarning,
)
ms_passed = 0
timeout = max(0, timeout - ms_passed)
continue
else:
break
result = []
for index in range(len(items)):
if items[index].revents > 0:
if not items[index].socket == ffi.NULL:
result.append(
(
low_level_to_socket_obj[items[index].socket][0],
items[index].revents,
)
)
else:
result.append((items[index].fd, items[index].revents))
return result
__all__ = ['zmq_poll']

View File

@ -0,0 +1,78 @@
"""zmq Context class"""
# Copyright (C) PyZMQ Developers
# Distributed under the terms of the Modified BSD License.
from zmq.constants import EINVAL, IO_THREADS
from zmq.error import InterruptedSystemCall, ZMQError, _check_rc
from ._cffi import ffi
from ._cffi import lib as C
class Context:
_zmq_ctx = None
_iothreads = None
_closed = None
_shadow = False
def __init__(self, io_threads=1, shadow=None):
if shadow:
self._zmq_ctx = ffi.cast("void *", shadow)
self._shadow = True
else:
self._shadow = False
if not io_threads >= 0:
raise ZMQError(EINVAL)
self._zmq_ctx = C.zmq_ctx_new()
if self._zmq_ctx == ffi.NULL:
raise ZMQError(C.zmq_errno())
if not shadow:
C.zmq_ctx_set(self._zmq_ctx, IO_THREADS, io_threads)
self._closed = False
@property
def underlying(self):
"""The address of the underlying libzmq context"""
return int(ffi.cast('size_t', self._zmq_ctx))
@property
def closed(self):
return self._closed
def set(self, option, value):
"""set a context option
see zmq_ctx_set
"""
rc = C.zmq_ctx_set(self._zmq_ctx, option, value)
_check_rc(rc)
def get(self, option):
"""get context option
see zmq_ctx_get
"""
rc = C.zmq_ctx_get(self._zmq_ctx, option)
_check_rc(rc, error_without_errno=False)
return rc
def term(self):
if self.closed:
return
rc = C.zmq_ctx_destroy(self._zmq_ctx)
try:
_check_rc(rc)
except InterruptedSystemCall:
# ignore interrupted term
# see PEP 475 notes about close & EINTR for why
pass
self._zmq_ctx = None
self._closed = True
__all__ = ['Context']

View File

@ -0,0 +1,63 @@
"""zmq device functions"""
# Copyright (C) PyZMQ Developers
# Distributed under the terms of the Modified BSD License.
from ._cffi import ffi
from ._cffi import lib as C
from .socket import Socket
from .utils import _retry_sys_call
def device(device_type, frontend, backend):
return proxy(frontend, backend)
def proxy(frontend, backend, capture=None):
if isinstance(capture, Socket):
capture = capture._zmq_socket
else:
capture = ffi.NULL
_retry_sys_call(C.zmq_proxy, frontend._zmq_socket, backend._zmq_socket, capture)
def proxy_steerable(frontend, backend, capture=None, control=None):
"""proxy_steerable(frontend, backend, capture, control)
Start a zeromq proxy with control flow.
.. versionadded:: libzmq-4.1
.. versionadded:: 18.0
Parameters
----------
frontend : Socket
The Socket instance for the incoming traffic.
backend : Socket
The Socket instance for the outbound traffic.
capture : Socket (optional)
The Socket instance for capturing traffic.
control : Socket (optional)
The Socket instance for control flow.
"""
if isinstance(capture, Socket):
capture = capture._zmq_socket
else:
capture = ffi.NULL
if isinstance(control, Socket):
control = control._zmq_socket
else:
control = ffi.NULL
_retry_sys_call(
C.zmq_proxy_steerable,
frontend._zmq_socket,
backend._zmq_socket,
capture,
control,
)
__all__ = ['device', 'proxy', 'proxy_steerable']

View File

@ -0,0 +1,20 @@
"""zmq error functions"""
# Copyright (C) PyZMQ Developers
# Distributed under the terms of the Modified BSD License.
from ._cffi import ffi
from ._cffi import lib as C
def strerror(errno):
s = ffi.string(C.zmq_strerror(errno))
if not isinstance(s, str):
# py3
s = s.decode()
return s
zmq_errno = C.zmq_errno
__all__ = ['strerror', 'zmq_errno']

View File

@ -0,0 +1,225 @@
"""Dummy Frame object"""
# Copyright (C) PyZMQ Developers
# Distributed under the terms of the Modified BSD License.
import errno
from threading import Event
import zmq
import zmq.error
from zmq.constants import ETERM
from ._cffi import ffi
from ._cffi import lib as C
zmq_gc = None
try:
from __pypy__.bufferable import bufferable as maybe_bufferable
except ImportError:
maybe_bufferable = object
def _content(obj):
"""Return content of obj as bytes"""
if type(obj) is bytes:
return obj
if not isinstance(obj, memoryview):
obj = memoryview(obj)
return obj.tobytes()
def _check_rc(rc):
err = C.zmq_errno()
if rc == -1:
if err == errno.EINTR:
raise zmq.error.InterrruptedSystemCall(err)
elif err == errno.EAGAIN:
raise zmq.error.Again(errno)
elif err == ETERM:
raise zmq.error.ContextTerminated(err)
else:
raise zmq.error.ZMQError(err)
return 0
class Frame(maybe_bufferable):
_data = None
tracker = None
closed = False
more = False
_buffer = None
_bytes = None
_failed_init = False
tracker_event = None
zmq_msg = None
def __init__(self, data=None, track=False, copy=None, copy_threshold=None):
self._failed_init = True
self.zmq_msg = ffi.cast('zmq_msg_t[1]', C.malloc(ffi.sizeof("zmq_msg_t")))
# self.tracker should start finished
# except in the case where we are sharing memory with libzmq
if track:
self.tracker = zmq._FINISHED_TRACKER
if isinstance(data, str):
raise TypeError(
"Unicode strings are not allowed. Only: bytes, buffer interfaces."
)
if data is None:
rc = C.zmq_msg_init(self.zmq_msg)
_check_rc(rc)
self._failed_init = False
return
self._data = data
if type(data) is bytes:
# avoid unnecessary copy on .bytes access
self._bytes = data
self._buffer = memoryview(data)
c_data = ffi.from_buffer(self._buffer)
data_len_c = self._buffer.nbytes
if copy is None:
if copy_threshold and data_len_c < copy_threshold:
copy = True
else:
copy = False
if copy:
# copy message data instead of sharing memory
rc = C.zmq_msg_init_size(self.zmq_msg, data_len_c)
_check_rc(rc)
ffi.buffer(C.zmq_msg_data(self.zmq_msg), data_len_c)[:] = self._buffer
self._failed_init = False
return
# Getting here means that we are doing a true zero-copy Frame,
# where libzmq and Python are sharing memory.
# Hook up garbage collection with MessageTracker and zmq_free_fn
# Event and MessageTracker for monitoring when zmq is done with data:
if track:
evt = Event()
self.tracker_event = evt
self.tracker = zmq.MessageTracker(evt)
# create the hint for zmq_free_fn
# two pointers: the zmq_gc context and a message to be sent to the zmq_gc PULL socket
# allows libzmq to signal to Python when it is done with Python-owned memory.
global zmq_gc
if zmq_gc is None:
from zmq.utils.garbage import gc as zmq_gc
# can't use ffi.new because it will be freed at the wrong time!
hint = ffi.cast("zhint[1]", C.malloc(ffi.sizeof("zhint")))
hint[0].id = zmq_gc.store(data, self.tracker_event)
if not zmq_gc._push_mutex:
zmq_gc._push_mutex = C.mutex_allocate()
hint[0].mutex = ffi.cast("mutex_t*", zmq_gc._push_mutex)
hint[0].sock = ffi.cast("void*", zmq_gc._push_socket.underlying)
# calls zmq_wrap_msg_init_data with the C.free_python_msg callback
rc = C.zmq_wrap_msg_init_data(
self.zmq_msg,
c_data,
data_len_c,
hint,
)
if rc != 0:
C.free(hint)
C.free(self.zmq_msg)
_check_rc(rc)
self._failed_init = False
def __del__(self):
if not self.closed and not self._failed_init:
self.close()
def close(self):
if self.closed or self._failed_init or self.zmq_msg is None:
return
self.closed = True
rc = C.zmq_msg_close(self.zmq_msg)
C.free(self.zmq_msg)
self.zmq_msg = None
if rc != 0:
_check_rc(rc)
def _buffer_from_zmq_msg(self):
"""one-time extract buffer from zmq_msg
for Frames created by recv
"""
if self._data is None:
self._data = ffi.buffer(
C.zmq_msg_data(self.zmq_msg), C.zmq_msg_size(self.zmq_msg)
)
if self._buffer is None:
self._buffer = memoryview(self._data)
@property
def buffer(self):
if self._buffer is None:
self._buffer_from_zmq_msg()
return self._buffer
@property
def bytes(self):
if self._bytes is None:
self._bytes = self.buffer.tobytes()
return self._bytes
def __len__(self):
return self.buffer.nbytes
def __eq__(self, other):
return self.bytes == _content(other)
def __str__(self):
return self.bytes.decode()
@property
def done(self):
return self.tracker.done()
def __buffer__(self, flags):
return self.buffer
def __copy__(self):
"""Create a shallow copy of the message.
This does not copy the contents of the Frame, just the pointer.
This will increment the 0MQ ref count of the message, but not
the ref count of the Python object. That is only done once when
the Python is first turned into a 0MQ message.
"""
return self.fast_copy()
def fast_copy(self):
"""Fast shallow copy of the Frame.
Does not copy underlying data.
"""
new_msg = Frame()
# This does not copy the contents, but just increases the ref-count
# of the zmq_msg by one.
C.zmq_msg_copy(new_msg.zmq_msg, self.zmq_msg)
# Copy the ref to underlying data
new_msg._data = self._data
new_msg._buffer = self._buffer
# Frame copies share the tracker and tracker_event
new_msg.tracker_event = self.tracker_event
new_msg.tracker = self.tracker
return new_msg
Message = Frame
__all__ = ['Frame', 'Message']

View File

@ -0,0 +1,351 @@
"""zmq Socket class"""
# Copyright (C) PyZMQ Developers
# Distributed under the terms of the Modified BSD License.
import errno as errno_mod
from ._cffi import ffi
from ._cffi import lib as C
nsp = new_sizet_pointer = lambda length: ffi.new('size_t*', length)
new_uint64_pointer = lambda: (ffi.new('uint64_t*'), nsp(ffi.sizeof('uint64_t')))
new_int64_pointer = lambda: (ffi.new('int64_t*'), nsp(ffi.sizeof('int64_t')))
new_int_pointer = lambda: (ffi.new('int*'), nsp(ffi.sizeof('int')))
new_binary_data = lambda length: (
ffi.new('char[%d]' % (length)),
nsp(ffi.sizeof('char') * length),
)
value_uint64_pointer = lambda val: (ffi.new('uint64_t*', val), ffi.sizeof('uint64_t'))
value_int64_pointer = lambda val: (ffi.new('int64_t*', val), ffi.sizeof('int64_t'))
value_int_pointer = lambda val: (ffi.new('int*', val), ffi.sizeof('int'))
value_binary_data = lambda val, length: (
ffi.new('char[%d]' % (length + 1), val),
ffi.sizeof('char') * length,
)
ZMQ_FD_64BIT = ffi.sizeof('ZMQ_FD_T') == 8
IPC_PATH_MAX_LEN = C.get_ipc_path_max_len()
import zmq
from zmq.constants import SocketOption, _OptType
from zmq.error import ZMQError, _check_rc, _check_version
from .message import Frame
from .utils import _retry_sys_call
def new_pointer_from_opt(option, length=0):
opt_type = getattr(option, "_opt_type", _OptType.int)
if opt_type == _OptType.int64 or (ZMQ_FD_64BIT and opt_type == _OptType.fd):
return new_int64_pointer()
elif opt_type == _OptType.bytes:
return new_binary_data(length)
else:
# default
return new_int_pointer()
def value_from_opt_pointer(option, opt_pointer, length=0):
try:
option = SocketOption(option)
except ValueError:
# unrecognized option,
# assume from the future,
# let EINVAL raise
opt_type = _OptType.int
else:
opt_type = option._opt_type
if opt_type == _OptType.bytes:
return ffi.buffer(opt_pointer, length)[:]
else:
return int(opt_pointer[0])
def initialize_opt_pointer(option, value, length=0):
opt_type = getattr(option, "_opt_type", _OptType.int)
if opt_type == _OptType.int64 or (ZMQ_FD_64BIT and opt_type == _OptType.fd):
return value_int64_pointer(value)
elif opt_type == _OptType.bytes:
return value_binary_data(value, length)
else:
return value_int_pointer(value)
class Socket:
context = None
socket_type = None
_zmq_socket = None
_closed = None
_ref = None
_shadow = False
copy_threshold = 0
def __init__(self, context=None, socket_type=None, shadow=None):
self.context = context
if shadow is not None:
if isinstance(shadow, Socket):
shadow = shadow.underlying
self._zmq_socket = ffi.cast("void *", shadow)
self._shadow = True
else:
self._shadow = False
self._zmq_socket = C.zmq_socket(context._zmq_ctx, socket_type)
if self._zmq_socket == ffi.NULL:
raise ZMQError()
self._closed = False
@property
def underlying(self):
"""The address of the underlying libzmq socket"""
return int(ffi.cast('size_t', self._zmq_socket))
def _check_closed_deep(self):
"""thorough check of whether the socket has been closed,
even if by another entity (e.g. ctx.destroy).
Only used by the `closed` property.
returns True if closed, False otherwise
"""
if self._closed:
return True
try:
self.get(zmq.TYPE)
except ZMQError as e:
if e.errno == zmq.ENOTSOCK:
self._closed = True
return True
else:
raise
return False
@property
def closed(self):
return self._check_closed_deep()
def close(self, linger=None):
rc = 0
if not self._closed and hasattr(self, '_zmq_socket'):
if self._zmq_socket is not None:
if linger is not None:
self.set(zmq.LINGER, linger)
rc = C.zmq_close(self._zmq_socket)
self._closed = True
if rc < 0:
_check_rc(rc)
def bind(self, address):
if isinstance(address, str):
address_b = address.encode('utf8')
else:
address_b = address
if isinstance(address, bytes):
address = address_b.decode('utf8')
rc = C.zmq_bind(self._zmq_socket, address_b)
if rc < 0:
if IPC_PATH_MAX_LEN and C.zmq_errno() == errno_mod.ENAMETOOLONG:
path = address.split('://', 1)[-1]
msg = (
'ipc path "{}" is longer than {} '
'characters (sizeof(sockaddr_un.sun_path)).'.format(
path, IPC_PATH_MAX_LEN
)
)
raise ZMQError(C.zmq_errno(), msg=msg)
elif C.zmq_errno() == errno_mod.ENOENT:
path = address.split('://', 1)[-1]
msg = f'No such file or directory for ipc path "{path}".'
raise ZMQError(C.zmq_errno(), msg=msg)
else:
_check_rc(rc)
def unbind(self, address):
_check_version((3, 2), "unbind")
if isinstance(address, str):
address = address.encode('utf8')
rc = C.zmq_unbind(self._zmq_socket, address)
_check_rc(rc)
def connect(self, address):
if isinstance(address, str):
address = address.encode('utf8')
rc = C.zmq_connect(self._zmq_socket, address)
_check_rc(rc)
def disconnect(self, address):
_check_version((3, 2), "disconnect")
if isinstance(address, str):
address = address.encode('utf8')
rc = C.zmq_disconnect(self._zmq_socket, address)
_check_rc(rc)
def set(self, option, value):
length = None
if isinstance(value, str):
raise TypeError("unicode not allowed, use bytes")
try:
option = SocketOption(option)
except ValueError:
# unrecognized option,
# assume from the future,
# let EINVAL raise
opt_type = _OptType.int
else:
opt_type = option._opt_type
if isinstance(value, bytes):
if opt_type != _OptType.bytes:
raise TypeError("not a bytes sockopt: %s" % option)
length = len(value)
c_value_pointer, c_sizet = initialize_opt_pointer(option, value, length)
_retry_sys_call(
C.zmq_setsockopt,
self._zmq_socket,
option,
ffi.cast('void*', c_value_pointer),
c_sizet,
)
def get(self, option):
try:
option = SocketOption(option)
except ValueError:
# unrecognized option,
# assume from the future,
# let EINVAL raise
opt_type = _OptType.int
else:
opt_type = option._opt_type
c_value_pointer, c_sizet_pointer = new_pointer_from_opt(option, length=255)
_retry_sys_call(
C.zmq_getsockopt, self._zmq_socket, option, c_value_pointer, c_sizet_pointer
)
sz = c_sizet_pointer[0]
v = value_from_opt_pointer(option, c_value_pointer, sz)
if (
option != zmq.SocketOption.ROUTING_ID
and opt_type == _OptType.bytes
and v.endswith(b'\0')
):
v = v[:-1]
return v
def _send_copy(self, buf, flags):
"""Send a copy of a bufferable"""
zmq_msg = ffi.new('zmq_msg_t*')
if not isinstance(buf, bytes):
# cast any bufferable data to bytes via memoryview
buf = memoryview(buf).tobytes()
c_message = ffi.new('char[]', buf)
rc = C.zmq_msg_init_size(zmq_msg, len(buf))
_check_rc(rc)
C.memcpy(C.zmq_msg_data(zmq_msg), c_message, len(buf))
_retry_sys_call(C.zmq_msg_send, zmq_msg, self._zmq_socket, flags)
rc2 = C.zmq_msg_close(zmq_msg)
_check_rc(rc2)
def _send_frame(self, frame, flags):
"""Send a Frame on this socket in a non-copy manner."""
# Always copy the Frame so the original message isn't garbage collected.
# This doesn't do a real copy, just a reference.
frame_copy = frame.fast_copy()
zmq_msg = frame_copy.zmq_msg
_retry_sys_call(C.zmq_msg_send, zmq_msg, self._zmq_socket, flags)
tracker = frame_copy.tracker
frame_copy.close()
return tracker
def send(self, data, flags=0, copy=False, track=False):
if isinstance(data, str):
raise TypeError("Message must be in bytes, not a unicode object")
if copy and not isinstance(data, Frame):
return self._send_copy(data, flags)
else:
close_frame = False
if isinstance(data, Frame):
if track and not data.tracker:
raise ValueError('Not a tracked message')
frame = data
else:
if self.copy_threshold:
buf = memoryview(data)
# always copy messages smaller than copy_threshold
if buf.nbytes < self.copy_threshold:
self._send_copy(buf, flags)
return zmq._FINISHED_TRACKER
frame = Frame(data, track=track, copy_threshold=self.copy_threshold)
close_frame = True
tracker = self._send_frame(frame, flags)
if close_frame:
frame.close()
return tracker
def recv(self, flags=0, copy=True, track=False):
if copy:
zmq_msg = ffi.new('zmq_msg_t*')
C.zmq_msg_init(zmq_msg)
else:
frame = zmq.Frame(track=track)
zmq_msg = frame.zmq_msg
try:
_retry_sys_call(C.zmq_msg_recv, zmq_msg, self._zmq_socket, flags)
except Exception:
if copy:
C.zmq_msg_close(zmq_msg)
raise
if not copy:
return frame
_buffer = ffi.buffer(C.zmq_msg_data(zmq_msg), C.zmq_msg_size(zmq_msg))
_bytes = _buffer[:]
rc = C.zmq_msg_close(zmq_msg)
_check_rc(rc)
return _bytes
def monitor(self, addr, events=-1):
"""s.monitor(addr, flags)
Start publishing socket events on inproc.
See libzmq docs for zmq_monitor for details.
Note: requires libzmq >= 3.2
Parameters
----------
addr : str
The inproc url used for monitoring. Passing None as
the addr will cause an existing socket monitor to be
deregistered.
events : int [default: zmq.EVENT_ALL]
The zmq event bitmask for which events will be sent to the monitor.
"""
_check_version((3, 2), "monitor")
if events < 0:
events = zmq.EVENT_ALL
if addr is None:
addr = ffi.NULL
if isinstance(addr, str):
addr = addr.encode('utf8')
C.zmq_socket_monitor(self._zmq_socket, addr, events)
__all__ = ['Socket', 'IPC_PATH_MAX_LEN']

View File

@ -0,0 +1,78 @@
"""miscellaneous zmq_utils wrapping"""
# Copyright (C) PyZMQ Developers
# Distributed under the terms of the Modified BSD License.
from zmq.error import InterruptedSystemCall, _check_rc, _check_version
from ._cffi import ffi
from ._cffi import lib as C
def has(capability):
"""Check for zmq capability by name (e.g. 'ipc', 'curve')
.. versionadded:: libzmq-4.1
.. versionadded:: 14.1
"""
_check_version((4, 1), 'zmq.has')
if isinstance(capability, str):
capability = capability.encode('utf8')
return bool(C.zmq_has(capability))
def curve_keypair():
"""generate a Z85 keypair for use with zmq.CURVE security
Requires libzmq (≥ 4.0) to have been built with CURVE support.
Returns
-------
(public, secret) : two bytestrings
The public and private keypair as 40 byte z85-encoded bytestrings.
"""
_check_version((3, 2), "curve_keypair")
public = ffi.new('char[64]')
private = ffi.new('char[64]')
rc = C.zmq_curve_keypair(public, private)
_check_rc(rc)
return ffi.buffer(public)[:40], ffi.buffer(private)[:40]
def curve_public(private):
"""Compute the public key corresponding to a private key for use
with zmq.CURVE security
Requires libzmq (≥ 4.2) to have been built with CURVE support.
Parameters
----------
private
The private key as a 40 byte z85-encoded bytestring
Returns
-------
bytestring
The public key as a 40 byte z85-encoded bytestring.
"""
if isinstance(private, str):
private = private.encode('utf8')
_check_version((4, 2), "curve_public")
public = ffi.new('char[64]')
rc = C.zmq_curve_public(public, private)
_check_rc(rc)
return ffi.buffer(public)[:40]
def _retry_sys_call(f, *args, **kwargs):
"""make a call, retrying if interrupted with EINTR"""
while True:
rc = f(*args)
try:
_check_rc(rc)
except InterruptedSystemCall:
continue
else:
break
__all__ = ['has', 'curve_keypair', 'curve_public']