#!/usr/bin/env python from commands import getstatusoutput from docutils.core import publish_string, publish_parts from docutils.nodes import SparseNodeVisitor from docutils.readers.standalone import Reader from docutils.writers import Writer import nose import os import pudge.browser import re import sys import textwrap import time # constants success = 0 div = '\n----\n' warning = """ *Do not edit above this line. Content above this line is automatically generated and edits above this line will be discarded.* = Comments = """ def ucfirst(s): return s[0].upper() + s[1:].lower() def words(s): return s.split(' ') def wiki_word(node): print "Unknown ref %s" % node.astext() node['refuri'] = ''.join(map(ucfirst, words(node.astext()))) del node['refname'] node.resolved = True return True wiki_word.priority = 100 class WWReader(Reader): unknown_reference_resolvers = (wiki_word,) class WikiWriter(Writer): def translate(self): visitor = WikiVisitor(self.document) self.document.walkabout(visitor) self.output = visitor.astext() class WikiVisitor(SparseNodeVisitor): def __init__(self, document): SparseNodeVisitor.__init__(self, document) self.list_depth = 0 self.list_item_prefix = None self.indent = self.old_indent = '' self.output = [] self.preformat = False def astext(self): return ''.join(self.output) def visit_Text(self, node): #print "Text", node data = node.astext() if not self.preformat: data = data.lstrip('\n\r') data = data.replace('\r', '') data = data.replace('\n', ' ') self.output.append(data) def visit_bullet_list(self, node): self.list_depth += 1 self.list_item_prefix = (' ' * self.list_depth) + '* ' def depart_bullet_list(self, node): self.list_depth -= 1 if self.list_depth == 0: self.list_item_prefix = None else: (' ' * self.list_depth) + '* ' self.output.append('\n\n') def visit_list_item(self, node): self.old_indent = self.indent self.indent = self.list_item_prefix def depart_list_item(self, node): self.indent = self.old_indent def visit_literal_block(self, node): self.output.extend(['{{{', '\n']) self.preformat = True def depart_literal_block(self, node): self.output.extend(['\n', '}}}', '\n\n']) self.preformat = False def visit_paragraph(self, node): self.output.append(self.indent) def depart_paragraph(self, node): self.output.append('\n\n') if self.indent == self.list_item_prefix: # we're in a sub paragraph of a list item self.indent = ' ' * self.list_depth def visit_reference(self, node): if node.has_key('refuri'): href = node['refuri'] elif node.has_key('refid'): href = '#' + node['refid'] else: href = None self.output.append('[' + href + ' ') def depart_reference(self, node): self.output.append(']') def visit_subtitle(self, node): self.output.append('=== ') def depart_subtitle(self, node): self.output.append(' ===\n\n') self.list_depth = 0 self.indent = '' def visit_title(self, node): self.output.append('== ') def depart_title(self, node): self.output.append(' ==\n\n') self.list_depth = 0 self.indent = '' def visit_title_reference(self, node): self.output.append("`") def depart_title_reference(self, node): self.output.append("`") def visit_emphasis(self, node): self.output.append('*') def depart_emphasis(self, node): self.output.append('*') def visit_literal(self, node): self.output.append('`') def depart_literal(self, node): self.output.append('`') def runcmd(cmd): print cmd (status,output) = getstatusoutput(cmd) if status != success: raise Exception(output) def section(doc, name): m = re.search(r'(%s\n%s.*?)\n[^\n-]{3,}\n-{3,}\n' % (name, '-' * len(name)), doc, re.DOTALL) if m: return m.groups()[0] raise Exception('Section %s not found' % name) def wikirst(doc): return publish_string(doc, reader=WWReader(), writer=WikiWriter()) def plugin_interface(): """use pudge browser to generate interface docs from nose.plugins.base.PluginInterface """ b = pudge.browser.Browser(['nose.plugins.base'], None) m = b.modules()[0] intf = list([ c for c in m.classes() if c.name == 'IPluginInterface'])[0] doc = wikirst(intf.doc()) methods = [ m for m in intf.routines() if not m.name.startswith('_') ] methods.sort(lambda a, b: cmp(a.name, b.name)) mdoc = [] for m in methods: mdoc.append('*%s%s*\n\n' % (m.name, m.formatargs())) mdoc.append(' ' + m.doc().replace('\n', '\n ')) mdoc.append('\n\n') doc = doc + ''.join(mdoc) return doc def example_plugin(): # FIXME dump whole example plugin code from setup.py and plug.py # into python source sections root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) exp = os.path.join(root, 'examples', 'plugin') setup = file(os.path.join(exp, 'setup.py'), 'r').read() plug = file(os.path.join(exp, 'plug.py'), 'r').read() wik = "*%s:*\n{{{\n%s\n}}}\n" return wik % ('setup.py', setup) + wik % ('plug.py', plug) def tools(): top = wikirst(nose.tools.__doc__) b = pudge.browser.Browser(['nose.tools'], None) m = b.modules()[0] funcs = [ (f.name, f.formatargs().replace('(self, ', '('), f.doc()) for f in m.routines() ] funcs.sort() mdoc = [top, '\n\n'] for name, args, doc in funcs: mdoc.append("*%s%s*\n\n" % (name, args)) mdoc.append(' ' + doc.replace('\n', '\n ')) mdoc.append('\n\n') return ''.join(mdoc) def usage(): doc = nose.core.TestProgram.__doc__.replace("\\", "\\\\") parser = nose.core.get_parser(env={}, builtin_only=True, doc=doc) out = '{{{\n' + \ parser.format_help().replace('mkwiki.py', 'nosetests') + \ '\n}}}\n' return out def mkwiki(path): # # Pages to publish and the docstring(s) to load for that page # pages = { #'SandBox': wikirst(section(nose.__doc__, 'Writing tests')) 'WritingTests': wikirst(section(nose.__doc__, 'Writing tests')), 'NoseFeatures': wikirst(section(nose.__doc__, 'Features')), 'WritingPlugins': wikirst(nose.plugins.__doc__), 'PluginInterface': plugin_interface(), 'TestingTools': tools(), 'FindingAndRunningTests': wikirst( section(nose.__doc__, 'Finding and running tests')), # FIXME finish example plugin doc... add some explanation 'ExamplePlugin': example_plugin(), 'NosetestsUsage': usage(), } current = os.getcwd() w = Wiki(path) for page, doc in pages.items(): print "====== %s ======" % page w.update_docs(page, doc) print "====== %s ======" % page os.chdir(current) class Wiki(object): doc_re = re.compile(r'(.*?)' + div, re.DOTALL) def __init__(self, path): self.path = path self.newpages = [] os.chdir(path) runcmd('svn up') def filename(self, page): if not page.endswith('.wiki'): page = page + '.wiki' return page def get_page(self, page): headers = [] content = [] try: fh = file(self.filename(page), 'r') in_header = True for line in fh: if in_header: if line.startswith('#'): headers.append(line) else: in_header = False content.append(line) else: content.append(line) fh.close() return (headers, ''.join(content)) except IOError: self.newpages.append(page) return '' def set_docs(self, page, headers, page_src, docs): wikified = docs + div if not page_src: new_src = wikified + warning print "! Adding new page" else: m = self.doc_re.search(page_src) if m: print "! Updating doc section" new_src = self.doc_re.sub(wikified, page_src, 1) else: print "! Adding new doc section" new_src = wikified + page_src if new_src == page_src: print "! No changes" return # Restore any headers (lines marked by # at start of file) if headers: new_src = ''.join(headers) + '\n' + new_src fh = file(self.filename(page), 'w') fh.write(new_src) fh.close() def update_docs(self, page, doc): headers, current = self.get_page(page) self.set_docs(page, headers, current, doc) if page in self.newpages: runcmd('svn add %s' % self.filename(page)) def main(): path = os.path.abspath( os.path.join(os.path.dirname(__file__), '..', '..', '..', 'wiki')) mkwiki(path) if __name__ == '__main__': main()