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