Commits

dvar...@26cce582-e6ba-44bf-b3dd-dff0d0f4b894  committed acf0c8e

- Added DottedName.contextualize() method.

  • Participants
  • Parent commits 9ead728

Comments (0)

Files changed (4)

File epydoc/src/epydoc/apidoc.py

             return False
         return self._identifiers == name._identifiers[:len(self)]
 
+    def contextualize(self, context):
+        """
+        Return a reduced version of a dotted name. This name may be ambigious
+        (it is if its name is the same of the context), but is should be
+        explicit enough for humans, and may be considerably shorter and more
+        readable than a fully qualified one.
+        """
+        if context is UNKNOWN or not context or len(self) <= 1:
+            return self
+        if self[0] == context[0]:
+            return self[1:].contextualize(context[1:])
+        else:
+            return self
+
 ######################################################################
 # UNKNOWN Value
 ######################################################################

File epydoc/src/epydoc/docwriter/dotgraph.py

     Return a `DotGraph` that graphically displays the package
     hierarchies for the given packages.
     """
-    graph = DotGraph('Package Tree for %s' % name_list(packages),
+    graph = DotGraph('Package Tree for %s' % name_list(packages, context),
                      node_defaults={'shape':'box', 'width': 0, 'height': 0},
                      edge_defaults={'sametail':True})
     
     Return a `DotGraph` that graphically displays the package
     hierarchies for the given packages.
     """
