[Checkins] SVN: z3c.memhunt.objgraph/trunk/ Importing rough z3c.memhunt.objgraph

Daniel Blackburn blackburnd at gmail.com
Wed Nov 10 05:26:41 EST 2010


Log message for revision 118310:
  Importing rough z3c.memhunt.objgraph

Changed:
  A   z3c.memhunt.objgraph/trunk/
  A   z3c.memhunt.objgraph/trunk/CHANGES.txt
  A   z3c.memhunt.objgraph/trunk/README.txt
  A   z3c.memhunt.objgraph/trunk/docs/
  A   z3c.memhunt.objgraph/trunk/docs/HISTORY.txt
  A   z3c.memhunt.objgraph/trunk/resurrect.c
  A   z3c.memhunt.objgraph/trunk/setup.cfg
  A   z3c.memhunt.objgraph/trunk/setup.py
  A   z3c.memhunt.objgraph/trunk/z3c/
  A   z3c.memhunt.objgraph/trunk/z3c/__init__.py
  A   z3c.memhunt.objgraph/trunk/z3c/memhunt/
  A   z3c.memhunt.objgraph/trunk/z3c/memhunt/__init__.py
  A   z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/
  A   z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/README.txt
  A   z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/__init__.py
  A   z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/checks.py
  A   z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/cond_perms.py
  A   z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/configure.zcml
  A   z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/configure_zope2.zcml
  A   z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/configure_zope3.zcml
  A   z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/debug_views.py
  A   z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/objgraph-1.py
  A   z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/objgraph.py
  A   z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/objgraph_orig.py
  A   z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/objgraph_trunk.diff
  A   z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/resources/
  A   z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/resources/history/
  A   z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/resources/history/keep.txt
  A   z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/resources/keep.txt
  A   z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/version.txt
  A   z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/zpt/
  A   z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/zpt/debug.css
  A   z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/zpt/ref_common_count.pt
  A   z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/zpt/ref_count.pt
  A   z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/zpt/start.pt
  A   z3c.memhunt.objgraph/trunk/z3c.memhunt.objgraph.egg-info/
  A   z3c.memhunt.objgraph/trunk/z3c.memhunt.objgraph.egg-info/PKG-INFO
  A   z3c.memhunt.objgraph/trunk/z3c.memhunt.objgraph.egg-info/SOURCES.txt
  A   z3c.memhunt.objgraph/trunk/z3c.memhunt.objgraph.egg-info/dependency_links.txt
  A   z3c.memhunt.objgraph/trunk/z3c.memhunt.objgraph.egg-info/namespace_packages.txt
  A   z3c.memhunt.objgraph/trunk/z3c.memhunt.objgraph.egg-info/not-zip-safe
  A   z3c.memhunt.objgraph/trunk/z3c.memhunt.objgraph.egg-info/requires.txt
  A   z3c.memhunt.objgraph/trunk/z3c.memhunt.objgraph.egg-info/top_level.txt

-=-
Added: z3c.memhunt.objgraph/trunk/CHANGES.txt
===================================================================
--- z3c.memhunt.objgraph/trunk/CHANGES.txt	                        (rev 0)
+++ z3c.memhunt.objgraph/trunk/CHANGES.txt	2010-11-10 10:26:41 UTC (rev 118310)
@@ -0,0 +1,4 @@
+
+0.0.1
+-----
+- Initial eggification of hc.debug.core

Added: z3c.memhunt.objgraph/trunk/README.txt
===================================================================
--- z3c.memhunt.objgraph/trunk/README.txt	                        (rev 0)
+++ z3c.memhunt.objgraph/trunk/README.txt	2010-11-10 10:26:41 UTC (rev 118310)
@@ -0,0 +1,4 @@
+This is a global debug package used to hold debug code across any
+site. 
+
+This package uses objgraph for introspection views
\ No newline at end of file

Added: z3c.memhunt.objgraph/trunk/docs/HISTORY.txt
===================================================================
--- z3c.memhunt.objgraph/trunk/docs/HISTORY.txt	                        (rev 0)
+++ z3c.memhunt.objgraph/trunk/docs/HISTORY.txt	2010-11-10 10:26:41 UTC (rev 118310)
@@ -0,0 +1,2 @@
+Changes
+=======

Added: z3c.memhunt.objgraph/trunk/resurrect.c
===================================================================
--- z3c.memhunt.objgraph/trunk/resurrect.c	                        (rev 0)
+++ z3c.memhunt.objgraph/trunk/resurrect.c	2010-11-10 10:26:41 UTC (rev 118310)
@@ -0,0 +1,30 @@
+#include "Python.h"
+
+static PyObject *
+Py_conjure (PyObject * self, PyObject * args)
+{
+  long address;
+  if (!PyArg_ParseTuple (args, "l", &address)) {
+    return NULL;
+  } else {
+    PyObject * thing = (PyObject *) address;
+    Py_INCREF(thing);
+    return thing;
+  }
+}
+
+/* List of functions defined in the module */
+
+static PyMethodDef resurrect_methods[] = {
+  {"conjure",Py_conjure,1},
+  {NULL,NULL}/* sentinel */
+};
+
+/* Initialization function for the module (*must* be called initresurrect) */
+
+void
+initresurrect()
+{
+  PyObject *m;
+  m = Py_InitModule("resurrect", resurrect_methods);
+}


