[platform] mock update server
[idea/community.git] / python / helpers / pydev / _pydevd_bundle / pydevd_referrers.py
1 from _pydevd_bundle.pydevd_constants import dict_contains
2 import sys
3 from _pydevd_bundle import pydevd_vars
4 from os.path import basename
5 import traceback
6 try:
7     from urllib import quote, quote_plus, unquote, unquote_plus
8 except:
9     from urllib.parse import quote, quote_plus, unquote, unquote_plus  #@Reimport @UnresolvedImport
10
11 #===================================================================================================
12 # print_var_node
13 #===================================================================================================
14 def print_var_node(xml_node, stream):
15     name = xml_node.getAttribute('name')
16     value = xml_node.getAttribute('value')
17     val_type = xml_node.getAttribute('type')
18
19     found_as = xml_node.getAttribute('found_as')
20     stream.write('Name: ')
21     stream.write(unquote_plus(name))
22     stream.write(', Value: ')
23     stream.write(unquote_plus(value))
24     stream.write(', Type: ')
25     stream.write(unquote_plus(val_type))
26     if found_as:
27         stream.write(', Found as: %s' % (unquote_plus(found_as),))
28     stream.write('\n')
29
30 #===================================================================================================
31 # print_referrers
32 #===================================================================================================
33 def print_referrers(obj, stream=None):
34     if stream is None:
35         stream = sys.stdout
36     result = get_referrer_info(obj)
37     from xml.dom.minidom import parseString
38     dom = parseString(result)
39
40     xml = dom.getElementsByTagName('xml')[0]
41     for node in xml.childNodes:
42         if node.nodeType == node.TEXT_NODE:
43             continue
44
45         if node.localName == 'for':
46             stream.write('Searching references for: ')
47             for child in node.childNodes:
48                 if child.nodeType == node.TEXT_NODE:
49                     continue
50                 print_var_node(child, stream)
51
52         elif node.localName == 'var':
53             stream.write('Referrer found: ')
54             print_var_node(node, stream)
55
56         else:
57             sys.stderr.write('Unhandled node: %s\n' % (node,))
58
59     return result
60
61
62 #===================================================================================================
63 # get_referrer_info
64 #===================================================================================================
65 def get_referrer_info(searched_obj):
66     DEBUG = 0
67     if DEBUG:
68         sys.stderr.write('Getting referrers info.\n')
69     try:
70         try:
71             if searched_obj is None:
72                 ret = ['<xml>\n']
73
74                 ret.append('<for>\n')
75                 ret.append(pydevd_vars.var_to_xml(
76                     searched_obj,
77                     'Skipping getting referrers for None',
78                     additionalInXml=' id="%s"' % (id(searched_obj),)))
79                 ret.append('</for>\n')
80                 ret.append('</xml>')
81                 ret = ''.join(ret)
82                 return ret
83
84             obj_id = id(searched_obj)
85
86             try:
87                 if DEBUG:
88                     sys.stderr.write('Getting referrers...\n')
89                 import gc
90                 referrers = gc.get_referrers(searched_obj)
91             except:
92                 traceback.print_exc()
93                 ret = ['<xml>\n']
94
95                 ret.append('<for>\n')
96                 ret.append(pydevd_vars.var_to_xml(
97                     searched_obj,
98                     'Exception raised while trying to get_referrers.',
99                     additionalInXml=' id="%s"' % (id(searched_obj),)))
100                 ret.append('</for>\n')
101                 ret.append('</xml>')
102                 ret = ''.join(ret)
103                 return ret
104
105             if DEBUG:
106                 sys.stderr.write('Found %s referrers.\n' % (len(referrers),))
107
108             curr_frame = sys._getframe()
109             frame_type = type(curr_frame)
110
111             #Ignore this frame and any caller frame of this frame
112
113             ignore_frames = {}  #Should be a set, but it's not available on all python versions.
114             while curr_frame is not None:
115                 if basename(curr_frame.f_code.co_filename).startswith('pydev'):
116                     ignore_frames[curr_frame] = 1
117                 curr_frame = curr_frame.f_back
118
119
120             ret = ['<xml>\n']
121
122             ret.append('<for>\n')
123             if DEBUG:
124                 sys.stderr.write('Searching Referrers of obj with id="%s"\n' % (obj_id,))
125
126             ret.append(pydevd_vars.var_to_xml(
127                 searched_obj,
128                 'Referrers of obj with id="%s"' % (obj_id,)))
129             ret.append('</for>\n')
130
131             all_objects = None
132
133             for r in referrers:
134                 try:
135                     if dict_contains(ignore_frames, r):
136                         continue  #Skip the references we may add ourselves
137                 except:
138                     pass  #Ok: unhashable type checked...
139
140                 if r is referrers:
141                     continue
142
143                 r_type = type(r)
144                 r_id = str(id(r))
145
146                 representation = str(r_type)
147
148                 found_as = ''
149                 if r_type == frame_type:
150                     if DEBUG:
151                         sys.stderr.write('Found frame referrer: %r\n' % (r,))
152                     for key, val in r.f_locals.items():
153                         if val is searched_obj:
154                             found_as = key
155                             break
156
157                 elif r_type == dict:
158                     if DEBUG:
159                         sys.stderr.write('Found dict referrer: %r\n' % (r,))
160
161                     # Try to check if it's a value in the dict (and under which key it was found)
162                     for key, val in r.items():
163                         if val is searched_obj:
164                             found_as = key
165                             if DEBUG:
166                                 sys.stderr.write('    Found as %r in dict\n' % (found_as,))
167                             break
168
169                     #Ok, there's one annoying thing: many times we find it in a dict from an instance,
170                     #but with this we don't directly have the class, only the dict, so, to workaround that
171                     #we iterate over all reachable objects ad check if one of those has the given dict.
172                     if all_objects is None:
173                         all_objects = gc.get_objects()
174
175                     for x in all_objects:
176                         try:
177                             if getattr(x, '__dict__', None) is r:
178                                 r = x
179                                 r_type = type(x)
180                                 r_id = str(id(r))
181                                 representation = str(r_type)
182                                 break
183                         except:
184                             pass  #Just ignore any error here (i.e.: ReferenceError, etc.)
185
186                 elif r_type in (tuple, list):
187                     if DEBUG:
188                         sys.stderr.write('Found tuple referrer: %r\n' % (r,))
189
190                     #Don't use enumerate() because not all Python versions have it.
191                     i = 0
192                     for x in r:
193                         if x is searched_obj:
194                             found_as = '%s[%s]' % (r_type.__name__, i)
195                             if DEBUG:
196                                 sys.stderr.write('    Found as %s in tuple: \n' % (found_as,))
197                             break
198                         i += 1
199
200                 if found_as:
201                     if not isinstance(found_as, str):
202                         found_as = str(found_as)
203                     found_as = ' found_as="%s"' % (pydevd_vars.make_valid_xml_value(found_as),)
204
205                 ret.append(pydevd_vars.var_to_xml(
206                     r,
207                     representation,
208                     additionalInXml=' id="%s"%s' % (r_id, found_as)))
209         finally:
210             if DEBUG:
211                 sys.stderr.write('Done searching for references.\n')
212
213             #If we have any exceptions, don't keep dangling references from this frame to any of our objects.
214             all_objects = None
215             referrers = None
216             searched_obj = None
217             r = None
218             x = None
219             key = None
220             val = None
221             curr_frame = None
222             ignore_frames = None
223     except:
224         traceback.print_exc()
225         ret = ['<xml>\n']
226
227         ret.append('<for>\n')
228         ret.append(pydevd_vars.var_to_xml(
229             searched_obj,
230             'Error getting referrers for:',
231             additionalInXml=' id="%s"' % (id(searched_obj),)))
232         ret.append('</for>\n')
233         ret.append('</xml>')
234         ret = ''.join(ret)
235         return ret
236
237     ret.append('</xml>')
238     ret = ''.join(ret)
239     return ret
240