Objgraph Readthedocs Io en Stable
Objgraph Readthedocs Io en Stable
Release 3.5.0
Marius Gedminas
2 Quick start 5
3 Backreferences 7
6 API Documentation 15
6.1 objgraph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
8 History 35
8.1 Changes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
Index 47
i
ii
objgraph Documentation, Release 3.5.0
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.
Contents 1
objgraph Documentation, Release 3.5.0
2 Contents
CHAPTER 1
3
objgraph Documentation, Release 3.5.0
Quick start
>>> x = []
>>> y = [x, [x], dict(x=x)]
>>> import objgraph
>>> objgraph.show_refs([y], filename='sample-graph.png')
Graph written to ....dot (... nodes)
Image generated as sample-graph.png
(If you’ve installed xdot, omit the filename argument to get the interactive viewer.)
You should see a graph like this:
If you prefer to handle your own file output, you can provide a file object to the output parameter of show_refs
and show_backrefs instead of a filename. The contents of this file will contain the graph source in DOT format.
5
objgraph Documentation, Release 3.5.0
Backreferences
Now try
7
objgraph Documentation, Release 3.5.0
8 Chapter 3. Backreferences
CHAPTER 4
The original purpose of objgraph was to help me find memory leaks. The idea was to pick an object in memory
that shouldn’t be there and then see what references are keeping it alive.
To get a quick overview of the objects in memory, use the imaginatively-named show_most_common_types():
>>> objgraph.show_most_common_types() # doctest: +RANDOM_OUTPUT
tuple 5224
function 1329
wrapper_descriptor 967
dict 790
builtin_function_or_method 658
method_descriptor 340
weakref 322
list 168
member_descriptor 167
type 163
But that’s looking for a small needle in a large haystack. Can we limit our haystack to objects that were created
recently? Perhaps.
Let’s define a function that “leaks” memory
>>> class MyBigFatObject(object):
... pass
...
>>> def computate_something(_cache={}):
... _cache[42] = dict(foo=MyBigFatObject(),
... bar=MyBigFatObject())
... # a very explicit and easy-to-find "leak" but oh well
... x = MyBigFatObject() # this one doesn't leak
We take a snapshot of all the objects counts that are alive before we call our function
>>> objgraph.show_growth(limit=3) # doctest: +RANDOM_OUTPUT
tuple 5228 +5228
(continues on next page)
9
objgraph Documentation, Release 3.5.0
>>> computate_something()
>>> objgraph.show_growth() # doctest: +RANDOM_OUTPUT
MyBigFatObject 2 +2
dict 797 +1
It’s easy to see MyBigFatObject instances that appeared and were not freed. I can pick one of them at random and
trace the reference chain back to one of the garbage collector’s roots.
For simplicity’s sake let’s assume all of the roots are modules. objgraph provides a function,
is_proper_module(), to check this. If you’ve any examples where that isn’t true, I’d love to hear about them
(although see Reference counting bugs).
It is perhaps surprising to find linecache at the end of that chain (apparently doctest monkey-patches it), but
the important things – computate_something() and its cache dictionary – are in there.
There are other tools, perhaps better suited for memory leak hunting: heapy, Dozer.
11
objgraph Documentation, Release 3.5.0
Bugs in C-level reference counting may leave objects in memory that do not have any other objects pointing at them.
You can find these by calling get_leaking_objects(), but you’ll have to filter out legitimate GC roots from
them, and there are a lot of those:
>>> objgraph.show_most_common_types(objects=roots)
... # doctest: +RANDOM_OUTPUT
tuple 4333
dict 171
list 74
instancemethod 4
listiterator 2
MemoryError 1
Sub 1
RuntimeError 1
Param 1
Add 1
13
objgraph Documentation, Release 3.5.0
API Documentation
6.1 objgraph
6.1.1 Statistics
objgraph.count(typename[, objects ])
Count objects tracked by the garbage collector with a given class name.
The class name can optionally be fully qualified.
Example:
>>> count('dict')
42
>>> count('mymodule.MyClass')
2
Note: The Python garbage collector does not track simple objects like int or str. See https://docs.python.org/3/
library/gc.html#gc.is_tracked for more information.
Instead of looking through all objects tracked by the GC, you may specify your own collection, e.g.
15
objgraph Documentation, Release 3.5.0
>>> typestats()
{'list': 12041, 'tuple': 10245, ...}
>>> typestats(get_leaking_objects())
{'MemoryError': 1, 'tuple': 2795, 'RuntimeError': 1, 'list': 47, ...}
>>> most_common_types(limit=2)
[('list', 12041), ('tuple', 10245)]
Example:
>>> show_most_common_types(limit=5)
tuple 8959
function 2442
wrapper_descriptor 1048
dict 953
builtin_function_or_method 800
>>> growth(2)
[(tuple, 12282, 10), (dict, 1922, 7)]
>>> show_growth()
wrapper_descriptor 970 +14
tuple 12282 +10
dict 1922 +7
...
6.1. objgraph 17
objgraph Documentation, Release 3.5.0
objgraph.get_leaking_objects([objects ])
Return objects that do not have any referents.
>>> by_type('MyClass')
[<mymodule.MyClass object at 0x...>]
Note that the GC does not track simple objects like int or str.
Changed in version 1.7: New parameter: objects.
Changed in version 1.8: Accepts fully-qualified type names (i.e. ‘package.module.ClassName’) as well as short
type names (i.e. ‘ClassName’).
objgraph.at(addr)
Return an object at a given memory address.
The reverse of id(obj):
Note that this function does not work on objects that are not tracked by the GC (e.g. ints or strings).
objgraph.at_addrs(address_set)
Return a list of objects for a given set of memory addresses.
The reverse of [id(obj1), id(obj2), . . . ]. Note that objects are returned in an arbitrary order.
When one gets new_ids from get_new_ids(), one can use this function to get a list of those objects. Then
one can iterate over the new objects, print out what they are, and call show_backrefs() or show_chain()
to see where they are referenced.
>>> a = [0, 1, 2]
>>> new_ids = get_new_ids()
>>> new_lists = at_addrs(new_ids['list'])
>>> a in new_lists
True
Note that this function does not work on objects that are not tracked by the GC (e.g. ints or strings).
New in version 3.4.
objgraph.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.
6.1. objgraph 19
objgraph Documentation, Release 3.5.0
You can specify if you want that chain traced backwards or forwards by passing a backrefs keyword argu-
ment, e.g.
Ideally this shouldn’t matter, but for some objects gc.get_referrers() and gc.get_referents()
are not perfectly symmetrical.
You can specify highlight, extra_info, refcounts, shortnames, filename or output argu-
ments like for show_backrefs() or show_refs().
New in version 1.5.
Changed in version 1.7: New parameter: backrefs.
Changed in version 2.0: New parameter: output.
objgraph.show_backrefs(objs[, max_depth=3, extra_ignore=(), filter=None, too_many=10, high-
light=None, filename=None, extra_info=None, refcounts=False, short-
names=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 and output are not specified, show_backrefs will try
to display the graph inline (if you’re using IPython), otherwise it’ll 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.
output if specified, the GraphViz output will be written to this file object. output and filename should
not both be specified.
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.
Use extra_node_attrs (a function taking the current object as argument, returning a dict of strings) to
add extra attributes to the nodes. See https://www.graphviz.org/doc/info/attrs.html for a list of possible node
attributes.
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())])
>>> show_backrefs(obj, extra_node_attrs=lambda x: dict(URL=str(id(x))))
6.1. objgraph 21
objgraph Documentation, Release 3.5.0
>>> 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_refs(obj, extra_node_attrs=lambda x: dict(URL=str(id(x))))
23
objgraph Documentation, Release 3.5.0
You can enable reference counts. The number of arrows pointing to an object should match the number in square
brackets, usually, but there can be exceptions. E.g. objects internal to objgraph’s implementation may inflate the
reference count somewhat.
We see two references to the one_reference object: the one not shown comes from the list passed to
show_backrefs.
I think the extra references to the frame object and locals dict come from the interpreter internals.
You can add extra information to object graphs, if you desire. For example, let’s add object IDs:
>>> x = []
>>> y = [x, [x], dict(x=x)]
>>> import objgraph
>>> objgraph.show_refs([y], extra_info=lambda x: hex(id(x)),
... filename='extra-info.png')
Graph written to ....dot (... nodes)
Image generated as extra-info.png
This way you can then look them up later with at(), if you desire to get a closer look at a particular object:
>>> objgraph.at(id(x)) is x
True
Warning: this doesn’t work with strings or ints or other simple types that aren’t tracked by the cyclic garbage collector:
7.4 Highlighting
Objects that have a __del__ method cannot be collected by the garbage collector if they participate in a cycle, prior
to Python 3.4.
>>> x = Nondestructible()
>>> y = []
>>> z = []
>>> x.append(y)
>>> y.append(z)
>>> z.append(x)
When you remove all other references to these, they end up in gc.garbage.
>>> objgraph.show_backrefs(objgraph.by_type('Nondestructible'),
... filename='finalizers.png') # doctest: +NODES_VARY
Graph written to ....dot (8 nodes)
Image generated as finalizers.png
Note that classes that define a __del__ method do not have this indicator
7.4. Highlighting 27
objgraph Documentation, Release 3.5.0
>>> it = count_to_three()
>>> next(it)
1
>>> objgraph.show_backrefs(objgraph.by_type('Canary'),
... max_depth=7,
... filename='canary.png') # doctest: +NODES_VARY
Graph written to ....dot (15 nodes)
Image generated as canary.png
Or we can examine just one of the reference chains leading straight to a module.
>>> objgraph.show_chain(
... objgraph.find_backref_chain(objgraph.by_type('Canary')[0],
... objgraph.is_proper_module),
... filename='canary-chain.png')
Graph written to ....dot (11 nodes)
Image generated as canary-chain.png
To a first approximation, modules are garbage-collection roots, which makes the latter technique most useful.
The other day I was wondering why pickling a particular object errored out with an error deep in one of the subobjects.
Let’s make sure our string quoting function can handle anything:
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
And here’s the change log
8.1 Changes
• Do not require mock for the test suite on Python 3; use unittest.mock instead.
• 100% test coverage for each version of Python rather than combined, using coverage-python-version.
• Add the optional extra_node_attrs parameter to show_backrefs and show_backrefs
• Fix IPython/Jupyter inline graph support code that would kick in even if you explicitly passed a file-
name=’foo.png’ argument to show_refs/show_backrefs. See issue 47.
• Add support for Python 3.8.
• Drop support for Python 3.5.
35
objgraph Documentation, Release 3.5.0
• Correct UTF-8 mojibake in the changelog and switch all links to HTTPS.
• The file argument of show_most_common_types() and show_growth() now defaults to None in-
stead of sys.stdout. None is interpreted to be the same as sys.stdout, which means the right stdout
will be used if you change it at runtime (which happens, in doctests).
• show_most_common_types() and show_growth() now accept a file argument if you want to redi-
rect the output elsewhere.
Fixes issue 24. Contributed by “d-sun-d”.
• Don’t trust __class__ to be accurate and __name__ to be a string. Fixes errors in some convoluted corner
cases when mocks are involved.
Contributed by Andrew Shannon Brown in PR 26.
• Drop support for Python 2.4, 2.5, and 2.6.
36 Chapter 8. History
objgraph Documentation, Release 3.5.0
• Avoid creating reference cycles between the stack frame and the local objects variable in by_type(),
count(), and typestats().
Fixes issue 22. Contributed by Erik Bray.
• show_refs() and show_backrefs() now accept a file-like object (via the new output argument) as an
alternative to a filename.
• Made internal helper methods private. This includes find_chain(), show_graph(), obj_node_id(),
obj_label(), quote(), long_typename(), safe_repr(), short_repr(), gradient(),
edge_label(), and _program_in_path().
• Correctly determine the name of old-style classes in count(), by_type(), and graph drawing functions.
Fixes issue 16. Contributed by Mike Lambert.
• Do not expect file objects to have an encoding attribute. Makes objgraph compatible with Eventlet’s monkey-
patching.
Fixes issue 6. Contributed by Jakub Stasiak.
• 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.
8.1. Changes 37
objgraph Documentation, Release 3.5.0
• 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.
38 Chapter 8. History
objgraph Documentation, Release 3.5.0
• 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.
• Compatibility with Python 2.4 and 2.5 (tempfile.NamedTemporaryFile has no delete argument).
• New function: most_common_types().
8.1. Changes 39
objgraph Documentation, Release 3.5.0
40 Chapter 8. History
CHAPTER 9
make test
The test suite is mostly smoke tests (i.e. crashes will be noticed, subtly wrong output will be missed). I hope to
improve that in the future, but don’t hold your breath. Most of the testing is done manually or semi-automatically, e.g.
by running make images and eyeballing the results (imgdiff is handy there).
GitHub pull requests are probably the best way to send me patches. Or just email them to <[email protected]>.
I’d appreciate issues in GitHub for each proposed change, be it a bug or a feature request.
41
objgraph Documentation, Release 3.5.0
You can run the test suite for all supported Python versions with
tox -p auto
If a test fails, often the easiest way to debug is is to compare the output visually
An easy way to get multiple Pythons versions on Ubuntu is to use Felix Krull’s “deadsnakes” PPA:
As I mentioned, the tests are mostly smoke tests, and even then they’re incomplete. Install coverage to see how
incomplete they are with
make coverage
I use a vim plugin to higlight lines not covered by tests while I edit
make coverage
vim objgraph.py
:HighlightCoverage
make coverage
coverage html
9.1.4 Documentation
Please git checkout -- the png files that haven’t changed significantly. (Many of the images include things like
memory addresses which tend to change from run to run.)
imgdiff is useful for comparing the images with their older versions:
It has a few options that may make the changes easier to see. I personally like:
def do_something():
"""Do something.
def do_something():
"""Do something.
Example:
>>> do_something()
42
"""
I find restview very handy for documentation writing: it lets me see how the text looks by pressing Ctrl-R in a browser
window, without having to re-run any documentation building commands. The downside is that restview doesn’t
support Sphinx extensions to ReStructuredText, so you end up with error messages all over the place. Then again this
is useful for bits that can’t use Sphinx extensions, like the PyPI long description.
To preview the PyPI long description (which is generated by concatenating README.rst and CHANGES.rst) with
restview, use this handy command:
make preview-pypi-description
because typing
You need write access to the PyPI package and to the Git branch on GitHub. At the moment of this writing, this means
you must be me.
Run make release and follow the instructions. It is safe to run this command at any time: it never com-
mits/pushes/uploads to PyPI, it just tells you what to do.
It is important to keep MANIFEST.in up to date so that source tarballs generated with python setup.py sdist
aren’t missing any files, even if you don’t have the right setuptools version control plugins installed. You can run
make distcheck
to be sure this is so, but it’s not necessary – make release will do this every time.
(I’ve later written a standalone tool, check-manifest that can do this check for every Python package.)
o
objgraph, 15
45
objgraph Documentation, Release 3.5.0
A
at() (in module objgraph), 19
at_addrs() (in module objgraph), 19
B
by_type() (in module objgraph), 19
C
count() (in module objgraph), 15
F
find_backref_chain() (in module objgraph), 20
find_ref_chain() (in module objgraph), 20
G
get_leaking_objects() (in module objgraph), 18
get_new_ids() (in module objgraph), 17
growth() (in module objgraph), 17
I
is_proper_module() (in module objgraph), 19
M
most_common_types() (in module objgraph), 16
O
objgraph (module), 15
S
show_backrefs() (in module objgraph), 21
show_chain() (in module objgraph), 20
show_growth() (in module objgraph), 17
show_most_common_types() (in module obj-
graph), 16
show_refs() (in module objgraph), 22
T
typestats() (in module objgraph), 16
47