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