objgraph-1.8.0/ 0000755 0001750 0001750 00000000000 12277007140 012344 5 ustar mg mg 0000000 0000000 objgraph-1.8.0/MANIFEST.in 0000664 0001750 0001750 00000000347 12252050156 014106 0 ustar mg mg 0000000 0000000 include Makefile
include *.rst
include tests.py
include tox.ini
include docs/*.txt
include docs/*.dot
include docs/*.png
include docs/conf.py
include docs/_static/*.css
include .gitignore
include .gitattributes
include .travis.yml
objgraph-1.8.0/tests.py 0000755 0001750 0001750 00000004350 12271153747 014076 0 ustar mg mg 0000000 0000000 #!/usr/bin/python
import doctest
import glob
import os
import re
import sys
import shutil
import tempfile
import unittest
NODES_VARY = doctest.register_optionflag('NODES_VARY')
RANDOM_OUTPUT = doctest.register_optionflag('RANDOM_OUTPUT')
class RandomOutputChecker(doctest.OutputChecker):
def check_output(self, want, got, optionflags):
if optionflags & RANDOM_OUTPUT:
return True
return doctest.OutputChecker.check_output(self, want, got, optionflags)
class IgnoreNodeCountChecker(RandomOutputChecker):
_r = re.compile('\(\d+ nodes\)$', re.MULTILINE)
def check_output(self, want, got, optionflags):
if optionflags & NODES_VARY:
want = self._r.sub('(X nodes)', want)
got = self._r.sub('(X nodes)', got)
return RandomOutputChecker.check_output(self, want, got, optionflags)
def setUp(test):
test.tmpdir = tempfile.mkdtemp(prefix='test-objgraph-')
test.prevdir = os.getcwd()
test.prevtempdir = tempfile.tempdir
tempfile.tempdir = test.tmpdir
os.chdir(test.tmpdir)
try:
next
except NameError:
# Python < 2.6 compatibility
test.globs['next'] = lambda it: it.next()
def tearDown(test):
tempfile.tempdir = test.prevtempdir
os.chdir(test.prevdir)
shutil.rmtree(test.tmpdir)
def find_doctests():
doctests = set(glob.glob('docs/*.txt'))
if sys.version_info >= (3, 4):
# Skip uncollectable.txt on Python 3.4 and newer
doctests.discard('docs/uncollectable.txt')
return sorted(doctests)
def doctest_setup_py_works():
"""Test that setup.py works
>>> import sys
>>> orig_argv = sys.argv
>>> sys.argv = ['setup.py', '--description']
>>> import setup
Draws Python object reference graphs with graphviz
>>> sys.argv = orig_argv
"""
def test_suite():
doctests = find_doctests()
return unittest.TestSuite([
doctest.DocFileSuite(setUp=setUp, tearDown=tearDown,
optionflags=doctest.ELLIPSIS,
checker=IgnoreNodeCountChecker(),
*doctests),
doctest.DocTestSuite(),
])
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')
objgraph-1.8.0/README.rst 0000664 0001750 0001750 00000002734 12252051005 014033 0 ustar mg mg 0000000 0000000 Python Object Graphs
====================
.. image:: https://travis-ci.org/mgedmin/objgraph.png?branch=master
:target: https://travis-ci.org/mgedmin/objgraph
``objgraph`` is a module that lets you visually explore Python object graphs.
You'll need `graphviz `_ if you want to draw
the pretty graphs.
I recommend `xdot `_ for interactive use.
``pip install xdot`` should suffice; objgraph will automatically look for it
in your ``PATH``.
Installation and Documentation
------------------------------
``pip install objgraph`` or `download it from PyPI
`_.
Documentation lives at http://mg.pov.lt/objgraph.
.. _history:
History
-------
I've developed a set of functions that eventually became objgraph when I
was hunting for memory leaks in a Python program. The whole story -- with
illustrated examples -- is in this series of blog posts:
* `Hunting memory leaks in Python
`_
* `Python object graphs
`_
* `Object graphs with graphviz
`_
.. _devel:
Support and Development
-----------------------
The source code can be found in this Git repository:
https://github.com/mgedmin/objgraph.
To check it out, use ``git clone https://github.com/mgedmin/objgraph``.
Report bugs at https://github.com/mgedmin/objgraph/issues.
objgraph-1.8.0/CHANGES.rst 0000644 0001750 0001750 00000012710 12277006546 014160 0 ustar mg mg 0000000 0000000 Changes
=======
.. currentmodule:: objgraph
1.8.0 (2014-02-13)
------------------
- Moved to GitHub.
- Python 3.4 support (`LP#1270872 `_).
- New function: :func:`is_proper_module`.
- New ``shortnames`` argument for :func:`typestats`, :func:`most_common_types`,
:func:`show_most_common_types`, :func:`show_growth`, :func:`show_refs`,
and :func:`show_backrefs`.
:func:`count` and :func:`by_type` accept fully-qualified type names now.
Fixes `issue 4 `_.
1.7.2 (2012-10-23)
------------------
- Bugfix: setup.py sdist was broken on Python 2.7 (UnicodeDecodeError in
tarfile).
- The ``filename`` argument for :func:`show_refs` and :func:`show_backrefs` now
allows arbitrary image formats, not just PNG. Patch by `Riccardo
Murri `_.
- Temporary dot files are now named `objgraph-*.dot` instead of `tmp*.dot`.
- Python 3.3 support: no code changes, but some tests started failing because
the new and improved dictionary implementation no longer holds references to
str objects used as dict keys.
- Added a tox.ini for convenient multi-Python testing.
1.7.1 (2011-12-11)
------------------
- Bugfix: non-ASCII characters in object representations would break graph
generation on Python 3.x, in some locales (e.g. with LC_ALL=C). Reported and
fixed by `Stefano Rivera `_.
- Bugfix: setup.py was broken on Python 3.x
- Bugfix: dot.exe/xdot.exe were not found on Windows (`LP#767239
`_).
- Documentation updates: document the forgotten :func:`find_ref_chain`,
update :func:`show_chain` prototype.
1.7.0 (2011-03-11)
------------------
- New function: :func:`find_ref_chain`.
- New ``backrefs`` argument for :func:`show_chain`.
- New function: :func:`get_leaking_objects`, based on `a blog post by
Kristján Valur
`_.
- New ``objects`` argument for :func:`count`, :func:`typestats`,
:func:`most_common_types`, :func:`show_most_common_types`, and
:func:`by_type`.
- Edges pointing to function attributes such as __defaults__ or __globals__
are now labeled.
- Edge labels that are not simple strings now show the type.
- Bugfix: '\0' and other unsafe characters used in a dictionary key could
break graph generation.
- Bugfix: show_refs(..., filename='graph.dot') would then go to complain
about unrecognized file types and then produce a png.
1.6.0 (2010-12-18)
------------------
- Python 3 support, thanks to Stefano Rivera (fixes `LP#687601
`_).
- Removed weird weakref special-casing.
1.5.1 (2010-12-09)
------------------
- Avoid test failures in uncollectable-garbage.txt (fixes `LP#686731
`_).
- Added HACKING.txt (later renamed to HACKING.rst).
1.5.0 (2010-12-05)
------------------
- Show frame objects as well (fixes `LP#361704
`_).
- New functions: :func:`show_growth`, :func:`show_chain`.
- :func:`find_backref_chain` returns ``[obj]`` instead of ``None`` when a chain
could not be found. This makes ``show_chain(find_backref_chain(...), ...)``
not break.
- Show how many references were skipped from the output of
:func:`show_refs`/:func:`show_backrefs` by specifying ``too_many``.
- Make :func:`show_refs` descend into modules.
- Do not highlight classes that define a ``__del__``, highlight only instances of
those classes.
- Option to show reference counts in :func:`show_refs`/:func:`show_backrefs`.
- Add `Sphinx `_ documentation and a PyPI
long description.
1.4.0 (2010-11-03)
------------------
- Compatibility with Python 2.4 and 2.5 (``tempfile.NamedTemporaryFile`` has no
``delete`` argument).
- New function: :func:`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 :func:`show_refs`, :func:`show_backrefs`, since obj might be a
list/tuple.
- Fixes `LP#514422 `_: :func:`show_refs`,
:func:`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.1 (2008-09-10)
----------------
- New function: :func:`show_refs` for showing forward references.
- New functions: :func:`typestats` and :func:`show_most_common_types`.
- Object boxes are less crammed with useless information (such as IDs).
- Spawns `xdot `_ if it is available.
1.0 (2008-06-14)
----------------
- First public release.
objgraph-1.8.0/objgraph.py 0000644 0001750 0001750 00000067117 12277006606 014534 0 ustar mg mg 0000000 0000000 """
Tools for drawing Python object reference graphs with graphviz.
You can find documentation online at http://mg.pov.lt/objgraph/
Copyright (c) 2008-2014 Marius Gedminas
Copyright (c) 2010 Stefano Rivera
Released under the MIT licence.
"""
# 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@gedmin.as)"
__copyright__ = "Copyright (c) 2008-2014 Marius Gedminas"
__license__ = "MIT"
__version__ = "1.8.0"
__date__ = "2014-02-13"
import codecs
import gc
import re
import inspect
import types
import operator
import os
import subprocess
import tempfile
import sys
import itertools
try:
basestring
except NameError:
# Python 3.x compatibility
basestring = str
try:
iteritems = dict.iteritems
except AttributeError:
# Python 3.x compatibility
iteritems = dict.items
def count(typename, objects=None):
"""Count objects tracked by the garbage collector with a given class name.
Example:
>>> count('dict')
42
>>> count('MyClass', get_leaking_objects())
3
>>> count('mymodule.MyClass')
2
Note that the GC does not track simple objects like int or str.
.. versionchanged:: 1.7
New parameter: ``objects``.
.. versionchanged:: 1.8
Accepts fully-qualified type names (i.e. 'package.module.ClassName')
as well as short type names (i.e. 'ClassName').
"""
if objects is None:
objects = gc.get_objects()
if '.' in typename:
return sum(1 for o in objects if long_typename(o) == typename)
else:
return sum(1 for o in objects if type(o).__name__ == typename)
def typestats(objects=None, shortnames=True):
"""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 if ``shortnames`` is True.
Example:
>>> typestats()
{'list': 12041, 'tuple': 10245, ...}
>>> typestats(get_leaking_objects())
{'MemoryError': 1, 'tuple': 2795, 'RuntimeError': 1, 'list': 47, ...}
.. versionadded:: 1.1
.. versionchanged:: 1.7
New parameter: ``objects``.
.. versionchanged:: 1.8
New parameter: ``shortnames``.
"""
if objects is None:
objects = gc.get_objects()
if shortnames:
typename = short_typename
else:
typename = long_typename
stats = {}
for o in objects:
n = typename(o)
stats[n] = stats.get(n, 0) + 1
return stats
def most_common_types(limit=10, objects=None, shortnames=True):
"""Count the names of types with the most instances.
Returns a list of (type_name, count), sorted most-frequent-first.
Limits the return value to at most ``limit`` items. You may set ``limit``
to None to avoid that.
The caveats documented in :func:`typestats` apply.
Example:
>>> most_common_types(limit=2)
[('list', 12041), ('tuple', 10245)]
.. versionadded:: 1.4
.. versionchanged:: 1.7
New parameter: ``objects``.
.. versionchanged:: 1.8
New parameter: ``shortnames``.
"""
stats = sorted(typestats(objects, shortnames=shortnames).items(),
key=operator.itemgetter(1), reverse=True)
if limit:
stats = stats[:limit]
return stats
def show_most_common_types(limit=10, objects=None, shortnames=True):
"""Print the table of types of most common instances.
The caveats documented in :func:`typestats` apply.
Example:
>>> show_most_common_types(limit=5)
tuple 8959
function 2442
wrapper_descriptor 1048
dict 953
builtin_function_or_method 800
.. versionadded:: 1.1
.. versionchanged:: 1.7
New parameter: ``objects``.
.. versionchanged:: 1.8
New parameter: ``shortnames``.
"""
stats = most_common_types(limit, objects, shortnames=shortnames)
width = max(len(name) for name, count in stats)
for name, count in stats:
print('%-*s %i' % (width, name, count))
def show_growth(limit=10, peak_stats={}, shortnames=True):
"""Show the increase in peak object counts since last call.
Limits the output to ``limit`` largest deltas. You may set ``limit`` to
None to see all of them.
Uses and updates ``peak_stats``, a dictionary from type names to previously
seen peak object counts. Usually you don't need to pay attention to this
argument.
The caveats documented in :func:`typestats` apply.
Example:
>>> objgraph.show_growth()
wrapper_descriptor 970 +14
tuple 12282 +10
dict 1922 +7
...
.. versionadded:: 1.5
.. versionchanged:: 1.8
New parameter: ``shortnames``.
"""
gc.collect()
stats = typestats(shortnames=shortnames)
deltas = {}
for name, count in iteritems(stats):
old_count = peak_stats.get(name, 0)
if count > old_count:
deltas[name] = count - old_count
peak_stats[name] = count
deltas = sorted(deltas.items(), key=operator.itemgetter(1),
reverse=True)
if limit:
deltas = deltas[:limit]
if deltas:
width = max(len(name) for name, count in deltas)
for name, delta in deltas:
print('%-*s%9d %+9d' % (width, name, stats[name], delta))
def get_leaking_objects(objects=None):
"""Return objects that do not have any referents.
These could indicate reference-counting bugs in C code. Or they could
be legitimate.
Note that the GC does not track simple objects like int or str.
.. versionadded:: 1.7
"""
if objects is None:
gc.collect()
objects = gc.get_objects()
try:
ids = set(id(i) for i in objects)
for i in objects:
ids.difference_update(id(j) for j in gc.get_referents(i))
# this then is our set of objects without referrers
return [i for i in objects if id(i) in ids]
finally:
objects = i = None # clear cyclic references to frame
def by_type(typename, objects=None):
"""Return objects tracked by the garbage collector with a given class name.
Example:
>>> by_type('MyClass')
[]
Note that the GC does not track simple objects like int or str.
.. versionchanged:: 1.7
New parameter: ``objects``.
.. versionchanged:: 1.8
Accepts fully-qualified type names (i.e. 'package.module.ClassName')
as well as short type names (i.e. 'ClassName').
"""
if objects is None:
objects = gc.get_objects()
if '.' in typename:
return [o for o in objects if long_typename(o) == typename]
else:
return [o for o in 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_ref_chain(obj, predicate, max_depth=20, extra_ignore=()):
"""Find a shortest chain of references leading from obj.
The end of the chain will be some object that matches your predicate.
``predicate`` is a function taking one argument and returning a boolean.
``max_depth`` limits the search depth.
``extra_ignore`` can be a list of object IDs to exclude those objects from
your search.
Example:
>>> find_chain(obj, lambda x: isinstance(x, MyClass))
[obj, ..., ]
Returns ``[obj]`` if such a chain could not be found.
.. versionadded:: 1.7
"""
return find_chain(obj, predicate, gc.get_referents,
max_depth=max_depth, extra_ignore=extra_ignore)[::-1]
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.
``predicate`` is a function taking one argument and returning a boolean.
``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, is_proper_module)
[, ..., obj]
Returns ``[obj]`` if such a chain could not be found.
.. versionchanged:: 1.5
Returns ``obj`` instead of ``None`` when a chain could not be found.
"""
return find_chain(obj, predicate, gc.get_referrers,
max_depth=max_depth, extra_ignore=extra_ignore)
def show_backrefs(objs, max_depth=3, extra_ignore=(), filter=None, too_many=10,
highlight=None, filename=None, extra_info=None,
refcounts=False, shortnames=True):
"""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.
``filename`` if specified, can be the name of a .dot or a image
file, whose extension indicates the desired output format; note
that output to a specific format is entirely handled by GraphViz:
if the desired format is not supported, you just get the .dot
file. If ``filename`` is not specified, ``show_backrefs`` will
try to produce a .dot file and spawn a viewer (xdot). If xdot is
not available, ``show_backrefs`` will convert the .dot file to a
.png and print its name.
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 taking one argument and returning a
string) to report extra information for objects.
Specify ``refcounts=True`` if you want to see reference counts.
These will mostly match the number of arrows pointing to an object,
but can be different for various reasons.
Specify ``shortnames=False`` if you want to see fully-qualified type
names ('package.module.ClassName'). By default you get to see only the
class name part.
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())])
.. versionchanged:: 1.3
New parameters: ``filename``, ``extra_info``.
.. versionchanged:: 1.5
New parameter: ``refcounts``.
.. versionchanged:: 1.8
New parameter: ``shortnames``.
"""
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, refcounts=refcounts,
shortnames=shortnames)
def show_refs(objs, max_depth=3, extra_ignore=(), filter=None, too_many=10,
highlight=None, filename=None, extra_info=None,
refcounts=False, shortnames=True):
"""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.
``filename`` if specified, can be the name of a .dot or a image
file, whose extension indicates the desired output format; note
that output to a specific format is entirely handled by GraphViz:
if the desired format is not supported, you just get the .dot
file. If ``filename`` is not specified, ``show_refs`` will
try to produce a .dot file and spawn a viewer (xdot). If xdot is
not available, ``show_refs`` will convert the .dot file to a
.png and print its name.
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.
Specify ``refcounts=True`` if you want to see reference counts.
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())])
.. versionadded:: 1.1
.. versionchanged:: 1.3
New parameters: ``filename``, ``extra_info``.
.. versionchanged:: 1.5
Follows references from module objects instead of stopping.
New parameter: ``refcounts``.
.. versionchanged:: 1.8
New parameter: ``shortnames``.
"""
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, refcounts=refcounts,
shortnames=shortnames)
def show_chain(*chains, **kw):
"""Show a chain (or several chains) of object references.
Useful in combination with :func:`find_ref_chain` or
:func:`find_backref_chain`, e.g.
>>> show_chain(find_backref_chain(obj, is_proper_module))
You can specify if you want that chain traced backwards or forwards
by passing a ``backrefs`` keyword argument, e.g.
>>> show_chain(find_ref_chain(obj, is_proper_module),
... backrefs=False)
Ideally this shouldn't matter, but for some objects
:func:`gc.get_referrers` and :func:`gc.get_referents` are not perfectly
symmetrical.
You can specify ``highlight``, ``extra_info``, ``refcounts``,
``shortnames`` or ``filename`` arguments like for :func:`show_backrefs` or
:func:`show_refs`.
.. versionadded:: 1.5
.. versionchanged:: 1.7
New parameter: ``backrefs``.
"""
backrefs = kw.pop('backrefs', True)
chains = [chain for chain in chains if chain] # remove empty ones
def in_chains(x, ids=set(map(id, itertools.chain(*chains)))):
return id(x) in ids
max_depth = max(map(len, chains)) - 1
if backrefs:
show_backrefs([chain[-1] for chain in chains], max_depth=max_depth,
filter=in_chains, **kw)
else:
show_refs([chain[0] for chain in chains], max_depth=max_depth,
filter=in_chains, **kw)
def is_proper_module(obj):
"""
Returns ``True`` if ``obj`` can be treated like a garbage collector root.
That is, if ``obj`` is a module that is in ``sys.modules``.
>>> import imp
>>> is_proper_module([])
False
>>> is_proper_module(imp)
True
>>> is_proper_module(imp.new_module('foo'))
False
.. versionadded:: 1.8
"""
return (inspect.ismodule(obj) and
obj is sys.modules.get(getattr(obj, '__name__', None)))
#
# Internal helpers
#
def find_chain(obj, predicate, edge_func, max_depth=20, extra_ignore=()):
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))
ignore.add(id(sys._getframe())) # this function
ignore.add(id(sys._getframe(1))) # find_chain/find_backref_chain, most likely
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 = edge_func(target)
ignore.add(id(referrers))
for source in referrers:
if id(source) in ignore:
continue
if id(source) not in depth:
depth[id(source)] = tdepth + 1
parent[id(source)] = target
queue.append(source)
return [obj] # not found
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=None,
refcounts=False, shortnames=True):
if not isinstance(objs, (list, tuple)):
objs = [objs]
if filename and filename.endswith('.dot'):
f = codecs.open(filename, 'w', encoding='utf-8')
dot_filename = filename
else:
fd, dot_filename = tempfile.mkstemp(prefix='objgraph-',
suffix='.dot', text=True)
f = os.fdopen(fd, "w")
if f.encoding:
# Python 3 will wrap the file in the user's preferred encoding
# Re-wrap it for utf-8
import io
f = io.TextIOWrapper(f.detach(), 'utf-8')
f.write('digraph ObjectGraph {\n'
' node[shape=box, style=filled, fillcolor=white];\n')
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))
ignore.add(id(sys._getframe())) # this function
ignore.add(id(sys._getframe(1))) # show_refs/show_backrefs, most likely
for obj in objs:
f.write(' %s[fontcolor=red];\n' % (obj_node_id(obj)))
depth[id(obj)] = 0
queue.append(obj)
del obj
gc.collect()
nodes = 0
while queue:
nodes += 1
target = queue.pop(0)
tdepth = depth[id(target)]
f.write(' %s[label="%s"];\n' % (obj_node_id(target), obj_label(target, extra_info, refcounts, shortnames)))
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
f.write(' %s[fillcolor="%g,%g,%g"];\n' % (obj_node_id(target), h, s, v))
if v < 0.5:
f.write(' %s[fontcolor=white];\n' % (obj_node_id(target)))
if hasattr(getattr(target, '__class__', None), '__del__'):
f.write(" %s->%s_has_a_del[color=red,style=dotted,len=0.25,weight=10];\n" % (obj_node_id(target), obj_node_id(target)))
f.write(' %s_has_a_del[label="__del__",shape=doublecircle,height=0.25,color=red,fillcolor="0,.5,1",fontsize=6];\n' % (obj_node_id(target)))
if tdepth >= max_depth:
continue
if is_proper_module(target) and not swap_source_target:
# For show_backrefs(), it makes sense to stop when reaching a
# module because you'll end up in sys.modules and explode the
# graph with useless clutter. For show_refs(), it makes sense
# to continue.
continue
neighbours = edge_func(target)
ignore.add(id(neighbours))
n = 0
skipped = 0
for source in neighbours:
if id(source) in ignore:
continue
if filter and not filter(source):
continue
if n >= too_many:
skipped += 1
continue
if swap_source_target:
srcnode, tgtnode = target, source
else:
srcnode, tgtnode = source, target
elabel = edge_label(srcnode, tgtnode, shortnames)
f.write(' %s -> %s%s;\n' % (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
del source
del neighbours
if skipped > 0:
h, s, v = gradient((0, 1, 1), (0, 1, .3), tdepth + 1, max_depth)
if swap_source_target:
label = "%d more references" % skipped
edge = "%s->too_many_%s" % (obj_node_id(target), obj_node_id(target))
else:
label = "%d more backreferences" % skipped
edge = "too_many_%s->%s" % (obj_node_id(target), obj_node_id(target))
f.write(' %s[color=red,style=dotted,len=0.25,weight=10];\n' % edge)
f.write(' too_many_%s[label="%s",shape=box,height=0.25,color=red,fillcolor="%g,%g,%g",fontsize=6];\n' % (obj_node_id(target), label, h, s, v))
f.write(' too_many_%s[fontcolor=white];\n' % (obj_node_id(target)))
f.write("}\n")
f.close()
print("Graph written to %s (%d nodes)" % (dot_filename, nodes))
if filename and filename.endswith('.dot'):
# nothing else to do, the user asked for a .dot file
return
if not filename and program_in_path('xdot'):
print("Spawning graph viewer (xdot)")
subprocess.Popen(['xdot', dot_filename], close_fds=True)
elif program_in_path('dot'):
if not filename:
print("Graph viewer (xdot) not found, generating a png instead")
filename = dot_filename[:-4] + '.png'
stem, ext = os.path.splitext(filename)
f = open(filename, 'wb')
dot = subprocess.Popen(['dot', ('-T' + ext[1:]), dot_filename],
stdout=f, close_fds=False)
dot.wait()
if dot.returncode != 0:
# XXX: shouldn't this go to stderr or a log?
print("dot failed to generate '%s' image: output format not supported?")
f.close()
print("Image generated as %s" % filename)
else:
if filename:
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):
return ('o%d' % id(obj)).replace('-', '_')
def obj_label(obj, extra_info=None, refcounts=False, shortnames=True):
if shortnames:
label = [type(obj).__name__]
else:
label = [long_typename(obj)]
if refcounts:
label[0] += ' [%d]' % (sys.getrefcount(obj) - 4)
# Why -4? To ignore the references coming from
# obj_label's frame (obj)
# show_graph's frame (target variable)
# sys.getrefcount()'s argument
# something else that doesn't show up in gc.get_referrers()
label.append(safe_repr(obj))
if extra_info:
label.append(str(extra_info(obj)))
return quote('\n'.join(label))
def quote(s):
return (s.replace("\\", "\\\\")
.replace("\"", "\\\"")
.replace("\n", "\\n")
.replace("\0", "\\\\0"))
def short_typename(obj):
return type(obj).__name__
def long_typename(obj):
objtype = type(obj)
name = objtype.__name__
module = getattr(objtype, '__module__', None)
if module:
return '%s.%s' % (module, name)
else:
return name
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):
try:
if obj.__self__ is not None:
return obj.__func__.__name__ + ' (bound)'
else:
return obj.__func__.__name__
except AttributeError:
# Python < 2.6 compatibility
if obj.im_self is not None:
return obj.im_func.__name__ + ' (bound)'
else:
return obj.im_func.__name__
if isinstance(obj, types.FrameType):
return '%s:%s' % (obj.f_code.co_filename, obj.f_lineno)
if isinstance(obj, (tuple, list, dict, set)):
return '%d items' % len(obj)
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, shortnames=True):
if isinstance(target, dict) and target is getattr(source, '__dict__', None):
return ' [label="__dict__",weight=10]'
if isinstance(source, types.FrameType):
if target is source.f_locals:
return ' [label="f_locals",weight=10]'
if target is source.f_globals:
return ' [label="f_globals",weight=10]'
if isinstance(source, types.MethodType):
try:
if target is source.__self__:
return ' [label="__self__",weight=10]'
if target is source.__func__:
return ' [label="__func__",weight=10]'
except AttributeError:
# Python < 2.6 compatibility
if target is source.im_self:
return ' [label="im_self",weight=10]'
if target is source.im_func:
return ' [label="im_func",weight=10]'
if isinstance(source, types.FunctionType):
for k in dir(source):
if target is getattr(source, k):
return ' [label="%s",weight=10]' % quote(k)
if isinstance(source, dict):
for k, v in iteritems(source):
if v is target:
if isinstance(k, basestring) and is_identifier(k):
return ' [label="%s",weight=2]' % quote(k)
else:
if shortnames:
tn = type(k).__name__
else:
tn = long_typename(k)
return ' [label="%s"]' % quote(tn + "\n" + safe_repr(k))
return ''
is_identifier = re.compile('[a-zA-Z_][a-zA-Z_0-9]*$').match
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) or os.path.isfile(file + '.exe')]
return bool(path)
objgraph-1.8.0/objgraph.egg-info/ 0000755 0001750 0001750 00000000000 12277007140 015632 5 ustar mg mg 0000000 0000000 objgraph-1.8.0/objgraph.egg-info/dependency_links.txt 0000664 0001750 0001750 00000000001 12277007137 021710 0 ustar mg mg 0000000 0000000
objgraph-1.8.0/objgraph.egg-info/PKG-INFO 0000664 0001750 0001750 00000022767 12277007137 016755 0 ustar mg mg 0000000 0000000 Metadata-Version: 1.1
Name: objgraph
Version: 1.8.0
Summary: Draws Python object reference graphs with graphviz
Home-page: http://mg.pov.lt/objgraph/
Author: Marius Gedminas
Author-email: marius@gedmin.as
License: MIT
Description: Python Object Graphs
====================
.. image:: https://travis-ci.org/mgedmin/objgraph.png?branch=master
:target: https://travis-ci.org/mgedmin/objgraph
``objgraph`` is a module that lets you visually explore Python object graphs.
You'll need `graphviz `_ if you want to draw
the pretty graphs.
I recommend `xdot `_ for interactive use.
``pip install xdot`` should suffice; objgraph will automatically look for it
in your ``PATH``.
Installation and Documentation
------------------------------
``pip install objgraph`` or `download it from PyPI
`_.
Documentation lives at http://mg.pov.lt/objgraph.
.. _history:
History
-------
I've developed a set of functions that eventually became objgraph when I
was hunting for memory leaks in a Python program. The whole story -- with
illustrated examples -- is in this series of blog posts:
* `Hunting memory leaks in Python
`_
* `Python object graphs
`_
* `Object graphs with graphviz
`_
.. _devel:
Support and Development
-----------------------
The source code can be found in this Git repository:
https://github.com/mgedmin/objgraph.
To check it out, use ``git clone https://github.com/mgedmin/objgraph``.
Report bugs at https://github.com/mgedmin/objgraph/issues.
Changes
=======
1.8.0 (2014-02-13)
------------------
- Moved to GitHub.
- Python 3.4 support (`LP#1270872 `_).
- New function: `is_proper_module`.
- New ``shortnames`` argument for `typestats`, `most_common_types`,
`show_most_common_types`, `show_growth`, `show_refs`,
and `show_backrefs`.
`count` and `by_type` accept fully-qualified type names now.
Fixes `issue 4 `_.
1.7.2 (2012-10-23)
------------------
- Bugfix: setup.py sdist was broken on Python 2.7 (UnicodeDecodeError in
tarfile).
- The ``filename`` argument for `show_refs` and `show_backrefs` now
allows arbitrary image formats, not just PNG. Patch by `Riccardo
Murri `_.
- Temporary dot files are now named `objgraph-*.dot` instead of `tmp*.dot`.
- Python 3.3 support: no code changes, but some tests started failing because
the new and improved dictionary implementation no longer holds references to
str objects used as dict keys.
- Added a tox.ini for convenient multi-Python testing.
1.7.1 (2011-12-11)
------------------
- Bugfix: non-ASCII characters in object representations would break graph
generation on Python 3.x, in some locales (e.g. with LC_ALL=C). Reported and
fixed by `Stefano Rivera `_.
- Bugfix: setup.py was broken on Python 3.x
- Bugfix: dot.exe/xdot.exe were not found on Windows (`LP#767239
`_).
- Documentation updates: document the forgotten `find_ref_chain`,
update `show_chain` prototype.
1.7.0 (2011-03-11)
------------------
- New function: `find_ref_chain`.
- New ``backrefs`` argument for `show_chain`.
- New function: `get_leaking_objects`, based on `a blog post by
Kristján Valur
`_.
- New ``objects`` argument for `count`, `typestats`,
`most_common_types`, `show_most_common_types`, and
`by_type`.
- Edges pointing to function attributes such as __defaults__ or __globals__
are now labeled.
- Edge labels that are not simple strings now show the type.
- Bugfix: '\0' and other unsafe characters used in a dictionary key could
break graph generation.
- Bugfix: show_refs(..., filename='graph.dot') would then go to complain
about unrecognized file types and then produce a png.
1.6.0 (2010-12-18)
------------------
- Python 3 support, thanks to Stefano Rivera (fixes `LP#687601
`_).
- Removed weird weakref special-casing.
1.5.1 (2010-12-09)
------------------
- Avoid test failures in uncollectable-garbage.txt (fixes `LP#686731
`_).
- Added HACKING.txt (later renamed to HACKING.rst).
1.5.0 (2010-12-05)
------------------
- Show frame objects as well (fixes `LP#361704
`_).
- New functions: `show_growth`, `show_chain`.
- `find_backref_chain` returns ``[obj]`` instead of ``None`` when a chain
could not be found. This makes ``show_chain(find_backref_chain(...), ...)``
not break.
- Show how many references were skipped from the output of
`show_refs`/`show_backrefs` by specifying ``too_many``.
- Make `show_refs` descend into modules.
- Do not highlight classes that define a ``__del__``, highlight only instances of
those classes.
- Option to show reference counts in `show_refs`/`show_backrefs`.
- Add `Sphinx `_ documentation and a PyPI
long description.
1.4.0 (2010-11-03)
------------------
- 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.1 (2008-09-10)
----------------
- 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.
1.0 (2008-06-14)
----------------
- First public release.
Platform: UNKNOWN
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.4
Classifier: Programming Language :: Python :: 2.5
Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.1
Classifier: Programming Language :: Python :: 3.2
Classifier: Programming Language :: Python :: 3.3
objgraph-1.8.0/objgraph.egg-info/SOURCES.txt 0000664 0001750 0001750 00000001447 12277007140 017526 0 ustar mg mg 0000000 0000000 .gitattributes
.gitignore
.travis.yml
CHANGES.rst
HACKING.rst
MANIFEST.in
Makefile
README.rst
objgraph.py
setup.py
tests.py
tox.ini
docs/42.png
docs/CHANGES.txt
docs/HACKING.txt
docs/all-the-chars.dot
docs/canary-chain.png
docs/canary.png
docs/chain.png
docs/chain.txt
docs/class-with-finalizers.png
docs/conf.py
docs/extra-info.png
docs/extra-info.txt
docs/finalizers.png
docs/forward-chain.png
docs/generator-sample.txt
docs/highlight.png
docs/highlighting.txt
docs/index.txt
docs/objgraph.txt
docs/quoting.txt
docs/refcounts.png
docs/references.txt
docs/roots.png
docs/sample-backref-graph.png
docs/sample-graph.png
docs/too-many.png
docs/uncollectable.txt
docs/_static/mg.css
objgraph.egg-info/PKG-INFO
objgraph.egg-info/SOURCES.txt
objgraph.egg-info/dependency_links.txt
objgraph.egg-info/top_level.txt objgraph-1.8.0/objgraph.egg-info/top_level.txt 0000664 0001750 0001750 00000000011 12277007137 020364 0 ustar mg mg 0000000 0000000 objgraph
objgraph-1.8.0/tox.ini 0000664 0001750 0001750 00000000115 12252047677 013672 0 ustar mg mg 0000000 0000000 [tox]
envlist = py26, py27, py32, py33
[testenv]
commands = python tests.py
objgraph-1.8.0/setup.py 0000755 0001750 0001750 00000006324 12232020540 014054 0 ustar mg mg 0000000 0000000 #!/usr/bin/python
import codecs, os, re, sys, unittest, doctest
try:
from setuptools import setup
except ImportError:
from distutils.core import setup
setuptools_options = {}
else:
setuptools_options = dict(
test_suite='tests.test_suite',
)
try:
unichr
except NameError:
# Python 3.x support
unichr = chr
def read(filename):
f = codecs.open(filename, 'r', 'utf-8')
try:
return f.read()
finally:
f.close()
def unsphinx(text):
# remove Sphinx extensions used in CHANGES.rst from reStructuredText
# so that it can be handled by plain docutils
return text.replace(':func:', '').replace('.. currentmodule:: objgraph', '')
def get_version():
r = re.compile('^__version__ = "(.+)"$')
for line in read('objgraph.py').splitlines():
m = r.match(line)
if m:
# our read() returns unicode; coerce it back into str, or
# python2.7 setup.py sdist will try to mix a unicode filename with
# the byte stream of the .tar file
return str(m.group(1))
raise AssertionError('Could not determine version number from objgraph.py')
def get_description():
readme = read('README.rst')
changelog = read('CHANGES.rst')
description = unsphinx(readme + '\n\n\n' + changelog)
if '--unicode-description' in sys.argv:
sys.argv.remove('--unicode-description')
else:
# can't use u'' literals, this is supposed to work on both Py2 and Py3
description = description.replace('Kristj%sn' % unichr(0xe1),
'Kristjan')
description = description.encode('ascii', 'replace').decode('ascii')
return description
def build_images(doctests=()):
import tests
if not doctests:
doctests = tests.find_doctests()
suite = doctest.DocFileSuite(optionflags=doctest.ELLIPSIS,
checker=tests.IgnoreNodeCountChecker(),
*doctests)
os.chdir('docs')
result = unittest.TextTestRunner().run(suite)
if not result.wasSuccessful():
sys.exit(1)
if len(sys.argv) > 1 and sys.argv[1] == '--build-images':
build_images(sys.argv[2:])
sys.exit(0)
setup(name='objgraph',
version=get_version(),
author='Marius Gedminas',
author_email='marius@gedmin.as',
url='http://mg.pov.lt/objgraph/',
license='MIT',
description='Draws Python object reference graphs with graphviz',
long_description=get_description(),
classifiers=[
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.4',
'Programming Language :: Python :: 2.5',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.1',
'Programming Language :: Python :: 3.2',
'Programming Language :: Python :: 3.3',
],
py_modules=['objgraph'],
**setuptools_options)
objgraph-1.8.0/setup.cfg 0000644 0001750 0001750 00000000073 12277007140 014165 0 ustar mg mg 0000000 0000000 [egg_info]
tag_build =
tag_date = 0
tag_svn_revision = 0
objgraph-1.8.0/PKG-INFO 0000644 0001750 0001750 00000022767 12277007140 013457 0 ustar mg mg 0000000 0000000 Metadata-Version: 1.1
Name: objgraph
Version: 1.8.0
Summary: Draws Python object reference graphs with graphviz
Home-page: http://mg.pov.lt/objgraph/
Author: Marius Gedminas
Author-email: marius@gedmin.as
License: MIT
Description: Python Object Graphs
====================
.. image:: https://travis-ci.org/mgedmin/objgraph.png?branch=master
:target: https://travis-ci.org/mgedmin/objgraph
``objgraph`` is a module that lets you visually explore Python object graphs.
You'll need `graphviz `_ if you want to draw
the pretty graphs.
I recommend `xdot `_ for interactive use.
``pip install xdot`` should suffice; objgraph will automatically look for it
in your ``PATH``.
Installation and Documentation
------------------------------
``pip install objgraph`` or `download it from PyPI
`_.
Documentation lives at http://mg.pov.lt/objgraph.
.. _history:
History
-------
I've developed a set of functions that eventually became objgraph when I
was hunting for memory leaks in a Python program. The whole story -- with
illustrated examples -- is in this series of blog posts:
* `Hunting memory leaks in Python
`_
* `Python object graphs
`_
* `Object graphs with graphviz
`_
.. _devel:
Support and Development
-----------------------
The source code can be found in this Git repository:
https://github.com/mgedmin/objgraph.
To check it out, use ``git clone https://github.com/mgedmin/objgraph``.
Report bugs at https://github.com/mgedmin/objgraph/issues.
Changes
=======
1.8.0 (2014-02-13)
------------------
- Moved to GitHub.
- Python 3.4 support (`LP#1270872 `_).
- New function: `is_proper_module`.
- New ``shortnames`` argument for `typestats`, `most_common_types`,
`show_most_common_types`, `show_growth`, `show_refs`,
and `show_backrefs`.
`count` and `by_type` accept fully-qualified type names now.
Fixes `issue 4 `_.
1.7.2 (2012-10-23)
------------------
- Bugfix: setup.py sdist was broken on Python 2.7 (UnicodeDecodeError in
tarfile).
- The ``filename`` argument for `show_refs` and `show_backrefs` now
allows arbitrary image formats, not just PNG. Patch by `Riccardo
Murri `_.
- Temporary dot files are now named `objgraph-*.dot` instead of `tmp*.dot`.
- Python 3.3 support: no code changes, but some tests started failing because
the new and improved dictionary implementation no longer holds references to
str objects used as dict keys.
- Added a tox.ini for convenient multi-Python testing.
1.7.1 (2011-12-11)
------------------
- Bugfix: non-ASCII characters in object representations would break graph
generation on Python 3.x, in some locales (e.g. with LC_ALL=C). Reported and
fixed by `Stefano Rivera `_.
- Bugfix: setup.py was broken on Python 3.x
- Bugfix: dot.exe/xdot.exe were not found on Windows (`LP#767239
`_).
- Documentation updates: document the forgotten `find_ref_chain`,
update `show_chain` prototype.
1.7.0 (2011-03-11)
------------------
- New function: `find_ref_chain`.
- New ``backrefs`` argument for `show_chain`.
- New function: `get_leaking_objects`, based on `a blog post by
Kristján Valur
`_.
- New ``objects`` argument for `count`, `typestats`,
`most_common_types`, `show_most_common_types`, and
`by_type`.
- Edges pointing to function attributes such as __defaults__ or __globals__
are now labeled.
- Edge labels that are not simple strings now show the type.
- Bugfix: '\0' and other unsafe characters used in a dictionary key could
break graph generation.
- Bugfix: show_refs(..., filename='graph.dot') would then go to complain
about unrecognized file types and then produce a png.
1.6.0 (2010-12-18)
------------------
- Python 3 support, thanks to Stefano Rivera (fixes `LP#687601
`_).
- Removed weird weakref special-casing.
1.5.1 (2010-12-09)
------------------
- Avoid test failures in uncollectable-garbage.txt (fixes `LP#686731
`_).
- Added HACKING.txt (later renamed to HACKING.rst).
1.5.0 (2010-12-05)
------------------
- Show frame objects as well (fixes `LP#361704
`_).
- New functions: `show_growth`, `show_chain`.
- `find_backref_chain` returns ``[obj]`` instead of ``None`` when a chain
could not be found. This makes ``show_chain(find_backref_chain(...), ...)``
not break.
- Show how many references were skipped from the output of
`show_refs`/`show_backrefs` by specifying ``too_many``.
- Make `show_refs` descend into modules.
- Do not highlight classes that define a ``__del__``, highlight only instances of
those classes.
- Option to show reference counts in `show_refs`/`show_backrefs`.
- Add `Sphinx `_ documentation and a PyPI
long description.
1.4.0 (2010-11-03)
------------------
- 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.1 (2008-09-10)
----------------
- 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.
1.0 (2008-06-14)
----------------
- First public release.
Platform: UNKNOWN
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.4
Classifier: Programming Language :: Python :: 2.5
Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.1
Classifier: Programming Language :: Python :: 3.2
Classifier: Programming Language :: Python :: 3.3
objgraph-1.8.0/docs/ 0000755 0001750 0001750 00000000000 12277007140 013274 5 ustar mg mg 0000000 0000000 objgraph-1.8.0/docs/too-many.png 0000644 0001750 0001750 00000034330 12277007131 015550 0 ustar mg mg 0000000 0000000 PNG
IHDR K bKGD IDATxy\T?;"Ȏ,
*)Dci}2XBi&Y~Qj
ʾ3,"02
YχÝ{ι.!ġ] B!4-0BI&L8BIBM@RRR]]U1BHQQQdW!ZOL8t:=22*DB)BH2a!Lp!$&B!Ʉ BH2a!Lp!FP&B#JY+B=xg
p!$&Bg{dqXTUF}}xHJ4QJB[//ŋ"8}8Bq ѣ11r% :.?>BՅzHKV??&Bز .#ڤ5D!4 q#A\]Bhde<<