File size: 4,257 Bytes
d1ceb73
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
"""0MQ Frame pure Python methods."""

# Copyright (C) PyZMQ Developers
# Distributed under the terms of the Modified BSD License.

import zmq
from zmq.backend import Frame as FrameBase

from .attrsettr import AttributeSetter


def _draft(v, feature):
    zmq.error._check_version(v, feature)
    if not zmq.DRAFT_API:
        raise RuntimeError(
            f"libzmq and pyzmq must be built with draft support for {feature}"
        )


class Frame(FrameBase, AttributeSetter):
    """
    A zmq message Frame class for non-copying send/recvs and access to message properties.

    A ``zmq.Frame`` wraps an underlying ``zmq_msg_t``.

    Message *properties* can be accessed by treating a Frame like a dictionary (``frame["User-Id"]``).

    .. versionadded:: 14.4, libzmq 4

        Frames created by ``recv(copy=False)`` can be used to access message properties and attributes,
        such as the CURVE User-Id.

        For example::

            frames = socket.recv_multipart(copy=False)
            user_id = frames[0]["User-Id"]

    This class is used if you want to do non-copying send and recvs.
    When you pass a chunk of bytes to this class, e.g. ``Frame(buf)``, the
    ref-count of `buf` is increased by two: once because the Frame saves `buf` as
    an instance attribute and another because a ZMQ message is created that
    points to the buffer of `buf`. This second ref-count increase makes sure
    that `buf` lives until all messages that use it have been sent.
    Once 0MQ sends all the messages and it doesn't need the buffer of ``buf``,
    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
        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: :const:`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)

    def __repr__(self):
        """Return the str form of the message."""
        nbytes = len(self)
        msg_suffix = ""
        if nbytes > 16:
            msg_bytes = bytes(memoryview(self)[:12])
            if nbytes >= 1e9:
                unit = "GB"
                n = nbytes // 1e9
            elif nbytes >= 2**20:
                unit = "MB"
                n = nbytes // 1e6
            elif nbytes >= 1e3:
                unit = "kB"
                n = nbytes // 1e3
            else:
                unit = "B"
                n = nbytes
            msg_suffix = f'...{n:.0f}{unit}'
        else:
            msg_bytes = self.bytes

        _module = self.__class__.__module__
        if _module == "zmq.sugar.frame":
            _module = "zmq"
        return f"<{_module}.{self.__class__.__name__}({msg_bytes!r}{msg_suffix})>"

    @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']