diff options
Diffstat (limited to 'python/helpers/pydev/pydevd_referrers.py')
-rw-r--r-- | python/helpers/pydev/pydevd_referrers.py | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/python/helpers/pydev/pydevd_referrers.py b/python/helpers/pydev/pydevd_referrers.py new file mode 100644 index 000000000000..66b1a0ef1df4 --- /dev/null +++ b/python/helpers/pydev/pydevd_referrers.py @@ -0,0 +1,238 @@ +from pydevd_constants import DictContains +import sys +import pydevd_vars +from os.path import basename +import traceback +try: + from urllib import quote, quote_plus, unquote, unquote_plus +except: + from urllib.parse import quote, quote_plus, unquote, unquote_plus #@Reimport @UnresolvedImport + +#=================================================================================================== +# print_var_node +#=================================================================================================== +def print_var_node(xml_node, stream): + name = xml_node.getAttribute('name') + value = xml_node.getAttribute('value') + val_type = xml_node.getAttribute('type') + + found_as = xml_node.getAttribute('found_as') + stream.write('Name: ') + stream.write(unquote_plus(name)) + stream.write(', Value: ') + stream.write(unquote_plus(value)) + stream.write(', Type: ') + stream.write(unquote_plus(val_type)) + if found_as: + stream.write(', Found as: %s' % (unquote_plus(found_as),)) + stream.write('\n') + +#=================================================================================================== +# print_referrers +#=================================================================================================== +def print_referrers(obj, stream=None): + if stream is None: + stream = sys.stdout + result = get_referrer_info(obj) + from xml.dom.minidom import parseString + dom = parseString(result) + + xml = dom.getElementsByTagName('xml')[0] + for node in xml.childNodes: + if node.nodeType == node.TEXT_NODE: + continue + + if node.localName == 'for': + stream.write('Searching references for: ') + for child in node.childNodes: + if child.nodeType == node.TEXT_NODE: + continue + print_var_node(child, stream) + + elif node.localName == 'var': + stream.write('Referrer found: ') + print_var_node(node, stream) + + else: + sys.stderr.write('Unhandled node: %s\n' % (node,)) + + return result + + +#=================================================================================================== +# get_referrer_info +#=================================================================================================== +def get_referrer_info(searched_obj): + DEBUG = 0 + if DEBUG: + sys.stderr.write('Getting referrers info.\n') + try: + try: + if searched_obj is None: + ret = ['<xml>\n'] + + ret.append('<for>\n') + ret.append(pydevd_vars.varToXML( + searched_obj, + 'Skipping getting referrers for None', + additionalInXml=' id="%s"' % (id(searched_obj),))) + ret.append('</for>\n') + ret.append('</xml>') + ret = ''.join(ret) + return ret + + obj_id = id(searched_obj) + + try: + if DEBUG: + sys.stderr.write('Getting referrers...\n') + import gc + referrers = gc.get_referrers(searched_obj) + except: + traceback.print_exc() + ret = ['<xml>\n'] + + ret.append('<for>\n') + ret.append(pydevd_vars.varToXML( + searched_obj, + 'Exception raised while trying to get_referrers.', + additionalInXml=' id="%s"' % (id(searched_obj),))) + ret.append('</for>\n') + ret.append('</xml>') + ret = ''.join(ret) + return ret + + if DEBUG: + sys.stderr.write('Found %s referrers.\n' % (len(referrers),)) + + curr_frame = sys._getframe() + frame_type = type(curr_frame) + + #Ignore this frame and any caller frame of this frame + + ignore_frames = {} #Should be a set, but it's not available on all python versions. + while curr_frame is not None: + if basename(curr_frame.f_code.co_filename).startswith('pydev'): + ignore_frames[curr_frame] = 1 + curr_frame = curr_frame.f_back + + + ret = ['<xml>\n'] + + ret.append('<for>\n') + if DEBUG: + sys.stderr.write('Searching Referrers of obj with id="%s"\n' % (obj_id,)) + + ret.append(pydevd_vars.varToXML( + searched_obj, + 'Referrers of obj with id="%s"' % (obj_id,))) + ret.append('</for>\n') + + all_objects = None + + for r in referrers: + try: + if DictContains(ignore_frames, r): + continue #Skip the references we may add ourselves + except: + pass #Ok: unhashable type checked... + + if r is referrers: + continue + + r_type = type(r) + r_id = str(id(r)) + + representation = str(r_type) + + found_as = '' + if r_type == frame_type: + if DEBUG: + sys.stderr.write('Found frame referrer: %r\n' % (r,)) + for key, val in r.f_locals.items(): + if val is searched_obj: + found_as = key + break + + elif r_type == dict: + if DEBUG: + sys.stderr.write('Found dict referrer: %r\n' % (r,)) + + # Try to check if it's a value in the dict (and under which key it was found) + for key, val in r.items(): + if val is searched_obj: + found_as = key + if DEBUG: + sys.stderr.write(' Found as %r in dict\n' % (found_as,)) + break + + #Ok, there's one annoying thing: many times we find it in a dict from an instance, + #but with this we don't directly have the class, only the dict, so, to workaround that + #we iterate over all reachable objects ad check if one of those has the given dict. + if all_objects is None: + all_objects = gc.get_objects() + + for x in all_objects: + try: + if getattr(x, '__dict__', None) is r: + r = x + r_type = type(x) + r_id = str(id(r)) + representation = str(r_type) + break + except: + pass #Just ignore any error here (i.e.: ReferenceError, etc.) + + elif r_type in (tuple, list): + if DEBUG: + sys.stderr.write('Found tuple referrer: %r\n' % (r,)) + + #Don't use enumerate() because not all Python versions have it. + i = 0 + for x in r: + if x is searched_obj: + found_as = '%s[%s]' % (r_type.__name__, i) + if DEBUG: + sys.stderr.write(' Found as %s in tuple: \n' % (found_as,)) + break + i += 1 + + if found_as: + found_as = ' found_as="%s"' % (pydevd_vars.makeValidXmlValue(found_as),) + + ret.append(pydevd_vars.varToXML( + r, + representation, + additionalInXml=' id="%s"%s' % (r_id, found_as))) + finally: + if DEBUG: + sys.stderr.write('Done searching for references.\n') + + #If we have any exceptions, don't keep dangling references from this frame to any of our objects. + all_objects = None + referrers = None + searched_obj = None + r = None + x = None + key = None + val = None + curr_frame = None + ignore_frames = None + except: + traceback.print_exc() + ret = ['<xml>\n'] + + ret.append('<for>\n') + ret.append(pydevd_vars.varToXML( + searched_obj, + 'Error getting referrers for:', + additionalInXml=' id="%s"' % (id(searched_obj),))) + ret.append('</for>\n') + ret.append('</xml>') + ret = ''.join(ret) + return ret + + ret.append('</xml>') + ret = ''.join(ret) + return ret + |