# # epydoc -- Utility functions used by regression tests (*.doctest) # Edward Loper # # Created [01/30/01 05:18 PM] # $Id: html.py 1420 2007-01-28 14:19:30Z dvarrazzo $ # """ Utility functions used by the regression tests (C{*.doctest}). """ __docformat__ = 'epytext en' import tempfile, re, os, os.path, textwrap, sys from epydoc.docbuilder import build_doc, build_doc_index from epydoc.docparser import parse_docs from epydoc.docintrospecter import introspect_docs from epydoc.apidoc import ClassDoc, RoutineDoc from epydoc.markup import ParsedDocstring from epydoc.docwriter.html import HTMLWriter ###################################################################### #{ Test Functions ###################################################################### def buildvaluedoc(s): """ This test function takes a string containing the contents of a module. It writes the string contents to a file, imports the file as a module, and uses build_doc to build documentation, and returns it as a C{ValueDoc} object. """ tmp_dir = write_pystring_to_tmp_dir(s) val_doc = build_doc(os.path.join(tmp_dir, 'epydoc_test.py')) cleanup_tmp_dir(tmp_dir) return val_doc def runbuilder(s, attribs='', build=None, exclude=''): """ This test function takes a string containing the contents of a module. It writes the string contents to a file, imports the file as a module, and uses build_doc to build documentation, and pretty prints the resulting ModuleDoc object. The C{attribs} argument specifies which attributes of the C{APIDoc}s should be displayed. The C{build} argument gives the name of a variable in the module whose documentation should be built, instead of bilding docs for the whole module. """ # Write it to a temp file. tmp_dir = write_pystring_to_tmp_dir(s) # Build it. val_doc = build_doc(os.path.join(tmp_dir, 'epydoc_test.py')) if build: val_doc = val_doc.variables[build].value # Display it. if isinstance(val_doc, ClassDoc): for val in val_doc.variables.values(): if isinstance(val.value, RoutineDoc): fun_to_plain(val.value) s = val_doc.pp(include=attribs.split(),exclude=exclude.split()) s = re.sub(r"(filename = ).*", r"\1...", s) s = re.sub(r"(", r"\1...>", s) s = re.sub(r"(<\w+ object at )0x\w+>", r"\1...>", s) print s # Clean up. cleanup_tmp_dir(tmp_dir) def runparser(s, attribs='', show=None, exclude=''): """ This test function takes a string containing the contents of a module, and writes it to a file, uses `parse_docs` to parse it, and pretty prints the resulting ModuleDoc object. The `attribs` argument specifies which attributes of the `APIDoc`s should be displayed. The `show` argument, if specifies, gives the name of the object in the module that should be displayed (but the whole module will always be inspected; this just selects what to display). """ # Write it to a temp file. tmp_dir = write_pystring_to_tmp_dir(s) # Parse it. val_doc = parse_docs(os.path.join(tmp_dir, 'epydoc_test.py')) if show is not None: for name in show.split('.'): if isinstance(val_doc, ClassDoc): val_doc = val_doc.local_variables[name].value else: val_doc = val_doc.variables[name].value # Display it. s = val_doc.pp(include=attribs.split(), exclude=exclude.split()) s = re.sub(r"filename = .*", "filename = ...", s) print s # Clean up. cleanup_tmp_dir(tmp_dir) def runintrospecter(s, attribs='', introspect=None, exclude=''): """ This test function takes a string containing the contents of a module. It writes the string contents to a file, imports the file as a module, and uses C{introspect_docs} to introspect it, and pretty prints the resulting ModuleDoc object. The C{attribs} argument specifies which attributes of the C{APIDoc}s should be displayed. The C{introspect} argument gives the name of a variable in the module whose value should be introspected, instead of introspecting the whole module. """ # Write it to a temp file. tmp_dir = write_pystring_to_tmp_dir(s) # Import it. sys.path.insert(0, tmp_dir) if introspect is None: import epydoc_test as val else: exec("from epydoc_test import %s as val" % introspect) del sys.path[0] # Introspect it. val_doc = introspect_docs(val) # Display it. s = val_doc.pp(include=attribs.split(),exclude=exclude.split()) s = re.sub(r"(filename = ).*", r"\1...", s) s = re.sub(r"(", r"\1...>", s) s = re.sub(r"(<\w+ object at )0x\w+>", r"\1...>", s) print s # Clean up. cleanup_tmp_dir(tmp_dir) def print_warnings(): """ Register a logger that will print warnings & errors. """ from epydoc import log del log._loggers[:] log.register_logger(log.SimpleLogger(log.DOCSTRING_WARNING)) def testencoding(s, introspect=True, parse=True, debug=False): """ An end-to-end test for unicode encodings. This function takes a given string, writes it to a python file, and processes that file's documentation. It then generates HTML output from the documentation, extracts all docstrings from the generated HTML output, and displays them. (In order to extract & display all docstrings, it monkey-patches the HMTLwriter.docstring_to_html() method.)""" # Monkey-patch docstring_to_html original_docstring_to_html = HTMLWriter.docstring_to_html HTMLWriter.docstring_to_html = print_docstring_as_html # Write s to a temporary file. tmp_dir = tempfile.mkdtemp() path = os.path.join(tmp_dir, 'enc_test.py') out = open(path, 'w') out.write(textwrap.dedent(s)) out.close() # Build docs for it docindex = build_doc_index([path], introspect, parse) if docindex is None: return sys.modules.pop('enc_test', None) # Write html output. writer = HTMLWriter(docindex, mark_docstrings=True) writer.write(tmp_dir) for file in os.listdir(tmp_dir): os.unlink(os.path.join(tmp_dir,file)) os.rmdir(tmp_dir) # Restore the HTMLWriter class to its original state. HTMLWriter.docstring_to_html = original_docstring_to_html ###################################################################### #{ Helper Functions ###################################################################### def write_pystring_to_tmp_dir(s): tmp_dir = tempfile.mkdtemp() out = open(os.path.join(tmp_dir, 'epydoc_test.py'), 'w') out.write(textwrap.dedent(s)) out.close() return tmp_dir def cleanup_tmp_dir(tmp_dir): os.unlink(os.path.join(tmp_dir, 'epydoc_test.py')) try: os.unlink(os.path.join(tmp_dir, 'epydoc_test.pyc')) except OSError: pass os.rmdir(tmp_dir) sys.modules.pop('epydoc_test', None) def to_plain(docstring): """Conver a parsed docstring into plain text""" if isinstance(docstring, ParsedDocstring): docstring = docstring.to_plaintext(None) return docstring.rstrip() def fun_to_plain(val_doc): """Convert parsed docstrings in text from a RoutineDoc""" for k, v in val_doc.arg_types.items(): val_doc.arg_types[k] = to_plain(v) for i, (k, v) in enumerate(val_doc.arg_descrs): val_doc.arg_descrs[i] = (k, to_plain(v)) def print_docstring_as_html(self, parsed_docstring, *varargs, **kwargs): """ Convert the given parsed_docstring to HTML and print it. Ignore any other arguments. This function is used by L{testencoding} to monkey-patch the HTMLWriter class's docstring_to_html() method. """ s = parsed_docstring.to_html(None).strip() s = s.encode('ascii', 'xmlcharrefreplace') s = remove_surrogates(s) print s return '' def remove_surrogates(s): """ The following is a helper function, used to convert two-character surrogate sequences into single characters. This is needed because some systems create surrogates but others don't. """ pieces = re.split('(&#\d+;)', s) for i in range(3, len(pieces)-1, 2): if pieces[i-1] != '': continue high,low = int(pieces[i-2][2:-1]), int(pieces[i][2:-1]) if 0xd800 <= high <= 0xdbff and 0xdc00 <= low <= 0xdfff: pieces[i-2] = '&#%d;' % (((high&0x3ff)<<10) + (low&0x3ff) + 0x10000) pieces[i] = '' return ''.join(pieces)