2
2
3
3
import ast
4
4
5
- from sentry_sdk import Hub
5
+ from sentry_sdk import Hub , serializer
6
6
from sentry_sdk ._types import MYPY
7
7
from sentry_sdk .integrations import Integration , DidNotEnable
8
8
from sentry_sdk .scope import add_global_event_processor
9
9
from sentry_sdk .utils import walk_exception_chain , iter_stacks
10
10
11
11
if MYPY :
12
- from typing import Optional , Dict , Any
12
+ from typing import Optional , Dict , Any , Tuple , List
13
13
from types import FrameType
14
14
15
15
from sentry_sdk ._types import Event , Hint
@@ -75,7 +75,9 @@ def add_executing_info(event, hint):
75
75
continue
76
76
77
77
for sentry_frame , tb in zip (sentry_frames , tbs ):
78
- sentry_frame ["vars" ].update (pure_eval_frame (tb .tb_frame ))
78
+ sentry_frame ["vars" ] = (
79
+ pure_eval_frame (tb .tb_frame ) or sentry_frame ["vars" ]
80
+ )
79
81
return event
80
82
81
83
@@ -89,16 +91,42 @@ def pure_eval_frame(frame):
89
91
if not statements :
90
92
return {}
91
93
92
- stmt = list (statements )[0 ]
94
+ scope = stmt = list (statements )[0 ]
93
95
while True :
94
96
# Get the parent first in case the original statement is already
95
97
# a function definition, e.g. if we're calling a decorator
96
98
# In that case we still want the surrounding scope, not that function
97
- stmt = stmt .parent
98
- if isinstance (stmt , (ast .FunctionDef , ast .ClassDef , ast .Module )):
99
+ scope = scope .parent
100
+ if isinstance (scope , (ast .FunctionDef , ast .ClassDef , ast .Module )):
99
101
break
100
102
101
103
evaluator = pure_eval .Evaluator .from_frame (frame )
102
- expressions = evaluator .interesting_expressions_grouped (stmt )
104
+ expressions = evaluator .interesting_expressions_grouped (scope )
105
+
106
+ def closeness (expression ):
107
+ # type: (Tuple[List[Any], Any]) -> int
108
+ # Prioritise expressions with a node closer to the statement executed
109
+ # without being after that statement
110
+ # A higher return value is better - the expression will appear
111
+ # earlier in the list of values and is less likely to be trimmed
112
+ nodes , _value = expression
113
+ nodes_before_stmt = [
114
+ node for node in nodes if node .first_token .startpos < stmt .last_token .endpos
115
+ ]
116
+ if nodes_before_stmt :
117
+ # The position of the last node before or in the statement
118
+ return max (node .first_token .startpos for node in nodes_before_stmt )
119
+ else :
120
+ # The position of the first node after the statement
121
+ # Negative means it's always lower priority than nodes that come before
122
+ # Less negative means closer to the statement and higher priority
123
+ return - min (node .first_token .startpos for node in nodes )
124
+
125
+ # This adds the first_token and last_token attributes to nodes
103
126
atok = source .asttokens ()
104
- return {atok .get_text (nodes [0 ]): value for nodes , value in expressions }
127
+
128
+ expressions .sort (key = closeness , reverse = True )
129
+ return {
130
+ atok .get_text (nodes [0 ]): value
131
+ for nodes , value in expressions [: serializer .MAX_DATABAG_BREADTH ]
132
+ }
0 commit comments