<NME> utils.py
<BEF> """General utility functions.
"""

import copy
import os
import re
except ImportError:
    import md5 as hashlib

from stomp.backward import NULL

##@namespace stomp.utils
# General utilities.
# addresses) that refer to the local host (both loopback interface
# and external interfaces).  This is used for determining
# preferred targets.
LOCALHOST_NAMES = ["localhost", "127.0.0.1"]

NULL = b"\x00"

if not os.environ.get("STOMP_SKIP_HOSTNAME_SCAN"):
    try:
        LOCALHOST_NAMES.append(socket.gethostbyname(socket.gethostname()))
    except Exception:
        pass

    try:
        LOCALHOST_NAMES.append(socket.gethostname())
    except Exception:
        pass

    try:
        LOCALHOST_NAMES.append(socket.getfqdn(socket.gethostname()))
    except Exception:
        pass


def decode(byte_data, encoding="utf-8"):
    """
    Decode the byte data to a string if not None.

##
# As of STOMP 1.2, lines can end with either line feed, or carriage return plus line feed. 
#
PREAMBLE_END_RE = re.compile('\n\n|\r\n\r\n')

##
# As of STOMP 1.2, lines can end with either line feed, or carriage return plus line feed. 
    return byte_data.decode(encoding, errors="replace")


def encode(char_data, encoding="utf-8"):
    """
    Encode the parameter as a byte string.

    :param char_data: the data to encode
    :param string encoding: character encoding

    :rtype: bytes
    """
    if type(char_data) is str:
        return char_data.encode(encoding, errors="replace")
    elif type(char_data) is bytes:
        return char_data
    else:
        raise TypeError("message should be a string or bytes, found %s" % type(char_data))


def pack(pieces=()):
    """
    Join a sequence of strings together.

    :param list pieces: list of strings

    :rtype: bytes
    """
    return b"".join(pieces)


def join(chars=()):
    """
    Join a sequence of characters into a string.

    :param bytes chars: list of chars

    :rtype: str
    """
    return b"".join(chars).decode()


def is_eol_default(c):
    return c == b"\x0a"


##
# Used to parse STOMP header lines in the format "key:value",
#
HEADER_LINE_RE = re.compile("(?P<key>[^:]+)[:](?P<value>.*)")

##
# As of STOMP 1.2, lines can end with either line feed, or carriage return plus line feed.
#
PREAMBLE_END_RE = re.compile(b"\n\n|\r\n\r\n")

##
# As of STOMP 1.2, lines can end with either line feed, or carriage return plus line feed.
#
LINE_END_RE = re.compile("\n|\r\n")

##
# Used to replace the "passcode" to be dumped in the transport log (at debug level)
#
PASSCODE_RE = re.compile(r"passcode:\s+[^\' ]+")

ENC_NEWLINE = encode("\n")
ENC_NULL = encode(NULL)

        preamble_end = mat.start()
    if preamble_end == -1:
        preamble_end = len(frame)
    preamble = frame[0:preamble_end]
    preamble_lines = LINE_END_RE.split(preamble)
    f.body = frame[preamble_end + 2:]

    """
    thread = threading.Thread(None, callback)
    thread.daemon = True  # Don't let thread prevent termination
    thread.start()
    return thread


def is_localhost(host_and_port):
    """
    Return 1 if the specified host+port is a member of the 'localhost' list of hosts, 2 if not (predominately used
    as a sort key.

    :param (str,int) host_and_port: tuple containing host and port

    :rtype: int
    """
    (host, _) = host_and_port
    if host in LOCALHOST_NAMES:
        return 1
    return 2


_HEADER_ESCAPES = {
    "\r": "\\r",
    "\n": "\\n",
    ":": "\\c",
    "\\": "\\\\",
}
_HEADER_UNESCAPES = dict((value, key) for (key, value) in _HEADER_ESCAPES.items())


def _unescape_header(matchobj):
    escaped = matchobj.group(0)
    unescaped = _HEADER_UNESCAPES.get(escaped)
    if not unescaped:
        # TODO: unknown escapes MUST be treated as fatal protocol error per spec
        unescaped = escaped
    return unescaped


def parse_headers(lines, offset=0):
    """
    Parse the headers in a STOMP response

    :param list(str) lines: the lines received in the message response
    :param int offset: the starting line number

    :rtype: dict(str,str)
    """
    headers = {}
    for header_line in lines[offset:]:
        header_match = HEADER_LINE_RE.match(header_line)
        if header_match:
            key = header_match.group("key")
            key = re.sub(r"\\.", _unescape_header, key)
            if key not in headers:
                value = header_match.group("value")
                value = re.sub(r"\\.", _unescape_header, value)
                headers[key] = value
    return headers


