mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
#510: Fix inheritance diagrams for classes that are not picklable.
No need to keep the actual list of classes; only names and edges need to be present in generate_dot().
This commit is contained in:
parent
80ad3d7d69
commit
b90ca05941
6
CHANGES
6
CHANGES
@ -1,3 +1,9 @@
|
||||
Release 1.0.3 (in development)
|
||||
==============================
|
||||
|
||||
* #510: Fix inheritance diagrams for classes that are not picklable.
|
||||
|
||||
|
||||
Release 1.0.2 (Aug 14, 2010)
|
||||
============================
|
||||
|
||||
|
@ -66,7 +66,7 @@ class InheritanceGraph(object):
|
||||
from all the way to the root "object", and then is able to generate a
|
||||
graphviz dot graph from them.
|
||||
"""
|
||||
def __init__(self, class_names, currmodule, show_builtins=False):
|
||||
def __init__(self, class_names, currmodule, show_builtins=False, parts=0):
|
||||
"""
|
||||
*class_names* is a list of child classes to show bases from.
|
||||
|
||||
@ -74,12 +74,11 @@ class InheritanceGraph(object):
|
||||
in the graph.
|
||||
"""
|
||||
self.class_names = class_names
|
||||
self.classes = self._import_classes(class_names, currmodule)
|
||||
self.all_classes = self._all_classes(self.classes)
|
||||
if len(self.all_classes) == 0:
|
||||
classes = self._import_classes(class_names, currmodule)
|
||||
self.class_info = self._class_info(classes, show_builtins, parts)
|
||||
if not self.class_info:
|
||||
raise InheritanceException('No classes found for '
|
||||
'inheritance diagram')
|
||||
self.show_builtins = show_builtins
|
||||
|
||||
def _import_class_or_module(self, name, currmodule):
|
||||
"""
|
||||
@ -129,36 +128,48 @@ class InheritanceGraph(object):
|
||||
'not a class or module' % name)
|
||||
|
||||
def _import_classes(self, class_names, currmodule):
|
||||
"""
|
||||
Import a list of classes.
|
||||
"""
|
||||
"""Import a list of classes."""
|
||||
classes = []
|
||||
for name in class_names:
|
||||
classes.extend(self._import_class_or_module(name, currmodule))
|
||||
return classes
|
||||
|
||||
def _all_classes(self, classes):
|
||||
"""
|
||||
Return a list of all classes that are ancestors of *classes*.
|
||||
def _class_info(self, classes, show_builtins, parts):
|
||||
"""Return name and bases for all classes that are ancestors of
|
||||
*classes*.
|
||||
|
||||
*parts* gives the number of dotted name parts that is removed from the
|
||||
displayed node names.
|
||||
"""
|
||||
all_classes = {}
|
||||
builtins = __builtins__.values()
|
||||
|
||||
def recurse(cls):
|
||||
all_classes[cls] = None
|
||||
for c in cls.__bases__:
|
||||
if c not in all_classes:
|
||||
recurse(c)
|
||||
if not show_builtins and cls in builtins:
|
||||
return
|
||||
|
||||
nodename = self.class_name(cls, parts)
|
||||
fullname = self.class_name(cls, 0)
|
||||
|
||||
baselist = []
|
||||
all_classes[cls] = (nodename, fullname, baselist)
|
||||
for base in cls.__bases__:
|
||||
if not show_builtins and base in builtins:
|
||||
return
|
||||
baselist.append(self.class_name(base, parts))
|
||||
if base not in all_classes:
|
||||
recurse(base)
|
||||
|
||||
for cls in classes:
|
||||
recurse(cls)
|
||||
|
||||
return all_classes.keys()
|
||||
return all_classes.values()
|
||||
|
||||
def class_name(self, cls, parts=0):
|
||||
"""
|
||||
Given a class object, return a fully-qualified name. This
|
||||
works for things I've tested in matplotlib so far, but may not
|
||||
be completely general.
|
||||
"""Given a class object, return a fully-qualified name.
|
||||
|
||||
This works for things I've tested in matplotlib so far, but may not be
|
||||
completely general.
|
||||
"""
|
||||
module = cls.__module__
|
||||
if module == '__builtin__':
|
||||
@ -174,7 +185,7 @@ class InheritanceGraph(object):
|
||||
"""
|
||||
Get all of the class names involved in the graph.
|
||||
"""
|
||||
return [self.class_name(x) for x in self.all_classes]
|
||||
return [fullname for (_, fullname, _) in self.class_info]
|
||||
|
||||
# These are the default attrs for graphviz
|
||||
default_graph_attrs = {
|
||||
@ -200,7 +211,7 @@ class InheritanceGraph(object):
|
||||
def _format_graph_attrs(self, attrs):
|
||||
return ''.join(['%s=%s;\n' % x for x in attrs.items()])
|
||||
|
||||
def generate_dot(self, name, parts=0, urls={}, env=None,
|
||||
def generate_dot(self, name, urls={}, env=None,
|
||||
graph_attrs={}, node_attrs={}, edge_attrs={}):
|
||||
"""
|
||||
Generate a graphviz dot graph from the classes that
|
||||
@ -228,26 +239,17 @@ class InheritanceGraph(object):
|
||||
res.append('digraph %s {\n' % name)
|
||||
res.append(self._format_graph_attrs(g_attrs))
|
||||
|
||||
for cls in self.all_classes:
|
||||
if not self.show_builtins and cls in __builtins__.values():
|
||||
continue
|
||||
|
||||
name = self.class_name(cls, parts)
|
||||
|
||||
for name, fullname, bases in self.class_info:
|
||||
# Write the node
|
||||
this_node_attrs = n_attrs.copy()
|
||||
url = urls.get(self.class_name(cls))
|
||||
url = urls.get(fullname)
|
||||
if url is not None:
|
||||
this_node_attrs['URL'] = '"%s"' % url
|
||||
res.append(' "%s" [%s];\n' %
|
||||
(name, self._format_node_attrs(this_node_attrs)))
|
||||
|
||||
# Write the edges
|
||||
for base in cls.__bases__:
|
||||
if not self.show_builtins and base in __builtins__.values():
|
||||
continue
|
||||
|
||||
base_name = self.class_name(base, parts)
|
||||
for base_name in bases:
|
||||
res.append(' "%s" -> "%s" [%s];\n' %
|
||||
(base_name, name,
|
||||
self._format_node_attrs(e_attrs)))
|
||||
@ -280,11 +282,15 @@ class InheritanceDiagram(Directive):
|
||||
env = self.state.document.settings.env
|
||||
class_names = self.arguments[0].split()
|
||||
class_role = env.get_domain('py').role('class')
|
||||
# Store the original content for use as a hash
|
||||
node['parts'] = self.options.get('parts', 0)
|
||||
node['content'] = ', '.join(class_names)
|
||||
|
||||
# Create a graph starting with the list of classes
|
||||
try:
|
||||
graph = InheritanceGraph(class_names,
|
||||
env.temp_data.get('py:module'))
|
||||
graph = InheritanceGraph(
|
||||
class_names, env.temp_data.get('py:module'),
|
||||
parts=node['parts'])
|
||||
except InheritanceException, err:
|
||||
return [node.document.reporter.warning(err.args[0],
|
||||
line=self.lineno)]
|
||||
@ -300,9 +306,6 @@ class InheritanceDiagram(Directive):
|
||||
# Store the graph object so we can use it to generate the
|
||||
# dot file later
|
||||
node['graph'] = graph
|
||||
# Store the original content for use as a hash
|
||||
node['parts'] = self.options.get('parts', 0)
|
||||
node['content'] = ', '.join(class_names)
|
||||
return [node]
|
||||
|
||||
|
||||
@ -316,7 +319,6 @@ def html_visit_inheritance_diagram(self, node):
|
||||
image map.
|
||||
"""
|
||||
graph = node['graph']
|
||||
parts = node['parts']
|
||||
|
||||
graph_hash = get_graph_hash(node)
|
||||
name = 'inheritance%s' % graph_hash
|
||||
@ -329,7 +331,7 @@ def html_visit_inheritance_diagram(self, node):
|
||||
elif child.get('refid') is not None:
|
||||
urls[child['reftitle']] = '#' + child.get('refid')
|
||||
|
||||
dotcode = graph.generate_dot(name, parts, urls, env=self.builder.env)
|
||||
dotcode = graph.generate_dot(name, urls, env=self.builder.env)
|
||||
render_dot_html(self, node, dotcode, [], 'inheritance', 'inheritance',
|
||||
alt='Inheritance diagram of ' + node['content'])
|
||||
raise nodes.SkipNode
|
||||
@ -340,12 +342,11 @@ def latex_visit_inheritance_diagram(self, node):
|
||||
Output the graph for LaTeX. This will insert a PDF.
|
||||
"""
|
||||
graph = node['graph']
|
||||
parts = node['parts']
|
||||
|
||||
graph_hash = get_graph_hash(node)
|
||||
name = 'inheritance%s' % graph_hash
|
||||
|
||||
dotcode = graph.generate_dot(name, parts, env=self.builder.env,
|
||||
dotcode = graph.generate_dot(name, env=self.builder.env,
|
||||
graph_attrs={'size': '"6.0,6.0"'})
|
||||
render_dot_latex(self, node, dotcode, [], 'inheritance')
|
||||
raise nodes.SkipNode
|
||||
|
Loading…
Reference in New Issue
Block a user