blob: ad49bd881ba074a96ffda3f162cbf3c8b8726ad7 [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
Tor Norbyec3d3a902014-09-04 13:24:04 -070016from pydevd_constants import DictIterItems, xrange
Tor Norbye3a2425a2013-11-04 10:16:08 -080017
18
Tor Norbyec3d3a902014-09-04 13:24:04 -070019# Note: 300 is already a lot to see in the outline (after that the user should really use the shell to get things)
20# and this also means we'll pass less information to the client side (which makes debugging faster).
21MAX_ITEMS_TO_HANDLE = 300
22
Tor Norbye3a2425a2013-11-04 10:16:08 -080023TOO_LARGE_MSG = 'Too large to show contents. Max items to show: ' + str(MAX_ITEMS_TO_HANDLE)
24TOO_LARGE_ATTR = 'Unable to handle:'
25
26#=======================================================================================================================
27# UnableToResolveVariableException
28#=======================================================================================================================
29class UnableToResolveVariableException(Exception):
30 pass
31
32
33#=======================================================================================================================
34# InspectStub
35#=======================================================================================================================
36class InspectStub:
37 def isbuiltin(self, _args):
38 return False
39 def isroutine(self, object):
40 return False
41
42try:
43 import inspect
44except:
45 inspect = InspectStub()
46
47try:
48 import java.lang #@UnresolvedImport
49except:
50 pass
51
52#types does not include a MethodWrapperType
53try:
54 MethodWrapperType = type([].__str__)
55except:
56 MethodWrapperType = None
57
58
59#=======================================================================================================================
60# AbstractResolver
61#=======================================================================================================================
62class AbstractResolver:
63 '''
64 This class exists only for documentation purposes to explain how to create a resolver.
Tor Norbye1aa2e092014-08-20 17:01:23 -070065
Tor Norbye3a2425a2013-11-04 10:16:08 -080066 Some examples on how to resolve things:
67 - list: getDictionary could return a dict with index->item and use the index to resolve it later
68 - set: getDictionary could return a dict with id(object)->object and reiterate in that array to resolve it later
69 - arbitrary instance: getDictionary could return dict with attr_name->attr and use getattr to resolve it later
70 '''
71
72 def resolve(self, var, attribute):
73 '''
74 In this method, we'll resolve some child item given the string representation of the item in the key
75 representing the previously asked dictionary.
Tor Norbye1aa2e092014-08-20 17:01:23 -070076
Tor Norbye3a2425a2013-11-04 10:16:08 -080077 @param var: this is the actual variable to be resolved.
78 @param attribute: this is the string representation of a key previously returned in getDictionary.
79 '''
80 raise NotImplementedError
81
82 def getDictionary(self, var):
83 '''
84 @param var: this is the variable that should have its children gotten.
Tor Norbye1aa2e092014-08-20 17:01:23 -070085
Tor Norbye3a2425a2013-11-04 10:16:08 -080086 @return: a dictionary where each pair key, value should be shown to the user as children items
87 in the variables view for the given var.
88 '''
89 raise NotImplementedError
90
91
92#=======================================================================================================================
93# DefaultResolver
94#=======================================================================================================================
95class DefaultResolver:
96 '''
97 DefaultResolver is the class that'll actually resolve how to show some variable.
98 '''
99
100 def resolve(self, var, attribute):
101 return getattr(var, attribute)
102
103 def getDictionary(self, var):
104 if MethodWrapperType:
105 return self._getPyDictionary(var)
106 else:
107 return self._getJyDictionary(var)
108
109 def _getJyDictionary(self, obj):
110 ret = {}
111 found = java.util.HashMap()
112
113 original = obj
114 if hasattr(obj, '__class__') and obj.__class__ == java.lang.Class:
115
116 #get info about superclasses
117 classes = []
118 classes.append(obj)
119 c = obj.getSuperclass()
120 while c != None:
121 classes.append(c)
122 c = c.getSuperclass()
123
124 #get info about interfaces
125 interfs = []
126 for obj in classes:
127 interfs.extend(obj.getInterfaces())
128 classes.extend(interfs)
129
130 #now is the time when we actually get info on the declared methods and fields
131 for obj in classes:
132
133 declaredMethods = obj.getDeclaredMethods()
134 declaredFields = obj.getDeclaredFields()
Tor Norbye1aa2e092014-08-20 17:01:23 -0700135 for i in xrange(len(declaredMethods)):
Tor Norbye3a2425a2013-11-04 10:16:08 -0800136 name = declaredMethods[i].getName()
137 ret[name] = declaredMethods[i].toString()
138 found.put(name, 1)
139
Tor Norbye1aa2e092014-08-20 17:01:23 -0700140 for i in xrange(len(declaredFields)):
Tor Norbye3a2425a2013-11-04 10:16:08 -0800141 name = declaredFields[i].getName()
142 found.put(name, 1)
143 #if declaredFields[i].isAccessible():
144 declaredFields[i].setAccessible(True)
145 #ret[name] = declaredFields[i].get( declaredFields[i] )
146 try:
147 ret[name] = declaredFields[i].get(original)
148 except:
149 ret[name] = declaredFields[i].toString()
150
151 #this simple dir does not always get all the info, that's why we have the part before
Tor Norbye1aa2e092014-08-20 17:01:23 -0700152 #(e.g.: if we do a dir on String, some methods that are from other interfaces such as
Tor Norbye3a2425a2013-11-04 10:16:08 -0800153 #charAt don't appear)
154 try:
155 d = dir(original)
156 for name in d:
157 if found.get(name) is not 1:
158 ret[name] = getattr(original, name)
159 except:
160 #sometimes we're unable to do a dir
161 pass
162
163 return ret
164
165 def _getPyDictionary(self, var):
166 filterPrivate = False
167 filterSpecial = True
168 filterFunction = True
169 filterBuiltIn = True
170
171 names = dir(var)
172 if not names and hasattr(var, '__members__'):
173 names = var.__members__
174 d = {}
175
Tor Norbye1aa2e092014-08-20 17:01:23 -0700176 #Be aware that the order in which the filters are applied attempts to
177 #optimize the operation by removing as many items as possible in the
Tor Norbye3a2425a2013-11-04 10:16:08 -0800178 #first filters, leaving fewer items for later filters
179
180 if filterBuiltIn or filterFunction:
181 for n in names:
182 if filterSpecial:
183 if n.startswith('__') and n.endswith('__'):
184 continue
185
186 if filterPrivate:
187 if n.startswith('_') or n.endswith('__'):
188 continue
189
190 try:
191 attr = getattr(var, n)
192
193 #filter builtins?
194 if filterBuiltIn:
195 if inspect.isbuiltin(attr):
196 continue
197
198 #filter functions?
199 if filterFunction:
200 if inspect.isroutine(attr) or isinstance(attr, MethodWrapperType):
201 continue
202 except:
203 #if some error occurs getting it, let's put it to the user.
204 strIO = StringIO.StringIO()
205 traceback.print_exc(file=strIO)
206 attr = strIO.getvalue()
207
208 d[ n ] = attr
209
210 return d
211
212
213#=======================================================================================================================
214# DictResolver
215#=======================================================================================================================
216class DictResolver:
217
218 def resolve(self, dict, key):
Tor Norbye1aa2e092014-08-20 17:01:23 -0700219 if key in ('__len__', TOO_LARGE_ATTR):
Tor Norbye3a2425a2013-11-04 10:16:08 -0800220 return None
221
222 if '(' not in key:
223 #we have to treat that because the dict resolver is also used to directly resolve the global and local
Tor Norbye1aa2e092014-08-20 17:01:23 -0700224 #scopes (which already have the items directly)
Tor Norbye3a2425a2013-11-04 10:16:08 -0800225 return dict[key]
226
227 #ok, we have to iterate over the items to find the one that matches the id, because that's the only way
228 #to actually find the reference from the string we have before.
229 expected_id = int(key.split('(')[-1][:-1])
Tor Norbye1aa2e092014-08-20 17:01:23 -0700230 for key, val in DictIterItems(dict):
Tor Norbye3a2425a2013-11-04 10:16:08 -0800231 if id(key) == expected_id:
232 return val
233
234 raise UnableToResolveVariableException()
235
236 def keyStr(self, key):
237 if isinstance(key, str):
238 return "'%s'"%key
239 else:
240 if not pydevd_constants.IS_PY3K:
241 if isinstance(key, unicode):
242 return "u'%s'"%key
243 return key
244
245 def getDictionary(self, dict):
246 ret = {}
247
Tor Norbye1aa2e092014-08-20 17:01:23 -0700248 i = 0
249 for key, val in DictIterItems(dict):
250 i += 1
Tor Norbye3a2425a2013-11-04 10:16:08 -0800251 #we need to add the id because otherwise we cannot find the real object to get its contents later on.
252 key = '%s (%s)' % (self.keyStr(key), id(key))
253 ret[key] = val
Tor Norbye1aa2e092014-08-20 17:01:23 -0700254 if i > MAX_ITEMS_TO_HANDLE:
255 ret[TOO_LARGE_ATTR] = TOO_LARGE_MSG
256 break
Tor Norbye3a2425a2013-11-04 10:16:08 -0800257
258 ret['__len__'] = len(dict)
259 return ret
260
261
262
263#=======================================================================================================================
264# TupleResolver
265#=======================================================================================================================
266class TupleResolver: #to enumerate tuples and lists
267
268 def resolve(self, var, attribute):
269 '''
270 @param var: that's the original attribute
271 @param attribute: that's the key passed in the dict (as a string)
272 '''
Tor Norbye1aa2e092014-08-20 17:01:23 -0700273 if attribute in ('__len__', TOO_LARGE_ATTR):
Tor Norbye3a2425a2013-11-04 10:16:08 -0800274 return None
275 return var[int(attribute)]
276
277 def getDictionary(self, var):
Tor Norbye3a2425a2013-11-04 10:16:08 -0800278 l = len(var)
279 d = {}
Tor Norbye1aa2e092014-08-20 17:01:23 -0700280
Tor Norbyec3d3a902014-09-04 13:24:04 -0700281 format_str = '%0' + str(int(len(str(l)))) + 'd'
Tor Norbye1aa2e092014-08-20 17:01:23 -0700282
Tor Norbyec3d3a902014-09-04 13:24:04 -0700283 i = 0
284 for item in var:
285 d[format_str % i] = item
286 i += 1
287
288 if i > MAX_ITEMS_TO_HANDLE:
289 d[TOO_LARGE_ATTR] = TOO_LARGE_MSG
290 break
291
Tor Norbye3a2425a2013-11-04 10:16:08 -0800292 d['__len__'] = len(var)
293 return d
294
295
296
297#=======================================================================================================================
298# SetResolver
299#=======================================================================================================================
300class SetResolver:
301 '''
302 Resolves a set as dict id(object)->object
303 '''
304
305 def resolve(self, var, attribute):
Tor Norbye1aa2e092014-08-20 17:01:23 -0700306 if attribute in ('__len__', TOO_LARGE_ATTR):
Tor Norbye3a2425a2013-11-04 10:16:08 -0800307 return None
308
309 attribute = int(attribute)
310 for v in var:
311 if id(v) == attribute:
312 return v
313
314 raise UnableToResolveVariableException('Unable to resolve %s in %s' % (attribute, var))
315
316 def getDictionary(self, var):
317 d = {}
Tor Norbye1aa2e092014-08-20 17:01:23 -0700318 i = 0
Tor Norbye3a2425a2013-11-04 10:16:08 -0800319 for item in var:
Tor Norbye1aa2e092014-08-20 17:01:23 -0700320 i+= 1
321 d[id(item)] = item
322
323 if i > MAX_ITEMS_TO_HANDLE:
324 d[TOO_LARGE_ATTR] = TOO_LARGE_MSG
325 break
326
327
Tor Norbye3a2425a2013-11-04 10:16:08 -0800328 d['__len__'] = len(var)
329 return d
330
331
332#=======================================================================================================================
333# InstanceResolver
334#=======================================================================================================================
335class InstanceResolver:
336
337 def resolve(self, var, attribute):
338 field = var.__class__.getDeclaredField(attribute)
339 field.setAccessible(True)
340 return field.get(var)
341
342 def getDictionary(self, obj):
343 ret = {}
344
345 declaredFields = obj.__class__.getDeclaredFields()
Tor Norbye1aa2e092014-08-20 17:01:23 -0700346 for i in xrange(len(declaredFields)):
Tor Norbye3a2425a2013-11-04 10:16:08 -0800347 name = declaredFields[i].getName()
348 try:
349 declaredFields[i].setAccessible(True)
350 ret[name] = declaredFields[i].get(obj)
351 except:
352 traceback.print_exc()
353
354 return ret
355
356
357#=======================================================================================================================
358# JyArrayResolver
359#=======================================================================================================================
360class JyArrayResolver:
361 '''
362 This resolves a regular Object[] array from java
363 '''
364
365 def resolve(self, var, attribute):
366 if attribute == '__len__':
367 return None
368 return var[int(attribute)]
369
370 def getDictionary(self, obj):
371 ret = {}
372
Tor Norbye1aa2e092014-08-20 17:01:23 -0700373 for i in xrange(len(obj)):
Tor Norbye3a2425a2013-11-04 10:16:08 -0800374 ret[ i ] = obj[i]
375
376 ret['__len__'] = len(obj)
377 return ret
378
Tor Norbyec667c1f2014-05-28 17:06:51 -0700379
380#=======================================================================================================================
381# NdArrayResolver
382#=======================================================================================================================
383class NdArrayResolver:
384 '''
385 This resolves a numpy ndarray returning some metadata about the NDArray
386 '''
387
Tor Norbyec3d3a902014-09-04 13:24:04 -0700388 def is_numeric(self, obj):
389 if not hasattr(obj, 'dtype'):
390 return False
391 return obj.dtype.kind in 'biufc'
392
Tor Norbyec667c1f2014-05-28 17:06:51 -0700393 def resolve(self, obj, attribute):
394 if attribute == '__internals__':
395 return defaultResolver.getDictionary(obj)
396 if attribute == 'min':
Tor Norbyec3d3a902014-09-04 13:24:04 -0700397 if self.is_numeric(obj):
398 return obj.min()
399 else:
400 return None
Tor Norbyec667c1f2014-05-28 17:06:51 -0700401 if attribute == 'max':
Tor Norbyec3d3a902014-09-04 13:24:04 -0700402 if self.is_numeric(obj):
403 return obj.max()
404 else:
405 return None
Tor Norbyec667c1f2014-05-28 17:06:51 -0700406 if attribute == 'shape':
407 return obj.shape
408 if attribute == 'dtype':
409 return obj.dtype
410 if attribute == 'size':
411 return obj.size
412 return None
413
414 def getDictionary(self, obj):
415 ret = dict()
416 ret['__internals__'] = defaultResolver.getDictionary(obj)
417 if obj.size > 1024 * 1024:
418 ret['min'] = 'ndarray too big, calculating min would slow down debugging'
419 ret['max'] = 'ndarray too big, calculating max would slow down debugging'
420 else:
Tor Norbyec3d3a902014-09-04 13:24:04 -0700421 if self.is_numeric(obj):
422 ret['min'] = obj.min()
423 ret['max'] = obj.max()
424 else:
425 ret['min'] = 'not a numeric object'
426 ret['max'] = 'not a numeric object'
Tor Norbyec667c1f2014-05-28 17:06:51 -0700427 ret['shape'] = obj.shape
428 ret['dtype'] = obj.dtype
429 ret['size'] = obj.size
430 return ret
431
432
433#=======================================================================================================================
434# FrameResolver
435#=======================================================================================================================
436class FrameResolver:
437 '''
438 This resolves a frame.
439 '''
440
441 def resolve(self, obj, attribute):
442 if attribute == '__internals__':
443 return defaultResolver.getDictionary(obj)
444
445 if attribute == 'stack':
446 return self.getFrameStack(obj)
447
448 if attribute == 'f_locals':
449 return obj.f_locals
450
451 return None
452
453
454 def getDictionary(self, obj):
455 ret = dict()
456 ret['__internals__'] = defaultResolver.getDictionary(obj)
457 ret['stack'] = self.getFrameStack(obj)
458 ret['f_locals'] = obj.f_locals
459 return ret
460
461
462 def getFrameStack(self, frame):
463 ret = []
464 if frame is not None:
465 ret.append(self.getFrameName(frame))
466
467 while frame.f_back:
468 frame = frame.f_back
469 ret.append(self.getFrameName(frame))
470
471 return ret
472
473 def getFrameName(self, frame):
474 if frame is None:
475 return 'None'
476 try:
477 name = basename(frame.f_code.co_filename)
478 return 'frame: %s [%s:%s] id:%s' % (frame.f_code.co_name, name, frame.f_lineno, id(frame))
479 except:
480 return 'frame object'
481
482
Tor Norbye3a2425a2013-11-04 10:16:08 -0800483defaultResolver = DefaultResolver()
484dictResolver = DictResolver()
485tupleResolver = TupleResolver()
486instanceResolver = InstanceResolver()
487jyArrayResolver = JyArrayResolver()
488setResolver = SetResolver()
Tor Norbyec667c1f2014-05-28 17:06:51 -0700489ndarrayResolver = NdArrayResolver()
490frameResolver = FrameResolver()