@tracecode/harness 0.6.2 → 0.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -31,9 +31,12 @@ import sys
31
31
  import json
32
32
  import math
33
33
  import ast
34
+ from typing import *
34
35
  import builtins as _builtins
35
36
  ${deps.PYTHON_CLASS_DEFINITIONS_SNIPPET}
36
37
 
38
+ _TRACECODE_TYPING_GLOBALS = {name for name in globals().keys() if not name.startswith('_')}
39
+
37
40
  _trace_data = []
38
41
  _console_output = []
39
42
  _original_print = _builtins.print
@@ -69,14 +72,16 @@ ${deps.PYTHON_TRACE_SERIALIZE_FUNCTION_SNIPPET}
69
72
 
70
73
  _call_stack = []
71
74
  _pending_accesses = {}
75
+ _last_trace_index_by_frame = {}
72
76
  _prev_hashmap_snapshots = {}
73
77
  _TRACE_MUTATING_METHODS = {'append', 'appendleft', 'pop', 'popleft', 'extend', 'insert'}
74
- _internal_funcs = {'_serialize', '_tracer', '_custom_print', '_dict_to_tree', '_dict_to_list', '_is_structural_constructor_frame', '_snapshot_call_stack', '_snapshot_locals', '_stable_token', '_looks_like_adjacency_list', '_looks_like_indexed_adjacency_list', '_extract_hashmap_snapshot', '_classify_runtime_object_kind', '_infer_hashmap_delta', '_clear_frame_hashmap_snapshots', '_build_runtime_visualization', '_resolve_inplace_result', '__tracecode_record_access', '__tracecode_flush_accesses', '__tracecode_normalize_indices', '__tracecode_make_access_event', '__tracecode_read_value', '__tracecode_write_value', '__tracecode_apply_augmented_value', '_tracecode_read_index', '_tracecode_write_index', '_tracecode_augassign_index', '_tracecode_mutating_call', '_tracecode_is_pure_literal_scaffold', '_tracecode_collect_collapsed_literal_lines', '__tracecode_attach_parents', '_tracecode_extract_named_subscript', '__TracecodeAccessTransformer', '__tracecode_compile_user_code', '<listcomp>', '<dictcomp>', '<setcomp>', '<genexpr>'}
78
+ _internal_funcs = {'_serialize', '_tracer', '_custom_print', '_dict_to_tree', '_dict_to_list', '_is_structural_constructor_frame', '_snapshot_call_stack', '_snapshot_locals', '_stable_token', '_looks_like_adjacency_list', '_looks_like_indexed_adjacency_list', '_extract_hashmap_snapshot', '_classify_runtime_object_kind', '_infer_hashmap_delta', '_clear_frame_hashmap_snapshots', '_build_runtime_visualization', '_resolve_inplace_result', '__tracecode_record_access', '__tracecode_flush_accesses', '__tracecode_append_trace_step', '__tracecode_attach_accesses_to_previous_step', '__tracecode_normalize_indices', '__tracecode_make_access_event', '__tracecode_read_value', '__tracecode_write_value', '__tracecode_apply_augmented_value', '_tracecode_read_index', '_tracecode_write_index', '_tracecode_augassign_index', '_tracecode_mutating_call', '_tracecode_mutating_index_call', '_tracecode_is_pure_literal_scaffold', '_tracecode_collect_collapsed_literal_lines', '__tracecode_attach_parents', '_tracecode_extract_named_subscript', '__TracecodeAccessTransformer', '__tracecode_compile_user_code', '<listcomp>', '<dictcomp>', '<setcomp>', '<genexpr>'}
75
79
  _internal_locals = {
76
80
  '_trace_data', '_console_output', '_original_print', '_target_function',
77
81
  '_MIRROR_PRINT_TO_WORKER_CONSOLE', '_MINIMAL_TRACE', '_SKIP_SENTINEL',
78
82
  '_SCRIPT_MODE', '_TRACE_INPUT_NAMES', '_SCRIPT_PRE_USER_GLOBALS',
79
- '_call_stack', '_pending_accesses', '_prev_hashmap_snapshots', '_TRACE_MUTATING_METHODS', '_internal_funcs', '_internal_locals', '_max_trace_steps',
83
+ '_TRACECODE_TYPING_GLOBALS',
84
+ '_call_stack', '_pending_accesses', '_last_trace_index_by_frame', '_prev_hashmap_snapshots', '_TRACE_MUTATING_METHODS', '_internal_funcs', '_internal_locals', '_max_trace_steps',
80
85
  '_trace_limit_exceeded', '_timeout_reason', '_total_line_events', '_max_line_events',
81
86
  '_line_hit_count', '_max_single_line_hits', '_infinite_loop_line',
82
87
  '_MAX_SERIALIZE_DEPTH', '_trace_failed', '_inplace',
@@ -84,10 +89,11 @@ _internal_locals = {
84
89
  '_is_structural_constructor_frame', '_snapshot_call_stack', '_snapshot_locals', '_stable_token',
85
90
  '_looks_like_adjacency_list', '_looks_like_indexed_adjacency_list', '_extract_hashmap_snapshot', '_classify_runtime_object_kind', '_infer_hashmap_delta',
86
91
  '_clear_frame_hashmap_snapshots', '_build_runtime_visualization', '_resolve_inplace_result',
87
- '__tracecode_record_access', '__tracecode_flush_accesses', '__tracecode_normalize_indices',
92
+ '__tracecode_record_access', '__tracecode_flush_accesses', '__tracecode_append_trace_step',
93
+ '__tracecode_attach_accesses_to_previous_step', '__tracecode_normalize_indices',
88
94
  '__tracecode_make_access_event', '__tracecode_read_value', '__tracecode_write_value',
89
95
  '__tracecode_apply_augmented_value', '_tracecode_read_index', '_tracecode_write_index',
90
- '_tracecode_augassign_index', '_tracecode_mutating_call', '_tracecode_collapsed_literal_lines',
96
+ '_tracecode_augassign_index', '_tracecode_mutating_call', '_tracecode_mutating_index_call', '_tracecode_collapsed_literal_lines',
91
97
  '_tracecode_is_pure_literal_scaffold', '_tracecode_collect_collapsed_literal_lines', '__tracecode_attach_parents',
92
98
  '_tracecode_extract_named_subscript', '__TracecodeAccessTransformer', '__tracecode_compile_user_code',
93
99
  '_InfiniteLoopDetected', '_tb', '_result', '_exc_type', '_exc_msg', '_exc_tb',
@@ -381,6 +387,27 @@ def __tracecode_flush_accesses(frame):
381
387
  return []
382
388
  return _pending_accesses.pop(id(frame), [])
383
389
 
390
+ def __tracecode_append_trace_step(frame, step):
391
+ _trace_data.append(step)
392
+ if frame is not None:
393
+ _last_trace_index_by_frame[id(frame)] = len(_trace_data) - 1
394
+
395
+ def __tracecode_attach_accesses_to_previous_step(frame):
396
+ accesses = __tracecode_flush_accesses(frame)
397
+ if not accesses:
398
+ return []
399
+ frame_key = id(frame)
400
+ previous_index = _last_trace_index_by_frame.get(frame_key)
401
+ if previous_index is not None and 0 <= previous_index < len(_trace_data):
402
+ previous_step = _trace_data[previous_index]
403
+ existing_accesses = previous_step.get('accesses')
404
+ if isinstance(existing_accesses, list):
405
+ existing_accesses.extend(accesses)
406
+ else:
407
+ previous_step['accesses'] = accesses
408
+ return []
409
+ return accesses
410
+
384
411
  def __tracecode_normalize_indices(indices, max_depth=2):
385
412
  if not isinstance(indices, (list, tuple)) or len(indices) == 0 or len(indices) > max_depth:
386
413
  return None
@@ -509,6 +536,18 @@ def _tracecode_mutating_call(var_name, container, method_name, *args, **kwargs):
509
536
  )
510
537
  return result
511
538
 
539
+ def _tracecode_mutating_index_call(var_name, container, indices, method_name, *args, **kwargs):
540
+ effective_indices = list(indices)
541
+ target = __tracecode_read_value(container, effective_indices)
542
+ result = getattr(target, method_name)(*args, **kwargs)
543
+ normalized = __tracecode_normalize_indices(effective_indices)
544
+ if method_name in _TRACE_MUTATING_METHODS:
545
+ __tracecode_record_access(
546
+ sys._getframe(1),
547
+ __tracecode_make_access_event(var_name, 'mutating-call', normalized, method_name),
548
+ )
549
+ return result
550
+
512
551
  def __tracecode_attach_parents(node, parent=None):
513
552
  for child in ast.iter_child_nodes(node):
514
553
  setattr(child, '__trace_parent__', node)
@@ -609,6 +648,25 @@ class __TracecodeAccessTransformer(ast.NodeTransformer):
609
648
  return ast.copy_location(ast.Expr(value=call), node)
610
649
 
611
650
  def visit_Call(self, node):
651
+ if isinstance(node.func, ast.Attribute):
652
+ method_name = node.func.attr
653
+ if method_name in _TRACE_MUTATING_METHODS:
654
+ extracted = _tracecode_extract_named_subscript(node.func.value)
655
+ if extracted is not None:
656
+ var_name, indices = extracted
657
+ call = ast.Call(
658
+ func=ast.Name(id='_tracecode_mutating_index_call', ctx=ast.Load()),
659
+ args=[
660
+ ast.Constant(value=var_name),
661
+ ast.Name(id=var_name, ctx=ast.Load()),
662
+ ast.List(elts=[self.visit(index) for index in indices], ctx=ast.Load()),
663
+ ast.Constant(value=method_name),
664
+ *[self.visit(arg) for arg in node.args],
665
+ ],
666
+ keywords=[self.visit(keyword) for keyword in node.keywords],
667
+ )
668
+ return ast.copy_location(call, node)
669
+
612
670
  node = self.generic_visit(node)
613
671
  if isinstance(node.func, ast.Attribute) and isinstance(node.func.value, ast.Name):
614
672
  method_name = node.func.attr
@@ -919,6 +977,7 @@ def _tracer(frame, event, arg):
919
977
 
920
978
  # Fast counter for any loops
921
979
  if event == 'line':
980
+ __tracecode_attach_accesses_to_previous_step(frame)
922
981
  if frame.f_code.co_filename == '<user_code>' and frame.f_lineno in _tracecode_collapsed_literal_lines:
923
982
  return _tracer
924
983
  _total_line_events += 1
@@ -929,14 +988,14 @@ def _tracer(frame, event, arg):
929
988
  _trace_limit_exceeded = True
930
989
  _timeout_reason = 'line-limit'
931
990
  _infinite_loop_line = frame.f_lineno
932
- _trace_data.append({
991
+ __tracecode_append_trace_step(frame, {
933
992
  'line': frame.f_lineno,
934
993
  'event': 'timeout',
935
994
  'variables': {'timeoutReason': _timeout_reason},
936
995
  'function': func_name,
937
996
  'callStack': _snapshot_call_stack(),
938
997
  'stdoutLineCount': len(_console_output),
939
- 'accesses': __tracecode_flush_accesses(frame),
998
+ 'accesses': [],
940
999
  })
941
1000
  sys.settrace(None)
942
1001
  raise _InfiniteLoopDetected(f"Exceeded {_max_line_events} line events")
@@ -951,7 +1010,7 @@ def _tracer(frame, event, arg):
951
1010
  _infinite_loop_line = frame.f_lineno
952
1011
  local_vars, local_sources = _snapshot_locals(frame, with_sources=True)
953
1012
  local_vars['timeoutReason'] = _timeout_reason
954
- _trace_data.append({
1013
+ __tracecode_append_trace_step(frame, {
955
1014
  'line': frame.f_lineno,
956
1015
  'event': 'timeout',
957
1016
  'variables': local_vars,
@@ -959,7 +1018,7 @@ def _tracer(frame, event, arg):
959
1018
  'function': func_name,
960
1019
  'callStack': _snapshot_call_stack(),
961
1020
  'stdoutLineCount': len(_console_output),
962
- 'accesses': __tracecode_flush_accesses(frame),
1021
+ 'accesses': [],
963
1022
  'visualization': _build_runtime_visualization(local_vars, frame)
964
1023
  })
965
1024
  sys.settrace(None)
@@ -971,14 +1030,15 @@ def _tracer(frame, event, arg):
971
1030
  _trace_limit_exceeded = True
972
1031
  _timeout_reason = 'trace-limit'
973
1032
  _infinite_loop_line = frame.f_lineno
974
- _trace_data.append({
1033
+ __tracecode_attach_accesses_to_previous_step(frame)
1034
+ __tracecode_append_trace_step(frame, {
975
1035
  'line': frame.f_lineno,
976
1036
  'event': 'timeout',
977
1037
  'variables': {'timeoutReason': _timeout_reason},
978
1038
  'function': func_name,
979
1039
  'callStack': _snapshot_call_stack(),
980
1040
  'stdoutLineCount': len(_console_output),
981
- 'accesses': __tracecode_flush_accesses(frame),
1041
+ 'accesses': [],
982
1042
  })
983
1043
  sys.settrace(None)
984
1044
  raise _InfiniteLoopDetected(f"Exceeded {_max_trace_steps} trace steps")
@@ -993,7 +1053,7 @@ def _tracer(frame, event, arg):
993
1053
  })
994
1054
  if _MINIMAL_TRACE:
995
1055
  return _tracer
996
- _trace_data.append({
1056
+ __tracecode_append_trace_step(frame, {
997
1057
  'line': frame.f_lineno,
998
1058
  'event': 'call',
999
1059
  'variables': local_vars,
@@ -1008,7 +1068,7 @@ def _tracer(frame, event, arg):
1008
1068
  if _MINIMAL_TRACE:
1009
1069
  return _tracer
1010
1070
  local_vars, local_sources = _snapshot_locals(frame, with_sources=True)
1011
- _trace_data.append({
1071
+ __tracecode_append_trace_step(frame, {
1012
1072
  'line': frame.f_lineno,
1013
1073
  'event': event,
1014
1074
  'variables': local_vars,
@@ -1016,13 +1076,14 @@ def _tracer(frame, event, arg):
1016
1076
  'function': func_name,
1017
1077
  'callStack': _snapshot_call_stack(),
1018
1078
  'stdoutLineCount': len(_console_output),
1019
- 'accesses': __tracecode_flush_accesses(frame),
1079
+ 'accesses': [],
1020
1080
  'visualization': _build_runtime_visualization(local_vars, frame)
1021
1081
  })
1022
1082
  elif event == 'return':
1083
+ __tracecode_attach_accesses_to_previous_step(frame)
1023
1084
  if not _MINIMAL_TRACE:
1024
1085
  local_vars, local_sources = _snapshot_locals(frame, with_sources=True)
1025
- _trace_data.append({
1086
+ __tracecode_append_trace_step(frame, {
1026
1087
  'line': frame.f_lineno,
1027
1088
  'event': 'return',
1028
1089
  'variables': local_vars,
@@ -1031,11 +1092,12 @@ def _tracer(frame, event, arg):
1031
1092
  'returnValue': _serialize(arg),
1032
1093
  'callStack': _snapshot_call_stack(),
1033
1094
  'stdoutLineCount': len(_console_output),
1034
- 'accesses': __tracecode_flush_accesses(frame),
1095
+ 'accesses': [],
1035
1096
  'visualization': _build_runtime_visualization(local_vars, frame)
1036
1097
  })
1037
1098
  _clear_frame_hashmap_snapshots(frame)
1038
1099
  _pending_accesses.pop(id(frame), None)
1100
+ _last_trace_index_by_frame.pop(id(frame), None)
1039
1101
  if _call_stack and _call_stack[-1]['function'] == func_name:
1040
1102
  _call_stack.pop()
1041
1103
 
@@ -1046,7 +1108,7 @@ def _tracer(frame, event, arg):
1046
1108
  _real_globals = __builtins__['globals'] if isinstance(__builtins__, dict) else getattr(__builtins__, 'globals')
1047
1109
  _real_list = __builtins__['list'] if isinstance(__builtins__, dict) else getattr(__builtins__, 'list')
1048
1110
  _globals_dict = _real_globals()
1049
- _preserve = {"TreeNode", "ListNode", 'sys', 'json', 'math', 'ast', 'print', '__builtins__', '__name__', '__doc__', '__package__', '__loader__', '__spec__'}
1111
+ _preserve = {"TreeNode", "ListNode", 'sys', 'json', 'math', 'ast', 'print', '__builtins__', '__name__', '__doc__', '__package__', '__loader__', '__spec__'} | _TRACECODE_TYPING_GLOBALS
1050
1112
  for _k in _real_list(_globals_dict.keys()):
1051
1113
  if not _k.startswith('_') and _k not in _preserve:
1052
1114
  _globals_dict.pop(_k, None)