def parse_frame(frame):
    """
    Parse a STOMP frame into a Frame object.

    :param bytes frame: the frame received from the server (as a byte string)

    :rtype: Frame
    """
    mat = PREAMBLE_END_RE.search(frame)
    if mat:
        preamble_end = mat.start()
        body_start = mat.end()
    else:
        preamble_end = len(frame)
        body_start = preamble_end
    preamble = decode(frame[0:preamble_end])
    preamble_lines = LINE_END_RE.split(preamble)
    preamble_len = len(preamble_lines)
    body = frame[body_start:]

    # Skip any leading newlines
    first_line = 0
    while first_line < preamble_len and len(preamble_lines[first_line]) == 0:
        first_line += 1

    if first_line >= preamble_len:
        return None

    # Extract frame type/command
    cmd = preamble_lines[first_line]

    # Put headers into a key/value map
    headers = parse_headers(preamble_lines, first_line + 1)

    return Frame(cmd, headers, body)


def merge_headers(header_map_list):
    """
    Helper function for combining multiple header maps into one.

    :param list(dict) header_map_list: list of maps

    :rtype: dict
    """
    headers = {}
    for header_map in header_map_list:
        if header_map:
            headers.update(header_map)
    return headers


def clean_headers(headers):
    rtn = headers
    if "passcode" in headers:
        rtn = copy.copy(headers)
        rtn["passcode"] = "********"
    return rtn


# lines: lines returned from a call to convert_frames
def clean_lines(lines):
    return re.sub(PASSCODE_RE, "passcode:********", str(lines))


def calculate_heartbeats(shb, chb):
    """
    Given a heartbeat string from the server, and a heartbeat tuple from the client,
    calculate what the actual heartbeat settings should be.

    :param (str,str) shb: server heartbeat numbers
    :param (int,int) chb: client heartbeat numbers

    :rtype: (int,int)
    """
    (sx, sy) = shb
    (cx, cy) = chb
    x = 0
    y = 0
    if cx != 0 and sy != "0":
        x = max(cx, int(sy))
    if cy != 0 and sx != "0":
        y = max(cy, int(sx))
    return x, y


def convert_frame(frame):
    """
    Convert a frame to a list of lines separated by newlines.

    :param Frame frame: the Frame object to convert

    :rtype: list(str)
    """
    lines = []

    body = None
    if frame.body:
        body = encode(frame.body)

        if HDR_CONTENT_LENGTH in frame.headers:
            frame.headers[HDR_CONTENT_LENGTH] = len(body)

    if frame.cmd:
        lines.append(encode(frame.cmd))
        lines.append(ENC_NEWLINE)
    for key, vals in sorted(frame.headers.items()):
        if vals is None:
            continue
        if type(vals) != tuple:
            vals = (vals,)
        for val in vals:
            lines.append(encode("%s:%s\n" % (key, val)))
    lines.append(ENC_NEWLINE)
    if body:
        lines.append(body)

    if frame.cmd:
        lines.append(ENC_NULL)
    return lines


def length(s):
    """
    Null (none) safe length function.

    :param str s: the string to return length of (None allowed)

    :rtype: int
    """
    if s is not None:
        return len(s)
    return 0


class Frame(object):
    """
    A STOMP frame (or message).

    :param str cmd: the protocol command
    :param dict headers: a map of headers for the frame
    :param body: the content of the frame.
    """
    def __init__(self, cmd, headers=None, body=None):
        self.cmd = cmd
        self.headers = headers if headers is not None else {}
        self.original_headers = copy.copy(self.headers)
        self.body = body

    def __str__(self):
        return "{cmd=%s,headers=[%s],body=%s}" % (self.cmd, self.headers, self.body)


def get_uuid():
    return str(uuid.uuid4())


def get_errno(e):
    """
    Return the errno of an exception, or the first argument if errno is not available.

    :param Exception e: the exception object
    """
    try:
        return e.errno
    except AttributeError:
        return e.args[0]


HEARTBEAT_FRAME = Frame("heartbeat")

<MSG> Don't attempt to parse message payloads as UTF-8.

STOMP messages can legitimately be binary, which fails to decode
as UTF-8. On python 2, no decoding is attempted, but on python 3
the code attempts to decode the entire frame as UTF-8, which fails
if the binary data contains invalid UTF-8 sequences.