Property changes on: z3c.memhunt.objgraph/trunk/resurrect.c
___________________________________________________________________
Added: svn:executable
   + 

Added: z3c.memhunt.objgraph/trunk/setup.cfg
===================================================================
--- z3c.memhunt.objgraph/trunk/setup.cfg	                        (rev 0)
+++ z3c.memhunt.objgraph/trunk/setup.cfg	2010-11-10 10:26:41 UTC (rev 118310)
@@ -0,0 +1,3 @@
+[egg_info]
+tag_build = dev
+tag_svn_revision = true

Added: z3c.memhunt.objgraph/trunk/setup.py
===================================================================
--- z3c.memhunt.objgraph/trunk/setup.py	                        (rev 0)
+++ z3c.memhunt.objgraph/trunk/setup.py	2010-11-10 10:26:41 UTC (rev 118310)
@@ -0,0 +1,66 @@
+# -*- coding: utf-8 -*-
+"""
+This module contains the tool of z3c.memhunt.objgraph
+
+"""
+import os
+from setuptools import setup, find_packages
+from distutils.core import setup, Extension
+
+def read(*rnames):
+    return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
+
+libname = 'z3c.memhunt.objgraph'
+libloc = libname.split('.')
+version = open(os.path.join(*libloc+['version.txt'])).read().strip()
+
+
+long_description = (
+    read('CHANGES.txt')
+    + '\n' +
+    read('README.txt')
+    + '\n' +
+    'Change history\n'
+    '**************\n'
+    )
+
+tests_require=['zope.testing']
+
+setup(name='z3c.memhunt.objgraph',
+      version=version,
+      description="",
+      long_description=long_description,
+      # Get more strings from http://www.python.org/pypi?%3Aaction=list_classifiers
+      keywords = "zope zope2 zope3 memory objgraph graphviz guppy",
+      classifiers = [
+        'Development Status :: 4 - Beta',
+        'Environment :: Web Environment',
+        'Intended Audience :: Developers',
+        'License :: OSI Approved :: Zope Public License',
+        'Programming Language :: Python',
+        'Natural Language :: English',
+        'Operating System :: OS Independent',
+        'Topic :: Internet :: WWW/HTTP',
+        'Framework :: Zope3'],
+      author='Daniel Blackburn & Holmes Corporation',
+      author_email='danielb at holmescorp.com',
+      url="''",
+      license = "ZPL 2.1",
+      packages=find_packages(exclude=['ez_setup']),
+      namespace_packages=['z3c',],
+      include_package_data=True,
+      zip_safe=False,
+      install_requires=['setuptools',
+                        'guppy',
+                        'objgraph',
+                        ],
+      tests_require=tests_require,
+#      ext_modules = [Extension("resurrect", ["resurrect.c"]),
+#                     Extension("malloc_stats", ["malloc_stats.c"])],
+      extras_require=dict(tests=tests_require),
+      test_suite = 'z3c.memhunt.objgraph.tests.test_docs.test_suite',
+      )
+
+
+
+      

Added: z3c.memhunt.objgraph/trunk/z3c/__init__.py
===================================================================
--- z3c.memhunt.objgraph/trunk/z3c/__init__.py	                        (rev 0)
+++ z3c.memhunt.objgraph/trunk/z3c/__init__.py	2010-11-10 10:26:41 UTC (rev 118310)
@@ -0,0 +1,6 @@
+# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages
+try:
+    __import__('pkg_resources').declare_namespace(__name__)
+except ImportError:
+    from pkgutil import extend_path
+    __path__ = extend_path(__path__, __name__)


Property changes on: z3c.memhunt.objgraph/trunk/z3c/__init__.py
___________________________________________________________________
Added: svn:executable
   + 

Added: z3c.memhunt.objgraph/trunk/z3c/memhunt/__init__.py
===================================================================
--- z3c.memhunt.objgraph/trunk/z3c/memhunt/__init__.py	                        (rev 0)
+++ z3c.memhunt.objgraph/trunk/z3c/memhunt/__init__.py	2010-11-10 10:26:41 UTC (rev 118310)
@@ -0,0 +1,6 @@
+# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages
+try:
+    __import__('pkg_resources').declare_namespace(__name__)
+except ImportError:
+    from pkgutil import extend_path
+    __path__ = extend_path(__path__, __name__)


Property changes on: z3c.memhunt.objgraph/trunk/z3c/memhunt/__init__.py
___________________________________________________________________
Added: svn:executable
   + 

Added: z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/README.txt
===================================================================
--- z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/README.txt	                        (rev 0)
+++ z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/README.txt	2010-11-10 10:26:41 UTC (rev 118310)
@@ -0,0 +1,5 @@
+
+Testing Debug views
+===================
+   >>> browser.open('http://nohost/plone/accessmanager/ref_count')
+    

Added: z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/__init__.py
===================================================================
--- z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/__init__.py	                        (rev 0)
+++ z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/__init__.py	2010-11-10 10:26:41 UTC (rev 118310)
@@ -0,0 +1 @@
+#

