Skip to content

Commit 2b69aa6

Browse files
authored
feat: fix up sending of string types (#7)
* feat: fix up sending of string types * chore: fix tests under py3 * feat: also repr for numbers
1 parent 4639416 commit 2b69aa6

File tree

5 files changed

+39
-26
lines changed

5 files changed

+39
-26
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@ pip-log.txt
1717
venv
1818
.vscode/tags
1919
.pytest_cache
20+
.hypothesis

dev-requirements.txt

Lines changed: 0 additions & 5 deletions
This file was deleted.

sentry_sdk/utils.py

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -220,13 +220,7 @@ def skip_internal_frames(frame):
220220

221221
def safe_str(value):
222222
try:
223-
try:
224-
rv = text_type(value)
225-
except UnicodeError:
226-
rv = bytes(value)
227-
if isinstance(rv, bytes):
228-
rv = rv.decode('utf-8', 'replace')
229-
return rv
223+
return text_type(value)
230224
except Exception:
231225
return safe_repr(value)
232226

@@ -236,25 +230,22 @@ def safe_repr(value):
236230
rv = repr(value)
237231
if isinstance(rv, bytes):
238232
rv = rv.decode('utf-8', 'replace')
239-
return rv
233+
try:
234+
return rv.encode('utf-8').decode('unicode-escape')
235+
except Exception:
236+
return rv
240237
except Exception:
241-
return '<broken repr>'
238+
return u'<broken repr>'
242239

243240

244241
def object_to_json(obj):
245242
def _walk(obj, depth):
246-
if depth >= 4:
247-
return safe_repr(obj)
248-
if obj is None or obj is True or obj is False or \
249-
isinstance(obj, number_types) or isinstance(obj, text_type):
250-
return obj
251-
if isinstance(obj, bytes):
252-
return obj.decode('utf-8', 'replace')
253-
if isinstance(obj, Sequence):
254-
return [_walk(x, depth + 1) for x in obj]
255-
if isinstance(obj, Mapping):
256-
return {safe_repr(k): _walk(v, depth + 1) for k, v in
257-
obj.items()}
243+
if depth < 4:
244+
if isinstance(obj, Sequence):
245+
return [_walk(x, depth + 1) for x in obj]
246+
if isinstance(obj, Mapping):
247+
return {safe_repr(k): _walk(v, depth + 1) for k, v in
248+
obj.items()}
258249
return safe_repr(obj)
259250
return _walk(obj, 0)
260251

tests/test_utils.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from hypothesis import given, assume, settings
2+
import hypothesis.strategies as st
3+
4+
from sentry_sdk.utils import safe_repr
5+
from sentry_sdk._compat import PY2, text_type
6+
7+
any_string = st.one_of(st.binary(), st.text())
8+
9+
@given(x=any_string)
10+
@settings(max_examples=1000)
11+
def test_safe_repr_never_broken_for_strings(x):
12+
r = safe_repr(x)
13+
assert isinstance(r, text_type)
14+
assert u'broken repr' not in r
15+
16+
@given(x=any_string)
17+
@settings(max_examples=1000)
18+
def test_safe_repr_never_leaves_escapes_in(x):
19+
if isinstance(x, bytes):
20+
assume(b'\\u' not in x and b'\\x' not in x)
21+
else:
22+
assume(u'\\u' not in x and u'\\x' not in x)
23+
r = safe_repr(x)
24+
assert isinstance(r, text_type)
25+
assert u'\\u' not in r and u'\\x' not in r

tox.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ envlist =
2626
[testenv]
2727
deps =
2828
pytest
29+
hypothesis
2930
django-{16,17,18}: pytest-django<3.0
3031
django-{19,110,110,111,200,dev}: pytest-django>=3.0,<3.3
3132
django-{18,19,110}: django-tastypie==0.14

0 commit comments

Comments
 (0)