<DFF> @@ -7,7 +7,7 @@ try:
 except ImportError:
     import md5 as hashlib
 
-from stomp.backward import NULL
+from stomp.backward import NULL, decode
 
 ##@namespace stomp.utils
 # General utilities.
@@ -42,7 +42,7 @@ HEADER_LINE_RE = re.compile('(?P<key>[^:]+)[:](?P<value>.*)')
 ##
 # As of STOMP 1.2, lines can end with either line feed, or carriage return plus line feed. 
 #
-PREAMBLE_END_RE = re.compile('\n\n|\r\n\r\n')
+PREAMBLE_END_RE = re.compile(b'\n\n|\r\n\r\n')
 
 ##
 # As of STOMP 1.2, lines can end with either line feed, or carriage return plus line feed. 
@@ -118,7 +118,7 @@ def parse_frame(frame):
         preamble_end = mat.start()
     if preamble_end == -1:
         preamble_end = len(frame)
-    preamble = frame[0:preamble_end]
+    preamble = decode(frame[0:preamble_end])
     preamble_lines = LINE_END_RE.split(preamble)
     f.body = frame[preamble_end + 2:]
 

{'commit': 'diff --git a/stomp/transport.py b/stomp/transport.py\nindex ba2e0fa..71b99f3 100644\n--- a/stomp/transport.py\n+++ b/stomp/transport.py\n@@ -14,10 +14,7 @@ import warnings\n # STOMP protocol\n \n \n-try:\n-    from cStringIO import StringIO\n-except ImportError:\n-    from io import StringIO\n+from io import BytesIO\n \n try:\n     import ssl\n@@ -63,7 +60,7 @@ class BaseTransport(listener.Publisher):\n             (block) for the server to respond with that receipt-id\n             before continuing\n         """\n-        self.__recvbuf = \'\'\n+        self.__recvbuf = b\'\'\n         self.listeners = {}\n         self.running = False\n         self.blocking = None\n@@ -320,7 +317,7 @@ class BaseTransport(listener.Publisher):\n         """\n         Read the next frame(s) from the socket.\n         """\n-        fastbuf = StringIO()\n+        fastbuf = BytesIO()\n         while self.running:\n             try:\n                 try:\n@@ -328,16 +325,15 @@ class BaseTransport(listener.Publisher):\n                 except InterruptedException:\n                     log.debug("socket read interrupted, restarting")\n                     continue\n-                c = decode(c)\n             except Exception:\n                 _, e, _ = sys.exc_info()\n                 c = \'\'\n             if len(c) == 0:\n                 raise exception.ConnectionClosedException()\n             fastbuf.write(c)\n-            if \'\\x00\' in c:\n+            if b\'\\x00\' in c:\n                 break\n-            elif c == \'\\x0a\':\n+            elif c == b\'\\x0a\':\n                 # heartbeat (special case)\n                 return c\n         self.__recvbuf += fastbuf.getvalue()\n@@ -346,13 +342,13 @@ class BaseTransport(listener.Publisher):\n \n         if len(self.__recvbuf) > 0 and self.running:\n             while True:\n-                pos = self.__recvbuf.find(\'\\x00\')\n+                pos = self.__recvbuf.find(b\'\\x00\')\n \n                 if pos >= 0:\n                     frame = self.__recvbuf[0:pos]\n-                    preamble_end = frame.find(\'\\n\\n\')\n+                    preamble_end = frame.find(b\'\\n\\n\')\n                     if preamble_end >= 0:\n-                        content_length_match = Transport.__content_length_re.search(frame[0:preamble_end])\n+                        content_length_match = Transport.__content_length_re.search(decode(frame[0:preamble_end]))\n                         if content_length_match:\n                             content_length = int(content_length_match.group(\'value\'))\n                             content_offset = preamble_end + 2\ndiff --git a/stomp/utils.py b/stomp/utils.py\nindex f051f76..66d1413 100755\n--- a/stomp/utils.py\n+++ b/stomp/utils.py\n@@ -7,7 +7,7 @@ try:\n except ImportError:\n     import md5 as hashlib\n \n-from stomp.backward import NULL\n+from stomp.backward import NULL, decode\n \n ##@namespace stomp.utils\n # General utilities.\n@@ -42,7 +42,7 @@ HEADER_LINE_RE = re.compile(\'(?P<key>[^:]+)[:](?P<value>.*)\')\n ##\n # As of STOMP 1.2, lines can end with either line feed, or carriage return plus line feed. \n #\n-PREAMBLE_END_RE = re.compile(\'\\n\\n|\\r\\n\\r\\n\')\n+PREAMBLE_END_RE = re.compile(b\'\\n\\n|\\r\\n\\r\\n\')\n \n ##\n # As of STOMP 1.2, lines can end with either line feed, or carriage return plus line feed. \n@@ -118,7 +118,7 @@ def parse_frame(frame):\n         preamble_end = mat.start()\n     if preamble_end == -1:\n         preamble_end = len(frame)\n-    preamble = frame[0:preamble_end]\n+    preamble = decode(frame[0:preamble_end])\n     preamble_lines = LINE_END_RE.split(preamble)\n     f.body = frame[preamble_end + 2:]\n \n', 'commit_subject': "Don't attempt to parse message payloads as UTF-8.", 'repo_name': 'jasonrbriggs/stomp.py', 'committer': {'name': 'George Goldberg', 'email': '9fd8de5fc2a7c2c0d469b2fff1afde4e5def37ba@grundleborg.com', 'time_sec': '1428745174', 'tz_offset': '60', 'date': {'seconds': '1428745174'}}, 'commit_tree': 'c85f54a97d55225983426c1ed9c27846cb7089a7', 'commit_message': "Don't attempt to parse message payloads as UTF-8.\n\nSTOMP messages can legitimately be binary, which fails to decode\nas UTF-8. On python 2, no decoding is attempted, but on python 3\nthe code attempts to decode the entire frame as UTF-8, which fails\nif the binary data contains invalid UTF-8 sequences.\n", 'language_name': 'Python', 'license': 'apache-2.0', 'hunks': "@@ -7,7 +7,7 @@ try:\n except ImportError:\n     import md5 as hashlib\n \n-from stomp.backward import NULL\n+from stomp.backward import NULL, decode\n \n ##@namespace stomp.utils\n # General utilities.\n@@ -42,7 +42,7 @@ HEADER_LINE_RE = re.compile('(?P<key>[^:]+)[:](?P<value>.*)')\n ##\n # As of STOMP 1.2, lines can end with either line feed, or carriage return plus line feed. \n #\n-PREAMBLE_END_RE = re.compile('\\n\\n|\\r\\n\\r\\n')\n+PREAMBLE_END_RE = re.compile(b'\\n\\n|\\r\\n\\r\\n')\n \n ##\n # As of STOMP 1.2, lines can end with either line feed, or carriage return plus line feed. \n@@ -118,7 +118,7 @@ def parse_frame(frame):\n         preamble_end = mat.start()\n     if preamble_end == -1:\n         preamble_end = len(frame)\n-    preamble = frame[0:preamble_end]\n+    preamble = decode(frame[0:preamble_end])\n     preamble_lines = LINE_END_RE.split(preamble)\n     f.body = frame[preamble_end + 2:]\n \n", 'addition_count': 3, 'deletion_count': 3, 'src_file': 'a/stomp/utils.py', 'tgt_file': 'b/stomp/utils.py', 'file_extension': '.py', 'before_file': '"""General utility functions.\n"""\n\nimport copy\nimport os\nimport re\nexcept ImportError:\n    import md5 as hashlib\n\nfrom stomp.backward import NULL\n\n##@namespace stomp.utils\n# General utilities.\n# addresses) that refer to the local host (both loopback interface\n# and external interfaces).  This is used for determining\n# preferred targets.\nLOCALHOST_NAMES = ["localhost", "127.0.0.1"]\n\nNULL = b"\\x00"\n\nif not os.environ.get("STOMP_SKIP_HOSTNAME_SCAN"):\n    try:\n        LOCALHOST_NAMES.append(socket.gethostbyname(socket.gethostname()))\n    except Exception:\n        pass\n\n    try:\n        LOCALHOST_NAMES.append(socket.gethostname())\n    except Exception:\n        pass\n\n    try:\n        LOCALHOST_NAMES.append(socket.getfqdn(socket.gethostname()))\n    except Exception:\n        pass\n\n\ndef decode(byte_data, encoding="utf-8"):\n    """\n    Decode the byte data to a string if not None.\n\n##\n# As of STOMP 1.2, lines can end with either line feed, or carriage return plus line feed. \n#\nPREAMBLE_END_RE = re.compile(\'\\n\\n|\\r\\n\\r\\n\')\n\n##\n# As of STOMP 1.2, lines can end with either line feed, or carriage return plus line feed. \n    return byte_data.decode(encoding, errors="replace")\n\n\ndef encode(char_data, encoding="utf-8"):\n    """\n    Encode the parameter as a byte string.\n\n    :param char_data: the data to encode\n    :param string encoding: character encoding\n\n    :rtype: bytes\n    """\n    if type(char_data) is str:\n        return char_data.encode(encoding, errors="replace")\n    elif type(char_data) is bytes:\n        return char_data\n    else:\n        raise TypeError("message should be a string or bytes, found %s" % type(char_data))\n\n\ndef pack(pieces=()):\n    """\n    Join a sequence of strings together.\n\n    :param list pieces: list of strings\n\n    :rtype: bytes\n    """\n    return b"".join(pieces)\n\n\ndef join(chars=()):\n    """\n    Join a sequence of characters into a string.\n\n    :param bytes chars: list of chars\n\n    :rtype: str\n    """\n    return b"".join(chars).decode()\n\n\ndef is_eol_default(c):\n    return c == b"\\x0a"\n\n\n##\n# Used to parse STOMP header lines in the format "key:value",\n#\nHEADER_LINE_RE = re.compile("(?P<key>[^:]+)[:](?P<value>.*)")\n\n##\n# As of STOMP 1.2, lines can end with either line feed, or carriage return plus line feed.\n#\nPREAMBLE_END_RE = re.compile(b"\\n\\n|\\r\\n\\r\\n")\n\n##\n# As of STOMP 1.2, lines can end with either line feed, or carriage return plus line feed.\n#\nLINE_END_RE = re.compile("\\n|\\r\\n")\n\n##\n# Used to replace the "passcode" to be dumped in the transport log (at debug level)\n#\nPASSCODE_RE = re.compile(r"passcode:\\s+[^\\\' ]+")\n\nENC_NEWLINE = encode("\\n")\nENC_NULL = encode(NULL)\n\n        preamble_end = mat.start()\n    if preamble_end == -1:\n        preamble_end = len(frame)\n    preamble = frame[0:preamble_end]\n    preamble_lines = LINE_END_RE.split(preamble)\n    f.body = frame[preamble_end + 2:]\n\n    """\n    thread = threading.Thread(None, callback)\n    thread.daemon = True  # Don\'t let thread prevent termination\n    thread.start()\n    return thread\n\n\ndef is_localhost(host_and_port):\n    """\n    Return 1 if the specified host+port is a member of the \'localhost\' list of hosts, 2 if not (predominately used\n    as a sort key.\n\n    :param (str,int) host_and_port: tuple containing host and port\n\n    :rtype: int\n    """\n    (host, _) = host_and_port\n    if host in LOCALHOST_NAMES:\n        return 1\n    return 2\n\n\n_HEADER_ESCAPES = {\n    "\\r": "\\\\r",\n    "\\n": "\\\\n",\n    ":": "\\\\c",\n    "\\\\": "\\\\\\\\",\n}\n_HEADER_UNESCAPES = dict((value, key) for (key, value) in _HEADER_ESCAPES.items())\n\n\ndef _unescape_header(matchobj):\n    escaped = matchobj.group(0)\n    unescaped = _HEADER_UNESCAPES.get(escaped)\n    if not unescaped:\n        # TODO: unknown escapes MUST be treated as fatal protocol error per spec\n        unescaped = escaped\n    return unescaped\n\n\ndef parse_headers(lines, offset=0):\n    """\n    Parse the headers in a STOMP response\n\n    :param list(str) lines: the lines received in the message response\n    :param int offset: the starting line number\n\n    :rtype: dict(str,str)\n    """\n    headers = {}\n    for header_line in lines[offset:]:\n        header_match = HEADER_LINE_RE.match(header_line)\n        if header_match:\n            key = header_match.group("key")\n            key = re.sub(r"\\\\.", _unescape_header, key)\n            if key not in headers:\n                value = header_match.group("value")\n                value = re.sub(r"\\\\.", _unescape_header, value)\n                headers[key] = value\n    return headers\n\n\ndef parse_frame(frame):\n    """\n    Parse a STOMP frame into a Frame object.\n\n    :param bytes frame: the frame received from the server (as a byte string)\n\n    :rtype: Frame\n    """\n    mat = PREAMBLE_END_RE.search(frame)\n    if mat:\n        preamble_end = mat.start()\n        body_start = mat.end()\n    else:\n        preamble_end = len(frame)\n        body_start = preamble_end\n    preamble = decode(frame[0:preamble_end])\n    preamble_lines = LINE_END_RE.split(preamble)\n    preamble_len = len(preamble_lines)\n    body = frame[body_start:]\n\n    # Skip any leading newlines\n    first_line = 0\n    while first_line < preamble_len and len(preamble_lines[first_line]) == 0:\n        first_line += 1\n\n    if first_line >= preamble_len:\n        return None\n\n    # Extract frame type/command\n    cmd = preamble_lines[first_line]\n\n    # Put headers into a key/value map\n    headers = parse_headers(preamble_lines, first_line + 1)\n\n    return Frame(cmd, headers, body)\n\n\ndef merge_headers(header_map_list):\n    """\n    Helper function for combining multiple header maps into one.\n\n    :param list(dict) header_map_list: list of maps\n\n    :rtype: dict\n    """\n    headers = {}\n    for header_map in header_map_list:\n        if header_map:\n            headers.update(header_map)\n    return headers\n\n\ndef clean_headers(headers):\n    rtn = headers\n    if "passcode" in headers:\n        rtn = copy.copy(headers)\n        rtn["passcode"] = "********"\n    return rtn\n\n\n# lines: lines returned from a call to convert_frames\ndef clean_lines(lines):\n    return re.sub(PASSCODE_RE, "passcode:********", str(lines))\n\n\ndef calculate_heartbeats(shb, chb):\n    """\n    Given a heartbeat string from the server, and a heartbeat tuple from the client,\n    calculate what the actual heartbeat settings should be.\n\n    :param (str,str) shb: server heartbeat numbers\n    :param (int,int) chb: client heartbeat numbers\n\n    :rtype: (int,int)\n    """\n    (sx, sy) = shb\n    (cx, cy) = chb\n    x = 0\n    y = 0\n    if cx != 0 and sy != "0":\n        x = max(cx, int(sy))\n    if cy != 0 and sx != "0":\n        y = max(cy, int(sx))\n    return x, y\n\n\ndef convert_frame(frame):\n    """\n    Convert a frame to a list of lines separated by newlines.\n\n    :param Frame frame: the Frame object to convert\n\n    :rtype: list(str)\n    """\n    lines = []\n\n    body = None\n    if frame.body:\n        body = encode(frame.body)\n\n        if HDR_CONTENT_LENGTH in frame.headers:\n            frame.headers[HDR_CONTENT_LENGTH] = len(body)\n\n    if frame.cmd:\n        lines.append(encode(frame.cmd))\n        lines.append(ENC_NEWLINE)\n    for key, vals in sorted(frame.headers.items()):\n        if vals is None:\n            continue\n        if type(vals) != tuple:\n            vals = (vals,)\n        for val in vals:\n            lines.append(encode("%s:%s\\n" % (key, val)))\n    lines.append(ENC_NEWLINE)\n    if body:\n        lines.append(body)\n\n    if frame.cmd:\n        lines.append(ENC_NULL)\n    return lines\n\n\ndef length(s):\n    """\n    Null (none) safe length function.\n\n    :param str s: the string to return length of (None allowed)\n\n    :rtype: int\n    """\n    if s is not None:\n        return len(s)\n    return 0\n\n\nclass Frame(object):\n    """\n    A STOMP frame (or message).\n\n    :param str cmd: the protocol command\n    :param dict headers: a map of headers for the frame\n    :param body: the content of the frame.\n    """\n    def __init__(self, cmd, headers=None, body=None):\n        self.cmd = cmd\n        self.headers = headers if headers is not None else {}\n        self.original_headers = copy.copy(self.headers)\n        self.body = body\n\n    def __str__(self):\n        return "{cmd=%s,headers=[%s],body=%s}" % (self.cmd, self.headers, self.body)\n\n\ndef get_uuid():\n    return str(uuid.uuid4())\n\n\ndef get_errno(e):\n    """\n    Return the errno of an exception, or the first argument if errno is not available.\n\n    :param Exception e: the exception object\n    """\n    try:\n        return e.errno\n    except AttributeError:\n        return e.args[0]\n\n\nHEARTBEAT_FRAME = Frame("heartbeat")\n', 'message': "Don't attempt to parse message payloads as UTF-8.\n\nSTOMP messages can legitimately be binary, which fails to decode\nas UTF-8. On python 2, no decoding is attempted, but on python 3\nthe code attempts to decode the entire frame as UTF-8, which fails\nif the binary data contains invalid UTF-8 sequences.\n", 'author_name': 'George Goldberg', 'lang': 'py'}