Added: z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/checks.py
===================================================================
--- z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/checks.py	                        (rev 0)
+++ z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/checks.py	2010-11-10 10:26:41 UTC (rev 118310)
@@ -0,0 +1,503 @@
+"""
+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)

Added: z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/cond_perms.py
===================================================================
--- z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/cond_perms.py	                        (rev 0)
+++ z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/cond_perms.py	2010-11-10 10:26:41 UTC (rev 118310)
@@ -0,0 +1,4 @@
+
+
+def version():
+    import pdb; pdb.set_trace()

Added: z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/configure.zcml
===================================================================
--- z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/configure.zcml	                        (rev 0)
+++ z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/configure.zcml	2010-11-10 10:26:41 UTC (rev 118310)
@@ -0,0 +1,15 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:zcml="http://namespaces.zope.org/zcml"
+    i18n_domain="zcml">
+    
+    <!--include zcml:condition="installed zope.app.component" file="configure_zope3.zcml"/-->
+    <include file="configure_zope3.zcml"/>
+
+</configure>
+
+
+
+
+
+

Added: z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/configure_zope2.zcml
===================================================================
--- z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/configure_zope2.zcml	                        (rev 0)
+++ z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/configure_zope2.zcml	2010-11-10 10:26:41 UTC (rev 118310)
@@ -0,0 +1,140 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:browser="http://namespaces.zope.org/browser"
+    i18n_domain="z3c.memhunt.objgraph">
+    
+    <browser:page
+        for="*"
+        name="ref_tools"
+        attribute="start"
+        class=".debug_views.DebugView"
+        permission="cmf.ManagePortal"
+        />
+
+    <browser:page
+        for="*"
+        name="ref_count"
+        attribute="ref_count"
+        class=".debug_views.DebugView"
+        permission="cmf.ManagePortal"
+        />
+
+    <browser:page
+        for="*"
+        name="ref_common_count"
+        attribute="most_common"
+        class=".debug_views.DebugView"
+        permission="cmf.ManagePortal"
+        />
+
+    <browser:page
+        for="*"
+        name="display_mem"
+        attribute="display_mem"
+        class=".debug_views.DebugView"
+        permission="cmf.ManagePortal"
+        />
+
+    <browser:page
+        for="*"
+        name="view_ref"
+        attribute="view_ref"
+        class=".debug_views.DebugView"
+        permission="cmf.ManagePortal"
+        />
+
+    <browser:page
+        for="*"
+        name="view_backref"
+        attribute="view_backref"
+        class=".debug_views.DebugView"
+        permission="cmf.ManagePortal"
+        />
+
+    <browser:page
+        for="*"
+        name="context_backrefs"
+        attribute="context_backrefs"
+        class=".debug_views.DebugView"
+        permission="cmf.ManagePortal"
+        />
+
+    <browser:page
+        for="*"
+        name="memory"
+        attribute="memory"
+        class=".debug_views.DebugView"
+        permission="cmf.ManagePortal"
+        />
+
+    <browser:page
+        for="*"
+        name="relative_memory"
+        attribute="relative_memory"
+        class=".debug_views.DebugView"
+        permission="cmf.ManagePortal"
+        />
+
+    <browser:page
+        for="*"
+        name="traverse_relative_memory"
+        attribute="traverse_relative_memory"
+        class=".debug_views.DebugView"
+        permission="cmf.ManagePortal"
+        />
+
+    <browser:page
+        for="*"
+        name="reset_heap"
+        attribute="reset_heap"
+        class=".debug_views.DebugView"
+        permission="cmf.ManagePortal"
+        />
+
+    <browser:page
+        for="*"
+        name="by_referrers"
+        attribute="by_referrers"
+        class=".debug_views.DebugView"
+        permission="cmf.ManagePortal"
+        />
+
+    <browser:page
+        for="*"
+        name="get_biggest_offender"
+        attribute="get_biggest_offender"
+        class=".debug_views.DebugView"
+        permission="cmf.ManagePortal"
+        />
+
+
+    <browser:page
+        for="*"
+        name="context_refs"
+        attribute="context_refs"
+        class=".debug_views.DebugView"
+        permission="cmf.ManagePortal"
+        />
+
+    <browser:page
+        for="*"
+        name="breakpoint"
+        attribute="breakpoint"
+        class=".debug_views.DebugView"
+        permission="cmf.ManagePortal"
+        />
+
+     <browser:resourceDirectory
+	      name="history"
+ 	      directory="resources/history"
+              permission="zope2.View"
+ 	      />
+
+
+</configure>
+
+
+
+
+
+

Added: z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/configure_zope3.zcml
===================================================================
--- z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/configure_zope3.zcml	                        (rev 0)
+++ z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/configure_zope3.zcml	2010-11-10 10:26:41 UTC (rev 118310)
@@ -0,0 +1,140 @@
+<configure
+    xmlns="http://namespaces.zope.org/zope"
+    xmlns:browser="http://namespaces.zope.org/browser"
+    i18n_domain="hc.am.base">
+
+    <browser:page
+        for="*"
+        name="ref_tools"
+        attribute="start"
+        class=".debug_views.DebugView"
+        permission="zope.ManageContent"
+        />
+
+    <browser:page
+        for="*"
+        name="ref_count"
+        attribute="ref_count"
+        class=".debug_views.DebugView"
+        permission="zope.ManageContent"
+        />
+
+    <browser:page
+        for="*"
+        name="ref_common_count"
+        attribute="most_common"
+        class=".debug_views.DebugView"
+        permission="zope.ManageContent"
+        />
+
+    <browser:page
+        for="*"
+        name="display_mem"
+        attribute="display_mem"
+        class=".debug_views.DebugView"
+        permission="zope.ManageContent"
+        />
+
+    <browser:page
+        for="*"
+        name="view_ref"
+        attribute="view_ref"
+        class=".debug_views.DebugView"
+        permission="zope.ManageContent"
+        />
+
+    <browser:page
+        for="*"
+        name="view_backref"
+        attribute="view_backref"
+        class=".debug_views.DebugView"
+        permission="zope.ManageContent"
+        />
+
+    <browser:page
+        for="*"
+        name="context_backrefs.png"
+        attribute="context_backrefs"
+        class=".debug_views.DebugView"
+        permission="zope.ManageContent"
+        />
+
+    <browser:page
+        for="*"
+        name="memory"
+        attribute="memory"
+        class=".debug_views.DebugView"
+        permission="zope.ManageContent"
+        />
+
+    <browser:page
+        for="*"
+        name="relative_memory"
+        attribute="relative_memory"
+        class=".debug_views.DebugView"
+        permission="zope.ManageContent"
+        />
+
+    <browser:page
+        for="*"
+        name="traverse_relative_memory"
+        attribute="traverse_relative_memory"
+        class=".debug_views.DebugView"
+        permission="zope.ManageContent"
+        />
+
+    <browser:page
+        for="*"
+        name="reset_heap"
+        attribute="reset_heap"
+        class=".debug_views.DebugView"
+        permission="zope.ManageContent"
+        />
+
+    <browser:page
+        for="*"
+        name="by_referrers"
+        attribute="by_referrers"
+        class=".debug_views.DebugView"
+        permission="zope.ManageContent"
+        />
+
+    <browser:page
+        for="*"
+        name="get_biggest_offender"
+        attribute="get_biggest_offender"
+        class=".debug_views.DebugView"
+        permission="zope.ManageContent"
+        />
+
+
+    <browser:page
+        for="*"
+        name="context_refs.png"
+        attribute="context_refs"
+        class=".debug_views.DebugView"
+        permission="zope.ManageContent"
+        />
+
+    <browser:page
+        for="*"
+        name="breakpoint"
+        attribute="breakpoint"
+        class=".debug_views.DebugView"
+        permission="zope.ManageContent"
+        />
+
+     <browser:resourceDirectory
+	      name="history"
+ 	      directory="resources/history"
+              permission="zope.ManageContent"
+ 	      />
+
+      <browser:resource
+          name="debug.css"
+          file="./zpt/debug.css"
+          />
+
+
+
+</configure>

Added: z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/debug_views.py
===================================================================
--- z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/debug_views.py	                        (rev 0)
+++ z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/debug_views.py	2010-11-10 10:26:41 UTC (rev 118310)
@@ -0,0 +1,177 @@
+import sys
+import gc
+import types
+from tempfile import NamedTemporaryFile
+
+try:
+    from Products.Five.browser import BrowserView
+    from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
+except ImportError:
+    from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile    
+    from zope.publisher.browser import BrowserView
+
+import checks
+import pprint 
+import guppy
+
+heapy = guppy.hpy()
+
+
+class DebugView(BrowserView):
+    __allow_access_to_unprotected_subobjects__ = 1
+
+    ref_template = ViewPageTemplateFile("zpt/ref_count.pt")
+    ref_common_template = ViewPageTemplateFile("zpt/ref_common_count.pt")
+    start_template = ViewPageTemplateFile("zpt/start.pt")
+
+    evenOddClasses = ('even','odd')
+    evenodd = 0
+
+    def start(self):
+        return self.start_template(self)
+
+    def cssclass(self):
+        """ determiner what background color to use for lists """
+        if self.evenodd != 1:
+            self.evenodd = 1
+        else:
+            self.evenodd = 0
+        return self.evenOddClasses[self.evenodd]
+
+    def ref_count(self):
+        d = {}
+        # collect all classes
+        self.garbage_containing = len(gc.garbage)
+        self.garbage_watching = len(gc.get_objects())
+        self.total_ref_count = 0
+
+        for m in sys.modules.values():
+            for sym in dir(m):
+                o = getattr (m, sym)
+                if type(o) is types.ClassType:
+                    d[o] = sys.getrefcount (o)
+
+                    # sort by refcount
+                    pairs = map (lambda x: (repr(x[0]),x[1]), d.items())
+                    pairs.sort()
+                    pairs.reverse()
+
+        self.pairs = []
+        for pair in pairs:
+            self.total_ref_count += pair[1]
+            self.pairs.append({'refcount':pair[1],
+                               'name':pair[0]})
+
+        self.pairs = sorted(self.pairs, key=lambda x: x['refcount'])
+        self.pairs.reverse()
+
+
+        return self.ref_template(self)
+
+    def most_common(self):
+        pairs = checks.most_common_types()
+        self.pairs = []
+        for pair in pairs:
+            self.pairs.append({'refcount':pair[1],
+                               'name':pair[0]})
+
+        return self.ref_common_template(self)
+
+    def display_mem(self):
+        import malloc_stats
+        return malloc_stats.malloc_stats()
+
+
+    @property
+    def target(self):
+        if getattr(self, '_target', None):
+            return getattr(self,'_target')
+
+        target =  self.request.form.get('name','')
+        if not target:
+            return None
+
+        target = target.strip('<').strip('>')
+        target = '_'.join(target.split(' ')[:-1])
+
+        coll = {}
+        for m in sys.modules.values():
+            for sym in dir(m):
+                o = getattr (m, sym)
+                if type(o) is types.ClassType:
+                    name = '_'.join(repr(o).strip('<').strip('>').split(' ')[:-1])
+                    coll[name] = o
+
+        self._target = coll.get(target, None)
+        return self._target
+
+
+
+    def view_backref(self):
+        if self.target is None:
+            return "Please select an item to introspect"
+        return self.back_ref_file
+
+
+    def view_ref(self):
+        if self.target is None:
+            return "Please select an item to introspect"
+        return self.ref_file
+
+
+    @property
+    def ref_file(self):
+        self.request.response.setHeader('content-type','image/png')
+        f = NamedTemporaryFile('wb', suffix='.png')
+        checks.show_refs([self.target], max_depth=6, filename=f.name)
+        return open(f.name,'r').read()
+
+    @property
+    def back_ref_file(self):
+        self.request.response.setHeader('content-type','image/png')
+        f = NamedTemporaryFile('wb', suffix='.png')
+        checks.show_backrefs([self.target], max_depth=6, filename=f.name)
+        return open(f.name,'r').read()
+
+
+    def context_refs(self):
+        self._target = self.context
+        return self.ref_file
+
+    def context_backrefs(self):
+        self._target = self.context
+        return self.back_ref_file
+
+    def reset_heap(self):
+        # Resets for testing
+        heapy.setrelheap()
+
+    def memory(self):
+        return pprint.pformat(heapy.heap())
+
+    # Print relative memory consumption since last sycle
+    def relative_memory(self):
+        res = pprint.pformat(heapy.heap())
+        heapy.setref()
+        return res
+
+    def by_referrers(self):
+        res = pprint.pformat(heapy.heap().byrcs)
+        return res
+
+    def get_biggest_offender(self):
+        obj = heapy.heap()[0].byrcs[0].referrers.byrcs
+        res = "SIZE: %s\n\n" % obj.domisize
+        res += pprint.pformat(obj)
+        return res
+
+    # Print relative memory consumption w/heap traversing
+    def traverse_relative_memory(self):
+        res = pprint.pformat(heapy.heap().get_rp(40))
+        heapy.setref()
+        return res
+
+    def breakpoint(self):
+        import pdb; pdb.set_trace()
+        obj = heapy.heap()
+        return "done"

Added: z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/objgraph-1.py
===================================================================
--- z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/objgraph-1.py	                        (rev 0)
+++ z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/objgraph-1.py	2010-11-10 10:26:41 UTC (rev 118310)
@@ -0,0 +1,503 @@
+"""
+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)

