frame.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. """0MQ Frame pure Python methods."""
  2. # Copyright (C) PyZMQ Developers
  3. # Distributed under the terms of the Modified BSD License.
  4. import zmq
  5. from zmq.backend import Frame as FrameBase
  6. from .attrsettr import AttributeSetter
  7. def _draft(v, feature):
  8. zmq.error._check_version(v, feature)
  9. if not zmq.DRAFT_API:
  10. raise RuntimeError(
  11. f"libzmq and pyzmq must be built with draft support for {feature}"
  12. )
  13. class Frame(FrameBase, AttributeSetter):
  14. """
  15. A zmq message Frame class for non-copying send/recvs and access to message properties.
  16. A ``zmq.Frame`` wraps an underlying ``zmq_msg_t``.
  17. Message *properties* can be accessed by treating a Frame like a dictionary (``frame["User-Id"]``).
  18. .. versionadded:: 14.4, libzmq 4
  19. Frames created by ``recv(copy=False)`` can be used to access message properties and attributes,
  20. such as the CURVE User-Id.
  21. For example::
  22. frames = socket.recv_multipart(copy=False)
  23. user_id = frames[0]["User-Id"]
  24. This class is used if you want to do non-copying send and recvs.
  25. When you pass a chunk of bytes to this class, e.g. ``Frame(buf)``, the
  26. ref-count of `buf` is increased by two: once because the Frame saves `buf` as
  27. an instance attribute and another because a ZMQ message is created that
  28. points to the buffer of `buf`. This second ref-count increase makes sure
  29. that `buf` lives until all messages that use it have been sent.
  30. Once 0MQ sends all the messages and it doesn't need the buffer of ``buf``,
  31. 0MQ will call ``Py_DECREF(s)``.
  32. Parameters
  33. ----------
  34. data : object, optional
  35. any object that provides the buffer interface will be used to
  36. construct the 0MQ message data.
  37. track : bool
  38. whether a MessageTracker_ should be created to track this object.
  39. Tracking a message has a cost at creation, because it creates a threadsafe
  40. Event object.
  41. copy : bool
  42. default: use copy_threshold
  43. Whether to create a copy of the data to pass to libzmq
  44. or share the memory with libzmq.
  45. If unspecified, copy_threshold is used.
  46. copy_threshold: int
  47. default: :const:`zmq.COPY_THRESHOLD`
  48. If copy is unspecified, messages smaller than this many bytes
  49. will be copied and messages larger than this will be shared with libzmq.
  50. """
  51. def __getitem__(self, key):
  52. # map Frame['User-Id'] to Frame.get('User-Id')
  53. return self.get(key)
  54. def __repr__(self):
  55. """Return the str form of the message."""
  56. nbytes = len(self)
  57. msg_suffix = ""
  58. if nbytes > 16:
  59. msg_bytes = bytes(memoryview(self.buffer)[:12])
  60. if nbytes >= 1e9:
  61. unit = "GB"
  62. n = nbytes // 1e9
  63. elif nbytes >= 2**20:
  64. unit = "MB"
  65. n = nbytes // 1e6
  66. elif nbytes >= 1e3:
  67. unit = "kB"
  68. n = nbytes // 1e3
  69. else:
  70. unit = "B"
  71. n = nbytes
  72. msg_suffix = f'...{n:.0f}{unit}'
  73. else:
  74. msg_bytes = self.bytes
  75. _module = self.__class__.__module__
  76. if _module == "zmq.sugar.frame":
  77. _module = "zmq"
  78. return f"<{_module}.{self.__class__.__name__}({msg_bytes!r}{msg_suffix})>"
  79. @property
  80. def group(self):
  81. """The RADIO-DISH group of the message.
  82. Requires libzmq >= 4.2 and pyzmq built with draft APIs enabled.
  83. .. versionadded:: 17
  84. """
  85. _draft((4, 2), "RADIO-DISH")
  86. return self.get('group')
  87. @group.setter
  88. def group(self, group):
  89. _draft((4, 2), "RADIO-DISH")
  90. self.set('group', group)
  91. @property
  92. def routing_id(self):
  93. """The CLIENT-SERVER routing id of the message.
  94. Requires libzmq >= 4.2 and pyzmq built with draft APIs enabled.
  95. .. versionadded:: 17
  96. """
  97. _draft((4, 2), "CLIENT-SERVER")
  98. return self.get('routing_id')
  99. @routing_id.setter
  100. def routing_id(self, routing_id):
  101. _draft((4, 2), "CLIENT-SERVER")
  102. self.set('routing_id', routing_id)
  103. # keep deprecated alias
  104. Message = Frame
  105. __all__ = ['Frame', 'Message']