-    graph = DotGraph('Class Hierarchy for %s' % name_list(bases),
+    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'})
     nodes = {}
     val_docs = sorted(val_docs, key=lambda d:d.canonical_name)
     for i, val_doc in enumerate(val_docs):
-        if (val_doc.canonical_name[:-1] == context.canonical_name[:-1] or
-            val_doc.canonical_name[:-1] == context.canonical_name[:]):
-            label = val_doc.canonical_name[-1]
-        else:
-            label = val_doc.canonical_name
+        label = val_doc.canonical_name.contextualize(context.canonical_name)
         node = nodes[val_doc] = DotGraphNode(label)
         graph.nodes.append(node)
         if val_doc == context:
             if url: node.attribs['href'] = url
     return nodes
 
-def name_list(api_docs):
-    names = ['%s' % d.canonical_name for d in api_docs]
+def name_list(api_docs, context=None):
+    if context is not None:
+        context = context.canonical_name
+    names = [str(d.canonical_name.contextualize(context)) for d in api_docs]
     if len(names) == 0: return ''
     if len(names) == 1: return '%s' % names[0]
     elif len(names) == 2: return '%s and %s' % (names[0], names[1])

File epydoc/src/epydoc/docwriter/html.py

                 if (doc.subclasses not in (UNKNOWN, None) and
                     len(doc.subclasses) > 0):
                     out('<dl><dt>Known Subclasses:</dt>\n<dd>\n    ')
-                    out(',\n    '.join([self.href(c, c.canonical_name[-1])
+                    out(',\n    '.join([self.href(c, context=doc)
                                         for c in doc.subclasses]))
                     out('\n</dd></dl>\n\n')
 
         if grouped_inh_vars:
             for base in doc.mro():
                 if base in grouped_inh_vars:
-                    hdr = 'Inherited from %s' % self.href(base)
+                    hdr = 'Inherited from %s' % self.href(base, context=doc)
                     tr_class = ''
                     if len([v for v in grouped_inh_vars[base]
                             if v.is_public]) == 0:
                 out('    <p class="varlist">'
                     '<span class="varlist-header">Inherited '
                     'from <code>%s</code></span>:\n' %
-                    self.href(base, self.base_label(doc, base)))
+                    self.href(base, context=doc))
                 self.write_var_list(out, public_vars)
                 out('      </p>\n')
             if private_vars and self._show_private:
                 out('    <p class="varlist">'
                     '<span class="varlist-header">Inherited '
                     'from <code>%s</code></span> (private):\n' %
-                    self.href(base, self.base_label(doc, base)))
+                    self.href(base, context=doc))
                 self.write_var_list(out, private_vars)
                 out('      </p></div>\n')
         out('    </td>\n  </tr>\n')
         >>> # === overrides ===
         >>> if var_doc.overrides not in (None, UNKNOWN):
             <dl><dt>Overrides:
-              $self.href(var_doc.overrides.value)$
+              $self.href(var_doc.overrides.value, context=var_doc)$
         >>>   if (func_doc.docstring in (None, UNKNOWN) and
         >>>       var_doc.overrides.value.docstring not in (None, UNKNOWN)):
                 <dd><em class="note">(inherited documentation)</em></dd>
     #{ Base Tree
     #////////////////////////////////////////////////////////////
 
-    def base_tree(self, doc, width=None, postfix=''):
+    def base_tree(self, doc, width=None, postfix='', context=None):
         """
         @return: The HTML code for a class's base tree.  The tree is
             drawn 'upside-down' and right justified, to allow for
             multiple inheritance.
         @rtype: C{string}
         """
-        if width == None: width = self.find_tree_width(doc)
+        if context is None:
+            context = doc
+        if width == None: width = self.find_tree_width(doc, context)
         if isinstance(doc, ClassDoc):
             bases = doc.bases
         else:
         if postfix == '':
             # [XX] use var name instead of canonical name?
             s = (' '*(width-2) + '<strong class="uidshort">'+
-                   doc.canonical_name[-1]+'</strong>\n')
+                   self.contextual_label(doc, context)+'</strong>\n')
         else: s = ''
         for i in range(len(bases)-1, -1, -1):
             base = bases[i]
-            label = self.base_label(doc, base)
+            label = self.contextual_label(base, context)
             s = (' '*(width-4-len(label)) + self.href(base, label)
                    +' --+'+postfix+'\n' + 
                    ' '*(width-4) +
                 s = (self.base_tree(base, width-4, '    '+postfix)+s)
         return s
 
-    def find_tree_width(self, doc):
+    def find_tree_width(self, doc, context):
         """
         Helper function for L{base_tree}.
         @return: The width of a base tree, when drawn
         if not isinstance(doc, ClassDoc): return 2
         width = 2
         for base in doc.bases:
-            width = max(width, len(self.base_label(doc, base))+4,
-                        self.find_tree_width(base)+4)
+            width = max(width, len(self.contextual_label(base, context))+4,
+                        self.find_tree_width(base, context)+4)
         return width
 
-    # [xx] is 'doc' what I care about here?? hm.. cuz base_tree
-    # recurses??
-    def base_label(self, doc, base):
-        if (base.canonical_name[:-1] == doc.canonical_name[:-1]):
-            return base.canonical_name[-1]
-        else:
-            return str(base.canonical_name)
-    
+    def contextual_label(self, doc, context):
+        """
+        Return the label for L{doc} to be shown in C{context}.
+        """
+        context = context.canonical_name
+        if context is not UNKNOWN:
+            context = context.container()
+            
+        return str(doc.canonical_name.contextualize(context))
+        
     #////////////////////////////////////////////////////////////
     #{ Function Signatures
     #////////////////////////////////////////////////////////////
         return None
 
     # [xx] add code to automatically do <code> wrapping or the like?
-    def href(self, target, label=None, css_class=None):
+    def href(self, target, label=None, css_class=None, context=None):
         """
         Return the HTML code for an HREF link to the given target
         (which can be a C{VariableDoc}, a C{ValueDoc}, or a
         C{DottedName}.
+        If a C{NamespaceDoc} C{context} is specified, the target label is
+        contextualized to it.
         """
         assert isinstance(target, (APIDoc, DottedName))
 
         # Pick a label, if none was given.
         if label is None:
             if isinstance(target, VariableDoc):
-                label = str(target.name)
+                label = target.name
             elif isinstance(target, ValueDoc):
-                label = str(target.canonical_name)
+                label = target.canonical_name
             elif isinstance(target, DottedName):
-                label = str(target)
+                label = target
             else:
                 raise ValueError, "bad label"
+                
+            if context is not None:
+                label = label.contextualize(context.canonical_name.container())
+                
+            label = str(label)
+            
             # Munge names for scripts & unreachable values
             if label.startswith('script-'):
                 label = label[7:] + ' (script)'

File epydoc/src/epydoc/test/apidoc.doctest

 there's a class that was used as the base class, but is not contained
 in any module or class).
     
+A dotted name can be queried into a context to obtain a reduced version:
+
+    >>> DottedName('foo.bar').contextualize(DottedName('foo'))
+    DottedName('bar')
+    >>> DottedName('foo.bar.baz.qux').contextualize(DottedName('foo.bar'))
+    DottedName('baz', 'qux')
+    >>> DottedName('foo.bar').contextualize(DottedName('baz'))
+    DottedName('foo', 'bar')
+    >>> DottedName('foo.bar').contextualize(DottedName('foo').container())
+    DottedName('foo', 'bar')
+    >>> DottedName('foo.bar').contextualize(UNKNOWN)
+    DottedName('foo', 'bar')
+    
+But a contextualization can't reduce to an empty DottedName:
+
+    >>> DottedName('foo').contextualize(DottedName('foo'))
+    DottedName('foo')
+
 APIDoc Objects
 ==============
 API documentation about Python programs is broken into small pieces,