Added: z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/objgraph.py
===================================================================
--- z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/objgraph.py	                        (rev 0)
+++ z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/objgraph.py	2010-11-10 10:26:41 UTC (rev 118310)
@@ -0,0 +1,485 @@
+"""
+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)

Added: z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/objgraph_orig.py
===================================================================
--- z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/objgraph_orig.py	                        (rev 0)
+++ z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/objgraph_orig.py	2010-11-10 10:26:41 UTC (rev 118310)
@@ -0,0 +1,485 @@
+"""
+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)

Added: z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/objgraph_trunk.diff
===================================================================
--- z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/objgraph_trunk.diff	                        (rev 0)
+++ z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/objgraph_trunk.diff	2010-11-10 10:26:41 UTC (rev 118310)
@@ -0,0 +1,32 @@
+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)

Added: z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/resources/history/keep.txt
===================================================================
--- z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/resources/history/keep.txt	                        (rev 0)
+++ z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/resources/history/keep.txt	2010-11-10 10:26:41 UTC (rev 118310)
@@ -0,0 +1 @@
+#pass
\ No newline at end of file

Added: z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/resources/keep.txt
===================================================================
--- z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/resources/keep.txt	                        (rev 0)
+++ z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/resources/keep.txt	2010-11-10 10:26:41 UTC (rev 118310)
@@ -0,0 +1 @@
+#pass
\ No newline at end of file

