Skip to content

Commit f75c5ef

Browse files
feat: use repr in code variables (#372)
1 parent 65785b8 commit f75c5ef

File tree

4 files changed

+102
-13
lines changed

4 files changed

+102
-13
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# 7.0.1 - 2025-11-15
2+
3+
Try to use repr() when formatting code variables
4+
15
# 7.0.0 - 2025-11-11
26

37
NB Python 3.9 is no longer supported

posthog/exception_utils.py

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -969,19 +969,30 @@ def _serialize_variable_value(value, limiter, max_length=1024):
969969
return result
970970
except Exception:
971971
try:
972-
fallback = f"<{type(value).__name__}>"
973-
fallback_size = len(fallback)
974-
if not limiter.can_add(fallback_size):
972+
result = repr(value)
973+
if len(result) > max_length:
974+
result = result[: max_length - 3] + "..."
975+
976+
result_size = len(result)
977+
if not limiter.can_add(result_size):
975978
return None
976-
limiter.add(fallback_size)
977-
return fallback
979+
limiter.add(result_size)
980+
return result
978981
except Exception:
979-
fallback = "<unserializable object>"
980-
fallback_size = len(fallback)
981-
if not limiter.can_add(fallback_size):
982-
return None
983-
limiter.add(fallback_size)
984-
return fallback
982+
try:
983+
fallback = f"<{type(value).__name__}>"
984+
fallback_size = len(fallback)
985+
if not limiter.can_add(fallback_size):
986+
return None
987+
limiter.add(fallback_size)
988+
return fallback
989+
except Exception:
990+
fallback = "<unserializable object>"
991+
fallback_size = len(fallback)
992+
if not limiter.can_add(fallback_size):
993+
return None
994+
limiter.add(fallback_size)
995+
return fallback
985996

986997

987998
def _is_simple_type(value):

posthog/test/test_exception_capture.py

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ def process_data():
9696
assert b"'my_number': 42" in output
9797
assert b"'my_bool': 'True'" in output
9898
assert b'"my_dict": "{\\"name\\": \\"test\\", \\"value\\": 123}"' in output
99-
assert b'"my_obj": "<UnserializableObject>"' in output
99+
assert b"<__main__.UnserializableObject object at" in output
100100
assert b"'my_password': '$$_posthog_redacted_based_on_masking_rules_$$'" in output
101101
assert b"'__should_be_ignored':" not in output
102102

@@ -332,3 +332,77 @@ def process_data():
332332
assert '"code_variables":' not in output
333333
assert "'my_var'" not in output
334334
assert "'important_value'" not in output
335+
336+
337+
def test_code_variables_repr_fallback(tmpdir):
338+
app = tmpdir.join("app.py")
339+
app.write(
340+
dedent(
341+
"""
342+
import os
343+
import re
344+
from datetime import datetime, timedelta
345+
from decimal import Decimal
346+
from fractions import Fraction
347+
from posthog import Posthog
348+
349+
class CustomReprClass:
350+
def __repr__(self):
351+
return '<CustomReprClass: custom representation>'
352+
353+
posthog = Posthog(
354+
'phc_x',
355+
host='https://eu.i.posthog.com',
356+
debug=True,
357+
enable_exception_autocapture=True,
358+
capture_exception_code_variables=True,
359+
project_root=os.path.dirname(os.path.abspath(__file__))
360+
)
361+
362+
def trigger_error():
363+
my_regex = re.compile(r'\\d+')
364+
my_datetime = datetime(2024, 1, 15, 10, 30, 45)
365+
my_timedelta = timedelta(days=5, hours=3)
366+
my_decimal = Decimal('123.456')
367+
my_fraction = Fraction(3, 4)
368+
my_set = {1, 2, 3}
369+
my_frozenset = frozenset([4, 5, 6])
370+
my_bytes = b'hello bytes'
371+
my_bytearray = bytearray(b'mutable bytes')
372+
my_memoryview = memoryview(b'memory view')
373+
my_complex = complex(3, 4)
374+
my_range = range(10)
375+
my_custom = CustomReprClass()
376+
my_lambda = lambda x: x * 2
377+
my_function = trigger_error
378+
379+
1/0
380+
381+
trigger_error()
382+
"""
383+
)
384+
)
385+
386+
with pytest.raises(subprocess.CalledProcessError) as excinfo:
387+
subprocess.check_output([sys.executable, str(app)], stderr=subprocess.STDOUT)
388+
389+
output = excinfo.value.output.decode("utf-8")
390+
391+
assert "ZeroDivisionError" in output
392+
assert "code_variables" in output
393+
394+
assert "re.compile(" in output and "\\\\d+" in output
395+
assert "datetime.datetime(2024, 1, 15, 10, 30, 45)" in output
396+
assert "datetime.timedelta(days=5, seconds=10800)" in output
397+
assert "Decimal('123.456')" in output
398+
assert "Fraction(3, 4)" in output
399+
assert "{1, 2, 3}" in output
400+
assert "frozenset({4, 5, 6})" in output
401+
assert "b'hello bytes'" in output
402+
assert "bytearray(b'mutable bytes')" in output
403+
assert "<memory at" in output
404+
assert "(3+4j)" in output
405+
assert "range(0, 10)" in output
406+
assert "<CustomReprClass: custom representation>" in output
407+
assert "<lambda>" in output
408+
assert "<function trigger_error at" in output

posthog/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
VERSION = "7.0.0"
1+
VERSION = "7.0.1"
22

33
if __name__ == "__main__":
44
print(VERSION, end="") # noqa: T201

0 commit comments

Comments
 (0)