blob: 614549f74f3793261b9be8b4e12fceeb916fad01 [file] [log] [blame]
Tor Norbye3a2425a2013-11-04 10:16:08 -08001try:
2 import StringIO
3except:
4 import io as StringIO
5import traceback
Tor Norbyec667c1f2014-05-28 17:06:51 -07006from os.path import basename
Tor Norbye3a2425a2013-11-04 10:16:08 -08007
8try:
9 __setFalse = False
10except:
11 import __builtin__
12 setattr(__builtin__, 'True', 1)
13 setattr(__builtin__, 'False', 0)
14
15import pydevd_constants
16
17
18MAX_ITEMS_TO_HANDLE = 500
19TOO_LARGE_MSG = 'Too large to show contents. Max items to show: ' + str(MAX_ITEMS_TO_HANDLE)
20TOO_LARGE_ATTR = 'Unable to handle:'
21
22#=======================================================================================================================
23# UnableToResolveVariableException
24#=======================================================================================================================
25class UnableToResolveVariableException(Exception):
26 pass
27
28
29#=======================================================================================================================
30# InspectStub
31#=======================================================================================================================
32class InspectStub:
33 def isbuiltin(self, _args):
34 return False
35 def isroutine(self, object):
36 return False
37
38try:
39 import inspect
40except:
41 inspect = InspectStub()
42
43try:
44 import java.lang #@UnresolvedImport
45except:
46 pass
47
48#types does not include a MethodWrapperType
49try:
50 MethodWrapperType = type([].__str__)
51except:
52 MethodWrapperType = None
53
54
55#=======================================================================================================================
56# AbstractResolver
57#=======================================================================================================================
58class AbstractResolver:
59 '''
60 This class exists only for documentation purposes to explain how to create a resolver.
61
62 Some examples on how to resolve things:
63 - list: getDictionary could return a dict with index->item and use the index to resolve it later
64 - set: getDictionary could return a dict with id(object)->object and reiterate in that array to resolve it later
65 - arbitrary instance: getDictionary could return dict with attr_name->attr and use getattr to resolve it later
66 '''
67
68 def resolve(self, var, attribute):
69 '''
70 In this method, we'll resolve some child item given the string representation of the item in the key
71 representing the previously asked dictionary.
72
73 @param var: this is the actual variable to be resolved.
74 @param attribute: this is the string representation of a key previously returned in getDictionary.
75 '''
76 raise NotImplementedError
77
78 def getDictionary(self, var):
79 '''
80 @param var: this is the variable that should have its children gotten.
81
82 @return: a dictionary where each pair key, value should be shown to the user as children items
83 in the variables view for the given var.
84 '''
85 raise NotImplementedError
86
87
88#=======================================================================================================================
89# DefaultResolver
90#=======================================================================================================================
91class DefaultResolver:
92 '''
93 DefaultResolver is the class that'll actually resolve how to show some variable.
94 '''
95
96 def resolve(self, var, attribute):
97 return getattr(var, attribute)
98
99 def getDictionary(self, var):
100 if MethodWrapperType:
101 return self._getPyDictionary(var)
102 else:
103 return self._getJyDictionary(var)
104
105 def _getJyDictionary(self, obj):
106 ret = {}
107 found = java.util.HashMap()
108
109 original = obj
110 if hasattr(obj, '__class__') and obj.__class__ == java.lang.Class:
111
112 #get info about superclasses
113 classes = []
114 classes.append(obj)
115 c = obj.getSuperclass()
116 while c != None:
117 classes.append(c)
118 c = c.getSuperclass()
119
120 #get info about interfaces
121 interfs = []
122 for obj in classes:
123 interfs.extend(obj.getInterfaces())
124 classes.extend(interfs)
125
126 #now is the time when we actually get info on the declared methods and fields
127 for obj in classes:
128
129 declaredMethods = obj.getDeclaredMethods()
130 declaredFields = obj.getDeclaredFields()
131 for i in range(len(declaredMethods)):
132 name = declaredMethods[i].getName()
133 ret[name] = declaredMethods[i].toString()
134 found.put(name, 1)
135
136 for i in range(len(declaredFields)):
137 name = declaredFields[i].getName()
138 found.put(name, 1)
139 #if declaredFields[i].isAccessible():
140 declaredFields[i].setAccessible(True)
141 #ret[name] = declaredFields[i].get( declaredFields[i] )
142 try:
143 ret[name] = declaredFields[i].get(original)
144 except:
145 ret[name] = declaredFields[i].toString()
146
147 #this simple dir does not always get all the info, that's why we have the part before
148 #(e.g.: if we do a dir on String, some methods that are from other interfaces such as
149 #charAt don't appear)
150 try:
151 d = dir(original)
152 for name in d:
153 if found.get(name) is not 1:
154 ret[name] = getattr(original, name)
155 except:
156 #sometimes we're unable to do a dir
157 pass
158
159 return ret
160
161 def _getPyDictionary(self, var):
162 filterPrivate = False
163 filterSpecial = True
164 filterFunction = True
165 filterBuiltIn = True
166
167 names = dir(var)
168 if not names and hasattr(var, '__members__'):
169 names = var.__members__
170 d = {}
171
172 #Be aware that the order in which the filters are applied attempts to
173 #optimize the operation by removing as many items as possible in the
174 #first filters, leaving fewer items for later filters
175
176 if filterBuiltIn or filterFunction:
177 for n in names:
178 if filterSpecial:
179 if n.startswith('__') and n.endswith('__'):
180 continue
181
182 if filterPrivate:
183 if n.startswith('_') or n.endswith('__'):
184 continue
185
186 try:
187 attr = getattr(var, n)
188
189 #filter builtins?
190 if filterBuiltIn:
191 if inspect.isbuiltin(attr):
192 continue
193
194 #filter functions?
195 if filterFunction:
196 if inspect.isroutine(attr) or isinstance(attr, MethodWrapperType):
197 continue
198 except:
199 #if some error occurs getting it, let's put it to the user.
200 strIO = StringIO.StringIO()
201 traceback.print_exc(file=strIO)
202 attr = strIO.getvalue()
203
204 d[ n ] = attr
205
206 return d
207
208
209#=======================================================================================================================
210# DictResolver
211#=======================================================================================================================
212class DictResolver:
213
214 def resolve(self, dict, key):
215 if key == '__len__':
216 return None
217
218 if '(' not in key:
219 #we have to treat that because the dict resolver is also used to directly resolve the global and local
220 #scopes (which already have the items directly)
221 return dict[key]
222
223 #ok, we have to iterate over the items to find the one that matches the id, because that's the only way
224 #to actually find the reference from the string we have before.
225 expected_id = int(key.split('(')[-1][:-1])
226 for key, val in dict.items():
227 if id(key) == expected_id:
228 return val
229
230 raise UnableToResolveVariableException()
231
232 def keyStr(self, key):
233 if isinstance(key, str):
234 return "'%s'"%key
235 else:
236 if not pydevd_constants.IS_PY3K:
237 if isinstance(key, unicode):
238 return "u'%s'"%key
239 return key
240
241 def getDictionary(self, dict):
242 ret = {}
243
244 for key, val in dict.items():
245 #we need to add the id because otherwise we cannot find the real object to get its contents later on.
246 key = '%s (%s)' % (self.keyStr(key), id(key))
247 ret[key] = val
248
249 ret['__len__'] = len(dict)
250 return ret
251
252
253
254#=======================================================================================================================
255# TupleResolver
256#=======================================================================================================================
257class TupleResolver: #to enumerate tuples and lists
258
259 def resolve(self, var, attribute):
260 '''
261 @param var: that's the original attribute
262 @param attribute: that's the key passed in the dict (as a string)
263 '''
264 if attribute == '__len__' or attribute == TOO_LARGE_ATTR:
265 return None
266 return var[int(attribute)]
267
268 def getDictionary(self, var):
269 #return dict( [ (i, x) for i, x in enumerate(var) ] )
270 # modified 'cause jython does not have enumerate support
271 l = len(var)
272 d = {}
273
274 if l < MAX_ITEMS_TO_HANDLE:
275 format = '%0' + str(int(len(str(l)))) + 'd'
276
277
278 for i, item in zip(range(l), var):
279 d[ format % i ] = item
280 else:
281 d[TOO_LARGE_ATTR] = TOO_LARGE_MSG
282 d['__len__'] = len(var)
283 return d
284
285
286
287#=======================================================================================================================
288# SetResolver
289#=======================================================================================================================
290class SetResolver:
291 '''
292 Resolves a set as dict id(object)->object
293 '''
294
295 def resolve(self, var, attribute):
296 if attribute == '__len__':
297 return None
298
299 attribute = int(attribute)
300 for v in var:
301 if id(v) == attribute:
302 return v
303
304 raise UnableToResolveVariableException('Unable to resolve %s in %s' % (attribute, var))
305
306 def getDictionary(self, var):
307 d = {}
308 for item in var:
309 d[ id(item) ] = item
310 d['__len__'] = len(var)
311 return d
312
313
314#=======================================================================================================================
315# InstanceResolver
316#=======================================================================================================================
317class InstanceResolver:
318
319 def resolve(self, var, attribute):
320 field = var.__class__.getDeclaredField(attribute)
321 field.setAccessible(True)
322 return field.get(var)
323
324 def getDictionary(self, obj):
325 ret = {}
326
327 declaredFields = obj.__class__.getDeclaredFields()
328 for i in range(len(declaredFields)):
329 name = declaredFields[i].getName()
330 try:
331 declaredFields[i].setAccessible(True)
332 ret[name] = declaredFields[i].get(obj)
333 except:
334 traceback.print_exc()
335
336 return ret
337
338
339#=======================================================================================================================
340# JyArrayResolver
341#=======================================================================================================================
342class JyArrayResolver:
343 '''
344 This resolves a regular Object[] array from java
345 '''
346
347 def resolve(self, var, attribute):
348 if attribute == '__len__':
349 return None
350 return var[int(attribute)]
351
352 def getDictionary(self, obj):
353 ret = {}
354
355 for i in range(len(obj)):
356 ret[ i ] = obj[i]
357
358 ret['__len__'] = len(obj)
359 return ret
360
Tor Norbyec667c1f2014-05-28 17:06:51 -0700361
362#=======================================================================================================================
363# NdArrayResolver
364#=======================================================================================================================
365class NdArrayResolver:
366 '''
367 This resolves a numpy ndarray returning some metadata about the NDArray
368 '''
369
370 def resolve(self, obj, attribute):
371 if attribute == '__internals__':
372 return defaultResolver.getDictionary(obj)
373 if attribute == 'min':
374 return obj.min()
375 if attribute == 'max':
376 return obj.max()
377 if attribute == 'shape':
378 return obj.shape
379 if attribute == 'dtype':
380 return obj.dtype
381 if attribute == 'size':
382 return obj.size
383 return None
384
385 def getDictionary(self, obj):
386 ret = dict()
387 ret['__internals__'] = defaultResolver.getDictionary(obj)
388 if obj.size > 1024 * 1024:
389 ret['min'] = 'ndarray too big, calculating min would slow down debugging'
390 ret['max'] = 'ndarray too big, calculating max would slow down debugging'
391 else:
392 ret['min'] = obj.min()
393 ret['max'] = obj.max()
394 ret['shape'] = obj.shape
395 ret['dtype'] = obj.dtype
396 ret['size'] = obj.size
397 return ret
398
399
400#=======================================================================================================================
401# FrameResolver
402#=======================================================================================================================
403class FrameResolver:
404 '''
405 This resolves a frame.
406 '''
407
408 def resolve(self, obj, attribute):
409 if attribute == '__internals__':
410 return defaultResolver.getDictionary(obj)
411
412 if attribute == 'stack':
413 return self.getFrameStack(obj)
414
415 if attribute == 'f_locals':
416 return obj.f_locals
417
418 return None
419
420
421 def getDictionary(self, obj):
422 ret = dict()
423 ret['__internals__'] = defaultResolver.getDictionary(obj)
424 ret['stack'] = self.getFrameStack(obj)
425 ret['f_locals'] = obj.f_locals
426 return ret
427
428
429 def getFrameStack(self, frame):
430 ret = []
431 if frame is not None:
432 ret.append(self.getFrameName(frame))
433
434 while frame.f_back:
435 frame = frame.f_back
436 ret.append(self.getFrameName(frame))
437
438 return ret
439
440 def getFrameName(self, frame):
441 if frame is None:
442 return 'None'
443 try:
444 name = basename(frame.f_code.co_filename)
445 return 'frame: %s [%s:%s] id:%s' % (frame.f_code.co_name, name, frame.f_lineno, id(frame))
446 except:
447 return 'frame object'
448
449
Tor Norbye3a2425a2013-11-04 10:16:08 -0800450defaultResolver = DefaultResolver()
451dictResolver = DictResolver()
452tupleResolver = TupleResolver()
453instanceResolver = InstanceResolver()
454jyArrayResolver = JyArrayResolver()
455setResolver = SetResolver()
Tor Norbyec667c1f2014-05-28 17:06:51 -0700456ndarrayResolver = NdArrayResolver()
457frameResolver = FrameResolver()