Added: z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/version.txt
===================================================================
--- z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/version.txt	                        (rev 0)
+++ z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/version.txt	2010-11-10 10:26:41 UTC (rev 118310)
@@ -0,0 +1 @@
+0.1
\ No newline at end of file

Added: z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/zpt/debug.css
===================================================================
--- z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/zpt/debug.css	                        (rev 0)
+++ z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/zpt/debug.css	2010-11-10 10:26:41 UTC (rev 118310)
@@ -0,0 +1,209 @@
+/* Normalize */
+
+body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, form, fieldset, input, p, blockquote, th, td {
+  margin : 0;
+  padding : 0;
+  font-size: 100%;
+  font-weight: normal;
+
+}
+
+/* remove borders from tables */
+
+table {
+  border-collapse : collapse;
+  border-spacing : 0;
+}
+
+/* start formatting the page */
+
+body {
+    padding: 5px;
+    font-family:Arial,sans-serif;
+    font-size:small;
+    margin: 20px;
+}
+
+.debug_body {
+    margin-left: 3em;
+}
+
+.debug_table {
+    margin-left: 40px;
+}
+
+h1 {
+    font-size: 140%;
+    font-weight: bold;
+    padding: 10px 20px;
+    width: 60%;
+    border-bottom: 1px solid #ccc;
+}
+
+.just_border { 
+    padding: 0px 20px;
+}
+
+h2 {
+    font-size: 110%;
+    font-weight: bold;
+    padding: 5px 25px;
+}
+
+h3 {
+    font-weight: bold;
+    text-decoration: underline;
+ }
+
+table {
+    border: 1px solid #999;
+    width: 60%;
+}
+
+table td {
+    text-align : left;
+    vertical-align : middle;
+    font-size: 90%;
+    padding: 2px 5px;
+}
+
+* html table {  /* for Internet Explorer */
+  font-size: 120%; 
+}
+
+table th {
+    background-color : #ccc;
+    font-size: 90%; 
+    font-weight: bolder;
+    padding:5px;
+    
+}
+
+table th a {
+    text-decoration:none;
+}
+
+table th a:visited {
+    color: blue;
+}
+
+.viewname {
+    font-weight : bold;
+}
+
+.view_perm {
+    padding-left: 2em;
+    text-align: left;
+}
+
+.viewObj {
+    font-weight : bold;
+}
+
+
+.view{
+    text-align : left;
+}
+
+.permission {
+    font-style:italic;
+}
+
+.odd {
+    padding-left: 3em;
+    background-color: #ddd;
+}
+
+.even {
+    padding-left: 3em;
+}
+
+.contentList {
+    margin-left: 1em;
+}
+.contextList {
+    text-decoration: underline;
+    margin-left: 1em;
+}
+
+
+.contextListIndent {
+    margin-left: 3em;
+}
+
+.finalPermissions {
+    margin-left: 1em;
+}
+
+.sectionInfo {
+    padding-top: 5px;
+    padding-left: 5px;
+    font-size: 85%;
+    font-style:italic;
+    color: grey;
+}
+
+.legend {
+   font-style: italic;
+   /*margin-left: 25px;*/
+   font-size: 85%;
+   margin-bottom: 2em;
+
+}
+
+.description {
+    margin-right: 35%;
+    margin-left: 3em;
+}
+
+.top_nav {
+    margin-top: 5px;
+    margin-left: 38px;
+    margin-bottom: 10px;
+}
+
+.top_nav_val {
+    font-weight: bold;
+    font-size: 115%;
+}
+
+
+.headings {
+    background-color: #999;
+}
+
+.sub_heading {
+   font-size: 115%;
+   color: grey;
+   margin-bottom: 8px;
+   /*margin-left: 25px;*/
+
+}
+
+.tree{
+   float: left;
+   padding: 10px 20px;
+   margin-bottom: 8px;
+   margin-right: 8px;
+   border: 1px solid #ccc;
+
+}
+.context {
+   float: left;
+   padding: 10px 20px;
+   margin-bottom: 8px;
+   margin-right: 8px;
+   border: 1px solid #ccc;
+
+}
+
+
+/* reference link colors */
+.refs {
+  color:green;
+}
+
+.backrefs {
+  font-weight:bold;
+  color:red;
+}

