Package epydoc :: Package docwriter :: Module latex
[hide private]
[frames] | no frames]

Source Code for Module epydoc.docwriter.latex

   1  # 
   2  # epydoc.py: epydoc LaTeX output generator 
   3  # Edward Loper 
   4  # 
   5  # Created [01/30/01 05:18 PM] 
   6  # $Id: latex.py 1809 2008-03-05 18:40:49Z edloper $ 
   7  # 
   8   
   9  """ 
  10  The LaTeX output generator for epydoc.  The main interface provided by 
  11  this module is the L{LatexWriter} class. 
  12   
  13  @todo: Inheritance=listed 
  14  """ 
  15  __docformat__ = 'epytext en' 
  16   
  17  import os.path, sys, time, re, textwrap, codecs 
  18   
  19  from epydoc.apidoc import * 
  20  from epydoc.compat import * 
  21  import epydoc 
  22  from epydoc import log 
  23  from epydoc import markup 
  24  from epydoc.util import plaintext_to_latex 
  25  import epydoc.markup 
  26  from epydoc.docwriter.dotgraph import * 
  27  from epydoc.docwriter.latex_sty import STYLESHEETS 
  28   
29 -class LatexWriter:
30 #: Expects (options, epydoc_sty_package) 31 PREAMBLE = [ 32 "\\documentclass{article}", 33 "\\usepackage[%s]{%s}", 34 ] 35 36 SECTIONS = ['\\part{%s}', '\\chapter{%s}', '\\section{%s}', 37 '\\subsection{%s}', '\\subsubsection{%s}', 38 '\\textbf{%s}'] 39 40 STAR_SECTIONS = ['\\part*{%s}', '\\chapter*{%s}', '\\section*{%s}', 41 '\\subsection*{%s}', '\\subsubsection*{%s}', 42 '\\textbf{%s}'] 43
44 - def __init__(self, docindex, **kwargs):
45 self.docindex = docindex 46 # Process keyword arguments 47 self._show_private = kwargs.get('show_private', 0) 48 self._show_deprecated = kwargs.get('show_deprecated', True) 49 self._prj_name = kwargs.get('prj_name', None) 50 self._show_crossrefs = kwargs.get('crossref', 1) 51 self._index = kwargs.get('index', 1) 52 self._hyperlink = kwargs.get('hyperlink', True) 53 self._list_classes_separately=kwargs.get('list_classes_separately',0) 54 self._inheritance = kwargs.get('inheritance', 'listed') 55 self._exclude = kwargs.get('exclude', 1) 56 self._list_submodules = kwargs.get('list_submodules', 1) 57 self._sty = kwargs.get('sty') 58 self._top_section = 2 59 self._index_functions = 1 60 self._hyperref = 1 61 self._show_submodule_list = kwargs.get('show_submodule_list', True) 62 self._graph_types = kwargs.get('graphs', ()) or () 63 """Graphs that we should include in our output.""" 64 65 #: The Python representation of the encoding. 66 #: Update L{latex_encodings} in case of mismatch between it and 67 #: the C{inputenc} LaTeX package. 68 self._encoding = kwargs.get('encoding', 'utf-8') 69 70 self.valdocs = valdocs = sorted(docindex.reachable_valdocs( 71 imports=False, packages=False, bases=False, submodules=False, 72 subclasses=False, private=self._show_private)) 73 if not self._show_deprecated: 74 self.valdocs = valdocs = self._filter_deprecated(valdocs) 75 self._num_files = self.num_files() 76 # For use with select_variables(): 77 if self._show_private: self._public_filter = None 78 else: self._public_filter = True 79 80 self.class_list = [d for d in valdocs if isinstance(d, ClassDoc)] 81 """The list of L{ClassDoc}s for the documented classes.""" 82 self.class_set = set(self.class_list) 83 """The set of L{ClassDoc}s for the documented classes.""" 84 self.module_list = [d for d in valdocs if isinstance(d, ModuleDoc)] 85 """The list of L{ModuleDoc}s for the documented modules.""" 86 self.module_set = set(self.module_list) 87 """The set of L{ModuleDoc}s for the documented modules."""
88
89 - def write(self, directory=None):
90 """ 91 Write the API documentation for the entire project to the 92 given directory. 93 94 @type directory: C{string} 95 @param directory: The directory to which output should be 96 written. If no directory is specified, output will be 97 written to the current directory. If the directory does 98 not exist, it will be created. 99 @rtype: C{None} 100 @raise OSError: If C{directory} cannot be created, 101 @raise OSError: If any file cannot be created or written to. 102 """ 103 # For progress reporting: 104 self._files_written = 0. 105 106 # Set the default values for ValueDoc formatted representations. 107 orig_valdoc_defaults = (ValueDoc.SUMMARY_REPR_LINELEN, 108 ValueDoc.REPR_LINELEN, 109 ValueDoc.REPR_MAXLINES) 110 ValueDoc.SUMMARY_REPR_LINELEN = 60 111 ValueDoc.REPR_LINELEN = 52 112 ValueDoc.REPR_MAXLINES = 5 113 114 # Create destination directories, if necessary 115 if not directory: directory = os.curdir 116 self._mkdir(directory) 117 self._directory = directory 118 119 # Write the style file. 120 self._write_sty(directory, self._sty) 121 122 # Write the top-level file. 123 self._write(self.write_topfile, directory, 'api.tex') 124 125 # Write the module & class files. 126 for val_doc in self.valdocs: 127 if isinstance(val_doc, ModuleDoc): 128 filename = '%s-module.tex' % val_doc.canonical_name 129 self._write(self.write_module, directory, filename, val_doc) 130 elif isinstance(val_doc, ClassDoc): 131 filename = '%s-class.tex' % val_doc.canonical_name 132 self._write(self.write_class, directory, filename, val_doc) 133 134 # Restore defaults that we changed. 135 (ValueDoc.SUMMARY_REPR_LINELEN, ValueDoc.REPR_LINELEN, 136 ValueDoc.REPR_MAXLINES) = orig_valdoc_defaults
137
138 - def _write_sty(self, directory, stylesheet):
139 """ 140 Copy the requested LaTeX stylesheet to the target directory. 141 The stylesheet can be specified as a name (i.e., a key from 142 the STYLESHEETS directory); a filename; or None for the default 143 stylesheet. If any stylesheet *other* than the default 144 stylesheet is selected, then the default stylesheet will be 145 copied to 'epydoc-default.sty', which makes it possible to 146 reference it via \RequirePackage. 147 """ 148 # Write all the standard style files 149 for (name, sty) in STYLESHEETS.items(): 150 out = open(os.path.join(directory, 'epydoc-%s.sty' % name), 'wb') 151 out.write(sty) 152 out.close() 153 154 # Default: use the 'epydoc-default' style. 155 if stylesheet is None: 156 self._epydoc_sty_package = 'epydoc-default' 157 158 # Stylesheet name: use the indicated style. 159 elif stylesheet in STYLESHEETS: 160 self._epydoc_sty_package = 'epydoc-%s' % stylesheet 161 162 # Custom user stylesheet: copy the style to epydoc-custom. 163 elif os.path.exists(stylesheet): 164 try: sty = open(stylesheet, 'rb').read() 165 except: raise IOError("Can't open LaTeX style file: %r" % 166 stylesheet) 167 out = open(os.path.join(directory, 'epydoc-custom.sty'), 'wb') 168 out.write(sty) 169 out.close() 170 self._epydoc_sty_package = 'epydoc-custom' 171 172 else: 173 raise IOError("Can't find LaTeX style file: %r" % stylesheet)
174
175 - def _write(self, write_func, directory, filename, *args):
176 # Display our progress. 177 self._files_written += 1 178 log.progress(self._files_written/self._num_files, filename) 179 180 path = os.path.join(directory, filename) 181 if self._encoding == 'utf-8': 182 f = codecs.open(path, 'w', 'utf-8') 183 write_func(f.write, *args) 184 f.close() 185 else: 186 result = [] 187 write_func(result.append, *args) 188 s = u''.join(result) 189 try: 190 s = s.encode(self._encoding) 191 except UnicodeError: 192 log.error("Output could not be represented with the " 193 "given encoding (%r). Unencodable characters " 194 "will be displayed as '?'. It is recommended " 195 "that you use a different output encoding (utf-8, " 196 "if it's supported by latex on your system)." 197 % self._encoding) 198 s = s.encode(self._encoding, 'replace') 199 f = open(path, 'w') 200 f.write(s) 201 f.close()
202
203 - def num_files(self):
204 """ 205 @return: The number of files that this C{LatexFormatter} will 206 generate. 207 @rtype: C{int} 208 """ 209 return 1 + len([doc for doc in self.valdocs 210 if isinstance(doc, (ClassDoc, ModuleDoc))])
211
212 - def _mkdir(self, directory):
213 """ 214 If the given directory does not exist, then attempt to create it. 215 @rtype: C{None} 216 """ 217 if not os.path.isdir(directory): 218 if os.path.exists(directory): 219 raise OSError('%r is not a directory' % directory) 220 os.mkdir(directory)
221 222 #//////////////////////////////////////////////////////////// 223 #{ Main Doc File 224 #//////////////////////////////////////////////////////////// 225
226 - def write_topfile(self, out):
227 self.write_header(out, 'Include File') 228 self.write_preamble(out) 229 out('\n\\begin{document}\n\n') 230 out(self.start_of('Header')) 231 232 # Write the title. 233 out(self.start_of('Title')) 234 out('\\title{%s}\n' % plaintext_to_latex( 235 self._prj_name or 'API Documentation', 1)) 236 out('\\author{API Documentation}\n') 237 out('\\maketitle\n') 238 239 # Add a table of contents. 240 out(self.start_of('Table of Contents')) 241 out('\\addtolength{\\parskip}{-1ex}\n') 242 out('\\tableofcontents\n') 243 out('\\addtolength{\\parskip}{1ex}\n') 244 245 # Include documentation files. 246 out(self.start_of('Includes')) 247 for val_doc in self.valdocs: 248 if isinstance(val_doc, ModuleDoc): 249 out('\\include{%s-module}\n' % val_doc.canonical_name) 250 251 # If we're listing classes separately, put them after all the 252 # modules. 253 if self._list_classes_separately: 254 for val_doc in self.valdocs: 255 if isinstance(val_doc, ClassDoc): 256 out('\\include{%s-class}\n' % val_doc.canonical_name) 257 258 # Add the index, if requested. 259 if self._index: 260 out(self.start_of('Index')) 261 out('\\printindex\n\n') 262 263 # Add the footer. 264 out(self.start_of('Footer')) 265 out('\\end{document}\n\n')
266
267 - def write_preamble(self, out):
268 # If we're generating an index, add it to the preamble. 269 options = [] 270 options.append('creator={epydoc %s}' % epydoc.__version__) 271 options.append('title={%s}' % plaintext_to_latex(self._prj_name or '')) 272 if self._index: options.append('index') 273 if self._hyperlink: options.append('hyperlink') 274 out('\n'.join(self.PREAMBLE) % (','.join(options), 275 self._epydoc_sty_package) + '\n') 276 277 # Set the encoding. 278 out('\\usepackage[%s]{inputenc}\n' % self.get_latex_encoding()) 279 280 # If we're generating hyperrefs, add the appropriate packages. 281 # Note: this needs to be the last thing in the preamble -JEG 282 if self._hyperref: 283 out('\\definecolor{UrlColor}{rgb}{0,0.08,0.45}\n') 284 285 # If restructuredtext was used, then we need to extend 286 # the prefix to include LatexTranslator.head_prefix. 287 if 'restructuredtext' in epydoc.markup.MARKUP_LANGUAGES_USED: 288 from epydoc.markup import restructuredtext 289 rst_head = restructuredtext.latex_head_prefix() 290 rst_head = ''.join(rst_head).split('\n') 291 for line in rst_head[1:]: 292 m = re.match(r'\\usepackage(\[.*?\])?{(.*?)}', line) 293 if m and m.group(2) in ( 294 'babel', 'hyperref', 'color', 'alltt', 'parskip', 295 'fancyhdr', 'boxedminipage', 'makeidx', 296 'multirow', 'longtable', 'tocbind', 'assymb', 297 'fullpage', 'inputenc'): 298 pass 299 else: 300 out(line+'\n')
301 302 303 #//////////////////////////////////////////////////////////// 304 #{ Chapters 305 #//////////////////////////////////////////////////////////// 306
307 - def write_module(self, out, doc):
308 self.write_header(out, doc) 309 out(self.start_of('Section Heading', doc)) 310 311 # Add this module to the index. 312 out(self.indexterm(doc, 'start')) 313 314 # Add a section marker. 315 out(self.section('%s %s' % (self.doc_kind(doc), 316 _dotted(doc.canonical_name)), 317 ref=doc)) 318 319 # Add the module's description. 320 if doc.descr not in (None, UNKNOWN): 321 out(self.start_of('Description', doc)) 322 out('\\begin{EpydocModuleDescription}%\n') 323 out(self.docstring_to_latex(doc.descr, doc, 4)) 324 out('\\end{EpydocModuleDescription}\n') 325 326 # Add version, author, warnings, requirements, notes, etc. 327 out(self.metadata(doc)) 328 329 # If it's a package, list the sub-modules. 330 if (self._list_submodules and self._show_submodule_list and 331 doc.submodules != UNKNOWN and doc.submodules): 332 self.write_module_list(out, doc) 333 334 # Contents. 335 if self._list_classes_separately: 336 self.write_class_list(out, doc) 337 self.write_list(out, 'Functions', doc, 'EpydocFunctionList', 338 'function') 339 self.write_list(out, 'Variables', doc, 'EpydocVariableList', 'other') 340 341 # Class list. 342 if not self._list_classes_separately: 343 classes = doc.select_variables(imported=False, value_type='class', 344 public=self._public_filter) 345 classes = self._filter_deprecated(classes) 346 # Only include classes that 'belong' to this module. 347 classes = [c for c in classes if c.defining_module is doc] 348 # List the classes 349 if classes: 350 out(self.start_of('Classes', doc)) 351 for var_doc in classes: 352 # don't use \include -- can't be nested. 353 out('\\input{%s-class}\n' % var_doc.value.canonical_name) 354 355 # Mark the end of the module (for the index) 356 out(self.start_of('Footer', doc)) 357 out(self.indexterm(doc, 'end'))
358
359 - def render_graph(self, graph):
360 if graph is None: return '' 361 graph.caption = graph.title = None 362 return graph.to_latex(self._directory) or ''
363
364 - def write_class(self, out, doc):
365 self.write_header(out, doc) 366 out(self.start_of('Section Heading', doc)) 367 368 # Add this class to the index. 369 out(self.indexterm(doc, 'start')) 370 371 # Decide on our short (contextualized) name. 372 if self._list_classes_separately: 373 short_name = doc.canonical_name 374 if doc.defining_module not in (None, UNKNOWN): 375 short_name = doc.canonical_name.contextualize( 376 doc.defining_module.canonical_name) 377 else: 378 short_name = doc.canonical_name[-1] 379 380 # Decidie on our initial section level. 381 if self._list_classes_separately: 382 seclevel = 0 383 else: 384 seclevel = 1 385 386 # Add a section marker. 387 out(self.section('%s %s' % (self.doc_kind(doc), _dotted(short_name)), 388 seclevel, ref=doc)) 389 390 # Display our base classes & subclasses 391 out(self.start_of('Class Tree', doc)) 392 if ((doc.bases not in (UNKNOWN, None) and len(doc.bases) > 0) or 393 (doc.subclasses not in (UNKNOWN,None) and len(doc.subclasses)>0)): 394 # Display bases graphically, if requested. 395 if 'umlclasstree' in self._graph_types: 396 graph = uml_class_tree_graph(doc, self._docstring_linker, doc) 397 out(self.render_graph(graph)) 398 399 elif 'classtree' in self._graph_types: 400 graph = class_tree_graph([doc], self._docstring_linker, doc) 401 out(self.render_graph(graph)) 402 403 # Otherwise, use ascii-art. 404 else: 405 406 # Add our base list. 407 if doc.bases not in (UNKNOWN, None) and len(doc.bases) > 0: 408 out(self.base_tree(doc)) 409 410 # The class's known subclasses 411 if (doc.subclasses not in (UNKNOWN, None) and 412 len(doc.subclasses) > 0): 413 sc_items = [_hyperlink(sc, '%s' % sc.canonical_name) 414 for sc in doc.subclasses] 415 out('{\\raggedright%\n') 416 out(self._descrlist(sc_items, 'Known Subclasses', short=1)) 417 out('}%\n') 418 419 # The class's description. 420 if doc.descr not in (None, UNKNOWN): 421 out(self.start_of('Description', doc)) 422 out('\\begin{EpydocClassDescription}%\n') 423 out(self.docstring_to_latex(doc.descr, doc, 4)) 424 out('\\end{EpydocClassDescription}\n') 425 426 # Version, author, warnings, requirements, notes, etc. 427 out(self.metadata(doc)) 428 429 # Contents. 430 self.write_list(out, 'Methods', doc, 'EpydocFunctionList', 431 'method', seclevel+1) 432 self.write_list(out, 'Properties', doc, 'EpydocPropertyList', 433 'property', seclevel+1) 434 self.write_list(out, 'Class Variables', doc, 435 'EpydocClassVariableList', 436 'classvariable', seclevel+1) 437 self.write_list(out, 'Instance Variables', doc, 438 'EpydocInstanceVariableList', 439 'instancevariable', seclevel+1) 440 441 # Mark the end of the class (for the index) 442 out(self.start_of('Footer', doc)) 443 out(self.indexterm(doc, 'end')) 444 445 # Write any nested classes. These will have their own 446 # section (at the same level as this section) 447 if not self._list_classes_separately: 448 nested_classes = self._filter_deprecated(doc.select_variables( 449 imported=False, value_type='class', 450 public=self._public_filter)) 451 if nested_classes: 452 out(self.start_of('Nested Classes', doc)) 453 for nested_class in nested_classes: 454 if (nested_class.value.canonical_name != UNKNOWN and 455 (nested_class.value.canonical_name[:-1] == 456 doc.canonical_name)): 457 # don't use \include -- can't be nested. 458 out('\\input{%s-class}\n' % 459 nested_class.value.canonical_name)
460 461 #//////////////////////////////////////////////////////////// 462 #{ Module hierarchy trees 463 #//////////////////////////////////////////////////////////// 464
465 - def write_module_tree(self, out):
466 modules = [doc for doc in self.valdocs 467 if isinstance(doc, ModuleDoc)] 468 if not modules: return 469 470 # Write entries for all top-level modules/packages. 471 out('\\begin{itemize}\n') 472 out('\\setlength{\\parskip}{0ex}\n') 473 for doc in modules: 474 if (doc.package in (None, UNKNOWN) or 475 doc.package not in self.valdocs): 476 self.write_module_tree_item(out, doc) 477 return s +'\\end{itemize}\n'
478
479 - def write_module_list(self, out, doc):
480 if len(doc.submodules) == 0: return 481 out(self.start_of('Submodules', doc)) 482 483 out(self.section('Submodules', 1)) 484 out('\\begin{EpydocModuleList}\n') 485 486 for group_name in doc.group_names(): 487 if not doc.submodule_groups[group_name]: continue 488 if group_name: 489 out(' \\EpydocGroup{%s}\n' % group_name) 490 #out(' \\begin{EpydocModuleList}\n') 491 for submodule in doc.submodule_groups[group_name]: 492 self.write_module_tree_item(out, submodule) 493 #if group_name: 494 # out(' \end{EpydocModuleList}\n') 495 496 out('\\end{EpydocModuleList}\n\n')
497
498 - def write_module_tree_item(self, out, doc, depth=0):
499 """ 500 Helper function for L{write_module_tree} and L{write_module_list}. 501 502 @rtype: C{string} 503 """ 504 out(' '*depth + '\\EpydocModule{%% <<<%s>>>\n' % doc.canonical_name) 505 out(' '*depth + ' name={%s}' % 506 _hyperlink(doc, doc.canonical_name[-1])) 507 if doc.summary not in (None, UNKNOWN): 508 out(',\n' + ' '*depth+' summary={\n') 509 out(self.docstring_to_latex(doc.summary, doc, depth+4)) 510 out(' '*depth+' }') 511 out(',\n' + ' '*depth + ' crossref={\n') 512 out(self.crossref(doc, depth+4) + ' '*depth+' }') 513 out('}%\n') 514 if doc.submodules != UNKNOWN and doc.submodules: 515 out(' '*depth+'\\begin{EpydocModuleList}%\n') 516 for submodule in doc.submodules: 517 self.write_module_tree_item(out, submodule, depth+4) 518 out(' '*depth+'\\end{EpydocModuleList}%\n')
519 520 521 #//////////////////////////////////////////////////////////// 522 #{ Base class trees 523 #//////////////////////////////////////////////////////////// 524
525 - def base_tree(self, doc, width=None, linespec=None):
526 if width is None: 527 width = self._find_tree_width(doc)+2 528 linespec = [] 529 s = (' %% Class tree line for this class (%s)\n ' % 530 doc.canonical_name + '&'*(width-4) + 531 '\\multicolumn{2}{l}{\\textbf{%s}}\n' % 532 _dotted('%s'%self._base_name(doc))) 533 s += '\\end{tabular}\n\n' 534 top = 1 535 else: 536 s = self._base_tree_line(doc, width, linespec) 537 top = 0 538 539 if isinstance(doc, ClassDoc): 540 for i in range(len(doc.bases)-1, -1, -1): 541 base = doc.bases[i] 542 spec = (i > 0) 543 s = self.base_tree(base, width, [spec]+linespec) + s 544 545 if top: 546 s = '\\begin{tabular}{%s}\n' % (width*'c') + s 547 548 return s
549
550 - def _base_name(self, doc):
551 if doc.canonical_name is None: 552 if doc.parse_repr is not None: 553 return doc.parse_repr 554 else: 555 return '??' 556 else: 557 return '%s' % doc.canonical_name
558
559 - def _find_tree_width(self, doc):
560 if not isinstance(doc, ClassDoc): return 2 561 width = 2 562 for base in doc.bases: 563 width = max(width, self._find_tree_width(base)+2) 564 return width
565
566 - def _base_tree_line(self, doc, width, linespec):
567 # linespec is a list of booleans. 568 base_name = _dotted(self._base_name(doc)) 569 570 s = ' %% Class tree line for base "%s"\n' % self._base_name(doc) 571 labelwidth = width-2*len(linespec)-2 572 573 # The base class name. 574 s += ' \\multicolumn{%s}{r}{\n' % labelwidth 575 s += ' \\settowidth{\\EpydocBCL}{%s}\n' % base_name 576 s += ' \\multirow{2}{\\EpydocBCL}{\n' 577 s += ' %s}}\n' % _hyperlink(doc, self._base_name(doc)) 578 579 # The vertical bars for other base classes (top half) 580 for vbar in linespec: 581 if vbar: s += ' &&\\multicolumn{1}{|c}{}\n' 582 else: s += ' &&\n' 583 584 # The horizontal line. 585 s += ' \\\\\\cline{%s-%s}\n' % (labelwidth+1, labelwidth+1) 586 587 # The vertical bar for this base class. 588 s += ' ' + '&'*labelwidth 589 s += '\\multicolumn{1}{c|}{}\n' 590 591 # The vertical bars for other base classes (bottom half) 592 for vbar in linespec: 593 if vbar: s += ' &\\multicolumn{1}{|c}{}&\n' 594 else: s += ' &&\n' 595 596 s += ' \\\\\n' 597 598 return s
599 600 #//////////////////////////////////////////////////////////// 601 #{ Class List 602 #//////////////////////////////////////////////////////////// 603
604 - def write_class_list(self, out, doc):
605 groups = [(plaintext_to_latex(group_name), 606 self._filter_deprecated( 607 doc.select_variables(group=group_name, imported=False, 608 value_type='class', 609 public=self._public_filter))) 610 for group_name in doc.group_names()] 611 612 # Discard any empty groups; and return if they're all empty. 613 groups = [(g,vars) for (g,vars) in groups if vars] 614 if not groups: return 615 616 # Write a header. 617 out(self.start_of('Classes', doc)) 618 out(self.section('Classes', 1)) 619 out('\\begin{EpydocClassList}\n') 620 621 for name, var_docs in groups: 622 if name: 623 out(' \\EpydocGroup{%s}\n' % name) 624 #out(' \\begin{EpydocClassList}\n') 625 # Add the lines for each class 626 for var_doc in var_docs: 627 self.write_class_list_line(out, var_doc) 628 #if name: 629 # out(' \\end{EpydocClassList}\n') 630 631 out('\\end{EpydocClassList}\n')
632
633 - def write_class_list_line(self, out, var_doc):
634 if var_doc.value in (None, UNKNOWN): return # shouldn't happen 635 doc = var_doc.value 636 out(' \\EpydocClass{%% <<<%s>>> \n' % var_doc.name) 637 out(' name={%s}' % 638 _hyperlink(var_doc.value, var_doc.name)) 639 if doc.summary not in (None, UNKNOWN): 640 out(',\n summary={\n%s }' % 641 self.docstring_to_latex(doc.summary, doc, 6)) 642 out(',\n crossref={\n%s }' % self.crossref(doc, 6)) 643 out('}%\n')
644 645 #//////////////////////////////////////////////////////////// 646 #{ Details Lists 647 #//////////////////////////////////////////////////////////// 648 649 # Also used for the property list.
650 - def write_list(self, out, heading, doc, list_type, 651 value_type, seclevel=1):
652 # Divide all public variables of the given type into groups. 653 groups = [(plaintext_to_latex(group_name), 654 self._filter_deprecated( 655 doc.select_variables(group=group_name, imported=False, 656 value_type=value_type, 657 public=self._public_filter))) 658 for group_name in doc.group_names()] 659 660 # Discard any empty groups; and return if they're all empty. 661 groups = [(g,vars) for (g,vars) in groups if vars] 662 if not groups: return 663 664 # Write a header. 665 out(self.start_of(heading, doc)) 666 out(self.section(heading, seclevel)) 667 668 out('\\begin{%s}\n' % list_type) 669 670 # Write a section for each group. 671 grouped_inh_vars = {} 672 for name, var_docs in groups: 673 self.write_list_group(out, doc, name, var_docs, grouped_inh_vars) 674 675 # Write a section for each inheritance pseudo-group (used if 676 # inheritance=='grouped') 677 if grouped_inh_vars: 678 for base in doc.mro(): 679 if base in grouped_inh_vars: 680 hdr = ('Inherited from %s' % 681 plaintext_to_latex('%s' % base.canonical_name)) 682 out(self.crossref(base) + '\n\n') 683 out('\\EpydocGroup{%s}\n' % hdr) 684 for var_doc in grouped_inh_vars[base]: 685 if isinstance(var_doc.value, RoutineDoc): 686 self.write_function(out, var_doc) 687 elif isinstance(var_doc.value, PropertyDoc): 688 self.write_property(out, var_doc) 689 else: 690 self.write_var(out, var_doc) 691 692 out('\\end{%s}\n\n' % list_type)
693
694 - def write_list_group(self, out, doc, name, var_docs, grouped_inh_vars):
695 # Split up the var_docs list, according to the way each var 696 # should be displayed: 697 # - listed_inh_vars -- for listed inherited variables. 698 # - grouped_inh_vars -- for grouped inherited variables. 699 # - normal_vars -- for all other variables. 700 listed_inh_vars = {} 701 normal_vars = [] 702 for var_doc in var_docs: 703 if var_doc.container != doc: 704 base = var_doc.container 705 if (base not in self.class_set or 706 self._inheritance == 'listed'): 707 listed_inh_vars.setdefault(base,[]).append(var_doc) 708 elif self._inheritance == 'grouped': 709 grouped_inh_vars.setdefault(base,[]).append(var_doc) 710 elif self._inheritance == 'hidden': 711 pass 712 else: 713 normal_vars.append(var_doc) 714 else: 715 normal_vars.append(var_doc) 716 717 # Write a header for the group. 718 if name: 719 out('\\EpydocGroup{%s}\n' % name) 720 # Write an entry for each object in the group: 721 for var_doc in normal_vars: 722 if isinstance(var_doc.value, RoutineDoc): 723 self.write_function(out, var_doc) 724 elif isinstance(var_doc.value, PropertyDoc): 725 self.write_property(out, var_doc) 726 else: 727 self.write_var(out, var_doc) 728 # Write a subsection for inherited objects: 729 if listed_inh_vars: 730 self.write_inheritance_list(out, doc, listed_inh_vars)
731
732 - def write_inheritance_list(self, out, doc, listed_inh_vars):
733 for base in doc.mro(): 734 if base not in listed_inh_vars: continue 735 #if str(base.canonical_name) == 'object': continue 736 var_docs = listed_inh_vars[base] 737 if not self._show_private: 738 var_docs = [v for v in var_docs if v.is_public] 739 if not self._show_deprecated: 740 var_docs = self._filter_deprecated(var_docs) 741 if var_docs: 742 out('\\EpydocInheritanceList{') 743 out(plaintext_to_latex('%s' % base.canonical_name)) 744 out(self.crossref(base)) 745 out('}{') 746 out(', '.join(['%s' % plaintext_to_latex(var_doc.name) + 747 self._parens_if_func(var_doc) 748 for var_doc in var_docs])) 749 out('}\n')
750
751 - def _parens_if_func(self, var_doc):
752 if isinstance(var_doc.value, RoutineDoc): return '()' 753 else: return ''
754 755 #//////////////////////////////////////////////////////////// 756 #{ Function Details 757 #//////////////////////////////////////////////////////////// 758
759 - def replace_par(self, out):
760 def new_out(s): 761 s = re.sub('(?m)\n([ \t]*\n)+', '\\par\n', s) 762 s = re.sub(r'\\par\b', r'\\EpydocPar', s) 763 s = re.sub(r'(?m)^([ \t]*)([^ \t].*)\\EpydocPar\n', 764 r'\1\2\n\1\\EpydocPar\n', s) 765 out(s)
766 return new_out
767
768 - def write_function(self, out, var_doc):
769 func_doc = var_doc.value 770 is_inherited = (var_doc.overrides not in (None, UNKNOWN)) 771 772 # Add the function to the index. Note: this will actually 773 # select the containing section, and won't give a reference 774 # directly to the function. 775 if not is_inherited: 776 out(' %s' % self.indexterm(func_doc)) 777 778 out(' \\EpydocFunction{%% <<< %s >>>\n' % var_doc.name) 779 780 # We're passing arguments using xkeyval, which is unhappy if 781 # the arguments contain \par. So replace every occurence of 782 # \par with \EpydocPar (which is defined to just return \par). 783 out = self.replace_par(out) 784 785 # Argument 1: the function signature 786 out(' signature={%%\n%s }' % 787 self.function_signature(var_doc)) 788 789 # Argument 2: the function description 790 if func_doc.descr not in (None, UNKNOWN): 791 out(',\n description={%\n') 792 out(self.docstring_to_latex(func_doc.descr, func_doc, 6)) 793 out(' }') 794 795 # Argument 3: the function parameter descriptions 796 if func_doc.arg_descrs or func_doc.arg_types: 797 out(',\n parameters={%\n') 798 self.write_function_parameters(out, var_doc) 799 out(' }') 800 801 # Argument 4: The return description 802 if func_doc.return_descr not in (None, UNKNOWN): 803 out(',\n returndescr={%\n') 804 out(self.docstring_to_latex(func_doc.return_descr, func_doc, 6)) 805 out(' }') 806 807 # Argument 5: The return type 808 if func_doc.return_type not in (None, UNKNOWN): 809 out(',\n returntype={%\n') 810 out(self.docstring_to_latex(func_doc.return_type, func_doc, 6)) 811 out(' }') 812 813 # Argument 6: The raises section 814 if func_doc.exception_descrs not in (None, UNKNOWN, [], ()): 815 out(',\n raises={%\n') 816 out(' '*6+'\\begin{EpydocFunctionRaises}\n') 817 for name, descr in func_doc.exception_descrs: 818 out(' '*10+'\\item[%s]\n\n' % 819 plaintext_to_latex('%s' % name)) 820 out(self.docstring_to_latex(descr, func_doc, 10)) 821 out(' '*6+'\\end{EpydocFunctionRaises}\n') 822 out(' }') 823 824 # Argument 7: The overrides section 825 if var_doc.overrides not in (None, UNKNOWN): 826 out(',\n overrides={%\n') 827 out('\\EpydocFunctionOverrides') 828 if (func_doc.docstring in (None, UNKNOWN) and 829 var_doc.overrides.value.docstring not in (None, UNKNOWN)): 830 out('[1]') 831 out('{%s}\n' 832 % _hyperlink(var_doc.overrides, 833 '%s' % var_doc.overrides.canonical_name)) 834 out(' }') 835 836 # Argument 8: The metadata section 837 metadata = self.metadata(func_doc, 6) 838 if metadata: 839 out(',\n metadata={%%\n%s }' % metadata) 840 841 out('}%\n')
842
843 - def write_function_parameters(self, out, var_doc):
844 func_doc = var_doc.value 845 # Find the longest name. 846 longest = max([0]+[len(n) for n in func_doc.arg_types]) 847 for names, descrs in func_doc.arg_descrs: 848 longest = max([longest]+[len(n) for n in names]) 849 # Table header. 850 out(' '*6+'\\begin{EpydocFunctionParameters}{%s}\n' % (longest*'x')) 851 # Add params that have @type but not @param info: 852 arg_descrs = list(func_doc.arg_descrs) 853 args = set() 854 for arg_names, arg_descr in arg_descrs: 855 args.update(arg_names) 856 for arg in var_doc.value.arg_types: 857 if arg not in args: 858 arg_descrs.append( ([arg],None) ) 859 # Display params 860 for (arg_names, arg_descr) in arg_descrs: 861 arg_name = plaintext_to_latex(', '.join(arg_names)) 862 out('%s\\item[%s]\n' % (' '*8, arg_name)) 863 if arg_descr: 864 out(self.docstring_to_latex(arg_descr, func_doc, 10)) 865 # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 866 # !!! JEG - this loop needs abstracting 867 # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 868 for arg_name in arg_names: 869 arg_typ = func_doc.arg_types.get(arg_name) 870 if arg_typ is not None: 871 if len(arg_names) == 1: 872 lhs = 'type' 873 else: 874 lhs = 'type of %s' % arg_name 875 rhs = self.docstring_to_latex(arg_typ, func_doc, 14) 876 out('%s\\textit{ (%s=%%\n%s%s)}\n' % (' '*12, lhs, 877 rhs, ' '*12)) 878 out(' '*6+'\\end{EpydocFunctionParameters}\n')
879
880 - def function_signature(self, var_doc, indent=6):
881 func_doc = var_doc.value 882 func_name = var_doc.name 883 884 s = ('%s\\begin{EpydocFunctionSignature}%%\n%s {%s}%%\n' % 885 (indent*' ', indent*' ', _hypertarget(var_doc, func_name))) 886 887 # This should never happen, but just in case: 888 if func_doc not in (None, UNKNOWN): 889 if func_doc.posargs == UNKNOWN: 890 args = ['\\GenericArg{}'] 891 else: 892 args = [self.func_arg(name, default) for (name, default) 893 in zip(func_doc.posargs, func_doc.posarg_defaults)] 894 if func_doc.vararg: 895 if func_doc.vararg == '...': 896 args.append('\\GenericArg{}') 897 else: 898 args.append('\\VarArg{%s}' % 899 plaintext_to_latex(func_doc.vararg)) 900 if func_doc.kwarg: 901 args.append('\\KWArg{%s}' % plaintext_to_latex(func_doc.kwarg)) 902 903 argindent = (indent*' '+' ') 904 s += argindent+('%%\n%s\\and' % argindent).join(args)+'%\n' 905 s += indent*' '+'\\end{EpydocFunctionSignature}%\n' 906 907 return s
908
909 - def func_arg(self, name, default):
910 s = '\\Param' 911 912 if default is not None: 913 s += "[%s]" % default.summary_pyval_repr().to_latex(None) 914 s += '{%s}' % self._arg_name(name) 915 916 return s
917
918 - def _arg_name(self, arg):
919 if isinstance(arg, basestring): 920 return plaintext_to_latex(arg) 921 else: 922 return '\\TupleArg{%s}' % '\\and '.join([self._arg_name(a) 923 for a in arg])
924 925 #//////////////////////////////////////////////////////////// 926 #{ Variable Details 927 #//////////////////////////////////////////////////////////// 928
929 - def write_var(self, out, var_doc):
930 # We're passing arguments using xkeyval, which is unhappy if 931 # the arguments contain \par. So replace every occurence of 932 # \par with \EpydocPar (which is defined to just return \par). 933 out = self.replace_par(out) 934 935 has_descr = var_doc.descr not in (None, UNKNOWN) 936 has_type = var_doc.type_descr not in (None, UNKNOWN) 937 has_repr = (var_doc.value not in (None, UNKNOWN) and 938 (var_doc.value.parse_repr is not UNKNOWN or 939 var_doc.value.pyval_repr() is not UNKNOWN)) 940 941 out(' \\EpydocVariable{%% <<< %s >>>\n' % var_doc.name) 942 out(' name={%s}' % _hypertarget(var_doc, var_doc.name)) 943 if has_descr: 944 out(',\n description={%%\n%s }' % 945 self.docstring_to_latex(var_doc.descr, var_doc, 6)) 946 if has_type: 947 out(',\n type={%%\n%s }' % 948 self.docstring_to_latex(var_doc.type_descr, var_doc, 6)) 949 if has_repr: 950 out(',\n value={%s}' % 951 var_doc.value.summary_pyval_repr().to_latex(None)) 952 metadata = self.metadata(var_doc, 6) 953 if metadata: 954 out(',\n metadata={%%\n%s }' % metadata) 955 out('}%\n')
956 957 #//////////////////////////////////////////////////////////// 958 #{ Property Details 959 #//////////////////////////////////////////////////////////// 960
961 - def write_property(self, out, var_doc):
962 # We're passing arguments using xkeyval, which is unhappy if 963 # the arguments contain \par. So replace every occurence of 964 # \par with \EpydocPar (which is defined to just return \par). 965 out = self.replace_par(out) 966 967 prop_doc = var_doc.value 968 has_descr = prop_doc.descr not in (None, UNKNOWN) 969 has_type = prop_doc.type_descr not in (None, UNKNOWN) 970 971 out(' \\EpydocProperty{%% <<< %s >>>\n' % var_doc.name) 972 out(' name={%s}' % _hypertarget(var_doc, var_doc.name)) 973 if has_descr: 974 out(',\n description={%%\n%s }' % 975 self.docstring_to_latex(prop_doc.descr, prop_doc, 6)) 976 if has_type: 977 out(',\n type={%%\n%s }' % 978 self.docstring_to_latex(prop_doc.type_descr, prop_doc, 6)) 979 # [xx] What if the accessor is private and show_private=False? 980 for accessor in ('fget', 'fset', 'fdel'): 981 accessor_func = getattr(prop_doc, accessor) 982 if (accessor_func not in (None, UNKNOWN) and 983 not accessor_func.canonical_name[0].startswith('??')): 984 if isinstance(accessor_func, RoutineDoc): 985 suffix = '()' 986 else: 987 suffix = '' 988 out(',\n %s={%s%s}' % 989 (accessor, _dotted(accessor_func.canonical_name), suffix)) 990 metadata = self.metadata(prop_doc, 6) 991 if metadata: 992 out(',\n metadata={%%\n%s }' % metadata) 993 out('}%\n')
994 995 #//////////////////////////////////////////////////////////// 996 #{ Standard Fields 997 #//////////////////////////////////////////////////////////// 998
999 - def metadata(self, doc, indent=0):
1000 fields = [] 1001 field_values = {} 1002 s = '' 1003 1004 for (field, arg, descr) in doc.metadata: 1005 if field not in field_values: 1006 fields.append(field) 1007 if field.takes_arg: 1008 subfields = field_values.setdefault(field,{}) 1009 subfields.setdefault(arg,[]).append(descr) 1010 else: 1011 field_values.setdefault(field,[]).append(descr) 1012 1013 if fields and indent == 0: 1014 s += self.start_of('Metadata', doc) 1015 1016 for field in fields: 1017 if field.takes_arg: 1018 for arg, descrs in field_values[field].items(): 1019 s += self.meatadata_field(doc, field, descrs, 1020 indent, arg) 1021 else: 1022 s += self.meatadata_field(doc, field, field_values[field], 1023 indent) 1024 return s
1025 1026
1027 - def meatadata_field(self, doc, field, descrs, indent, arg=''):
1028 singular = field.singular 1029 plural = field.plural 1030 if arg: 1031 singular += ' (%s)' % arg 1032 plural += ' (%s)' % arg 1033 return (' '*indent + '%% %s:\n' % field.singular + 1034 self._descrlist([self.docstring_to_latex(d, doc, indent+2) 1035 for d in descrs], 1036 field.singular, field.plural, field.short, 1037 indent))
1038 1039 # [xx] ignores indent for now
1040 - def _descrlist(self, items, singular, plural=None, short=0, indent=0):
1041 ind = indent*' ' 1042 if plural is None: plural = singular 1043 if len(items) == 0: return '' 1044 if len(items) == 1 and singular is not None: 1045 return ('%s\\EpydocMetadataSingleValue{%s}{\n%s%s}\n' % 1046 (ind, singular, items[0], ind)) 1047 if short: 1048 s = '%s\\begin{EpydocMetadataShortList}{%s}\n' % (ind, plural) 1049 s += ('%s\\and\n' % ind).join(items) 1050 s += '%s\\end{EpydocMetadataShortList}\n' % ind 1051 return s 1052 else: 1053 s = '%s\\begin{EpydocMetadataLongList}{%s}\n' % (ind, plural) 1054 s += ''.join(['%s \item\n%s' % (ind,item) for item in items]) 1055 s += '%s\\end{EpydocMetadataLongList}\n' % ind 1056 return s
1057 1058 1059 #//////////////////////////////////////////////////////////// 1060 #{ Docstring -> LaTeX Conversion 1061 #//////////////////////////////////////////////////////////// 1062 1063 # We only need one linker, since we don't use context:
1064 - class _LatexDocstringLinker(markup.DocstringLinker):
1065 - def translate_indexterm(self, indexterm):
1066 indexstr = re.sub(r'["!|@]', r'"\1', indexterm.to_latex(self)) 1067 return ('\\index{%s}\\textit{%s}' % (indexstr, indexstr))
1068 - def translate_identifier_xref(self, identifier, label=None):
1069 if label is None: label = markup.plaintext_to_latex(identifier) 1070 return '\\texttt{%s}' % label
1071 1072 _docstring_linker = _LatexDocstringLinker() 1073
1074 - def docstring_to_latex(self, docstring, where, indent=0, breakany=0):
1075 """ 1076 Return a latex string that renders the given docstring. This 1077 string expects to start at the beginning of a line; and ends 1078 with a newline. 1079 """ 1080 if docstring is None: return '' 1081 s = docstring.to_latex(self._docstring_linker, indent=indent+2, 1082 directory=self._directory, 1083 docindex=self.docindex, 1084 context=where, 1085 hyperref=self._hyperref) 1086 return (' '*indent + '\\begin{EpydocDescription}\n' + 1087 ' '*indent + ' ' + s.strip() + '%\n' + 1088 ' '*indent + '\\end{EpydocDescription}\n')
1089 1090 #//////////////////////////////////////////////////////////// 1091 #{ Helpers 1092 #//////////////////////////////////////////////////////////// 1093
1094 - def write_header(self, out, where):
1095 out('%\n% API Documentation') 1096 if self._prj_name: out(' for %s' % self._prj_name) 1097 if isinstance(where, APIDoc): 1098 out('\n%% %s %s' % (self.doc_kind(where), where.canonical_name)) 1099 else: 1100 out('\n%% %s' % where) 1101 out('\n%%\n%% Generated by epydoc %s\n' % epydoc.__version__) 1102 out('%% [%s]\n%%\n' % time.asctime(time.localtime(time.time())))
1103
1104 - def start_of(self, section_name, doc=None):
1105 return ('\n' + 75*'%' + '\n' + 1106 '%%' + section_name.center(71) + '%%\n' + 1107 75*'%' + '\n\n')
1108
1109 - def section(self, title, depth=0, ref=None):
1110 sec = self.SECTIONS[depth+self._top_section] 1111 text = (sec % title) + '%\n' 1112 if ref: 1113 text += _hypertarget(ref, "") + '%\n' 1114 return text
1115 1116 # [xx] not used:
1117 - def sectionstar(self, title, depth, ref=None):
1118 sec = self.STARSECTIONS[depth+self._top_section] 1119 text = (sec % title) + '%\n' 1120 if ref: 1121 text += _hypertarget(ref, "") + '%\n' 1122 return text
1123
1124 - def doc_kind(self, doc):
1125 if isinstance(doc, ModuleDoc) and doc.is_package == True: 1126 return 'Package' 1127 elif (isinstance(doc, ModuleDoc) and 1128 doc.canonical_name[0].startswith('script')): 1129 return 'Script' 1130 elif isinstance(doc, ModuleDoc): 1131 return 'Module' 1132 elif isinstance(doc, ClassDoc): 1133 return 'Class' 1134 elif isinstance(doc, ClassMethodDoc): 1135 return 'Class Method' 1136 elif isinstance(doc, StaticMethodDoc): 1137 return 'Static Method' 1138 elif isinstance(doc, RoutineDoc): 1139 if isinstance(self.docindex.container(doc), ClassDoc): 1140 return 'Method' 1141 else: 1142 return 'Function' 1143 else: 1144 return 'Variable'
1145 1146 # [xx] list modules, classes, and functions as top-level index 1147 # items. Methods are listed under their classes. Nested classes 1148 # are listed under their classes.
1149 - def indexterm(self, doc, pos='only', indent=0):
1150 """Return a latex string that marks the given term or section 1151 for inclusion in the index. This string ends with a newline.""" 1152 if not self._index: return '' 1153 if isinstance(doc, RoutineDoc) and not self._index_functions: 1154 return '' 1155 1156 pieces = [] 1157 kinds = [] 1158 while True: 1159 if doc.canonical_name in (None, UNKNOWN): return '' # Give up. 1160 pieces.append(doc.canonical_name[-1]) 1161 kinds.append(self.doc_kind(doc).lower()) 1162 doc = self.docindex.container(doc) 1163 if isinstance(doc, ModuleDoc): break 1164 if doc is None: break 1165 if doc == UNKNOWN: return '' # give up. 1166 1167 pieces.reverse() 1168 kinds.reverse() 1169 for i in range(1, len(pieces)): 1170 if not kinds[i].endswith('method'): 1171 pieces[i] = '%s.%s' % (pieces[i-1], pieces[i]) 1172 pieces = ['\\EpydocIndex{%s}{%s}{%s}' % 1173 (_dotted(piece.lower()), _dotted(piece), kind) 1174 for (piece, kind) in zip (pieces, kinds)] 1175 1176 if pos == 'only': modifier = '' 1177 elif pos == 'start': modifier = '|(' 1178 elif pos == 'end': modifier = '|)' 1179 else: raise AssertionError('Bad index position %s' % pos) 1180 1181 return '%s\\index{%s%s}%%\n' % (' '*indent, '!'.join(pieces), modifier)
1182 1183 #: Map the Python encoding representation into mismatching LaTeX ones. 1184 latex_encodings = { 1185 'utf-8': 'utf8x', 1186 } 1187
1188 - def get_latex_encoding(self):
1189 """ 1190 @return: The LaTeX representation of the selected encoding. 1191 @rtype: C{str} 1192 """ 1193 enc = self._encoding.lower() 1194 return self.latex_encodings.get(enc, enc)
1195
1196 - def crossref(self, doc, indent=0):
1197 if (self._show_crossrefs and 1198 ((isinstance(doc, ModuleDoc) and doc in self.module_set) or 1199 (isinstance(doc, ClassDoc) and doc in self.class_set))): 1200 return '%s\\CrossRef{%s}%%\n' % (' '*indent, _label(doc),) 1201 else: 1202 return ''
1203
1204 - def _filter_deprecated(self, docs):
1205 if self._show_deprecated: 1206 return docs 1207 else: 1208 return [d for d in docs if not self._is_deprecated(d)]
1209
1210 - def _is_deprecated(self, doc):
1211 if doc in (None, UNKNOWN): return False 1212 if doc.metadata not in (None, UNKNOWN): 1213 for (field, arg, descr) in doc.metadata: 1214 if 'deprecated' in field.tags: 1215 return True 1216 if isinstance(doc, VariableDoc): 1217 return self._is_deprecated(doc.value) 1218 return False
1219 1220
1221 -def _label(doc):
1222 # Convert to a string & replace . and _. 1223 s = '%s' % doc.canonical_name 1224 s = s.replace('.', ':').replace('_','-') 1225 1226 # Get rid of any other characters. This is necessary only if 1227 # we're processing a script (whose name can be just about 1228 # anything), an unreachable value, or an object whose name has 1229 # been mangled with black magic. 1230 s = re.sub('[^\w:-]', '-', s) 1231 1232 return s
1233 1234 # [xx] this should get used more often than it does, I think: 1237
1238 -def _hypertarget(uid, sig):
1239 return '\\EpydocHypertarget{%s}{%s}' % (_label(uid), _dotted(sig))
1240
1241 -def _dotted(name):
1242 if not name: return '' 1243 return '\\EpydocDottedName{%s}' % plaintext_to_latex('%s' % name)
1244 1245 LATEX_WARNING_RE = re.compile('|'.join([ 1246 r'(?P<file>\([\.a-zA-Z_\-/\\0-9]+[.\n][a-z]{2,3}\b)', 1247 (r'(?P<pkgwarn>^(Package|Latex) (?P<pkgname>[\w-]+) '+ 1248 r'Warning:[^\n]*\n(\((?P=pkgname)\)[^\n]*\n)*)'), 1249 r'(?P<overfull>^(Overfull|Underfull)[^\n]*\n[^\n]*)', 1250 r'(?P<latexwarn>^LaTeX\s+Warning:\s+[^\n]*)', 1251 r'(?P<otherwarn>^[^\n]*Warning:[^\n]*)', 1252 r'(?P<paren>[()])', 1253 r'(?P<pageno>\[\d+({[^\}]+})?\])']), 1254 re.MULTILINE+re.IGNORECASE) 1255 1256 OVERFULL_RE = re.compile( 1257 r'(?P<typ>Underfull|Overfull)\s+\\(?P<boxtype>[vh]box)\s+' 1258 r'\((?P<size>\d+)[^\n\)]+\)[^\n]+\s+lines\s+' 1259 r'(?P<start>\d+)') 1260 1261 IGNORE_WARNING_REGEXPS = [ 1262 re.compile(r'LaTeX\s+Font\s+Warning:\s+.*\n\(Font\)\s*using.*instead'), 1263 re.compile(r'LaTeX\s+Font\s+Warning:\s+Some\s+font\s+shapes\s+' 1264 r'were\s+not\s+available,\s+defaults\s+substituted.'), 1265 ] 1266
1267 -def find_latex_error(s):
1268 filestack = ['latex'] 1269 pageno = 1 1270 for m in LATEX_WARNING_RE.finditer(s): 1271 if m.group('file'): 1272 filename = ''.join(m.group('file')[1:].split()) 1273 filename = re.sub(r'^\./', '', filename) 1274 if filename == 'api.toc': filename = 'Table of contents (api.toc)' 1275 if filename == 'api.ind': filename = 'Index (api.ind)' 1276 filestack.append(filename) 1277 elif m.group('pageno'): 1278 if pageno == int(m.group()[1:-1].split('{')[0]): 1279 pageno += 1 1280 elif m.group('overfull'): pass 1281 elif m.group('latexwarn'): pass 1282 elif m.group('pkgwarn'): pass 1283 else: 1284 # Update to account for parens. 1285 n = m.group().count('(') - m.group().count(')') 1286 if n > 0: filestack += [None] * n 1287 if n < 0: del filestack[n:] 1288 # Don't let filestack become empty: 1289 if not filestack: filestack.append('latex') 1290 1291 return filestack[-1], pageno
1292
1293 -def show_latex_warnings(s):
1294 s = re.sub('(.{79,79})\n', r'\1', s) 1295 1296 #[xx] we should probably pay special attention to overfull \vboxes. 1297 overfull = underfull = 0 1298 filestack = ['latex'] 1299 block = None 1300 BLOCK = 'LaTeX Warnings: %s' 1301 pageno = 1 1302 for m in LATEX_WARNING_RE.finditer(s): 1303 #log.debug(m.group()) 1304 # Check if it's something we don't care about. 1305 for regexp in IGNORE_WARNING_REGEXPS: 1306 if regexp.match(m.group()): 1307 m = None; break 1308 if m is None: continue 1309 # LaTeX started reading a new file: 1310 if m.group('file'): 1311 filename = ''.join(m.group('file')[1:].split()) 1312 filename = re.sub(r'^\./', '', filename) 1313 if filename == 'api.toc': filename = 'Table of contents (api.toc)' 1314 if filename == 'api.ind': filename = 'Index (api.ind)' 1315 filestack.append(filename) 1316 if block is not None: epydoc.log.end_block() 1317 epydoc.log.start_block(BLOCK % filename) 1318 block = filename 1319 # LaTeX started writing a new page 1320 elif m.group('pageno'): 1321 if pageno == int(m.group()[1:-1].split('{')[0]): 1322 pageno += 1 1323 # LateX reported an overfull/underfull warning: 1324 elif m.group('overfull'): 1325 msg = m.group('overfull').strip().split('\n')[0] 1326 msg = re.sub(r'(\d+)\.\d+', r'\1', msg) 1327 msg = re.sub(r'(lines \d+)--(\d+)', r'\1-\2', msg) 1328 if msg.lower().startswith('overfull'): overfull += 1 1329 else: underfull += 1 1330 log.warning(msg)#+' (page %d)' % pageno) 1331 # m2 = OVERFULL_RE.match(msg) 1332 # if m2: 1333 # if m2.group('boxtype') == 'vbox': 1334 # log.warning(msg) 1335 # elif (m2.group('typ').lower()=='overfull' and 1336 # int(m2.group('size')) > 50): 1337 # log.warning(msg) 1338 # else: 1339 # log.debug(msg) 1340 # else: 1341 # log.debug(msg) 1342 # Latex reported a warning: 1343 elif m.group('latexwarn'): 1344 log.warning(m.group('latexwarn').strip()+' (page %d)' % pageno) 1345 # A package reported a warning: 1346 elif m.group('pkgwarn'): 1347 log.warning(m.group('pkgwarn').strip()) 1348 else: 1349 # Display anything else that looks like a warning: 1350 if m.group('otherwarn'): 1351 log.warning(m.group('otherwarn').strip()) 1352 # Update to account for parens. 1353 n = m.group().count('(') - m.group().count(')') 1354 if n > 0: filestack += [None] * n 1355 if n < 0: del filestack[n:] 1356 # Don't let filestack become empty: 1357 if not filestack: filestack.append('latex') 1358 if (filestack[-1] is not None and 1359 block is not None and block != filestack[-1]): 1360 epydoc.log.end_block() 1361 epydoc.log.start_block(BLOCK % filestack[-1]) 1362 1363 if block: 1364 epydoc.log.end_block()
1365 1366 # if overfull or underfull: 1367 # msgs = [] 1368 # if overfull == 1: msgs.append('1 overfull box') 1369 # elif overfull: msgs.append('%d overfull boxes' % overfull) 1370 # if underfull == 1: msgs.append('1 underfull box') 1371 # elif underfull: msgs.append('%d underfull boxes' % underfull) 1372 # log.warning('LaTeX reported %s' % ' and '.join(msgs)) 1373 1374 #log.register_logger(log.SimpleLogger(log.DEBUG)) 1375 #show_latex_warnings(open('/tmp/po.test').read()) 1376