Fix bug 638 - "Documentation for phonon doesn't show class inheritance diagrams."

This commit is contained in:
Hugo Parente Lima 2011-02-10 18:38:46 -02:00
commit dae317b831
3 changed files with 63 additions and 65 deletions

View file

@ -17,6 +17,7 @@ import sys, os
# add these directories to sys.path here. If the directory is relative to the # add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here. # documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.append('@CMAKE_CURRENT_SOURCE_DIR@') sys.path.append('@CMAKE_CURRENT_SOURCE_DIR@')
sys.path.append('@pyside_BINARY_DIR@')
# -- General configuration ----------------------------------------------------- # -- General configuration -----------------------------------------------------

View file

@ -1,4 +1,5 @@
""" # -*- coding: utf-8 -*-
r"""
sphinx.ext.inheritance_diagram sphinx.ext.inheritance_diagram
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -31,7 +32,8 @@
The graph is inserted as a PNG+image map into HTML and a PDF in The graph is inserted as a PNG+image map into HTML and a PDF in
LaTeX. LaTeX.
:copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS. :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
:copyright: Copyright 2010-2011 by the PySide team.
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
@ -39,7 +41,6 @@ import os
import re import re
import sys import sys
import inspect import inspect
import subprocess
try: try:
from hashlib import md5 from hashlib import md5
except ImportError: except ImportError:
@ -48,7 +49,6 @@ except ImportError:
from docutils import nodes from docutils import nodes
from docutils.parsers.rst import directives from docutils.parsers.rst import directives
from sphinx.roles import xfileref_role
from sphinx.ext.graphviz import render_dot_html, render_dot_latex from sphinx.ext.graphviz import render_dot_html, render_dot_latex
from sphinx.util.compat import Directive from sphinx.util.compat import Directive
@ -68,7 +68,7 @@ class InheritanceGraph(object):
from all the way to the root "object", and then is able to generate a from all the way to the root "object", and then is able to generate a
graphviz dot graph from them. 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. *class_names* is a list of child classes to show bases from.
@ -76,12 +76,11 @@ class InheritanceGraph(object):
in the graph. in the graph.
""" """
self.class_names = class_names self.class_names = class_names
self.classes = self._import_classes(class_names, currmodule) classes = self._import_classes(class_names, currmodule)
self.all_classes = self._all_classes(self.classes) self.class_info = self._class_info(classes, show_builtins, parts)
if len(self.all_classes) == 0: if not self.class_info:
raise InheritanceException('No classes found for ' raise InheritanceException('No classes found for '
'inheritance diagram') 'inheritance diagram')
self.show_builtins = show_builtins
def _import_class_or_module(self, name, currmodule): def _import_class_or_module(self, name, currmodule):
""" """
@ -89,7 +88,7 @@ class InheritanceGraph(object):
""" """
try: try:
path, base = class_sig_re.match(name).groups() path, base = class_sig_re.match(name).groups()
except ValueError: except (AttributeError, ValueError):
raise InheritanceException('Invalid class or module %r specified ' raise InheritanceException('Invalid class or module %r specified '
'for inheritance diagram' % name) 'for inheritance diagram' % name)
@ -98,25 +97,18 @@ class InheritanceGraph(object):
# two possibilities: either it is a module, then import it # two possibilities: either it is a module, then import it
try: try:
module = __import__(fullname) __import__(fullname)
todoc = sys.modules[fullname] todoc = sys.modules[fullname]
except ImportError: except ImportError:
# else it is a class, then import the module
if not path:
if currmodule:
# try the current module
path = currmodule
else:
raise InheritanceException(
'Could not import class %r specified for '
'inheritance diagram' % base)
try: try:
module = __import__(path) __import__(currmodule)
todoc = getattr(sys.modules[path], base) todoc = sys.modules[currmodule]
for attr in name.split('.'):
todoc = getattr(todoc, attr)
except (ImportError, AttributeError): except (ImportError, AttributeError):
raise InheritanceException( raise InheritanceException(
'Could not import class or module %r specified for ' 'Could not import class or module %r specified for '
'inheritance diagram' % (path + '.' + base)) 'inheritance diagram' % (currmodule + '.' + name))
# If a class, just return it # If a class, just return it
if inspect.isclass(todoc): if inspect.isclass(todoc):
@ -131,36 +123,50 @@ class InheritanceGraph(object):
'not a class or module' % name) 'not a class or module' % name)
def _import_classes(self, class_names, currmodule): def _import_classes(self, class_names, currmodule):
""" """Import a list of classes."""
Import a list of classes.
"""
classes = [] classes = []
for name in class_names: for name in class_names:
classes.extend(self._import_class_or_module(name, currmodule)) classes.extend(self._import_class_or_module(name, currmodule))
return classes return classes
def _all_classes(self, classes): def _class_info(self, classes, show_builtins, parts):
""" """Return name and bases for all classes that are ancestors of
Return a list of all classes that are ancestors of *classes*. *classes*.
*parts* gives the number of dotted name parts that is removed from the
displayed node names.
""" """
all_classes = {} all_classes = {}
builtins = __builtins__.values()
def recurse(cls): def recurse(cls):
all_classes[cls] = None if not show_builtins and cls in builtins:
for c in cls.__bases__: return
if c not in all_classes and not (c.__name__ == "Object" and c.__module__ == "Shiboken"):
recurse(c) 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:
continue
if base.__name__ == "Object" and base.__module__ == "Shiboken":
continue
baselist.append(self.class_name(base, parts))
if base not in all_classes:
recurse(base)
for cls in classes: for cls in classes:
recurse(cls) recurse(cls)
return all_classes.keys() return all_classes.values()
def class_name(self, cls, parts=0): def class_name(self, cls, parts=0):
""" """Given a class object, return a fully-qualified name.
Given a class object, return a fully-qualified name. This
works for things I've tested in matplotlib so far, but may not This works for things I've tested in matplotlib so far, but may not be
be completely general. completely general.
""" """
module = cls.__module__ module = cls.__module__
if module == '__builtin__': if module == '__builtin__':
@ -176,7 +182,7 @@ class InheritanceGraph(object):
""" """
Get all of the class names involved in the graph. 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 # These are the default attrs for graphviz
default_graph_attrs = { default_graph_attrs = {
@ -202,7 +208,7 @@ class InheritanceGraph(object):
def _format_graph_attrs(self, attrs): def _format_graph_attrs(self, attrs):
return ''.join(['%s=%s;\n' % x for x in attrs.items()]) 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={}): graph_attrs={}, node_attrs={}, edge_attrs={}):
""" """
Generate a graphviz dot graph from the classes that Generate a graphviz dot graph from the classes that
@ -230,28 +236,17 @@ class InheritanceGraph(object):
res.append('digraph %s {\n' % name) res.append('digraph %s {\n' % name)
res.append(self._format_graph_attrs(g_attrs)) res.append(self._format_graph_attrs(g_attrs))
for cls in self.all_classes: for name, fullname, bases in self.class_info:
if not self.show_builtins and cls in __builtins__.values():
continue
name = self.class_name(cls, parts)
# Write the node # Write the node
this_node_attrs = n_attrs.copy() this_node_attrs = n_attrs.copy()
url = urls.get(self.class_name(cls)) url = urls.get(fullname)
if url is not None: if url is not None:
this_node_attrs['URL'] = '"%s"' % url this_node_attrs['URL'] = '"%s"' % url
res.append(' "%s" [%s];\n' % res.append(' "%s" [%s];\n' %
(name, self._format_node_attrs(this_node_attrs))) (name, self._format_node_attrs(this_node_attrs)))
# Write the edges # Write the edges
for base in cls.__bases__: for base_name in bases:
if base.__name__ == "Object" and base.__module__ == "Shiboken":
continue
if not self.show_builtins and base in __builtins__.values():
continue
base_name = self.class_name(base, parts)
res.append(' "%s" -> "%s" [%s];\n' % res.append(' "%s" -> "%s" [%s];\n' %
(base_name, name, (base_name, name,
self._format_node_attrs(e_attrs))) self._format_node_attrs(e_attrs)))
@ -283,10 +278,16 @@ class InheritanceDiagram(Directive):
node.document = self.state.document node.document = self.state.document
env = self.state.document.settings.env env = self.state.document.settings.env
class_names = self.arguments[0].split() 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 # Create a graph starting with the list of classes
try: try:
graph = InheritanceGraph(class_names, env.currmodule) graph = InheritanceGraph(
class_names, env.temp_data.get('py:module'),
parts=node['parts'])
except InheritanceException, err: except InheritanceException, err:
return [node.document.reporter.warning(err.args[0], return [node.document.reporter.warning(err.args[0],
line=self.lineno)] line=self.lineno)]
@ -296,15 +297,12 @@ class InheritanceDiagram(Directive):
# references to real URLs later. These nodes will eventually be # references to real URLs later. These nodes will eventually be
# removed from the doctree after we're done with them. # removed from the doctree after we're done with them.
for name in graph.get_all_class_names(): for name in graph.get_all_class_names():
refnodes, x = xfileref_role( refnodes, x = class_role(
'class', ':class:`%s`' % name, name, 0, self.state) 'class', ':class:`%s`' % name, name, 0, self.state)
node.extend(refnodes) node.extend(refnodes)
# Store the graph object so we can use it to generate the # Store the graph object so we can use it to generate the
# dot file later # dot file later
node['graph'] = graph 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] return [node]
@ -318,7 +316,6 @@ def html_visit_inheritance_diagram(self, node):
image map. image map.
""" """
graph = node['graph'] graph = node['graph']
parts = node['parts']
graph_hash = get_graph_hash(node) graph_hash = get_graph_hash(node)
name = 'inheritance%s' % graph_hash name = 'inheritance%s' % graph_hash
@ -331,7 +328,7 @@ def html_visit_inheritance_diagram(self, node):
elif child.get('refid') is not None: elif child.get('refid') is not None:
urls[child['reftitle']] = '#' + child.get('refid') 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', render_dot_html(self, node, dotcode, [], 'inheritance', 'inheritance',
alt='Inheritance diagram of ' + node['content']) alt='Inheritance diagram of ' + node['content'])
raise nodes.SkipNode raise nodes.SkipNode
@ -342,12 +339,11 @@ def latex_visit_inheritance_diagram(self, node):
Output the graph for LaTeX. This will insert a PDF. Output the graph for LaTeX. This will insert a PDF.
""" """
graph = node['graph'] graph = node['graph']
parts = node['parts']
graph_hash = get_graph_hash(node) graph_hash = get_graph_hash(node)
name = 'inheritance%s' % graph_hash 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"'}) graph_attrs={'size': '"6.0,6.0"'})
render_dot_latex(self, node, dotcode, [], 'inheritance') render_dot_latex(self, node, dotcode, [], 'inheritance')
raise nodes.SkipNode raise nodes.SkipNode
@ -363,7 +359,8 @@ def setup(app):
inheritance_diagram, inheritance_diagram,
latex=(latex_visit_inheritance_diagram, None), latex=(latex_visit_inheritance_diagram, None),
html=(html_visit_inheritance_diagram, None), html=(html_visit_inheritance_diagram, None),
text=(skip, None)) text=(skip, None),
man=(skip, None))
app.add_directive('inheritance-diagram', InheritanceDiagram) app.add_directive('inheritance-diagram', InheritanceDiagram)
app.add_config_value('inheritance_graph_attrs', {}, False), app.add_config_value('inheritance_graph_attrs', {}, False),
app.add_config_value('inheritance_node_attrs', {}, False), app.add_config_value('inheritance_node_attrs', {}, False),

View file

@ -15,7 +15,7 @@ class Bug617(unittest.TestCase):
def testOutOfBounds(self): def testOutOfBounds(self):
e = MyEvent() e = MyEvent()
self.assertEqual(repr(e.type()), '<enum-item Type.#out of bounds# (999)>') self.assertEqual(repr(e.type()), '<enum-item PySide.QtCore.QEvent.Type.#out of bounds# (999)>')
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()