Added: z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/zpt/ref_common_count.pt
===================================================================
--- z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/zpt/ref_common_count.pt	                        (rev 0)
+++ z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/zpt/ref_common_count.pt	2010-11-10 10:26:41 UTC (rev 118310)
@@ -0,0 +1,39 @@
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
+      xmlns:tal="http://xml.zope.org/namespaces/tal"
+      xmlns:metal="http://xml.zope.org/namespaces/metal"
+      xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+      lang="en"
+      i18n:domain="hc.debug.core">
+
+<body>
+
+
+
+        <div class="title">
+            <h1>Reference Count</h1>
+
+        </div>
+
+        <div>
+            <p>
+                <table summary="Most common in garbage" width="100%">
+                    <thead>
+                        <tr>
+                            <th scope="col" class="date" nowrap="nowrap">Ref Count</th>
+                            <th scope="col" class="actor">Object</th>
+                        </tr>
+                    </thead>
+            
+                    <tbody>
+                            <metal:block tal:repeat="pair view/pairs">
+                             <tr tal:define="oddrow repeat/pair/odd">
+                                 <td nowrap="nowrap" valign="top" tal:content="pair/refcount|nothing"/>
+                                 <td nowrap="nowrap" valign="top" tal:content="pair/name|nothing"/>
+                              </tr>
+                            </metal:block>
+                    </tbody>
+                </table>
+
+                <a class="button" href="javascript:history.back(1)" class="back-link">Cancel</a>
+
+</html>

