Skip to content

Commit

Permalink
fix(serializer): Add support for byterray and memoryview built-in…
Browse files Browse the repository at this point in the history
… types (#1833)

Both `bytearray` and `memoryview` built-in types weren't explicitly
mentioned in the serializer logic, and as they are subtyping Sequence,
this led their instances to be enumerated upon, and to be output as a
list of bytes, byte per byte.

In the case of `memoryview`, this could also lead to a segmentation
fault if the memory referenced was already freed and unavailable to the
process by then.

By explicitly adding them as seralizable types, bytearray will be
decoded as a string just like bytes, and memoryview will use its
__repr__ method instead.

Close GH-1829

Co-authored-by: Thomas Dehghani <thomas.dehghani@imc.com>
  • Loading branch information
Tarty and Thomas Dehghani committed Jan 12, 2023
1 parent 20c25f2 commit c6d7b67
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 4 deletions.
2 changes: 2 additions & 0 deletions sentry_sdk/_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
number_types = (int, long, float) # noqa
int_types = (int, long) # noqa
iteritems = lambda x: x.iteritems() # noqa: B301
binary_sequence_types = (bytearray, memoryview)

def implements_str(cls):
# type: (T) -> T
Expand All @@ -44,6 +45,7 @@ def implements_str(cls):
number_types = (int, float) # type: Tuple[type, type]
int_types = (int,)
iteritems = lambda x: x.items()
binary_sequence_types = (bytes, bytearray, memoryview)

def implements_str(x):
# type: (T) -> T
Expand Down
15 changes: 11 additions & 4 deletions sentry_sdk/serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,14 @@

import sentry_sdk.utils

from sentry_sdk._compat import text_type, PY2, string_types, number_types, iteritems
from sentry_sdk._compat import (
text_type,
PY2,
string_types,
number_types,
iteritems,
binary_sequence_types,
)

from sentry_sdk._types import MYPY

Expand Down Expand Up @@ -47,15 +54,15 @@
# https://github.com/python/cpython/blob/master/Lib/collections/__init__.py#L49
from collections import Mapping, Sequence, Set

serializable_str_types = string_types
serializable_str_types = string_types + binary_sequence_types

else:
# New in 3.3
# https://docs.python.org/3/library/collections.abc.html
from collections.abc import Mapping, Sequence, Set

# Bytes are technically not strings in Python 3, but we can serialize them
serializable_str_types = (str, bytes)
serializable_str_types = string_types + binary_sequence_types


# Maximum length of JSON-serialized event payloads that can be safely sent
Expand Down Expand Up @@ -350,7 +357,7 @@ def _serialize_node_impl(
if should_repr_strings:
obj = safe_repr(obj)
else:
if isinstance(obj, bytes):
if isinstance(obj, bytes) or isinstance(obj, bytearray):
obj = obj.decode("utf-8", "replace")

if not isinstance(obj, string_types):
Expand Down
20 changes: 20 additions & 0 deletions tests/test_serializer.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import re
import sys
import pytest

Expand Down Expand Up @@ -62,6 +63,25 @@ def test_bytes_serialization_repr(message_normalizer):
assert result == r"b'abc123\x80\xf0\x9f\x8d\x95'"


def test_bytearray_serialization_decode(message_normalizer):
binary = bytearray(b"abc123\x80\xf0\x9f\x8d\x95")
result = message_normalizer(binary, should_repr_strings=False)
assert result == "abc123\ufffd\U0001f355"


@pytest.mark.xfail(sys.version_info < (3,), reason="Known safe_repr bugs in Py2.7")
def test_bytearray_serialization_repr(message_normalizer):
binary = bytearray(b"abc123\x80\xf0\x9f\x8d\x95")
result = message_normalizer(binary, should_repr_strings=True)
assert result == r"bytearray(b'abc123\x80\xf0\x9f\x8d\x95')"


def test_memoryview_serialization_repr(message_normalizer):
binary = memoryview(b"abc123\x80\xf0\x9f\x8d\x95")
result = message_normalizer(binary, should_repr_strings=False)
assert re.match(r"^<memory at 0x\w+>$", result)


def test_serialize_sets(extra_normalizer):
result = extra_normalizer({1, 2, 3})
assert result == [1, 2, 3]
Expand Down

0 comments on commit c6d7b67

Please sign in to comment.