Merge remote-tracking branch 'origin/master' into numpy-array-view
[idea/community.git] / python / helpers / pydev / pydevd_vars.py
1 """ pydevd_vars deals with variables:
2     resolution/conversion to XML.
3 """
4 import pickle
5 from django_frame import DjangoTemplateFrame
6 from pydevd_constants import * #@UnusedWildImport
7 from types import * #@UnusedWildImport
8
9 from pydevd_custom_frames import getCustomFrame
10 from pydevd_xml import *
11 from _pydev_imps import _pydev_thread
12
13 try:
14     from StringIO import StringIO
15 except ImportError:
16     from io import StringIO
17 import sys #@Reimport
18
19 import _pydev_threading as threading
20 import traceback
21 import pydevd_save_locals
22 from pydev_imports import Exec, quote, execfile
23
24 try:
25     import types
26     frame_type = types.FrameType
27 except:
28     frame_type = type(sys._getframe())
29
30
31 #-------------------------------------------------------------------------- defining true and false for earlier versions
32
33 try:
34     __setFalse = False
35 except:
36     import __builtin__
37     setattr(__builtin__, 'True', 1)
38     setattr(__builtin__, 'False', 0)
39
40 #------------------------------------------------------------------------------------------------------ class for errors
41
42 class VariableError(RuntimeError):pass
43
44 class FrameNotFoundError(RuntimeError):pass
45
46 def iterFrames(initialFrame):
47     '''NO-YIELD VERSION: Iterates through all the frames starting at the specified frame (which will be the first returned item)'''
48     #cannot use yield
49     frames = []
50
51     while initialFrame is not None:
52         frames.append(initialFrame)
53         initialFrame = initialFrame.f_back
54
55     return frames
56
57 def dumpFrames(thread_id):
58     sys.stdout.write('dumping frames\n')
59     if thread_id != GetThreadId(threading.currentThread()):
60         raise VariableError("findFrame: must execute on same thread")
61
62     curFrame = GetFrame()
63     for frame in iterFrames(curFrame):
64         sys.stdout.write('%s\n' % pickle.dumps(frame))
65
66
67 #===============================================================================
68 # AdditionalFramesContainer
69 #===============================================================================
70 class AdditionalFramesContainer:
71     lock = _pydev_thread.allocate_lock()
72     additional_frames = {} #dict of dicts
73
74
75 def addAdditionalFrameById(thread_id, frames_by_id):
76     AdditionalFramesContainer.additional_frames[thread_id] = frames_by_id
77
78
79 def removeAdditionalFrameById(thread_id):
80     del AdditionalFramesContainer.additional_frames[thread_id]
81
82
83
84
85 def findFrame(thread_id, frame_id):
86     """ returns a frame on the thread that has a given frame_id """
87     try:
88         curr_thread_id = GetThreadId(threading.currentThread())
89         if thread_id != curr_thread_id :
90             try:
91                 return getCustomFrame(thread_id, frame_id)  #I.e.: thread_id could be a stackless frame id + thread_id.
92             except:
93                 pass
94
95             raise VariableError("findFrame: must execute on same thread (%s != %s)" % (thread_id, curr_thread_id))
96
97         lookingFor = int(frame_id)
98
99         if AdditionalFramesContainer.additional_frames:
100             if DictContains(AdditionalFramesContainer.additional_frames, thread_id):
101                 frame = AdditionalFramesContainer.additional_frames[thread_id].get(lookingFor)
102
103                 if frame is not None:
104                     return frame
105
106         curFrame = GetFrame()
107         if frame_id == "*":
108             return curFrame  # any frame is specified with "*"
109
110         frameFound = None
111
112         for frame in iterFrames(curFrame):
113             if lookingFor == id(frame):
114                 frameFound = frame
115                 del frame
116                 break
117
118             del frame
119
120         #Important: python can hold a reference to the frame from the current context
121         #if an exception is raised, so, if we don't explicitly add those deletes
122         #we might have those variables living much more than we'd want to.
123
124         #I.e.: sys.exc_info holding reference to frame that raises exception (so, other places
125         #need to call sys.exc_clear())
126         del curFrame
127
128         if frameFound is None:
129             msgFrames = ''
130             i = 0
131
132             for frame in iterFrames(GetFrame()):
133                 i += 1
134                 msgFrames += str(id(frame))
135                 if i % 5 == 0:
136                     msgFrames += '\n'
137                 else:
138                     msgFrames += '  -  '
139
140             errMsg = '''findFrame: frame not found.
141     Looking for thread_id:%s, frame_id:%s
142     Current     thread_id:%s, available frames:
143     %s\n
144     ''' % (thread_id, lookingFor, curr_thread_id, msgFrames)
145
146             sys.stderr.write(errMsg)
147             return None
148
149         return frameFound
150     except:
151         import traceback
152         traceback.print_exc()
153         return None
154
155 def getVariable(thread_id, frame_id, scope, attrs):
156     """
157     returns the value of a variable
158
159     :scope: can be BY_ID, EXPRESSION, GLOBAL, LOCAL, FRAME
160
161     BY_ID means we'll traverse the list of all objects alive to get the object.
162
163     :attrs: after reaching the proper scope, we have to get the attributes until we find
164             the proper location (i.e.: obj\tattr1\tattr2)
165
166     :note: when BY_ID is used, the frame_id is considered the id of the object to find and
167            not the frame (as we don't care about the frame in this case).
168     """
169     if scope == 'BY_ID':
170         if thread_id != GetThreadId(threading.currentThread()) :
171             raise VariableError("getVariable: must execute on same thread")
172
173         try:
174             import gc
175             objects = gc.get_objects()
176         except:
177             pass  #Not all python variants have it.
178         else:
179             frame_id = int(frame_id)
180             for var in objects:
181                 if id(var) == frame_id:
182                     if attrs is not None:
183                         attrList = attrs.split('\t')
184                         for k in attrList:
185                             _type, _typeName, resolver = getType(var)
186                             var = resolver.resolve(var, k)
187
188                     return var
189
190         #If it didn't return previously, we coudn't find it by id (i.e.: alrceady garbage collected).
191         sys.stderr.write('Unable to find object with id: %s\n' % (frame_id,))
192         return None
193
194     frame = findFrame(thread_id, frame_id)
195     if frame is None:
196         return {}
197
198     if attrs is not None:
199         attrList = attrs.split('\t')
200     else:
201         attrList = []
202
203     if scope == 'EXPRESSION':
204         for count in xrange(len(attrList)):
205             if count == 0:
206                 # An Expression can be in any scope (globals/locals), therefore it needs to evaluated as an expression
207                 var = evaluateExpression(thread_id, frame_id, attrList[count], False)
208             else:
209                 _type, _typeName, resolver = getType(var)
210                 var = resolver.resolve(var, attrList[count])
211     else:
212         if scope == "GLOBAL":
213             var = frame.f_globals
214             del attrList[0]  # globals are special, and they get a single dummy unused attribute
215         else:
216             # for a frame access both locals and globals as Python does
217             var = {}
218             var.update(frame.f_globals)
219             var.update(frame.f_locals)
220
221         for k in attrList:
222             _type, _typeName, resolver = getType(var)
223             var = resolver.resolve(var, k)
224
225     return var
226
227
228 def resolveCompoundVariable(thread_id, frame_id, scope, attrs):
229     """ returns the value of the compound variable as a dictionary"""
230
231     var = getVariable(thread_id, frame_id, scope, attrs)
232
233     try:
234         _type, _typeName, resolver = getType(var)
235         return resolver.getDictionary(var)
236     except:
237         sys.stderr.write('Error evaluating: thread_id: %s\nframe_id: %s\nscope: %s\nattrs: %s\n' % (
238             thread_id, frame_id, scope, attrs,))
239         traceback.print_exc()
240
241
242 def resolveVar(var, attrs):
243     attrList = attrs.split('\t')
244
245     for k in attrList:
246         type, _typeName, resolver = getType(var)
247
248         var = resolver.resolve(var, k)
249
250     try:
251         type, _typeName, resolver = getType(var)
252         return resolver.getDictionary(var)
253     except:
254         traceback.print_exc()
255
256
257 def customOperation(thread_id, frame_id, scope, attrs, style, code_or_file, operation_fn_name):
258     """
259     We'll execute the code_or_file and then search in the namespace the operation_fn_name to execute with the given var.
260
261     code_or_file: either some code (i.e.: from pprint import pprint) or a file to be executed.
262     operation_fn_name: the name of the operation to execute after the exec (i.e.: pprint)
263     """
264     expressionValue = getVariable(thread_id, frame_id, scope, attrs)
265
266     try:
267         namespace = {'__name__': '<customOperation>'}
268         if style == "EXECFILE":
269             namespace['__file__'] = code_or_file
270             execfile(code_or_file, namespace, namespace)
271         else:  # style == EXEC
272             namespace['__file__'] = '<customOperationCode>'
273             Exec(code_or_file, namespace, namespace)
274
275         return str(namespace[operation_fn_name](expressionValue))
276     except:
277         traceback.print_exc()
278
279
280 def evaluateExpression(thread_id, frame_id, expression, doExec):
281     '''returns the result of the evaluated expression
282     @param doExec: determines if we should do an exec or an eval
283     '''
284     frame = findFrame(thread_id, frame_id)
285     if frame is None:
286         return
287
288     expression = str(expression.replace('@LINE@', '\n'))
289
290
291     #Not using frame.f_globals because of https://sourceforge.net/tracker2/?func=detail&aid=2541355&group_id=85796&atid=577329
292     #(Names not resolved in generator expression in method)
293     #See message: http://mail.python.org/pipermail/python-list/2009-January/526522.html
294     updated_globals = {}
295     updated_globals.update(frame.f_globals)
296     updated_globals.update(frame.f_locals)  #locals later because it has precedence over the actual globals
297
298     try:
299
300         if doExec:
301             try:
302                 #try to make it an eval (if it is an eval we can print it, otherwise we'll exec it and
303                 #it will have whatever the user actually did)
304                 compiled = compile(expression, '<string>', 'eval')
305             except:
306                 Exec(expression, updated_globals, frame.f_locals)
307                 pydevd_save_locals.save_locals(frame)
308             else:
309                 result = eval(compiled, updated_globals, frame.f_locals)
310                 if result is not None:  #Only print if it's not None (as python does)
311                     sys.stdout.write('%s\n' % (result,))
312             return
313
314         else:
315             result = None
316             try:
317                 result = eval(expression, updated_globals, frame.f_locals)
318             except Exception:
319                 s = StringIO()
320                 traceback.print_exc(file=s)
321                 result = s.getvalue()
322
323                 try:
324                     try:
325                         etype, value, tb = sys.exc_info()
326                         result = value
327                     finally:
328                         etype = value = tb = None
329                 except:
330                     pass
331
332                 result = ExceptionOnEvaluate(result)
333
334                 # Ok, we have the initial error message, but let's see if we're dealing with a name mangling error...
335                 try:
336                     if '__' in expression:
337                         # Try to handle '__' name mangling...
338                         split = expression.split('.')
339                         curr = frame.f_locals.get(split[0])
340                         for entry in split[1:]:
341                             if entry.startswith('__') and not hasattr(curr, entry):
342                                 entry = '_%s%s' % (curr.__class__.__name__, entry)
343                             curr = getattr(curr, entry)
344
345                         result = curr
346                 except:
347                     pass
348
349
350             return result
351     finally:
352         #Should not be kept alive if an exception happens and this frame is kept in the stack.
353         del updated_globals
354         del frame
355
356 def changeAttrExpression(thread_id, frame_id, attr, expression):
357     '''Changes some attribute in a given frame.
358     '''
359     frame = findFrame(thread_id, frame_id)
360     if frame is None:
361         return
362
363     try:
364         expression = expression.replace('@LINE@', '\n')
365
366         # if isinstance(frame, DjangoTemplateFrame): # TODO: implemente for plugins
367         #     result = eval(expression, frame.f_globals, frame.f_locals)
368         #     frame.changeVariable(attr, result)
369         #     return result
370
371         if attr[:7] == "Globals":
372             attr = attr[8:]
373             if attr in frame.f_globals:
374                 frame.f_globals[attr] = eval(expression, frame.f_globals, frame.f_locals)
375                 return frame.f_globals[attr]
376         else:
377             if pydevd_save_locals.is_save_locals_available():
378                 frame.f_locals[attr] = eval(expression, frame.f_globals, frame.f_locals)
379                 pydevd_save_locals.save_locals(frame)
380                 return frame.f_locals[attr]
381
382             #default way (only works for changing it in the topmost frame)
383             result = eval(expression, frame.f_globals, frame.f_locals)
384             Exec('%s=%s' % (attr, expression), frame.f_globals, frame.f_locals)
385             return result
386
387
388     except Exception:
389         traceback.print_exc()
390
391
392
393
394