Added: z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/zpt/ref_count.pt
===================================================================
--- z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/zpt/ref_count.pt	                        (rev 0)
+++ z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/zpt/ref_count.pt	2010-11-10 10:26:41 UTC (rev 118310)
@@ -0,0 +1,57 @@
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
+      xmlns:tal="http://xml.zope.org/namespaces/tal"
+      xmlns:metal="http://xml.zope.org/namespaces/metal"
+      xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+      lang="en"
+      i18n:domain="hc.debug.core">
+<body>
+<head>
+      <link type="text/css" rel="stylesheet" media="all" href=""
+           tal:attributes="href string:++resource++debug.css"/>
+</head>
+
+        <metal:block tal:condition="context/title|nothing">
+            <br/><a tal:attributes="href string:."
+                   tal:content="string: Back to ${context/title}"/>
+        </metal:block>
+
+        <div class="title">
+            <h1>Reference Count</h1>
+
+        </div>
+
+        <div class="debug_table">
+            <p class="debug_body description">
+            Garbage Container Objects: <span tal:replace="view/garbage_containing"/><br/>
+            Garbage Watching Objects: <span tal:replace="view/garbage_watching"/><br/>
+            Total Reference Count From Modules: <span tal:replace="view/total_ref_count"/><br/>
+        
+                <table summary="Module Ref Counts" width="100%" class="debug_table">
+                    <thead>
+                        <tr>
+                            <th scope="col" class="date" nowrap="nowrap">Ref Count</th>
+                            <th scope="col" class="actor">Module</th>
+                            <th scope="col" class="actor">&nbsp;</th>
+                        </tr>
+                    </thead>
+            
+                    <tbody>
+                            <metal:block tal:repeat="pair view/pairs"
+                                         tal:attributes="class string:even">
+                             <tr tal:define="oddrow repeat/pair/odd">
+                                 <td nowrap="nowrap" valign="top" tal:content="pair/refcount|nothing"/>
+                                 <td nowrap="nowrap" valign="top" tal:content="pair/name|nothing"/>
+                                 <td nowrap="nowrap">
+                                    <a tal:attributes="href string:./view_backref?name=${pair/name}"
+                                       tal:content="string: backrefs"/>
+                                    <a tal:attributes="href string:./view_ref?name=${pair/name}"
+                                       tal:content="string: refs"/>
+                                 </td>
+                              </tr>
+                            </metal:block>
+                    </tbody>
+                </table>
+
+                <a class="button" href="javascript:history.back(1)" class="back-link">Cancel</a>
+
+</html>

Added: z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/zpt/start.pt
===================================================================
--- z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/zpt/start.pt	                        (rev 0)
+++ z3c.memhunt.objgraph/trunk/z3c/memhunt/objgraph/zpt/start.pt	2010-11-10 10:26:41 UTC (rev 118310)
@@ -0,0 +1,83 @@
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
+      xmlns:tal="http://xml.zope.org/namespaces/tal"
+      xmlns:metal="http://xml.zope.org/namespaces/metal"
+      xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+      lang="en"
+      i18n:domain="z3c.memhunt.objgraph">
+<body>
+<head>
+      <link type="text/css" rel="stylesheet" media="all" href=""
+           tal:attributes="href string:++resource++debug.css"/>
+</head>
+    <div class="debug_table">
+        <metal:block tal:condition="context/title|nothing">
+            <br/><a tal:attributes="href string:."
+                   tal:content="string: Back to ${context/title}"/>
+        </metal:block>
+
+       <h1>Memory and Reference Counting Tools</h1>
+
+       <p class="description"> 
+            This tool is split into two logical tools. First is the Dependancy reference graphs.
+            Second is the memory introspection tools which use the guppy library for intelligence.
+       </p>
+
+           <br/>
+            <h3 class="sub_heading"> Dependancy Graph Views </h3>
+            <ul class="debug_body">
+                <li>
+                <a tal:attributes="href string:./context_refs.png"
+                   tal:content="string:This Context's Reference Graph"/>
+                </li>
+                
+                <li>
+                <a tal:attributes="href string:context_backrefs.png"
+                   tal:content="string:This Context's Back-Reference Graph"/>
+                </li>
+            
+                <li>
+                <a tal:attributes="href string:ref_count"
+                   tal:content="string: View Modules Ref-Counts"/>
+                </li>
+
+                <li>
+                <a tal:attributes="href string:ref_common_count"
+                   tal:content="string: View Most Common Objects in Garbage"/>
+                </li>
+
+            </ul>
+           <br/>
+            <h3 class="sub_heading"> Memory Introspection Views </h3>
+            <ul class="debug_body">
+                <li>
+                <a tal:attributes="href string:memory"
+                   tal:content="string: View Memory"/>
+                </li>
+
+                <li>
+                <a tal:attributes="href string:relative_memory"
+                   tal:content="string: View Relative Memory"/>
+                </li>
+
+
+                <li>
+                <a tal:attributes="href string:traverse_relative_memory"
+                   tal:content="string: View Traversed Relative Memory"/>
+                </li>
+
+
+                <li>
+                <a tal:attributes="href string:by_referrers"
+                   tal:content="string: View memory by Referrers"/>
+                </li>
+
+                <li>
+                <a tal:attributes="href string:get_biggest_offender"
+                   tal:content="string: View Biggest Memory Offender"/>
+                </li>
+
+            </ul>
+
+    </div>
+</body>
+</html>

