[Epydoc-commits] SF.net SVN: epydoc: [1473] trunk/epydoc/src/epydoc
Brought to you by:
edloper
From: <ed...@us...> - 2007-02-13 19:46:10
|
Revision: 1473 http://svn.sourceforge.net/epydoc/?rev=1473&view=rev Author: edloper Date: 2007-02-13 11:46:05 -0800 (Tue, 13 Feb 2007) Log Message: ----------- - Changed ValueDoc.pyval_repr and summary_pval_repr to return ParsedDocstring's instead of string. These ParsedDocstrings are generated by epydoc.markup.pyval_repr. Instead of returning a separate value to indicate whether there's left-over (for GenericValueDoc.is_detailed), add an 'is_complete' attribute to the parsed docstring. - Defer importing of dotgraph from epydoc.markup.epytext to avoid a circular dependency. Modified Paths: -------------- trunk/epydoc/src/epydoc/apidoc.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/epytext.py trunk/epydoc/src/epydoc/markup/pyval_repr.py trunk/epydoc/src/epydoc/test/docbuilder.doctest trunk/epydoc/src/epydoc/test/pyval_repr.doctest Modified: trunk/epydoc/src/epydoc/apidoc.py =================================================================== --- trunk/epydoc/src/epydoc/apidoc.py 2007-02-13 15:27:45 UTC (rev 1472) +++ trunk/epydoc/src/epydoc/apidoc.py 2007-02-13 19:46:05 UTC (rev 1473) @@ -44,6 +44,7 @@ import __builtin__ from epydoc.compat import * # Backwards compatibility from epydoc.util import decode_with_backslashreplace, py_src_filename +import epydoc.markup.pyval_repr ###################################################################### # Dotted Names @@ -726,8 +727,30 @@ the initial assignment). @type: C{unicode}""" - summary_linelen = 75 - """@cvar: The maximum length of a row to fit in a summary.""" + REPR_MAXLINES = 5 + """@cvar: The maximum number of lines of text that should be + generated by L{pyval_repr()}. If the string representation does + not fit in this number of lines, an ellpsis marker (...) will + be placed at the end of the formatted representation.""" + + REPR_LINELEN = 75 + """@cvar: The maximum number of characters for lines of text that + should be generated by L{pyval_repr()}. Any lines that exceed + this number of characters will be line-wrappped; The S{crarr} + symbol will be used to indicate that the line was wrapped.""" + + SUMMARY_REPR_LINELEN = 75 + """@cvar: The maximum number of characters for the single-line + text representation generated by L{summary_pyval_repr()}. If + the value's representation does not fit in this number of + characters, an ellipsis marker (...) will be placed at the end + of the formatted representation.""" + + REPR_MIN_SCORE = 0 + """@cvar: The minimum score that a value representation based on + L{pyval} should have in order to be used instead of L{parse_repr} + as the canonical representation for this C{ValueDoc}'s value. + @see: L{epydoc.markup.pyval_repr}""" #} end of "value representation" group #{ Context @@ -767,7 +790,8 @@ if self.canonical_name is not UNKNOWN: return '<%s %s>' % (self.__class__.__name__, self.canonical_name) else: - return '<%s %s>' % (self.__class__.__name__, self.pyval_repr()) + return '<%s %s>' % (self.__class__.__name__, + self.summary_pyval_repr().to_plaintext(None)) def __setstate__(self, state): self.__dict__ = state @@ -782,101 +806,64 @@ # as a private attribute, so we can reuse it later, since # merged objects need to share a single dictionary. if not hasattr(self, '_ValueDoc__pickle_state'): + # Make sure __pyval_repr & __summary_pyval_repr are cached: + self.pyval_repr(), self.summary_pyval_repr() + # Construct the dictionary; leave out 'pyval'. self.__pickle_state = self.__dict__.copy() self.__pickle_state['pyval'] = UNKNOWN - self.__pickle_state['_ValueDoc__pyval_repr'] = self.pyval_repr() if not isinstance(self, GenericValueDoc): assert self.__pickle_state != {} # Return the pickle state. return self.__pickle_state - UNKNOWN_REPR = "??" - """@cvar: The string representation of an unknown value.""" - - STOCK_REPR = re.compile(r"(?i)^<.+\bat\b.+[0-9a-f]+.*>$") - """Match the return of repr() for object that didn't customize it.""" - + #{ Value Representation def pyval_repr(self): - """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 + Return a formatted representation of the Python object + described by this C{ValueDoc}. This representation may + include data from introspection or parsing, and is authorative + as 'the best way to represent a Python value.' Any lines that + go beyond L{REPR_LINELEN} characters will be wrapped; and if + the representation as a whole takes more than L{REPR_MAXLINES} + lines, then it will be truncated (with an ellipsis marker). + This function will never return L{UNKNOWN} or C{None}. + + @rtype: L{ColorizedPyvalRepr} + """ + # Use self.__pyval_repr to cache the result. + if not hasattr(self, '_ValueDoc__pyval_repr'): + self.__pyval_repr = epydoc.markup.pyval_repr.colorize_pyval( + self.pyval, self.parse_repr, self.REPR_MIN_SCORE, + self.REPR_LINELEN, self.REPR_MAXLINES, linebreakok=True) + return self.__pyval_repr - rv = self._get_pyval_repr() - if rv is UNKNOWN: - rv = self.parse_repr - - elif self.STOCK_REPR.match(rv) and self.parse_repr is not 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 single-line formatted representation of the Python + object described by this C{ValueDoc}. This representation may + include data from introspection or parsing, and is authorative + as 'the best way to summarize a Python value.' If the + representation takes more then L{SUMMARY_REPR_LINELEN} + characters, then it will be truncated (with an ellipsis + marker). This function will never return L{UNKNOWN} or + C{None}. + + @rtype: L{ColorizedPyvalRepr} """ - Return a string representation of this value based on its pyval; - or UNKNOWN if we don't succeed. This should probably eventually - be replaced by more of a safe-repr variant. - """ - if self.pyval is UNKNOWN: - if hasattr(self, '_ValueDoc__pyval_repr'): - return self.__pyval_repr # used after unpickling. - return UNKNOWN - try: - s = '%r' % (self.pyval,) - if isinstance(s, str): - s = decode_with_backslashreplace(s) - return s - except KeyboardInterrupt: raise - except: return UNKNOWN + # If max_len is specified, then do *not* cache the result. + if max_len is not None: + return epydoc.markup.pyval_repr.colorize_pyval( + self.pyval, self.parse_repr, self.REPR_MIN_SCORE, + max_len, maxlines=1, linebreakok=False) + + # Use self.__summary_pyval_repr to cache the result. + if not hasattr(self, '_ValueDoc__summary_pyval_repr'): + self.__summary_pyval_repr = epydoc.markup.pyval_repr.colorize_pyval( + self.pyval, self.parse_repr, self.REPR_MIN_SCORE, + self.SUMMARY_REPR_LINELEN, maxlines=1, linebreakok=False) + return self.__summary_pyval_repr + #} end of "value representation" group def apidoc_links(self, **filters): return [] @@ -891,7 +878,7 @@ canonical_name = None def is_detailed(self): - return self.summary_pyval_repr()[1] + return (not self.summary_pyval_repr().is_complete) class NamespaceDoc(ValueDoc): """ Modified: trunk/epydoc/src/epydoc/docwriter/dotgraph.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2007-02-13 15:27:45 UTC (rev 1472) +++ trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2007-02-13 19:46:05 UTC (rev 1473) @@ -694,7 +694,7 @@ if default is None: return '%s' % name else: - pyval_repr = default.summary_pyval_repr()[0] + pyval_repr = default.summary_pyval_repr().to_plaintext(None) return '%s=%s' % (name, pyval_repr) def _qualifier_cell(self, key_label, port): Modified: trunk/epydoc/src/epydoc/docwriter/html.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/html.py 2007-02-13 15:27:45 UTC (rev 1472) +++ trunk/epydoc/src/epydoc/docwriter/html.py 2007-02-13 19:46:05 UTC (rev 1473) @@ -19,7 +19,7 @@ import __builtin__ from epydoc.apidoc import * import epydoc.docstringparser -import time, epydoc, epydoc.markup +import time, epydoc, epydoc.markup, epydoc.markup.epytext from epydoc.docwriter.html_colorize import colorize_re from epydoc.docwriter.html_colorize import PythonSourceColorizer from epydoc.docwriter import html_colorize @@ -330,7 +330,7 @@ """Max line length for variable values""" self._variable_summary_linelen = \ - kwargs.get('variable_summary_linelength', 55) + kwargs.get('variable_summary_linelength', 65) """Max length for variable value summaries""" self._variable_tooltip_linelen = \ @@ -501,6 +501,21 @@ # For progress reporting: self._files_written = 0. + # Set the default values for ValueDoc formatted representations. + orig_valdoc_defaults = (ValueDoc.SUMMARY_REPR_LINELEN, + ValueDoc.REPR_LINELEN, + ValueDoc.REPR_MAXLINES) + ValueDoc.SUMMARY_REPR_LINELEN = self._variable_summary_linelen + ValueDoc.REPR_LINELEN = self._variable_linelen + ValueDoc.REPR_MAXLINES = self._variable_maxlines + + # Use an image for the crarr symbol. + from epydoc.markup.epytext import ParsedEpytextDocstring + orig_crarr_html = ParsedEpytextDocstring.SYMBOL_TO_HTML['crarr'] + ParsedEpytextDocstring.SYMBOL_TO_HTML['crarr'] = ( + r'<span class="variable-linewrap">' + r'<img src="crarr.png" alt="\" /></span>') + # Keep track of failed xrefs, and report them at the end. self._failed_xrefs = {} @@ -509,11 +524,6 @@ 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') @@ -524,7 +534,7 @@ log.progress(self._files_written/self._num_files, 'epydoc.js') self.write_javascript(directory) - # Write images. + # Write images self.write_images(directory) # Build the indices. @@ -656,6 +666,11 @@ "wrote %d files" % (self._num_files, int(self._files_written))) + # Restore defaults that we changed. + (ValueDoc.SUMMARY_REPR_LINELEN, ValueDoc.REPR_LINELEN, + ValueDoc.REPR_MAXLINES) = orig_valdoc_defaults + ParsedEpytextDocstring.SYMBOL_TO_HTML['crarr'] = orig_crarr_html + def _write(self, write_func, directory, filename, *args): # Display our progress. self._files_written += 1 @@ -1920,12 +1935,12 @@ description = self.summary_name(var_doc, link_name=True) if isinstance(var_doc.value, GenericValueDoc): # 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) + # L{ValueDoc.SUMMARY_REPR_LINELEN} in the constructor + max_len=self._variable_summary_linelen-3-len(var_doc.name) + val_repr = var_doc.value.summary_pyval_repr(max_len) tooltip = self.variable_tooltip(var_doc) description += (' = <code%s>%s</code>' % - (tooltip, val_repr)) + (tooltip, val_repr.to_html(None))) # Add the summary to the description (if there is one). summary = self.summary(var_doc, indent=6) @@ -2058,9 +2073,7 @@ if isinstance(val_doc, RoutineDoc): return self.function_signature(val_doc, True, True) elif isinstance(val_doc, GenericValueDoc): - return ('<table><tr><td><pre class="variable">\n' + - self.pprint_value(val_doc) + - '\n</pre></td></tr></table>\n') + return self.pprint_value(val_doc) else: return self.href(val_doc) else: @@ -2272,9 +2285,7 @@ >>> self.write_standard_fields(out, var_doc) >>> if var_doc.value is not UNKNOWN: <dl><dt>Value:</dt> - <dd><table><tr><td><pre class="variable"> - $self.pprint_value(var_doc.value)$ - </pre></td></tr></table></dd> + <dd>$self.pprint_value(var_doc.value)$</dd> </dl> >>> #endif </dd></dl> @@ -2286,168 +2297,21 @@ def variable_tooltip(self, var_doc): if var_doc.value in (None, UNKNOWN): return '' - - s = var_doc.value.pyval_repr() + s = var_doc.value.pyval_repr().to_plaintext(None) if len(s) > self._variable_tooltip_linelen: s = s[:self._variable_tooltip_linelen-3]+'...' return ' title="%s"' % plaintext_to_html(s) def pprint_value(self, val_doc): - if val_doc is UNKNOWN: return '' - if val_doc.pyval is not UNKNOWN: - return self.pprint_pyval(val_doc.pyval) + if val_doc is UNKNOWN: + return '??' elif isinstance(val_doc, GenericValueDoc): - s = plaintext_to_html(val_doc.pyval_repr()) + return ('<table><tr><td><pre class="variable">\n' + + val_doc.pyval_repr().to_html(None) + + '\n</pre></td></tr></table>\n') else: - s = self.href(val_doc) - return self._linewrap_html(s, self._variable_linelen, - self._variable_maxlines) + return self.href(val_doc) - def pprint_pyval(self, pyval): - # Handle the most common cases first. The following types - # will never need any line wrapping, etc; and they cover most - # variable values (esp int, for constants). I leave out - # LongType on purpose, since long values may need line- - # wrapping. - if (type(pyval) is types.IntType or type(pyval) is types.FloatType or - type(pyval) is types.NoneType or type(pyval) is types.ComplexType): - # none of these should contain '&', '<' or '>'. - vstr = repr(pyval) - return vstr + ' ' * (self._variable_linelen-len(vstr)) - - # For strings, use repr. Use tripple-quoted-strings where - # appropriate. - elif isinstance(pyval, basestring): - vstr = repr(pyval) - # Find the left quote. - lquote = vstr.find(vstr[-1]) - # Use tripple quotes if the string is multi-line: - if vstr.find(r'\n') >= 0: - body = vstr[lquote+1:-1].replace(r'\n', '\n') - vstr = ('<span class="variable-quote">'+vstr[:lquote]+ - vstr[lquote]*3+'</span>'+ - plaintext_to_html(body) + - '<span class="variable-quote">'+vstr[-1]*3+'</span>') - # Use single quotes if the string is single-line: - else: - vstr = ('<span class="variable-quote">'+vstr[:lquote+1]+ - '</span>'+ plaintext_to_html(vstr[lquote+1:-1])+ - '<span class="variable-quote">'+vstr[-1:]+'</span>') - - # For lists, tuples, and dicts, use pprint. When possible, - # restrict the amount of stuff that pprint needs to look at, - # since pprint is surprisingly slow. - elif type(pyval) is types.TupleType or type(pyval) is types.ListType: - try: vstr = repr(pyval) - except: vstr = '...' - if len(vstr) > self._variable_linelen: - vstr = pprint.pformat(pyval[:self._variable_maxlines+1]) - vstr = plaintext_to_html(vstr) - elif type(pyval) is type({}): - try: vstr = repr(pyval) - except: vstr = '...' - if len(vstr) > self._variable_linelen: - if len(pyval) < self._variable_maxlines+50: - vstr = pprint.pformat(pyval) - else: - shortval = {} - for (k,v) in pyval.items()[:self._variable_maxlines+1]: - shortval[k]=v - vstr = pprint.pformat(shortval) - vstr = plaintext_to_html(vstr) - - # For regexps, use colorize_re. - elif type(pyval).__name__ == 'SRE_Pattern': - try: vstr = colorize_re(pyval) - except TypeError, sre_constants.error: - try: vstr = plaintext_to_html(repr(pyval)) - except: vstr = '...' - - # For other objects, use repr to generate a representation. - else: - try: vstr = plaintext_to_html(repr(pyval)) - except: vstr = '...' - - # Encode vstr, if necessary. - if isinstance(vstr, str): - vstr = decode_with_backslashreplace(vstr) - - # Do line-wrapping. - return self._linewrap_html(vstr, self._variable_linelen, - self._variable_maxlines) - - def _linewrap_html(self, s, linelen, maxlines): - """ - Add line-wrapping to the HTML string C{s}. Line length is - determined by C{linelen}; and the maximum number of - lines to display is determined by C{maxlines}. This - function treats HTML entities (e.g., C{&}) as single - characters; and ignores HTML tags (e.g., C{<p>}). - """ - LINEWRAP_MARKER = (r'<span class="variable-linewrap">' - '<img src="crarr.png" alt="\" /></span>') - ELLIPSIS_MARKER = r'<span class="variable-ellipsis">...</span>' - - open_elements = [] # tag stack - lines = [] - start = end = cnum = 0 - while len(lines) <= maxlines and end < len(s): - # Skip over HTML tags. - if s[end] == '<': - newend = s.find('>', end) - tag = s[end+1:newend] - if tag[-1]!="/": - # non-empty tag - tagname = tag.split(None,1)[0] - if tagname[0] == "/": - open_elements.pop() - else: - open_elements.append(tagname) - end = newend - cnum -= 1 - - # HTML entities just count as 1 char. - elif s[end] == '&': - end = s.find(';', end) - - # Go on to the next character. - cnum += 1 - end += 1 - - # Check for end-of-line. - if s[end-1] == '\n': - lines.append(s[start:end-1]) - cnum = 0 - start = end - - # Check for line-wrap - if cnum == linelen and end<len(s) and s[end] != '\n': - if maxlines == 1: - closing_tags = "" - for tag in open_elements: - closing_tags += "</%s>" % (tag,) - return s[start:end]+closing_tags+ELLIPSIS_MARKER - lines.append(s[start:end]+LINEWRAP_MARKER) - cnum = 0 - start = end - - # Add on anything that's left. - if end == len(s): - lines.append(s[start:end]) - - # Use the ellipsis marker if the string is too long. - if len(lines) > maxlines: - closing_tags = "" - for tag in open_elements: - closing_tags += "</%s>" % (tag,) - lines[-1] = closing_tags+ELLIPSIS_MARKER - cnum = 3 - - # Pad the last line to linelen. - lines[-1] += ' '*(linelen-cnum+1) - - return ('\n').join(lines) - #//////////////////////////////////////////////////////////// #{ Base Tree #//////////////////////////////////////////////////////////// @@ -2573,8 +2437,7 @@ s = '<span class="%s-arg">%s</span>' % (css_class, name) if default is not None: s += ('=<span class="%s-default">%s</span>' % - (css_class, plaintext_to_html( - default.summary_pyval_repr()[0]))) + (css_class, default.summary_pyval_repr().to_html(None))) 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-13 15:27:45 UTC (rev 1472) +++ trunk/epydoc/src/epydoc/docwriter/html_css.py 2007-02-13 19:46:05 UTC (rev 1473) @@ -69,6 +69,7 @@ font-weight: bold; } h3 { font-size: +110%; font-style: italic; font-weight: normal; } +code { font-size: 100%; } /* Page Header & Footer * - The standard page header consists of a navigation bar (with @@ -210,6 +211,10 @@ .variable-linewrap { color: $variable_linewrap; font-weight: bold; } .variable-ellipsis { color: $variable_ellipsis; font-weight: bold; } .variable-quote { color: $variable_quote; font-weight: bold; } +.variable-group { color: $variable_group; font-weight: bold; } +.variable-op { color: $variable_op; font-weight: bold; } +.variable-string { color: $variable_string; } +.variable-unknown { color: $variable_unknown; font-weight: bold; } .re { color: $re; } .re-char { color: $re_char; } .re-op { color: $re_op; } @@ -384,6 +389,10 @@ variable_linewrap = '#604000', variable_ellipsis = '#604000', variable_quote = '#604000', + variable_group = '#008000', + variable_string = '#006030', + variable_op = '#604000', + variable_unknown = '#a00000', re = '#000000', re_char = '#006030', re_op = '#600000', Modified: trunk/epydoc/src/epydoc/docwriter/latex.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/latex.py 2007-02-13 15:27:45 UTC (rev 1472) +++ trunk/epydoc/src/epydoc/docwriter/latex.py 2007-02-13 19:46:05 UTC (rev 1473) @@ -144,6 +144,14 @@ # For progress reporting: self._files_written = 0. + # Set the default values for ValueDoc formatted representations. + orig_valdoc_defaults = (ValueDoc.SUMMARY_REPR_LINELEN, + ValueDoc.REPR_LINELEN, + ValueDoc.REPR_MAXLINES) + ValueDoc.SUMMARY_REPR_LINELEN = 60 + ValueDoc.REPR_LINELEN = 52 + ValueDoc.REPR_MAXLINES = 5 + # Create destination directories, if necessary if not directory: directory = os.curdir self._mkdir(directory) @@ -162,6 +170,10 @@ filename = '%s-class.tex' % val_doc.canonical_name self._write(self.write_class, directory, filename, val_doc) + # Restore defaults that we changed. + (ValueDoc.SUMMARY_REPR_LINELEN, ValueDoc.REPR_LINELEN, + ValueDoc.REPR_MAXLINES) = orig_valdoc_defaults + def _write(self, write_func, directory, filename, *args): # Display our progress. self._files_written += 1 @@ -727,8 +739,7 @@ def func_arg(self, name, default): s = '\\textit{%s}' % plaintext_to_latex(self._arg_name(name)) if default is not None: - s += '=\\texttt{%s}' % plaintext_to_latex( - default.summary_pyval_repr()[0]) + s += '=\\texttt{%s}' % default.summary_pyval_repr().to_latex(None) return s def _arg_name(self, arg): @@ -801,31 +812,28 @@ out(' & ') 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.pyval_repr() != var_doc.value.UNKNOWN_REPR) - if has_descr or has_type: - out('\\raggedright ') + out('\\raggedright ') if has_descr: out(self.docstring_to_latex(var_doc.descr, 10).strip()) if has_type or has_repr: out('\n\n') - if has_repr: - out('\\textbf{Value:} \n') - pyval_repr = var_doc.value.pyval_repr() - out(self._pprint_var_value(pyval_repr, 80)) + out('\\textbf{Value:} \n') + out(self._pprint_var_value(var_doc)) if has_type: ptype = self.docstring_to_latex(var_doc.type_descr, 12).strip() out('%s\\textit{(type=%s)}' % (' '*12, ptype)) out('&\\\\\n') out('\\cline{1-2}\n') - def _pprint_var_value(self, s, maxwidth=100): - if len(s) > maxwidth: s = s[:maxwidth-3] + '...' - if '\n' in s: + def _pprint_var_value(self, var_doc): + pyval_repr = var_doc.value.pyval_repr().to_latex(None) + if '\n' in pyval_repr: return ('\\begin{alltt}\n%s\\end{alltt}' % - plaintext_to_latex(s, nbsp=False, breakany=True)) + pyval_repr) + #plaintext_to_latex(pyval_repr, nbsp=False, breakany=True)) else: - return '{\\tt %s}' % plaintext_to_latex(s, nbsp=True, - breakany=True) + return '{\\tt %s}' % pyval_repr + #plaintext_to_latex(pyval_repr, nbsp=True, + #breakany=True) def write_property_list_line(self, out, var_doc): prop_doc = var_doc.value Modified: trunk/epydoc/src/epydoc/docwriter/plaintext.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/plaintext.py 2007-02-13 15:27:45 UTC (rev 1472) +++ trunk/epydoc/src/epydoc/docwriter/plaintext.py 2007-02-13 19:46:05 UTC (rev 1473) @@ -15,10 +15,12 @@ import re class PlaintextWriter: - def write(self, api_doc): + def write(self, api_doc, **options): result = [] out = result.append + self._cols = options.get('cols', 75) + try: if isinstance(api_doc, ModuleDoc): self.write_module(out, api_doc) @@ -128,7 +130,7 @@ self.write_list(out, 'Inherited Nested Classes', class_doc, value_type='class', prefix=prefix, inherited=True, verbose=False) - + def write_variable(self, out, var_doc, name=None, prefix='', verbose=True): if name is None: name = var_doc.name out(prefix+self.bold(str(name))) @@ -137,8 +139,9 @@ var_doc.value.canonical_name not in (None, UNKNOWN)): out(' = %s' % var_doc.value.canonical_name) elif var_doc.value not in (UNKNOWN, None): - val_repr = var_doc.value.summary_pyval_repr(max_len=len(name)-75)[0] - out(' = %s' % val_repr.expandtabs()) + val_repr = var_doc.value.summary_pyval_repr( + max_len=self._cols-len(name)-len(prefix)-3) + out(' = %s' % val_repr.to_plaintext(None)) out('\n') if not verbose: return prefix += ' ' # indent the body. @@ -199,7 +202,8 @@ if default is None: return '%s' % name else: - return '%s=%s' % (name, default.summary_pyval_repr()[0]) + default_repr = default.summary_pyval_repr() + return '%s=%s' % (name, default_repr.to_plaintext(None)) def write_list(self, out, heading, doc, value_type=None, imported=False, inherited=False, prefix='', noindent=False, Modified: trunk/epydoc/src/epydoc/markup/epytext.py =================================================================== --- trunk/epydoc/src/epydoc/markup/epytext.py 2007-02-13 15:27:45 UTC (rev 1472) +++ trunk/epydoc/src/epydoc/markup/epytext.py 2007-02-13 19:46:05 UTC (rev 1473) @@ -148,6 +148,11 @@ ''.join([str(child) for child in self.children]) + '</%s>' % self.tag) + def __repr__(self): + attribs = ''.join([', %s=%r' % t for t in self.attribs.items()]) + args = ''.join([', %r' % c for c in self.children]) + return 'Element(%s%s%s)' % (self.tag, args, attribs) + ################################################## ## Constants ################################################## @@ -1591,7 +1596,6 @@ ################################################################# ## SUPPORT FOR EPYDOC ################################################################# -from epydoc.docwriter.dotgraph import * def parse_docstring(docstring, errors, **options): """ @@ -1854,6 +1858,7 @@ log.warning("Could not construct class tree: you must " "specify one or more base classes.") return None + from epydoc.docwriter.dotgraph import class_tree_graph return class_tree_graph(bases, linker, context) elif graph_type == 'packagetree': if graph_args: @@ -1865,9 +1870,11 @@ log.warning("Could not construct package tree: you must " "specify one or more root packages.") return None + from epydoc.docwriter.dotgraph import package_tree_graph return package_tree_graph(packages, linker, context) elif graph_type == 'importgraph': modules = [d for d in docindex.root if isinstance(d, ModuleDoc)] + from epydoc.docwriter.dotgraph import import_graph return import_graph(modules, docindex, linker, context) elif graph_type == 'callgraph': @@ -1876,6 +1883,7 @@ docs = [doc for doc in docs if doc is not None] else: docs = [context] + from epydoc.docwriter.dotgraph import call_graph return call_graph(docs, docindex, linker, context) else: log.warning("Unknown graph type %s" % graph_type) Modified: trunk/epydoc/src/epydoc/markup/pyval_repr.py =================================================================== --- trunk/epydoc/src/epydoc/markup/pyval_repr.py 2007-02-13 15:27:45 UTC (rev 1472) +++ trunk/epydoc/src/epydoc/markup/pyval_repr.py 2007-02-13 19:46:05 UTC (rev 1473) @@ -79,28 +79,44 @@ generates a string containing a newline, but the state object's linebreakok variable is False.""" +class ColorizedPyvalRepr(ParsedEpytextDocstring): + """ + @ivar score: A score, evaluating how good this repr is. + @ivar is_complete: True if this colorized repr completely describes + the object. + """ + def __init__(self, tree, score, is_complete): + ParsedEpytextDocstring.__init__(self, tree) + self.score = score + self.is_complete = is_complete +def colorize_pyval(pyval, parse_repr=None, min_score=None, + linelen=75, maxlines=5, linebreakok=True, sort=True): + return PyvalColorizer(linelen, maxlines, linebreakok, sort).colorize( + pyval, parse_repr, min_score) + class PyvalColorizer: """ Syntax highlighter for Python values. """ - def __init__(self, linelen=75, maxlines=5, sort=True): + def __init__(self, linelen=75, maxlines=5, linebreakok=True, sort=True): self.linelen = linelen self.maxlines = maxlines + self.linebreakok = linebreakok self.sort = sort #//////////////////////////////////////////////////////////// - # Colorization Tags + # Colorization Tags & other constants #//////////////////////////////////////////////////////////// - GROUP_TAG = 'val-group' # e.g., "[" and "]" - COMMA_TAG = 'val-op' # The "," that separates elements - COLON_TAG = 'val-op' # The ":" in dictionaries - CONST_TAG = None # None, True, False - NUMBER_TAG = None # ints, floats, etc - QUOTE_TAG = 'val-quote' # Quotes around strings. - STRING_TAG = 'val-string' # Body of string literals + GROUP_TAG = 'variable-group' # e.g., "[" and "]" + COMMA_TAG = 'variable-op' # The "," that separates elements + COLON_TAG = 'variable-op' # The ":" in dictionaries + CONST_TAG = None # None, True, False + NUMBER_TAG = None # ints, floats, etc + QUOTE_TAG = 'variable-quote' # Quotes around strings. + STRING_TAG = 'variable-string' # Body of string literals RE_CHAR_TAG = None RE_GROUP_TAG = 're-group' @@ -108,45 +124,60 @@ RE_OP_TAG = 're-op' RE_FLAGS_TAG = 're-flags' - # Should these use symbols instead? - ELLIPSIS = Element('code', '...', style='ellipsis') - LINEWRAP = Element('symbol', 'crarr') + ELLIPSIS = Element('code', u'...', style='variable-ellipsis') + LINEWRAP = Element('symbol', u'crarr') + UNKNOWN_REPR = Element('code', u'??', style='variable-unknown') + GENERIC_OBJECT_RE = re.compile(r'^<.* at 0x[0-9a-f]+>$', re.IGNORECASE) + #//////////////////////////////////////////////////////////// # Entry Point #//////////////////////////////////////////////////////////// - def colorize(self, pyval, min_score=None): - pds, score = self.colorize_and_score(pyval) - if min_score is None or score >= min_score: - return pds - else: - return None - - def colorize_and_score(self, pyval): + def colorize(self, pyval, parse_repr=None, min_score=None): """ - @return: A tuple (parsed_docstring, score). + @return: A L{ColorizedPyvalRepr} describing the given pyval. """ + UNKNOWN = epydoc.apidoc.UNKNOWN # Create an object to keep track of the colorization. state = _ColorizerState() + state.linebreakok = self.linebreakok # Colorize the value. If we reach maxlines, then add on an # ellipsis marker and call it a day. try: - self._colorize(pyval, state) - except _Maxlines: - state.result.append(self.ELLIPSIS) + if pyval is not UNKNOWN: + self._colorize(pyval, state) + elif parse_repr not in (None, UNKNOWN): + self._output(parse_repr, None, state) + else: + state.result.append(PyvalColorizer.UNKNOWN_REPR) + is_complete = True + except (_Maxlines, _Linebreak): + if self.linebreakok: + state.result.append('\n') + state.result.append(self.ELLIPSIS) + else: + if state.result[-1] is self.LINEWRAP: + state.result.pop() + self._trim_result(state.result, 3) + state.result.append(self.ELLIPSIS) + is_complete = False + # If we didn't score high enough, then try again. + if (pyval is not UNKNOWN and parse_repr not in (None, UNKNOWN) + and min_score is not None and state.score < min_score): + return self.colorize(UNKNOWN, parse_repr) # Put it all together. tree = Element('epytext', *state.result) - return ParsedEpytextDocstring(tree), state.score + return ColorizedPyvalRepr(tree, state.score, is_complete) def _colorize(self, pyval, state): pyval_type = type(pyval) state.score += 1 if pyval is None or pyval is True or pyval is False: - self._output(str(pyval), self.CONST_TAG, state) + self._output(unicode(pyval), self.CONST_TAG, state) elif pyval_type in (int, float, long, types.ComplexType): - self._output(str(pyval), self.NUMBER_TAG, state) + self._output(unicode(pyval), self.NUMBER_TAG, state) elif pyval_type is str: self._colorize_str(pyval, state, '', 'string-escape') elif pyval_type is unicode: @@ -156,34 +187,56 @@ elif pyval_type is tuple: self._multiline(self._colorize_iter, pyval, state, '(', ')') elif pyval_type is set: - if self.sort: pyval = sorted(pyval) - self._multiline(self._colorize_iter, pyval, state, - 'set([', '])') + self._multiline(self._colorize_iter, self._sort(pyval), + state, 'set([', '])') elif pyval_type is frozenset: - if self.sort: pyval = sorted(pyval) - self._multiline(self._colorize_iter, pyval, state, - 'frozenset([', '])') + self._multiline(self._colorize_iter, self._sort(pyval), + state, 'frozenset([', '])') elif pyval_type is dict: - items = pyval.items() - if self.sort: items = sorted(items) - self._multiline(self._colorize_dict, items, state, '{', '}') + self._multiline(self._colorize_dict, self._sort(pyval.items()), + state, '{', '}') elif is_re_pattern(pyval): self._colorize_re(pyval, state) else: try: pyval_repr = repr(pyval) - self._output(pyval_repr, None, state) - if self.GENERIC_OBJECT_RE.match(pyval_repr): - state.score -= 5 + if not isinstance(pyval_repr, (str, unicode)): + pyval_repr = unicode(pyval_repr) + pyval_repr_ok = True except KeyboardInterrupt: raise except: - pyval_repr = '...' + pyval_repr_ok = False state.score -= 100 - GENERIC_OBJECT_RE = re.compile(r'^<.* at 0x[0-9a-f]+>$', - re.IGNORECASE) + if pyval_repr_ok: + self._output(pyval_repr, None, state) + if self.GENERIC_OBJECT_RE.match(pyval_repr): + state.score -= 5 + else: + state.result.append(self.UNKNOWN_REPR) + + def _sort(self, items): + if not self.sort: return items + try: return sorted(items) + except KeyboardInterrupt: raise + except: return items + def _trim_result(self, result, num_chars): + while num_chars > 0: + if not result: return + if isinstance(result[-1], Element): + assert len(result[-1].children) == 1 + trim = min(num_chars, len(result[-1].children[0])) + result[-1].children[0] = result[-1].children[0][:-trim] + if not result[-1].children[0]: result.pop() + num_chars -= trim + else: + trim = min(num_chars, len(result[-1])) + result[-1] = result[-1][:-trim] + if not result[-1]: result.pop() + num_chars -= trim + #//////////////////////////////////////////////////////////// # Object Colorization Functions #//////////////////////////////////////////////////////////// @@ -238,12 +291,17 @@ def _colorize_str(self, pyval, state, prefix, encoding): # Decide which quote to use. - if '\n' in pyval: quote = "'''" + if '\n' in pyval and state.linebreakok: quote = "'''" else: quote = "'" + # Divide the string into lines. + if state.linebreakok: + lines = pyval.split('\n') + else: + lines = [pyval] # Open quote. self._output(prefix+quote, self.QUOTE_TAG, state) # Body - for i, line in enumerate(pyval.split('\n')): + for i, line in enumerate(lines): if i>0: self._output('\n', None, state) self._output(line.encode(encoding), self.STRING_TAG, state) # Close quote. @@ -260,8 +318,10 @@ groups = dict([(num,name) for (name,num) in tree.pattern.groupdict.items()]) # Colorize it! + self._output("re.compile(r'", None, state) self._colorize_re_flags(tree.pattern.flags, state) self._colorize_re_tree(tree, state, True, groups) + self._output("')", None, state) def _colorize_re_flags(self, flags, state): if flags: @@ -281,7 +341,7 @@ if op == sre_constants.LITERAL: c = unichr(args) # Add any appropriate escaping. - if c in '.^$\\*+?{}[]|()': c = '\\'+c + if c in '.^$\\*+?{}[]|()\'': c = '\\'+c elif c == '\t': c = '\\t' elif c == '\r': c = '\\r' elif c == '\n': c = '\\n' @@ -419,8 +479,9 @@ be line-wrapped. If the total number of lines exceeds `self.maxlines`, then raise a `_Maxlines` exception. """ - if '\n' in s and not state.linebreakok: - raise _Linebreak() + # Make sure the string is unicode. + if isinstance(s, str): + s = decode_with_backslashreplace(s) # Split the string into segments. The first segment is the # content to add to the current line, and the remaining @@ -431,13 +492,13 @@ # If this isn't the first segment, then add a newline to # split it from the previous segment. if i > 0: + if (state.lineno+1) > self.maxlines: + raise _Maxlines() if not state.linebreakok: raise _Linebreak() - state.result.append('\n') + state.result.append(u'\n') state.lineno += 1 state.charpos = 0 - if state.lineno > self.maxlines: - raise _Maxlines() # If the segment fits on the current line, then just call # markup to tag it, and store the result. Modified: trunk/epydoc/src/epydoc/test/docbuilder.doctest =================================================================== --- trunk/epydoc/src/epydoc/test/docbuilder.doctest 2007-02-13 15:27:45 UTC (rev 1472) +++ trunk/epydoc/src/epydoc/test/docbuilder.doctest 2007-02-13 19:46:05 UTC (rev 1473) @@ -300,17 +300,39 @@ >>> from epydoc.test.util import buildvaluedoc >>> def print_py_reprs(s): ... value_doc = buildvaluedoc(s) + ... print 'Var Score Repr\n'+'-'*50 ... for (name, var_doc) in sorted(value_doc.variables.items()): - ... print "%-5s %r" % (name, var_doc.value.pyval_repr()) + ... if len(name) > 1: continue + ... var_repr = var_doc.value.pyval_repr() + ... print " %s %4s %r" % (name, var_repr.score, + ... var_repr.to_plaintext(None)) >>> print_py_reprs(''' + ... import re ... class Foo: pass - ... a = Foo() - ... b = [1, 2, 3] - ... c = 3+5 - ... b.append(99) + ... class Bar: + ... def __repr__(self): return "<specialized repr>" + ... class Baz: + ... def __repr__(self): raise ValueError() + ... a = Foo() # pyval score < 0; use parse repr. + ... b = Bar() # pyval score > 0; use pyval repr. + ... c = Baz() # pyval score < 0; use parse repr. + ... d = [1, 2, 3] # pyval score > 0; use pyval repr. + ... d.append(99) + ... e = 3+5 # pyval score > 0; use pyval repr. + ... f = re.compile('hi+') # pyval score > 0; use pyval repr. + ... globals()['h'] = Baz() # pyval score < 0; can't be parsed. + ... i = [Foo(), 1, 2] # pyval score < 0; use parse repr. + ... j = [Foo(), 1, 2, 3] # pyval score = 0; use pyval repr. ... ''') - Foo u'<class epydoc_test.Foo at ...>' - a u'Foo()' - b u'[1, 2, 3, 99]' - c u'8' + Var Score Repr + -------------------------------------------------- + a 0 u'Foo()' + b 1 u'<specialized repr>' + c 0 u'Baz()' + d 5 u'[1, 2, 3, 99]' + e 1 u'8' + f 1 u"re.compile(r'hi+')" + h -99 u'??' + i 0 u'[Foo(), 1, 2]' + j 0 u'[<epydoc_test.Foo instance at ...>, 1, 2, 3]' Modified: trunk/epydoc/src/epydoc/test/pyval_repr.doctest =================================================================== --- trunk/epydoc/src/epydoc/test/pyval_repr.doctest 2007-02-13 15:27:45 UTC (rev 1472) +++ trunk/epydoc/src/epydoc/test/pyval_repr.doctest 2007-02-13 19:46:05 UTC (rev 1473) @@ -3,7 +3,10 @@ >>> from epydoc.markup.pyval_repr import * >>> colorizer = PyvalColorizer(linelen=40) - >>> def color(s): print colorizer.colorize(s).to_html(None).rstrip() + >>> def color(v, linebreakok=True): + ... colorizer = PyvalColorizer(linelen=40, linebreakok=linebreakok) + ... pds = colorizer.colorize(v, None) + ... print pds.to_html(None).rstrip() Simple Types ============ @@ -27,6 +30,7 @@ 1000000000000000000000000000000000000000↵ 0000000000000000000000000000000000000000↵ 00000000000 + >>> colorizer = PyvalColorizer(linelen=40) >>> print '-'*40+'\n'+colorizer.colorize(10**90).to_plaintext(None) ---------------------------------------- 1000000000000000000000000000000000000000\ @@ -39,37 +43,42 @@ escaped using the 'string-escape' encoding. >>> color(''.join(chr(i) for i in range(256))) - <code class="val-quote">'''</code><code class="val-string">\x00\x01\x02\x03\x04\x05\x06\x07\x08\</code>↵ - <code class="val-string">t</code> - <code class="val-string">\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x</code>↵ - <code class="val-string">15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x</code>↵ - <code class="val-string">1f !"#$%&\'()*+,-./0123456789:;<=>?@ABCD</code>↵ - <code class="ellipsis">...</code> + <code class="variable-quote">'''</code><code class="variable-string">\x00\x01\x02\x03\x04\x05\x06\x07\x08\</code>↵ + <code class="variable-string">t</code> + <code class="variable-string">\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x</code>↵ + <code class="variable-string">15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x</code>↵ + <code class="variable-string">1f !"#$%&\'()*+,-./0123456789:;<=>?@ABCD</code>↵ + <code class="variable-ellipsis">...</code> Currently, the "'" quote is always used, because that's what the 'string-escape' encoding expects. >>> color('Hello') - <code class="val-quote">'</code><code class="val-string">Hello</code><code class="val-quote">'</code> + <code class="variable-quote">'</code><code class="variable-string">Hello</code><code class="variable-quote">'</code> >>> color('"Hello"') - <code class="val-quote">'</code><code class="val-string">"Hello"</code><code class="val-quote">'</code> + <code class="variable-quote">'</code><code class="variable-string">"Hello"</code><code class="variable-quote">'</code> >>> color("'Hello'") - <code class="val-quote">'</code><code class="val-string">\'Hello\'</code><code class="val-quote">'</code> + <code class="variable-quote">'</code><code class="variable-string">\'Hello\'</code><code class="variable-quote">'</code> Strings containing newlines are automatically rendered as multiline strings. >>> color("This\n is a multiline\n string!") - <code class="val-quote">'''</code><code class="val-string">This</code> - <code class="val-string"> is a multiline</code> - <code class="val-string"> string!</code><code class="val-quote">'''</code> + <code class="variable-quote">'''</code><code class="variable-string">This</code> + <code class="variable-string"> is a multiline</code> + <code class="variable-string"> string!</code><code class="variable-quote">'''</code> +Unless we ask for them not to be: + + >>> color("This\n is a multiline\n string!", linebreakok=False) + <code class="variable-quote">'</code><code class="variable-string">This\n is a multiline\n string!</code><code class="variable-quote">'</code> + Unicode strings are handled properly. >>> color(u"Hello world") - <code class="val-quote">u'</code><code class="val-string">Hello world</code><code class="val-quote">'</code> + <code class="variable-quote">u'</code><code class="variable-string">Hello world</code><code class="variable-quote">'</code> >>> color(u"\uaaaa And \ubbbb") - <code class="val-quote">u'</code><code class="val-string">\uaaaa And \ubbbb</code><code class="val-quote">'</code> + <code class="variable-quote">u'</code><code class="variable-string">\uaaaa And \ubbbb</code><code class="variable-quote">'</code> Lists, Tuples, etc. =================== @@ -79,28 +88,28 @@ listed on a separate line, indented by the size of the open-bracket. >>> color(range(10)) - <code class="val-group">[</code>0<code class="val-op">, </code>1<code class="val-op">, </code>2<code class="val-op">, </code>3<code class="val-op">, </code>4<code class="val-op">, </code>5<code class="val-op">, </code>6<code class="val-op">, </code>7<code class="val-op">, </code>8<code class="val-op">, </code>9<code class="val-group">]</code> + <code class="variable-group">[</code>0<code class="variable-op">, </code>1<code class="variable-op">, </code>2<code class="variable-op">, </code>3<code class="variable-op">, </code>4<code class="variable-op">, </code>5<code class="variable-op">, </code>6<code class="variable-op">, </code>7<code class="variable-op">, </code>8<code class="variable-op">, </code>9<code class="variable-group">]</code> >>> color(range(100)) - <code class="val-group">[</code>0<code class="val-op">,</code> - 1<code class="val-op">,</code> - 2<code class="val-op">,</code> - 3<code class="val-op">,</code> - 4<code class="val-op">,</code> - <code class="ellipsis">...</code> + <code class="variable-group">[</code>0<code class="variable-op">,</code> + 1<code class="variable-op">,</code> + 2<code class="variable-op">,</code> + 3<code class="variable-op">,</code> + 4<code class="variable-op">,</code> + <code class="variable-ellipsis">...</code> >>> color([1,2,[5,6,[(11,22,33),9],10],11]+[99,98,97,96,95]) - <code class="val-group">[</code>1<code class="val-op">,</code> - 2<code class="val-op">,</code> - <code class="val-group">[</code>5<code class="val-op">, </code>6<code class="val-op">, </code><code class="val-group">[</code><code class="val-group">(</code>11<code class="val-op">, </code>22<code class="val-op">, </code>33<code class="val-group">)</code><code class="val-op">, </code>9<code class="val-group">]</code><code class="val-op">, </code>10<code class="val-group">]</code><code class="val-op">,</code> - 11<code class="val-op">,</code> - 99<code class="val-op">,</code> - <code class="ellipsis">...</code> + <code class="variable-group">[</code>1<code class="variable-op">,</code> + 2<code class="variable-op">,</code> + <code class="variable-group">[</code>5<code class="variable-op">, </code>6<code class="variable-op">, </code><code class="variable-group">[</code><code class="variable-group">(</code>11<code class="variable-op">, </code>22<code class="variable-op">, </code>33<code class="variable-group">)</code><code class="variable-op">, </code>9<code class="variable-group">]</code><code class="variable-op">, </code>10<code class="variable-group">]</code><code class="variable-op">,</code> + 11<code class="variable-op">,</code> + 99<code class="variable-op">,</code> + <code class="variable-ellipsis">...</code> >>> color(set(range(20))) - <code class="val-group">set([</code>0<code class="val-op">,</code> - 1<code class="val-op">,</code> - 2<code class="val-op">,</code> - 3<code class="val-op">,</code> - 4<code class="val-op">,</code> - <code class="ellipsis">...</code> + <code class="variable-group">set([</code>0<code class="variable-op">,</code> + 1<code class="variable-op">,</code> + 2<code class="variable-op">,</code> + 3<code class="variable-op">,</code> + 4<code class="variable-op">,</code> + <code class="variable-ellipsis">...</code> Dictionaries ============ @@ -108,12 +117,12 @@ "op". >>> color({1:33, 2:[1,2,3,{7:'oo'*20}]}) - <code class="val-group">{</code>1<code class="val-op">: </code>33<code class="val-op">,</code> - 2<code class="val-op">: </code><code class="val-group">[</code>1<code class="val-op">,</code> - 2<code class="val-op">,</code> - 3<code class="val-op">,</code> - <code class="val-group">{</code>7<code class="val-op">: </code><code class="val-quote">'</code><code class="val-string">oooooooooooooooooooooooooooooo</code>↵ - <code class="ellipsis">...</code> + <code class="variable-group">{</code>1<code class="variable-op">: </code>33<code class="variable-op">,</code> + 2<code class="variable-op">: </code><code class="variable-group">[</code>1<code class="variable-op">,</code> + 2<code class="variable-op">,</code> + 3<code class="variable-op">,</code> + <code class="variable-group">{</code>7<code class="variable-op">: </code><code class="variable-quote">'</code><code class="variable-string">oooooooooooooooooooooooooooooo</code>↵ + <code class="variable-ellipsis">...</code> Regular Expressions =================== @@ -124,16 +133,17 @@ >>> import re >>> def color_re(s, check_roundtrip=True): + ... colorizer = PyvalColorizer(linelen=55) ... val = colorizer.colorize(re.compile(s)) ... if check_roundtrip: - ... assert textcontent(val._tree) == s, val._tree - ... print val.to_html(None).rstrip() + ... assert textcontent(val._tree)[13:-2] == s, val._tree + ... print val.to_html(None).rstrip()[13:-2] >>> # Literal characters - >>> color_re(u'abc \t\r\n\f\v \xff \uffff \U000fffff', False) - abc \t\r\n\f\v \xff \uffff \U000fffff - >>> color_re(r'\.\^\$\\\*\+\?\{\}\[\]\|\(\)') - \.\^\$\\\*\+\?\{\}\[\]\|\(\) + >>> color_re(u'abc \t\r\n\f\v \xff \uffff', False) + abc \t\r\n\f\v \xff \uffff + >>> color_re(r'\.\^\$\\\*\+\?\{\}\[\]\|\(\)\'') + \.\^\$\\\*\+\?\{\}\[\]\|\(\)\' >>> # Any character & character classes >>> color_re(r".\d\D\s\S\w\W\A^$\b\B\Z") @@ -193,6 +203,33 @@ >>> color_re(r"(?x)This is verbose", False) <code class="re-flags">(?x)</code>Thisisverbose +Line Wrapping +============= +If a line goes beyond linelen, it is wrapped using ``↵`` (which +gets translated to ``\\`` by `to_plaintext()`). + + >>> colorizer = PyvalColorizer(linelen=40) + >>> print colorizer.colorize('x'*100).to_plaintext(None) + 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\ + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\ + xxxxxxxxxxxxxxxxxxxxx' + +Check that the last line gets a ``↵`` when maxlines is exceeded: + + >>> print colorizer.colorize('x'*1000).to_plaintext(None) + 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\ + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\ + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\ + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\ + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\ + ... + +If linebreakok is False, then line wrapping gives an ellipsis instead: + + >>> colorizer = PyvalColorizer(linelen=40, linebreakok=False) + >>> print colorizer.colorize('x'*100).to_plaintext(None) + 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx... + Representation Scores ===================== When colorized representations are built, a score is computed @@ -211,9 +248,10 @@ scores; if the score is too low, then `colorize` will return `None`. >>> def color2(v): - ... pds, score = colorizer.colorize_and_score(v) + ... colorizer = PyvalColorizer(linelen=40) + ... pds = colorizer.colorize(v) ... print 'repr: %s' % pds.to_plaintext(None) - ... print 'score: %s (%s)' % (score, score>0 and 'ok' or 'bad') + ... print 'score: %s (%s)' % (pds.score, pds.score>0 and 'ok' or 'bad') >>> class A: pass @@ -242,3 +280,19 @@ ... score: 1 (ok) +Summary +======= +To generate summary-reprs, use maxlines=1 and linebreakok=False: + + >>> summarizer = PyvalColorizer(linelen=60, maxlines=1, linebreakok=False) + >>> def summarize(v): + ... print summarizer.colorize(v).to_plaintext(None) + + >>> summarize(range(100)) + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16... + >>> summarize('hello\nworld') + 'hello\nworld' + >>> summarize('hello\nworld'*100) + 'hello\nworldhello\nworldhello\nworldhello\nworldhello\nw... + + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |