blob: b8f95fc62a246228ec3f568e25380dc2fa253bdf [file] [log] [blame]
Tor Norbye3a2425a2013-11-04 10:16:08 -08001""" pydevd_vars deals with variables:
2 resolution/conversion to XML.
3"""
4import pickle
5from django_frame import DjangoTemplateFrame
6from pydevd_constants import * #@UnusedWildImport
7from types import * #@UnusedWildImport
8
9from pydevd_xml import *
10
11try:
12 from StringIO import StringIO
13except ImportError:
14 from io import StringIO
15import sys #@Reimport
16
17if USE_LIB_COPY:
18 import _pydev_threading as threading
19else:
20 import threading
21import pydevd_resolver
22import traceback
23
24try:
25 from pydevd_exec import Exec
26except:
27 from pydevd_exec2 import Exec
28
29#-------------------------------------------------------------------------- defining true and false for earlier versions
30
31try:
32 __setFalse = False
33except:
34 import __builtin__
35
36 setattr(__builtin__, 'True', 1)
37 setattr(__builtin__, 'False', 0)
38
39#------------------------------------------------------------------------------------------------------ class for errors
40
41class VariableError(RuntimeError): pass
42
43class FrameNotFoundError(RuntimeError): pass
44
45
46if USE_PSYCO_OPTIMIZATION:
47 try:
48 import psyco
49
50 varToXML = psyco.proxy(varToXML)
51 except ImportError:
52 if hasattr(sys, 'exc_clear'): #jython does not have it
53 sys.exc_clear() #don't keep the traceback -- clients don't want to see it
54
55def iterFrames(initialFrame):
56 """NO-YIELD VERSION: Iterates through all the frames starting at the specified frame (which will be the first returned item)"""
57 #cannot use yield
58 frames = []
59
60 while initialFrame is not None:
61 frames.append(initialFrame)
62 initialFrame = initialFrame.f_back
63
64 return frames
65
66def dumpFrames(thread_id):
67 sys.stdout.write('dumping frames\n')
68 if thread_id != GetThreadId(threading.currentThread()):
69 raise VariableError("findFrame: must execute on same thread")
70
71 curFrame = GetFrame()
72 for frame in iterFrames(curFrame):
73 sys.stdout.write('%s\n' % pickle.dumps(frame))
74
75
76#===============================================================================
77# AdditionalFramesContainer
78#===============================================================================
79class AdditionalFramesContainer:
80 lock = threading.Lock()
81 additional_frames = {} #dict of dicts
82
83
84def addAdditionalFrameById(thread_id, frames_by_id):
85 AdditionalFramesContainer.additional_frames[thread_id] = frames_by_id
86
87
88def removeAdditionalFrameById(thread_id):
89 del AdditionalFramesContainer.additional_frames[thread_id]
90
91
92
93def findFrame(thread_id, frame_id):
94 """ returns a frame on the thread that has a given frame_id """
95 if thread_id != GetThreadId(threading.currentThread()):
96 raise VariableError("findFrame: must execute on same thread")
97
98 lookingFor = int(frame_id)
99
100 if AdditionalFramesContainer.additional_frames:
101 if DictContains(AdditionalFramesContainer.additional_frames, thread_id):
102 frame = AdditionalFramesContainer.additional_frames[thread_id].get(lookingFor)
103
104 if frame is not None:
105 return frame
106
107 curFrame = GetFrame()
108 if frame_id == "*":
109 return curFrame # any frame is specified with "*"
110
111 frameFound = None
112
113 for frame in iterFrames(curFrame):
114 if lookingFor == id(frame):
115 frameFound = frame
116 del frame
117 break
118
119 del frame
120
121 #Important: python can hold a reference to the frame from the current context
122 #if an exception is raised, so, if we don't explicitly add those deletes
123 #we might have those variables living much more than we'd want to.
124
125 #I.e.: sys.exc_info holding reference to frame that raises exception (so, other places
126 #need to call sys.exc_clear())
127 del curFrame
128
129 if frameFound is None:
130 msgFrames = ''
131 i = 0
132
133 for frame in iterFrames(GetFrame()):
134 i += 1
135 msgFrames += str(id(frame))
136 if i % 5 == 0:
137 msgFrames += '\n'
138 else:
139 msgFrames += ' - '
140
141 errMsg = '''findFrame: frame not found.
142Looking for thread_id:%s, frame_id:%s
143Current thread_id:%s, available frames:
144%s
145''' % (thread_id, lookingFor, GetThreadId(threading.currentThread()), msgFrames)
146
147 sys.stderr.write(errMsg)
148 return None
149
150 return frameFound
151
152def resolveCompoundVariable(thread_id, frame_id, scope, attrs):
153 """ returns the value of the compound variable as a dictionary"""
154 frame = findFrame(thread_id, frame_id)
155 if frame is None:
156 return {}
157
158 attrList = attrs.split('\t')
159
160 if scope == "GLOBAL":
161 var = frame.f_globals
162 del attrList[0] # globals are special, and they get a single dummy unused attribute
163 else:
164 var = frame.f_locals
165 type, _typeName, resolver = getType(var)
166 try:
167 resolver.resolve(var, attrList[0])
168 except:
169 var = frame.f_globals
170
171 for k in attrList:
172 type, _typeName, resolver = getType(var)
173 var = resolver.resolve(var, k)
174
175 try:
176 type, _typeName, resolver = getType(var)
177 return resolver.getDictionary(var)
178 except:
179 traceback.print_exc()
180
181
182def resolveVar(var, attrs):
183 attrList = attrs.split('\t')
184
185 for k in attrList:
186 type, _typeName, resolver = getType(var)
187
188 var = resolver.resolve(var, k)
189
190 try:
191 type, _typeName, resolver = getType(var)
192 return resolver.getDictionary(var)
193 except:
194 traceback.print_exc()
195
196
197def evaluateExpression(thread_id, frame_id, expression, doExec):
198 """returns the result of the evaluated expression
199 @param doExec: determines if we should do an exec or an eval
200 """
201 frame = findFrame(thread_id, frame_id)
202 if frame is None:
203 return
204
205 expression = str(expression.replace('@LINE@', '\n'))
206
207
208 #Not using frame.f_globals because of https://sourceforge.net/tracker2/?func=detail&aid=2541355&group_id=85796&atid=577329
209 #(Names not resolved in generator expression in method)
210 #See message: http://mail.python.org/pipermail/python-list/2009-January/526522.html
211 updated_globals = {}
212 updated_globals.update(frame.f_globals)
213 updated_globals.update(frame.f_locals) #locals later because it has precedence over the actual globals
214
215 try:
216 if doExec:
217 try:
218 #try to make it an eval (if it is an eval we can print it, otherwise we'll exec it and
219 #it will have whatever the user actually did)
220 compiled = compile(expression, '<string>', 'eval')
221 except:
222 Exec(expression, updated_globals, frame.f_locals)
223 else:
224 result = eval(compiled, updated_globals, frame.f_locals)
225 if result is not None: #Only print if it's not None (as python does)
226 sys.stdout.write('%s\n' % (result,))
227 return
228
229 else:
230 result = None
231 try:
232 result = eval(expression, updated_globals, frame.f_locals)
233 except Exception:
234 s = StringIO()
235 traceback.print_exc(file=s)
236
237 result = s.getvalue()
238
239 try:
240 try:
241 etype, value, tb = sys.exc_info()
242 result = value
243 finally:
244 etype = value = tb = None
245 except:
246 pass
247
248 result = ExceptionOnEvaluate(result)
249
250 return result
251 finally:
252 #Should not be kept alive if an exception happens and this frame is kept in the stack.
253 del updated_globals
254 del frame
255
256def changeAttrExpression(thread_id, frame_id, attr, expression):
257 """Changes some attribute in a given frame.
258 @note: it will not (currently) work if we're not in the topmost frame (that's a python
259 deficiency -- and it appears that there is no way of making it currently work --
260 will probably need some change to the python internals)
261 """
262 frame = findFrame(thread_id, frame_id)
263 if frame is None:
264 return
265
266 if isinstance(frame, DjangoTemplateFrame):
267 result = eval(expression, frame.f_globals, frame.f_locals)
268 frame.changeVariable(attr, result)
269
270 try:
271 expression = expression.replace('@LINE@', '\n')
272 #tests (needs proposed patch in python accepted)
273 # if hasattr(frame, 'savelocals'):
274 # if attr in frame.f_locals:
275 # frame.f_locals[attr] = eval(expression, frame.f_globals, frame.f_locals)
276 # frame.savelocals()
277 # return
278 #
279 # elif attr in frame.f_globals:
280 # frame.f_globals[attr] = eval(expression, frame.f_globals, frame.f_locals)
281 # return
282
283
284 if attr[:7] == "Globals":
285 attr = attr[8:]
286 if attr in frame.f_globals:
287 frame.f_globals[attr] = eval(expression, frame.f_globals, frame.f_locals)
288 return frame.f_globals[attr]
289 else:
290 #default way (only works for changing it in the topmost frame)
291 result = eval(expression, frame.f_globals, frame.f_locals)
292 Exec('%s=%s' % (attr, expression), frame.f_globals, frame.f_locals)
293 return result
294
295
296 except Exception:
297 traceback.print_exc()
298
299
300
301
302