[Epydoc-commits] SF.net SVN: epydoc: [1444] trunk/epydoc/src/epydoc
Brought to you by:
edloper
|
From: <dva...@us...> - 2007-02-10 19:26:57
|
Revision: 1444
http://svn.sourceforge.net/epydoc/?rev=1444&view=rev
Author: dvarrazzo
Date: 2007-02-10 11:26:54 -0800 (Sat, 10 Feb 2007)
Log Message:
-----------
- Added regression tests for sumarization for each markup.
- Added APIDoc.is_detailed() method, with implementation for relevant
subclasses.
- APIDoc.pyval_repr() now always return the best representation for the
object; single writers don't have to choose between parsed or introspected
representation.
- Added APIDoc.summary_pyval_repr() method to return a short version of
the Python value.
- select_variables() method can filter out (or in, if it cares) objects
requiring extra details.
- Summary lines in HTML output can be not linked to a detail box. If so,
they gain an anchor to permit being referred in place of the detail box.
- Added a link to source in summary items, which can be used for object whose
details box is missing.
- Classes, modules and variables names in the summary rendered in monotype
font as function names.
Modified Paths:
--------------
trunk/epydoc/src/epydoc/apidoc.py
trunk/epydoc/src/epydoc/docstringparser.py
trunk/epydoc/src/epydoc/docwriter/dotgraph.py
trunk/epydoc/src/epydoc/docwriter/html.py
trunk/epydoc/src/epydoc/docwriter/html_css.py
trunk/epydoc/src/epydoc/docwriter/latex.py
trunk/epydoc/src/epydoc/docwriter/plaintext.py
trunk/epydoc/src/epydoc/markup/__init__.py
trunk/epydoc/src/epydoc/markup/epytext.py
trunk/epydoc/src/epydoc/markup/javadoc.py
trunk/epydoc/src/epydoc/markup/plaintext.py
trunk/epydoc/src/epydoc/markup/restructuredtext.py
trunk/epydoc/src/epydoc/test/epytext.doctest
Added Paths:
-----------
trunk/epydoc/src/epydoc/test/javadoc.doctest
trunk/epydoc/src/epydoc/test/plaintext.doctest
trunk/epydoc/src/epydoc/test/restructuredtext.doctest
Modified: trunk/epydoc/src/epydoc/apidoc.py
===================================================================
--- trunk/epydoc/src/epydoc/apidoc.py 2007-02-08 04:17:45 UTC (rev 1443)
+++ trunk/epydoc/src/epydoc/apidoc.py 2007-02-10 19:26:54 UTC (rev 1444)
@@ -341,6 +341,11 @@
its docstring.
@type: L{ParsedDocstring<epydoc.markup.ParsedDocstring>}"""
+ other_docs = UNKNOWN
+ """@ivar: A flag indicating if the entire L{docstring} body (except tags
+ if any) is entirely included in the L{summary}.
+ @type: C{bool}"""
+
metadata = UNKNOWN
"""@ivar: Metadata about the documented item, extracted from fields in
its docstring. I{Currently} this is encoded as a list of tuples
@@ -446,7 +451,20 @@
name_cmp = cmp(self.canonical_name, other.canonical_name)
if name_cmp == 0: return -1
else: return name_cmp
-
+
+ def is_detailed(self):
+ """
+ Does this object deserve a box with extra details?
+
+ @return: True if the object needs extra details, else False.
+ @rtype: C{bool}
+ """
+ if self.other_docs is True:
+ return True
+
+ if self.metadata is not UNKNOWN:
+ return bool(self.metadata)
+
__mergeset = None
"""The set of all C{APIDoc} objects that have been merged with
this C{APIDoc} (using L{merge_and_overwrite()}). Each C{APIDoc}
@@ -655,6 +673,12 @@
else:
return [self.value]
+ def is_detailed(self):
+ if (self.value in (None, UNKNOWN)):
+ return super(VariableDoc, self).is_detailed()
+ else:
+ return self.value.is_detailed()
+
######################################################################
# Value Documentation Objects
######################################################################
@@ -699,6 +723,9 @@
reflect the actual value (e.g., if the value was modified after
the initial assignment).
@type: C{unicode}"""
+
+ summary_linelen = 75
+ """@cvar: The maximum length of a row to fit in a summary."""
#} end of "value representation" group
#{ Context
@@ -737,12 +764,8 @@
def __repr__(self):
if self.canonical_name is not UNKNOWN:
return '<%s %s>' % (self.__class__.__name__, self.canonical_name)
- elif self.pyval_repr() is not UNKNOWN:
+ else:
return '<%s %s>' % (self.__class__.__name__, self.pyval_repr())
- elif self.parse_repr is not UNKNOWN:
- return '<%s %s>' % (self.__class__.__name__, self.parse_repr)
- else:
- return '<%s>' % self.__class__.__name__
def __setstate__(self, state):
self.__dict__ = state
@@ -766,11 +789,69 @@
# Return the pickle state.
return self.__pickle_state
+ UNKNOWN_REPR = "??"
+ """@cvar: The string representation of an unknown value."""
+
def pyval_repr(self):
- if not hasattr(self, '_pyval_repr'):
- self._pyval_repr = self._get_pyval_repr()
- return self._pyval_repr
-
+ """Return a string representation of the python value.
+
+ The string representation may include data from introspection, parsing
+ and is authoritative as "the best way to represent a Python value."
+
+ @return: A nice string representation. Never C{None} nor L{UNKNOWN}
+ @rtype: C{str}
+
+ @todo: String representation can be made better here.
+ """
+ if hasattr(self, '_pyval_repr'):
+ return self._pyval_repr
+
+ rv = self._get_pyval_repr()
+ if rv is UNKNOWN:
+ rv = self.parse_repr
+
+ if rv is UNKNOWN:
+ rv = self.UNKNOWN_REPR
+
+ assert isinstance(rv, basestring)
+ self._pyval_repr = rv
+ return rv
+
+ def summary_pyval_repr(self, max_len=None):
+ """Return a short version of L{pyval_repr}, fitting on a single line.
+
+ Notice that L{GenericValueDoc.is_detailed()} uses this function to
+ return an answer leavling C{max_len} to the default value. So, if
+ a writer is to decide whether to emit a complete representation or
+ limit itself to the summary, it should call this function leaving
+ C{max_len} to its default value too, if it wants to generate consistent
+ results.
+
+ @param max_len: The maximum length allowed. If None, use
+ L{summary_linelen}
+
+ @return: the short representation and a boolean value stating if there
+ is further value to represent after such representation or not.
+
+ @rtype: C{(str, bool)}
+ """
+ ro = self.pyval_repr()
+ lo = False
+
+ # Reduce to a single line
+ if "\n" in ro:
+ ro = ro.split("\n",1)[0]
+ lo = True
+
+ # Truncate a long line
+ if max_len is None:
+ max_len = self.summary_linelen
+ if len(ro) > max_len:
+ ro = ro[:max_len-3]+'...'
+ lo = True
+
+ return (ro, lo)
+
def _get_pyval_repr(self):
"""
Return a string representation of this value based on its pyval;
@@ -801,6 +882,9 @@
"""
canonical_name = None
+ def is_detailed(self):
+ return self.summary_pyval_repr()[1]
+
class NamespaceDoc(ValueDoc):
"""
API documentation information about a singe Python namespace
@@ -857,6 +941,9 @@
APIDoc.__init__(self, **kwargs)
assert self.variables is not UNKNOWN
+ def is_detailed(self):
+ return True
+
def apidoc_links(self, **filters):
variables = filters.get('variables', True)
imports = filters.get('imports', True)
@@ -1038,7 +1125,7 @@
self.submodule_groups = self._init_grouping(elts)
def select_variables(self, group=None, value_type=None, public=None,
- imported=None):
+ imported=None, detailed=None):
"""
Return a specified subset of this module's L{sorted_variables}
list. If C{value_type} is given, then only return variables
@@ -1065,6 +1152,11 @@
always the special group name C{''}, which is used for
variables that do not belong to any group.
@type group: C{string}
+
+ @param detailed: If True (False), return only the variables
+ deserving (not deserving) a detailed informative box.
+ If C{None}, don't care.
+ @type detailed: C{bool}
"""
if (self.sorted_variables is UNKNOWN or
self.variable_groups is UNKNOWN):
@@ -1087,6 +1179,12 @@
elif imported is False:
var_list = [v for v in var_list if v.is_imported is not True]
+ # Detailed filter
+ if detailed is True:
+ var_list = [v for v in var_list if v.is_detailed() is True]
+ elif detailed is False:
+ var_list = [v for v in var_list if v.is_detailed() is not True]
+
# [xx] Modules are not currently included in any of these
# value types.
if value_type is None:
@@ -1211,8 +1309,8 @@
for seq in nonemptyseqs: # remove cand
if seq[0] == cand: del seq[0]
- def select_variables(self, group=None, value_type=None,
- inherited=None, public=None, imported=None):
+ def select_variables(self, group=None, value_type=None, inherited=None,
+ public=None, imported=None, detailed=None):
"""
Return a specified subset of this class's L{sorted_variables}
list. If C{value_type} is given, then only return variables
@@ -1257,6 +1355,11 @@
@param inherited: If C{None}, then return both inherited and
local variables; if C{True}, then return only inherited
variables; if C{False}, then return only local variables.
+
+ @param detailed: If True (False), return only the variables
+ deserving (not deserving) a detailed informative box.
+ If C{None}, don't care.
+ @type detailed: C{bool}
"""
if (self.sorted_variables is UNKNOWN or
self.variable_groups is UNKNOWN):
@@ -1284,7 +1387,13 @@
var_list = [v for v in var_list if v.is_imported is True]
elif imported is False:
var_list = [v for v in var_list if v.is_imported is not True]
-
+
+ # Detailed filter
+ if detailed is True:
+ var_list = [v for v in var_list if v.is_detailed() is True]
+ elif detailed is False:
+ var_list = [v for v in var_list if v.is_detailed() is not True]
+
if value_type is None:
return var_list
elif value_type == 'method':
@@ -1399,6 +1508,29 @@
@type: C{list}"""
#} end of "information extracted from docstrings" group
+ def is_detailed(self):
+ if super(RoutineDoc, self).is_detailed():
+ return True
+
+ if self.arg_descrs not in (None, UNKNOWN) and self.arg_descrs:
+ return True
+
+ if self.arg_types not in (None, UNKNOWN) and self.arg_types:
+ return True
+
+ if self.return_descr not in (None, UNKNOWN):
+ return True
+
+ if self.exception_descrs not in (None, UNKNOWN) and self.exception_descrs:
+ return True
+
+ if (self.decorators not in (None, UNKNOWN)
+ and [ d for d in self.decorators
+ if d not in ('classmethod', 'staticmethod') ]):
+ return True
+
+ return False
+
def all_args(self):
"""
@return: A list of the names of all arguments (positional,
@@ -1460,6 +1592,19 @@
if self.fdel not in (None, UNKNOWN): val_docs.append(self.fdel)
return val_docs
+ def is_detailed(self):
+ if super(PropertyDoc, self).is_detailed():
+ return True
+
+ if self.fget not in (None, UNKNOWN) and self.fget.pyval is not None:
+ return True
+ if self.fset not in (None, UNKNOWN) and self.fset.pyval is not None:
+ return True
+ if self.fdel not in (None, UNKNOWN) and self.fdel.pyval is not None:
+ return True
+
+ return False
+
######################################################################
## Index
######################################################################
Modified: trunk/epydoc/src/epydoc/docstringparser.py
===================================================================
--- trunk/epydoc/src/epydoc/docstringparser.py 2007-02-08 04:17:45 UTC (rev 1443)
+++ trunk/epydoc/src/epydoc/docstringparser.py 2007-02-10 19:26:54 UTC (rev 1444)
@@ -235,13 +235,15 @@
# Extract a summary
if api_doc.summary is None and api_doc.descr is not None:
- api_doc.summary = api_doc.descr.summary()
+ api_doc.summary, api_doc.other_docs = api_doc.descr.summary()
# If the summary is empty, but the return field is not, then use
# the return field to generate a summary description.
if (isinstance(api_doc, RoutineDoc) and api_doc.summary is None and
api_doc.return_descr is not None):
- api_doc.summary = RETURN_PDS + api_doc.return_descr.summary()
+ s, o = api_doc.return_descr.summary()
+ api_doc.summary = RETURN_PDS + s
+ api_doc.other_docs = o
# [XX] Make sure we don't have types/param descrs for unknown
# vars/params?
@@ -653,7 +655,7 @@
_check(api_doc, tag, arg, expect_arg=False)
api_doc.is_instvar = False
api_doc.descr = markup.ConcatenatedDocstring(api_doc.descr, descr)
- api_doc.summary = descr.summary()
+ api_doc.summary, api_doc.other_docs = descr.summary()
# Otherwise, @cvar should be used in a class.
else:
@@ -671,7 +673,7 @@
# require that there be no other descr?
api_doc.is_instvar = True
api_doc.descr = markup.ConcatenatedDocstring(api_doc.descr, descr)
- api_doc.summary = descr.summary()
+ api_doc.summary, api_doc.other_docs = descr.summary()
# Otherwise, @ivar should be used in a class.
else:
@@ -768,7 +770,7 @@
raise ValueError(REDEFINED % ('description for '+ident))
var_doc.descr = descr
if var_doc.summary in (None, UNKNOWN):
- var_doc.summary = var_doc.descr.summary()
+ var_doc.summary, var_doc.other_docs = var_doc.descr.summary()
def set_var_type(api_doc, ident, descr):
if ident not in api_doc.variables:
Modified: trunk/epydoc/src/epydoc/docwriter/dotgraph.py
===================================================================
--- trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2007-02-08 04:17:45 UTC (rev 1443)
+++ trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2007-02-10 19:26:54 UTC (rev 1444)
@@ -693,14 +693,9 @@
"""
if default is None:
return '%s' % name
- elif default.parse_repr is not UNKNOWN:
- return '%s=%s' % (name, default.parse_repr)
else:
- pyval_repr = default.pyval_repr()
- if pyval_repr is not UNKNOWN:
- return '%s=%s' % (name, pyval_repr)
- else:
- return '%s=??' % name
+ pyval_repr = default.summary_pyval_repr()[0]
+ return '%s=%s' % (name, pyval_repr)
def _qualifier_cell(self, key_label, port):
return self._QUALIFIER_CELL % (port, self.bgcolor, key_label)
Modified: trunk/epydoc/src/epydoc/docwriter/html.py
===================================================================
--- trunk/epydoc/src/epydoc/docwriter/html.py 2007-02-08 04:17:45 UTC (rev 1443)
+++ trunk/epydoc/src/epydoc/docwriter/html.py 2007-02-10 19:26:54 UTC (rev 1444)
@@ -400,7 +400,7 @@
if not isinstance(d, GenericValueDoc)]
for doc in valdocs:
if isinstance(doc, NamespaceDoc):
- # add any vars with generic vlaues; but don't include
+ # add any vars with generic values; but don't include
# inherited vars.
self.indexed_docs += [d for d in doc.variables.values() if
isinstance(d.value, GenericValueDoc)
@@ -509,6 +509,11 @@
self._mkdir(directory)
self._directory = directory
+ # Set the default value for L{ValueDoc.summary_linelen} so that
+ # L{ValueDoc.summary_pyval_repr()} return value is consistent with
+ # L{ValueDoc.is_detailed()}
+ ValueDoc.summary_linelen = self._variable_summary_linelen
+
# Write the CSS file.
self._files_written += 1
log.progress(self._files_written/self._num_files, 'epydoc.css')
@@ -1898,6 +1903,8 @@
@param container: The API documentation for the class or
module whose summary table we're writing.
"""
+ link = None # link to the source code
+
# If it's a private variable, then mark its <tr>.
if var_doc.is_public: tr_class = ''
else: tr_class = ' class="private"'
@@ -1907,24 +1914,18 @@
if isinstance(var_doc.value, RoutineDoc):
typ = self.return_type(var_doc, indent=6)
description = self.function_signature(var_doc, True, True)
+ link = self.pysrc_link(var_doc.value)
else:
typ = self.type_descr(var_doc, indent=6)
- description = self.href(var_doc)
+ description = self.summary_name(var_doc, link_name=True)
if isinstance(var_doc.value, GenericValueDoc):
- pyval_repr = var_doc.value.pyval_repr()
- if pyval_repr is not UNKNOWN:
- val_repr = pyval_repr
- else:
- val_repr = var_doc.value.parse_repr
- if val_repr is not UNKNOWN:
- val_repr = ' '.join(val_repr.strip().split())
- maxlen = self._variable_summary_linelen
- if len(val_repr) > maxlen:
- val_repr = val_repr[:maxlen-3]+'...'
- val_repr = plaintext_to_html(val_repr)
- tooltip = self.variable_tooltip(var_doc)
- description += (' = <code%s>%s</code>' %
- (tooltip, val_repr))
+ # The summary max length has been chosen setting
+ # L{ValueDoc.summary_linelen} when L{write()} was called
+ val_repr = var_doc.value.summary_pyval_repr()[0]
+ val_repr = plaintext_to_html(val_repr)
+ tooltip = self.variable_tooltip(var_doc)
+ description += (' = <code%s>%s</code>' %
+ (tooltip, val_repr))
# Add the summary to the description (if there is one).
summary = self.summary(var_doc, indent=6)
@@ -1936,11 +1937,11 @@
self.href(var_doc.container) + ")</em>")
# Write the summary line.
- self._write_summary_line(out, typ, description, tr_class)
+ self._write_summary_line(out, typ, description, tr_class, link)
_write_summary_line = compile_template(
"""
- _write_summary_line(self, out, typ, description, tr_class)
+ _write_summary_line(self, out, typ, description, tr_class, link)
""",
# /------------------------- Template -------------------------\
'''
@@ -1948,7 +1949,17 @@
<td width="15%" align="right" valign="top" class="summary">
<span class="summary-type">$typ or " "$</span>
</td><td class="summary">
- $description$
+ >>> if link is not None:
+ <table width="100%" cellpadding="0" cellspacing="0" border="0">
+ <tr>
+ <td>$description$</td>
+ <td align="right" valign="top">$link$</td>
+ </tr>
+ </table>
+ >>> #endif
+ >>> if link is None:
+ $description$
+ >>> #endif
</td>
</tr>
''')
@@ -1963,11 +1974,13 @@
if isinstance(doc, ClassDoc):
var_docs = doc.select_variables(value_type=value_type,
imported=False, inherited=False,
- public=self._public_filter)
+ public=self._public_filter,
+ detailed=True)
else:
var_docs = doc.select_variables(value_type=value_type,
imported=False,
- public=self._public_filter)
+ public=self._public_filter,
+ detailed=True)
if not var_docs: return
# Write a header
@@ -2273,15 +2286,8 @@
def variable_tooltip(self, var_doc):
if var_doc.value in (None, UNKNOWN):
return ''
- else:
- pyval_repr = var_doc.value.pyval_repr()
- if pyval_repr is not UNKNOWN:
- s = pyval_repr
- elif var_doc.value.parse_repr is not UNKNOWN:
- s = var_doc.value.parse_repr
- else:
- return ''
-
+
+ s = var_doc.value.pyval_repr()
if len(s) > self._variable_tooltip_linelen:
s = s[:self._variable_tooltip_linelen-3]+'...'
return ' title="%s"' % plaintext_to_html(s)
@@ -2290,15 +2296,8 @@
if val_doc is UNKNOWN: return ''
if val_doc.pyval is not UNKNOWN:
return self.pprint_pyval(val_doc.pyval)
- elif val_doc.pyval_repr() is not UNKNOWN:
- s = plaintext_to_html(val_doc.pyval_repr())
- elif val_doc.parse_repr is not UNKNOWN:
- s = plaintext_to_html(val_doc.parse_repr)
elif isinstance(val_doc, GenericValueDoc):
- # This *should* never happen -- GenericValueDoc's should always
- # have a pyval_repr or a parse_repr.
- log.debug('pprint_value() got GenericValueDoc w/ UNKNOWN repr')
- return ''
+ s = plaintext_to_html(val_doc.pyval_repr())
else:
s = self.href(val_doc)
return self._linewrap_html(s, self._variable_linelen,
@@ -2537,15 +2536,12 @@
'</span>(...)</span>') %
(css_class, css_class, api_doc.name))
# Get the function's name.
- if link_name:
- name = self.href(api_doc, css_class=css_class+'-name')
- else:
- name = ('<span class="%s-name">%s</span>' %
- (css_class, api_doc.name))
+ name = self.summary_name(api_doc, css_class=css_class+'-name',
+ link_name=link_name)
else:
func_doc = api_doc
name = self.href(api_doc, css_class=css_class+'-name')
-
+
if func_doc.posargs == UNKNOWN:
args = ['...']
else:
@@ -2564,6 +2560,13 @@
return ('<span class="%s">%s(%s)</span>' %
(css_class, name, ',\n '.join(args)))
+ def summary_name(self, api_doc, css_class='summary-name', link_name=False):
+ if link_name and api_doc.is_detailed():
+ return self.href(api_doc, css_class=css_class)
+ else:
+ return '<a name="%s" /><span class="%s">%s</span>' % \
+ (api_doc.name, css_class, api_doc.name)
+
# [xx] tuple args???
def func_arg(self, name, default, css_class):
name = self._arg_name(name)
@@ -2573,12 +2576,8 @@
s += ('=<span class="%s-default">%s</span>' %
(css_class, plaintext_to_html(default.parse_repr)))
else:
- pyval_repr = default.pyval_repr()
- if pyval_repr is not UNKNOWN:
- s += ('=<span class="%s-default">%s</span>' %
- (css_class, plaintext_to_html(pyval_repr)))
- else:
- s += '=<span class="%s-default">??</span>' % css_class
+ s += ('=<span class="%s-default">%s</span>' %
+ (css_class, plaintext_to_html(default.pyval_repr())))
return s
def _arg_name(self, arg):
Modified: trunk/epydoc/src/epydoc/docwriter/html_css.py
===================================================================
--- trunk/epydoc/src/epydoc/docwriter/html_css.py 2007-02-08 04:17:45 UTC (rev 1443)
+++ trunk/epydoc/src/epydoc/docwriter/html_css.py 2007-02-10 19:26:54 UTC (rev 1444)
@@ -187,6 +187,10 @@
.summary-sig-arg { color: $summary_sig_arg; }
.summary-sig-default { color: $summary_sig_default; }
+/* To render variables, classes etc. like functions */
+.summary-name { color: $summary_sig_name; font-weight: bold;
+ font-family: monospace; }
+
/* Variable values
* - In the 'variable details' sections, each varaible's value is
* listed in a 'pre.variable' box. The width of this box is
Modified: trunk/epydoc/src/epydoc/docwriter/latex.py
===================================================================
--- trunk/epydoc/src/epydoc/docwriter/latex.py 2007-02-08 04:17:45 UTC (rev 1443)
+++ trunk/epydoc/src/epydoc/docwriter/latex.py 2007-02-10 19:26:54 UTC (rev 1444)
@@ -727,12 +727,7 @@
def func_arg(self, name, default):
s = '\\textit{%s}' % plaintext_to_latex(self._arg_name(name))
if default is not None:
- if default.parse_repr is not UNKNOWN:
- s += '=\\texttt{%s}' % plaintext_to_latex(default.parse_repr)
- elif default.pyval_repr() is not UNKNOWN:
- s += '=\\texttt{%s}' % plaintext_to_latex(default.pyval_repr())
- else:
- s += '=\\texttt{??}'
+ s += '=\\texttt{%s}' % default.summary_pyval_repr()[0]
return s
def _arg_name(self, arg):
@@ -806,8 +801,7 @@
has_descr = var_doc.descr not in (None, UNKNOWN)
has_type = var_doc.type_descr not in (None, UNKNOWN)
has_repr = (var_doc.value not in (None, UNKNOWN) and
- (var_doc.value.parse_repr is not UNKNOWN or
- var_doc.value.pyval_repr() is not UNKNOWN))
+ var_doc.value.pyval_repr() != var_doc.value.UNKNOWN_REPR)
if has_descr or has_type:
out('\\raggedright ')
if has_descr:
@@ -816,10 +810,7 @@
if has_repr:
out('\\textbf{Value:} \n')
pyval_repr = var_doc.value.pyval_repr()
- if pyval_repr is not UNKNOWN:
- out(self._pprint_var_value(pyval_repr, 80))
- elif var_doc.value.parse_repr is not UNKNOWN:
- out(self._pprint_var_value(var_doc.value.parse_repr, 80))
+ out(self._pprint_var_value(pyval_repr, 80))
if has_type:
ptype = self.docstring_to_latex(var_doc.type_descr, 12).strip()
out('%s\\textit{(type=%s)}' % (' '*12, ptype))
Modified: trunk/epydoc/src/epydoc/docwriter/plaintext.py
===================================================================
--- trunk/epydoc/src/epydoc/docwriter/plaintext.py 2007-02-08 04:17:45 UTC (rev 1443)
+++ trunk/epydoc/src/epydoc/docwriter/plaintext.py 2007-02-10 19:26:54 UTC (rev 1444)
@@ -137,16 +137,8 @@
var_doc.value.canonical_name not in (None, UNKNOWN)):
out(' = %s' % var_doc.value.canonical_name)
elif var_doc.value not in (UNKNOWN, None):
- pyval_repr = var_doc.value.pyval_repr()
- if pyval_repr is not UNKNOWN:
- val_repr = pyval_repr.expandtabs()
- else:
- val_repr = var_doc.value.parse_repr
- if val_repr is not UNKNOWN:
- if len(val_repr)+len(name) > 75:
- val_repr = '%s...' % val_repr[:75-len(name)-3]
- if '\n' in val_repr: val_repr = '%s...' % (val_repr.split()[0])
- out(' = %s' % val_repr)
+ val_repr = var_doc.value.summary_pyval_repr(max_len=len(name)-75)[0]
+ out(' = %s' % val_repr.expandtabs())
out('\n')
if not verbose: return
prefix += ' ' # indent the body.
@@ -206,14 +198,8 @@
def fmt_arg(self, name, default):
if default is None:
return '%s' % name
- elif default.parse_repr is not UNKNOWN:
- return '%s=%s' % (name, default.parse_repr)
else:
- pyval_repr = default.pyval_repr()
- if pyval_repr is not UNKNOWN:
- return '%s=%s' % (name, pyval_repr)
- else:
- return '%s=??' % name
+ return '%s=%s' % (name, default.summary_pyval_repr()[0])
def write_list(self, out, heading, doc, value_type=None, imported=False,
inherited=False, prefix='', noindent=False,
Modified: trunk/epydoc/src/epydoc/markup/__init__.py
===================================================================
--- trunk/epydoc/src/epydoc/markup/__init__.py 2007-02-08 04:17:45 UTC (rev 1443)
+++ trunk/epydoc/src/epydoc/markup/__init__.py 2007-02-10 19:26:54 UTC (rev 1444)
@@ -258,12 +258,14 @@
def summary(self):
"""
- @return: A short summary of this docstring. Typically, the
- summary consists of the first sentence of the docstring.
- @rtype: L{ParsedDocstring}
+ @return: A pair consisting of a short summary of this docstring and a
+ boolean value indicating whether there is further documentation
+ in addition to the summary. Typically, the summary consists of the
+ first sentence of the docstring.
+ @rtype: (L{ParsedDocstring}, C{bool})
"""
# Default behavior:
- return self
+ return self, False
def concatenate(self, other):
"""
Modified: trunk/epydoc/src/epydoc/markup/epytext.py
===================================================================
--- trunk/epydoc/src/epydoc/markup/epytext.py 2007-02-08 04:17:45 UTC (rev 1443)
+++ trunk/epydoc/src/epydoc/markup/epytext.py 2007-02-10 19:26:54 UTC (rev 1444)
@@ -1952,7 +1952,7 @@
return childstr
def summary(self):
- if self._tree is None: return self
+ if self._tree is None: return self, False
tree = self._tree
doc = Element('epytext')
@@ -1974,8 +1974,16 @@
variables[0].children.append(str)
# If we didn't find a paragraph, return an empty epytext.
- if len(variables) == 0: return ParsedEpytextDocstring(doc)
+ if len(variables) == 0: return ParsedEpytextDocstring(doc), False
+ # Is there anything else, excluding tags, after the first variable?
+ long_docs = False
+ for var in variables[1:]:
+ if isinstance(var, Element) and var.tag == 'fieldlist':
+ continue
+ long_docs = True
+ break
+
# Extract the first sentence.
parachildren = variables[0].children
para = Element('para')
@@ -1985,10 +1993,15 @@
m = re.match(r'(\s*[\w\W]*?\.)(\s|$)', parachild)
if m:
para.children.append(m.group(1))
- return ParsedEpytextDocstring(doc)
+ long_docs |= parachild is not parachildren[-1]
+ if not long_docs:
+ other = parachild[m.end():]
+ if other and not other.isspace():
+ long_docs = True
+ return ParsedEpytextDocstring(doc), long_docs
para.children.append(parachild)
- return ParsedEpytextDocstring(doc)
+ return ParsedEpytextDocstring(doc), long_docs
def split_fields(self, errors=None):
if self._tree is None: return (self, ())
Modified: trunk/epydoc/src/epydoc/markup/javadoc.py
===================================================================
--- trunk/epydoc/src/epydoc/markup/javadoc.py 2007-02-08 04:17:45 UTC (rev 1443)
+++ trunk/epydoc/src/epydoc/markup/javadoc.py 2007-02-10 19:26:54 UTC (rev 1444)
@@ -221,12 +221,26 @@
# Jeff's hack to get summary working
def summary(self):
- m = re.match(r'(\s*[\w\W]*?\.)(\s|$)', self._docstring)
+ # Drop tags
+ doc = "\n".join([ row for row in self._docstring.split('\n')
+ if not row.lstrip().startswith('@') ])
+
+ m = re.match(r'(\s*[\w\W]*?\.)(\s|$)', doc)
if m:
- return ParsedJavadocDocstring(m.group(1))
+ other = doc[m.end():]
+ return (ParsedJavadocDocstring(m.group(1)),
+ other != '' and not other.isspace())
+
else:
- summary = self._docstring.split('\n', 1)[0]+'...'
- return ParsedJavadocDocstring(summary)
+ parts = doc.strip('\n').split('\n', 1)
+ if len(parts) == 1:
+ summary = parts[0]
+ other = False
+ else:
+ summary = parts[0] + '...'
+ other = True
+
+ return ParsedJavadocDocstring(summary), other
# def concatenate(self, other):
# if not isinstance(other, ParsedJavadocDocstring):
Modified: trunk/epydoc/src/epydoc/markup/plaintext.py
===================================================================
--- trunk/epydoc/src/epydoc/markup/plaintext.py 2007-02-08 04:17:45 UTC (rev 1443)
+++ trunk/epydoc/src/epydoc/markup/plaintext.py 2007-02-10 19:26:54 UTC (rev 1444)
@@ -53,10 +53,19 @@
def summary(self):
m = re.match(r'(\s*[\w\W]*?\.)(\s|$)', self._text)
if m:
- return ParsedPlaintextDocstring(m.group(1), verbatim=0)
+ other = self._text[m.end():]
+ return (ParsedPlaintextDocstring(m.group(1), verbatim=0),
+ other != '' and not other.isspace())
else:
- summary = self._text.split('\n', 1)[0]+'...'
- return ParsedPlaintextDocstring(summary, verbatim=0)
+ parts = self._text.strip('\n').split('\n', 1)
+ if len(parts) == 1:
+ summary = parts[0]
+ other = False
+ else:
+ summary = parts[0] + '...'
+ other = True
+
+ return ParsedPlaintextDocstring(summary, verbatim=0), other
# def concatenate(self, other):
# if not isinstance(other, ParsedPlaintextDocstring):
Modified: trunk/epydoc/src/epydoc/markup/restructuredtext.py
===================================================================
--- trunk/epydoc/src/epydoc/markup/restructuredtext.py 2007-02-08 04:17:45 UTC (rev 1443)
+++ trunk/epydoc/src/epydoc/markup/restructuredtext.py 2007-02-10 19:26:54 UTC (rev 1444)
@@ -174,7 +174,7 @@
visitor = _SummaryExtractor(self._document)
try: self._document.walk(visitor)
except docutils.nodes.NodeFound: pass
- return visitor.summary
+ return visitor.summary, bool(visitor.other_docs)
# def concatenate(self, other):
# result = self._document.copy()
@@ -278,12 +278,16 @@
def __init__(self, document):
NodeVisitor.__init__(self, document)
self.summary = None
+ self.other_docs = None
def visit_document(self, node):
self.summary = None
def visit_paragraph(self, node):
- if self.summary is not None: return
+ if self.summary is not None:
+ # found a paragraph after the first one
+ self.other_docs = True
+ raise docutils.nodes.NodeFound('Found summary')
summary_pieces = []
@@ -293,6 +297,9 @@
m = re.match(r'(\s*[\w\W]*?\.)(\s|$)', child.data)
if m:
summary_pieces.append(docutils.nodes.Text(m.group(1)))
+ other = child.data[m.end():]
+ if other and not other.isspace():
+ self.other_docs = True
break
summary_pieces.append(child)
@@ -301,8 +308,10 @@
summary_doc[:] = [summary_para]
summary_para[:] = summary_pieces
self.summary = ParsedRstDocstring(summary_doc)
- raise docutils.nodes.NodeFound('Found summary')
+ def visit_field(self, node):
+ raise SkipNode
+
def unknown_visit(self, node):
'Ignore all unknown nodes'
Modified: trunk/epydoc/src/epydoc/test/epytext.doctest
===================================================================
--- trunk/epydoc/src/epydoc/test/epytext.doctest 2007-02-08 04:17:45 UTC (rev 1443)
+++ trunk/epydoc/src/epydoc/test/epytext.doctest 2007-02-10 19:26:54 UTC (rev 1444)
@@ -25,10 +25,10 @@
>>> print testparse("""
... this is one paragraph.
- ...
+ ...
... This is
... another.
- ...
+ ...
... This is a third""")
<para>this is one paragraph.</para>
<para>This is another.</para>
@@ -38,7 +38,7 @@
>>> print testparse("""
... This is a paragraph.
- ...
+ ...
... @foo: This is a field.""")
<para>This is a paragraph.</para>
<fieldlist><field><tag>foo</tag>
@@ -79,7 +79,7 @@
>>> print testparse("""
... This is a paragraph.
- ...
+ ...
... - This is a list item.""")
Traceback (most recent call last):
StructuringError: Line 4: Lists must be indented.
@@ -102,7 +102,7 @@
... This is a paragraph.
... - This is a list item.
... Hello.
- ...
+ ...
... - Sublist item""")
Traceback (most recent call last):
StructuringError: Line 6: Lists must be indented.
@@ -137,9 +137,9 @@
>>> print testparse("""
... This is a paragraph.
- ...
+ ...
... - This is a list item.
- ...
+ ...
... This is a paragraph""")
<para>This is a paragraph.</para>
<ulist><li><para>This is a list item.</para></li></ulist>
@@ -147,7 +147,7 @@
>>> print testparse("""
... This is a paragraph.
- ...
+ ...
... - This is a list item.
... This is a paragraph""")
<para>This is a paragraph.</para>
@@ -186,7 +186,7 @@
... checkparse('%s\n%s' % (li1, p), ONELIST+PARA)
... checkparse('%s\n%s\n%s' % (p, li1, p),
... PARA+ONELIST+PARA)
- ...
+ ...
... for li2 in (LI1, LI2, LI3, LI4):
... checkparse('%s\n%s' % (li1, li2), TWOLIST)
... checkparse('%s\n%s\n%s' % (p, li1, li2), PARA+TWOLIST)
@@ -223,3 +223,62 @@
... checkparse(nl1+indent+LI, ONELIST)
... for nl2 in ('\n', '\n\n'):
... checkparse(nl1+indent+LI+nl2+indent+LI, TWOLIST)
+
+Summary
+=======
+The implementation of the summaization function works as expected.
+
+>>> from epydoc.markup import epytext
+>>> def getsummary(s):
+... p = epytext.parse_docstring(s, [])
+... s, o = p.summary()
+... s = s.to_plaintext(None).strip()
+... return s, o
+
+Let's not lose anything!
+
+>>> getsummary("Single line")
+('Single line', False)
+
+>>> getsummary("Single line.")
+('Single line.', False)
+
+>>> getsummary("""
+... Single line C{with} period.
+... """)
+('Single line with period.', False)
+
+>>> getsummary("""
+... Single line C{with }period.
+...
+... @type: Also with a tag.
+... """)
+('Single line with period.', False)
+
+>>> getsummary("""
+... Other lines C{with} period.
+... This is attached
+... """)
+('Other lines with period.', True)
+
+>>> getsummary("""
+... Other lines C{with} period.
+...
+... This is detached
+...
+... @type: Also with a tag.
+... """)
+('Other lines with period.', True)
+
+>>> getsummary("""
+... Other lines without period
+... This is attached
+... """)
+('Other lines without period This is attached', False)
+
+>>> getsummary("""
+... Other lines without period
+...
+... This is detached
+... """)
+('Other lines without period...', True)
Added: trunk/epydoc/src/epydoc/test/javadoc.doctest
===================================================================
--- trunk/epydoc/src/epydoc/test/javadoc.doctest (rev 0)
+++ trunk/epydoc/src/epydoc/test/javadoc.doctest 2007-02-10 19:26:54 UTC (rev 1444)
@@ -0,0 +1,68 @@
+Regression Testing for plaintext
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Summary
+=======
+The implementation of the summaization function works as expected.
+
+>>> from epydoc.markup import javadoc
+>>> def getsummary(s):
+... p = javadoc.parse_docstring(s, [])
+... s, o = p.summary()
+... s = s.to_plaintext(None).strip()
+... return s, o
+
+#Let's not lose anything!
+
+>>> getsummary("Single line")
+('Single line', False)
+
+>>> getsummary("Single line.")
+('Single line.', False)
+
+>>> getsummary("""
+... Single line <i>with</i> period.
+... """)
+('Single line <i>with</i> period.', False)
+
+>>> getsummary("""
+... Single line <i>with</i> period.
+...
+... @type Also with a tag.
+... """)
+('Single line <i>with</i> period.', False)
+
+>>> getsummary("""
+... Other lines <i>with</i> period.
+... This is attached
+... """)
+('Other lines <i>with</i> period.', True)
+
+>>> getsummary("""
+... Other lines <i>with</i> period.
+...
+... This is detached
+...
+... @type Also with a tag.
+... """)
+('Other lines <i>with</i> period.', True)
+
+>>> getsummary("""
+... Other lines without period
+... This is attached
+... """)
+('Other lines without period...', True)
+
+>>> getsummary("""
+... Other lines without period
+...
+... This is detached
+... """)
+('Other lines without period...', True)
+
+>>> getsummary("""
+... Single line <i>without</i> period
+...
+... @type Also with a tag.
+... """)
+('Single line <i>without</i> period', False)
Added: trunk/epydoc/src/epydoc/test/plaintext.doctest
===================================================================
--- trunk/epydoc/src/epydoc/test/plaintext.doctest (rev 0)
+++ trunk/epydoc/src/epydoc/test/plaintext.doctest 2007-02-10 19:26:54 UTC (rev 1444)
@@ -0,0 +1,52 @@
+Regression Testing for plaintext
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Summary
+=======
+The implementation of the summaization function works as expected.
+
+>>> from epydoc.markup import plaintext
+>>> def getsummary(s):
+... p = plaintext.parse_docstring(s, [])
+... s, o = p.summary()
+... s = s.to_plaintext(None).strip()
+... return s, o
+
+Let's not lose anything!
+
+>>> getsummary("Single line")
+('Single line', False)
+
+>>> getsummary("Single line.")
+('Single line.', False)
+
+>>> getsummary("""
+... Single line with period.
+... """)
+('Single line with period.', False)
+
+>>> getsummary("""
+... Other lines with period.
+... This is attached
+... """)
+('Other lines with period.', True)
+
+>>> getsummary("""
+... Other lines with period.
+...
+... This is detached
+... """)
+('Other lines with period.', True)
+
+>>> getsummary("""
+... Other lines without period
+... This is attached
+... """)
+('Other lines without period...', True)
+
+>>> getsummary("""
+... Other lines without period
+...
+... This is detached
+... """)
+('Other lines without period...', True)
Added: trunk/epydoc/src/epydoc/test/restructuredtext.doctest
===================================================================
--- trunk/epydoc/src/epydoc/test/restructuredtext.doctest (rev 0)
+++ trunk/epydoc/src/epydoc/test/restructuredtext.doctest 2007-02-10 19:26:54 UTC (rev 1444)
@@ -0,0 +1,77 @@
+Regression Testing for plaintext
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Summary
+=======
+The implementation of the summaization function works as expected.
+
+>>> from epydoc.markup import restructuredtext
+>>> def getsummary(s):
+... p = restructuredtext.parse_docstring(s, [])
+... s, o = p.summary()
+... s = s.to_plaintext(None).strip()
+... return s, o
+
+#Let's not lose anything!
+
+>>> getsummary("Single line")
+(u'Single line', False)
+
+>>> getsummary("Single line.")
+(u'Single line.', False)
+
+>>> getsummary("""
+... Single line *with* period.
+... """)
+(u'Single line with period.', False)
+
+>>> getsummary("""
+... Single line `with` period.
+...
+... :type: Also with a tag.
+... """)
+(u'Single line with period.', False)
+
+>>> getsummary("""
+... Other lines **with** period.
+... This is attached
+... """)
+(u'Other lines with period.', True)
+
+>>> getsummary("""
+... Other lines *with* period.
+...
+... This is detached
+...
+... :type: Also with a tag.
+... """)
+(u'Other lines with period.', True)
+
+>>> getsummary("""
+... Other lines without period
+... This is attached
+... """)
+(u'Other lines without period\nThis is attached', False)
+
+>>> getsummary("""
+... Other lines without period
+...
+... This is detached
+... """)
+(u'Other lines without period...', True)
+
+>>> getsummary("""
+... Single line *without* period
+...
+... :type: Also with a tag.
+... """)
+(u'Single line without period', False)
+
+>>> getsummary("""
+... This is the first line.
+...
+... :type: Also with a tag.
+...
+... Other stuff after a tag.
+... """)
+(u'This is the first line.', True)
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|