From 7ef6005f3b9fec47944442bad1412c1387854661 Mon Sep 17 00:00:00 2001 From: zah Date: Sun, 18 May 2025 12:31:48 +0300 Subject: [PATCH] fix(tracer): sort object fields for stable traces --- README.md | 9 + src/trace.py | 39 +- tests/fixtures/array_sum.json | 62 +- tests/fixtures/calc.json | 36 ++ tests/fixtures/new_types.json | 1150 +++++++++++++++++++++++++++++++++ tests/programs/new_types.py | 14 + 6 files changed, 1295 insertions(+), 15 deletions(-) create mode 100644 tests/fixtures/new_types.json create mode 100644 tests/programs/new_types.py diff --git a/README.md b/README.md index 49365ea..d2c3530 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,15 @@ python trace.py however you probably want to use it in combination with CodeTracer, which would be released soon. +### Supported value types + +The tracer currently understands a subset of Python's built‑in types. It can +record integers, booleans, strings, lists, floats, tuples, byte strings, +complex numbers and simple objects (via ``__dict__`` introspection). Values that +don't fall in these categories are stored as raw strings. + +Dictionary and set types are not yet handled. + ## Future directions The current Python support is an unfinished prototype. We can finish it. In the future, it may be expanded to function in a way to similar to the more complete implementations, e.g. [Noir](https://github.com/blocksense-network/noir/tree/blocksense/tooling/tracer). diff --git a/src/trace.py b/src/trace.py index 0cdfcb1..905dcac 100644 --- a/src/trace.py +++ b/src/trace.py @@ -61,6 +61,10 @@ def _register_builtin_types(self) -> None: self._register_type(12, "Bool") self._register_type(9, "Symbol") self._register_type(24, "No type") + self._register_type(8, "Float") + self._register_type(27, "Tuple") + self._register_type(16, "Bytes") + self._register_type(6, "Complex") # -------------------------------------------------------------------- paths def _register_path(self, path: str) -> int: @@ -119,12 +123,18 @@ def _ensure_type(self, kind: int, name: str) -> int: return self.types[name] def _value(self, val: Any) -> Dict[str, Any]: - if isinstance(val, int): - return {"kind": "Int", "type_id": self.types["Integer"], "i": val} if isinstance(val, bool): return {"kind": "Bool", "type_id": self.types["Bool"], "b": val} + if isinstance(val, int): + return {"kind": "Int", "type_id": self.types["Integer"], "i": val} + if isinstance(val, float): + type_id = self._ensure_type(8, "Float") + return {"kind": "Float", "type_id": type_id, "f": val} if isinstance(val, str): return {"kind": "String", "type_id": self.types["String"], "text": val} + if isinstance(val, (bytes, bytearray)): + type_id = self._ensure_type(16, "Bytes") + return {"kind": "Raw", "type_id": type_id, "r": str(val)} if isinstance(val, list): type_id = self._ensure_type(0, "Array") return { @@ -133,8 +143,33 @@ def _value(self, val: Any) -> Dict[str, Any]: "elements": [self._value(v) for v in val], "is_slice": False, } + if isinstance(val, tuple): + type_id = self._ensure_type(27, "Tuple") + return { + "kind": "Tuple", + "type_id": type_id, + "elements": [self._value(v) for v in val], + } + if isinstance(val, complex): + type_id = self._ensure_type(6, "Complex") + return { + "kind": "Struct", + "type_id": type_id, + "field_values": [self._value(val.real), self._value(val.imag)], + } if val is None: return {"kind": "None", "type_id": self.types["No type"]} + if hasattr(val, "__dict__"): + type_id = self._ensure_type(6, val.__class__.__name__) + fields = [ + self._value(v) + for _, v in sorted(val.__dict__.items(), key=lambda kv: kv[0]) + ] + return { + "kind": "Struct", + "type_id": type_id, + "field_values": fields, + } type_id = self._ensure_type(16, "Object") return {"kind": "Raw", "type_id": type_id, "r": str(val)} diff --git a/tests/fixtures/array_sum.json b/tests/fixtures/array_sum.json index c52a7a6..5dcad5e 100644 --- a/tests/fixtures/array_sum.json +++ b/tests/fixtures/array_sum.json @@ -44,6 +44,42 @@ } } }, + { + "Type": { + "kind": 8, + "lang_type": "Float", + "specific_info": { + "kind": "None" + } + } + }, + { + "Type": { + "kind": 27, + "lang_type": "Tuple", + "specific_info": { + "kind": "None" + } + } + }, + { + "Type": { + "kind": 16, + "lang_type": "Bytes", + "specific_info": { + "kind": "None" + } + } + }, + { + "Type": { + "kind": 6, + "lang_type": "Complex", + "specific_info": { + "kind": "None" + } + } + }, { "Path": "" }, @@ -111,7 +147,7 @@ "variable_id": 0, "value": { "kind": "Sequence", - "type_id": 5, + "type_id": 9, "elements": [ { "kind": "Int", @@ -148,7 +184,7 @@ "variable_id": 0, "value": { "kind": "Sequence", - "type_id": 5, + "type_id": 9, "elements": [ { "kind": "Int", @@ -183,7 +219,7 @@ "variable_id": 0, "value": { "kind": "Sequence", - "type_id": 5, + "type_id": 9, "elements": [ { "kind": "Int", @@ -216,7 +252,7 @@ "variable_id": 0, "value": { "kind": "Sequence", - "type_id": 5, + "type_id": 9, "elements": [ { "kind": "Int", @@ -262,7 +298,7 @@ "variable_id": 0, "value": { "kind": "Sequence", - "type_id": 5, + "type_id": 9, "elements": [ { "kind": "Int", @@ -318,7 +354,7 @@ "variable_id": 0, "value": { "kind": "Sequence", - "type_id": 5, + "type_id": 9, "elements": [ { "kind": "Int", @@ -371,7 +407,7 @@ "variable_id": 0, "value": { "kind": "Sequence", - "type_id": 5, + "type_id": 9, "elements": [ { "kind": "Int", @@ -424,7 +460,7 @@ "variable_id": 0, "value": { "kind": "Sequence", - "type_id": 5, + "type_id": 9, "elements": [ { "kind": "Int", @@ -477,7 +513,7 @@ "variable_id": 0, "value": { "kind": "Sequence", - "type_id": 5, + "type_id": 9, "elements": [ { "kind": "Int", @@ -530,7 +566,7 @@ "variable_id": 0, "value": { "kind": "Sequence", - "type_id": 5, + "type_id": 9, "elements": [ { "kind": "Int", @@ -583,7 +619,7 @@ "variable_id": 0, "value": { "kind": "Sequence", - "type_id": 5, + "type_id": 9, "elements": [ { "kind": "Int", @@ -636,7 +672,7 @@ "variable_id": 0, "value": { "kind": "Sequence", - "type_id": 5, + "type_id": 9, "elements": [ { "kind": "Int", @@ -718,7 +754,7 @@ "variable_id": 0, "value": { "kind": "Sequence", - "type_id": 5, + "type_id": 9, "elements": [ { "kind": "Int", diff --git a/tests/fixtures/calc.json b/tests/fixtures/calc.json index fdd9f7d..eb46799 100644 --- a/tests/fixtures/calc.json +++ b/tests/fixtures/calc.json @@ -44,6 +44,42 @@ } } }, + { + "Type": { + "kind": 8, + "lang_type": "Float", + "specific_info": { + "kind": "None" + } + } + }, + { + "Type": { + "kind": 27, + "lang_type": "Tuple", + "specific_info": { + "kind": "None" + } + } + }, + { + "Type": { + "kind": 16, + "lang_type": "Bytes", + "specific_info": { + "kind": "None" + } + } + }, + { + "Type": { + "kind": 6, + "lang_type": "Complex", + "specific_info": { + "kind": "None" + } + } + }, { "Path": "" }, diff --git a/tests/fixtures/new_types.json b/tests/fixtures/new_types.json new file mode 100644 index 0000000..a64b38c --- /dev/null +++ b/tests/fixtures/new_types.json @@ -0,0 +1,1150 @@ +[ + { + "Type": { + "kind": 7, + "lang_type": "Integer", + "specific_info": { + "kind": "None" + } + } + }, + { + "Type": { + "kind": 9, + "lang_type": "String", + "specific_info": { + "kind": "None" + } + } + }, + { + "Type": { + "kind": 12, + "lang_type": "Bool", + "specific_info": { + "kind": "None" + } + } + }, + { + "Type": { + "kind": 9, + "lang_type": "Symbol", + "specific_info": { + "kind": "None" + } + } + }, + { + "Type": { + "kind": 24, + "lang_type": "No type", + "specific_info": { + "kind": "None" + } + } + }, + { + "Type": { + "kind": 8, + "lang_type": "Float", + "specific_info": { + "kind": "None" + } + } + }, + { + "Type": { + "kind": 27, + "lang_type": "Tuple", + "specific_info": { + "kind": "None" + } + } + }, + { + "Type": { + "kind": 16, + "lang_type": "Bytes", + "specific_info": { + "kind": "None" + } + } + }, + { + "Type": { + "kind": 6, + "lang_type": "Complex", + "specific_info": { + "kind": "None" + } + } + }, + { + "Path": "" + }, + { + "Function": { + "path_id": 0, + "line": 1, + "name": "" + } + }, + { + "Call": { + "function_id": 0, + "args": [] + } + }, + { + "Path": "new_types.py" + }, + { + "Function": { + "path_id": 1, + "line": 1, + "name": "" + } + }, + { + "Call": { + "function_id": 1, + "args": [] + } + }, + { + "Step": { + "path_id": 1, + "line": 1 + } + }, + { + "Step": { + "path_id": 1, + "line": 14 + } + }, + { + "Function": { + "path_id": 1, + "line": 1, + "name": "demo" + } + }, + { + "Call": { + "function_id": 2, + "args": [] + } + }, + { + "Step": { + "path_id": 1, + "line": 2 + } + }, + { + "Function": { + "path_id": 1, + "line": 2, + "name": "MyObj" + } + }, + { + "Call": { + "function_id": 3, + "args": [] + } + }, + { + "Step": { + "path_id": 1, + "line": 2 + } + }, + { + "Step": { + "path_id": 1, + "line": 3 + } + }, + { + "Step": { + "path_id": 1, + "line": 3 + } + }, + { + "VariableName": "" + }, + { + "Value": { + "variable_id": 0, + "value": { + "kind": "None", + "type_id": 4 + } + } + }, + { + "Return": { + "return_value": { + "kind": "None", + "type_id": 4 + } + } + }, + { + "Step": { + "path_id": 1, + "line": 7 + } + }, + { + "VariableName": "MyObj" + }, + { + "Type": { + "kind": 6, + "lang_type": "type", + "specific_info": { + "kind": "None" + } + } + }, + { + "Type": { + "kind": 16, + "lang_type": "Object", + "specific_info": { + "kind": "None" + } + } + }, + { + "Type": { + "kind": 6, + "lang_type": "function", + "specific_info": { + "kind": "None" + } + } + }, + { + "Value": { + "variable_id": 1, + "value": { + "kind": "Struct", + "type_id": 9, + "field_values": [ + { + "kind": "Raw", + "type_id": 10, + "r": "" + }, + { + "kind": "None", + "type_id": 4 + }, + { + "kind": "Struct", + "type_id": 11, + "field_values": [] + }, + { + "kind": "String", + "type_id": 1, + "text": "__main__" + }, + { + "kind": "Raw", + "type_id": 10, + "r": "" + } + ] + } + } + }, + { + "Step": { + "path_id": 1, + "line": 8 + } + }, + { + "Value": { + "variable_id": 1, + "value": { + "kind": "Struct", + "type_id": 9, + "field_values": [ + { + "kind": "Raw", + "type_id": 10, + "r": "" + }, + { + "kind": "None", + "type_id": 4 + }, + { + "kind": "Struct", + "type_id": 11, + "field_values": [] + }, + { + "kind": "String", + "type_id": 1, + "text": "__main__" + }, + { + "kind": "Raw", + "type_id": 10, + "r": "" + } + ] + } + } + }, + { + "VariableName": "fl" + }, + { + "Value": { + "variable_id": 2, + "value": { + "kind": "Float", + "type_id": 5, + "f": 1.5 + } + } + }, + { + "Step": { + "path_id": 1, + "line": 9 + } + }, + { + "Value": { + "variable_id": 1, + "value": { + "kind": "Struct", + "type_id": 9, + "field_values": [ + { + "kind": "Raw", + "type_id": 10, + "r": "" + }, + { + "kind": "None", + "type_id": 4 + }, + { + "kind": "Struct", + "type_id": 11, + "field_values": [] + }, + { + "kind": "String", + "type_id": 1, + "text": "__main__" + }, + { + "kind": "Raw", + "type_id": 10, + "r": "" + } + ] + } + } + }, + { + "Value": { + "variable_id": 2, + "value": { + "kind": "Float", + "type_id": 5, + "f": 1.5 + } + } + }, + { + "VariableName": "tpl" + }, + { + "Value": { + "variable_id": 3, + "value": { + "kind": "Tuple", + "type_id": 6, + "elements": [ + { + "kind": "Int", + "type_id": 0, + "i": 1 + }, + { + "kind": "Int", + "type_id": 0, + "i": 2 + }, + { + "kind": "Int", + "type_id": 0, + "i": 3 + } + ] + } + } + }, + { + "Step": { + "path_id": 1, + "line": 10 + } + }, + { + "Value": { + "variable_id": 1, + "value": { + "kind": "Struct", + "type_id": 9, + "field_values": [ + { + "kind": "Raw", + "type_id": 10, + "r": "" + }, + { + "kind": "None", + "type_id": 4 + }, + { + "kind": "Struct", + "type_id": 11, + "field_values": [] + }, + { + "kind": "String", + "type_id": 1, + "text": "__main__" + }, + { + "kind": "Raw", + "type_id": 10, + "r": "" + } + ] + } + } + }, + { + "Value": { + "variable_id": 2, + "value": { + "kind": "Float", + "type_id": 5, + "f": 1.5 + } + } + }, + { + "Value": { + "variable_id": 3, + "value": { + "kind": "Tuple", + "type_id": 6, + "elements": [ + { + "kind": "Int", + "type_id": 0, + "i": 1 + }, + { + "kind": "Int", + "type_id": 0, + "i": 2 + }, + { + "kind": "Int", + "type_id": 0, + "i": 3 + } + ] + } + } + }, + { + "VariableName": "bb" + }, + { + "Value": { + "variable_id": 4, + "value": { + "kind": "Raw", + "type_id": 7, + "r": "b'xy'" + } + } + }, + { + "Step": { + "path_id": 1, + "line": 11 + } + }, + { + "Value": { + "variable_id": 1, + "value": { + "kind": "Struct", + "type_id": 9, + "field_values": [ + { + "kind": "Raw", + "type_id": 10, + "r": "" + }, + { + "kind": "None", + "type_id": 4 + }, + { + "kind": "Struct", + "type_id": 11, + "field_values": [] + }, + { + "kind": "String", + "type_id": 1, + "text": "__main__" + }, + { + "kind": "Raw", + "type_id": 10, + "r": "" + } + ] + } + } + }, + { + "Value": { + "variable_id": 2, + "value": { + "kind": "Float", + "type_id": 5, + "f": 1.5 + } + } + }, + { + "Value": { + "variable_id": 3, + "value": { + "kind": "Tuple", + "type_id": 6, + "elements": [ + { + "kind": "Int", + "type_id": 0, + "i": 1 + }, + { + "kind": "Int", + "type_id": 0, + "i": 2 + }, + { + "kind": "Int", + "type_id": 0, + "i": 3 + } + ] + } + } + }, + { + "Value": { + "variable_id": 4, + "value": { + "kind": "Raw", + "type_id": 7, + "r": "b'xy'" + } + } + }, + { + "VariableName": "cmplx" + }, + { + "Value": { + "variable_id": 5, + "value": { + "kind": "Struct", + "type_id": 8, + "field_values": [ + { + "kind": "Float", + "type_id": 5, + "f": 1.0 + }, + { + "kind": "Float", + "type_id": 5, + "f": 2.0 + } + ] + } + } + }, + { + "Function": { + "path_id": 1, + "line": 3, + "name": "__init__" + } + }, + { + "VariableName": "self" + }, + { + "Type": { + "kind": 6, + "lang_type": "MyObj", + "specific_info": { + "kind": "None" + } + } + }, + { + "Call": { + "function_id": 4, + "args": [ + { + "variable_id": 6, + "value": { + "kind": "Struct", + "type_id": 12, + "field_values": [] + } + } + ] + } + }, + { + "Step": { + "path_id": 1, + "line": 4 + } + }, + { + "Value": { + "variable_id": 6, + "value": { + "kind": "Struct", + "type_id": 12, + "field_values": [] + } + } + }, + { + "Step": { + "path_id": 1, + "line": 5 + } + }, + { + "Value": { + "variable_id": 6, + "value": { + "kind": "Struct", + "type_id": 12, + "field_values": [ + { + "kind": "Int", + "type_id": 0, + "i": 10 + } + ] + } + } + }, + { + "Step": { + "path_id": 1, + "line": 5 + } + }, + { + "Value": { + "variable_id": 6, + "value": { + "kind": "Struct", + "type_id": 12, + "field_values": [ + { + "kind": "String", + "type_id": 1, + "text": "hi" + }, + { + "kind": "Int", + "type_id": 0, + "i": 10 + } + ] + } + } + }, + { + "Value": { + "variable_id": 0, + "value": { + "kind": "None", + "type_id": 4 + } + } + }, + { + "Return": { + "return_value": { + "kind": "None", + "type_id": 4 + } + } + }, + { + "Step": { + "path_id": 1, + "line": 12 + } + }, + { + "Value": { + "variable_id": 1, + "value": { + "kind": "Struct", + "type_id": 9, + "field_values": [ + { + "kind": "Raw", + "type_id": 10, + "r": "" + }, + { + "kind": "None", + "type_id": 4 + }, + { + "kind": "Struct", + "type_id": 11, + "field_values": [] + }, + { + "kind": "String", + "type_id": 1, + "text": "__main__" + }, + { + "kind": "Raw", + "type_id": 10, + "r": "" + } + ] + } + } + }, + { + "Value": { + "variable_id": 2, + "value": { + "kind": "Float", + "type_id": 5, + "f": 1.5 + } + } + }, + { + "Value": { + "variable_id": 3, + "value": { + "kind": "Tuple", + "type_id": 6, + "elements": [ + { + "kind": "Int", + "type_id": 0, + "i": 1 + }, + { + "kind": "Int", + "type_id": 0, + "i": 2 + }, + { + "kind": "Int", + "type_id": 0, + "i": 3 + } + ] + } + } + }, + { + "Value": { + "variable_id": 4, + "value": { + "kind": "Raw", + "type_id": 7, + "r": "b'xy'" + } + } + }, + { + "Value": { + "variable_id": 5, + "value": { + "kind": "Struct", + "type_id": 8, + "field_values": [ + { + "kind": "Float", + "type_id": 5, + "f": 1.0 + }, + { + "kind": "Float", + "type_id": 5, + "f": 2.0 + } + ] + } + } + }, + { + "VariableName": "obj" + }, + { + "Value": { + "variable_id": 7, + "value": { + "kind": "Struct", + "type_id": 12, + "field_values": [ + { + "kind": "String", + "type_id": 1, + "text": "hi" + }, + { + "kind": "Int", + "type_id": 0, + "i": 10 + } + ] + } + } + }, + { + "Step": { + "path_id": 1, + "line": 12 + } + }, + { + "Value": { + "variable_id": 1, + "value": { + "kind": "Struct", + "type_id": 9, + "field_values": [ + { + "kind": "Raw", + "type_id": 10, + "r": "" + }, + { + "kind": "None", + "type_id": 4 + }, + { + "kind": "Struct", + "type_id": 11, + "field_values": [] + }, + { + "kind": "String", + "type_id": 1, + "text": "__main__" + }, + { + "kind": "Raw", + "type_id": 10, + "r": "" + } + ] + } + } + }, + { + "Value": { + "variable_id": 2, + "value": { + "kind": "Float", + "type_id": 5, + "f": 1.5 + } + } + }, + { + "Value": { + "variable_id": 3, + "value": { + "kind": "Tuple", + "type_id": 6, + "elements": [ + { + "kind": "Int", + "type_id": 0, + "i": 1 + }, + { + "kind": "Int", + "type_id": 0, + "i": 2 + }, + { + "kind": "Int", + "type_id": 0, + "i": 3 + } + ] + } + } + }, + { + "Value": { + "variable_id": 4, + "value": { + "kind": "Raw", + "type_id": 7, + "r": "b'xy'" + } + } + }, + { + "Value": { + "variable_id": 5, + "value": { + "kind": "Struct", + "type_id": 8, + "field_values": [ + { + "kind": "Float", + "type_id": 5, + "f": 1.0 + }, + { + "kind": "Float", + "type_id": 5, + "f": 2.0 + } + ] + } + } + }, + { + "Value": { + "variable_id": 7, + "value": { + "kind": "Struct", + "type_id": 12, + "field_values": [ + { + "kind": "String", + "type_id": 1, + "text": "hi" + }, + { + "kind": "Int", + "type_id": 0, + "i": 10 + } + ] + } + } + }, + { + "Value": { + "variable_id": 0, + "value": { + "kind": "Tuple", + "type_id": 6, + "elements": [ + { + "kind": "Float", + "type_id": 5, + "f": 1.5 + }, + { + "kind": "Tuple", + "type_id": 6, + "elements": [ + { + "kind": "Int", + "type_id": 0, + "i": 1 + }, + { + "kind": "Int", + "type_id": 0, + "i": 2 + }, + { + "kind": "Int", + "type_id": 0, + "i": 3 + } + ] + }, + { + "kind": "Raw", + "type_id": 7, + "r": "b'xy'" + }, + { + "kind": "Struct", + "type_id": 8, + "field_values": [ + { + "kind": "Float", + "type_id": 5, + "f": 1.0 + }, + { + "kind": "Float", + "type_id": 5, + "f": 2.0 + } + ] + }, + { + "kind": "Struct", + "type_id": 12, + "field_values": [ + { + "kind": "String", + "type_id": 1, + "text": "hi" + }, + { + "kind": "Int", + "type_id": 0, + "i": 10 + } + ] + } + ] + } + } + }, + { + "Return": { + "return_value": { + "kind": "Tuple", + "type_id": 6, + "elements": [ + { + "kind": "Float", + "type_id": 5, + "f": 1.5 + }, + { + "kind": "Tuple", + "type_id": 6, + "elements": [ + { + "kind": "Int", + "type_id": 0, + "i": 1 + }, + { + "kind": "Int", + "type_id": 0, + "i": 2 + }, + { + "kind": "Int", + "type_id": 0, + "i": 3 + } + ] + }, + { + "kind": "Raw", + "type_id": 7, + "r": "b'xy'" + }, + { + "kind": "Struct", + "type_id": 8, + "field_values": [ + { + "kind": "Float", + "type_id": 5, + "f": 1.0 + }, + { + "kind": "Float", + "type_id": 5, + "f": 2.0 + } + ] + }, + { + "kind": "Struct", + "type_id": 12, + "field_values": [ + { + "kind": "String", + "type_id": 1, + "text": "hi" + }, + { + "kind": "Int", + "type_id": 0, + "i": 10 + } + ] + } + ] + } + } + }, + { + "Step": { + "path_id": 1, + "line": 14 + } + }, + { + "Value": { + "variable_id": 0, + "value": { + "kind": "None", + "type_id": 4 + } + } + }, + { + "Return": { + "return_value": { + "kind": "None", + "type_id": 4 + } + } + } +] diff --git a/tests/programs/new_types.py b/tests/programs/new_types.py new file mode 100644 index 0000000..f7b4f9f --- /dev/null +++ b/tests/programs/new_types.py @@ -0,0 +1,14 @@ +def demo(): + class MyObj: + def __init__(self): + self.x = 10 + self.msg = "hi" + + fl = 1.5 + tpl = (1, 2, 3) + bb = b'xy' + cmplx = 1 + 2j + obj = MyObj() + return fl, tpl, bb, cmplx, obj + +demo()