Added: z3c.memhunt.objgraph/trunk/z3c.memhunt.objgraph.egg-info/PKG-INFO
===================================================================
--- z3c.memhunt.objgraph/trunk/z3c.memhunt.objgraph.egg-info/PKG-INFO	                        (rev 0)
+++ z3c.memhunt.objgraph/trunk/z3c.memhunt.objgraph.egg-info/PKG-INFO	2010-11-10 10:26:41 UTC (rev 118310)
@@ -0,0 +1,31 @@
+Metadata-Version: 1.0
+Name: z3c.memhunt.objgraph
+Version: 0.1dev
+Summary: UNKNOWN
+Home-page: ''
+Author: Daniel Blackburn & Holmes Corporation
+Author-email: danielb at holmescorp.com
+License: ZPL 2.1
+Description: 
+        0.0.1
+        -----
+        - Initial eggification of hc.debug.core
+        
+        This is a global debug package used to hold debug code across any
+        site.
+        
+        This package uses objgraph for introspection views
+        Change history
+        **************
+        
+Keywords: zope zope2 zope3 memory objgraph graphviz guppy
+Platform: UNKNOWN
+Classifier: Development Status :: 4 - Beta
+Classifier: Environment :: Web Environment
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Zope Public License
+Classifier: Programming Language :: Python
+Classifier: Natural Language :: English
+Classifier: Operating System :: OS Independent
+Classifier: Topic :: Internet :: WWW/HTTP
+Classifier: Framework :: Zope3

Added: z3c.memhunt.objgraph/trunk/z3c.memhunt.objgraph.egg-info/SOURCES.txt
===================================================================
--- z3c.memhunt.objgraph/trunk/z3c.memhunt.objgraph.egg-info/SOURCES.txt	                        (rev 0)
+++ z3c.memhunt.objgraph/trunk/z3c.memhunt.objgraph.egg-info/SOURCES.txt	2010-11-10 10:26:41 UTC (rev 118310)
@@ -0,0 +1,19 @@
+README.txt
+setup.cfg
+setup.py
+z3c/__init__.py
+z3c.memhunt.objgraph.egg-info/PKG-INFO
+z3c.memhunt.objgraph.egg-info/SOURCES.txt
+z3c.memhunt.objgraph.egg-info/dependency_links.txt
+z3c.memhunt.objgraph.egg-info/namespace_packages.txt
+z3c.memhunt.objgraph.egg-info/not-zip-safe
+z3c.memhunt.objgraph.egg-info/requires.txt
+z3c.memhunt.objgraph.egg-info/top_level.txt
+z3c/memhunt/__init__.py
+z3c/memhunt/objgraph/__init__.py
+z3c/memhunt/objgraph/checks.py
+z3c/memhunt/objgraph/cond_perms.py
+z3c/memhunt/objgraph/debug_views.py
+z3c/memhunt/objgraph/objgraph-1.py
+z3c/memhunt/objgraph/objgraph.py
+z3c/memhunt/objgraph/objgraph_orig.py
\ No newline at end of file

Added: z3c.memhunt.objgraph/trunk/z3c.memhunt.objgraph.egg-info/dependency_links.txt
===================================================================
--- z3c.memhunt.objgraph/trunk/z3c.memhunt.objgraph.egg-info/dependency_links.txt	                        (rev 0)
+++ z3c.memhunt.objgraph/trunk/z3c.memhunt.objgraph.egg-info/dependency_links.txt	2010-11-10 10:26:41 UTC (rev 118310)
@@ -0,0 +1 @@
+

Added: z3c.memhunt.objgraph/trunk/z3c.memhunt.objgraph.egg-info/namespace_packages.txt
===================================================================
--- z3c.memhunt.objgraph/trunk/z3c.memhunt.objgraph.egg-info/namespace_packages.txt	                        (rev 0)
+++ z3c.memhunt.objgraph/trunk/z3c.memhunt.objgraph.egg-info/namespace_packages.txt	2010-11-10 10:26:41 UTC (rev 118310)
@@ -0,0 +1 @@
+z3c

Added: z3c.memhunt.objgraph/trunk/z3c.memhunt.objgraph.egg-info/not-zip-safe
===================================================================
--- z3c.memhunt.objgraph/trunk/z3c.memhunt.objgraph.egg-info/not-zip-safe	                        (rev 0)
+++ z3c.memhunt.objgraph/trunk/z3c.memhunt.objgraph.egg-info/not-zip-safe	2010-11-10 10:26:41 UTC (rev 118310)
@@ -0,0 +1 @@
+

Added: z3c.memhunt.objgraph/trunk/z3c.memhunt.objgraph.egg-info/requires.txt
===================================================================
--- z3c.memhunt.objgraph/trunk/z3c.memhunt.objgraph.egg-info/requires.txt	                        (rev 0)
+++ z3c.memhunt.objgraph/trunk/z3c.memhunt.objgraph.egg-info/requires.txt	2010-11-10 10:26:41 UTC (rev 118310)
@@ -0,0 +1,6 @@
+setuptools
+guppy
+objgraph
+
+[tests]
+zope.testing
\ No newline at end of file

Added: z3c.memhunt.objgraph/trunk/z3c.memhunt.objgraph.egg-info/top_level.txt
===================================================================
--- z3c.memhunt.objgraph/trunk/z3c.memhunt.objgraph.egg-info/top_level.txt	                        (rev 0)
+++ z3c.memhunt.objgraph/trunk/z3c.memhunt.objgraph.egg-info/top_level.txt	2010-11-10 10:26:41 UTC (rev 118310)
@@ -0,0 +1 @@
+z3c



More information about the checkins mailing list