epydoc-commits Mailing List for Python API documentation generation tool (Page 4)
Brought to you by:
edloper
You can subscribe to this list here.
2006 |
Jan
|
Feb
|
Mar
|
Apr
(77) |
May
|
Jun
(6) |
Jul
(8) |
Aug
(91) |
Sep
(67) |
Oct
(4) |
Nov
|
Dec
(1) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2007 |
Jan
(17) |
Feb
(135) |
Mar
(25) |
Apr
|
May
(1) |
Jun
(1) |
Jul
(7) |
Aug
|
Sep
(62) |
Oct
(1) |
Nov
(3) |
Dec
|
2008 |
Jan
(40) |
Feb
(102) |
Mar
(5) |
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
2009 |
Jan
|
Feb
(2) |
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
From: <ed...@us...> - 2008-02-23 01:07:49
|
Revision: 1737 http://epydoc.svn.sourceforge.net/epydoc/?rev=1737&view=rev Author: edloper Date: 2008-02-22 17:07:47 -0800 (Fri, 22 Feb 2008) Log Message: ----------- Clarified a docstring Modified Paths: -------------- trunk/epydoc/src/epydoc/markup/__init__.py Modified: trunk/epydoc/src/epydoc/markup/__init__.py =================================================================== --- trunk/epydoc/src/epydoc/markup/__init__.py 2008-02-23 01:07:30 UTC (rev 1736) +++ trunk/epydoc/src/epydoc/markup/__init__.py 2008-02-23 01:07:47 UTC (rev 1737) @@ -466,7 +466,9 @@ should be linked to. @type label: C{string} or C{None} @param label: The label that should be used for the identifier, - if it's different from the name of the identifier. + if it's different from the name of the identifier. This + should be expressed in the target markup language -- e.g. + for latex, "_"s should be escaped. @rtype: C{string} @return: The translated crossreference link. """ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2008-02-23 01:07:34
|
Revision: 1736 http://epydoc.svn.sourceforge.net/epydoc/?rev=1736&view=rev Author: edloper Date: 2008-02-22 17:07:30 -0800 (Fri, 22 Feb 2008) Log Message: ----------- - Use raw string for TEMPLATE Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/html_css.py Modified: trunk/epydoc/src/epydoc/docwriter/html_css.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/html_css.py 2008-02-23 01:07:05 UTC (rev 1735) +++ trunk/epydoc/src/epydoc/docwriter/html_css.py 2008-02-23 01:07:30 UTC (rev 1736) @@ -41,7 +41,7 @@ # Black on white, with blue highlights. This is similar to how # javadoc looks. -TEMPLATE = """ +TEMPLATE = r""" /* Epydoc CSS Stylesheet * This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2008-02-23 01:07:14
|
Revision: 1735 http://epydoc.svn.sourceforge.net/epydoc/?rev=1735&view=rev Author: edloper Date: 2008-02-22 17:07:05 -0800 (Fri, 22 Feb 2008) Log Message: ----------- - Added note to self Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/html.py Modified: trunk/epydoc/src/epydoc/docwriter/html.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/html.py 2008-02-23 00:55:52 UTC (rev 1734) +++ trunk/epydoc/src/epydoc/docwriter/html.py 2008-02-23 01:07:05 UTC (rev 1735) @@ -2280,7 +2280,9 @@ [('Get', prop_doc.fget), ('Set', prop_doc.fset), ('Delete', prop_doc.fdel)] if val_doc not in (None, UNKNOWN) + # [xx] this requires introspection -- why do this? and val_doc.pyval is not None + # [xx] (end) and not val_doc.canonical_name[0].startswith('??')] self.write_property_details_entry(out, var_doc, descr, This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2008-02-23 00:55:53
|
Revision: 1734 http://epydoc.svn.sourceforge.net/epydoc/?rev=1734&view=rev Author: edloper Date: 2008-02-22 16:55:52 -0800 (Fri, 22 Feb 2008) Log Message: ----------- - Incorporated (and extended) patches from Jonathan Guyer, to move formatting decisions for the latex writer into a separate stylesheet. The new version is not compatible with Jonathan Guyer's original patch -- I redesigned the interface between epydoc.docwriter.latex and the stylesheet somewhat. At present, three style files are available: 'base', 'boxes', and 'shaded'. 'boxes' resembles the old (3.0) output. Modified Paths: -------------- trunk/epydoc/src/epydoc/cli.py trunk/epydoc/src/epydoc/docwriter/latex.py Added Paths: ----------- trunk/epydoc/src/epydoc/docwriter/latex_sty.py Modified: trunk/epydoc/src/epydoc/cli.py =================================================================== --- trunk/epydoc/src/epydoc/cli.py 2008-02-23 00:52:21 UTC (rev 1733) +++ trunk/epydoc/src/epydoc/cli.py 2008-02-23 00:55:52 UTC (rev 1734) @@ -325,6 +325,11 @@ help="The CSS stylesheet. STYLESHEET can be either a " "builtin stylesheet or the name of a CSS file.") + output_group.add_option("--sty", + dest="sty", metavar="LATEXSTYLE", + help="The LaTeX style file. LATEXSTYLE can be either a " + "builtin style file or the name of a .sty file.") + output_group.add_option("--url", "-u", dest="prj_url", metavar="URL", help="The documented project's URL (for the navigation bar).") @@ -698,13 +703,13 @@ if options.load_pickle: stages = [30] # Loading pickled documentation if 'html' in options.actions: stages += [100] - if 'text' in options.actions: stages += [30] if 'check' in options.actions: stages += [10] if 'pickle' in options.actions: stages += [10] if 'latex' in options.actions: stages += [60] if 'pdf' in options.actions: stages += [50] - elif 'ps' in options.actions: stages += [40] - elif 'dvi' in options.actions: stages += [30] + elif 'ps' in options.actions: stages += [40] # implied by pdf + elif 'dvi' in options.actions: stages += [30] # implied by ps + if 'text' in options.actions: stages += [30] if options.parse and not options.introspect: del stages[1] # no merging @@ -828,13 +833,15 @@ if profile_stats is not None: docindex.read_profiling_info(profile_stats) - # Perform the specified action. + # Perform the specified action. NOTE: It is important that these + # are performed in the same order that the 'stages' list was + # constructed, at the top of this function. + if 'html' in options.actions: + write_html(docindex, options) if 'check' in options.actions: check_docs(docindex, options) if 'pickle' in options.actions: write_pickle(docindex, options) - if 'html' in options.actions: - write_html(docindex, options) if ('latex' in options.actions or 'dvi' in options.actions or 'ps' in options.actions or 'pdf' in options.actions): write_latex(docindex, options) Modified: trunk/epydoc/src/epydoc/docwriter/latex.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/latex.py 2008-02-23 00:52:21 UTC (rev 1733) +++ trunk/epydoc/src/epydoc/docwriter/latex.py 2008-02-23 00:55:52 UTC (rev 1734) @@ -23,100 +23,16 @@ from epydoc import markup from epydoc.util import plaintext_to_latex import epydoc.markup +from epydoc.docwriter.dotgraph import * +from epydoc.docwriter.latex_sty import STYLESHEETS class LatexWriter: + #: Expects (options,) PREAMBLE = [ "\\documentclass{article}", - "\\usepackage{alltt, parskip, fancyhdr, boxedminipage}", - "\\usepackage{makeidx, multirow, longtable, tocbibind, amssymb}", - "\\usepackage{fullpage}", - "\\usepackage[usenames]{color}", - # Fix the heading position -- without this, the headings generated - # by the fancyheadings package sometimes overlap the text. - "\\setlength{\\headheight}{16pt}", - "\\setlength{\\headsep}{24pt}", - "\\setlength{\\topmargin}{-\\headsep}", - # By default, do not indent paragraphs. - "\\setlength{\\parindent}{0ex}", - "\\setlength{\\parskip}{2ex}", - # Double the standard size boxedminipage outlines. - "\\setlength{\\fboxrule}{2\\fboxrule}", - # Create a 'base class' length named BCL for use in base trees. - "\\newlength{\\BCL} % base class length, for base trees.", - # Display the section & subsection names in a header. - "\\pagestyle{fancy}", - "\\renewcommand{\\sectionmark}[1]{\\markboth{#1}{}}", - "\\renewcommand{\\subsectionmark}[1]{\\markright{#1}}", - # Colorization for python source code - "\\definecolor{py@keywordcolour}{rgb}{1,0.45882,0}", - "\\definecolor{py@stringcolour}{rgb}{0,0.666666,0}", - "\\definecolor{py@commentcolour}{rgb}{1,0,0}", - "\\definecolor{py@ps1colour}{rgb}{0.60784,0,0}", - "\\definecolor{py@ps2colour}{rgb}{0.60784,0,1}", - "\\definecolor{py@inputcolour}{rgb}{0,0,0}", - "\\definecolor{py@outputcolour}{rgb}{0,0,1}", - "\\definecolor{py@exceptcolour}{rgb}{1,0,0}", - "\\definecolor{py@defnamecolour}{rgb}{1,0.5,0.5}", - "\\definecolor{py@builtincolour}{rgb}{0.58039,0,0.58039}", - "\\definecolor{py@identifiercolour}{rgb}{0,0,0}", - "\\definecolor{py@linenumcolour}{rgb}{0.4,0.4,0.4}", - "\\definecolor{py@inputcolour}{rgb}{0,0,0}", - "% Prompt", - "\\newcommand{\\pysrcprompt}[1]{\\textcolor{py@ps1colour}" - "{\\small\\textbf{#1}}}", - "\\newcommand{\\pysrcmore}[1]{\\textcolor{py@ps2colour}" - "{\\small\\textbf{#1}}}", - "% Source code", - "\\newcommand{\\pysrckeyword}[1]{\\textcolor{py@keywordcolour}" - "{\\small\\textbf{#1}}}", - "\\newcommand{\\pysrcbuiltin}[1]{\\textcolor{py@builtincolour}" - "{\\small\\textbf{#1}}}", - "\\newcommand{\\pysrcstring}[1]{\\textcolor{py@stringcolour}" - "{\\small\\textbf{#1}}}", - "\\newcommand{\\pysrcdefname}[1]{\\textcolor{py@defnamecolour}" - "{\\small\\textbf{#1}}}", - "\\newcommand{\\pysrcother}[1]{\\small\\textbf{#1}}", - "% Comments", - "\\newcommand{\\pysrccomment}[1]{\\textcolor{py@commentcolour}" - "{\\small\\textbf{#1}}}", - "% Output", - "\\newcommand{\\pysrcoutput}[1]{\\textcolor{py@outputcolour}" - "{\\small\\textbf{#1}}}", - "% Exceptions", - "\\newcommand{\\pysrcexcept}[1]{\\textcolor{py@exceptcolour}" - "{\\small\\textbf{#1}}}", - # Size of the function description boxes. - "\\newlength{\\funcindent}", - "\\newlength{\\funcwidth}", - "\\setlength{\\funcindent}{1cm}", - "\\setlength{\\funcwidth}{\\textwidth}", - "\\addtolength{\\funcwidth}{-2\\funcindent}", - # Size of the var description tables. - "\\newlength{\\varindent}", - "\\newlength{\\varnamewidth}", - "\\newlength{\\vardescrwidth}", - "\\newlength{\\varwidth}", - "\\setlength{\\varindent}{1cm}", - "\\setlength{\\varnamewidth}{.3\\textwidth}", - "\\setlength{\\varwidth}{\\textwidth}", - "\\addtolength{\\varwidth}{-4\\tabcolsep}", - "\\addtolength{\\varwidth}{-3\\arrayrulewidth}", - "\\addtolength{\\varwidth}{-2\\varindent}", - "\\setlength{\\vardescrwidth}{\\varwidth}", - "\\addtolength{\\vardescrwidth}{-\\varnamewidth}", - # Define new environment for displaying parameter lists. - textwrap.dedent("""\ - \\newenvironment{Ventry}[1]% - {\\begin{list}{}{% - \\renewcommand{\\makelabel}[1]{\\texttt{##1:}\\hfil}% - \\settowidth{\\labelwidth}{\\texttt{#1:}}% - \\setlength{\\leftmargin}{\\labelsep}% - \\addtolength{\\leftmargin}{\\labelwidth}}}% - {\\end{list}}"""), + "\\usepackage[%s]{epydoc}", ] - HRULE = '\\rule{\\textwidth}{0.5\\fboxrule}\n\n' - SECTIONS = ['\\part{%s}', '\\chapter{%s}', '\\section{%s}', '\\subsection{%s}', '\\subsubsection{%s}', '\\textbf{%s}'] @@ -130,14 +46,23 @@ # Process keyword arguments self._show_private = kwargs.get('show_private', 0) self._prj_name = kwargs.get('prj_name', None) or 'API Documentation' - self._crossref = kwargs.get('crossref', 1) + self._show_crossrefs = kwargs.get('crossref', 1) self._index = kwargs.get('index', 1) + self._hyperlink = kwargs.get('hyperlink', True) self._list_classes_separately=kwargs.get('list_classes_separately',0) self._inheritance = kwargs.get('inheritance', 'listed') self._exclude = kwargs.get('exclude', 1) + self._list_submodules = kwargs.get('list_submodules', 1) + self._sty = kwargs.get('sty') self._top_section = 2 self._index_functions = 1 self._hyperref = 1 + + # [xx] check into this: + self._pdflatex = kwargs.get('action', '') == "pdflatex" + + self._graph_types = kwargs.get('graphs', ()) or () + """Graphs that we should include in our output.""" #: The Python representation of the encoding. #: Update L{latex_encodings} in case of mismatch between it and @@ -156,6 +81,10 @@ """The list of L{ClassDoc}s for the documented classes.""" self.class_set = set(self.class_list) """The set of L{ClassDoc}s for the documented classes.""" + self.module_list = [d for d in valdocs if isinstance(d, ModuleDoc)] + """The list of L{ModuleDoc}s for the documented modules.""" + self.module_set = set(self.module_list) + """The set of L{ModuleDoc}s for the documented modules.""" def write(self, directory=None): """ @@ -186,6 +115,9 @@ if not directory: directory = os.curdir self._mkdir(directory) self._directory = directory + + # Write the style file. + self._write_sty(directory, self._sty) # Write the top-level file. self._write(self.write_topfile, directory, 'api.tex') @@ -203,6 +135,35 @@ # Restore defaults that we changed. (ValueDoc.SUMMARY_REPR_LINELEN, ValueDoc.REPR_LINELEN, ValueDoc.REPR_MAXLINES) = orig_valdoc_defaults + + def _write_sty(self, directory, stylesheet, filename='epydoc.sty'): + """ + Copy the requested LaTeX stylesheet to the target directory. + The stylesheet can be specified as a name (i.e., a key from + the STYLESHEETS directory); a filename; or None for the default + stylesheet. If any stylesheet *other* than the default + stylesheet is selected, then the default stylesheet will be + copied to 'epydoc-default.sty', which makes it possible to + reference it via \RequirePackage. + """ + if stylesheet is None: + sty = STYLESHEETS['base'] + elif os.path.exists(stylesheet): + try: sty = open(stylesheet, 'rb').read() + except: raise IOError("Can't open LaTeX style file: %r" % + stylesheet) + self._write_sty(directory, None, 'epydoc-default.sty') + elif stylesheet in STYLESHEETS: + sty = STYLESHEETS[stylesheet] + if sty != STYLESHEETS['base']: + self._write_sty(directory, None, 'epydoc-default.sty') + else: + raise IOError("Can't find LaTeX style file: %r" % stylesheet) + + # Write the stylesheet. + out = open(os.path.join(directory, filename), 'wb') + out.write(sty) + out.close() def _write(self, write_func, directory, filename, *args): # Display our progress. @@ -273,9 +234,9 @@ # Add a table of contents. self.write_start_of(out, 'Table of Contents') - out('\\addtolength{\\parskip}{-2ex}\n') + out('\\addtolength{\\parskip}{-1ex}\n') out('\\tableofcontents\n') - out('\\addtolength{\\parskip}{2ex}\n') + out('\\addtolength{\\parskip}{1ex}\n') # Include documentation files. self.write_start_of(out, 'Includes') @@ -300,27 +261,35 @@ out('\\end{document}\n\n') def write_preamble(self, out): - out('\n'.join(self.PREAMBLE)) - out('\n') + # If we're generating an index, add it to the preamble. + options = [] + if self._index: options.append('index') + if self._hyperlink: options.append('hyperlink') + out('\n'.join(self.PREAMBLE) % (','.join(options),) + '\n') # Set the encoding. out('\\usepackage[%s]{inputenc}\n' % self.get_latex_encoding()) # If we're generating hyperrefs, add the appropriate packages. + # !!!!!!!!!!!!!!!!!!!!!! + # !!! JEG - this needs to be the last thing in the preamble + # !!!!!!!!!!!!!!!!!!!!!! if self._hyperref: out('\\definecolor{UrlColor}{rgb}{0,0.08,0.45}\n') - out('\\usepackage[dvips, pagebackref, pdftitle={%s}, ' + + if self._pdflatex: + driver = 'pdftex' + else: + driver = 'dvips' + + out('\\usepackage[%s, pagebackref, pdftitle={%s}, ' 'pdfcreator={epydoc %s}, bookmarks=true, ' 'bookmarksopen=false, pdfpagemode=UseOutlines, ' 'colorlinks=true, linkcolor=black, anchorcolor=black, ' 'citecolor=black, filecolor=black, menucolor=black, ' 'pagecolor=black, urlcolor=UrlColor]{hyperref}\n' % - (self._prj_name or '', epydoc.__version__)) + (driver, self._prj_name or '', epydoc.__version__)) - # If we're generating an index, add it to the preamble. - if self._index: - out("\\makeindex\n") - # If restructuredtext was used, then we need to extend # the prefix to include LatexTranslator.head_prefix. if 'restructuredtext' in epydoc.markup.MARKUP_LANGUAGES_USED: @@ -352,27 +321,28 @@ # Add a section marker. out(self.section('%s %s' % (self.doc_kind(doc), - doc.canonical_name))) + _dotted(doc.canonical_name)), + ref=doc)) - # Label our current location. - out(' \\label{%s}\n' % self.label(doc)) - # Add the module's description. if doc.descr not in (None, UNKNOWN): - out(self.docstring_to_latex(doc.descr)) + out(' '*4 + '\\begin{EpydocModuleDescription}%\n') + out(self.docstring_to_latex(doc.descr, 4)) + out(' '*4 + '\\end{EpydocModuleDescription}\n') # Add version, author, warnings, requirements, notes, etc. self.write_standard_fields(out, doc) # If it's a package, list the sub-modules. - if doc.submodules != UNKNOWN and doc.submodules: + if self._list_submodules and doc.submodules != UNKNOWN and doc.submodules: self.write_module_list(out, doc) # Contents. if self._list_classes_separately: self.write_class_list(out, doc) - self.write_func_list(out, 'Functions', doc, 'function') - self.write_var_list(out, 'Variables', doc, 'other') + self.write_list(out, 'Functions', doc, 'EpydocFunctionList', + 'function') + self.write_list(out, 'Variables', doc, 'EpydocVariableList', 'other') # Class list. if not self._list_classes_separately: @@ -384,6 +354,19 @@ # Mark the end of the module (for the index) out(' ' + self.indexterm(doc, 'end')) + def render_graph(self, graph): + if graph is None: return '' + graph.caption = graph.title = None + if self._pdflatex: + return graph.to_dot2tex() +## image_url = '%s.ps' % graph.uid +## image_file = os.path.join(self._directory, image_url) +## return graph.to_pdf(image_file, image_url) + else: + image_url = '%s.eps' % graph.uid + image_file = os.path.join(self._directory, image_url) + return graph.to_latex(image_file, image_url) + def write_class(self, out, doc): if self._list_classes_separately: self.write_header(out, doc) @@ -396,41 +379,60 @@ if self._list_classes_separately: seclevel = 0 out(self.section('%s %s' % (self.doc_kind(doc), - doc.canonical_name), seclevel)) + _dotted(doc.canonical_name)), + seclevel, ref=doc)) else: seclevel = 1 out(self.section('%s %s' % (self.doc_kind(doc), - doc.canonical_name[-1]), seclevel)) + _dotted(doc.canonical_name[-1])), + seclevel, ref=doc)) - # Label our current location. - out(' \\label{%s}\n' % self.label(doc)) + if ((doc.bases not in (UNKNOWN, None) and len(doc.bases) > 0) or + (doc.subclasses not in (UNKNOWN,None) and len(doc.subclasses)>0)): + # Display bases graphically, if requested. + if 'umlclasstree' in self._graph_types: +## linker = self._LatexDocstringLinker() + graph = uml_class_tree_graph(doc, self._docstring_linker, doc) + out(self.render_graph(graph)) + + elif 'classtree' in self._graph_types: +## linker = self._LatexDocstringLinker() + graph = class_tree_graph([doc], self._docstring_linker, doc) + out(self.render_graph(graph)) - # Add our base list. - if doc.bases not in (UNKNOWN, None) and len(doc.bases) > 0: - out(self.base_tree(doc)) + # Otherwise, use ascii-art. + else: + + # Add our base list. + if doc.bases not in (UNKNOWN, None) and len(doc.bases) > 0: + out(self.base_tree(doc)) - # The class's known subclasses - if doc.subclasses not in (UNKNOWN, None) and len(doc.subclasses) > 0: - sc_items = [plaintext_to_latex('%s' % sc.canonical_name) - for sc in doc.subclasses] - out(self._descrlist(sc_items, 'Known Subclasses', short=1)) + # The class's known subclasses + if doc.subclasses not in (UNKNOWN, None) and len(doc.subclasses) > 0: + sc_items = [_hyperlink(sc, '%s' % sc.canonical_name) + for sc in doc.subclasses] + out(self._descrlist(sc_items, 'Known Subclasses', short=1)) # The class's description. if doc.descr not in (None, UNKNOWN): + out(' '*4 + '\\begin{EpydocClassDescription}\n') out(self.docstring_to_latex(doc.descr)) + out(' '*4 + '\\end{EpydocClassDescription}\n') # Version, author, warnings, requirements, notes, etc. self.write_standard_fields(out, doc) # Contents. - self.write_func_list(out, 'Methods', doc, 'method', - seclevel+1) - self.write_var_list(out, 'Properties', doc, - 'property', seclevel+1) - self.write_var_list(out, 'Class Variables', doc, - 'classvariable', seclevel+1) - self.write_var_list(out, 'Instance Variables', doc, - 'instancevariable', seclevel+1) + self.write_list(out, 'Methods', doc, 'EpydocFunctionList', + 'method', seclevel+1) + self.write_list(out, 'Properties', doc, 'EpydocPropertyList', + 'property', seclevel+1) + self.write_list(out, 'Class Variables', doc, + 'EpydocClassVariableList', + 'classvariable', seclevel+1) + self.write_list(out, 'Instance Variables', doc, + 'EpydocInstanceVariableList', + 'instancevariable', seclevel+1) # Mark the end of the class (for the index) out(' ' + self.indexterm(doc, 'end')) @@ -455,23 +457,22 @@ def write_module_list(self, out, doc): if len(doc.submodules) == 0: return - self.write_start_of(out, 'Modules') + self.write_start_of(out, 'Submodules') - out(self.section('Modules', 1)) - out('\\begin{itemize}\n') - out('\\setlength{\\parskip}{0ex}\n') + out(self.section('Submodules', 1)) + out('\\begin{EpydocModuleList}\n') for group_name in doc.group_names(): if not doc.submodule_groups[group_name]: continue if group_name: - out(' \\item \\textbf{%s}\n' % group_name) - out(' \\begin{itemize}\n') + out(' \\EpydocGroup{%s}\n' % group_name) + out(' \\begin{EpydocModuleList}\n') for submodule in doc.submodule_groups[group_name]: self.write_module_tree_item(out, submodule) if group_name: - out(' \end{itemize}\n') + out(' \end{EpydocModuleList}\n') - out('\\end{itemize}\n\n') + out('\\end{EpydocModuleList}\n\n') def write_module_tree_item(self, out, doc, depth=0): """ @@ -479,19 +480,16 @@ @rtype: C{string} """ - out(' '*depth + '\\item \\textbf{') - out(plaintext_to_latex(doc.canonical_name[-1]) +'}') + out(' '*depth + '\\item[%s]' % _hyperlink(doc, doc.canonical_name[-1])) + if doc.summary not in (None, UNKNOWN): - out(': %s\n' % self.docstring_to_latex(doc.summary)) - if self._crossref: - out('\n \\textit{(Section \\ref{%s}' % self.label(doc)) - out(', p.~\\pageref{%s})}\n\n' % self.label(doc)) + out(' %s\n' % self.docstring_to_latex(doc.summary)) + out(self.crossref(doc) + '\n\n') if doc.submodules != UNKNOWN and doc.submodules: - out(' '*depth + ' \\begin{itemize}\n') - out(' '*depth + '\\setlength{\\parskip}{0ex}\n') + out(' '*depth + ' \\begin{EpydocModuleList}\n') for submodule in doc.submodules: self.write_module_tree_item(out, submodule, depth+4) - out(' '*depth + ' \\end{itemize}\n') + out(' '*depth + ' \\end{EpydocModuleList}\n') #//////////////////////////////////////////////////////////// #{ Base class trees @@ -502,7 +500,7 @@ width = self._find_tree_width(doc)+2 linespec = [] s = ('&'*(width-4)+'\\multicolumn{2}{l}{\\textbf{%s}}\n' % - plaintext_to_latex('%s'%self._base_name(doc))) + _dotted('%s'%self._base_name(doc))) s += '\\end{tabular}\n\n' top = 1 else: @@ -537,7 +535,7 @@ return width def _base_tree_line(self, doc, width, linespec): - base_name = plaintext_to_latex(self._base_name(doc)) + base_name = _dotted(self._base_name(doc)) # linespec is a list of booleans. s = '%% Line for %s, linespec=%s\n' % (base_name, linespec) @@ -546,8 +544,8 @@ # The base class name. s += ('\\multicolumn{%s}{r}{' % labelwidth) - s += '\\settowidth{\\BCL}{%s}' % base_name - s += '\\multirow{2}{\\BCL}{%s}}\n' % base_name + s += '\\settowidth{\\EpydocBCL}{%s}' % base_name + s += '\\multirow{2}{\\EpydocBCL}{%s}}\n' % _hyperlink(doc, self._base_name(doc)) # The vertical bars for other base classes (top half) for vbar in linespec: @@ -587,38 +585,36 @@ # Write a header. self.write_start_of(out, 'Classes') out(self.section('Classes', 1)) - out('\\begin{itemize}') - out(' \\setlength{\\parskip}{0ex}\n') + out('\\begin{EpydocClassList}\n') for name, var_docs in groups: if name: - out(' \\item \\textbf{%s}\n' % name) - out(' \\begin{itemize}\n') + out(' \\EpydocGroup{%s}\n' % name) + out(' \\begin{EpydocClassList}\n') # Add the lines for each class for var_doc in var_docs: self.write_class_list_line(out, var_doc) if name: - out(' \\end{itemize}\n') + out(' \\end{EpydocClassList}\n') - out('\\end{itemize}\n') + out('\\end{EpydocClassList}\n') def write_class_list_line(self, out, var_doc): if var_doc.value in (None, UNKNOWN): return # shouldn't happen doc = var_doc.value - out(' ' + '\\item \\textbf{') - out(plaintext_to_latex(var_doc.name) + '}') + out(' ' + '\\item[%s]' % _hyperlink(var_doc.target, + var_doc.name)) if doc.summary not in (None, UNKNOWN): out(': %s\n' % self.docstring_to_latex(doc.summary)) - if self._crossref: - out(('\n \\textit{(Section \\ref{%s}' % self.label(doc))) - out((', p.~\\pageref{%s})}\n\n' % self.label(doc))) + out(self.crossref(doc) + '\n\n') #//////////////////////////////////////////////////////////// - #{ Function List + #{ Details Lists #//////////////////////////////////////////////////////////// - _FUNC_GROUP_HEADER = '\n\\large{\\textbf{\\textit{%s}}}\n\n' - def write_func_list(self, out, heading, doc, value_type, seclevel=1): + # Also used for the property list. + def write_list(self, out, heading, doc, list_type, + value_type, seclevel=1): # Divide all public variables of the given type into groups. groups = [(plaintext_to_latex(group_name), doc.select_variables(group=group_name, imported=False, @@ -634,10 +630,12 @@ self.write_start_of(out, heading) out(' '+self.section(heading, seclevel)) + out('\\begin{%s}\n' % list_type) + # Write a section for each group. grouped_inh_vars = {} for name, var_docs in groups: - self.write_func_group(out, doc, name, var_docs, grouped_inh_vars) + self.write_list_group(out, doc, name, var_docs, grouped_inh_vars) # Write a section for each inheritance pseudo-group (used if # inheritance=='grouped') @@ -646,14 +644,19 @@ if base in grouped_inh_vars: hdr = ('Inherited from %s' % plaintext_to_latex('%s' % base.canonical_name)) - if self._crossref and base in self.class_set: - hdr += ('\\textit{(Section \\ref{%s})}' % - self.label(base)) - out(self._FUNC_GROUP_HEADER % (hdr)) + out(self.crossref(base) + '\n\n') + out('\\EpydocGroup{%s}\n' % hdr) for var_doc in grouped_inh_vars[base]: - self.write_func_list_box(out, var_doc) + if isinstance(var_doc.value, RoutineDoc): + self.write_function(out, var_doc) + elif isinstance(var_doc.value, PropertyDoc): + self.write_property(out, var_doc) + else: + self.write_var(out, var_doc) + + out('\\end{%s}\n\n' % list_type) - def write_func_group(self, out, doc, name, var_docs, grouped_inh_vars): + def write_list_group(self, out, doc, name, var_docs, grouped_inh_vars): # Split up the var_docs list, according to the way each var # should be displayed: # - listed_inh_vars -- for listed inherited variables. @@ -678,15 +681,20 @@ # Write a header for the group. if name: - out(self._FUNC_GROUP_HEADER % name) - # Write an entry for each normal var: + out('\\EpydocGroup{%s}\n' % name) + # Write an entry for each object in the group: for var_doc in normal_vars: - self.write_func_list_box(out, var_doc) - # Write a subsection for inherited vars: + if isinstance(var_doc.value, RoutineDoc): + self.write_function(out, var_doc) + elif isinstance(var_doc.value, PropertyDoc): + self.write_property(out, var_doc) + else: + self.write_var(out, var_doc) + # Write a subsection for inherited objects: if listed_inh_vars: - self.write_func_inheritance_list(out, doc, listed_inh_vars) - - def write_func_inheritance_list(self, out, doc, listed_inh_vars): + self.write_inheritance_list(out, doc, listed_inh_vars) + + def write_inheritance_list(self, out, doc, listed_inh_vars): for base in doc.mro(): if base not in listed_inh_vars: continue #if str(base.canonical_name) == 'object': continue @@ -694,333 +702,324 @@ if self._public_filter: var_docs = [v for v in var_docs if v.is_public] if var_docs: - hdr = ('Inherited from %s' % - plaintext_to_latex('%s' % base.canonical_name)) - if self._crossref and base in self.class_set: - hdr += ('\\textit{(Section \\ref{%s})}' % - self.label(base)) - out(self._FUNC_GROUP_HEADER % hdr) - out('\\begin{quote}\n') - out('%s\n' % ', '.join( - ['%s()' % plaintext_to_latex(var_doc.name) - for var_doc in var_docs])) - out('\\end{quote}\n') - - def write_func_list_box(self, out, var_doc): + out('\\EpydocInheritanceList{') + out(plaintext_to_latex('%s' % base.canonical_name)) + out(self.crossref(base)) + out('}{') + out(', '.join(['%s' % plaintext_to_latex(var_doc.name) + + self._parens_if_func(var_doc) + for var_doc in var_docs])) + out('}\n') + + def _parens_if_func(self, var_doc): + if isinstance(var_doc.value, RoutineDoc): return '()' + else: return '' + + #//////////////////////////////////////////////////////////// + #{ Function Details + #//////////////////////////////////////////////////////////// + + def write_function(self, out, var_doc): func_doc = var_doc.value is_inherited = (var_doc.overrides not in (None, UNKNOWN)) - # nb: this gives the containing section, not a reference + # Add the function to the index. Note: this will actually + # select the containing section, and won't give a reference # directly to the function. if not is_inherited: - out(' \\label{%s}\n' % self.label(func_doc)) - out(' %s\n' % self.indexterm(func_doc)) + out(' %s%%\n' % self.indexterm(func_doc)) - # Start box for this function. - out(' \\vspace{0.5ex}\n\n') - out('\\hspace{.8\\funcindent}') - out('\\begin{boxedminipage}{\\funcwidth}\n\n') + # This latex command takes 8 arguments. + out('\\EpydocFunction{%\n') - # Function signature. - out(' %s\n\n' % self.function_signature(var_doc)) + # Argument 1: the function signature + out(self.function_signature(var_doc)) + out('}{%\n') - if (func_doc.docstring not in (None, UNKNOWN) and - func_doc.docstring.strip() != ''): - out(' \\vspace{-1.5ex}\n\n') - out(' \\rule{\\textwidth}{0.5\\fboxrule}\n') - - # Description - out("\\setlength{\\parskip}{2ex}\n") + # Argument 2: the function description if func_doc.descr not in (None, UNKNOWN): out(self.docstring_to_latex(func_doc.descr, 4)) + out('}{%\n') - # Parameters - out("\\setlength{\\parskip}{1ex}\n") + # Argument 3: the function parameter descriptions if func_doc.arg_descrs or func_doc.arg_types: - # Find the longest name. - longest = max([0]+[len(n) for n in func_doc.arg_types]) - for names, descrs in func_doc.arg_descrs: - longest = max([longest]+[len(n) for n in names]) - # Table header. - out(' '*6+'\\textbf{Parameters}\n') - out(' \\vspace{-1ex}\n\n') - out(' '*6+'\\begin{quote}\n') - out(' \\begin{Ventry}{%s}\n\n' % (longest*'x')) - # Add params that have @type but not @param info: - arg_descrs = list(func_doc.arg_descrs) - args = set() - for arg_names, arg_descr in arg_descrs: - args.update(arg_names) - for arg in var_doc.value.arg_types: - if arg not in args: - arg_descrs.append( ([arg],None) ) - # Display params - for (arg_names, arg_descr) in arg_descrs: - arg_name = plaintext_to_latex(', '.join(arg_names)) - out('%s\\item[%s]\n\n' % (' '*10, arg_name)) - if arg_descr: - out(self.docstring_to_latex(arg_descr, 10)) - for arg_name in arg_names: - arg_typ = func_doc.arg_types.get(arg_name) - if arg_typ is not None: - if len(arg_names) == 1: - lhs = 'type' - else: - lhs = 'type of %s' % arg_name - rhs = self.docstring_to_latex(arg_typ).strip() - out('%s{\\it (%s=%s)}\n\n' % (' '*12, lhs, rhs)) - out(' \\end{Ventry}\n\n') - out(' '*6+'\\end{quote}\n\n') - - # Returns - rdescr = func_doc.return_descr - rtype = func_doc.return_type - if rdescr not in (None, UNKNOWN) or rtype not in (None, UNKNOWN): - out(' '*6+'\\textbf{Return Value}\n') - out(' \\vspace{-1ex}\n\n') - out(' '*6+'\\begin{quote}\n') - if rdescr not in (None, UNKNOWN): - out(self.docstring_to_latex(rdescr, 6)) - if rtype not in (None, UNKNOWN): - out(' '*6+'{\\it (type=%s)}\n\n' % - self.docstring_to_latex(rtype, 6).strip()) - elif rtype not in (None, UNKNOWN): - out(self.docstring_to_latex(rtype, 6)) - out(' '*6+'\\end{quote}\n\n') + self.write_function_parameters(out, var_doc) + out('}{%\n') - # Raises + # Argument 4: The return description + if func_doc.return_descr not in (None, UNKNOWN): + out(self.docstring_to_latex(func_doc.return_descr, 6)) + out('}{%\n') + + # Argument 5: The return type + if func_doc.return_type not in (None, UNKNOWN): + out(self.docstring_to_latex(func_doc.return_type, 6).strip()) + out('}{%\n') + + # Argument 6: The raises section if func_doc.exception_descrs not in (None, UNKNOWN, [], ()): - out(' '*6+'\\textbf{Raises}\n') - out(' \\vspace{-1ex}\n\n') - out(' '*6+'\\begin{quote}\n') - out(' \\begin{description}\n\n') + out(' '*6+'\\begin{EpydocFunctionRaises}\n') for name, descr in func_doc.exception_descrs: - out(' '*10+'\\item[\\texttt{%s}]\n\n' % + out(' '*10+'\\item[%s]\n\n' % plaintext_to_latex('%s' % name)) out(self.docstring_to_latex(descr, 10)) - out(' \\end{description}\n\n') - out(' '*6+'\\end{quote}\n\n') + out(' '*6+'\\end{EpydocFunctionRaises}\n\n') + out('}{%\n') - ## Overrides + # Argument 7: The overrides section if var_doc.overrides not in (None, UNKNOWN): - out(' Overrides: ' + - plaintext_to_latex('%s'%var_doc.overrides.canonical_name)) + out('\\EpydocFunctionOverrides') if (func_doc.docstring in (None, UNKNOWN) and var_doc.overrides.value.docstring not in (None, UNKNOWN)): - out(' \textit{(inherited documentation)}') - out('\n\n') + out('[1]') + out('{%s}\n\n' + % _hyperlink(var_doc.overrides, + '%s' % var_doc.overrides.canonical_name)) + out('}{%\n') - # Add version, author, warnings, requirements, notes, etc. + # Argument 8: The metadata section self.write_standard_fields(out, func_doc) + out('}\n') + - out(' \\end{boxedminipage}\n\n') - + def write_function_parameters(self, out, var_doc): + func_doc = var_doc.value + # Find the longest name. + longest = max([0]+[len(n) for n in func_doc.arg_types]) + for names, descrs in func_doc.arg_descrs: + longest = max([longest]+[len(n) for n in names]) + # Table header. + out(' '*6+'\\begin{EpydocFunctionParameters}{%s}\n' % (longest*'x')) + # Add params that have @type but not @param info: + arg_descrs = list(func_doc.arg_descrs) + args = set() + for arg_names, arg_descr in arg_descrs: + args.update(arg_names) + for arg in var_doc.value.arg_types: + if arg not in args: + arg_descrs.append( ([arg],None) ) + # Display params + for (arg_names, arg_descr) in arg_descrs: + arg_name = plaintext_to_latex(', '.join(arg_names)) + out('%s\\item[%s]\n\n' % (' '*10, arg_name)) + if arg_descr: + out(self.docstring_to_latex(arg_descr, 10)) + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + # !!! JEG - this loop needs abstracting + # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + for arg_name in arg_names: + arg_typ = func_doc.arg_types.get(arg_name) + if arg_typ is not None: + if len(arg_names) == 1: + lhs = 'type' + else: + lhs = 'type of %s' % arg_name + rhs = self.docstring_to_latex(arg_typ).strip() + out('%s{\\it (%s=%s)}\n\n' % (' '*12, lhs, rhs)) + out(' '*6+'\\end{EpydocFunctionParameters}\n\n') + def function_signature(self, var_doc): func_doc = var_doc.value func_name = var_doc.name - + + s = ('\\begin{EpydocFunctionSignature}{%s}%%\n' % + _hypertarget(var_doc, func_name)) + # This should never happen, but just in case: - if func_doc in (None, UNKNOWN): - return ('\\raggedright \\textbf{%s}(...)' % - plaintext_to_latex(func_name)) - - if func_doc.posargs == UNKNOWN: - args = ['...'] - else: - args = [self.func_arg(name, default) for (name, default) - in zip(func_doc.posargs, func_doc.posarg_defaults)] - if func_doc.vararg: - if func_doc.vararg == '...': - args.append('\\textit{...}') + if func_doc not in (None, UNKNOWN): + if func_doc.posargs == UNKNOWN: + args = ['\\GenericArg{}'] else: - args.append('*\\textit{%s}' % - plaintext_to_latex(func_doc.vararg)) - if func_doc.kwarg: - args.append('**\\textit{%s}' % - plaintext_to_latex(func_doc.kwarg)) - return ('\\raggedright \\textbf{%s}(%s)' % - (plaintext_to_latex(func_name), ', '.join(args))) + args = [self.func_arg(name, default) for (name, default) + in zip(func_doc.posargs, func_doc.posarg_defaults)] + if func_doc.vararg: + if func_doc.vararg == '...': + args.append('\\GenericArg{}') + else: + args.append('\\VarArg{%s}' % + plaintext_to_latex(func_doc.vararg)) + if func_doc.kwarg: + args.append('\\KWArg{%s}' % plaintext_to_latex(func_doc.kwarg)) + + s += ' '+'%\n \\and'.join(args)+'%\n' + s += '\\end{EpydocFunctionSignature}%\n' + + return s def func_arg(self, name, default): - s = '\\textit{%s}' % plaintext_to_latex(self._arg_name(name)) + s = '\\Param' + if default is not None: - s += '={\\tt %s}' % default.summary_pyval_repr().to_latex(None) + s += "[%s]" % default.summary_pyval_repr().to_latex(None) + s += '{%s}' % self._arg_name(name) + return s - + def _arg_name(self, arg): if isinstance(arg, basestring): - return arg - elif len(arg) == 1: - return '(%s,)' % self._arg_name(arg[0]) + return plaintext_to_latex(arg) else: - return '(%s)' % (', '.join([self._arg_name(a) for a in arg])) + return '\\TupleArg{%s}' % '\\and '.join([self._arg_name(a) + for a in arg]) - #//////////////////////////////////////////////////////////// - #{ Variable List - #//////////////////////////////////////////////////////////// - _VAR_GROUP_HEADER = '\\multicolumn{2}{|l|}{\\textit{%s}}\\\\\n' + +# def write_func_list_box(self, out, var_doc): +# func_doc = var_doc.value +# is_inherited = (var_doc.overrides not in (None, UNKNOWN)) - # Also used for the property list. - def write_var_list(self, out, heading, doc, value_type, seclevel=1): - groups = [(plaintext_to_latex(group_name), - doc.select_variables(group=group_name, imported=False, - value_type=value_type, - public=self._public_filter)) - for group_name in doc.group_names()] +# out('\\begin{EpydocFunction}%\n') +# # Function signature. +# out(self.function_signature(var_doc)) - # Discard any empty groups; and return if they're all empty. - groups = [(g,vars) for (g,vars) in groups if vars] - if not groups: return +# # nb: this gives the containing section, not a reference +# # directly to the function. +# if not is_inherited: +# out(' %s%%\n' % self.indexterm(func_doc)) - # Write a header. - self.write_start_of(out, heading) - out(' '+self.section(heading, seclevel)) - - # [xx] without this, there's a huge gap before the table -- why?? - out(' \\vspace{-1cm}\n') +# # If we have nothing else to say, then don't create an +# # \EpydocFunctionInfo environment. +# if not (func_doc.descr not in (None, UNKNOWN) or +# func_doc.arg_descrs or func_doc.arg_types or +# func_doc.return_descr not in (None, UNKNOWN) or +# func_doc.return_type not in (None, UNKNOWN) or +# func_doc.exception_descrs not in (None, UNKNOWN, [], ()) or +# var_doc.overrides not in (None, UNKNOWN) or +# func_doc.metadata not in (None, UNKNOWN, [], ())): +# out(' \\end{EpydocFunction}\n\n') +# return - out('\\hspace{\\varindent}') - out('\\begin{longtable}') - out('{|p{\\varnamewidth}|') - out('p{\\vardescrwidth}|l}\n') - out('\\cline{1-2}\n') +# out('\\begin{EpydocFunctionInfo}%\n') + +# # Description +# if func_doc.descr not in (None, UNKNOWN): +# out(' '*4 + '\\begin{EpydocFunctionDescription}\n') +# out(self.docstring_to_latex(func_doc.descr, 4)) +# out(' '*4 + '\\end{EpydocFunctionDescription}\n') - # Set up the headers & footer (this makes the table span - # multiple pages in a happy way). - out('\\cline{1-2} ') - out('\\centering \\textbf{Name} & ') - out('\\centering \\textbf{Description}& \\\\\n') - out('\\cline{1-2}\n') - out('\\endhead') - out('\\cline{1-2}') - out('\\multicolumn{3}{r}{\\small\\textit{') - out('continued on next page}}\\\\') - out('\\endfoot') - out('\\cline{1-2}\n') - out('\\endlastfoot') +# # Parameters +# if func_doc.arg_descrs or func_doc.arg_types: +# # Find the longest name. +# longest = max([0]+[len(n) for n in func_doc.arg_types]) +# for names, descrs in func_doc.arg_descrs: +# longest = max([longest]+[len(n) for n in names]) +# # Table header. +# out(' '*6+'\\begin{EpydocFunctionParameters}{%s}\n' % (longest*'x')) +# # Add params that have @type but not @param info: +# arg_descrs = list(func_doc.arg_descrs) +# args = set() +# for arg_names, arg_descr in arg_descrs: +# args.update(arg_names) +# for arg in var_doc.value.arg_types: +# if arg not in args: +# arg_descrs.append( ([arg],None) ) +# # Display params +# for (arg_names, arg_descr) in arg_descrs: +# arg_name = plaintext_to_latex(', '.join(arg_names)) +# out('%s\\item[%s]\n\n' % (' '*10, arg_name)) +# if arg_descr: +# out(self.docstring_to_latex(arg_descr, 10)) +# # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +# # !!! JEG - this loop needs abstracting +# # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +# for arg_name in arg_names: +# arg_typ = func_doc.arg_types.get(arg_name) +# if arg_typ is not None: +# if len(arg_names) == 1: +# lhs = 'type' +# else: +# lhs = 'type of %s' % arg_name +# rhs = self.docstring_to_latex(arg_typ).strip() +# out('%s{\\it (%s=%s)}\n\n' % (' '*12, lhs, rhs)) +# out(' '*6+'\\end{EpydocFunctionParameters}\n\n') + +# # Returns +# rdescr = func_doc.return_descr +# rtype = func_doc.return_type +# if rdescr not in (None, UNKNOWN) or rtype not in (None, UNKNOWN): +# out(' '*6+'\\EpydocFunctionReturns') +# if rtype not in (None, UNKNOWN): +# out('[%s]' % self.docstring_to_latex(rtype, 6).strip()) +# if rdescr not in (None, UNKNOWN): +# out('{%s}' % self.docstring_to_latex(rdescr, 6)) +# else: +# out('{}') +# out('\n\n') - # Write a section for each group. - grouped_inh_vars = {} - for name, var_docs in groups: - self.write_var_group(out, doc, name, var_docs, grouped_inh_vars) +# # Raises +# if func_doc.exception_descrs not in (None, UNKNOWN, [], ()): +# out(' '*6+'\\begin{EpydocFunctionRaises}\n') +# for name, descr in func_doc.exception_descrs: +# out(' '*10+'\\item[%s]\n\n' % +# plaintext_to_latex('%s' % name)) +# out(self.docstring_to_latex(descr, 10)) +# out(' '*6+'\\end{EpydocFunctionRaises}\n\n') - # Write a section for each inheritance pseudo-group (used if - # inheritance=='grouped') - if grouped_inh_vars: - for base in doc.mro(): - if base in grouped_inh_vars: - hdr = ('Inherited from %s' % - plaintext_to_latex('%s' % base.canonical_name)) - if self._crossref and base in self.class_set: - hdr += (' \\textit{(Section \\ref{%s})}' % - self.label(base)) - out(self._VAR_GROUP_HEADER % (hdr)) - out('\\cline{1-2}\n') - for var_doc in grouped_inh_vars[base]: - if isinstance(var_doc.value3, PropertyDoc): - self.write_property_list_line(out, var_doc) - else: - self.write_var_list_line(out, var_doc) - - out('\\end{longtable}\n\n') - - def write_var_group(self, out, doc, name, var_docs, grouped_inh_vars): - # Split up the var_docs list, according to the way each var - # should be displayed: - # - listed_inh_vars -- for listed inherited variables. - # - grouped_inh_vars -- for grouped inherited variables. - # - normal_vars -- for all other variables. - listed_inh_vars = {} - normal_vars = [] - for var_doc in var_docs: - if var_doc.container != doc: - base = var_doc.container - if (base not in self.class_set or - self._inheritance == 'listed'): - listed_inh_vars.setdefault(base,[]).append(var_doc) - elif self._inheritance == 'grouped': - grouped_inh_vars.setdefault(base,[]).append(var_doc) - elif self._inheritance == 'hidden': - pass - else: - normal_vars.append(var_doc) - else: - normal_vars.append(var_doc) - - # Write a header for the group. - if name: - out(self._VAR_GROUP_HEADER % name) - out('\\cline{1-2}\n') - # Write an entry for each normal var: - for var_doc in normal_vars: - if isinstance(var_doc.value, PropertyDoc): - self.write_property_list_line(out, var_doc) - else: - self.write_var_list_line(out, var_doc) - # Write a subsection for inherited vars: - if listed_inh_vars: - self.write_var_inheritance_list(out, doc, listed_inh_vars) +# ## Overrides +# if var_doc.overrides not in (None, UNKNOWN): +# out('\\EpydocFunctionOverrides') +# if (func_doc.docstring in (None, UNKNOWN) and +# var_doc.overrides.value.docstring not in (None, UNKNOWN)): +# out('[1]') +# out('{%s}\n\n' +# % _hyperlink(var_doc.overrides, +# '%s' % var_doc.overrides.canonical_name)) - def write_var_inheritance_list(self, out, doc, listed_inh_vars): - for base in doc.mro(): - if base not in listed_inh_vars: continue - #if str(base.canonical_name) == 'object': continue - var_docs = listed_inh_vars[base] - if self._public_filter: - var_docs = [v for v in var_docs if v.is_public] - if var_docs: - hdr = ('Inherited from %s' % - plaintext_to_latex('%s' % base.canonical_name)) - if self._crossref and base in self.class_set: - hdr += (' \\textit{(Section \\ref{%s})}' % - self.label(base)) - out(self._VAR_GROUP_HEADER % hdr) - out('\\multicolumn{2}{|p{\\varwidth}|}{' - '\\raggedright %s}\\\\\n' % - ', '.join(['%s' % plaintext_to_latex(var_doc.name) - for var_doc in var_docs])) - out('\\cline{1-2}\n') +# # Add version, author, warnings, requirements, notes, etc. +# self.write_standard_fields(out, func_doc) - - def write_var_list_line(self, out, var_doc): - out('\\raggedright ') - out(plaintext_to_latex(var_doc.name, nbsp=True, breakany=True)) - out(' & ') +# out(' \\end{EpydocFunctionInfo}\n') +# out(' \\end{EpydocFunction}\n\n') + + #//////////////////////////////////////////////////////////// + #{ Variable Details + #//////////////////////////////////////////////////////////// + + def write_var(self, out, var_doc): has_descr = var_doc.descr not in (None, UNKNOWN) has_type = var_doc.type_descr not in (None, UNKNOWN) - has_value = var_doc.value is not UNKNOWN - if has_type or has_value: - out('\\raggedright ') + 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)) + + out('\\EpydocVariable{%s}{' % _hypertarget(var_doc, var_doc.name)) if has_descr: out(self.docstring_to_latex(var_doc.descr, 10).strip()) - if has_type or has_value: out('\n\n') - if has_value: - out('\\textbf{Value:} \n{\\tt %s}' % - var_doc.value.summary_pyval_repr().to_latex(None)) + out('}{') if has_type: - ptype = self.docstring_to_latex(var_doc.type_descr, 12).strip() - out('%s{\\it (type=%s)}' % (' '*12, ptype)) - out('&\\\\\n') - out('\\cline{1-2}\n') + out(self.docstring_to_latex(var_doc.type_descr, 12).strip()) + out('}{') + if has_repr: + out(var_doc.value.summary_pyval_repr().to_latex(None)) + out('}\n') - def write_property_list_line(self, out, var_doc): + #//////////////////////////////////////////////////////////// + #{ Property Details + #//////////////////////////////////////////////////////////// + + def write_property(self, out, var_doc): prop_doc = var_doc.value - out('\\raggedright ') - out(plaintext_to_latex(var_doc.name, nbsp=True, breakany=True)) - out(' & ') has_descr = prop_doc.descr not in (None, UNKNOWN) has_type = prop_doc.type_descr not in (None, UNKNOWN) - if has_descr or has_type: - out('\\raggedright ') + out('\\EpydocProperty{%s}{' % _hypertarget(var_doc, var_doc.name)) if has_descr: out(self.docstring_to_latex(prop_doc.descr, 10).strip()) - if has_type: out('\n\n') + out('}{') if has_type: - ptype = self.docstring_to_latex(prop_doc.type_descr, 12).strip() - out('%s{\\it (type=%s)}' % (' '*12, ptype)) - # [xx] List the fget/fset/fdel functions? - out('&\\\\\n') - out('\\cline{1-2}\n') + out(self.docstring_to_latex(prop_doc.type_descr, 12).strip()) + out('}{') + # [xx] What if the accessor is private and show_private=False? + if (prop_doc.fget not in (None, UNKNOWN) and + not prop_doc.fget.canonical_name[0].startswith('??')): + out(_dotted(prop_doc.fget.canonical_name)) + out('}{') + if (prop_doc.fset not in (None, UNKNOWN) and + not prop_doc.fset.canonical_name[0].startswith('??')): + out(_dotted(prop_doc.fset.canonical_name)) + out('}{') + if (prop_doc.fdel not in (None, UNKNOWN) and + not prop_doc.fdel.canonical_name[0].startswith('??')): + out(_dotted(prop_doc.fdel.canonical_name)) + out('}\n') #//////////////////////////////////////////////////////////// #{ Standard Fields @@ -1063,18 +1062,18 @@ if plural is None: plural = singular if len(items) == 0: return '' if len(items) == 1 and singular is not None: - return '\\textbf{%s:} %s\n\n' % (singular, items[0]) + return ('\\EpydocMetadataSingleValue{%s}{%s}\n\n' % + (singular, items[0])) if short: - s = '\\textbf{%s:}\n' % plural - items = [item.strip() for item in items] - return s + ',\n '.join(items) + '\n\n' + s = '\\begin{EpydocMetadataShortList}{%s}%%\n ' % plural + s += '%\n \\and '.join([item.strip() for item in items]) + s += '%\n\\end{EpydocMetadataShortList}\n\n' + return s else: - s = '\\textbf{%s:}\n' % plural - s += '\\begin{quote}\n' - s += ' \\begin{itemize}\n\n \item\n' - s += ' \\setlength{\\parskip}{0.6ex}\n' - s += '\n\n \item '.join(items) - return s + '\n\n\\end{itemize}\n\n\\end{quote}\n\n' + s = '\\begin{EpydocMetadataLongList}{%s}%%\n' % plural + s += '\n\n'.join([' \item %s%%' % item for item in items]) + s += '\n\\end{EpydocMetadataLongList}\n\n' + return s #//////////////////////////////////////////////////////////// @@ -1089,12 +1088,32 @@ def translate_identifier_xref(self, identifier, label=None): if label is None: label = markup.plaintext_to_latex(identifier) return '\\texttt{%s}' % label + # [xx] Should this be added to the DocstringLinker interface??? + # Currently, this is *only* used by dotgraph. + def url_for(self, identifier): + return None +## if isinstance(identifier, (basestring, DottedName)): +## doc = self.docindex.find(identifier, self.container) +## if doc: +## return identifier +## else: +## return None +## +## elif isinstance(identifier, APIDoc): +## return ':'.join(identifier.canonical_name) +## doc = identifier +## +## else: +## raise TypeError('Expected string or APIDoc') _docstring_linker = _LatexDocstringLinker() def docstring_to_latex(self, docstring, indent=0, breakany=0): if docstring is None: return '' - return docstring.to_latex(self._docstring_linker, indent=indent, - hyperref=self._hyperref) + s = docstring.to_latex(self._docstring_linker, indent=indent, + hyperref=self._hyperref) + return (' '*indent + '\\begin{EpydocDescription}%\n' + + s.strip() + '%\n' + + ' '*indent + '\\end{EpydocDescription}\n\n') #//////////////////////////////////////////////////////////// #{ Helpers @@ -1112,18 +1131,23 @@ def write_start_of(self, out, section_name): out('\n' + 75*'%' + '\n') - out('%%' + ((71-len(section_name))/2)*' ') - out(section_name) - out(((72-len(section_name))/2)*' ' + '%%\n') + out('%%' + section_name.center(71) + '%%\n') out(75*'%' + '\n\n') - def section(self, title, depth=0): + def section(self, title, depth=0, ref=None): sec = self.SECTIONS[depth+self._top_section] - return (('%s\n\n' % sec) % plaintext_to_latex(title)) - - def sectionstar(self, title, depth): + text = (sec % title) + '%\n' + if ref: + text += _hypertarget(ref, "") + '%\n' + return text + + # [xx] not used: + def sectionstar(self, title, depth, ref=None): sec = self.STARSECTIONS[depth+self._top_section] - return (('%s\n\n' % sec) % plaintext_to_latex(title)) + text = (sec % title) + '%\n' + if ref: + text += _hypertarget(ref, "") + '%\n' + return text def doc_kind(self, doc): if isinstance(doc, ModuleDoc) and doc.is_package == True: @@ -1154,28 +117... [truncated message content] |
From: <ed...@us...> - 2008-02-23 00:52:24
|
Revision: 1733 http://epydoc.svn.sourceforge.net/epydoc/?rev=1733&view=rev Author: edloper Date: 2008-02-22 16:52:21 -0800 (Fri, 22 Feb 2008) Log Message: ----------- - Fixed minor bug in handling of markup like `foo <bar>` Modified Paths: -------------- trunk/epydoc/src/epydoc/markup/restructuredtext.py Modified: trunk/epydoc/src/epydoc/markup/restructuredtext.py =================================================================== --- trunk/epydoc/src/epydoc/markup/restructuredtext.py 2008-02-20 21:14:12 UTC (rev 1732) +++ trunk/epydoc/src/epydoc/markup/restructuredtext.py 2008-02-23 00:52:21 UTC (rev 1733) @@ -89,6 +89,7 @@ from epydoc.apidoc import ModuleDoc, ClassDoc from epydoc.docwriter.dotgraph import * from epydoc.docwriter.xlink import ApiLinkReader +from epydoc.util import wordwrap, plaintext_to_html, plaintext_to_latex from epydoc.markup.doctest import doctest_to_html, doctest_to_latex, \ HTMLDoctestColorizer @@ -201,7 +202,7 @@ # Inherit docs visitor = _EpydocLaTeXTranslator(self._document, docstring_linker) self._document.walkabout(visitor) - return ''.join(visitor.body) + return ''.join(visitor.body).strip()+'\n' def to_plaintext(self, docstring_linker, **options): # This is should be replaced by something better: @@ -565,6 +566,7 @@ m = _TARGET_RE.match(node.astext()) if m: text, target = m.groups() else: target = text = node.astext() + text = plaintext_to_latex(text) xref = self._linker.translate_identifier_xref(target, text) self.body.append(xref) raise SkipNode() @@ -605,6 +607,7 @@ m = _TARGET_RE.match(node.astext()) if m: text, target = m.groups() else: target = text = node.astext() + text = plaintext_to_latex(text) xref = self._linker.translate_identifier_xref(target, text) self.body.append(xref) raise SkipNode() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2008-02-20 21:14:16
|
Revision: 1732 http://epydoc.svn.sourceforge.net/epydoc/?rev=1732&view=rev Author: edloper Date: 2008-02-20 13:14:12 -0800 (Wed, 20 Feb 2008) Log Message: ----------- - Added "--inheritance hidden" to not list inherited vars at all. Modified Paths: -------------- trunk/epydoc/src/epydoc/cli.py trunk/epydoc/src/epydoc/docwriter/html.py trunk/epydoc/src/epydoc/docwriter/latex.py Modified: trunk/epydoc/src/epydoc/cli.py =================================================================== --- trunk/epydoc/src/epydoc/cli.py 2008-02-20 00:49:48 UTC (rev 1731) +++ trunk/epydoc/src/epydoc/cli.py 2008-02-20 21:14:12 UTC (rev 1732) @@ -82,7 +82,7 @@ except: xlink = None -INHERITANCE_STYLES = ('grouped', 'listed', 'included') +INHERITANCE_STYLES = ('grouped', 'listed', 'included', 'hidden') GRAPH_TYPES = ('classtree', 'callgraph', 'umlclasstree') ACTIONS = ('html', 'text', 'latex', 'dvi', 'ps', 'pdf', 'check') DEFAULT_DOCFORMAT = 'epytext' Modified: trunk/epydoc/src/epydoc/docwriter/html.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/html.py 2008-02-20 00:49:48 UTC (rev 1731) +++ trunk/epydoc/src/epydoc/docwriter/html.py 2008-02-20 21:14:12 UTC (rev 1732) @@ -383,7 +383,8 @@ self._public_filter = True # Make sure inheritance has a sane value. - if self._inheritance not in ('listed', 'included', 'grouped'): + if self._inheritance not in ('listed', 'included', + 'grouped', 'hidden'): raise ValueError, 'Bad value for inheritance' # Create the project homepage link, if it was not specified. @@ -2056,6 +2057,8 @@ listed_inh_vars.setdefault(base,[]).append(var_doc) elif self._inheritance == 'grouped': grouped_inh_vars.setdefault(base,[]).append(var_doc) + elif self._inheritance == 'hidden': + pass else: normal_vars.append(var_doc) else: Modified: trunk/epydoc/src/epydoc/docwriter/latex.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/latex.py 2008-02-20 00:49:48 UTC (rev 1731) +++ trunk/epydoc/src/epydoc/docwriter/latex.py 2008-02-20 21:14:12 UTC (rev 1732) @@ -669,6 +669,8 @@ listed_inh_vars.setdefault(base,[]).append(var_doc) elif self._inheritance == 'grouped': grouped_inh_vars.setdefault(base,[]).append(var_doc) + elif self._inheritance == 'hidden': + pass else: normal_vars.append(var_doc) else: @@ -938,6 +940,8 @@ listed_inh_vars.setdefault(base,[]).append(var_doc) elif self._inheritance == 'grouped': grouped_inh_vars.setdefault(base,[]).append(var_doc) + elif self._inheritance == 'hidden': + pass else: normal_vars.append(var_doc) else: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2008-02-20 00:49:51
|
Revision: 1731 http://epydoc.svn.sourceforge.net/epydoc/?rev=1731&view=rev Author: edloper Date: 2008-02-19 16:49:48 -0800 (Tue, 19 Feb 2008) Log Message: ----------- - Use 'show_private' option to specify whether to show/hide private vars. NOTE: This changes the default behavior of that latex writer. Previously, it hid private vars by default; now it will show them by default. Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/latex.py Modified: trunk/epydoc/src/epydoc/docwriter/latex.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/latex.py 2008-02-19 16:51:20 UTC (rev 1730) +++ trunk/epydoc/src/epydoc/docwriter/latex.py 2008-02-20 00:49:48 UTC (rev 1731) @@ -128,7 +128,7 @@ def __init__(self, docindex, **kwargs): self.docindex = docindex # Process keyword arguments - self._show_private = kwargs.get('private', 0) + self._show_private = kwargs.get('show_private', 0) self._prj_name = kwargs.get('prj_name', None) or 'API Documentation' self._crossref = kwargs.get('crossref', 1) self._index = kwargs.get('index', 1) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2008-02-19 16:51:23
|
Revision: 1730 http://epydoc.svn.sourceforge.net/epydoc/?rev=1730&view=rev Author: edloper Date: 2008-02-19 08:51:20 -0800 (Tue, 19 Feb 2008) Log Message: ----------- - If we're using introspection, then import everything that we plan to document before we start introspecting anything. This can be important if one module modifies another. The bug that inspired this fix was sf bug #1896919. Modified Paths: -------------- trunk/epydoc/src/epydoc/docbuilder.py Modified: trunk/epydoc/src/epydoc/docbuilder.py =================================================================== --- trunk/epydoc/src/epydoc/docbuilder.py 2008-02-19 16:49:16 UTC (rev 1729) +++ trunk/epydoc/src/epydoc/docbuilder.py 2008-02-19 16:51:20 UTC (rev 1730) @@ -70,6 +70,7 @@ import sys, os, os.path, __builtin__, imp, re, inspect from epydoc.apidoc import * from epydoc.docintrospecter import introspect_docs +from epydoc.docintrospecter import get_value_from_filename, get_value_from_name from epydoc.docparser import parse_docs, ParseError from epydoc.docstringparser import parse_docstring from epydoc import log @@ -203,7 +204,12 @@ return None # Get the basic docs for each item. + log.start_progress('Building documentation') + if introspect: + # Import everything before we introspect anything. + _import_docs_from_items(items, options) doc_pairs = _get_docs_from_items(items, options) + log.end_progress() # Merge the introspection & parse docs. if options.parse and options.introspect: @@ -317,13 +323,67 @@ log.progress(float(i)/len(val_docs), val_doc.canonical_name) #///////////////////////////////////////////////////////////////// +# Pre-Import +#///////////////////////////////////////////////////////////////// + +def _import_docs_from_items(items, options): + for item in items: + # Make sure the item's module is imported. + if isinstance(item, basestring): + if os.path.isfile(item): + _do_import(item, options) + elif is_package_dir(item): + pkg = os.path.abspath(os.path.join(item, '__init__.py')) + val = _do_import(pkg, options) + if options.add_submodules and inspect.ismodule(val): + _import_docs_from_package(val, options) + elif is_pyname(item): + if options.must_introspect(item): + val = get_value_from_name(item) + if options.add_submodules and inspect.ismodule(val): + _import_docs_from_package(val, options) + +def _import_docs_from_package(pkg, options): + subpackage_filenames = set() + module_filenames = {} + pkg_path = getattr(pkg, '__path__', ()) + for subdir in pkg_path: + if os.path.isdir(subdir): + for name in os.listdir(subdir): + filename = os.path.join(subdir, name) + if is_module_file(filename): + basename = os.path.splitext(filename)[0] + if os.path.split(basename)[1] != '__init__': + module_filenames[basename] = filename + elif is_package_dir(filename): + subpackage_filenames.add(os.path.join(filename, + '__init__.py')) + + for filename in module_filenames.values(): + _do_import(filename, options, pkg.__name__) + for subpackage_filename in subpackage_filenames: + subpackage = _do_import(subpackage_filename, options, pkg.__name__) + if inspect.ismodule(subpackage): + _import_docs_from_package(subpackage, options) + +def _do_import(filename, options, parent=None): + filename = os.path.abspath(filename) + modulename = os.path.splitext(os.path.split(filename)[1])[0] + if modulename == '__init__': + modulename = os.path.split(os.path.split(filename)[0])[1] + if parent: + modulename = DottedName(parent, modulename) + if options.must_introspect(modulename): + log.progress(0, 'Importing %s' % modulename) + #log.debug('importing %r (%s)' % (filename, modulename)) + return get_value_from_filename(filename) + +#///////////////////////////////////////////////////////////////// # Documentation Generation #///////////////////////////////////////////////////////////////// def _get_docs_from_items(items, options): - - # Start the progress bar. - log.start_progress('Building documentation') + # Used to estimate progress when there are packages: progress_estimator = _ProgressEstimator(items) # Check for duplicate item names. @@ -397,7 +457,6 @@ doc_pairs += _get_docs_from_submodules( item, doc_pairs[-1], options, progress_estimator) - log.end_progress() return doc_pairs def _get_docs_from_pyobject(obj, options, progress_estimator): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2008-02-19 16:49:21
|
Revision: 1729 http://epydoc.svn.sourceforge.net/epydoc/?rev=1729&view=rev Author: edloper Date: 2008-02-19 08:49:16 -0800 (Tue, 19 Feb 2008) Log Message: ----------- - Fixed typo in error message Modified Paths: -------------- trunk/epydoc/src/epydoc/apidoc.py Modified: trunk/epydoc/src/epydoc/apidoc.py =================================================================== --- trunk/epydoc/src/epydoc/apidoc.py 2008-02-18 18:25:40 UTC (rev 1728) +++ trunk/epydoc/src/epydoc/apidoc.py 2008-02-19 16:49:16 UTC (rev 1729) @@ -506,7 +506,7 @@ if other.__has_been_hashed and not ignore_hash_conflict: raise ValueError("%r has already been hashed! Merging it " - "would cause its has value to change." % other) + "would cause its hash value to change." % other) # If other was itself already merged with anything, # then we need to merge those too. This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2008-02-18 18:25:49
|
Revision: 1728 http://epydoc.svn.sourceforge.net/epydoc/?rev=1728&view=rev Author: edloper Date: 2008-02-18 10:25:40 -0800 (Mon, 18 Feb 2008) Log Message: ----------- - When constructing UML graph nodes, guard against the case where class_doc.sorted_variables is UNKNOWN. This is known to happen if the user defines a class that subclasses from an undocumented class that itself has a nested class. E.g., Tkinter.Misc.getint. Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/dotgraph.py Modified: trunk/epydoc/src/epydoc/docwriter/dotgraph.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2008-02-16 04:50:59 UTC (rev 1727) +++ trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2008-02-18 18:25:40 UTC (rev 1728) @@ -491,16 +491,17 @@ show_private = options.get('show_private_vars', False) show_magic = options.get('show_magic_vars', True) show_inherited = options.get('show_inherited_vars', False) - for var in class_doc.sorted_variables: - name = var.canonical_name[-1] - if ((not show_private and var.is_public == False) or - (not show_magic and re.match('__\w+__$', name)) or - (not show_inherited and var.container != class_doc)): - pass - elif isinstance(var.value, RoutineDoc): - self.operations.append(var) - else: - self.attributes.append(var) + if class_doc.sorted_variables not in (None, UNKNOWN): + for var in class_doc.sorted_variables: + name = var.canonical_name[-1] + if ((not show_private and var.is_public == False) or + (not show_magic and re.match('__\w+__$', name)) or + (not show_inherited and var.container != class_doc)): + pass + elif isinstance(var.value, RoutineDoc): + self.operations.append(var) + else: + self.attributes.append(var) # Initialize our dot node settings. tooltip = self._summary(class_doc) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2008-02-16 04:51:01
|
Revision: 1727 http://epydoc.svn.sourceforge.net/epydoc/?rev=1727&view=rev Author: edloper Date: 2008-02-15 20:50:59 -0800 (Fri, 15 Feb 2008) Log Message: ----------- - In mro calculation: if there's an inconsistent base class hierarchy, then print an error and recover using depth first search. Modified Paths: -------------- trunk/epydoc/src/epydoc/apidoc.py Modified: trunk/epydoc/src/epydoc/apidoc.py =================================================================== --- trunk/epydoc/src/epydoc/apidoc.py 2008-02-16 04:49:50 UTC (rev 1726) +++ trunk/epydoc/src/epydoc/apidoc.py 2008-02-16 04:50:59 UTC (rev 1727) @@ -1297,7 +1297,13 @@ def mro(self, warn_about_bad_bases=False): if self.is_newstyle_class(): - return self._c3_mro(warn_about_bad_bases) + try: + return self._c3_mro(warn_about_bad_bases) + except ValueError, e: # (inconsistent hierarchy) + log.error('Error finding mro for %s: %s' % + (self.canonical_name, e)) + # Better than nothing: + return self._dfs_bases([], set(), warn_about_bad_bases) else: return self._dfs_bases([], set(), warn_about_bad_bases) @@ -1355,7 +1361,7 @@ nothead=[s for s in nonemptyseqs if cand in s[1:]] if nothead: cand=None #reject candidate else: break - if not cand: raise "Inconsistent hierarchy" + if not cand: raise ValueError("Inconsistent hierarchy") res.append(cand) for seq in nonemptyseqs: # remove cand if seq[0] == cand: del seq[0] This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2008-02-16 04:49:51
|
Revision: 1726 http://epydoc.svn.sourceforge.net/epydoc/?rev=1726&view=rev Author: edloper Date: 2008-02-15 20:49:50 -0800 (Fri, 15 Feb 2008) Log Message: ----------- - Display more useful error message when '--graph' gets something unexpected. Modified Paths: -------------- trunk/epydoc/src/epydoc/cli.py Modified: trunk/epydoc/src/epydoc/cli.py =================================================================== --- trunk/epydoc/src/epydoc/cli.py 2008-02-16 04:49:21 UTC (rev 1725) +++ trunk/epydoc/src/epydoc/cli.py 2008-02-16 04:49:50 UTC (rev 1726) @@ -512,7 +512,8 @@ options.graphs = [g for g in GRAPH_TYPES if g != 'callgraph'] break elif graph_type not in GRAPH_TYPES: - optparser.error("Invalid graph type %s." % graph_type) + optparser.error("Invalid graph type %s. Expected one of: %s." % + (graph_type, ', '.join(GRAPH_TYPES + ('all',)))) # Calculate verbosity. verbosity = getattr(options, 'verbosity', 0) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2008-02-16 04:49:24
|
Revision: 1725 http://epydoc.svn.sourceforge.net/epydoc/?rev=1725&view=rev Author: edloper Date: 2008-02-15 20:49:21 -0800 (Fri, 15 Feb 2008) Log Message: ----------- - refactored class_tree_graph & uml_class_tree_graph -- now they share most of their code. - added truncation when class graphs get too big: max_subclass_depth truncates subclasses that are too far down in the tree, and max_subclasses limits the number of subclasses that are shown for any given class. - undocumented base classes are now drawn with a grey background - uml graph options: - Changed default values for max_attributes & max_operations - Added options to control whether parameter defaults are shown; and to truncate signatures that are too long - set minimum width for uml nodes to 100 - Fixed bug in DotGraphNode -- subclasses should share the same _next_id var, not use their own. - If dot fails & epydoc.DEBUG is true, then print the broken dotfile to a temp file. Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/dotgraph.py Modified: trunk/epydoc/src/epydoc/docwriter/dotgraph.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2008-02-16 02:02:58 UTC (rev 1724) +++ trunk/epydoc/src/epydoc/docwriter/dotgraph.py 2008-02-16 04:49:21 UTC (rev 1725) @@ -33,6 +33,7 @@ SELECTED_BG = '#ffd0d0' BASECLASS_BG = '#e0b0a0' SUBCLASS_BG = '#e0b0a0' +UNDOCUMENTED_BG = '#c0c0c0' ROUTINE_BG = '#e8d0b0' # maybe? INH_LINK_COLOR = '#800000' @@ -43,7 +44,7 @@ DOT_COMMAND = 'dot' """The command that should be used to spawn dot""" -class DotGraph: +class DotGraph(object): """ A ``dot`` directed graph. The contents of the graph are constructed from the following instance variables: @@ -248,8 +249,15 @@ self.to_dotfile()) if err: log.warning("Graphviz dot warning(s):\n%s" % err) except OSError, e: - log.warning("Unable to render Graphviz dot graph:\n%s" % e) - #log.debug(self.to_dotfile()) + log.warning("Unable to render Graphviz dot graph (%s):\n%s" % + (self.title, e)) + import tempfile, epydoc + if epydoc.DEBUG: + filename = tempfile.mktemp('.dot') + out = open(filename, 'wb') + out.write(self.to_dotfile()) + out.close() + log.debug('Failed dot graph written to %s' % filename) return None return result @@ -277,7 +285,7 @@ # Default dot input encoding is UTF-8 return u'\n'.join(lines).encode('utf-8') -class DotGraphNode: +class DotGraphNode(object): _next_id = 0 def __init__(self, label=None, html_label=None, **attribs): if label is not None and html_label is not None: @@ -285,8 +293,8 @@ if label is not None: attribs['label'] = label self._html_label = html_label self._attribs = attribs - self.id = self.__class__._next_id - self.__class__._next_id += 1 + self.id = DotGraphNode._next_id + DotGraphNode._next_id += 1 self.port = None def __getitem__(self, attr): @@ -311,7 +319,7 @@ if attribs: attribs = ' [%s]' % (','.join(attribs)) return 'node%d%s' % (self.id, attribs) -class DotGraphEdge: +class DotGraphEdge(object): def __init__(self, start, end, label=None, **attribs): """ :type start: `DotGraphNode` @@ -386,7 +394,6 @@ - show/hide attribute types - use qualifiers """ - def __init__(self, class_doc, linker, context, collapsed=False, bgcolor=CLASS_BG, **options): """ @@ -420,13 +427,20 @@ - `max_attributes`: The maximum number of attributes that should be listed in the attribute box. If the class has more than this number of attributes, some will be - ellided. Ellipsis is marked with ``'...'``. + ellided. Ellipsis is marked with ``'...'``. (Default: 10) - `max_operations`: The maximum number of operations that - should be listed in the operation box. + should be listed in the operation box. (Default: 5) - `add_nodes_for_linked_attributes`: If true, then `link_attributes()` will create new a collapsed node for the types of a linked attributes if no node yet exists for that type. + - `show_signature_defaults`: If true, then show default + parameter values in method signatures; if false, then + hide them. (Default: *False*) + - `max_signature_width`: The maximum width (in chars) for + method signatures. If the signature is longer than this, + then it will be trunctated (with ``'...'``). (Default: + *60*) """ if not isinstance(class_doc, ClassDoc): raise TypeError('Expected a ClassDoc as 1st argument') @@ -463,6 +477,16 @@ These should not be added to the `DotGraph`; this node will generate their dotfile code directly.""" + self.same_rank = [] + """List of nodes that should have the same rank as this one. + (Used for nodes that are created by _link_attributes).""" + + # Keyword options: + self._show_signature_defaults = options.get( + 'show_signature_defaults', False) + self._max_signature_width = options.get( + 'max_signature_width', 60) + # Initialize operations & attributes lists. show_private = options.get('show_private_vars', False) show_magic = options.get('show_magic_vars', True) @@ -515,7 +539,7 @@ r'^(None or|optional) ([\w\.]+)$|^([\w\.]+) or None$') """A regular expression that matches descriptions of optional types.""" - def link_attributes(self, nodes): + def link_attributes(self, graph, nodes): """ Convert any attributes with type descriptions corresponding to documented classes to edges. The following type descriptions @@ -549,9 +573,9 @@ # that var from our attribute list; otherwise, leave that var # in our attribute list. self.attributes = [var for var in self.attributes - if not self._link_attribute(var, nodes)] + if not self._link_attribute(var, graph, nodes)] - def _link_attribute(self, var, nodes): + def _link_attribute(self, var, graph, nodes): """ Helper for `link_attributes()`: try to convert the attribute variable `var` into an edge, and add that edge to @@ -563,18 +587,19 @@ # Simple type. m = self.SIMPLE_TYPE_RE.match(type_descr) - if m and self._add_attribute_edge(var, nodes, m.group(1)): + if m and self._add_attribute_edge(var, graph, nodes, m.group(1)): return True # Collection type. m = self.COLLECTION_TYPE_RE.match(type_descr) - if m and self._add_attribute_edge(var, nodes, m.group(2), + if m and self._add_attribute_edge(var, graph, nodes, m.group(2), headlabel='*'): return True # Optional type. m = self.OPTIONAL_TYPE_RE.match(type_descr) - if m and self._add_attribute_edge(var, nodes, m.group(2) or m.group(3), + if m and self._add_attribute_edge(var, graph, nodes, + m.group(2) or m.group(3), headlabel='0..1'): return True @@ -582,7 +607,7 @@ m = self.MAPPING_TYPE_RE.match(type_descr) if m: port = 'qualifier_%s' % var.name - if self._add_attribute_edge(var, nodes, m.group(3), + if self._add_attribute_edge(var, graph, nodes, m.group(3), tailport='%s:e' % port): self.qualifiers.append( (m.group(2), port) ) return True @@ -591,7 +616,8 @@ m = self.MAPPING_TO_COLLECTION_TYPE_RE.match(type_descr) if m: port = 'qualifier_%s' % var.name - if self._add_attribute_edge(var, nodes, m.group(4), headlabel='*', + if self._add_attribute_edge(var, graph, nodes, m.group(4), + headlabel='*', tailport='%s:e' % port): self.qualifiers.append( (m.group(2), port) ) return True @@ -599,7 +625,7 @@ # We were unable to link this attribute. return False - def _add_attribute_edge(self, var, nodes, type_str, **attribs): + def _add_attribute_edge(self, var, graph, nodes, type_str, **attribs): """ Helper for `link_attributes()`: try to add an edge for the given attribute variable `var`. Return ``True`` if @@ -619,7 +645,9 @@ if self.options.get('add_nodes_for_linked_attributes', True): type_node = DotGraphUmlClassNode(type_doc, self.linker, self.context, collapsed=True) + self.same_rank.append(type_node) nodes[type_doc] = type_node + graph.nodes.append(type_node) else: return False @@ -629,7 +657,7 @@ attribs.setdefault('tailport', 'body') url = self.linker.url_for(var) or NOOP_URL self.edges.append(DotGraphEdge(self, type_node, label=var.name, - arrowhead='open', href=url, + arrowtail='odiamond', arrowhead='none', href=url, tooltip=var.canonical_name, labeldistance=1.5, **attribs)) return True @@ -690,6 +718,8 @@ if func_doc.vararg: args.append('*'+func_doc.vararg) if func_doc.kwarg: args.append('**'+func_doc.kwarg) label = '%s(%s)' % (var_doc.name, ', '.join(args)) + if len(label) > self._max_signature_width: + label = label[:self._max_signature_width-4]+'...)' # Get the URL url = self.linker.url_for(var_doc) or NOOP_URL # Construct & return the pseudo-html code @@ -700,7 +730,7 @@ :todo: Handle tuple args better :todo: Optionally add type info? """ - if default is None: + if default is None or not self._show_signature_defaults: return '%s' % name else: pyval_repr = default.summary_pyval_repr().to_plaintext(None) @@ -732,12 +762,12 @@ _LABEL = ''' <TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" CELLPADDING="0"> <TR><TD ROWSPAN="%s"> - <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" + <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" WIDTH="100" CELLPADDING="0" PORT="body" BGCOLOR="%s"> - <TR><TD>%s</TD></TR> - <TR><TD><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0"> + <TR><TD WIDTH="100">%s</TD></TR> + <TR><TD WIDTH="100"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0"> %s</TABLE></TD></TR> - <TR><TD><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0"> + <TR><TD WIDTH="100"><TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0"> %s</TABLE></TD></TR> </TABLE> </TD></TR> @@ -746,7 +776,7 @@ _COLLAPSED_LABEL = ''' <TABLE CELLBORDER="0" BGCOLOR="%s" PORT="body"> - <TR><TD>%s</TD></TR> + <TR><TD WIDTH="50">%s</TD></TR> </TABLE>''' def _get_html_label(self): @@ -760,7 +790,7 @@ # Construct the attribute list. (If it's too long, truncate) attrib_cells = [self._attribute_cell(a) for a in self.attributes] - max_attributes = self.options.get('max_attributes', 15) + max_attributes = self.options.get('max_attributes', 10) if len(attrib_cells) == 0: attrib_cells = ['<TR><TD></TD></TR>'] elif len(attrib_cells) > max_attributes: @@ -769,7 +799,7 @@ # Construct the operation list. (If it's too long, truncate) oper_cells = [self._operation_cell(a) for a in self.operations] - max_operations = self.options.get('max_operations', 15) + max_operations = self.options.get('max_operations', 5) if len(oper_cells) == 0: oper_cells = ['<TR><TD></TD></TR>'] elif len(oper_cells) > max_operations: @@ -797,6 +827,11 @@ if not self.collapsed: for edge in self.edges: s += '\n' + edge.to_dotfile() + if self.same_rank: + sr_nodes = ''.join(['node%s; ' % node.id + for node in self.same_rank]) + # [xx] This can cause dot to crash! not sure why! + #s += '{rank=same; node%s; %s}' % (self.id, sr_nodes) return s class DotGraphUmlModuleNode(DotGraphNode): @@ -953,7 +988,7 @@ log.warning('UML style package trees require dot version 2.0+') graph = DotGraph('Package Tree for %s' % name_list(packages, context), - body='ranksep=.3\n;nodesep=.1\n', + body='ranksep=.3\nnodesep=.1\n', edge_defaults={'dir':'none'}) # Options @@ -1003,149 +1038,226 @@ return graph ###################################################################### -def class_tree_graph(bases, linker, context=None, **options): +def class_tree_graph(classes, linker, context=None, **options): """ Return a `DotGraph` that graphically displays the class hierarchy for the given classes. Options: - - exclude + - exclude: A list of classes that should be excluded - dir: LR|RL|BT requests a left-to-right, right-to-left, or bottom-to- top, drawing. (corresponds to the dot option 'rankdir' + - max_subclass_depth: The maximum depth to which subclasses + will be drawn. + - max_subclasses: A list of ints, specifying how many + subclasses should be drawn per class at each level of the + graph. E.g., [5,3,1] means draw up to 5 subclasses for the + specified classes; up to 3 subsubclasses for each of those (up + to) 5 subclasses; and up to 1 subclass for each of those. """ - if isinstance(bases, ClassDoc): bases = [bases] - graph = DotGraph('Class Hierarchy for %s' % name_list(bases, context), + # Callbacks: + def mknode(cls, nodetype, linker, context, options): + return mk_valdoc_node(cls, linker, context) + def mkedge(start, end, edgetype, options): + return DotGraphEdge(start, end) + + if isinstance(classes, ClassDoc): classes = [classes] + graph = DotGraph('Class Hierarchy for %s' % name_list(classes, context), body='ranksep=0.3\n', edge_defaults={'sametail':True, 'dir':'none'}) + _class_tree_graph(graph, classes, mknode, mkedge, linker, + context, options, cls2node={}) + return graph - # Options - if options.get('dir', 'TB') != 'TB': # default: top-down - graph.body += 'rankdir=%s\n' % options.get('dir', 'TB') +def _class_tree_graph(graph, classes, mknode, mkedge, linker, + context, options, cls2node): + """ + A helper function that is used by both `class_tree_graph()` and + `uml_class_tree_graph()` to draw class trees. To abstract over + the differences between the two, this function takes two callback + functions that create graph nodes and edges: + + - ``mknode(base, nodetype, linker, context, options)``: Returns + a `DotGraphNode`. ``nodetype`` is one of: subclass, superclass, + selected, undocumented. + - ``mkedge(begin, end, edgetype, options)``: Returns a + `DotGraphEdge`. ``edgetype`` is one of: subclass, + truncate-subclass. + """ + rankdir = options.get('dir', 'TB') + graph.body += 'rankdir=%s\n' % rankdir + truncated = set() # Classes whose subclasses were truncated + _add_class_tree_superclasses(graph, classes, mknode, mkedge, linker, + context, options, cls2node) + _add_class_tree_subclasses(graph, classes, mknode, mkedge, linker, + context, options, cls2node, truncated) + _add_class_tree_inheritance(graph, classes, mknode, mkedge, linker, + context, options, cls2node, truncated) + +def _add_class_tree_superclasses(graph, classes, mknode, mkedge, linker, + context, options, cls2node): exclude = options.get('exclude', ()) + + # Create nodes for all bases. + for cls in classes: + for base in cls.mro(): + # Don't include 'object' + if base.canonical_name == DottedName('object'): continue + # Stop if we reach an excluded class. + if base in exclude: break + # Don't do the same class twice. + if base in cls2node: continue + # Make the node. + if linker.url_for(base) is None: typ = 'undocumented' + elif base in classes: typ = 'selected' + else: typ = 'superclass' + cls2node[base] = mknode(base, typ, linker, context, options) + graph.nodes.append(cls2node[base]) - # Find all superclasses & subclasses of the given classes. - classes = set(bases) - queue = list(bases) +def _add_class_tree_subclasses(graph, classes, mknode, mkedge, linker, + context, options, cls2node, truncated): + exclude = options.get('exclude', ()) + max_subclass_depth = options.get('max_subclass_depth', 3) + max_subclasses = list(options.get('max_subclasses', (5,3,2,1))) + max_subclasses += len(classes)*max_subclasses[-1:] # repeat last num + + # Find the depth of each subclass (for truncation) + subclass_depth = _get_subclass_depth_map(classes) + + queue = list(classes) for cls in queue: - if isinstance(cls, ClassDoc): - if cls.subclasses not in (None, UNKNOWN): - subclasses = cls.subclasses - if exclude: - subclasses = [d for d in subclasses if d not in exclude] - queue.extend(subclasses) - classes.update(subclasses) - queue = list(bases) - for cls in queue: - if isinstance(cls, ClassDoc): - if cls.bases not in (None, UNKNOWN): - bases = cls.bases - if exclude: - bases = [d for d in bases if d not in exclude] - queue.extend(bases) - classes.update(bases) + # If there are no subclasses, then we're done. + if not isinstance(cls, ClassDoc): continue + if cls.subclasses in (None, UNKNOWN, (), []): continue + # Get the list of subclasses. + subclasses = [subcls for subcls in cls.subclasses + if subcls not in cls2node and subcls not in exclude] + # If the subclass list is too long, then truncate it. + if len(subclasses) > max_subclasses[subclass_depth[cls]]: + subclasses = subclasses[:max_subclasses[subclass_depth[cls]]] + truncated.add(cls) + # Truncate any classes that are too deep. + num_subclasses = len(subclasses) + subclasses = [subcls for subcls in subclasses + if subclass_depth[subcls] <= max_subclass_depth] + if len(subclasses) < num_subclasses: truncated.add(cls) + # Add a node for each subclass. + for subcls in subclasses: + cls2node[subcls] = mknode(subcls, 'subclass', linker, + context, options) + graph.nodes.append(cls2node[subcls]) + # Add the subclasses to our queue. + queue.extend(subclasses) - # Add a node for each cls. - classes = [d for d in classes if isinstance(d, ClassDoc) - if d.pyval is not object] - nodes = add_valdoc_nodes(graph, classes, linker, context) +def _add_class_tree_inheritance(graph, classes, mknode, mkedge, linker, + context, options, cls2node, truncated): + # Add inheritance edges. + for (cls, node) in cls2node.items(): + for base in cls.bases: + if base in cls2node: + graph.edges.append(mkedge(cls2node[base], node, + 'subclass', options)) + # Mark truncated classes + for cls in truncated: + ellipsis = DotGraphNode('...', shape='plaintext', + width='0', height='0') + graph.nodes.append(ellipsis) + graph.edges.append(mkedge(cls2node[cls], ellipsis, + 'truncate-subclass', options)) - # Add an edge for each package/subclass relationship. - edges = set() - for cls in classes: - for subcls in cls.subclasses: - if cls in nodes and subcls in nodes: - edges.add((nodes[cls], nodes[subcls])) - graph.edges = [DotGraphEdge(src,dst) for (src,dst) in edges] +def _get_subclass_depth_map(classes): + subclass_depth = dict([(cls,0) for cls in classes]) + queue = list(classes) + for cls in queue: + if (isinstance(cls, ClassDoc) and + cls.subclasses not in (None, UNKNOWN)): + for subcls in cls.subclasses: + subclass_depth[subcls] = max(subclass_depth.get(subcls,0), + subclass_depth[cls]+1) + queue.append(subcls) + return subclass_depth - return graph + ###################################################################### -def uml_class_tree_graph(class_doc, linker, context=None, **options): +def uml_class_tree_graph(classes, linker, context=None, **options): """ Return a `DotGraph` that graphically displays the class hierarchy for the given class, using UML notation. Options: + - exclude: A list of classes that should be excluded + - dir: LR|RL|BT requests a left-to-right, right-to-left, or + bottom-to- top, drawing. (corresponds to the dot option + 'rankdir' + - max_subclass_depth: The maximum depth to which subclasses + will be drawn. + - max_subclasses: A list of ints, specifying how many + subclasses should be drawn per class at each level of the + graph. E.g., [5,3,1] means draw up to 5 subclasses for the + specified classes; up to 3 subsubclasses for each of those (up + to) 5 subclasses; and up to 1 subclass for each of those. - max_attributes - max_operations - show_private_vars - show_magic_vars - link_attributes + - show_signature_defaults + - max_signature_width """ - nodes = {} # ClassDoc -> DotGraphUmlClassNode - exclude = options.get('exclude', ()) - - # Create nodes for class_doc and all its bases. - for cls in class_doc.mro(): - if cls.pyval is object: continue # don't include `object`. - if cls in exclude: break # stop if we get to an excluded class. - if cls == class_doc: color = SELECTED_BG - else: color = BASECLASS_BG - nodes[cls] = DotGraphUmlClassNode(cls, linker, context, - show_inherited_vars=False, - collapsed=False, bgcolor=color) + cls2node = {} - # Create nodes for all class_doc's subclasses. - queue = [class_doc] - for cls in queue: - if (isinstance(cls, ClassDoc) and - cls.subclasses not in (None, UNKNOWN)): - for subcls in cls.subclasses: - subcls_name = subcls.canonical_name[-1] - if subcls not in nodes and subcls not in exclude: - queue.append(subcls) - nodes[subcls] = DotGraphUmlClassNode( - subcls, linker, context, collapsed=True, - bgcolor=SUBCLASS_BG) - - # Only show variables in the class where they're defined for - # *class_doc*. - mro = class_doc.mro() - for name, var in class_doc.variables.items(): - i = mro.index(var.container) - for base in mro[i+1:]: - if base.pyval is object: continue # don't include `object`. - overridden_var = base.variables.get(name) - if overridden_var and overridden_var.container == base: - try: - if isinstance(overridden_var.value, RoutineDoc): - nodes[base].operations.remove(overridden_var) - else: - nodes[base].attributes.remove(overridden_var) - except ValueError: - pass # var is filtered (eg private or magic) + # Draw the basic graph: + if isinstance(classes, ClassDoc): classes = [classes] + graph = DotGraph('UML class diagram for %s' % name_list(classes, context), + body='ranksep=.2\n;nodesep=.3\n') + _class_tree_graph(graph, classes, _uml_mknode, _uml_mkedge, + linker, context, options, cls2node) - # Keep track of which nodes are part of the inheritance graph - # (since link_attributes might add new nodes) - inheritance_nodes = set(nodes.values()) - - # Turn attributes into links. + # Turn attributes into links (optional): + inheritance_nodes = set(graph.nodes) if options.get('link_attributes', True): - for node in nodes.values(): - node.link_attributes(nodes) - # Make sure that none of the new attribute edges break the - # rank ordering assigned by inheritance. - for edge in node.edges: - if edge.end in inheritance_nodes: - edge['constraint'] = 'False' - - # Construct the graph. - graph = DotGraph('UML class diagram for %s' % class_doc.canonical_name, - body='ranksep=.2\n;nodesep=.3\n') - graph.nodes = nodes.values() - - # Add inheritance edges. - for node in inheritance_nodes: - for base in node.class_doc.bases: - if base in nodes: - graph.edges.append(DotGraphEdge(nodes[base], node, - dir='back', arrowtail='empty', - headport='body', tailport='body', - color=INH_LINK_COLOR, weight=100, - style='bold')) + for cls in classes: + for base in cls.mro(): + node = cls2node.get(base) + if node is None: continue + node.link_attributes(graph, cls2node) + # Make sure that none of the new attribute edges break + # the rank ordering assigned by inheritance. + for edge in node.edges: + if edge.end in inheritance_nodes: + edge['constraint'] = 'False' - # And we're done! return graph +# A callback to make graph nodes: +def _uml_mknode(cls, nodetype, linker, context, options): + if nodetype == 'subclass': + return DotGraphUmlClassNode( + cls, linker, context, collapsed=True, + bgcolor=SUBCLASS_BG, **options) + elif nodetype in ('selected', 'superclass', 'undocumented'): + if nodetype == 'selected': bgcolor = SELECTED_BG + if nodetype == 'superclass': bgcolor = BASECLASS_BG + if nodetype == 'undocumented': bgcolor = UNDOCUMENTED_BG + return DotGraphUmlClassNode( + cls, linker, context, show_inherited_vars=False, + collapsed=False, bgcolor=bgcolor, **options) + assert 0, 'bad nodetype' + +# A callback to make graph edges: +def _uml_mkedge(start, end, edgetype, options): + if edgetype == 'subclass': + return DotGraphEdge( + start, end, dir='back', arrowtail='empty', + headport='body', tailport='body', color=INH_LINK_COLOR, + weight=100, style='bold') + if edgetype == 'truncate-subclass': + return DotGraphEdge( + start, end, dir='back', arrowtail='empty', + tailport='body', color=INH_LINK_COLOR, + weight=100, style='bold') + assert 0, 'bad edgetype' + ###################################################################### def import_graph(modules, docindex, linker, context=None, **options): graph = DotGraph('Import Graph', body='ranksep=.3\n;nodesep=.3\n') @@ -1272,14 +1384,17 @@ :todo: Use different node styles for different subclasses of APIDoc """ nodes = {} - val_docs = sorted(val_docs, key=lambda d:d.canonical_name) - for i, val_doc in enumerate(val_docs): - label = val_doc.canonical_name.contextualize(context.canonical_name) - node = nodes[val_doc] = DotGraphNode(label) - graph.nodes.append(node) - specialize_valdoc_node(node, val_doc, context, linker.url_for(val_doc)) + for val_doc in sorted(val_docs, key=lambda d:d.canonical_name): + nodes[val_doc] = mk_valdoc_node(graph, val_doc, linker, context) + graph.nodes.append(nodes[val_doc]) return nodes +def mk_valdoc_node(val_doc, linker, context): + label = val_doc.canonical_name.contextualize(context.canonical_name) + node = DotGraphNode(label) + specialize_valdoc_node(node, val_doc, context, linker.url_for(val_doc)) + return node + NOOP_URL = 'javascript:void(0);' MODULE_NODE_HTML = ''' <TABLE BORDER="0" CELLBORDER="0" CELLSPACING="0" @@ -1307,6 +1422,10 @@ # Set the URL. (Do this even if it points to the page we're # currently on; otherwise, the tooltip is ignored.) node['href'] = url or NOOP_URL + + if url is None: + node['fillcolor'] = UNDOCUMENTED_BG + node['style'] = 'filled' if isinstance(val_doc, ModuleDoc) and dot_version >= [2]: node['shape'] = 'plaintext' This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2008-02-16 02:03:15
|
Revision: 1724 http://epydoc.svn.sourceforge.net/epydoc/?rev=1724&view=rev Author: edloper Date: 2008-02-15 18:02:58 -0800 (Fri, 15 Feb 2008) Log Message: ----------- - Always write the subclass list, even if the class tree is drawn. I plan to truncate large class diagrams, so the list will be useful in those cases. Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/html.py Modified: trunk/epydoc/src/epydoc/docwriter/html.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/html.py 2008-02-15 01:16:54 UTC (rev 1723) +++ trunk/epydoc/src/epydoc/docwriter/html.py 2008-02-16 02:02:58 UTC (rev 1724) @@ -869,19 +869,19 @@ out('<pre class="base-tree">\n%s</pre>\n\n' % self.base_tree(doc)) - # Write the known subclasses - if (doc.subclasses not in (UNKNOWN, None) and - len(doc.subclasses) > 0): - out('<dl><dt>Known Subclasses:</dt>\n<dd>\n ') - out(' <ul class="subclass-list">\n') - for i, subclass in enumerate(doc.subclasses): - href = self.href(subclass, context=doc) - if self._val_is_public(subclass): css = '' - else: css = ' class="private"' - if i > 0: href = ', '+href - out('<li%s>%s</li>' % (css, href)) - out(' </ul>\n') - out('</dd></dl>\n\n') + # Write the known subclasses + if (doc.subclasses not in (UNKNOWN, None) and + len(doc.subclasses) > 0): + out('<dl><dt>Known Subclasses:</dt>\n<dd>\n ') + out(' <ul class="subclass-list">\n') + for i, subclass in enumerate(doc.subclasses): + href = self.href(subclass, context=doc) + if self._val_is_public(subclass): css = '' + else: css = ' class="private"' + if i > 0: href = ', '+href + out('<li%s>%s</li>' % (css, href)) + out(' </ul>\n') + out('</dd></dl>\n\n') out('<hr />\n') This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2008-02-15 01:16:56
|
Revision: 1723 http://epydoc.svn.sourceforge.net/epydoc/?rev=1723&view=rev Author: edloper Date: 2008-02-14 17:16:54 -0800 (Thu, 14 Feb 2008) Log Message: ----------- - If a class's metaclass is known, then show that class instead of 'Class' on its class html page - When displaying class description tables, don't bother with the 'type' column -- it's always empty anyway. - If a property's fget/fset/fdel function has the name '??', then don't bother to list it. Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/html.py Modified: trunk/epydoc/src/epydoc/docwriter/html.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/html.py 2008-02-15 01:11:18 UTC (rev 1722) +++ trunk/epydoc/src/epydoc/docwriter/html.py 2008-02-15 01:16:54 UTC (rev 1723) @@ -842,7 +842,11 @@ self.write_breadcrumbs(out, doc, self.url(doc)) # Write the name of the class we're describing. - if doc.is_type(): typ = 'Type' + if (doc.metaclass is not UNKNOWN and + doc.metaclass.canonical_name is not UNKNOWN and + doc.metaclass.canonical_name != 'type'): + typ = self.href(doc.metaclass, doc.metaclass.canonical_name[-1]) + elif doc.is_type(): typ = 'Type' elif doc.is_exception(): typ = 'Exception' else: typ = 'Class' out('<!-- ==================== %s ' % typ.upper() + @@ -2139,6 +2143,10 @@ var_doc.value.callgraph_uid = callgraph.uid else: callgraph = None + elif isinstance(var_doc.value, ClassDoc): + typ = -1 # use the whole row for description. + description = self.summary_name(var_doc, + link_name=link_name, anchor=anchor) else: typ = self.type_descr(var_doc, indent=6) description = self.summary_name(var_doc, @@ -2171,6 +2179,9 @@ # /------------------------- Template -------------------------\ ''' <tr$tr_class$> + >>> if typ == -1: + <td class="summary" colspan="2"> + >>> else: <td width="15%" align="right" valign="top" class="summary"> <span class="summary-type">$typ or " "$</span> </td><td class="summary"> @@ -2266,7 +2277,8 @@ [('Get', prop_doc.fget), ('Set', prop_doc.fset), ('Delete', prop_doc.fdel)] if val_doc not in (None, UNKNOWN) - and val_doc.pyval is not None ] + and val_doc.pyval is not None + and not val_doc.canonical_name[0].startswith('??')] self.write_property_details_entry(out, var_doc, descr, accessors, div_class) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2008-02-15 01:11:20
|
Revision: 1722 http://epydoc.svn.sourceforge.net/epydoc/?rev=1722&view=rev Author: edloper Date: 2008-02-14 17:11:18 -0800 (Thu, 14 Feb 2008) Log Message: ----------- - Added 'metaclass' var to ClassDoc, to record the metaclass (when defined) Modified Paths: -------------- trunk/epydoc/src/epydoc/apidoc.py trunk/epydoc/src/epydoc/docintrospecter.py trunk/epydoc/src/epydoc/docparser.py Modified: trunk/epydoc/src/epydoc/apidoc.py =================================================================== --- trunk/epydoc/src/epydoc/apidoc.py 2008-02-15 01:02:30 UTC (rev 1721) +++ trunk/epydoc/src/epydoc/apidoc.py 2008-02-15 01:11:18 UTC (rev 1722) @@ -1257,6 +1257,9 @@ """@ivar: API documentation for the class's known subclasses. @type: C{list} of L{ClassDoc}""" #} + #{ Information about Metaclasses + metaclass = UNKNOWN + #} def apidoc_links(self, **filters): val_docs = NamespaceDoc.apidoc_links(self, **filters) Modified: trunk/epydoc/src/epydoc/docintrospecter.py =================================================================== --- trunk/epydoc/src/epydoc/docintrospecter.py 2008-02-15 01:02:30 UTC (rev 1721) +++ trunk/epydoc/src/epydoc/docintrospecter.py 2008-02-15 01:11:18 UTC (rev 1722) @@ -321,7 +321,7 @@ #: class's API documentation. UNDOCUMENTED_CLASS_VARS = ( '__doc__', '__module__', '__dict__', '__weakref__', '__slots__', - '__pyx_vtable__') + '__pyx_vtable__', '__metaclass__') def introspect_class(cls, class_doc, module_name=None): """ @@ -341,6 +341,9 @@ except KeyboardInterrupt: raise except: pass + # Record the class's metaclass + class_doc.metaclass = introspect_docs(type(cls)) + # Start a list of subclasses. class_doc.subclasses = [] Modified: trunk/epydoc/src/epydoc/docparser.py =================================================================== --- trunk/epydoc/src/epydoc/docparser.py 2008-02-15 01:02:30 UTC (rev 1721) +++ trunk/epydoc/src/epydoc/docparser.py 2008-02-15 01:11:18 UTC (rev 1722) @@ -1128,6 +1128,12 @@ if lhs_name[-1] == '__slots__': continue + # Handle metaclass assignment + if (lhs_name[-1] == '__metaclass__' and + isinstance(parent_docs[-1], ClassDoc)): + parent_docs[-1].metaclass = rhs_val + continue + # Create the VariableDoc. var_doc = VariableDoc(name=lhs_name[-1], value=rhs_val, is_imported=False, is_alias=is_alias, This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2008-02-15 01:02:31
|
Revision: 1721 http://epydoc.svn.sourceforge.net/epydoc/?rev=1721&view=rev Author: edloper Date: 2008-02-14 17:02:30 -0800 (Thu, 14 Feb 2008) Log Message: ----------- - Minor changes to TerminalController class Modified Paths: -------------- trunk/epydoc/src/epydoc/util.py Modified: trunk/epydoc/src/epydoc/util.py =================================================================== --- trunk/epydoc/src/epydoc/util.py 2008-02-15 01:01:08 UTC (rev 1720) +++ trunk/epydoc/src/epydoc/util.py 2008-02-15 01:02:30 UTC (rev 1721) @@ -309,10 +309,12 @@ BOLD = '' #: Turn on bold mode NORMAL = '' #: Turn off all modes COLS = 75 #: Width of the terminal (default to 75) + UNDERLINE = '' #: Underline the text + REVERSE = '' #: Reverse the foreground & background BLACK = BLUE = GREEN = CYAN = RED = MAGENTA = YELLOW = WHITE = '' _STRING_CAPABILITIES = """ - BOL=cr UP=cuu1 DOWN=cud1 LEFT=cub1 RIGHT=cuf1 + BOL=cr UP=cuu1 DOWN=cud1 LEFT=cub1 RIGHT=cuf1 REVERSE=rev CLEAR_EOL=el BOLD=bold UNDERLINE=smul NORMAL=sgr0""".split() _COLORS = """BLACK BLUE GREEN CYAN RED MAGENTA YELLOW WHITE""".split() _ANSICOLORS = "BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE".split() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2008-02-15 01:01:11
|
Revision: 1720 http://epydoc.svn.sourceforge.net/epydoc/?rev=1720&view=rev Author: edloper Date: 2008-02-14 17:01:08 -0800 (Thu, 14 Feb 2008) Log Message: ----------- - Added support for the syntax `name <target>` in rst interpreted text roles. Modified Paths: -------------- trunk/epydoc/src/epydoc/markup/restructuredtext.py Modified: trunk/epydoc/src/epydoc/markup/restructuredtext.py =================================================================== --- trunk/epydoc/src/epydoc/markup/restructuredtext.py 2008-02-15 01:00:39 UTC (rev 1719) +++ trunk/epydoc/src/epydoc/markup/restructuredtext.py 2008-02-15 01:01:08 UTC (rev 1720) @@ -539,6 +539,8 @@ translator = _EpydocLaTeXTranslator(document, None) return translator.head_prefix +_TARGET_RE = re.compile(r'^(.*?)\s*<(?:URI:|URL:)?([^<>]+)>$') + class _EpydocLaTeXTranslator(LaTeXTranslator): settings = None def __init__(self, document, docstring_linker): @@ -560,8 +562,10 @@ # Handle interpreted text (crossreferences) def visit_title_reference(self, node): - target = self.encode(node.astext()) - xref = self._linker.translate_identifier_xref(target, target) + m = _TARGET_RE.match(node.astext()) + if m: text, target = m.groups() + else: target = text = node.astext() + xref = self._linker.translate_identifier_xref(target, text) self.body.append(xref) raise SkipNode() @@ -598,8 +602,10 @@ # Handle interpreted text (crossreferences) def visit_title_reference(self, node): - target = self.encode(node.astext()) - xref = self._linker.translate_identifier_xref(target, target) + m = _TARGET_RE.match(node.astext()) + if m: text, target = m.groups() + else: target = text = node.astext() + xref = self._linker.translate_identifier_xref(target, text) self.body.append(xref) raise SkipNode() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2008-02-15 01:00:44
|
Revision: 1719 http://epydoc.svn.sourceforge.net/epydoc/?rev=1719&view=rev Author: edloper Date: 2008-02-14 17:00:39 -0800 (Thu, 14 Feb 2008) Log Message: ----------- - Added support for the syntax `name <target>` in xlink. Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/xlink.py Modified: trunk/epydoc/src/epydoc/docwriter/xlink.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/xlink.py 2008-02-14 22:25:14 UTC (rev 1718) +++ trunk/epydoc/src/epydoc/docwriter/xlink.py 2008-02-15 01:00:39 UTC (rev 1719) @@ -364,6 +364,8 @@ docutils = roles = nodes = utils = None class Reader: settings_spec = () +_TARGET_RE = re.compile(r'^(.*?)\s*<(?:URI:|URL:)?([^<>]+)>$') + def create_api_role(name, problematic): """ Create and register a new role to create links for an API documentation. @@ -384,13 +386,18 @@ if docutils is None: raise AssertionError('requires docutils') + # Check if there's separate text & targets + m = _TARGET_RE.match(text) + if m: text, target = m.groups() + else: target = text + # node in monotype font text = utils.unescape(text) node = nodes.literal(rawtext, text, **options) # Get the resolver from the register and create an url from it. try: - url = api_register[name].get_url(text) + url = api_register[name].get_url(target) except IndexError, exc: msg = inliner.reporter.warning(str(exc), line=lineno) if problematic: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2008-02-14 22:25:16
|
Revision: 1718 http://epydoc.svn.sourceforge.net/epydoc/?rev=1718&view=rev Author: edloper Date: 2008-02-14 14:25:14 -0800 (Thu, 14 Feb 2008) Log Message: ----------- - If the source file is not encoded correctly, then issue an error (rather than crashing) and give up on parsing. Modified Paths: -------------- trunk/epydoc/src/epydoc/docparser.py Modified: trunk/epydoc/src/epydoc/docparser.py =================================================================== --- trunk/epydoc/src/epydoc/docparser.py 2008-02-14 22:21:41 UTC (rev 1717) +++ trunk/epydoc/src/epydoc/docparser.py 2008-02-14 22:25:14 UTC (rev 1718) @@ -285,7 +285,7 @@ raise ParseError('Error during parsing: %s ' '(%s, line %d, char %d)' % (msg, module_doc.filename, srow, scol)) - except IndentationError, e: + except (IndentationError, UnicodeDecodeError), e: raise ParseError('Error during parsing: %s (%s)' % (e, module_doc.filename)) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2008-02-14 22:21:44
|
Revision: 1717 http://epydoc.svn.sourceforge.net/epydoc/?rev=1717&view=rev Author: edloper Date: 2008-02-14 14:21:41 -0800 (Thu, 14 Feb 2008) Log Message: ----------- - Minor css fix for subclass list Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/html_css.py Modified: trunk/epydoc/src/epydoc/docwriter/html_css.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/html_css.py 2008-02-14 22:21:23 UTC (rev 1716) +++ trunk/epydoc/src/epydoc/docwriter/html_css.py 2008-02-14 22:21:41 UTC (rev 1717) @@ -205,8 +205,8 @@ /* Subclass list */ -ul.subclass-list { display: inline; } -ul.subclass-list li { display: inline; } +ul.subclass-list { display: inline; margin: 0; padding: 0; } +ul.subclass-list li { display: inline; margin: 0; padding: 0; } /* To render variables, classes etc. like functions */ table.summary .summary-name { color: $summary_sig_name; font-weight: bold; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2008-02-14 22:21:25
|
Revision: 1716 http://epydoc.svn.sourceforge.net/epydoc/?rev=1716&view=rev Author: edloper Date: 2008-02-14 14:21:23 -0800 (Thu, 14 Feb 2008) Log Message: ----------- - If the source file is not encoded correctly, then recover as well as we can, and issue a warning. Modified Paths: -------------- trunk/epydoc/src/epydoc/docwriter/html_colorize.py Modified: trunk/epydoc/src/epydoc/docwriter/html_colorize.py =================================================================== --- trunk/epydoc/src/epydoc/docwriter/html_colorize.py 2008-02-13 19:19:47 UTC (rev 1715) +++ trunk/epydoc/src/epydoc/docwriter/html_colorize.py 2008-02-14 22:21:23 UTC (rev 1716) @@ -488,6 +488,12 @@ except LookupError: coding = 'iso-8859-1' html = html.decode(coding).encode('ascii', 'xmlcharrefreplace') + except UnicodeDecodeError, e: + log.warning("Unicode error while generating syntax-highlighted " + "source code: %s (%s)" % (e, self.module_filename)) + html = html.decode(coding, 'ignore').encode( + 'ascii', 'xmlcharrefreplace') + # Call expandto. html += PYSRC_EXPANDTO_JAVASCRIPT This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2008-02-13 19:19:49
|
Revision: 1715 http://epydoc.svn.sourceforge.net/epydoc/?rev=1715&view=rev Author: edloper Date: 2008-02-13 11:19:47 -0800 (Wed, 13 Feb 2008) Log Message: ----------- Fixed a bug that caused exceptions to propagate when reading a metadata variable failed. Modified Paths: -------------- trunk/epydoc/src/epydoc/docstringparser.py Modified: trunk/epydoc/src/epydoc/docstringparser.py =================================================================== --- trunk/epydoc/src/epydoc/docstringparser.py 2008-02-02 06:31:52 UTC (rev 1714) +++ trunk/epydoc/src/epydoc/docstringparser.py 2008-02-13 19:19:47 UTC (rev 1715) @@ -322,7 +322,7 @@ if field.multivalue and not value: try: value = epydoc.docparser.parse_string_list(val_doc.toktree) except KeyboardInterrupt: raise - except: raise + except: pass # Add any values that we found. for elt in value: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2008-02-02 06:32:01
|
Revision: 1714 http://epydoc.svn.sourceforge.net/epydoc/?rev=1714&view=rev Author: edloper Date: 2008-02-01 22:31:52 -0800 (Fri, 01 Feb 2008) Log Message: ----------- - Moved TerminalController to epydoc.util - Added TerminalController.render Modified Paths: -------------- trunk/epydoc/src/epydoc/cli.py trunk/epydoc/src/epydoc/util.py Modified: trunk/epydoc/src/epydoc/cli.py =================================================================== --- trunk/epydoc/src/epydoc/cli.py 2008-02-02 06:28:09 UTC (rev 1713) +++ trunk/epydoc/src/epydoc/cli.py 2008-02-02 06:31:52 UTC (rev 1714) @@ -70,7 +70,7 @@ import epydoc from epydoc import log from epydoc.util import wordwrap, run_subprocess, RunSubprocessError -from epydoc.util import plaintext_to_html +from epydoc.util import plaintext_to_html, TerminalController from epydoc.apidoc import UNKNOWN from epydoc.compat import * import ConfigParser @@ -1118,82 +1118,6 @@ ###################################################################### # [xx] this should maybe move to util.py or log.py -class TerminalController: - """ - A class that can be used to portably generate formatted output to - a terminal. See - U{http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/475116} - for documentation. (This is a somewhat stripped-down version.) - """ - BOL = '' #: Move the cursor to the beginning of the line - UP = '' #: Move the cursor up one line - DOWN = '' #: Move the cursor down one line - LEFT = '' #: Move the cursor left one char - RIGHT = '' #: Move the cursor right one char - CLEAR_EOL = '' #: Clear to the end of the line. - CLEAR_LINE = '' #: Clear the current line; cursor to BOL. - BOLD = '' #: Turn on bold mode - NORMAL = '' #: Turn off all modes - COLS = 75 #: Width of the terminal (default to 75) - BLACK = BLUE = GREEN = CYAN = RED = MAGENTA = YELLOW = WHITE = '' - - _STRING_CAPABILITIES = """ - BOL=cr UP=cuu1 DOWN=cud1 LEFT=cub1 RIGHT=cuf1 - CLEAR_EOL=el BOLD=bold UNDERLINE=smul NORMAL=sgr0""".split() - _COLORS = """BLACK BLUE GREEN CYAN RED MAGENTA YELLOW WHITE""".split() - _ANSICOLORS = "BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE".split() - - #: If this is set to true, then new TerminalControllers will - #: assume that the terminal is not capable of doing manipulation - #: of any kind. - FORCE_SIMPLE_TERM = False - - def __init__(self, term_stream=sys.stdout): - # If the stream isn't a tty, then assume it has no capabilities. - if not term_stream.isatty(): return - if self.FORCE_SIMPLE_TERM: return - - # Curses isn't available on all platforms - try: import curses - except: - # If it's not available, then try faking enough to get a - # simple progress bar. - self.BOL = '\r' - self.CLEAR_LINE = '\r' + ' '*self.COLS + '\r' - - # Check the terminal type. If we fail, then assume that the - # terminal has no capabilities. - try: curses.setupterm() - except: return - - # Look up numeric capabilities. - self.COLS = curses.tigetnum('cols') - - # Look up string capabilities. - for capability in self._STRING_CAPABILITIES: - (attrib, cap_name) = capability.split('=') - setattr(self, attrib, self._tigetstr(cap_name) or '') - if self.BOL and self.CLEAR_EOL: - self.CLEAR_LINE = self.BOL+self.CLEAR_EOL - - # Colors - set_fg = self._tigetstr('setf') - if set_fg: - for i,color in zip(range(len(self._COLORS)), self._COLORS): - setattr(self, color, curses.tparm(set_fg, i) or '') - set_fg_ansi = self._tigetstr('setaf') - if set_fg_ansi: - for i,color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS): - setattr(self, color, curses.tparm(set_fg_ansi, i) or '') - - def _tigetstr(self, cap_name): - # String capabilities can include "delays" of the form "$<2>". - # For any modern terminal, we should be able to just ignore - # these, so strip them out. - import curses - cap = curses.tigetstr(cap_name) or '' - return re.sub(r'\$<\d+>[/*]?', '', cap) - class ConsoleLogger(log.Logger): def __init__(self, verbosity, progress_mode=None): self._verbosity = verbosity Modified: trunk/epydoc/src/epydoc/util.py =================================================================== --- trunk/epydoc/src/epydoc/util.py 2008-02-02 06:28:09 UTC (rev 1713) +++ trunk/epydoc/src/epydoc/util.py 2008-02-02 06:31:52 UTC (rev 1714) @@ -16,7 +16,7 @@ """ __docformat__ = 'epytext en' -import os, os.path, re +import os, os.path, re, sys ###################################################################### ## Python Source Types @@ -287,3 +287,97 @@ return out, err else: raise RunSubprocessError(cmd, out, err) + +###################################################################### +## Terminal Control +###################################################################### + +class TerminalController: + """ + A class that can be used to portably generate formatted output to + a terminal. See + U{http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/475116} + for documentation. (This is a somewhat stripped-down version.) + """ + BOL = '' #: Move the cursor to the beginning of the line + UP = '' #: Move the cursor up one line + DOWN = '' #: Move the cursor down one line + LEFT = '' #: Move the cursor left one char + RIGHT = '' #: Move the cursor right one char + CLEAR_EOL = '' #: Clear to the end of the line. + CLEAR_LINE = '' #: Clear the current line; cursor to BOL. + BOLD = '' #: Turn on bold mode + NORMAL = '' #: Turn off all modes + COLS = 75 #: Width of the terminal (default to 75) + BLACK = BLUE = GREEN = CYAN = RED = MAGENTA = YELLOW = WHITE = '' + + _STRING_CAPABILITIES = """ + BOL=cr UP=cuu1 DOWN=cud1 LEFT=cub1 RIGHT=cuf1 + CLEAR_EOL=el BOLD=bold UNDERLINE=smul NORMAL=sgr0""".split() + _COLORS = """BLACK BLUE GREEN CYAN RED MAGENTA YELLOW WHITE""".split() + _ANSICOLORS = "BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE".split() + + #: If this is set to true, then new TerminalControllers will + #: assume that the terminal is not capable of doing manipulation + #: of any kind. + FORCE_SIMPLE_TERM = False + + def __init__(self, term_stream=sys.stdout): + # If the stream isn't a tty, then assume it has no capabilities. + if not term_stream.isatty(): return + if self.FORCE_SIMPLE_TERM: return + + # Curses isn't available on all platforms + try: import curses + except: + # If it's not available, then try faking enough to get a + # simple progress bar. + self.BOL = '\r' + self.CLEAR_LINE = '\r' + ' '*self.COLS + '\r' + + # Check the terminal type. If we fail, then assume that the + # terminal has no capabilities. + try: curses.setupterm() + except: return + + # Look up numeric capabilities. + self.COLS = curses.tigetnum('cols') + + # Look up string capabilities. + for capability in self._STRING_CAPABILITIES: + (attrib, cap_name) = capability.split('=') + setattr(self, attrib, self._tigetstr(cap_name) or '') + if self.BOL and self.CLEAR_EOL: + self.CLEAR_LINE = self.BOL+self.CLEAR_EOL + + # Colors + set_fg = self._tigetstr('setf') + if set_fg: + for i,color in zip(range(len(self._COLORS)), self._COLORS): + setattr(self, color, curses.tparm(set_fg, i) or '') + set_fg_ansi = self._tigetstr('setaf') + if set_fg_ansi: + for i,color in zip(range(len(self._ANSICOLORS)), self._ANSICOLORS): + setattr(self, color, curses.tparm(set_fg_ansi, i) or '') + + def _tigetstr(self, cap_name): + # String capabilities can include "delays" of the form "$<2>". + # For any modern terminal, we should be able to just ignore + # these, so strip them out. + import curses + cap = curses.tigetstr(cap_name) or '' + return re.sub(r'\$<\d+>[/*]?', '', cap) + + def render(self, template): + """ + Replace each $-substitutions in the given template string with + the corresponding terminal control string (if it's defined) or + '' (if it's not). + """ + return re.sub(r'\$\$|\${\w+}', self._render_sub, template) + + def _render_sub(self, match): + s = match.group() + if s == '$$': return s + else: return getattr(self, s[2:-1]) + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ed...@us...> - 2008-02-02 06:28:12
|
Revision: 1713 http://epydoc.svn.sourceforge.net/epydoc/?rev=1713&view=rev Author: edloper Date: 2008-02-01 22:28:09 -0800 (Fri, 01 Feb 2008) Log Message: ----------- - Removed restriction that --text can only take one name - Fixed bug that caused --text and --check to fail - Put blank lines around generated text docs Modified Paths: -------------- trunk/epydoc/src/epydoc/cli.py Modified: trunk/epydoc/src/epydoc/cli.py =================================================================== --- trunk/epydoc/src/epydoc/cli.py 2008-02-02 02:38:41 UTC (rev 1712) +++ trunk/epydoc/src/epydoc/cli.py 2008-02-02 06:28:09 UTC (rev 1713) @@ -495,8 +495,6 @@ if not options.parse and not options.introspect: optparser.error("Invalid option combination: --parse-only " "and --introspect-only.") - if 'text' in options.actions and len(names) > 1: - optparser.error("--text option takes only one name.") # Check the list of requested graph types to make sure they're # acceptable. @@ -730,6 +728,7 @@ # check the output targets. for action in options.actions: + if action not in TARGET_ACTIONS: continue target = options.target[action] if os.path.exists(target): if action not in ['html', 'latex'] and os.path.isdir(target): @@ -1026,13 +1025,13 @@ log.start_progress('Writing output') from epydoc.docwriter.plaintext import PlaintextWriter plaintext_writer = PlaintextWriter() - s = '' + s = '\n' for apidoc in docindex.root: - s += plaintext_writer.write(apidoc, **options.__dict__) + s += plaintext_writer.write(apidoc, **options.__dict__)+'\n' log.end_progress() if isinstance(s, unicode): s = s.encode('ascii', 'backslashreplace') - print s + sys.stdout.write(s) def check_docs(docindex, options): from epydoc.checker import DocChecker @@ -1117,6 +1116,7 @@ ###################################################################### #{ Logging ###################################################################### +# [xx] this should maybe move to util.py or log.py class TerminalController: """ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |