Thread: [Epydoc-commits] SF.net SVN: epydoc: [1173] trunk/epydoc/src/epydoc/docwriter/dotgraph.py
Brought to you by:
edloper
From: <ed...@us...> - 2006-04-05 19:47:42
|
Revision: 1173 Author: edloper Date: 2006-04-05 12:47:34 -0700 (Wed, 05 Apr 2006) ViewCVS: http://svn.sourceforge.net/epydoc/?rev=1173&view=rev Log Message: ----------- - Better unicode support for dotgraphs (encode on write, and decode cmapx files on read) Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/dotgraph.py Modified: trunk/epydoc/src/epydoc/docwriter/dotgraph.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2006-04-05 17:19:53 UTC (rev 1172) +++ trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2006-04-05 19:47:34 UTC (rev 1173) @@ -214,8 +214,12 @@ try: result, err = run_subprocess([DOT_COMMAND, '-T%s' % language], self.to_dotfile()) + # Decode into unicode, if necessary. + if language == 'cmapx' and result is not None: + result = result.decode('utf-8') except OSError, e: log.warning("Unable to render Graphviz dot graph:\n%s" % e) + log.debug(self.to_dotfile()) return None return result @@ -239,8 +243,10 @@ for edge in self.edges: lines.append(edge.to_dotfile()) lines.append('}') - return '\n'.join(lines) + # Default dot input encoding is UTF-8 + return u'\n'.join(lines).encode('utf-8') + class DotGraphNode: _next_id = 0 def __init__(self, label=None, **attribs): @@ -357,8 +363,7 @@ def import_graph(modules, docindex, linker, context=None, **options): graph = DotGraph('Import Graph', - node_defaults={'shape':'box', 'width': 0, 'height': 0}, - edge_defaults={'sametail':True}) + node_defaults={'shape':'box', 'width': 0, 'height': 0}) # Options if options.get('dir', 'RL') != 'TB': # default: right-to-left. @@ -429,8 +434,7 @@ func_set.update(docindex.callees.get(func_doc, ())) graph = DotGraph('Call Graph for %s' % name_list(api_docs, context), - node_defaults={'shape':'box', 'width': 0, 'height': 0}, - edge_defaults={'sametail':True}) + node_defaults={'shape':'box', 'width': 0, 'height': 0}) # Options if options.get('dir', 'LR') != 'TB': # default: left-to-right This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2006-04-05 22:05:37
|
Revision: 1175 Author: edloper Date: 2006-04-05 15:05:32 -0700 (Wed, 05 Apr 2006) ViewCVS: http://svn.sourceforge.net/epydoc/?rev=1175&view=rev Log Message: ----------- - Added get_dot_version function, which tries to determine the available version of dot (so we can decide which features are safeto use) - Added specialize_valdoc_node(), which takes care of displaying different subclasses of APIDoc with different styles. Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/dotgraph.py Modified: trunk/epydoc/src/epydoc/docwriter/dotgraph.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2006-04-05 22:02:43 UTC (rev 1174) +++ trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2006-04-05 22:05:32 UTC (rev 1175) @@ -24,7 +24,7 @@ import sys from epydoc import log from epydoc.apidoc import * -from epydoc.util import plaintext_to_html, run_subprocess +from epydoc.util import * from epydoc.compat import * # Backwards compatibility ###################################################################### @@ -219,7 +219,7 @@ result = result.decode('utf-8') except OSError, e: log.warning("Unable to render Graphviz dot graph:\n%s" % e) - log.debug(self.to_dotfile()) + #log.debug(self.to_dotfile()) return None return result @@ -249,19 +249,34 @@ class DotGraphNode: _next_id = 0 - def __init__(self, label=None, **attribs): + def __init__(self, label=None, html_label=None, **attribs): + if label is not None and html_label is not None: + raise ValueError('Use label or html_label, not both.') if label is not None: attribs['label'] = label - self.attribs = attribs + self._html_label = html_label + self._attribs = attribs self.id = self.__class__._next_id self.__class__._next_id += 1 + def __getitem__(self, attr): + return self._attribs[attr] + + def __setitem__(self, attr, val): + if attr == 'html_label': + self._attribs.pop('label') + self._html_label = val + else: + if attr == 'label': self._html_label = None + self._attribs[attr] = val + def to_dotfile(self): """ Return the dot commands that should be used to render this node. """ - attribs = ','.join(['%s="%s"' % (k,v) for (k,v) - in self.attribs.items()]) - if attribs: attribs = ' [%s]' % attribs + attribs = ['%s="%s"' % (k,v) for (k,v) in self._attribs.items()] + if self._html_label: + attribs.insert(0, 'label=<%s>' % self._html_label) + if attribs: attribs = ' [%s]' % (','.join(attribs)) return 'node%d%s' % (self.id, attribs) class DotGraphEdge: @@ -273,14 +288,20 @@ if label is not None: attribs['label'] = label self.start = start #: :type: `DotGraphNode` self.end = end #: :type: `DotGraphNode` - self.attribs = attribs + self._attribs = attribs + def __getitem__(self, attr): + return self._attribs[attr] + + def __setitem__(self, attr, val): + self._attribs[attr] = val + def to_dotfile(self): """ Return the dot commands that should be used to render this edge. """ attribs = ','.join(['%s="%s"' % (k,v) for (k,v) - in self.attribs.items()]) + in self._attribs.items()]) if attribs: attribs = ' [%s]' % attribs return 'node%d -> node%d%s' % (self.start.id, self.end.id, attribs) @@ -294,7 +315,6 @@ hierarchies for the given packages. """ graph = DotGraph('Package Tree for %s' % name_list(packages, context), - node_defaults={'shape':'box', 'width': 0, 'height': 0}, edge_defaults={'sametail':True}) # Options @@ -362,8 +382,7 @@ return graph def import_graph(modules, docindex, linker, context=None, **options): - graph = DotGraph('Import Graph', - node_defaults={'shape':'box', 'width': 0, 'height': 0}) + graph = DotGraph('Import Graph') # Options if options.get('dir', 'RL') != 'TB': # default: right-to-left. @@ -456,6 +475,28 @@ return graph ###################################################################### +#{ Dot Version +###################################################################### + +_dot_version = None +_DOT_VERSION_RE = re.compile(r'dot version ([\d\.]+)') +def get_dot_version(): + global _dot_version + if _dot_version is None: + try: + out, err = run_subprocess([DOT_COMMAND, '-V']) + version_info = err or out + m = _DOT_VERSION_RE.match(version_info) + if m: + _dot_version = [int(x) for x in m.group(1).split('.')] + else: + _dot_version = (0,) + except RunSubprocessError, e: + _dot_version = (0,) + log.info('Detected dot version %s' % _dot_version) + return _dot_version + +###################################################################### #{ Helper Functions ###################################################################### @@ -467,17 +508,55 @@ val_docs = sorted(val_docs, key=lambda d:d.canonical_name) for i, val_doc in enumerate(val_docs): label = val_doc.canonical_name.contextualize(context.canonical_name) - if isinstance(val_doc, RoutineDoc): label = '%s()' % label node = nodes[val_doc] = DotGraphNode(label) graph.nodes.append(node) - if val_doc == context: - node.attribs.update({'fillcolor':'black', 'fontcolor':'white', - 'style':'filled'}) - else: - url = linker.url_for(val_doc) - if url: node.attribs['href'] = url + specialize_valdoc_node(node, val_doc, context, linker.url_for(val_doc)) return nodes +MODULE_NODE_HTML = ''' + <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="0"> + <TR><TD BORDER="0"></TD><TD BORDER="0"></TD> + <TD HEIGHT="8" BGCOLOR="%s"></TD></TR> + <TR><TD COLSPAN="3" BGCOLOR="%s">%s</TD></TR></TABLE>'''.strip() +MODULE_BG = '#d0e0ff' +SELECTED_BG = '#ffd0d0' + +def specialize_valdoc_node(node, val_doc, context, url): + """ + Update the style attributes of C{node} to reflext its type + and context. + """ + dot_version = get_dot_version() + if val_doc != context: + if url: node['href'] = url + + if isinstance(val_doc, VariableDoc) and val_doc.value is not UNKNOWN: + val_doc = val_doc.value + + if isinstance(val_doc, ModuleDoc) and dot_version > [2]: + node['shape'] = 'plaintext' + if val_doc == context: color = SELECTED_BG + else: color = MODULE_BG + node['html_label'] = MODULE_NODE_HTML % (color, color, node['label']) + + elif isinstance(val_doc, RoutineDoc): + node['shape'] = 'box' + node['style'] = 'rounded' + node['width'] = 0 + node['height'] = 0 + node['label'] = '%s()' % node['label'] + if val_doc == context: + node['fillcolor'] = SELECTED_BG + node['style'] = 'filled,rounded' + + else: + node['shape'] = 'box' + node['width'] = 0 + node['height'] = 0 + if val_doc == context: + node['fillcolor'] = SELECTED_BG + node['style'] = 'filled' + def name_list(api_docs, context=None): if context is not None: context = context.canonical_name This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2006-04-07 01:05:22
|
Revision: 1183 Author: edloper Date: 2006-04-06 18:05:18 -0700 (Thu, 06 Apr 2006) ViewCVS: http://svn.sourceforge.net/epydoc/?rev=1183&view=rev Log Message: ----------- - Docstring fixes - Set tooltips on nodes for ValueDocs Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/dotgraph.py Modified: trunk/epydoc/src/epydoc/docwriter/dotgraph.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2006-04-07 01:04:42 UTC (rev 1182) +++ trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2006-04-07 01:05:18 UTC (rev 1183) @@ -207,7 +207,7 @@ def render(self, language='gif'): """ - Use the `dot` command to render this graph, using the output + Use the ``dot`` command to render this graph, using the output format `language`. Return the result as a string, or `None` if the rendering failed. """ @@ -408,13 +408,13 @@ def call_graph(api_docs, docindex, linker, context=None, **options): """ - @param options: - - C{dir}: rankdir for the graph. (default=LR) - - C{add_callers}: also include callers for any of the - routines in C{api_docs}. (default=False) - - C{add_callees}: also include callees for any of the - routines in C{api_docs}. (default=False) - @todo: Add an C{exclude} option? + :param options: + - `dir`: rankdir for the graph. (default=LR) + - `add_callers`: also include callers for any of the + routines in `api_docs`. (default=False) + - `add_callees`: also include callees for any of the + routines in `api_docs`. (default=False) + :todo: Add an `exclude` option? """ if docindex.callers is None: log.warning("No profiling information for call graph!") @@ -523,7 +523,7 @@ def specialize_valdoc_node(node, val_doc, context, url): """ - Update the style attributes of C{node} to reflext its type + Update the style attributes of `node` to reflext its type and context. """ dot_version = get_dot_version() @@ -537,7 +537,9 @@ node['shape'] = 'plaintext' if val_doc == context: color = SELECTED_BG else: color = MODULE_BG + node['tooltip'] = node['label'] node['html_label'] = MODULE_NODE_HTML % (color, color, node['label']) + node['width'] = node['height'] = 0 elif isinstance(val_doc, RoutineDoc): node['shape'] = 'box' @@ -545,6 +547,7 @@ node['width'] = 0 node['height'] = 0 node['label'] = '%s()' % node['label'] + node['tooltip'] = node['label'] if val_doc == context: node['fillcolor'] = SELECTED_BG node['style'] = 'filled,rounded' @@ -553,6 +556,7 @@ node['shape'] = 'box' node['width'] = 0 node['height'] = 0 + node['tooltip'] = node['label'] if val_doc == context: node['fillcolor'] = SELECTED_BG node['style'] = 'filled' This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2006-04-07 23:38:54
|
Revision: 1190 Author: edloper Date: 2006-04-07 16:38:47 -0700 (Fri, 07 Apr 2006) ViewCVS: http://svn.sourceforge.net/epydoc/?rev=1190&view=rev Log Message: ----------- - Added 'center' option to DotGraph.to_html() - If graphviz dot prints to stderr, generate a warning with that output. - DotGraphNode's can now define a 'port' variable; edges connecting to it will connect to that port, unless otherwise specified. - Added 'uml' style for package_tree: draws the package as a nested set of uml package symbols. - Decrased minimum inter-node spacing in import graph - selected elements now use bold outlines (for some reason, filled,rounded style isn't working with my version of dot) Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/dotgraph.py Modified: trunk/epydoc/src/epydoc/docwriter/dotgraph.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2006-04-07 23:35:25 UTC (rev 1189) +++ trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2006-04-07 23:38:47 UTC (rev 1190) @@ -116,7 +116,7 @@ self.uid = '%s_%s' % (self.uid, n) self._uids.add(self.uid) - def to_html(self, image_url): + def to_html(self, image_url, center=True): """ Return the HTML code that should be uesd to display this graph (including a client-side image map). @@ -138,12 +138,13 @@ title_align = 'center' table_width = '' - s = '<center>' + if center: s = '<center>' if title or caption: - s += ('<table border="0" cellpadding="0" cellspacing="0" ' - '%s>\n <tr><td align="center">\n') % table_width + s += ('<p><table border="0" cellpadding="0" cellspacing="0" ' + 'class="graph"%s>\n <tr><td align="center">\n' % + table_width) s += (' %s\n <img src="%s" alt=%r usemap="#%s" ' - 'ismap="ismap" class=%s">\n' % + 'ismap="ismap" class="%s">\n' % (cmapx.strip(), image_url, title, self.uid, css_class)) if title or caption: s += ' </td></tr>\n <tr><td align=%r>\n' % title_align @@ -153,8 +154,8 @@ s += ' -- ' if caption: s += '<span class="graph-caption">%s</span>' % caption - s += '\n </th></tr>\n</table>' - s += '</center>' + s += '\n </th></tr>\n</table></p>' + if center: s += '</center>' return s def link(self, docstring_linker): @@ -217,6 +218,8 @@ # Decode into unicode, if necessary. if language == 'cmapx' and result is not None: result = result.decode('utf-8') + if err: + log.warning("Graphviz dot warning(s):\n%s" % err) except OSError, e: log.warning("Unable to render Graphviz dot graph:\n%s" % e) #log.debug(self.to_dotfile()) @@ -257,6 +260,7 @@ self._attribs = attribs self.id = self.__class__._next_id self.__class__._next_id += 1 + self.port = None def __getitem__(self, attr): return self._attribs[attr] @@ -275,7 +279,7 @@ """ attribs = ['%s="%s"' % (k,v) for (k,v) in self._attribs.items()] if self._html_label: - attribs.insert(0, 'label=<%s>' % self._html_label) + attribs.insert(0, 'label=<%s>' % (self._html_label,)) if attribs: attribs = ' [%s]' % (','.join(attribs)) return 'node%d%s' % (self.id, attribs) @@ -300,9 +304,16 @@ """ Return the dot commands that should be used to render this edge. """ - attribs = ','.join(['%s="%s"' % (k,v) for (k,v) - in self._attribs.items()]) + # Set head & tail ports, if the nodes have preferred ports. + attribs = self._attribs.copy() + if (self.start.port is not None and 'headport' not in attribs): + attribs['headport'] = self.start.port + if (self.end.port is not None and 'tailport' not in attribs): + attribs['tailport'] = self.end.port + # Convert attribs to a string + attribs = ','.join(['%s="%s"' % (k,v) for (k,v) in attribs.items()]) if attribs: attribs = ' [%s]' % attribs + # Return the dotfile edge. return 'node%d -> node%d%s' % (self.start.id, self.end.id, attribs) ###################################################################### @@ -314,12 +325,20 @@ Return a `DotGraph` that graphically displays the package hierarchies for the given packages. """ + if options.get('style', 'uml') == 'uml': # default to uml style? + if get_dot_version() >= [2]: + return _nested_package_uml_graph(packages, linker, context, + **options) + elif 'style' in options: + log.warning('UML style package trees require dot version 2.0+') + graph = DotGraph('Package Tree for %s' % name_list(packages, context), - edge_defaults={'sametail':True}) + body='ranksep=.3\n;nodesep=.1\n', + edge_defaults={'dir':'none'}) # Options - if options.get('dir', 'LR') != 'TB': # default: left-to-right - graph.body += 'rankdir=%s\n' % options.get('dir', 'LR') + if options.get('dir', 'TB') != 'TB': # default: top-to-bottom + graph.body += 'rankdir=%s\n' % options.get('dir', 'TB') # Get a list of all modules in the package. queue = list(packages) @@ -334,11 +353,111 @@ # Add an edge for each package/submodule relationship. for module in modules: for submodule in module.submodules: - graph.edges.append(DotGraphEdge(nodes[module], nodes[submodule])) + graph.edges.append(DotGraphEdge(nodes[module], nodes[submodule], + headport='tab')) return graph +def _nested_package_uml_graph(packages, linker, context=None, **options): + """ + Return a `DotGraph` that graphically displays the package + hierarchies for the given packages as a nested set of UML + symbols. + """ + graph = DotGraph('Package Tree for %s' % name_list(packages, context)) + # Remove any packages whose containers are also in the list. + root_packages = [] + for package1 in packages: + for package2 in packages: + if (package1 is not package2 and + package2.canonical_name.dominates(package1.canonical_name)): + break + else: + root_packages.append(package1) + # If the context is a variable, then get its value. + if isinstance(context, VariableDoc) and context.value is not UNKNOWN: + context = context.value + # Build one (complex) node for each root package. + for package in root_packages: + html_label, _, _ = _nested_uml_package_label(package, linker, context) + node = DotGraphNode(html_label=html_label, shape='plaintext', + url=linker.url_for(package), + tooltip=package.canonical_name) + graph.nodes.append(node) + return graph +def _nested_uml_package_label(package, linker, context): + """ + :Return: (label, depth, width) where: + + - `label` is the HTML label + - `depth` is the depth of the package tree + - `width` is the max width of the HTML label, roughly in + units of characters. + + :todo: Add hrefs/tooltips to appropriate <td> or <table> cells. + """ + MAX_ROW_WIDTH = 80 # unit is roughly characters. + pkg_name = package.canonical_name + pkg_url = linker.url_for(package) or NOOP_URL + + if not package.is_package or len(package.submodules) == 0: + pkg_color = _nested_uml_package_color(package, context, 1) + label = MODULE_NODE_HTML % (pkg_color, pkg_color, pkg_url, + pkg_name, pkg_name[-1]) + return (label, 1, len(pkg_name[-1])+3) + + submodule_labels = [_nested_uml_package_label(submodule, linker, context) + for submodule in package.submodules] + + ROW_HDR = '<TABLE BORDER="0" CELLBORDER="0"><TR>' + # Build the body of the package's icon. + body = '<TABLE BORDER="0" CELLBORDER="0">' + body += '<TR><TD ALIGN="LEFT">%s</TD></TR>' % pkg_name[-1] + body += '<TR><TD>%s' % ROW_HDR + row_width = [0] + for i, (label, depth, width) in enumerate(submodule_labels): + if row_width[-1] > 0 and width+row_width[-1] > MAX_ROW_WIDTH: + body += '</TR></TABLE></TD></TR>' + body += '<TR><TD>%s' % ROW_HDR + row_width.append(0) + #submodule_url = linker.url_for(package.submodules[i]) or '#' + #submodule_name = package.submodules[i].canonical_name + body += '<TD ALIGN="LEFT">%s</TD>' % label + row_width [-1] += width + body += '</TR></TABLE></TD></TR></TABLE>' + + # Put together our return value. + depth = max([d for (l,d,w) in submodule_labels])+1 + pkg_color = _nested_uml_package_color(package, context, depth) + label = ('<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0"><TR>' + '<TD ALIGN="LEFT" HEIGHT="8" WIDTH="16" FIXEDSIZE="true" ' + 'BORDER="1" VALIGN="BOTTOM" BGCOLOR="%s"></TD></TR><TR>' + '<TD COLSPAN="5" VALIGN="TOP" ALIGN="LEFT" BORDER="1" ' + 'BGCOLOR="%s" HREF="%s" TOOLTIP="%s">%s</TD></TR></TABLE>' % + (pkg_color, pkg_color, pkg_url, pkg_name, body)) + width = max(max(row_width), len(pkg_name[-1])+3) + return label, depth, width + +def _nested_uml_package_color(package, context, depth): + if package == context: return SELECTED_BG + else: + # Parse the base color. + if re.match(MODULE_BG, 'r#[0-9a-fA-F]{6}$'): + base = int(MODULE_BG[1:], 16) + else: + base = int('d8e8ff', 16) + red = (base & 0xff0000) >> 16 + green = (base & 0x00ff00) >> 8 + blue = (base & 0x0000ff) + # Make it darker with each level of depth. + red = max(0, red-(depth-1)*10) + green = max(0, green-(depth-1)*10) + blue = max(0, blue-(depth-1)*10) + # Convert it back to a color string + return '#%06x' % ((red<<16)+(green<<8)+blue) + +###################################################################### def class_tree_graph(bases, linker, context=None, **options): """ Return a `DotGraph` that graphically displays the package @@ -346,7 +465,6 @@ """ graph = DotGraph('Class Hierarchy for %s' % name_list(bases, context), body='ranksep=0.3\n', - node_defaults={'shape':'box', 'width': 0, 'height': 0}, edge_defaults={'sametail':True, 'dir':'none'}) # Options @@ -381,8 +499,9 @@ return graph +###################################################################### def import_graph(modules, docindex, linker, context=None, **options): - graph = DotGraph('Import Graph') + graph = DotGraph('Import Graph', body='ranksep=.3\n;nodesep=.3\n') # Options if options.get('dir', 'RL') != 'TB': # default: right-to-left. @@ -406,6 +525,7 @@ return graph +###################################################################### def call_graph(api_docs, docindex, linker, context=None, **options): """ :param options: @@ -513,12 +633,18 @@ specialize_valdoc_node(node, val_doc, context, linker.url_for(val_doc)) return nodes +NOOP_URL = '#' +NOOP_URL = 'javascript:;' # this option is more evil. + MODULE_NODE_HTML = ''' - <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="0"> - <TR><TD BORDER="0"></TD><TD BORDER="0"></TD> - <TD HEIGHT="8" BGCOLOR="%s"></TD></TR> - <TR><TD COLSPAN="3" BGCOLOR="%s">%s</TD></TR></TABLE>'''.strip() -MODULE_BG = '#d0e0ff' + <TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" + CELLPADDING="0" PORT="table" ALIGN="LEFT"> + <TR><TD ALIGN="LEFT" VALIGN="BOTTOM" HEIGHT="8" WIDTH="16" FIXEDSIZE="true" + BGCOLOR="%s" BORDER="1" PORT="tab"></TD></TR> + <TR><TD ALIGN="LEFT" VALIGN="TOP" BGCOLOR="%s" BORDER="1" + PORT="body" HREF="%s" TOOLTIP="%s">%s</TD></TR> + </TABLE>'''.strip() +MODULE_BG = '#d8e8ff' SELECTED_BG = '#ffd0d0' def specialize_valdoc_node(node, val_doc, context, url): @@ -526,20 +652,29 @@ Update the style attributes of `node` to reflext its type and context. """ + # We can only use html-style nodes if dot_version>2. dot_version = get_dot_version() - if val_doc != context: - if url: node['href'] = url - + + # If val_doc or context is a variable, get its value. if isinstance(val_doc, VariableDoc) and val_doc.value is not UNKNOWN: val_doc = val_doc.value - - if isinstance(val_doc, ModuleDoc) and dot_version > [2]: + if isinstance(context, VariableDoc) and context.value is not UNKNOWN: + context = context.value + + # Set the URL. (Do this even if it points to the page we're + # currently on; otherwise, the tooltip is ignored.) + node['href'] = url or NOOP_URL + + if isinstance(val_doc, ModuleDoc) and dot_version >= [2]: node['shape'] = 'plaintext' if val_doc == context: color = SELECTED_BG else: color = MODULE_BG node['tooltip'] = node['label'] - node['html_label'] = MODULE_NODE_HTML % (color, color, node['label']) + node['html_label'] = MODULE_NODE_HTML % (color, color, url, + val_doc.canonical_name, + node['label']) node['width'] = node['height'] = 0 + node.port = 'body' elif isinstance(val_doc, RoutineDoc): node['shape'] = 'box' @@ -550,7 +685,7 @@ node['tooltip'] = node['label'] if val_doc == context: node['fillcolor'] = SELECTED_BG - node['style'] = 'filled,rounded' + node['style'] = 'filled,rounded,bold' else: node['shape'] = 'box' @@ -559,7 +694,7 @@ node['tooltip'] = node['label'] if val_doc == context: node['fillcolor'] = SELECTED_BG - node['style'] = 'filled' + node['style'] = 'filled,bold' def name_list(api_docs, context=None): if context is not None: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2006-04-09 20:43:12
|
Revision: 1198 Author: edloper Date: 2006-04-09 13:43:09 -0700 (Sun, 09 Apr 2006) ViewCVS: http://svn.sourceforge.net/epydoc/?rev=1198&view=rev Log Message: ----------- - Cleand up DotGraphUmlModuleNode (now used by uml_package_tree_graph) Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/dotgraph.py Modified: trunk/epydoc/src/epydoc/docwriter/dotgraph.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2006-04-09 18:17:52 UTC (rev 1197) +++ trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2006-04-09 20:43:09 UTC (rev 1198) @@ -772,7 +772,6 @@ s += '\n' + edge.to_dotfile() return s -# [XX] Not used yet. class DotGraphUmlModuleNode(DotGraphNode): """ A specialized dot grah node used to display C{ModuleDoc}s using @@ -799,12 +798,40 @@ """ def __init__(self, module_doc, linker, context, collapsed=False, - **options): + excluded_submodules=(), **options): self.module_doc = module_doc self.linker = linker self.context = context + self.collapsed = collapsed + self.options = options + self.excluded_submodules = excluded_submodules + DotGraphNode.__init__(self, shape='plaintext', + href=linker.url_for(module_doc) or NOOP_URL, + tooltip=module_doc.canonical_name) - def _nested_package_label(package): + #: Expects: (color, color, url, tooltip, body) + _MODULE_LABEL = ''' + <TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" ALIGN="LEFT"> + <TR><TD ALIGN="LEFT" VALIGN="BOTTOM" HEIGHT="8" WIDTH="16" + FIXEDSIZE="true" BGCOLOR="%s" BORDER="1" PORT="tab"></TD></TR> + <TR><TD ALIGN="LEFT" VALIGN="TOP" BGCOLOR="%s" BORDER="1" WIDTH="20" + PORT="body" HREF="%s" TOOLTIP="%s">%s</TD></TR> + </TABLE>''' + + #: Expects: (name, body_rows) + _NESTED_BODY = ''' + <TABLE BORDER="0" CELLBORDER="0" CELLPADDING="0" CELLSPACING="0"> + <TR><TD ALIGN="LEFT">%s</TD></TR> + %s + </TABLE>''' + + #: Expects: (cells,) + _NESTED_BODY_ROW = ''' + <TR><TD> + <TABLE BORDER="0" CELLBORDER="0"><TR>%s</TR></TABLE> + </TD></TR>''' + + def _get_html_label(self, package): """ :Return: (label, depth, width) where: @@ -817,46 +844,45 @@ pkg_name = package.canonical_name pkg_url = self.linker.url_for(package) or NOOP_URL - if not package.is_package or len(package.submodules) == 0: - pkg_color = _nested_uml_package_color(package, self.context, 1) - label = MODULE_NODE_HTML % (pkg_color, pkg_color, pkg_url, - pkg_name, pkg_name[-1]) + if (not package.is_package or len(package.submodules) == 0 or + self.collapsed): + pkg_color = self._color(package, 1) + label = self._MODULE_LABEL % (pkg_color, pkg_color, + pkg_url, pkg_name, pkg_name[-1]) return (label, 1, len(pkg_name[-1])+3) - submodule_labels = [self._nested_package_label(submodule, self.linker, - self.context) - for submodule in package.submodules] - - ROW_HDR = '<TABLE BORDER="0" CELLBORDER="0"><TR>' - # Build the body of the package's icon. - body = '<TABLE BORDER="0" CELLBORDER="0">' - body += '<TR><TD ALIGN="LEFT">%s</TD></TR>' % pkg_name[-1] - body += '<TR><TD>%s' % ROW_HDR - row_width = [0] - for i, (label, depth, width) in enumerate(submodule_labels): - if row_width[-1] > 0 and width+row_width[-1] > MAX_ROW_WIDTH: - body += '</TR></TABLE></TD></TR>' - body += '<TR><TD>%s' % ROW_HDR - row_width.append(0) - #submodule_url = self.linker.url_for(package.submodules[i]) or '#' - #submodule_name = package.submodules[i].canonical_name - body += '<TD ALIGN="LEFT">%s</TD>' % label - row_width [-1] += width - body += '</TR></TABLE></TD></TR></TABLE>' - - # Put together our return value. - depth = max([d for (l,d,w) in submodule_labels])+1 - pkg_color = self._color(package, depth) - label = ('<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0"><TR>' - '<TD ALIGN="LEFT" HEIGHT="8" WIDTH="16" FIXEDSIZE="true" ' - 'BORDER="1" VALIGN="BOTTOM" BGCOLOR="%s"></TD></TR><TR>' - '<TD COLSPAN="5" VALIGN="TOP" ALIGN="LEFT" BORDER="1" ' - 'BGCOLOR="%s" HREF="%s" TOOLTIP="%s">%s</TD></TR></TABLE>' % - (pkg_color, pkg_color, pkg_url, pkg_name, body)) - width = max(max(row_width), len(pkg_name[-1])+3) - return label, depth, width - - def _color(package, depth): + # Get the label for each submodule, and divide them into rows. + row_list = [''] + row_width = 0 + max_depth = 0 + max_row_width = len(pkg_name[-1])+3 + for submodule in package.submodules: + if submodule in self.excluded_submodules: continue + # Get the submodule's label. + label, depth, width = self._get_html_label(submodule) + # Check if we should start a new row. + if row_width > 0 and width+row_width > MAX_ROW_WIDTH: + row_list.append('') + row_width = 0 + # Add the submodule's label to the row. + row_width += width + row_list[-1] += '<TD ALIGN="LEFT">%s</TD>' % label + # Update our max's. + max_depth = max(depth, max_depth) + max_row_width = max(row_width, max_row_width) + + # Figure out which color to use. + pkg_color = self._color(package, depth+1) + + # Assemble & return the label. + rows = ''.join([self._NESTED_BODY_ROW % r for r in row_list]) + body = self._NESTED_BODY % (pkg_name, rows) + label = self._MODULE_LABEL % (pkg_color, pkg_color, + pkg_url, pkg_name, body) + return label, max_depth+1, max_row_width + + _COLOR_DIFF = 24 + def _color(self, package, depth): if package == self.context: return SELECTED_BG else: # Parse the base color. @@ -869,12 +895,17 @@ blue = (base & 0x0000ff) # Make it darker with each level of depth. (but not *too* # dark -- package name needs to be readable) - red = max(64, red-(depth-1)*10) - green = max(64, green-(depth-1)*10) - blue = max(64, blue-(depth-1)*10) + red = max(64, red-(depth-1)*self._COLOR_DIFF) + green = max(64, green-(depth-1)*self._COLOR_DIFF) + blue = max(64, blue-(depth-1)*self._COLOR_DIFF) # Convert it back to a color string return '#%06x' % ((red<<16)+(green<<8)+blue) + def to_dotfile(self): + attribs = ['%s="%s"' % (k,v) for (k,v) in self._attribs.items()] + label, depth, width = self._get_html_label(self.module_doc) + attribs.append('label=<%s>' % label) + return 'node%d%s' % (self.id, ' [%s]' % (','.join(attribs))) @@ -889,7 +920,7 @@ """ if options.get('style', 'uml') == 'uml': # default to uml style? if get_dot_version() >= [2]: - return _nested_package_uml_graph(packages, linker, context, + return uml_package_tree_graph(packages, linker, context, **options) elif 'style' in options: log.warning('UML style package trees require dot version 2.0+') @@ -920,7 +951,7 @@ return graph -def _nested_package_uml_graph(packages, linker, context=None, **options): +def uml_package_tree_graph(packages, linker, context=None, **options): """ Return a `DotGraph` that graphically displays the package hierarchies for the given packages as a nested set of UML @@ -939,86 +970,11 @@ # If the context is a variable, then get its value. if isinstance(context, VariableDoc) and context.value is not UNKNOWN: context = context.value - # Build one (complex) node for each root package. + # Return a graph with one node for each root package. for package in root_packages: - html_label, _, _ = _nested_uml_package_label(package, linker, context) - node = DotGraphNode(html_label=html_label, shape='plaintext', - href=linker.url_for(package) or NOOP_URL, - tooltip=package.canonical_name) - graph.nodes.append(node) + graph.nodes.append(DotGraphUmlModuleNode(package, linker, context)) return graph -def _nested_uml_package_label(package, linker, context): - """ - :Return: (label, depth, width) where: - - - `label` is the HTML label - - `depth` is the depth of the package tree - - `width` is the max width of the HTML label, roughly in - units of characters. - - :todo: Add hrefs/tooltips to appropriate <td> or <table> cells. - """ - MAX_ROW_WIDTH = 80 # unit is roughly characters. - pkg_name = package.canonical_name - pkg_url = linker.url_for(package) or NOOP_URL - - if not package.is_package or len(package.submodules) == 0: - pkg_color = _nested_uml_package_color(package, context, 1) - label = MODULE_NODE_HTML % (pkg_color, pkg_color, pkg_url, - pkg_name, pkg_name[-1]) - return (label, 1, len(pkg_name[-1])+3) - - submodule_labels = [_nested_uml_package_label(submodule, linker, context) - for submodule in package.submodules] - - ROW_HDR = '<TABLE BORDER="0" CELLBORDER="0"><TR>' - # Build the body of the package's icon. - body = '<TABLE BORDER="0" CELLBORDER="0">' - body += '<TR><TD ALIGN="LEFT">%s</TD></TR>' % pkg_name[-1] - body += '<TR><TD>%s' % ROW_HDR - row_width = [0] - for i, (label, depth, width) in enumerate(submodule_labels): - if row_width[-1] > 0 and width+row_width[-1] > MAX_ROW_WIDTH: - body += '</TR></TABLE></TD></TR>' - body += '<TR><TD>%s' % ROW_HDR - row_width.append(0) - #submodule_url = linker.url_for(package.submodules[i]) or '#' - #submodule_name = package.submodules[i].canonical_name - body += '<TD ALIGN="LEFT">%s</TD>' % label - row_width [-1] += width - body += '</TR></TABLE></TD></TR></TABLE>' - - # Put together our return value. - depth = max([d for (l,d,w) in submodule_labels])+1 - pkg_color = _nested_uml_package_color(package, context, depth) - label = ('<TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0"><TR>' - '<TD ALIGN="LEFT" HEIGHT="8" WIDTH="16" FIXEDSIZE="true" ' - 'BORDER="1" VALIGN="BOTTOM" BGCOLOR="%s"></TD></TR><TR>' - '<TD COLSPAN="5" VALIGN="TOP" ALIGN="LEFT" BORDER="1" ' - 'BGCOLOR="%s" HREF="%s" TOOLTIP="%s">%s</TD></TR></TABLE>' % - (pkg_color, pkg_color, pkg_url, pkg_name, body)) - width = max(max(row_width), len(pkg_name[-1])+3) - return label, depth, width - -def _nested_uml_package_color(package, context, depth): - if package == context: return SELECTED_BG - else: - # Parse the base color. - if re.match(MODULE_BG, 'r#[0-9a-fA-F]{6}$'): - base = int(MODULE_BG[1:], 16) - else: - base = int('d8e8ff', 16) - red = (base & 0xff0000) >> 16 - green = (base & 0x00ff00) >> 8 - blue = (base & 0x0000ff) - # Make it darker with each level of depth. - red = max(0, red-(depth-1)*10) - green = max(0, green-(depth-1)*10) - blue = max(0, blue-(depth-1)*10) - # Convert it back to a color string - return '#%06x' % ((red<<16)+(green<<8)+blue) - ###################################################################### def class_tree_graph(bases, linker, context=None, **options): """ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2006-04-09 21:17:50
|
Revision: 1204 Author: edloper Date: 2006-04-09 14:17:46 -0700 (Sun, 09 Apr 2006) ViewCVS: http://svn.sourceforge.net/epydoc/?rev=1204&view=rev Log Message: ----------- - Fixed bug in trunation of attribute/operation lists for uml class node Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/dotgraph.py Modified: trunk/epydoc/src/epydoc/docwriter/dotgraph.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2006-04-09 21:17:16 UTC (rev 1203) +++ trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2006-04-09 21:17:46 UTC (rev 1204) @@ -735,17 +735,19 @@ # Construct the attribute list. (If it's too long, truncate) attrib_cells = [self._attribute_cell(a) for a in self.attributes] + max_attributes = self.options.get('max_attributes', 15) if len(attrib_cells) == 0: attrib_cells = ['<TR><TD></TD></TR>'] - elif len(attrib_cells) > self.options.get('max_attributes', 15): + elif len(attrib_cells) > max_attributes: attrib_cells[max_attributes-2:-1] = ['<TR><TD>...</TD></TR>'] attributes = ''.join(attrib_cells) # Construct the operation list. (If it's too long, truncate) oper_cells = [self._operation_cell(a) for a in self.operations] + max_operations = self.options.get('max_operations', 15) if len(oper_cells) == 0: oper_cells = ['<TR><TD></TD></TR>'] - elif len(oper_cells) > self.options.get('max_operations', 15): + elif len(oper_cells) > max_operations: oper_cells[max_operations-2:-1] = ['<TR><TD>...</TD></TR>'] operations = ''.join(oper_cells) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2006-04-09 23:28:33
|
Revision: 1207 Author: edloper Date: 2006-04-09 16:28:28 -0700 (Sun, 09 Apr 2006) ViewCVS: http://svn.sourceforge.net/epydoc/?rev=1207&view=rev Log Message: ----------- - Docstring fixes Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/dotgraph.py Modified: trunk/epydoc/src/epydoc/docwriter/dotgraph.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2006-04-09 23:28:07 UTC (rev 1206) +++ trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2006-04-09 23:28:28 UTC (rev 1207) @@ -338,7 +338,7 @@ class DotGraphUmlClassNode(DotGraphNode): """ - A specialized dot graph node used to display C{ClassDoc}s using + A specialized dot graph node used to display `ClassDoc`\s using UML notation. The node is rendered as a table with three cells: the top cell contains the class name; the middle cell contains a list of attributes; and the bottom cell contains a list of @@ -354,7 +354,7 @@ | ... | +-------------+ - `DotGraphUmlClassNode`s may be *collapsed*, in which case they are + `DotGraphUmlClassNode`\s may be *collapsed*, in which case they are drawn as a simple box containing the class name:: +-------------+ @@ -379,16 +379,16 @@ `class_doc`. :Parameters: - linker: `DocstringLinker<markup.DocstringLinker>` + `linker` : `DocstringLinker<markup.DocstringLinker>` Used to look up URLs for classes. - context: `APIDoc` + `context` : `APIDoc` The context in which this node will be drawn; dotted names will be contextualized to this context. - collapsed: ``bool`` + `collapsed` : ``bool`` If true, then display this node as a simple box. - bgcolor: ``str`` + `bgcolor` : ``str`` The background color for this node. - options: ``dict`` + `options` : ``dict`` A set of options used to control how the node should be displayed. @@ -512,8 +512,8 @@ by `DotGraphUmlClassNode`; they should *not* be added directly to the `DotGraph`. - :param nodes: A dictionary mapping from `ClassDoc`s to - `DotGraphUmlClassNode`s, used to look up the nodes for + :param nodes: A dictionary mapping from `ClassDoc`\s to + `DotGraphUmlClassNode`\s, used to look up the nodes for attribute types. If the ``add_nodes_for_linked_attributes`` option is used, then new nodes will be added to this dictionary for any types that are not already listed. @@ -776,7 +776,7 @@ class DotGraphUmlModuleNode(DotGraphNode): """ - A specialized dot grah node used to display C{ModuleDoc}s using + A specialized dot grah node used to display `ModuleDoc`\s using UML notation. Simple module nodes look like:: .----. @@ -1024,6 +1024,7 @@ """ Return a `DotGraph` that graphically displays the class hierarchy for the given class, using UML notation. Options: + - max_attributes - max_operations - show_private_vars This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2006-04-12 19:50:18
|
Revision: 1222 Author: edloper Date: 2006-04-12 12:50:15 -0700 (Wed, 12 Apr 2006) ViewCVS: http://svn.sourceforge.net/epydoc/?rev=1222&view=rev Log Message: ----------- - Fixed base tree list in the case where a base is not a ClassDoc Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/dotgraph.py Modified: trunk/epydoc/src/epydoc/docwriter/dotgraph.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2006-04-12 19:49:51 UTC (rev 1221) +++ trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2006-04-12 19:50:15 UTC (rev 1222) @@ -1002,14 +1002,16 @@ classes = set(bases) queue = list(bases) for cls in queue: - if cls.subclasses not in (None, UNKNOWN): - queue.extend(cls.subclasses) - classes.update(cls.subclasses) + if isinstance(cls, ClassDoc): + if cls.subclasses not in (None, UNKNOWN): + queue.extend(cls.subclasses) + classes.update(cls.subclasses) queue = list(bases) for cls in queue: - if cls.bases not in (None, UNKNOWN): - queue.extend(cls.bases) - classes.update(cls.bases) + if isinstance(cls, ClassDoc): + if cls.bases not in (None, UNKNOWN): + queue.extend(cls.bases) + classes.update(cls.bases) # Add a node for each cls. classes = [d for d in classes if isinstance(d, ClassDoc) @@ -1052,7 +1054,8 @@ # Create nodes for all class_doc's subclasses. queue = [class_doc] for cls in queue: - if cls.subclasses not in (None, UNKNOWN): + if (isinstance(cls, ClassDoc) and + cls.subclasses not in (None, UNKNOWN)): queue.extend(cls.subclasses) for cls in cls.subclasses: if cls not in nodes: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2006-08-21 12:14:52
|
Revision: 1274 Author: edloper Date: 2006-08-21 05:14:48 -0700 (Mon, 21 Aug 2006) ViewCVS: http://svn.sourceforge.net/epydoc/?rev=1274&view=rev Log Message: ----------- - fixed sf bug [ 1511214 ] uml error. This involved an exception raised because the uml graph tried to create a class node for non-class objects. Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/dotgraph.py Modified: trunk/epydoc/src/epydoc/docwriter/dotgraph.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2006-08-21 11:49:30 UTC (rev 1273) +++ trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2006-08-21 12:14:48 UTC (rev 1274) @@ -420,6 +420,9 @@ the types of a linked attributes if no node yet exists for that type. """ + if not isinstance(class_doc, ClassDoc): + raise TypeError('Expected a ClassDoc as 1st argument') + self.class_doc = class_doc """The class represented by this node.""" @@ -591,6 +594,9 @@ type_doc = self.linker.docindex.find(type_str, var) if not type_doc: return False + # Make sure the type is a class. + if not isinstance(type_doc, ClassDoc): return False + # Get the type ValueDoc's node. If it doesn't have one (and # add_nodes_for_linked_attributes=True), then create it. type_node = nodes.get(type_doc) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2006-08-29 21:01:50
|
Revision: 1328 Author: edloper Date: 2006-08-29 14:01:43 -0700 (Tue, 29 Aug 2006) ViewCVS: http://svn.sourceforge.net/epydoc/?rev=1328&view=rev Log Message: ----------- Fixed SF bug [ 1546616 ] Unicode escaped characters. The cmapx returned by dot needed to be decoded (using utf-8, since that's what dot uses for all i/o) before being used. Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/dotgraph.py Modified: trunk/epydoc/src/epydoc/docwriter/dotgraph.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2006-08-29 20:32:24 UTC (rev 1327) +++ trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2006-08-29 21:01:43 UTC (rev 1328) @@ -146,7 +146,15 @@ if not self.write(image_file): return '' # failed to render cmapx = self.render('cmapx') or '' - + + # Decode the cmapx (dot uses utf-8) + try: + cmapx = cmapx.decode('utf-8') + except UnicodeDecodeError: + log.debug('%s: unable to decode cmapx from dot; graph will ' + 'not have clickable regions' % image_file) + cmapx = '' + title = plaintext_to_html(self.title or '') caption = plaintext_to_html(self.caption or '') if title or caption: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dva...@us...> - 2007-03-05 00:53:11
|
Revision: 1567 http://svn.sourceforge.net/epydoc/?rev=1567&view=rev Author: dvarrazzo Date: 2007-03-04 16:53:10 -0800 (Sun, 04 Mar 2007) Log Message: ----------- - Fixed docstring (it appeared a definition list). Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/dotgraph.py Modified: trunk/epydoc/src/epydoc/docwriter/dotgraph.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2007-03-05 00:40:54 UTC (rev 1566) +++ trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2007-03-05 00:53:10 UTC (rev 1567) @@ -864,7 +864,7 @@ - ``label`` is the HTML label - ``depth`` is the depth of the package tree (for coloring) - ``width`` is the max width of the HTML label, roughly in - units of characters. + units of characters. """ MAX_ROW_WIDTH = 80 # unit is roughly characters. pkg_name = package.canonical_name This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2007-09-25 18:09:09
|
Revision: 1642 http://epydoc.svn.sourceforge.net/epydoc/?rev=1642&view=rev Author: edloper Date: 2007-09-25 11:09:07 -0700 (Tue, 25 Sep 2007) Log Message: ----------- - Added 'show_private_subclasses' option to class_tree_graph and uml_class_tre_graph. - If class_tree_graph is given a classdoc, instead of a list of classes, it will now just use that single classdoc Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/dotgraph.py Modified: trunk/epydoc/src/epydoc/docwriter/dotgraph.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2007-09-24 22:55:21 UTC (rev 1641) +++ trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2007-09-25 18:09:07 UTC (rev 1642) @@ -1005,8 +1005,14 @@ def class_tree_graph(bases, linker, context=None, **options): """ Return a `DotGraph` that graphically displays the class - hierarchy for the given classes. + hierarchy for the given classes. Options: + + - show_private_subclasses + - dir: LR|RL|BT requests a left-to-right, right-to-left, or + bottom-to- top, drawing. (corresponds to the dot option + 'rankdir' """ + if isinstance(bases, ClassDoc): bases = [bases] graph = DotGraph('Class Hierarchy for %s' % name_list(bases, context), body='ranksep=0.3\n', edge_defaults={'sametail':True, 'dir':'none'}) @@ -1014,6 +1020,7 @@ # Options if options.get('dir', 'TB') != 'TB': # default: top-down graph.body += 'rankdir=%s\n' % options.get('dir', 'TB') + show_private_subclasses = options.get('show_private_subclasses', True) # Find all superclasses & subclasses of the given classes. classes = set(bases) @@ -1021,8 +1028,13 @@ for cls in queue: if isinstance(cls, ClassDoc): if cls.subclasses not in (None, UNKNOWN): - queue.extend(cls.subclasses) - classes.update(cls.subclasses) + if show_private_subclasses: + subclasses = cls.subclasses + else: + subclasses = [c for c in cls.subclasses + if not c.canonical_name[-1].startswith('_')] + queue.extend(subclasses) + classes.update(subclasses) queue = list(bases) for cls in queue: if isinstance(cls, ClassDoc): @@ -1050,15 +1062,18 @@ """ Return a `DotGraph` that graphically displays the class hierarchy for the given class, using UML notation. Options: - + + - show_private_subclasses - max_attributes - max_operations - show_private_vars - show_magic_vars - link_attributes """ + show_private_subclasses = options.get('show_private_subclasses', True) + nodes = {} # ClassDoc -> DotGraphUmlClassNode - + # Create nodes for class_doc and all its bases. for cls in class_doc.mro(): if cls.pyval is object: continue # don't include `object`. @@ -1074,11 +1089,14 @@ if (isinstance(cls, ClassDoc) and cls.subclasses not in (None, UNKNOWN)): queue.extend(cls.subclasses) - for cls in cls.subclasses: - if cls not in nodes: - nodes[cls] = DotGraphUmlClassNode(cls, linker, context, - collapsed=True, - bgcolor=SUBCLASS_BG) + for subcls in cls.subclasses: + subcls_name = subcls.canonical_name[-1] + if ( (subcls not in nodes) and + (show_private_subclasses or + not subcls_name.startswith('_')) ): + nodes[subcls] = DotGraphUmlClassNode( + subcls, linker, context, collapsed=True, + bgcolor=SUBCLASS_BG) # Only show variables in the class where they're defined for # *class_doc*. This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2008-02-16 04:49:24
|
Revision: 1725 http://epydoc.svn.sourceforge.net/epydoc/?rev=1725&view=rev Author: edloper Date: 2008-02-15 20:49:21 -0800 (Fri, 15 Feb 2008) Log Message: ----------- - refactored class_tree_graph & uml_class_tree_graph -- now they share most of their code. - added truncation when class graphs get too big: max_subclass_depth truncates subclasses that are too far down in the tree, and max_subclasses limits the number of subclasses that are shown for any given class. - undocumented base classes are now drawn with a grey background - uml graph options: - Changed default values for max_attributes & max_operations - Added options to control whether parameter defaults are shown; and to truncate signatures that are too long - set minimum width for uml nodes to 100 - Fixed bug in DotGraphNode -- subclasses should share the same _next_id var, not use their own. - If dot fails & epydoc.DEBUG is true, then print the broken dotfile to a temp file. Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/dotgraph.py Modified: trunk/epydoc/src/epydoc/docwriter/dotgraph.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2008-02-16 02:02:58 UTC (rev 1724) +++ trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2008-02-16 04:49:21 UTC (rev 1725) @@ -33,6 +33,7 @@ SELECTED_BG = '#ffd0d0' BASECLASS_BG = '#e0b0a0' SUBCLASS_BG = '#e0b0a0' +UNDOCUMENTED_BG = '#c0c0c0' ROUTINE_BG = '#e8d0b0' # maybe? INH_LINK_COLOR = '#800000' @@ -43,7 +44,7 @@ DOT_COMMAND = 'dot' """The command that should be used to spawn dot""" -class DotGraph: +class DotGraph(object): """ A ``dot`` directed graph. The contents of the graph are constructed from the following instance variables: @@ -248,8 +249,15 @@ self.to_dotfile()) if err: log.warning("Graphviz dot warning(s):\n%s" % err) except OSError, e: - log.warning("Unable to render Graphviz dot graph:\n%s" % e) - #log.debug(self.to_dotfile()) + log.warning("Unable to render Graphviz dot graph (%s):\n%s" % + (self.title, e)) + import tempfile, epydoc + if epydoc.DEBUG: + filename = tempfile.mktemp('.dot') + out = open(filename, 'wb') + out.write(self.to_dotfile()) + out.close() + log.debug('Failed dot graph written to %s' % filename) return None return result @@ -277,7 +285,7 @@ # Default dot input encoding is UTF-8 return u'\n'.join(lines).encode('utf-8') -class DotGraphNode: +class DotGraphNode(object): _next_id = 0 def __init__(self, label=None, html_label=None, **attribs): if label is not None and html_label is not None: @@ -285,8 +293,8 @@ if label is not None: attribs['label'] = label self._html_label = html_label self._attribs = attribs - self.id = self.__class__._next_id - self.__class__._next_id += 1 + self.id = DotGraphNode._next_id + DotGraphNode._next_id += 1 self.port = None def __getitem__(self, attr): @@ -311,7 +319,7 @@ if attribs: attribs = ' [%s]' % (','.join(attribs)) return 'node%d%s' % (self.id, attribs) -class DotGraphEdge: +class DotGraphEdge(object): def __init__(self, start, end, label=None, **attribs): """ :type start: `DotGraphNode` @@ -386,7 +394,6 @@ - show/hide attribute types - use qualifiers """ - def __init__(self, class_doc, linker, context, collapsed=False, bgcolor=CLASS_BG, **options): """ @@ -420,13 +427,20 @@ - `max_attributes`: The maximum number of attributes that should be listed in the attribute box. If the class has more than this number of attributes, some will be - ellided. Ellipsis is marked with ``'...'``. + ellided. Ellipsis is marked with ``'...'``. (Default: 10) - `max_operations`: The maximum number of operations that - should be listed in the operation box. + should be listed in the operation box. (Default: 5) - `add_nodes_for_linked_attributes`: If true, then `link_attributes()` will create new a collapsed node for the types of a linked attributes if no node yet exists for that type. + - `show_signature_defaults`: If true, then show default + parameter values in method signatures; if false, then + hide them. (Default: *False*) + - `max_signature_width`: The maximum width (in chars) for + method signatures. If the signature is longer than this, + then it will be trunctated (with ``'...'``). (Default: + *60*) """ if not isinstance(class_doc, ClassDoc): raise TypeError('Expected a ClassDoc as 1st argument') @@ -463,6 +477,16 @@ These should not be added to the `DotGraph`; this node will generate their dotfile code directly.""" + self.same_rank = [] + """List of nodes that should have the same rank as this one. + (Used for nodes that are created by _link_attributes).""" + + # Keyword options: + self._show_signature_defaults = options.get( + 'show_signature_defaults', False) + self._max_signature_width = options.get( + 'max_signature_width', 60) + # Initialize operations & attributes lists. show_private = options.get('show_private_vars', False) show_magic = options.get('show_magic_vars', True) @@ -515,7 +539,7 @@ r'^(None or|optional) ([\w\.]+)$|^([\w\.]+) or None$') """A regular expression that matches descriptions of optional types.""" - def link_attributes(self, nodes): + def link_attributes(self, graph, nodes): """ Convert any attributes with type descriptions corresponding to documented classes to edges. The following type descriptions @@ -549,9 +573,9 @@ # that var from our attribute list; otherwise, leave that var # in our attribute list. self.attributes = [var for var in self.attributes - if not self._link_attribute(var, nodes)] + if not self._link_attribute(var, graph, nodes)] - def _link_attribute(self, var, nodes): + def _link_attribute(self, var, graph, nodes): """ Helper for `link_attributes()`: try to convert the attribute variable `var` into an edge, and add that edge to @@ -563,18 +587,19 @@ # Simple type. m = self.SIMPLE_TYPE_RE.match(type_descr) - if m and self._add_attribute_edge(var, nodes, m.group(1)): + if m and self._add_attribute_edge(var, graph, nodes, m.group(1)): return True # Collection type. m = self.COLLECTION_TYPE_RE.match(type_descr) - if m and self._add_attribute_edge(var, nodes, m.group(2), + if m and self._add_attribute_edge(var, graph, nodes, m.group(2), headlabel='*'): return True # Optional type. m = self.OPTIONAL_TYPE_RE.match(type_descr) - if m and self._add_attribute_edge(var, nodes, m.group(2) or m.group(3), + if m and self._add_attribute_edge(var, graph, nodes, + m.group(2) or m.group(3), headlabel='0..1'): return True @@ -582,7 +607,7 @@ m = self.MAPPING_TYPE_RE.match(type_descr) if m: port = 'qualifier_%s' % var.name - if self._add_attribute_edge(var, nodes, m.group(3), + if self._add_attribute_edge(var, graph, nodes, m.group(3), tailport='%s:e' % port): self.qualifiers.append( (m.group(2), port) ) return True @@ -591,7 +616,8 @@ m = self.MAPPING_TO_COLLECTION_TYPE_RE.match(type_descr) if m: port = 'qualifier_%s' % var.name - if self._add_attribute_edge(var, nodes, m.group(4), headlabel='*', + if self._add_attribute_edge(var, graph, nodes, m.group(4), + headlabel='*', tailport='%s:e' % port): self.qualifiers.append( (m.group(2), port) ) return True @@ -599,7 +625,7 @@ # We were unable to link this attribute. return False - def _add_attribute_edge(self, var, nodes, type_str, **attribs): + def _add_attribute_edge(self, var, graph, nodes, type_str, **attribs): """ Helper for `link_attributes()`: try to add an edge for the given attribute variable `var`. Return ``True`` if @@ -619,7 +645,9 @@ if self.options.get('add_nodes_for_linked_attributes', True): type_node = DotGraphUmlClassNode(type_doc, self.linker, self.context, collapsed=True) + self.same_rank.append(type_node) nodes[type_doc] = type_node + graph.nodes.append(type_node) else: return False @@ -629,7 +657,7 @@ attribs.setdefault('tailport', 'body') url = self.linker.url_for(var) or NOOP_URL self.edges.append(DotGraphEdge(self, type_node, label=var.name, - arrowhead='open', href=url, + arrowtail='odiamond', arrowhead='none', href=url, tooltip=var.canonical_name, labeldistance=1.5, **attribs)) return True @@ -690,6 +718,8 @@ if func_doc.vararg: args.append('*'+func_doc.vararg) if func_doc.kwarg: args.append('**'+func_doc.kwarg) label = '%s(%s)' % (var_doc.name, ', '.join(args)) + if len(label) > self._max_signature_width: + label = label[:self._max_signature_width-4]+'...)' # Get the URL url = self.linker.url_for(var_doc) or NOOP_URL # Construct & return the pseudo-html code @@ -700,7 +730,7 @@ :todo: Handle tuple args better :todo: Optionally add type info? """ - if default is None: + if default is None or not self._show_signature_defaults: return '%s' % name else: pyval_repr = default.summary_pyval_repr().to_plaintext(None) @@ -732,12 +762,12 @@ _LABEL = ''' <TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="0"> <TR><TD ROWSPAN="%s"> - <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" + <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" WIDTH="100" CELLPADDING="0" PORT="body" BGCOLOR="%s"> - <TR><TD>%s</TD></TR> - <TR><TD><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0"> + <TR><TD WIDTH="100">%s</TD></TR> + <TR><TD WIDTH="100"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0"> %s</TABLE></TD></TR> - <TR><TD><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0"> + <TR><TD WIDTH="100"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0"> %s</TABLE></TD></TR> </TABLE> </TD></TR> @@ -746,7 +776,7 @@ _COLLAPSED_LABEL = ''' <TABLE CELLBORDER="0" BGCOLOR="%s" PORT="body"> - <TR><TD>%s</TD></TR> + <TR><TD WIDTH="50">%s</TD></TR> </TABLE>''' def _get_html_label(self): @@ -760,7 +790,7 @@ # Construct the attribute list. (If it's too long, truncate) attrib_cells = [self._attribute_cell(a) for a in self.attributes] - max_attributes = self.options.get('max_attributes', 15) + max_attributes = self.options.get('max_attributes', 10) if len(attrib_cells) == 0: attrib_cells = ['<TR><TD></TD></TR>'] elif len(attrib_cells) > max_attributes: @@ -769,7 +799,7 @@ # Construct the operation list. (If it's too long, truncate) oper_cells = [self._operation_cell(a) for a in self.operations] - max_operations = self.options.get('max_operations', 15) + max_operations = self.options.get('max_operations', 5) if len(oper_cells) == 0: oper_cells = ['<TR><TD></TD></TR>'] elif len(oper_cells) > max_operations: @@ -797,6 +827,11 @@ if not self.collapsed: for edge in self.edges: s += '\n' + edge.to_dotfile() + if self.same_rank: + sr_nodes = ''.join(['node%s; ' % node.id + for node in self.same_rank]) + # [xx] This can cause dot to crash! not sure why! + #s += '{rank=same; node%s; %s}' % (self.id, sr_nodes) return s class DotGraphUmlModuleNode(DotGraphNode): @@ -953,7 +988,7 @@ log.warning('UML style package trees require dot version 2.0+') graph = DotGraph('Package Tree for %s' % name_list(packages, context), - body='ranksep=.3\n;nodesep=.1\n', + body='ranksep=.3\nnodesep=.1\n', edge_defaults={'dir':'none'}) # Options @@ -1003,149 +1038,226 @@ return graph ###################################################################### -def class_tree_graph(bases, linker, context=None, **options): +def class_tree_graph(classes, linker, context=None, **options): """ Return a `DotGraph` that graphically displays the class hierarchy for the given classes. Options: - - exclude + - exclude: A list of classes that should be excluded - dir: LR|RL|BT requests a left-to-right, right-to-left, or bottom-to- top, drawing. (corresponds to the dot option 'rankdir' + - max_subclass_depth: The maximum depth to which subclasses + will be drawn. + - max_subclasses: A list of ints, specifying how many + subclasses should be drawn per class at each level of the + graph. E.g., [5,3,1] means draw up to 5 subclasses for the + specified classes; up to 3 subsubclasses for each of those (up + to) 5 subclasses; and up to 1 subclass for each of those. """ - if isinstance(bases, ClassDoc): bases = [bases] - graph = DotGraph('Class Hierarchy for %s' % name_list(bases, context), + # Callbacks: + def mknode(cls, nodetype, linker, context, options): + return mk_valdoc_node(cls, linker, context) + def mkedge(start, end, edgetype, options): + return DotGraphEdge(start, end) + + if isinstance(classes, ClassDoc): classes = [classes] + graph = DotGraph('Class Hierarchy for %s' % name_list(classes, context), body='ranksep=0.3\n', edge_defaults={'sametail':True, 'dir':'none'}) + _class_tree_graph(graph, classes, mknode, mkedge, linker, + context, options, cls2node={}) + return graph - # Options - if options.get('dir', 'TB') != 'TB': # default: top-down - graph.body += 'rankdir=%s\n' % options.get('dir', 'TB') +def _class_tree_graph(graph, classes, mknode, mkedge, linker, + context, options, cls2node): + """ + A helper function that is used by both `class_tree_graph()` and + `uml_class_tree_graph()` to draw class trees. To abstract over + the differences between the two, this function takes two callback + functions that create graph nodes and edges: + + - ``mknode(base, nodetype, linker, context, options)``: Returns + a `DotGraphNode`. ``nodetype`` is one of: subclass, superclass, + selected, undocumented. + - ``mkedge(begin, end, edgetype, options)``: Returns a + `DotGraphEdge`. ``edgetype`` is one of: subclass, + truncate-subclass. + """ + rankdir = options.get('dir', 'TB') + graph.body += 'rankdir=%s\n' % rankdir + truncated = set() # Classes whose subclasses were truncated + _add_class_tree_superclasses(graph, classes, mknode, mkedge, linker, + context, options, cls2node) + _add_class_tree_subclasses(graph, classes, mknode, mkedge, linker, + context, options, cls2node, truncated) + _add_class_tree_inheritance(graph, classes, mknode, mkedge, linker, + context, options, cls2node, truncated) + +def _add_class_tree_superclasses(graph, classes, mknode, mkedge, linker, + context, options, cls2node): exclude = options.get('exclude', ()) + + # Create nodes for all bases. + for cls in classes: + for base in cls.mro(): + # Don't include 'object' + if base.canonical_name == DottedName('object'): continue + # Stop if we reach an excluded class. + if base in exclude: break + # Don't do the same class twice. + if base in cls2node: continue + # Make the node. + if linker.url_for(base) is None: typ = 'undocumented' + elif base in classes: typ = 'selected' + else: typ = 'superclass' + cls2node[base] = mknode(base, typ, linker, context, options) + graph.nodes.append(cls2node[base]) - # Find all superclasses & subclasses of the given classes. - classes = set(bases) - queue = list(bases) +def _add_class_tree_subclasses(graph, classes, mknode, mkedge, linker, + context, options, cls2node, truncated): + exclude = options.get('exclude', ()) + max_subclass_depth = options.get('max_subclass_depth', 3) + max_subclasses = list(options.get('max_subclasses', (5,3,2,1))) + max_subclasses += len(classes)*max_subclasses[-1:] # repeat last num + + # Find the depth of each subclass (for truncation) + subclass_depth = _get_subclass_depth_map(classes) + + queue = list(classes) for cls in queue: - if isinstance(cls, ClassDoc): - if cls.subclasses not in (None, UNKNOWN): - subclasses = cls.subclasses - if exclude: - subclasses = [d for d in subclasses if d not in exclude] - queue.extend(subclasses) - classes.update(subclasses) - queue = list(bases) - for cls in queue: - if isinstance(cls, ClassDoc): - if cls.bases not in (None, UNKNOWN): - bases = cls.bases - if exclude: - bases = [d for d in bases if d not in exclude] - queue.extend(bases) - classes.update(bases) + # If there are no subclasses, then we're done. + if not isinstance(cls, ClassDoc): continue + if cls.subclasses in (None, UNKNOWN, (), []): continue + # Get the list of subclasses. + subclasses = [subcls for subcls in cls.subclasses + if subcls not in cls2node and subcls not in exclude] + # If the subclass list is too long, then truncate it. + if len(subclasses) > max_subclasses[subclass_depth[cls]]: + subclasses = subclasses[:max_subclasses[subclass_depth[cls]]] + truncated.add(cls) + # Truncate any classes that are too deep. + num_subclasses = len(subclasses) + subclasses = [subcls for subcls in subclasses + if subclass_depth[subcls] <= max_subclass_depth] + if len(subclasses) < num_subclasses: truncated.add(cls) + # Add a node for each subclass. + for subcls in subclasses: + cls2node[subcls] = mknode(subcls, 'subclass', linker, + context, options) + graph.nodes.append(cls2node[subcls]) + # Add the subclasses to our queue. + queue.extend(subclasses) - # Add a node for each cls. - classes = [d for d in classes if isinstance(d, ClassDoc) - if d.pyval is not object] - nodes = add_valdoc_nodes(graph, classes, linker, context) +def _add_class_tree_inheritance(graph, classes, mknode, mkedge, linker, + context, options, cls2node, truncated): + # Add inheritance edges. + for (cls, node) in cls2node.items(): + for base in cls.bases: + if base in cls2node: + graph.edges.append(mkedge(cls2node[base], node, + 'subclass', options)) + # Mark truncated classes + for cls in truncated: + ellipsis = DotGraphNode('...', shape='plaintext', + width='0', height='0') + graph.nodes.append(ellipsis) + graph.edges.append(mkedge(cls2node[cls], ellipsis, + 'truncate-subclass', options)) - # Add an edge for each package/subclass relationship. - edges = set() - for cls in classes: - for subcls in cls.subclasses: - if cls in nodes and subcls in nodes: - edges.add((nodes[cls], nodes[subcls])) - graph.edges = [DotGraphEdge(src,dst) for (src,dst) in edges] +def _get_subclass_depth_map(classes): + subclass_depth = dict([(cls,0) for cls in classes]) + queue = list(classes) + for cls in queue: + if (isinstance(cls, ClassDoc) and + cls.subclasses not in (None, UNKNOWN)): + for subcls in cls.subclasses: + subclass_depth[subcls] = max(subclass_depth.get(subcls,0), + subclass_depth[cls]+1) + queue.append(subcls) + return subclass_depth - return graph + ###################################################################### -def uml_class_tree_graph(class_doc, linker, context=None, **options): +def uml_class_tree_graph(classes, linker, context=None, **options): """ Return a `DotGraph` that graphically displays the class hierarchy for the given class, using UML notation. Options: + - exclude: A list of classes that should be excluded + - dir: LR|RL|BT requests a left-to-right, right-to-left, or + bottom-to- top, drawing. (corresponds to the dot option + 'rankdir' + - max_subclass_depth: The maximum depth to which subclasses + will be drawn. + - max_subclasses: A list of ints, specifying how many + subclasses should be drawn per class at each level of the + graph. E.g., [5,3,1] means draw up to 5 subclasses for the + specified classes; up to 3 subsubclasses for each of those (up + to) 5 subclasses; and up to 1 subclass for each of those. - max_attributes - max_operations - show_private_vars - show_magic_vars - link_attributes + - show_signature_defaults + - max_signature_width """ - nodes = {} # ClassDoc -> DotGraphUmlClassNode - exclude = options.get('exclude', ()) - - # Create nodes for class_doc and all its bases. - for cls in class_doc.mro(): - if cls.pyval is object: continue # don't include `object`. - if cls in exclude: break # stop if we get to an excluded class. - if cls == class_doc: color = SELECTED_BG - else: color = BASECLASS_BG - nodes[cls] = DotGraphUmlClassNode(cls, linker, context, - show_inherited_vars=False, - collapsed=False, bgcolor=color) + cls2node = {} - # Create nodes for all class_doc's subclasses. - queue = [class_doc] - for cls in queue: - if (isinstance(cls, ClassDoc) and - cls.subclasses not in (None, UNKNOWN)): - for subcls in cls.subclasses: - subcls_name = subcls.canonical_name[-1] - if subcls not in nodes and subcls not in exclude: - queue.append(subcls) - nodes[subcls] = DotGraphUmlClassNode( - subcls, linker, context, collapsed=True, - bgcolor=SUBCLASS_BG) - - # Only show variables in the class where they're defined for - # *class_doc*. - mro = class_doc.mro() - for name, var in class_doc.variables.items(): - i = mro.index(var.container) - for base in mro[i+1:]: - if base.pyval is object: continue # don't include `object`. - overridden_var = base.variables.get(name) - if overridden_var and overridden_var.container == base: - try: - if isinstance(overridden_var.value, RoutineDoc): - nodes[base].operations.remove(overridden_var) - else: - nodes[base].attributes.remove(overridden_var) - except ValueError: - pass # var is filtered (eg private or magic) + # Draw the basic graph: + if isinstance(classes, ClassDoc): classes = [classes] + graph = DotGraph('UML class diagram for %s' % name_list(classes, context), + body='ranksep=.2\n;nodesep=.3\n') + _class_tree_graph(graph, classes, _uml_mknode, _uml_mkedge, + linker, context, options, cls2node) - # Keep track of which nodes are part of the inheritance graph - # (since link_attributes might add new nodes) - inheritance_nodes = set(nodes.values()) - - # Turn attributes into links. + # Turn attributes into links (optional): + inheritance_nodes = set(graph.nodes) if options.get('link_attributes', True): - for node in nodes.values(): - node.link_attributes(nodes) - # Make sure that none of the new attribute edges break the - # rank ordering assigned by inheritance. - for edge in node.edges: - if edge.end in inheritance_nodes: - edge['constraint'] = 'False' - - # Construct the graph. - graph = DotGraph('UML class diagram for %s' % class_doc.canonical_name, - body='ranksep=.2\n;nodesep=.3\n') - graph.nodes = nodes.values() - - # Add inheritance edges. - for node in inheritance_nodes: - for base in node.class_doc.bases: - if base in nodes: - graph.edges.append(DotGraphEdge(nodes[base], node, - dir='back', arrowtail='empty', - headport='body', tailport='body', - color=INH_LINK_COLOR, weight=100, - style='bold')) + for cls in classes: + for base in cls.mro(): + node = cls2node.get(base) + if node is None: continue + node.link_attributes(graph, cls2node) + # Make sure that none of the new attribute edges break + # the rank ordering assigned by inheritance. + for edge in node.edges: + if edge.end in inheritance_nodes: + edge['constraint'] = 'False' - # And we're done! return graph +# A callback to make graph nodes: +def _uml_mknode(cls, nodetype, linker, context, options): + if nodetype == 'subclass': + return DotGraphUmlClassNode( + cls, linker, context, collapsed=True, + bgcolor=SUBCLASS_BG, **options) + elif nodetype in ('selected', 'superclass', 'undocumented'): + if nodetype == 'selected': bgcolor = SELECTED_BG + if nodetype == 'superclass': bgcolor = BASECLASS_BG + if nodetype == 'undocumented': bgcolor = UNDOCUMENTED_BG + return DotGraphUmlClassNode( + cls, linker, context, show_inherited_vars=False, + collapsed=False, bgcolor=bgcolor, **options) + assert 0, 'bad nodetype' + +# A callback to make graph edges: +def _uml_mkedge(start, end, edgetype, options): + if edgetype == 'subclass': + return DotGraphEdge( + start, end, dir='back', arrowtail='empty', + headport='body', tailport='body', color=INH_LINK_COLOR, + weight=100, style='bold') + if edgetype == 'truncate-subclass': + return DotGraphEdge( + start, end, dir='back', arrowtail='empty', + tailport='body', color=INH_LINK_COLOR, + weight=100, style='bold') + assert 0, 'bad edgetype' + ###################################################################### def import_graph(modules, docindex, linker, context=None, **options): graph = DotGraph('Import Graph', body='ranksep=.3\n;nodesep=.3\n') @@ -1272,14 +1384,17 @@ :todo: Use different node styles for different subclasses of APIDoc """ nodes = {} - val_docs = sorted(val_docs, key=lambda d:d.canonical_name) - for i, val_doc in enumerate(val_docs): - label = val_doc.canonical_name.contextualize(context.canonical_name) - node = nodes[val_doc] = DotGraphNode(label) - graph.nodes.append(node) - specialize_valdoc_node(node, val_doc, context, linker.url_for(val_doc)) + for val_doc in sorted(val_docs, key=lambda d:d.canonical_name): + nodes[val_doc] = mk_valdoc_node(graph, val_doc, linker, context) + graph.nodes.append(nodes[val_doc]) return nodes +def mk_valdoc_node(val_doc, linker, context): + label = val_doc.canonical_name.contextualize(context.canonical_name) + node = DotGraphNode(label) + specialize_valdoc_node(node, val_doc, context, linker.url_for(val_doc)) + return node + NOOP_URL = 'javascript:void(0);' MODULE_NODE_HTML = ''' <TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" @@ -1307,6 +1422,10 @@ # Set the URL. (Do this even if it points to the page we're # currently on; otherwise, the tooltip is ignored.) node['href'] = url or NOOP_URL + + if url is None: + node['fillcolor'] = UNDOCUMENTED_BG + node['style'] = 'filled' if isinstance(val_doc, ModuleDoc) and dot_version >= [2]: node['shape'] = 'plaintext' This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2008-02-23 19:50:30
|
Revision: 1761 http://epydoc.svn.sourceforge.net/epydoc/?rev=1761&view=rev Author: edloper Date: 2008-02-23 11:50:28 -0800 (Sat, 23 Feb 2008) Log Message: ----------- - Added max_width and max_height attributes to DotGraph -- if the graph is larger than this, then it will be scaled down. Default value is 6 inches wide by 8 inches tall. Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/dotgraph.py Modified: trunk/epydoc/src/epydoc/docwriter/dotgraph.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2008-02-23 19:33:37 UTC (rev 1760) +++ trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2008-02-23 19:50:28 UTC (rev 1761) @@ -84,7 +84,8 @@ DEFAULT_EDGE_DEFAULTS={'fontsize':10, 'fontname': 'Helvetica'} def __init__(self, title, body='', node_defaults=None, - edge_defaults=None, caption=None): + edge_defaults=None, caption=None, + max_width=6, max_height=8): """ Create a new `DotGraph`. """ @@ -109,7 +110,13 @@ graph. :type: ``str``""" + + self.max_width = max_width + """The maximum width of the graph (in inches)""" + self.max_height = max_height + """The maximum height of the graph (in inches)""" + self.node_defaults = node_defaults or self.DEFAULT_NODE_DEFAULTS """Default attribute values for nodes.""" @@ -329,6 +336,7 @@ to render this graph. """ lines = ['digraph %s {' % self.uid, + 'size="%d,%d"\n' % (self.max_width, self.max_height), 'node [%s]' % ','.join(['%s="%s"' % (k,v) for (k,v) in self.node_defaults.items()]), 'edge [%s]' % ','.join(['%s="%s"' % (k,v) for (k,v) @@ -1126,6 +1134,8 @@ return DotGraphEdge(start, end) if isinstance(classes, ClassDoc): classes = [classes] + # [xx] this should be done earlier, and should generate a warning: + classes = [c for c in classes if c is not None] graph = DotGraph('Class Hierarchy for %s' % name_list(classes, context), body='ranksep=0.3\n', edge_defaults={'sametail':True, 'dir':'none'}) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2006-09-02 00:52:52
|
Revision: 1340 http://svn.sourceforge.net/epydoc/?rev=1340&view=rev Author: edloper Date: 2006-09-01 17:52:49 -0700 (Fri, 01 Sep 2006) Log Message: ----------- - Minor docstring fixes - Changed graph title for UML class diagrams to use the class's name, not the entire pprinted repr of the classdoc. Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/dotgraph.py Modified: trunk/epydoc/src/epydoc/docwriter/dotgraph.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2006-09-02 00:51:36 UTC (rev 1339) +++ trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2006-09-02 00:52:49 UTC (rev 1340) @@ -90,18 +90,18 @@ self.nodes = [] """A list of the nodes that are present in the graph. - :type: `list` of `DotGraphNode`""" + :type: ``list`` of `DotGraphNode`""" self.edges = [] """A list of the edges that are present in the graph. - :type: `list` of `DotGraphEdge`""" + :type: ``list`` of `DotGraphEdge`""" self.body = body """A string that should be included as-is in the body of the graph. - :type: `str`""" + :type: ``str``""" self.node_defaults = node_defaults or self.DEFAULT_NODE_DEFAULTS """Default attribute values for nodes.""" @@ -401,7 +401,7 @@ names will be contextualized to this context. `collapsed` : ``bool`` If true, then display this node as a simple box. - `bgcolor` : ``str`` + `bgcolor` : ```str``` The background color for this node. `options` : ``dict`` A set of options used to control how the node should @@ -858,9 +858,9 @@ """ :Return: (label, depth, width) where: - - `label` is the HTML label - - `depth` is the depth of the package tree (for coloring) - - `width` is the max width of the HTML label, roughly in + - ``label`` is the HTML label + - ``depth`` is the depth of the package tree (for coloring) + - ``width`` is the max width of the HTML label, roughly in units of characters. """ MAX_ROW_WIDTH = 80 # unit is roughly characters. @@ -1109,7 +1109,7 @@ edge['constraint'] = 'False' # Construct the graph. - graph = DotGraph('UML class diagram for %s' % class_doc, + graph = DotGraph('UML class diagram for %s' % class_doc.canonical_name, body='ranksep=.2\n;nodesep=.3\n') graph.nodes = nodes.values() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dva...@us...> - 2007-02-04 02:45:40
|
Revision: 1430 http://svn.sourceforge.net/epydoc/?rev=1430&view=rev Author: dvarrazzo Date: 2007-02-03 18:45:37 -0800 (Sat, 03 Feb 2007) Log Message: ----------- - Intercept the base OSError, which can be raised in some circumstance, e.g. when the command is not found. Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/dotgraph.py Modified: trunk/epydoc/src/epydoc/docwriter/dotgraph.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2007-02-03 03:51:05 UTC (rev 1429) +++ trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2007-02-04 02:45:37 UTC (rev 1430) @@ -1238,7 +1238,7 @@ _dot_version = [int(x) for x in m.group(1).split('.')] else: _dot_version = (0,) - except RunSubprocessError, e: + except OSError, e: _dot_version = (0,) log.info('Detected dot version %s' % _dot_version) return _dot_version This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dva...@us...> - 2007-02-14 00:24:24
|
Revision: 1484 http://svn.sourceforge.net/epydoc/?rev=1484&view=rev Author: dvarrazzo Date: 2007-02-13 16:24:22 -0800 (Tue, 13 Feb 2007) Log Message: ----------- - Tag fixed. Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/dotgraph.py Modified: trunk/epydoc/src/epydoc/docwriter/dotgraph.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2007-02-13 22:39:34 UTC (rev 1483) +++ trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2007-02-14 00:24:22 UTC (rev 1484) @@ -1244,7 +1244,7 @@ def add_valdoc_nodes(graph, val_docs, linker, context): """ - @todo: Use different node styles for different subclasses of APIDoc + :todo: Use different node styles for different subclasses of APIDoc """ nodes = {} val_docs = sorted(val_docs, key=lambda d:d.canonical_name) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dva...@us...> - 2007-02-16 01:28:59
|
Revision: 1510 http://svn.sourceforge.net/epydoc/?rev=1510&view=rev Author: dvarrazzo Date: 2007-02-15 17:28:57 -0800 (Thu, 15 Feb 2007) Log Message: ----------- - Fixed tooltip for class nodes. Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/dotgraph.py Modified: trunk/epydoc/src/epydoc/docwriter/dotgraph.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2007-02-15 23:53:21 UTC (rev 1509) +++ trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2007-02-16 01:28:57 UTC (rev 1510) @@ -478,7 +478,13 @@ self.attributes.append(var) # Initialize our dot node settings. - DotGraphNode.__init__(self, tooltip=class_doc.canonical_name, + tooltip = self._summary(class_doc) + if tooltip: + # dot chokes on a \n in the attribute... + tooltip = " ".join(tooltip.split()) + else: + tooltip = class_doc.canonical_name + DotGraphNode.__init__(self, tooltip=tooltip, width=0, height=0, shape='plaintext', href=linker.url_for(class_doc) or NOOP_URL) @@ -634,9 +640,11 @@ """Return a plaintext summary for `api_doc`""" if not isinstance(api_doc, APIDoc): return '' if api_doc.summary in (None, UNKNOWN): return '' - summary = api_doc.summary.to_plaintext(self.linker).strip() + summary = api_doc.summary.to_plaintext(None).strip() return plaintext_to_html(summary) + _summary = classmethod(_summary) + def _type_descr(self, api_doc): """Return a plaintext type description for `api_doc`""" if not hasattr(api_doc, 'type_descr'): return '' This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2007-09-25 18:34:00
|
Revision: 1643 http://epydoc.svn.sourceforge.net/epydoc/?rev=1643&view=rev Author: edloper Date: 2007-09-25 11:33:58 -0700 (Tue, 25 Sep 2007) Log Message: ----------- - Removed 'show_private_subclasses' option that was added in the last checkin; replaced it with an 'exclude' option that can be used to exclude selected classes from the tree. Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/dotgraph.py Modified: trunk/epydoc/src/epydoc/docwriter/dotgraph.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2007-09-25 18:09:07 UTC (rev 1642) +++ trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2007-09-25 18:33:58 UTC (rev 1643) @@ -1007,7 +1007,7 @@ Return a `DotGraph` that graphically displays the class hierarchy for the given classes. Options: - - show_private_subclasses + - exclude - dir: LR|RL|BT requests a left-to-right, right-to-left, or bottom-to- top, drawing. (corresponds to the dot option 'rankdir' @@ -1020,7 +1020,7 @@ # Options if options.get('dir', 'TB') != 'TB': # default: top-down graph.body += 'rankdir=%s\n' % options.get('dir', 'TB') - show_private_subclasses = options.get('show_private_subclasses', True) + exclude = options.get('exclude', ()) # Find all superclasses & subclasses of the given classes. classes = set(bases) @@ -1028,19 +1028,20 @@ for cls in queue: if isinstance(cls, ClassDoc): if cls.subclasses not in (None, UNKNOWN): - if show_private_subclasses: - subclasses = cls.subclasses - else: - subclasses = [c for c in cls.subclasses - if not c.canonical_name[-1].startswith('_')] + subclasses = cls.subclasses + if exclude: + subclasses = [d for d in subclasses if d not in exclude] queue.extend(subclasses) classes.update(subclasses) queue = list(bases) for cls in queue: if isinstance(cls, ClassDoc): if cls.bases not in (None, UNKNOWN): - queue.extend(cls.bases) - classes.update(cls.bases) + bases = cls.bases + if exclude: + bases = [d for d in bases if d not in exclude] + queue.extend(bases) + classes.update(bases) # Add a node for each cls. classes = [d for d in classes if isinstance(d, ClassDoc) @@ -1063,20 +1064,19 @@ Return a `DotGraph` that graphically displays the class hierarchy for the given class, using UML notation. Options: - - show_private_subclasses - max_attributes - max_operations - show_private_vars - show_magic_vars - link_attributes """ - show_private_subclasses = options.get('show_private_subclasses', True) - nodes = {} # ClassDoc -> DotGraphUmlClassNode + exclude = options.get('exclude', ()) # Create nodes for class_doc and all its bases. for cls in class_doc.mro(): if cls.pyval is object: continue # don't include `object`. + if cls in exclude: break # stop if we get to an excluded class. if cls == class_doc: color = SELECTED_BG else: color = BASECLASS_BG nodes[cls] = DotGraphUmlClassNode(cls, linker, context, @@ -1088,12 +1088,10 @@ for cls in queue: if (isinstance(cls, ClassDoc) and cls.subclasses not in (None, UNKNOWN)): - queue.extend(cls.subclasses) for subcls in cls.subclasses: subcls_name = subcls.canonical_name[-1] - if ( (subcls not in nodes) and - (show_private_subclasses or - not subcls_name.startswith('_')) ): + if subcls not in nodes and subcls not in exclude: + queue.append(subcls) nodes[subcls] = DotGraphUmlClassNode( subcls, linker, context, collapsed=True, bgcolor=SUBCLASS_BG) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dva...@us...> - 2007-10-20 16:48:46
|
Revision: 1660 http://epydoc.svn.sourceforge.net/epydoc/?rev=1660&view=rev Author: dvarrazzo Date: 2007-10-20 09:48:44 -0700 (Sat, 20 Oct 2007) Log Message: ----------- - Methods and variables order consistent with other generated docs. Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/dotgraph.py Modified: trunk/epydoc/src/epydoc/docwriter/dotgraph.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2007-09-27 10:17:07 UTC (rev 1659) +++ trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2007-10-20 16:48:44 UTC (rev 1660) @@ -467,7 +467,8 @@ show_private = options.get('show_private_vars', False) show_magic = options.get('show_magic_vars', True) show_inherited = options.get('show_inherited_vars', False) - for name, var in class_doc.variables.iteritems(): + for var in class_doc.sorted_variables: + name = var.canonical_name[-1] if ((not show_private and var.is_public == False) or (not show_magic and re.match('__\w+__$', name)) or (not show_inherited and var.container != class_doc)): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dva...@us...> - 2007-11-07 15:29:48
|
Revision: 1663 http://epydoc.svn.sourceforge.net/epydoc/?rev=1663&view=rev Author: dvarrazzo Date: 2007-11-07 07:29:47 -0800 (Wed, 07 Nov 2007) Log Message: ----------- - Import graph correctly reconstructed. Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/dotgraph.py Modified: trunk/epydoc/src/epydoc/docwriter/dotgraph.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2007-11-07 15:23:41 UTC (rev 1662) +++ trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2007-11-07 15:29:47 UTC (rev 1663) @@ -1163,7 +1163,7 @@ if dst.imports in (None, UNKNOWN): continue for var_name in dst.imports: for i in range(len(var_name), 0, -1): - val_doc = docindex.get_valdoc(var_name[:i]) + val_doc = docindex.find(var_name[:i], context) if isinstance(val_doc, ModuleDoc): if val_doc in nodes and dst in nodes: edges.add((nodes[val_doc], nodes[dst])) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2008-02-18 18:25:49
|
Revision: 1728 http://epydoc.svn.sourceforge.net/epydoc/?rev=1728&view=rev Author: edloper Date: 2008-02-18 10:25:40 -0800 (Mon, 18 Feb 2008) Log Message: ----------- - When constructing UML graph nodes, guard against the case where class_doc.sorted_variables is UNKNOWN. This is known to happen if the user defines a class that subclasses from an undocumented class that itself has a nested class. E.g., Tkinter.Misc.getint. Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/dotgraph.py Modified: trunk/epydoc/src/epydoc/docwriter/dotgraph.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2008-02-16 04:50:59 UTC (rev 1727) +++ trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2008-02-18 18:25:40 UTC (rev 1728) @@ -491,16 +491,17 @@ show_private = options.get('show_private_vars', False) show_magic = options.get('show_magic_vars', True) show_inherited = options.get('show_inherited_vars', False) - for var in class_doc.sorted_variables: - name = var.canonical_name[-1] - if ((not show_private and var.is_public == False) or - (not show_magic and re.match('__\w+__$', name)) or - (not show_inherited and var.container != class_doc)): - pass - elif isinstance(var.value, RoutineDoc): - self.operations.append(var) - else: - self.attributes.append(var) + if class_doc.sorted_variables not in (None, UNKNOWN): + for var in class_doc.sorted_variables: + name = var.canonical_name[-1] + if ((not show_private and var.is_public == False) or + (not show_magic and re.match('__\w+__$', name)) or + (not show_inherited and var.container != class_doc)): + pass + elif isinstance(var.value, RoutineDoc): + self.operations.append(var) + else: + self.attributes.append(var) # Initialize our dot node settings. tooltip = self._summary(class_doc) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2008-02-23 05:59:56
|
Revision: 1745 http://epydoc.svn.sourceforge.net/epydoc/?rev=1745&view=rev Author: edloper Date: 2008-02-22 21:59:54 -0800 (Fri, 22 Feb 2008) Log Message: ----------- - Add latex support for dotgraphs Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/dotgraph.py Modified: trunk/epydoc/src/epydoc/docwriter/dotgraph.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2008-02-23 05:19:30 UTC (rev 1744) +++ trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2008-02-23 05:59:54 UTC (rev 1745) @@ -22,6 +22,7 @@ import re import sys +import tempfile from epydoc import log from epydoc.apidoc import * from epydoc.util import * @@ -129,6 +130,32 @@ self.uid = '%s_%s' % (self.uid, n) self._uids.add(self.uid) + def to_latex(self, image_file, language='ps', center=True): + # Write the image file. + if language == 'ps': + self.write(image_file, language='ps') + elif language == 'pdf': + ps = self._run_dot('-Tps') + psfilename = tempfile.mktemp('.ps') + psfile = open(psfilename, 'wb') + psfile.write('%!PS-Adobe-2.0 EPSF-1.2\n') + psfile.write(ps) + psfile.close() + try: run_subprocess(('ps2pdf', '-dEPSCrop', psfilename, + image_file)) + except RunSubprocessError, e: + log.warning("Unable to render Graphviz dot graph (%s):\n" + "ps2pdf failed." % self.title) + return None + else: + raise ValueError('Expected language to be "ps" or "pdf"') + + # Generate the latex code to display the graph. + name = os.path.splitext(os.path.split(image_file)[-1])[0] + s = ' \\includegraphics{%s}\n' % name + if center: s = '\\begin{center}\n%s\\end{center}\n' % s + return s + def to_html(self, image_file, image_url, center=True): """ Return the HTML code that should be uesd to display this graph @@ -633,6 +660,7 @@ successful. """ # Use the type string to look up a corresponding ValueDoc. + if not hasattr(self.linker, 'docindex'): return False type_doc = self.linker.docindex.find(type_str, var) if not type_doc: return False This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2008-02-23 07:02:44
|
Revision: 1749 http://epydoc.svn.sourceforge.net/epydoc/?rev=1749&view=rev Author: edloper Date: 2008-02-22 23:02:42 -0800 (Fri, 22 Feb 2008) Log Message: ----------- - Fixed typo from previous checkin Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/dotgraph.py Modified: trunk/epydoc/src/epydoc/docwriter/dotgraph.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2008-02-23 06:59:20 UTC (rev 1748) +++ trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2008-02-23 07:02:42 UTC (rev 1749) @@ -812,7 +812,7 @@ def _get_html_label(self): # Get the class name & contextualize it. classname = self.class_doc.canonical_name - if context is not None: + if self.context is not None: classname = classname.contextualize(self.context.canonical_name) # If we're collapsed, display the node as a single box. This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2008-02-23 16:37:16
|
Revision: 1753 http://epydoc.svn.sourceforge.net/epydoc/?rev=1753&view=rev Author: edloper Date: 2008-02-23 08:37:10 -0800 (Sat, 23 Feb 2008) Log Message: ----------- - Fixed bug where graphs in latex would mark nodes as undocumented Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/dotgraph.py Modified: trunk/epydoc/src/epydoc/docwriter/dotgraph.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2008-02-23 16:24:45 UTC (rev 1752) +++ trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2008-02-23 16:37:10 UTC (rev 1753) @@ -1139,8 +1139,10 @@ # Don't do the same class twice. if base in cls2node: continue # Make the node. - if linker.url_for(base) is None: typ = 'undocumented' - elif base in classes: typ = 'selected' + if base in classes: typ = 'selected' + elif (hasattr(linker, 'docindex') and + linker.docindex.find(identifier, self.container) is None): + typ = 'undocumented' else: typ = 'superclass' cls2node[base] = mknode(base, typ, linker, context, options) graph.nodes.append(cls2node[base]) @@ -1425,7 +1427,7 @@ if context is not None: label = label.contextualize(context.canonical_name) node = DotGraphNode(label) - specialize_valdoc_node(node, val_doc, context, linker.url_for(val_doc)) + specialize_valdoc_node(node, val_doc, context, linker) return node NOOP_URL = 'javascript:void(0);' @@ -1438,7 +1440,7 @@ PORT="body" HREF="%s" TOOLTIP="%s">%s</TD></TR> </TABLE>'''.strip() -def specialize_valdoc_node(node, val_doc, context, url): +def specialize_valdoc_node(node, val_doc, context, linker): """ Update the style attributes of `node` to reflext its type and context. @@ -1454,9 +1456,12 @@ # Set the URL. (Do this even if it points to the page we're # currently on; otherwise, the tooltip is ignored.) + url = linker.url_for(val_doc) node['href'] = url or NOOP_URL - - if url is None: + + if (url is None and + hasattr(linker, 'docindex') and + linker.docindex.find(identifier, self.container) is None): node['fillcolor'] = UNDOCUMENTED_BG node['style'] = 'filled' This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |