@@ -96,7 +96,8 @@ 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+ # With repr() fallback, objects without custom __repr__ show full repr including module and memory address
100+ assert b"<__main__.UnserializableObject object at" in output
100101 assert b"'my_password': '$$_posthog_redacted_based_on_masking_rules_$$'" in output
101102 assert b"'__should_be_ignored':" not in output
102103
@@ -332,3 +333,78 @@ def process_data():
332333 assert '"code_variables":' not in output
333334 assert "'my_var'" not in output
334335 assert "'important_value'" not in output
336+
337+
338+ def test_code_variables_repr_fallback (tmpdir ):
339+ """Test that repr() is used for variables that can't be JSON-serialized but can be repr'd"""
340+ app = tmpdir .join ("app.py" )
341+ app .write (
342+ dedent (
343+ """
344+ import os
345+ import re
346+ from datetime import datetime, timedelta
347+ from decimal import Decimal
348+ from fractions import Fraction
349+ from posthog import Posthog
350+
351+ class CustomReprClass:
352+ def __repr__(self):
353+ return '<CustomReprClass: custom representation>'
354+
355+ posthog = Posthog(
356+ 'phc_x',
357+ host='https://eu.i.posthog.com',
358+ debug=True,
359+ enable_exception_autocapture=True,
360+ capture_exception_code_variables=True,
361+ project_root=os.path.dirname(os.path.abspath(__file__))
362+ )
363+
364+ def trigger_error():
365+ my_regex = re.compile(r'\\ d+')
366+ my_datetime = datetime(2024, 1, 15, 10, 30, 45)
367+ my_timedelta = timedelta(days=5, hours=3)
368+ my_decimal = Decimal('123.456')
369+ my_fraction = Fraction(3, 4)
370+ my_set = {1, 2, 3}
371+ my_frozenset = frozenset([4, 5, 6])
372+ my_bytes = b'hello bytes'
373+ my_bytearray = bytearray(b'mutable bytes')
374+ my_memoryview = memoryview(b'memory view')
375+ my_complex = complex(3, 4)
376+ my_range = range(10)
377+ my_custom = CustomReprClass()
378+ my_lambda = lambda x: x * 2
379+ my_function = trigger_error
380+
381+ 1/0
382+
383+ trigger_error()
384+ """
385+ )
386+ )
387+
388+ with pytest .raises (subprocess .CalledProcessError ) as excinfo :
389+ subprocess .check_output ([sys .executable , str (app )], stderr = subprocess .STDOUT )
390+
391+ output = excinfo .value .output .decode ("utf-8" )
392+
393+ assert "ZeroDivisionError" in output
394+ assert "code_variables" in output
395+
396+ assert "re.compile(" in output and "\\ \\ d+" in output
397+ assert "datetime.datetime(2024, 1, 15, 10, 30, 45)" in output
398+ assert "datetime.timedelta(days=5, seconds=10800)" in output
399+ assert "Decimal('123.456')" in output
400+ assert "Fraction(3, 4)" in output
401+ assert "{1, 2, 3}" in output
402+ assert "frozenset({4, 5, 6})" in output
403+ assert "b'hello bytes'" in output
404+ assert "bytearray(b'mutable bytes')" in output
405+ assert "<memory at" in output
406+ assert "(3+4j)" in output
407+ assert "range(0, 10)" in output
408+ assert "<CustomReprClass: custom representation>" in output
409+ assert "<lambda>" in output
410+ assert "<function trigger_error at" in output
0 commit comments