[Checkins] SVN: z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/ Removed garbage from package
Daniel Blackburn
blackburnd at gmail.com
Wed Nov 10 05:29:44 EST 2010
Log message for revision 118311:
Removed garbage from package
Changed:
D z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/checks.py
D z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/cond_perms.py
D z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/objgraph-1.py
D z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/objgraph.py
D z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/objgraph_orig.py
D z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/objgraph_trunk.diff
-=-
Deleted: z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/checks.py
===================================================================
--- z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/checks.py 2010-11-10 10:26:41 UTC (rev 118310)
+++ z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/checks.py 2010-11-10 10:29:43 UTC (rev 118311)
@@ -1,503 +0,0 @@
-"""
-Ad-hoc tools for drawing Python object reference graphs with graphviz.
-
-This module is more useful as a repository of sample code and ideas, than
-as a finished product. For documentation and background, read
-
- http://mg.pov.lt/blog/hunting-python-memleaks.html
- http://mg.pov.lt/blog/python-object-graphs.html
- http://mg.pov.lt/blog/object-graphs-with-graphviz.html
-
-in that order. Then use pydoc to read the docstrings, as there were
-improvements made since those blog posts.
-
-Copyright (c) 2008-2010 Marius Gedminas <marius at pov.lt>
-
-Released under the MIT licence.
-
-
-Changes
-=======
-
-1.3.2 (unreleased)
-------------------
-
-Compatibility with Python 2.4 and 2.5 (tempfile.NamedTemporaryFile has no
-delete argument).
-
-New function: most_common_types().
-
-
-1.3.1 (2010-07-17)
-------------------
-
-Rebuild an sdist with no missing files (fixes LP#606604).
-
-Added MANIFEST.in and a Makefile to check that setup.py sdist generates
-source distributions with no files missing.
-
-
-1.3 (2010-07-13)
-----------------
-
-Highlight objects with a __del__ method.
-
-Fixes LP#483411: suggest always passing [obj] to show_refs, show_backrefs,
-since obj might be a list/tuple.
-
-Fixes LP#514422: show_refs, show_backrefs don't create files in the current
-working directory any more. Instead they accept a filename argument, which
-can be a .dot file or a .png file. If None or not specified, those functions
-will try to spawn xdot as before.
-
-New extra_info argument to graph-generating functions (patch by Thouis Jones,
-LP#558914).
-
-setup.py should work with distutils now (LP#604430, thanks to Randy Heydon).
-
-
-1.2 (2009-03-25)
-----------------
-
-Project website, public source repository, uploaded to PyPI.
-
-No code changes.
-
-
-1.1dev (2008-09-05)
--------------------
-
-New function: show_refs() for showing forward references.
-
-New functions: typestats() and show_most_common_types().
-
-Object boxes are less crammed with useless information (such as IDs).
-
-Spawns xdot if it is available.
-"""
-# Permission is hereby granted, free of charge, to any person obtaining a
-# copy of this software and associated documentation files (the "Software"),
-# to deal in the Software without restriction, including without limitation
-# the rights to use, copy, modify, merge, publish, distribute, sublicense,
-# and/or sell copies of the Software, and to permit persons to whom the
-# Software is furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-# DEALINGS IN THE SOFTWARE.
-
-__author__ = "Marius Gedminas (marius at gedmin.as)"
-__copyright__ = "Copyright (c) 2008-2010 Marius Gedminas"
-__license__ = "MIT"
-__version__ = "1.3.2dev"
-__date__ = "2010-11-02"
-
-
-import gc
-import inspect
-import types
-import weakref
-import operator
-import os
-import subprocess
-import tempfile
-
-
-def count(typename):
- """Count objects tracked by the garbage collector with a given class name.
-
- Example:
-
- >>> count('dict')
- 42
- >>> count('MyClass')
- 3
-
- Note that the GC does not track simple objects like int or str.
- """
- return sum(1 for o in gc.get_objects() if type(o).__name__ == typename)
-
-
-def typestats():
- """Count the number of instances for each type tracked by the GC.
-
- Note that the GC does not track simple objects like int or str.
-
- Note that classes with the same name but defined in different modules
- will be lumped together.
- """
- stats = {}
- for o in gc.get_objects():
- stats.setdefault(type(o).__name__, 0)
- stats[type(o).__name__] += 1
- return stats
-
-
-def most_common_types(limit=10):
- """Count the names of types with the most instances.
-
- Note that the GC does not track simple objects like int or str.
-
- Note that classes with the same name but defined in different modules
- will be lumped together.
-
- Returns a list of (type_name, count), sorted most-frequent-first.
- """
- stats = sorted(typestats().items(), key=operator.itemgetter(1),
- reverse=True)
- if limit:
- stats = stats[:limit]
- return stats
-
-
-def show_most_common_types(limit=10):
- """Print the table of types of most common instances
-
- Note that the GC does not track simple objects like int or str.
-
- Note that classes with the same name but defined in different modules
- will be lumped together.
- """
- stats = most_common_types(limit)
- width = max(len(name) for name, count in stats)
- for name, count in stats:
- print name.ljust(width), count
-
-
-def by_type(typename):
- """Return objects tracked by the garbage collector with a given class name.
-
- Example:
-
- >>> by_type('MyClass')
- [<mymodule.MyClass object at 0x...>]
-
- Note that the GC does not track simple objects like int or str.
- """
- return [o for o in gc.get_objects() if type(o).__name__ == typename]
-
-
-def at(addr):
- """Return an object at a given memory address.
-
- The reverse of id(obj):
-
- >>> at(id(obj)) is obj
- True
-
- Note that this function does not work on objects that are not tracked by
- the GC (e.g. ints or strings).
- """
- for o in gc.get_objects():
- if id(o) == addr:
- return o
- return None
-
-
-def find_backref_chain(obj, predicate, max_depth=20, extra_ignore=()):
- """Find a shortest chain of references leading to obj.
-
- The start of the chain will be some object that matches your predicate.
-
- ``max_depth`` limits the search depth.
-
- ``extra_ignore`` can be a list of object IDs to exclude those objects from
- your search.
-
- Example:
-
- >>> find_backref_chain(obj, inspect.ismodule)
- [<module ...>, ..., obj]
-
- Returns None if such a chain could not be found.
- """
- queue = [obj]
- depth = {id(obj): 0}
- parent = {id(obj): None}
- ignore = set(extra_ignore)
- ignore.add(id(extra_ignore))
- ignore.add(id(queue))
- ignore.add(id(depth))
- ignore.add(id(parent))
- ignore.add(id(ignore))
- gc.collect()
- while queue:
- target = queue.pop(0)
- if predicate(target):
- chain = [target]
- while parent[id(target)] is not None:
- target = parent[id(target)]
- chain.append(target)
- return chain
- tdepth = depth[id(target)]
- if tdepth < max_depth:
- referrers = gc.get_referrers(target)
- ignore.add(id(referrers))
- for source in referrers:
- if inspect.isframe(source) or id(source) in ignore:
- continue
- if id(source) not in depth:
- depth[id(source)] = tdepth + 1
- parent[id(source)] = target
- queue.append(source)
- return None # not found
-
-
-def show_backrefs(objs, max_depth=3, extra_ignore=(), filter=None, too_many=10,
- highlight=None, filename=None, extra_info=(lambda _: '')):
- """Generate an object reference graph ending at ``objs``
-
- The graph will show you what objects refer to ``objs``, directly and
- indirectly.
-
- ``objs`` can be a single object, or it can be a list of objects. If
- unsure, wrap the single object in a new list.
-
- Produces a Graphviz .dot file and spawns a viewer (xdot) if one is
- installed, otherwise converts the graph to a .png image.
-
- Use ``max_depth`` and ``too_many`` to limit the depth and breadth of the
- graph.
-
- Use ``filter`` (a predicate) and ``extra_ignore`` (a list of object IDs) to
- remove undesired objects from the graph.
-
- Use ``highlight`` (a predicate) to highlight certain graph nodes in blue.
-
- Use ``extra_info`` (a function returning a string) to report extra
- information for objects.
-
- Examples:
-
- >>> show_backrefs(obj)
- >>> show_backrefs([obj1, obj2])
- >>> show_backrefs(obj, max_depth=5)
- >>> show_backrefs(obj, filter=lambda x: not inspect.isclass(x))
- >>> show_backrefs(obj, highlight=inspect.isclass)
- >>> show_backrefs(obj, extra_ignore=[id(locals())])
-
- """
- show_graph(objs, max_depth=max_depth, extra_ignore=extra_ignore,
- filter=filter, too_many=too_many, highlight=highlight,
- edge_func=gc.get_referrers, swap_source_target=False,
- filename=filename, extra_info=extra_info)
-
-
-def show_refs(objs, max_depth=3, extra_ignore=(), filter=None, too_many=10,
- highlight=None, filename=None, extra_info=(lambda _: '')):
- """Generate an object reference graph starting at ``objs``
-
- The graph will show you what objects are reachable from ``objs``, directly
- and indirectly.
-
- ``objs`` can be a single object, or it can be a list of objects. If
- unsure, wrap the single object in a new list.
-
- Produces a Graphviz .dot file and spawns a viewer (xdot) if one is
- installed, otherwise converts the graph to a .png image.
-
- Use ``max_depth`` and ``too_many`` to limit the depth and breadth of the
- graph.
-
- Use ``filter`` (a predicate) and ``extra_ignore`` (a list of object IDs) to
- remove undesired objects from the graph.
-
- Use ``highlight`` (a predicate) to highlight certain graph nodes in blue.
-
- Use ``extra_info`` (a function returning a string) to report extra
- information for objects.
-
- Examples:
-
- >>> show_refs(obj)
- >>> show_refs([obj1, obj2])
- >>> show_refs(obj, max_depth=5)
- >>> show_refs(obj, filter=lambda x: not inspect.isclass(x))
- >>> show_refs(obj, highlight=inspect.isclass)
- >>> show_refs(obj, extra_ignore=[id(locals())])
-
- """
- show_graph(objs, max_depth=max_depth, extra_ignore=extra_ignore,
- filter=filter, too_many=too_many, highlight=highlight,
- edge_func=gc.get_referents, swap_source_target=True,
- filename=filename, extra_info=extra_info)
-
-#
-# Internal helpers
-#
-
-def show_graph(objs, edge_func, swap_source_target,
- max_depth=3, extra_ignore=(), filter=None, too_many=10,
- highlight=None, filename=None, extra_info=(lambda _: '')):
- if not isinstance(objs, (list, tuple)):
- objs = [objs]
- if filename and filename.endswith('.dot'):
- f = file(filename, 'w')
- dot_filename = filename
- else:
- fd, dot_filename = tempfile.mkstemp('.dot', text=True)
- f = os.fdopen(fd, "w")
- print >> f, 'digraph ObjectGraph {'
- print >> f, ' node[shape=box, style=filled, fillcolor=white];'
- queue = []
- depth = {}
- ignore = set(extra_ignore)
- ignore.add(id(objs))
- ignore.add(id(extra_ignore))
- ignore.add(id(queue))
- ignore.add(id(depth))
- ignore.add(id(ignore))
- for obj in objs:
- print >> f, ' %s[fontcolor=red];' % (obj_node_id(obj))
- depth[id(obj)] = 0
- queue.append(obj)
- gc.collect()
- nodes = 0
- while queue:
- nodes += 1
- target = queue.pop(0)
- tdepth = depth[id(target)]
- print >> f, ' %s[label="%s"];' % (obj_node_id(target), obj_label(target, tdepth, extra_info))
- h, s, v = gradient((0, 0, 1), (0, 0, .3), tdepth, max_depth)
- if inspect.ismodule(target):
- h = .3
- s = 1
- if highlight and highlight(target):
- h = .6
- s = .6
- v = 0.5 + v * 0.5
- print >> f, ' %s[fillcolor="%g,%g,%g"];' % (obj_node_id(target), h, s, v)
- if v < 0.5:
- print >> f, ' %s[fontcolor=white];' % (obj_node_id(target))
- if hasattr(target, '__del__'):
- print >> f, " %s->%s_has_a_del[color=red,style=dotted,len=0.25,weight=10];" % (obj_node_id(target), obj_node_id(target))
- print >> f, ' %s_has_a_del[label="__del__",shape=doublecircle,height=0.25,color=red,fillcolor="0,.5,1",fontsize=6];' % (obj_node_id(target))
- if inspect.ismodule(target) or tdepth >= max_depth:
- continue
- neighbours = edge_func(target)
- ignore.add(id(neighbours))
- n = 0
- for source in neighbours:
- if inspect.isframe(source) or id(source) in ignore:
- continue
- if filter and not filter(source):
- continue
- if swap_source_target:
- srcnode, tgtnode = target, source
- else:
- srcnode, tgtnode = source, target
- elabel = edge_label(srcnode, tgtnode)
- print >> f, ' %s -> %s%s;' % (obj_node_id(srcnode), obj_node_id(tgtnode), elabel)
- if id(source) not in depth:
- depth[id(source)] = tdepth + 1
- queue.append(source)
- n += 1
- if n >= too_many:
- print >> f, ' %s[color=red];' % obj_node_id(target)
- break
- print >> f, "}"
- f.close()
- print "Graph written to %s (%d nodes)" % (dot_filename, nodes)
- if filename is None and program_in_path('xdot'):
- print "Spawning graph viewer (xdot)"
- subprocess.Popen(['xdot', dot_filename])
- elif program_in_path('dot'):
- if filename is None:
- print "Graph viewer (xdot) not found, generating a png instead"
- if filename and filename.endswith('.png'):
- f = file(filename, 'wb')
- png_filename = filename
- else:
- if filename is not None:
- print "Unrecognized file type (%s)" % filename
- fd, png_filename = tempfile.mkstemp('.png', text=False)
- f = os.fdopen(fd, "wb")
- dot = subprocess.Popen(['dot', '-Tpng', dot_filename],
- stdout=f)
- dot.wait()
- f.close()
- print "Image generated as %s" % png_filename
- else:
- if filename is None:
- print "Graph viewer (xdot) and image renderer (dot) not found, not doing anything else"
- else:
- print "Unrecognized file type (%s), not doing anything else" % filename
-
-
-def obj_node_id(obj):
- if isinstance(obj, weakref.ref):
- return 'all_weakrefs_are_one'
- return ('o%d' % id(obj)).replace('-', '_')
-
-
-def obj_label(obj, depth, extra_info):
- return quote(type(obj).__name__ + ':\n' +
- safe_repr(obj) + '\n' +
- extra_info(obj))
-
-
-def quote(s):
- return s.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n")
-
-
-def safe_repr(obj):
- try:
- return short_repr(obj)
- except:
- return '(unrepresentable)'
-
-
-def short_repr(obj):
- if isinstance(obj, (type, types.ModuleType, types.BuiltinMethodType,
- types.BuiltinFunctionType)):
- return obj.__name__
- if isinstance(obj, types.MethodType):
- if obj.im_self is not None:
- return obj.im_func.__name__ + ' (bound)'
- else:
- return obj.im_func.__name__
- if isinstance(obj, (tuple, list, dict, set)):
- return '%d items' % len(obj)
- if isinstance(obj, weakref.ref):
- return 'all_weakrefs_are_one'
- return repr(obj)[:40]
-
-
-def gradient(start_color, end_color, depth, max_depth):
- if max_depth == 0:
- # avoid division by zero
- return start_color
- h1, s1, v1 = start_color
- h2, s2, v2 = end_color
- f = float(depth) / max_depth
- h = h1 * (1-f) + h2 * f
- s = s1 * (1-f) + s2 * f
- v = v1 * (1-f) + v2 * f
- return h, s, v
-
-
-def edge_label(source, target):
- if isinstance(target, dict) and target is getattr(source, '__dict__', None):
- return ' [label="__dict__",weight=10]'
- elif isinstance(source, dict):
- for k, v in source.iteritems():
- if v is target:
- if isinstance(k, basestring) and k:
- return ' [label="%s",weight=2]' % quote(k)
- else:
- return ' [label="%s"]' % quote(safe_repr(k))
- return ''
-
-
-def program_in_path(program):
- path = os.environ.get("PATH", os.defpath).split(os.pathsep)
- path = [os.path.join(dir, program) for dir in path]
- path = [True for file in path if os.path.isfile(file)]
- return bool(path)
Deleted: z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/cond_perms.py
===================================================================
--- z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/cond_perms.py 2010-11-10 10:26:41 UTC (rev 118310)
+++ z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/cond_perms.py 2010-11-10 10:29:43 UTC (rev 118311)
@@ -1,4 +0,0 @@
-
-
-def version():
- import pdb; pdb.set_trace()
Deleted: z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/objgraph-1.py
===================================================================
--- z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/objgraph-1.py 2010-11-10 10:26:41 UTC (rev 118310)
+++ z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/objgraph-1.py 2010-11-10 10:29:43 UTC (rev 118311)
@@ -1,503 +0,0 @@
-"""
-Ad-hoc tools for drawing Python object reference graphs with graphviz.
-
-This module is more useful as a repository of sample code and ideas, than
-as a finished product. For documentation and background, read
-
- http://mg.pov.lt/blog/hunting-python-memleaks.html
- http://mg.pov.lt/blog/python-object-graphs.html
- http://mg.pov.lt/blog/object-graphs-with-graphviz.html
-
-in that order. Then use pydoc to read the docstrings, as there were
-improvements made since those blog posts.
-
-Copyright (c) 2008-2010 Marius Gedminas <marius at pov.lt>
-
-Released under the MIT licence.
-
-
-Changes
-=======
-
-1.3.2 (unreleased)
-------------------
-
-Compatibility with Python 2.4 and 2.5 (tempfile.NamedTemporaryFile has no
-delete argument).
-
-New function: most_common_types().
-
-
-1.3.1 (2010-07-17)
-------------------
-
-Rebuild an sdist with no missing files (fixes LP#606604).
-
-Added MANIFEST.in and a Makefile to check that setup.py sdist generates
-source distributions with no files missing.
-
-
-1.3 (2010-07-13)
-----------------
-
-Highlight objects with a __del__ method.
-
-Fixes LP#483411: suggest always passing [obj] to show_refs, show_backrefs,
-since obj might be a list/tuple.
-
-Fixes LP#514422: show_refs, show_backrefs don't create files in the current
-working directory any more. Instead they accept a filename argument, which
-can be a .dot file or a .png file. If None or not specified, those functions
-will try to spawn xdot as before.
-
-New extra_info argument to graph-generating functions (patch by Thouis Jones,
-LP#558914).
-
-setup.py should work with distutils now (LP#604430, thanks to Randy Heydon).
-
-
-1.2 (2009-03-25)
-----------------
-
-Project website, public source repository, uploaded to PyPI.
-
-No code changes.
-
-
-1.1dev (2008-09-05)
--------------------
-
-New function: show_refs() for showing forward references.
-
-New functions: typestats() and show_most_common_types().
-
-Object boxes are less crammed with useless information (such as IDs).
-
-Spawns xdot if it is available.
-"""
-# Permission is hereby granted, free of charge, to any person obtaining a
-# copy of this software and associated documentation files (the "Software"),
-# to deal in the Software without restriction, including without limitation
-# the rights to use, copy, modify, merge, publish, distribute, sublicense,
-# and/or sell copies of the Software, and to permit persons to whom the
-# Software is furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-# DEALINGS IN THE SOFTWARE.
-
-__author__ = "Marius Gedminas (marius at gedmin.as)"
-__copyright__ = "Copyright (c) 2008-2010 Marius Gedminas"
-__license__ = "MIT"
-__version__ = "1.3.2dev"
-__date__ = "2010-11-02"
-
-
-import gc
-import inspect
-import types
-import weakref
-import operator
-import os
-import subprocess
-import tempfile
-
-
-def count(typename):
- """Count objects tracked by the garbage collector with a given class name.
-
- Example:
-
- >>> count('dict')
- 42
- >>> count('MyClass')
- 3
-
- Note that the GC does not track simple objects like int or str.
- """
- return sum(1 for o in gc.get_objects() if type(o).__name__ == typename)
-
-
-def typestats():
- """Count the number of instances for each type tracked by the GC.
-
- Note that the GC does not track simple objects like int or str.
-
- Note that classes with the same name but defined in different modules
- will be lumped together.
- """
- stats = {}
- for o in gc.get_objects():
- stats.setdefault(type(o).__name__, 0)
- stats[type(o).__name__] += 1
- return stats
-
-
-def most_common_types(limit=10):
- """Count the names of types with the most instances.
-
- Note that the GC does not track simple objects like int or str.
-
- Note that classes with the same name but defined in different modules
- will be lumped together.
-
- Returns a list of (type_name, count), sorted most-frequent-first.
- """
- stats = sorted(typestats().items(), key=operator.itemgetter(1),
- reverse=True)
- if limit:
- stats = stats[:limit]
- return stats
-
-
-def show_most_common_types(limit=10):
- """Print the table of types of most common instances
-
- Note that the GC does not track simple objects like int or str.
-
- Note that classes with the same name but defined in different modules
- will be lumped together.
- """
- stats = most_common_types(limit)
- width = max(len(name) for name, count in stats)
- for name, count in stats:
- print name.ljust(width), count
-
-
-def by_type(typename):
- """Return objects tracked by the garbage collector with a given class name.
-
- Example:
-
- >>> by_type('MyClass')
- [<mymodule.MyClass object at 0x...>]
-
- Note that the GC does not track simple objects like int or str.
- """
- return [o for o in gc.get_objects() if type(o).__name__ == typename]
-
-
-def at(addr):
- """Return an object at a given memory address.
-
- The reverse of id(obj):
-
- >>> at(id(obj)) is obj
- True
-
- Note that this function does not work on objects that are not tracked by
- the GC (e.g. ints or strings).
- """
- for o in gc.get_objects():
- if id(o) == addr:
- return o
- return None
-
-
-def find_backref_chain(obj, predicate, max_depth=20, extra_ignore=()):
- """Find a shortest chain of references leading to obj.
-
- The start of the chain will be some object that matches your predicate.
-
- ``max_depth`` limits the search depth.
-
- ``extra_ignore`` can be a list of object IDs to exclude those objects from
- your search.
-
- Example:
-
- >>> find_backref_chain(obj, inspect.ismodule)
- [<module ...>, ..., obj]
-
- Returns None if such a chain could not be found.
- """
- queue = [obj]
- depth = {id(obj): 0}
- parent = {id(obj): None}
- ignore = set(extra_ignore)
- ignore.add(id(extra_ignore))
- ignore.add(id(queue))
- ignore.add(id(depth))
- ignore.add(id(parent))
- ignore.add(id(ignore))
- gc.collect()
- while queue:
- target = queue.pop(0)
- if predicate(target):
- chain = [target]
- while parent[id(target)] is not None:
- target = parent[id(target)]
- chain.append(target)
- return chain
- tdepth = depth[id(target)]
- if tdepth < max_depth:
- referrers = gc.get_referrers(target)
- ignore.add(id(referrers))
- for source in referrers:
- if inspect.isframe(source) or id(source) in ignore:
- continue
- if id(source) not in depth:
- depth[id(source)] = tdepth + 1
- parent[id(source)] = target
- queue.append(source)
- return None # not found
-
-
-def show_backrefs(objs, max_depth=3, extra_ignore=(), filter=None, too_many=10,
- highlight=None, filename=None, extra_info=(lambda _: '')):
- """Generate an object reference graph ending at ``objs``
-
- The graph will show you what objects refer to ``objs``, directly and
- indirectly.
-
- ``objs`` can be a single object, or it can be a list of objects. If
- unsure, wrap the single object in a new list.
-
- Produces a Graphviz .dot file and spawns a viewer (xdot) if one is
- installed, otherwise converts the graph to a .png image.
-
- Use ``max_depth`` and ``too_many`` to limit the depth and breadth of the
- graph.
-
- Use ``filter`` (a predicate) and ``extra_ignore`` (a list of object IDs) to
- remove undesired objects from the graph.
-
- Use ``highlight`` (a predicate) to highlight certain graph nodes in blue.
-
- Use ``extra_info`` (a function returning a string) to report extra
- information for objects.
-
- Examples:
-
- >>> show_backrefs(obj)
- >>> show_backrefs([obj1, obj2])
- >>> show_backrefs(obj, max_depth=5)
- >>> show_backrefs(obj, filter=lambda x: not inspect.isclass(x))
- >>> show_backrefs(obj, highlight=inspect.isclass)
- >>> show_backrefs(obj, extra_ignore=[id(locals())])
-
- """
- show_graph(objs, max_depth=max_depth, extra_ignore=extra_ignore,
- filter=filter, too_many=too_many, highlight=highlight,
- edge_func=gc.get_referrers, swap_source_target=False,
- filename=filename, extra_info=extra_info)
-
-
-def show_refs(objs, max_depth=3, extra_ignore=(), filter=None, too_many=10,
- highlight=None, filename=None, extra_info=(lambda _: '')):
- """Generate an object reference graph starting at ``objs``
-
- The graph will show you what objects are reachable from ``objs``, directly
- and indirectly.
-
- ``objs`` can be a single object, or it can be a list of objects. If
- unsure, wrap the single object in a new list.
-
- Produces a Graphviz .dot file and spawns a viewer (xdot) if one is
- installed, otherwise converts the graph to a .png image.
-
- Use ``max_depth`` and ``too_many`` to limit the depth and breadth of the
- graph.
-
- Use ``filter`` (a predicate) and ``extra_ignore`` (a list of object IDs) to
- remove undesired objects from the graph.
-
- Use ``highlight`` (a predicate) to highlight certain graph nodes in blue.
-
- Use ``extra_info`` (a function returning a string) to report extra
- information for objects.
-
- Examples:
-
- >>> show_refs(obj)
- >>> show_refs([obj1, obj2])
- >>> show_refs(obj, max_depth=5)
- >>> show_refs(obj, filter=lambda x: not inspect.isclass(x))
- >>> show_refs(obj, highlight=inspect.isclass)
- >>> show_refs(obj, extra_ignore=[id(locals())])
-
- """
- show_graph(objs, max_depth=max_depth, extra_ignore=extra_ignore,
- filter=filter, too_many=too_many, highlight=highlight,
- edge_func=gc.get_referents, swap_source_target=True,
- filename=filename, extra_info=extra_info)
-
-#
-# Internal helpers
-#
-
-def show_graph(objs, edge_func, swap_source_target,
- max_depth=3, extra_ignore=(), filter=None, too_many=10,
- highlight=None, filename=None, extra_info=(lambda _: '')):
- if not isinstance(objs, (list, tuple)):
- objs = [objs]
- if filename and filename.endswith('.dot'):
- f = file(filename, 'w')
- dot_filename = filename
- else:
- fd, dot_filename = tempfile.mkstemp('.dot', text=True)
- f = os.fdopen(fd, "w")
- print >> f, 'digraph ObjectGraph {'
- print >> f, ' node[shape=box, style=filled, fillcolor=white];'
- queue = []
- depth = {}
- ignore = set(extra_ignore)
- ignore.add(id(objs))
- ignore.add(id(extra_ignore))
- ignore.add(id(queue))
- ignore.add(id(depth))
- ignore.add(id(ignore))
- for obj in objs:
- print >> f, ' %s[fontcolor=red];' % (obj_node_id(obj))
- depth[id(obj)] = 0
- queue.append(obj)
- gc.collect()
- nodes = 0
- while queue:
- nodes += 1
- target = queue.pop(0)
- tdepth = depth[id(target)]
- print >> f, ' %s[label="%s"];' % (obj_node_id(target), obj_label(target, tdepth, extra_info))
- h, s, v = gradient((0, 0, 1), (0, 0, .3), tdepth, max_depth)
- if inspect.ismodule(target):
- h = .3
- s = 1
- if highlight and highlight(target):
- h = .6
- s = .6
- v = 0.5 + v * 0.5
- print >> f, ' %s[fillcolor="%g,%g,%g"];' % (obj_node_id(target), h, s, v)
- if v < 0.5:
- print >> f, ' %s[fontcolor=white];' % (obj_node_id(target))
- if hasattr(target, '__del__'):
- print >> f, " %s->%s_has_a_del[color=red,style=dotted,len=0.25,weight=10];" % (obj_node_id(target), obj_node_id(target))
- print >> f, ' %s_has_a_del[label="__del__",shape=doublecircle,height=0.25,color=red,fillcolor="0,.5,1",fontsize=6];' % (obj_node_id(target))
- if inspect.ismodule(target) or tdepth >= max_depth:
- continue
- neighbours = edge_func(target)
- ignore.add(id(neighbours))
- n = 0
- for source in neighbours:
- if inspect.isframe(source) or id(source) in ignore:
- continue
- if filter and not filter(source):
- continue
- if swap_source_target:
- srcnode, tgtnode = target, source
- else:
- srcnode, tgtnode = source, target
- elabel = edge_label(srcnode, tgtnode)
- print >> f, ' %s -> %s%s;' % (obj_node_id(srcnode), obj_node_id(tgtnode), elabel)
- if id(source) not in depth:
- depth[id(source)] = tdepth + 1
- queue.append(source)
- n += 1
- if n >= too_many:
- print >> f, ' %s[color=red];' % obj_node_id(target)
- break
- print >> f, "}"
- f.close()
- print "Graph written to %s (%d nodes)" % (dot_filename, nodes)
- if filename is None and program_in_path('xdot'):
- print "Spawning graph viewer (xdot)"
- subprocess.Popen(['xdot', dot_filename])
- elif program_in_path('dot'):
- if filename is None:
- print "Graph viewer (xdot) not found, generating a png instead"
- if filename and filename.endswith('.png'):
- f = file(filename, 'wb')
- png_filename = filename
- else:
- if filename is not None:
- print "Unrecognized file type (%s)" % filename
- fd, png_filename = tempfile.mkstemp('.png', text=False)
- f = os.fdopen(fd, "wb")
- dot = subprocess.Popen(['dot', '-Tpng', dot_filename],
- stdout=f)
- dot.wait()
- f.close()
- print "Image generated as %s" % png_filename
- else:
- if filename is None:
- print "Graph viewer (xdot) and image renderer (dot) not found, not doing anything else"
- else:
- print "Unrecognized file type (%s), not doing anything else" % filename
-
-
-def obj_node_id(obj):
- if isinstance(obj, weakref.ref):
- return 'all_weakrefs_are_one'
- return ('o%d' % id(obj)).replace('-', '_')
-
-
-def obj_label(obj, depth, extra_info):
- return quote(type(obj).__name__ + ':\n' +
- safe_repr(obj) + '\n' +
- extra_info(obj))
-
-
-def quote(s):
- return s.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n")
-
-
-def safe_repr(obj):
- try:
- return short_repr(obj)
- except:
- return '(unrepresentable)'
-
-
-def short_repr(obj):
- if isinstance(obj, (type, types.ModuleType, types.BuiltinMethodType,
- types.BuiltinFunctionType)):
- return obj.__name__
- if isinstance(obj, types.MethodType):
- if obj.im_self is not None:
- return obj.im_func.__name__ + ' (bound)'
- else:
- return obj.im_func.__name__
- if isinstance(obj, (tuple, list, dict, set)):
- return '%d items' % len(obj)
- if isinstance(obj, weakref.ref):
- return 'all_weakrefs_are_one'
- return repr(obj)[:40]
-
-
-def gradient(start_color, end_color, depth, max_depth):
- if max_depth == 0:
- # avoid division by zero
- return start_color
- h1, s1, v1 = start_color
- h2, s2, v2 = end_color
- f = float(depth) / max_depth
- h = h1 * (1-f) + h2 * f
- s = s1 * (1-f) + s2 * f
- v = v1 * (1-f) + v2 * f
- return h, s, v
-
-
-def edge_label(source, target):
- if isinstance(target, dict) and target is getattr(source, '__dict__', None):
- return ' [label="__dict__",weight=10]'
- elif isinstance(source, dict):
- for k, v in source.iteritems():
- if v is target:
- if isinstance(k, basestring) and k:
- return ' [label="%s",weight=2]' % quote(k)
- else:
- return ' [label="%s"]' % quote(safe_repr(k))
- return ''
-
-
-def program_in_path(program):
- path = os.environ.get("PATH", os.defpath).split(os.pathsep)
- path = [os.path.join(dir, program) for dir in path]
- path = [True for file in path if os.path.isfile(file)]
- return bool(path)
Deleted: z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/objgraph.py
===================================================================
--- z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/objgraph.py 2010-11-10 10:26:41 UTC (rev 118310)
+++ z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/objgraph.py 2010-11-10 10:29:43 UTC (rev 118311)
@@ -1,485 +0,0 @@
-"""
-Ad-hoc tools for drawing Python object reference graphs with graphviz.
-
-This module is more useful as a repository of sample code and ideas, than
-as a finished product. For documentation and background, read
-
- http://mg.pov.lt/blog/hunting-python-memleaks.html
- http://mg.pov.lt/blog/python-object-graphs.html
- http://mg.pov.lt/blog/object-graphs-with-graphviz.html
-
-in that order. Then use pydoc to read the docstrings, as there were
-improvements made since those blog posts.
-
-Copyright (c) 2008-2010 Marius Gedminas <marius at pov.lt>
-
-Released under the MIT licence.
-
-
-Changes
-=======
-
-1.3.2 (unreleased)
-------------------
-
-(no changes yet)
-
-
-1.3.1 (2010-07-17)
-------------------
-
-Rebuild an sdist with no missing files (fixes LP#606604)
-
-Added MANIFEST.in and a Makefile to check that setup.py sdist generates
-source distributions with no files missing.
-
-
-1.3 (2010-07-13)
-----------------
-
-Highlight objects with a __del__ method.
-
-Fixes LP#483411, LP#514422,
-
-show_refs, show_backrefs don't create files in the current working
-directory any more. Instead they accept a filename argument. It can be a
-.dot file or a .png file. If None or not specified, those functions will try
-to spawn xdot as before. Fixes LP#514422.
-
-New extra_info argument to graph-generating functions (patch by Thouis Jones,
-LP#558914).
-
-setup.py should work with distutils now (LP#604430, thanks to Randy Heydon).
-
-
-1.2 (2009-03-25)
-----------------
-
-Project website, public source repository, uploaded to PyPI.
-
-No code changes.
-
-
-1.1dev (2008-09-05)
--------------------
-
-New function: show_refs() for showing forward references.
-
-New functions: typestats() and show_most_common_types().
-
-Object boxes are less crammed with useless information (such as IDs).
-
-Spawns xdot if it is available.
-"""
-# Permission is hereby granted, free of charge, to any person obtaining a
-# copy of this software and associated documentation files (the "Software"),
-# to deal in the Software without restriction, including without limitation
-# the rights to use, copy, modify, merge, publish, distribute, sublicense,
-# and/or sell copies of the Software, and to permit persons to whom the
-# Software is furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-# DEALINGS IN THE SOFTWARE.
-
-__author__ = "Marius Gedminas (marius at gedmin.as)"
-__copyright__ = "Copyright (c) 2008-2010 Marius Gedminas"
-__license__ = "MIT"
-__version__ = "1.3.2dev"
-__date__ = "2010-07-17"
-
-
-import gc
-import inspect
-import types
-import weakref
-import operator
-import os
-import subprocess
-from tempfile import NamedTemporaryFile
-
-
-def count(typename):
- """Count objects tracked by the garbage collector with a given class name.
-
- Example:
-
- >>> count('dict')
- 42
- >>> count('MyClass')
- 3
-
- Note that the GC does not track simple objects like int or str.
- """
- return sum(1 for o in gc.get_objects() if type(o).__name__ == typename)
-
-
-def typestats():
- """Count the number of instances for each type tracked by the GC.
-
- Note that the GC does not track simple objects like int or str.
-
- Note that classes with the same name but defined in different modules
- will be lumped together.
- """
- stats = {}
- for o in gc.get_objects():
- stats.setdefault(type(o).__name__, 0)
- stats[type(o).__name__] += 1
- return stats
-
-
-def show_most_common_types(limit=10):
- """Count the names of types with the most instances.
-
- Note that the GC does not track simple objects like int or str.
-
- Note that classes with the same name but defined in different modules
- will be lumped together.
- """
- stats = sorted(typestats().items(), key=operator.itemgetter(1),
- reverse=True)
- if limit:
- stats = stats[:limit]
- width = max(len(name) for name, count in stats)
- for name, count in stats[:limit]:
- print name.ljust(width), count
-
-
-def by_type(typename):
- """Return objects tracked by the garbage collector with a given class name.
-
- Example:
-
- >>> by_type('MyClass')
- [<mymodule.MyClass object at 0x...>]
-
- Note that the GC does not track simple objects like int or str.
- """
- return [o for o in gc.get_objects() if type(o).__name__ == typename]
-
-
-def at(addr):
- """Return an object at a given memory address.
-
- The reverse of id(obj):
-
- >>> at(id(obj)) is obj
- True
-
- Note that this function does not work on objects that are not tracked by
- the GC (e.g. ints or strings).
- """
- for o in gc.get_objects():
- if id(o) == addr:
- return o
- return None
-
-
-def find_backref_chain(obj, predicate, max_depth=20, extra_ignore=()):
- """Find a shortest chain of references leading to obj.
-
- The start of the chain will be some object that matches your predicate.
-
- ``max_depth`` limits the search depth.
-
- ``extra_ignore`` can be a list of object IDs to exclude those objects from
- your search.
-
- Example:
-
- >>> find_backref_chain(obj, inspect.ismodule)
- [<module ...>, ..., obj]
-
- Returns None if such a chain could not be found.
- """
- queue = [obj]
- depth = {id(obj): 0}
- parent = {id(obj): None}
- ignore = set(extra_ignore)
- ignore.add(id(extra_ignore))
- ignore.add(id(queue))
- ignore.add(id(depth))
- ignore.add(id(parent))
- ignore.add(id(ignore))
- gc.collect()
- while queue:
- target = queue.pop(0)
- if predicate(target):
- chain = [target]
- while parent[id(target)] is not None:
- target = parent[id(target)]
- chain.append(target)
- return chain
- tdepth = depth[id(target)]
- if tdepth < max_depth:
- referrers = gc.get_referrers(target)
- ignore.add(id(referrers))
- for source in referrers:
- if inspect.isframe(source) or id(source) in ignore:
- continue
- if id(source) not in depth:
- depth[id(source)] = tdepth + 1
- parent[id(source)] = target
- queue.append(source)
- return None # not found
-
-
-def show_backrefs(objs, max_depth=3, extra_ignore=(), filter=None, too_many=10,
- highlight=None, filename=None, extra_info=(lambda _: '')):
- """Generate an object reference graph ending at ``objs``
-
- The graph will show you what objects refer to ``objs``, directly and
- indirectly.
-
- ``objs`` can be a single object, or it can be a list of objects. If
- unsure, wrap the single object in a new list.
-
- Produces a Graphviz .dot file and spawns a viewer (xdot) if one is
- installed, otherwise converts the graph to a .png image.
-
- Use ``max_depth`` and ``too_many`` to limit the depth and breadth of the
- graph.
-
- Use ``filter`` (a predicate) and ``extra_ignore`` (a list of object IDs) to
- remove undesired objects from the graph.
-
- Use ``highlight`` (a predicate) to highlight certain graph nodes in blue.
-
- Use ``extra_info`` (a function returning a string) to report extra
- information for objects.
-
- Examples:
-
- >>> show_backrefs(obj)
- >>> show_backrefs([obj1, obj2])
- >>> show_backrefs(obj, max_depth=5)
- >>> show_backrefs(obj, filter=lambda x: not inspect.isclass(x))
- >>> show_backrefs(obj, highlight=inspect.isclass)
- >>> show_backrefs(obj, extra_ignore=[id(locals())])
-
- """
- show_graph(objs, max_depth=max_depth, extra_ignore=extra_ignore,
- filter=filter, too_many=too_many, highlight=highlight,
- edge_func=gc.get_referrers, swap_source_target=False,
- filename=filename, extra_info=extra_info)
-
-
-def show_refs(objs, max_depth=3, extra_ignore=(), filter=None, too_many=10,
- highlight=None, filename=None, extra_info=(lambda _: '')):
- """Generate an object reference graph starting at ``objs``
-
- The graph will show you what objects are reachable from ``objs``, directly
- and indirectly.
-
- ``objs`` can be a single object, or it can be a list of objects. If
- unsure, wrap the single object in a new list.
-
- Produces a Graphviz .dot file and spawns a viewer (xdot) if one is
- installed, otherwise converts the graph to a .png image.
-
- Use ``max_depth`` and ``too_many`` to limit the depth and breadth of the
- graph.
-
- Use ``filter`` (a predicate) and ``extra_ignore`` (a list of object IDs) to
- remove undesired objects from the graph.
-
- Use ``highlight`` (a predicate) to highlight certain graph nodes in blue.
-
- Use ``extra_info`` (a function returning a string) to report extra
- information for objects.
-
- Examples:
-
- >>> show_refs(obj)
- >>> show_refs([obj1, obj2])
- >>> show_refs(obj, max_depth=5)
- >>> show_refs(obj, filter=lambda x: not inspect.isclass(x))
- >>> show_refs(obj, highlight=inspect.isclass)
- >>> show_refs(obj, extra_ignore=[id(locals())])
-
- """
- show_graph(objs, max_depth=max_depth, extra_ignore=extra_ignore,
- filter=filter, too_many=too_many, highlight=highlight,
- edge_func=gc.get_referents, swap_source_target=True,
- filename=filename, extra_info=extra_info)
-
-#
-# Internal helpers
-#
-
-def show_graph(objs, edge_func, swap_source_target,
- max_depth=3, extra_ignore=(), filter=None, too_many=10,
- highlight=None, filename=None, extra_info=(lambda _: '')):
- if not isinstance(objs, (list, tuple)):
- objs = [objs]
- if filename and filename.endswith('.dot'):
- f = file(filename, 'w')
- dot_filename = filename
- else:
- f = NamedTemporaryFile('w', suffix='.dot', delete=False)
- dot_filename = f.name
- print >> f, 'digraph ObjectGraph {'
- print >> f, ' node[shape=box, style=filled, fillcolor=white];'
- queue = []
- depth = {}
- ignore = set(extra_ignore)
- ignore.add(id(objs))
- ignore.add(id(extra_ignore))
- ignore.add(id(queue))
- ignore.add(id(depth))
- ignore.add(id(ignore))
- for obj in objs:
- print >> f, ' %s[fontcolor=red];' % (obj_node_id(obj))
- depth[id(obj)] = 0
- queue.append(obj)
- gc.collect()
- nodes = 0
- while queue:
- nodes += 1
- target = queue.pop(0)
- tdepth = depth[id(target)]
- print >> f, ' %s[label="%s"];' % (obj_node_id(target), obj_label(target, tdepth, extra_info))
- h, s, v = gradient((0, 0, 1), (0, 0, .3), tdepth, max_depth)
- if inspect.ismodule(target):
- h = .3
- s = 1
- if highlight and highlight(target):
- h = .6
- s = .6
- v = 0.5 + v * 0.5
- print >> f, ' %s[fillcolor="%g,%g,%g"];' % (obj_node_id(target), h, s, v)
- if v < 0.5:
- print >> f, ' %s[fontcolor=white];' % (obj_node_id(target))
- if hasattr(target, '__del__'):
- print >> f, " %s->%s_has_a_del[color=red,style=dotted,len=0.25,weight=10];" % (obj_node_id(target), obj_node_id(target))
- print >> f, ' %s_has_a_del[label="__del__",shape=doublecircle,height=0.25,color=red,fillcolor="0,.5,1",fontsize=6];' % (obj_node_id(target))
- if inspect.ismodule(target) or tdepth >= max_depth:
- continue
- neighbours = edge_func(target)
- ignore.add(id(neighbours))
- n = 0
- for source in neighbours:
- if inspect.isframe(source) or id(source) in ignore:
- continue
- if filter and not filter(source):
- continue
- if swap_source_target:
- srcnode, tgtnode = target, source
- else:
- srcnode, tgtnode = source, target
- elabel = edge_label(srcnode, tgtnode)
- print >> f, ' %s -> %s%s;' % (obj_node_id(srcnode), obj_node_id(tgtnode), elabel)
- if id(source) not in depth:
- depth[id(source)] = tdepth + 1
- queue.append(source)
- n += 1
- if n >= too_many:
- print >> f, ' %s[color=red];' % obj_node_id(target)
- break
- print >> f, "}"
- f.close()
- print "Graph written to %s (%d nodes)" % (dot_filename, nodes)
- if filename is None and program_in_path('xdot'):
- print "Spawning graph viewer (xdot)"
- subprocess.Popen(['xdot', dot_filename])
- elif program_in_path('dot'):
- if filename is None:
- print "Graph viewer (xdot) not found, generating a png instead"
- if filename and filename.endswith('.png'):
- f = file(filename, 'wb')
- png_filename = filename
- else:
- if filename is not None:
- print "Unrecognized file type (%s)" % filename
- f = NamedTemporaryFile('wb', suffix='.png', delete=False)
- png_filename = f.name
- dot = subprocess.Popen(['dot', '-Tpng', dot_filename],
- stdout=f)
- dot.wait()
- f.close()
- print "Image generated as %s" % png_filename
- else:
- if filename is None:
- print "Graph viewer (xdot) and image renderer (dot) not found, not doing anything else"
- else:
- print "Unrecognized file type (%s), not doing anything else" % filename
-
-
-def obj_node_id(obj):
- if isinstance(obj, weakref.ref):
- return 'all_weakrefs_are_one'
- return ('o%d' % id(obj)).replace('-', '_')
-
-
-def obj_label(obj, depth, extra_info):
- return quote(type(obj).__name__ + ':\n' +
- safe_repr(obj) + '\n' +
- extra_info(obj))
-
-
-def quote(s):
- return s.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n")
-
-
-def safe_repr(obj):
- try:
- return short_repr(obj)
- except:
- return '(unrepresentable)'
-
-
-def short_repr(obj):
- if isinstance(obj, (type, types.ModuleType, types.BuiltinMethodType,
- types.BuiltinFunctionType)):
- return obj.__name__
- if isinstance(obj, types.MethodType):
- if obj.im_self is not None:
- return obj.im_func.__name__ + ' (bound)'
- else:
- return obj.im_func.__name__
- if isinstance(obj, (tuple, list, dict, set)):
- return '%d items' % len(obj)
- if isinstance(obj, weakref.ref):
- return 'all_weakrefs_are_one'
- return repr(obj)[:40]
-
-
-def gradient(start_color, end_color, depth, max_depth):
- if max_depth == 0:
- # avoid division by zero
- return start_color
- h1, s1, v1 = start_color
- h2, s2, v2 = end_color
- f = float(depth) / max_depth
- h = h1 * (1-f) + h2 * f
- s = s1 * (1-f) + s2 * f
- v = v1 * (1-f) + v2 * f
- return h, s, v
-
-
-def edge_label(source, target):
- if isinstance(target, dict) and target is getattr(source, '__dict__', None):
- return ' [label="__dict__",weight=10]'
- elif isinstance(source, dict):
- for k, v in source.iteritems():
- if v is target:
- if isinstance(k, basestring) and k:
- return ' [label="%s",weight=2]' % quote(k)
- else:
- return ' [label="%s"]' % quote(safe_repr(k))
- return ''
-
-
-def program_in_path(program):
- path = os.environ.get("PATH", os.defpath).split(os.pathsep)
- path = [os.path.join(dir, program) for dir in path]
- path = [True for file in path if os.path.isfile(file)]
- return bool(path)
Deleted: z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/objgraph_orig.py
===================================================================
--- z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/objgraph_orig.py 2010-11-10 10:26:41 UTC (rev 118310)
+++ z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/objgraph_orig.py 2010-11-10 10:29:43 UTC (rev 118311)
@@ -1,485 +0,0 @@
-"""
-Ad-hoc tools for drawing Python object reference graphs with graphviz.
-
-This module is more useful as a repository of sample code and ideas, than
-as a finished product. For documentation and background, read
-
- http://mg.pov.lt/blog/hunting-python-memleaks.html
- http://mg.pov.lt/blog/python-object-graphs.html
- http://mg.pov.lt/blog/object-graphs-with-graphviz.html
-
-in that order. Then use pydoc to read the docstrings, as there were
-improvements made since those blog posts.
-
-Copyright (c) 2008-2010 Marius Gedminas <marius at pov.lt>
-
-Released under the MIT licence.
-
-
-Changes
-=======
-
-1.3.2 (unreleased)
-------------------
-
-(no changes yet)
-
-
-1.3.1 (2010-07-17)
-------------------
-
-Rebuild an sdist with no missing files (fixes LP#606604)
-
-Added MANIFEST.in and a Makefile to check that setup.py sdist generates
-source distributions with no files missing.
-
-
-1.3 (2010-07-13)
-----------------
-
-Highlight objects with a __del__ method.
-
-Fixes LP#483411, LP#514422,
-
-show_refs, show_backrefs don't create files in the current working
-directory any more. Instead they accept a filename argument. It can be a
-.dot file or a .png file. If None or not specified, those functions will try
-to spawn xdot as before. Fixes LP#514422.
-
-New extra_info argument to graph-generating functions (patch by Thouis Jones,
-LP#558914).
-
-setup.py should work with distutils now (LP#604430, thanks to Randy Heydon).
-
-
-1.2 (2009-03-25)
-----------------
-
-Project website, public source repository, uploaded to PyPI.
-
-No code changes.
-
-
-1.1dev (2008-09-05)
--------------------
-
-New function: show_refs() for showing forward references.
-
-New functions: typestats() and show_most_common_types().
-
-Object boxes are less crammed with useless information (such as IDs).
-
-Spawns xdot if it is available.
-"""
-# Permission is hereby granted, free of charge, to any person obtaining a
-# copy of this software and associated documentation files (the "Software"),
-# to deal in the Software without restriction, including without limitation
-# the rights to use, copy, modify, merge, publish, distribute, sublicense,
-# and/or sell copies of the Software, and to permit persons to whom the
-# Software is furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-# DEALINGS IN THE SOFTWARE.
-
-__author__ = "Marius Gedminas (marius at gedmin.as)"
-__copyright__ = "Copyright (c) 2008-2010 Marius Gedminas"
-__license__ = "MIT"
-__version__ = "1.3.2dev"
-__date__ = "2010-07-17"
-
-
-import gc
-import inspect
-import types
-import weakref
-import operator
-import os
-import subprocess
-from tempfile import NamedTemporaryFile
-
-
-def count(typename):
- """Count objects tracked by the garbage collector with a given class name.
-
- Example:
-
- >>> count('dict')
- 42
- >>> count('MyClass')
- 3
-
- Note that the GC does not track simple objects like int or str.
- """
- return sum(1 for o in gc.get_objects() if type(o).__name__ == typename)
-
-
-def typestats():
- """Count the number of instances for each type tracked by the GC.
-
- Note that the GC does not track simple objects like int or str.
-
- Note that classes with the same name but defined in different modules
- will be lumped together.
- """
- stats = {}
- for o in gc.get_objects():
- stats.setdefault(type(o).__name__, 0)
- stats[type(o).__name__] += 1
- return stats
-
-
-def show_most_common_types(limit=10):
- """Count the names of types with the most instances.
-
- Note that the GC does not track simple objects like int or str.
-
- Note that classes with the same name but defined in different modules
- will be lumped together.
- """
- stats = sorted(typestats().items(), key=operator.itemgetter(1),
- reverse=True)
- if limit:
- stats = stats[:limit]
- width = max(len(name) for name, count in stats)
- for name, count in stats[:limit]:
- print name.ljust(width), count
-
-
-def by_type(typename):
- """Return objects tracked by the garbage collector with a given class name.
-
- Example:
-
- >>> by_type('MyClass')
- [<mymodule.MyClass object at 0x...>]
-
- Note that the GC does not track simple objects like int or str.
- """
- return [o for o in gc.get_objects() if type(o).__name__ == typename]
-
-
-def at(addr):
- """Return an object at a given memory address.
-
- The reverse of id(obj):
-
- >>> at(id(obj)) is obj
- True
-
- Note that this function does not work on objects that are not tracked by
- the GC (e.g. ints or strings).
- """
- for o in gc.get_objects():
- if id(o) == addr:
- return o
- return None
-
-
-def find_backref_chain(obj, predicate, max_depth=20, extra_ignore=()):
- """Find a shortest chain of references leading to obj.
-
- The start of the chain will be some object that matches your predicate.
-
- ``max_depth`` limits the search depth.
-
- ``extra_ignore`` can be a list of object IDs to exclude those objects from
- your search.
-
- Example:
-
- >>> find_backref_chain(obj, inspect.ismodule)
- [<module ...>, ..., obj]
-
- Returns None if such a chain could not be found.
- """
- queue = [obj]
- depth = {id(obj): 0}
- parent = {id(obj): None}
- ignore = set(extra_ignore)
- ignore.add(id(extra_ignore))
- ignore.add(id(queue))
- ignore.add(id(depth))
- ignore.add(id(parent))
- ignore.add(id(ignore))
- gc.collect()
- while queue:
- target = queue.pop(0)
- if predicate(target):
- chain = [target]
- while parent[id(target)] is not None:
- target = parent[id(target)]
- chain.append(target)
- return chain
- tdepth = depth[id(target)]
- if tdepth < max_depth:
- referrers = gc.get_referrers(target)
- ignore.add(id(referrers))
- for source in referrers:
- if inspect.isframe(source) or id(source) in ignore:
- continue
- if id(source) not in depth:
- depth[id(source)] = tdepth + 1
- parent[id(source)] = target
- queue.append(source)
- return None # not found
-
-
-def show_backrefs(objs, max_depth=3, extra_ignore=(), filter=None, too_many=10,
- highlight=None, filename=None, extra_info=(lambda _: '')):
- """Generate an object reference graph ending at ``objs``
-
- The graph will show you what objects refer to ``objs``, directly and
- indirectly.
-
- ``objs`` can be a single object, or it can be a list of objects. If
- unsure, wrap the single object in a new list.
-
- Produces a Graphviz .dot file and spawns a viewer (xdot) if one is
- installed, otherwise converts the graph to a .png image.
-
- Use ``max_depth`` and ``too_many`` to limit the depth and breadth of the
- graph.
-
- Use ``filter`` (a predicate) and ``extra_ignore`` (a list of object IDs) to
- remove undesired objects from the graph.
-
- Use ``highlight`` (a predicate) to highlight certain graph nodes in blue.
-
- Use ``extra_info`` (a function returning a string) to report extra
- information for objects.
-
- Examples:
-
- >>> show_backrefs(obj)
- >>> show_backrefs([obj1, obj2])
- >>> show_backrefs(obj, max_depth=5)
- >>> show_backrefs(obj, filter=lambda x: not inspect.isclass(x))
- >>> show_backrefs(obj, highlight=inspect.isclass)
- >>> show_backrefs(obj, extra_ignore=[id(locals())])
-
- """
- show_graph(objs, max_depth=max_depth, extra_ignore=extra_ignore,
- filter=filter, too_many=too_many, highlight=highlight,
- edge_func=gc.get_referrers, swap_source_target=False,
- filename=filename, extra_info=extra_info)
-
-
-def show_refs(objs, max_depth=3, extra_ignore=(), filter=None, too_many=10,
- highlight=None, filename=None, extra_info=(lambda _: '')):
- """Generate an object reference graph starting at ``objs``
-
- The graph will show you what objects are reachable from ``objs``, directly
- and indirectly.
-
- ``objs`` can be a single object, or it can be a list of objects. If
- unsure, wrap the single object in a new list.
-
- Produces a Graphviz .dot file and spawns a viewer (xdot) if one is
- installed, otherwise converts the graph to a .png image.
-
- Use ``max_depth`` and ``too_many`` to limit the depth and breadth of the
- graph.
-
- Use ``filter`` (a predicate) and ``extra_ignore`` (a list of object IDs) to
- remove undesired objects from the graph.
-
- Use ``highlight`` (a predicate) to highlight certain graph nodes in blue.
-
- Use ``extra_info`` (a function returning a string) to report extra
- information for objects.
-
- Examples:
-
- >>> show_refs(obj)
- >>> show_refs([obj1, obj2])
- >>> show_refs(obj, max_depth=5)
- >>> show_refs(obj, filter=lambda x: not inspect.isclass(x))
- >>> show_refs(obj, highlight=inspect.isclass)
- >>> show_refs(obj, extra_ignore=[id(locals())])
-
- """
- show_graph(objs, max_depth=max_depth, extra_ignore=extra_ignore,
- filter=filter, too_many=too_many, highlight=highlight,
- edge_func=gc.get_referents, swap_source_target=True,
- filename=filename, extra_info=extra_info)
-
-#
-# Internal helpers
-#
-
-def show_graph(objs, edge_func, swap_source_target,
- max_depth=3, extra_ignore=(), filter=None, too_many=10,
- highlight=None, filename=None, extra_info=(lambda _: '')):
- if not isinstance(objs, (list, tuple)):
- objs = [objs]
- if filename and filename.endswith('.dot'):
- f = file(filename, 'w')
- dot_filename = filename
- else:
- f = NamedTemporaryFile('w', suffix='.dot', delete=False)
- dot_filename = f.name
- print >> f, 'digraph ObjectGraph {'
- print >> f, ' node[shape=box, style=filled, fillcolor=white];'
- queue = []
- depth = {}
- ignore = set(extra_ignore)
- ignore.add(id(objs))
- ignore.add(id(extra_ignore))
- ignore.add(id(queue))
- ignore.add(id(depth))
- ignore.add(id(ignore))
- for obj in objs:
- print >> f, ' %s[fontcolor=red];' % (obj_node_id(obj))
- depth[id(obj)] = 0
- queue.append(obj)
- gc.collect()
- nodes = 0
- while queue:
- nodes += 1
- target = queue.pop(0)
- tdepth = depth[id(target)]
- print >> f, ' %s[label="%s"];' % (obj_node_id(target), obj_label(target, tdepth, extra_info))
- h, s, v = gradient((0, 0, 1), (0, 0, .3), tdepth, max_depth)
- if inspect.ismodule(target):
- h = .3
- s = 1
- if highlight and highlight(target):
- h = .6
- s = .6
- v = 0.5 + v * 0.5
- print >> f, ' %s[fillcolor="%g,%g,%g"];' % (obj_node_id(target), h, s, v)
- if v < 0.5:
- print >> f, ' %s[fontcolor=white];' % (obj_node_id(target))
- if hasattr(target, '__del__'):
- print >> f, " %s->%s_has_a_del[color=red,style=dotted,len=0.25,weight=10];" % (obj_node_id(target), obj_node_id(target))
- print >> f, ' %s_has_a_del[label="__del__",shape=doublecircle,height=0.25,color=red,fillcolor="0,.5,1",fontsize=6];' % (obj_node_id(target))
- if inspect.ismodule(target) or tdepth >= max_depth:
- continue
- neighbours = edge_func(target)
- ignore.add(id(neighbours))
- n = 0
- for source in neighbours:
- if inspect.isframe(source) or id(source) in ignore:
- continue
- if filter and not filter(source):
- continue
- if swap_source_target:
- srcnode, tgtnode = target, source
- else:
- srcnode, tgtnode = source, target
- elabel = edge_label(srcnode, tgtnode)
- print >> f, ' %s -> %s%s;' % (obj_node_id(srcnode), obj_node_id(tgtnode), elabel)
- if id(source) not in depth:
- depth[id(source)] = tdepth + 1
- queue.append(source)
- n += 1
- if n >= too_many:
- print >> f, ' %s[color=red];' % obj_node_id(target)
- break
- print >> f, "}"
- f.close()
- print "Graph written to %s (%d nodes)" % (dot_filename, nodes)
- if filename is None and program_in_path('xdot'):
- print "Spawning graph viewer (xdot)"
- subprocess.Popen(['xdot', dot_filename])
- elif program_in_path('dot'):
- if filename is None:
- print "Graph viewer (xdot) not found, generating a png instead"
- if filename and filename.endswith('.png'):
- f = file(filename, 'wb')
- png_filename = filename
- else:
- if filename is not None:
- print "Unrecognized file type (%s)" % filename
- f = NamedTemporaryFile('wb', suffix='.png', delete=False)
- png_filename = f.name
- dot = subprocess.Popen(['dot', '-Tpng', dot_filename],
- stdout=f)
- dot.wait()
- f.close()
- print "Image generated as %s" % png_filename
- else:
- if filename is None:
- print "Graph viewer (xdot) and image renderer (dot) not found, not doing anything else"
- else:
- print "Unrecognized file type (%s), not doing anything else" % filename
-
-
-def obj_node_id(obj):
- if isinstance(obj, weakref.ref):
- return 'all_weakrefs_are_one'
- return ('o%d' % id(obj)).replace('-', '_')
-
-
-def obj_label(obj, depth, extra_info):
- return quote(type(obj).__name__ + ':\n' +
- safe_repr(obj) + '\n' +
- extra_info(obj))
-
-
-def quote(s):
- return s.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n")
-
-
-def safe_repr(obj):
- try:
- return short_repr(obj)
- except:
- return '(unrepresentable)'
-
-
-def short_repr(obj):
- if isinstance(obj, (type, types.ModuleType, types.BuiltinMethodType,
- types.BuiltinFunctionType)):
- return obj.__name__
- if isinstance(obj, types.MethodType):
- if obj.im_self is not None:
- return obj.im_func.__name__ + ' (bound)'
- else:
- return obj.im_func.__name__
- if isinstance(obj, (tuple, list, dict, set)):
- return '%d items' % len(obj)
- if isinstance(obj, weakref.ref):
- return 'all_weakrefs_are_one'
- return repr(obj)[:40]
-
-
-def gradient(start_color, end_color, depth, max_depth):
- if max_depth == 0:
- # avoid division by zero
- return start_color
- h1, s1, v1 = start_color
- h2, s2, v2 = end_color
- f = float(depth) / max_depth
- h = h1 * (1-f) + h2 * f
- s = s1 * (1-f) + s2 * f
- v = v1 * (1-f) + v2 * f
- return h, s, v
-
-
-def edge_label(source, target):
- if isinstance(target, dict) and target is getattr(source, '__dict__', None):
- return ' [label="__dict__",weight=10]'
- elif isinstance(source, dict):
- for k, v in source.iteritems():
- if v is target:
- if isinstance(k, basestring) and k:
- return ' [label="%s",weight=2]' % quote(k)
- else:
- return ' [label="%s"]' % quote(safe_repr(k))
- return ''
-
-
-def program_in_path(program):
- path = os.environ.get("PATH", os.defpath).split(os.pathsep)
- path = [os.path.join(dir, program) for dir in path]
- path = [True for file in path if os.path.isfile(file)]
- return bool(path)
Deleted: z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/objgraph_trunk.diff
===================================================================
--- z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/objgraph_trunk.diff 2010-11-10 10:26:41 UTC (rev 118310)
+++ z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/objgraph_trunk.diff 2010-11-10 10:29:43 UTC (rev 118311)
@@ -1,32 +0,0 @@
-152d151
-< res = set([])
-154,155c153
-< res.add((name.ljust(width), count))
-< return res
----
-> print name.ljust(width), count
-325,328d322
-<
-< if filename and os.path.exists(filename):
-< os.unlink(filename)
-<
-330c324
-< f = file(filename, 'wb')
----
-> f = file(filename, 'w')
-333c327
-< f = NamedTemporaryFile('wb', suffix='.dot')
----
-> f = NamedTemporaryFile('w', suffix='.dot', delete=False)
-335,341d328
-<
-< # NOTE:
-< # Need to do this to ensure the file is created within transaction??
-< f.close()
-< f = open(f.name, 'wb')
-< f.write('')
-<
-415c402
-< f = NamedTemporaryFile('wb', suffix='.png')
----
-> f = NamedTemporaryFile('wb', suffix='.png', delete=False)
More information about the checkins
mailing list