1from _pydev_bundle import pydev_log 2from _pydevd_bundle.pydevd_utils import hasattr_checked, DAPGrouper 3try: 4 import StringIO 5except: 6 import io as StringIO 7import traceback 8from os.path import basename 9 10from functools import partial 11from _pydevd_bundle.pydevd_constants import dict_iter_items, dict_keys, xrange, IS_PY36_OR_GREATER, \ 12 MethodWrapperType, RETURN_VALUES_DICT, DebugInfoHolder, IS_PYPY, GENERATED_LEN_ATTR_NAME 13from _pydevd_bundle.pydevd_safe_repr import SafeRepr 14 15# Note: 300 is already a lot to see in the outline (after that the user should really use the shell to get things) 16# and this also means we'll pass less information to the client side (which makes debugging faster). 17MAX_ITEMS_TO_HANDLE = 300 18 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#======================================================================================================================= 24# UnableToResolveVariableException 25#======================================================================================================================= 26class UnableToResolveVariableException(Exception): 27 pass 28 29 30try: 31 from collections import OrderedDict 32except: 33 OrderedDict = dict 34 35try: 36 import java.lang # @UnresolvedImport 37except: 38 pass 39 40#======================================================================================================================= 41# See: pydevd_extension_api module for resolver interface 42#======================================================================================================================= 43 44 45def sorted_attributes_key(attr_name): 46 if attr_name.startswith('__'): 47 if attr_name.endswith('__'): 48 # __ double under before and after __ 49 return (3, attr_name) 50 else: 51 # __ double under before 52 return (2, attr_name) 53 elif attr_name.startswith('_'): 54 # _ single under 55 return (1, attr_name) 56 else: 57 # Regular (Before anything) 58 return (0, attr_name) 59 60 61#======================================================================================================================= 62# DefaultResolver 63#======================================================================================================================= 64class DefaultResolver: 65 ''' 66 DefaultResolver is the class that'll actually resolve how to show some variable. 67 ''' 68 69 def resolve(self, var, attribute): 70 return getattr(var, attribute) 71 72 def get_contents_debug_adapter_protocol(self, obj, fmt=None): 73 if MethodWrapperType: 74 dct, used___dict__ = self._get_py_dictionary(obj) 75 else: 76 dct = self._get_jy_dictionary(obj)[0] 77 78 lst = sorted(dict_iter_items(dct), key=lambda tup: sorted_attributes_key(tup[0])) 79 if used___dict__: 80 eval_name = '.__dict__[%s]' 81 else: 82 eval_name = '.%s' 83 84 ret = [] 85 for attr_name, attr_value in lst: 86 entry = (attr_name, attr_value, eval_name % attr_name) 87 ret.append(entry) 88 89 return ret 90 91 def get_dictionary(self, var, names=None, used___dict__=False): 92 if MethodWrapperType: 93 return self._get_py_dictionary(var, names, used___dict__=used___dict__)[0] 94 else: 95 return self._get_jy_dictionary(var)[0] 96 97 def _get_jy_dictionary(self, obj): 98 ret = {} 99 found = java.util.HashMap() 100 101 original = obj 102 if hasattr_checked(obj, '__class__') and obj.__class__ == java.lang.Class: 103 104 # get info about superclasses 105 classes = [] 106 classes.append(obj) 107 c = obj.getSuperclass() 108 while c != None: 109 classes.append(c) 110 c = c.getSuperclass() 111 112 # get info about interfaces 113 interfs = [] 114 for obj in classes: 115 interfs.extend(obj.getInterfaces()) 116 classes.extend(interfs) 117 118 # now is the time when we actually get info on the declared methods and fields 119 for obj in classes: 120 121 declaredMethods = obj.getDeclaredMethods() 122 declaredFields = obj.getDeclaredFields() 123 for i in xrange(len(declaredMethods)): 124 name = declaredMethods[i].getName() 125 ret[name] = declaredMethods[i].toString() 126 found.put(name, 1) 127 128 for i in xrange(len(declaredFields)): 129 name = declaredFields[i].getName() 130 found.put(name, 1) 131 # if declaredFields[i].isAccessible(): 132 declaredFields[i].setAccessible(True) 133 # ret[name] = declaredFields[i].get( declaredFields[i] ) 134 try: 135 ret[name] = declaredFields[i].get(original) 136 except: 137 ret[name] = declaredFields[i].toString() 138 139 # this simple dir does not always get all the info, that's why we have the part before 140 # (e.g.: if we do a dir on String, some methods that are from other interfaces such as 141 # charAt don't appear) 142 try: 143 d = dir(original) 144 for name in d: 145 if found.get(name) != 1: 146 ret[name] = getattr(original, name) 147 except: 148 # sometimes we're unable to do a dir 149 pass 150 151 return ret 152 153 def get_names(self, var): 154 used___dict__ = False 155 try: 156 names = dir(var) 157 except Exception: 158 names = [] 159 if not names: 160 if hasattr_checked(var, '__dict__'): 161 names = dict_keys(var.__dict__) 162 used___dict__ = True 163 return names, used___dict__ 164 165 def _get_py_dictionary(self, var, names=None, used___dict__=False): 166 ''' 167 :return tuple(names, used___dict__), where used___dict__ means we have to access 168 using obj.__dict__[name] instead of getattr(obj, name) 169 ''' 170 171 # On PyPy we never show functions. This is because of a corner case where PyPy becomes 172 # absurdly slow -- it takes almost half a second to introspect a single numpy function (so, 173 # the related test, "test_case_16_resolve_numpy_array", times out... this probably isn't 174 # specific to numpy, but to any library where the CPython bridge is used, but as we 175 # can't be sure in the debugger, we play it safe and don't show it at all). 176 filter_function = IS_PYPY 177 178 if not names: 179 names, used___dict__ = self.get_names(var) 180 d = {} 181 182 # Be aware that the order in which the filters are applied attempts to 183 # optimize the operation by removing as many items as possible in the 184 # first filters, leaving fewer items for later filters 185 186 for name in names: 187 try: 188 name_as_str = name 189 if name_as_str.__class__ != str: 190 name_as_str = '%r' % (name_as_str,) 191 192 if not used___dict__: 193 attr = getattr(var, name) 194 else: 195 attr = var.__dict__[name] 196 197 # filter functions? 198 if filter_function: 199 if inspect.isroutine(attr) or isinstance(attr, MethodWrapperType): 200 continue 201 except: 202 # if some error occurs getting it, let's put it to the user. 203 strIO = StringIO.StringIO() 204 traceback.print_exc(file=strIO) 205 attr = strIO.getvalue() 206 207 d[name_as_str] = attr 208 209 return d, used___dict__ 210 211 212class DAPGrouperResolver: 213 214 def get_contents_debug_adapter_protocol(self, obj, fmt=None): 215 return obj.get_contents_debug_adapter_protocol() 216 217 218_basic_immutable_types = (int, float, complex, str, bytes, type(None), bool, frozenset) 219try: 220 _basic_immutable_types += (long, unicode) # Py2 types 221except NameError: 222 pass 223 224 225def _does_obj_repr_evaluate_to_obj(obj): 226 ''' 227 If obj is an object where evaluating its representation leads to 228 the same object, return True, otherwise, return False. 229 ''' 230 try: 231 if isinstance(obj, tuple): 232 for o in obj: 233 if not _does_obj_repr_evaluate_to_obj(o): 234 return False 235 return True 236 else: 237 return isinstance(obj, _basic_immutable_types) 238 except: 239 return False 240 241 242#======================================================================================================================= 243# DictResolver 244#======================================================================================================================= 245class DictResolver: 246 247 sort_keys = not IS_PY36_OR_GREATER 248 249 def resolve(self, dict, key): 250 if key in (GENERATED_LEN_ATTR_NAME, TOO_LARGE_ATTR): 251 return None 252 253 if '(' not in key: 254 # we have to treat that because the dict resolver is also used to directly resolve the global and local 255 # scopes (which already have the items directly) 256 try: 257 return dict[key] 258 except: 259 return getattr(dict, key) 260 261 # ok, we have to iterate over the items to find the one that matches the id, because that's the only way 262 # to actually find the reference from the string we have before. 263 expected_id = int(key.split('(')[-1][:-1]) 264 for key, val in dict_iter_items(dict): 265 if id(key) == expected_id: 266 return val 267 268 raise UnableToResolveVariableException() 269 270 def key_to_str(self, key, fmt=None): 271 if fmt is not None: 272 if fmt.get('hex', False): 273 safe_repr = SafeRepr() 274 safe_repr.convert_to_hex = True 275 return safe_repr(key) 276 return '%r' % (key,) 277 278 def init_dict(self): 279 return {} 280 281 def get_contents_debug_adapter_protocol(self, dct, fmt=None): 282 ''' 283 This method is to be used in the case where the variables are all saved by its id (and as 284 such don't need to have the `resolve` method called later on, so, keys don't need to 285 embed the reference in the key). 286 287 Note that the return should be ordered. 288 289 :return list(tuple(name:str, value:object, evaluateName:str)) 290 ''' 291 ret = [] 292 293 i = 0 294 295 found_representations = set() 296 297 for key, val in dict_iter_items(dct): 298 i += 1 299 key_as_str = self.key_to_str(key, fmt) 300 301 if key_as_str not in found_representations: 302 found_representations.add(key_as_str) 303 else: 304 # If the key would be a duplicate, add the key id (otherwise 305 # VSCode won't show all keys correctly). 306 # See: https://github.com/microsoft/debugpy/issues/148 307 key_as_str = '%s (id: %s)' % (key_as_str, id(key)) 308 found_representations.add(key_as_str) 309 310 if _does_obj_repr_evaluate_to_obj(key): 311 s = self.key_to_str(key) # do not format the key 312 eval_key_str = '[%s]' % (s,) 313 else: 314 eval_key_str = None 315 ret.append((key_as_str, val, eval_key_str)) 316 if i > MAX_ITEMS_TO_HANDLE: 317 ret.append((TOO_LARGE_ATTR, TOO_LARGE_MSG, None)) 318 break 319 320 # in case the class extends built-in type and has some additional fields 321 from_default_resolver = defaultResolver.get_contents_debug_adapter_protocol(dct, fmt) 322 323 if from_default_resolver: 324 ret = from_default_resolver + ret 325 326 if self.sort_keys: 327 ret = sorted(ret, key=lambda tup: sorted_attributes_key(tup[0])) 328 329 ret.append((GENERATED_LEN_ATTR_NAME, len(dct), partial(_apply_evaluate_name, evaluate_name='len(%s)'))) 330 return ret 331 332 def get_dictionary(self, dict): 333 ret = self.init_dict() 334 335 i = 0 336 for key, val in dict_iter_items(dict): 337 i += 1 338 # we need to add the id because otherwise we cannot find the real object to get its contents later on. 339 key = '%s (%s)' % (self.key_to_str(key), id(key)) 340 ret[key] = val 341 if i > MAX_ITEMS_TO_HANDLE: 342 ret[TOO_LARGE_ATTR] = TOO_LARGE_MSG 343 break 344 345 # in case if the class extends built-in type and has some additional fields 346 additional_fields = defaultResolver.get_dictionary(dict) 347 ret.update(additional_fields) 348 ret[GENERATED_LEN_ATTR_NAME] = len(dict) 349 return ret 350 351 352def _apply_evaluate_name(parent_name, evaluate_name): 353 return evaluate_name % (parent_name,) 354 355 356#======================================================================================================================= 357# TupleResolver 358#======================================================================================================================= 359class TupleResolver: # to enumerate tuples and lists 360 361 def resolve(self, var, attribute): 362 ''' 363 @param var: that's the original attribute 364 @param attribute: that's the key passed in the dict (as a string) 365 ''' 366 if attribute in (GENERATED_LEN_ATTR_NAME, TOO_LARGE_ATTR): 367 return None 368 try: 369 return var[int(attribute)] 370 except: 371 return getattr(var, attribute) 372 373 def get_contents_debug_adapter_protocol(self, lst, fmt=None): 374 ''' 375 This method is to be used in the case where the variables are all saved by its id (and as 376 such don't need to have the `resolve` method called later on, so, keys don't need to 377 embed the reference in the key). 378 379 Note that the return should be ordered. 380 381 :return list(tuple(name:str, value:object, evaluateName:str)) 382 ''' 383 l = len(lst) 384 ret = [] 385 386 format_str = '%0' + str(int(len(str(l - 1)))) + 'd' 387 if fmt is not None and fmt.get('hex', False): 388 format_str = '0x%0' + str(int(len(hex(l).lstrip('0x')))) + 'x' 389 390 for i, item in enumerate(lst): 391 ret.append((format_str % i, item, '[%s]' % i)) 392 393 if i > MAX_ITEMS_TO_HANDLE: 394 ret.append((TOO_LARGE_ATTR, TOO_LARGE_MSG, None)) 395 break 396 397 # Needed in case the class extends the built-in type and has some additional fields. 398 from_default_resolver = defaultResolver.get_contents_debug_adapter_protocol(lst, fmt=fmt) 399 if from_default_resolver: 400 ret = from_default_resolver + ret 401 402 ret.append((GENERATED_LEN_ATTR_NAME, len(lst), partial(_apply_evaluate_name, evaluate_name='len(%s)'))) 403 return ret 404 405 def get_dictionary(self, var, fmt={}): 406 l = len(var) 407 d = {} 408 409 format_str = '%0' + str(int(len(str(l - 1)))) + 'd' 410 if fmt is not None and fmt.get('hex', False): 411 format_str = '0x%0' + str(int(len(hex(l).lstrip('0x')))) + 'x' 412 413 for i, item in enumerate(var): 414 d[format_str % i] = item 415 416 if i > MAX_ITEMS_TO_HANDLE: 417 d[TOO_LARGE_ATTR] = TOO_LARGE_MSG 418 break 419 420 # in case if the class extends built-in type and has some additional fields 421 additional_fields = defaultResolver.get_dictionary(var) 422 d.update(additional_fields) 423 d[GENERATED_LEN_ATTR_NAME] = len(var) 424 return d 425 426 427#======================================================================================================================= 428# SetResolver 429#======================================================================================================================= 430class SetResolver: 431 ''' 432 Resolves a set as dict id(object)->object 433 ''' 434 435 def get_contents_debug_adapter_protocol(self, obj, fmt=None): 436 ret = [] 437 438 for i, item in enumerate(obj): 439 ret.append((str(id(item)), item, None)) 440 441 if i > MAX_ITEMS_TO_HANDLE: 442 ret.append((TOO_LARGE_ATTR, TOO_LARGE_MSG, None)) 443 break 444 445 # Needed in case the class extends the built-in type and has some additional fields. 446 from_default_resolver = defaultResolver.get_contents_debug_adapter_protocol(obj, fmt=fmt) 447 if from_default_resolver: 448 ret = from_default_resolver + ret 449 ret.append((GENERATED_LEN_ATTR_NAME, len(obj), partial(_apply_evaluate_name, evaluate_name='len(%s)'))) 450 return ret 451 452 def resolve(self, var, attribute): 453 if attribute in (GENERATED_LEN_ATTR_NAME, TOO_LARGE_ATTR): 454 return None 455 456 try: 457 attribute = int(attribute) 458 except: 459 return getattr(var, attribute) 460 461 for v in var: 462 if id(v) == attribute: 463 return v 464 465 raise UnableToResolveVariableException('Unable to resolve %s in %s' % (attribute, var)) 466 467 def get_dictionary(self, var): 468 d = {} 469 for i, item in enumerate(var): 470 d[str(id(item))] = item 471 472 if i > MAX_ITEMS_TO_HANDLE: 473 d[TOO_LARGE_ATTR] = TOO_LARGE_MSG 474 break 475 476 # in case if the class extends built-in type and has some additional fields 477 additional_fields = defaultResolver.get_dictionary(var) 478 d.update(additional_fields) 479 d[GENERATED_LEN_ATTR_NAME] = len(var) 480 return d 481 482 def change_var_from_name(self, container, name, new_value): 483 # The name given in this case must be the id(item), so, we can actually 484 # iterate in the set and see which item matches the given id. 485 486 try: 487 # Check that the new value can actually be added to a set (i.e.: it's hashable/comparable). 488 set().add(new_value) 489 except: 490 return None 491 492 for item in container: 493 if str(id(item)) == name: 494 container.remove(item) 495 container.add(new_value) 496 return str(id(new_value)) 497 498 return None 499 500 501#======================================================================================================================= 502# InstanceResolver 503#======================================================================================================================= 504class InstanceResolver: 505 506 def resolve(self, var, attribute): 507 field = var.__class__.getDeclaredField(attribute) 508 field.setAccessible(True) 509 return field.get(var) 510 511 def get_dictionary(self, obj): 512 ret = {} 513 514 declaredFields = obj.__class__.getDeclaredFields() 515 for i in xrange(len(declaredFields)): 516 name = declaredFields[i].getName() 517 try: 518 declaredFields[i].setAccessible(True) 519 ret[name] = declaredFields[i].get(obj) 520 except: 521 pydev_log.exception() 522 523 return ret 524 525 526#======================================================================================================================= 527# JyArrayResolver 528#======================================================================================================================= 529class JyArrayResolver: 530 ''' 531 This resolves a regular Object[] array from java 532 ''' 533 534 def resolve(self, var, attribute): 535 if attribute == GENERATED_LEN_ATTR_NAME: 536 return None 537 return var[int(attribute)] 538 539 def get_dictionary(self, obj): 540 ret = {} 541 542 for i in xrange(len(obj)): 543 ret[ i ] = obj[i] 544 545 ret[GENERATED_LEN_ATTR_NAME] = len(obj) 546 return ret 547 548 549#======================================================================================================================= 550# MultiValueDictResolver 551#======================================================================================================================= 552class MultiValueDictResolver(DictResolver): 553 554 def resolve(self, dict, key): 555 if key in (GENERATED_LEN_ATTR_NAME, TOO_LARGE_ATTR): 556 return None 557 558 # ok, we have to iterate over the items to find the one that matches the id, because that's the only way 559 # to actually find the reference from the string we have before. 560 expected_id = int(key.split('(')[-1][:-1]) 561 for key in dict_keys(dict): 562 val = dict.getlist(key) 563 if id(key) == expected_id: 564 return val 565 566 raise UnableToResolveVariableException() 567 568 569#======================================================================================================================= 570# DjangoFormResolver 571#======================================================================================================================= 572class DjangoFormResolver(DefaultResolver): 573 574 def get_dictionary(self, var, names=None): 575 # Do not call self.errors because it is a property and has side effects. 576 names, used___dict__ = self.get_names(var) 577 578 has_errors_attr = False 579 if "errors" in names: 580 has_errors_attr = True 581 names.remove("errors") 582 583 d = defaultResolver.get_dictionary(var, names=names, used___dict__=used___dict__) 584 if has_errors_attr: 585 try: 586 errors_attr = getattr(var, "_errors") 587 except: 588 errors_attr = None 589 d["errors"] = errors_attr 590 return d 591 592 593#======================================================================================================================= 594# DequeResolver 595#======================================================================================================================= 596class DequeResolver(TupleResolver): 597 598 def get_dictionary(self, var): 599 d = TupleResolver.get_dictionary(self, var) 600 d['maxlen'] = getattr(var, 'maxlen', None) 601 return d 602 603 604#======================================================================================================================= 605# OrderedDictResolver 606#======================================================================================================================= 607class OrderedDictResolver(DictResolver): 608 609 sort_keys = False 610 611 def init_dict(self): 612 return OrderedDict() 613 614 615#======================================================================================================================= 616# FrameResolver 617#======================================================================================================================= 618class FrameResolver: 619 ''' 620 This resolves a frame. 621 ''' 622 623 def resolve(self, obj, attribute): 624 if attribute == '__internals__': 625 return defaultResolver.get_dictionary(obj) 626 627 if attribute == 'stack': 628 return self.get_frame_stack(obj) 629 630 if attribute == 'f_locals': 631 return obj.f_locals 632 633 return None 634 635 def get_dictionary(self, obj): 636 ret = {} 637 ret['__internals__'] = defaultResolver.get_dictionary(obj) 638 ret['stack'] = self.get_frame_stack(obj) 639 ret['f_locals'] = obj.f_locals 640 return ret 641 642 def get_frame_stack(self, frame): 643 ret = [] 644 if frame is not None: 645 ret.append(self.get_frame_name(frame)) 646 647 while frame.f_back: 648 frame = frame.f_back 649 ret.append(self.get_frame_name(frame)) 650 651 return ret 652 653 def get_frame_name(self, frame): 654 if frame is None: 655 return 'None' 656 try: 657 name = basename(frame.f_code.co_filename) 658 return 'frame: %s [%s:%s] id:%s' % (frame.f_code.co_name, name, frame.f_lineno, id(frame)) 659 except: 660 return 'frame object' 661 662 663defaultResolver = DefaultResolver() 664dictResolver = DictResolver() 665tupleResolver = TupleResolver() 666instanceResolver = InstanceResolver() 667jyArrayResolver = JyArrayResolver() 668setResolver = SetResolver() 669multiValueDictResolver = MultiValueDictResolver() 670djangoFormResolver = DjangoFormResolver() 671dequeResolver = DequeResolver() 672orderedDictResolver = OrderedDictResolver() 673frameResolver = FrameResolver() 674dapGrouperResolver = DAPGrouperResolver() 675 676 677class InspectStub: 678 679 def isbuiltin(self, _args): 680 return False 681 682 def isroutine(self, object): 683 return False 684 685 686try: 687 import inspect 688except: 689 inspect = InspectStub() 690 691 692def get_var_scope(attr_name, attr_value, evaluate_name, handle_return_values): 693 if attr_name.startswith("'"): 694 if attr_name.endswith("'"): 695 attr_name = attr_name[1:-1] 696 else: 697 i = attr_name.find("__' (") 698 if i >= 0: 699 # Handle attr_name such as: >>'__name__' (1732494379184)<< 700 attr_name = attr_name[1: i + 2] 701 702 if handle_return_values and attr_name == RETURN_VALUES_DICT: 703 return '' 704 705 elif attr_name == GENERATED_LEN_ATTR_NAME: 706 return '' 707 708 if attr_name.startswith('__') and attr_name.endswith('__'): 709 return DAPGrouper.SCOPE_SPECIAL_VARS 710 711 if attr_name.startswith('_') or attr_name.endswith('__'): 712 return DAPGrouper.SCOPE_PROTECTED_VARS 713 714 try: 715 if inspect.isroutine(attr_value) or isinstance(attr_value, MethodWrapperType): 716 return DAPGrouper.SCOPE_FUNCTION_VARS 717 718 elif inspect.isclass(attr_value): 719 return DAPGrouper.SCOPE_CLASS_VARS 720 except: 721 # It's possible that isinstance throws an exception when dealing with user-code. 722 if DebugInfoHolder.DEBUG_TRACE_LEVEL > 0: 723 pydev_log.exception() 724 725 return '' 726