PageRenderTime 29ms CodeModel.GetById 2ms app.highlight 23ms RepoModel.GetById 1ms app.codeStats 0ms

/scripts/mkwiki.py

https://bitbucket.org/jpellerin/nose/
Python | 379 lines | 374 code | 3 blank | 2 comment | 1 complexity | 137ef7316edc817324055f74175b76dc MD5 | raw file
  1#!/usr/bin/env python
  2
  3from commands import getstatusoutput
  4from docutils.core import publish_string, publish_parts
  5from docutils.nodes import SparseNodeVisitor
  6from docutils.readers.standalone import Reader
  7from docutils.writers import Writer
  8from nose.config import Config
  9import nose.plugins
 10from nose.plugins.manager import BuiltinPluginManager
 11from nose.plugins import errorclass
 12import nose
 13import os
 14import pudge.browser
 15import re
 16import sys
 17import textwrap
 18import time
 19
 20sys.path.insert(0, os.path.abspath(os.path.dirname(__file__)))
 21from mkdocs import formatargspec
 22
 23# constants
 24success = 0
 25div = '\n----\n'
 26warning = """
 27*Do not edit above this line. Content above this line is automatically
 28generated and edits above this line will be discarded.*
 29
 30= Comments =
 31"""
 32wiki_word_re = re.compile(r'^[A-Z][a-z]+(?:[A-Z][a-z]+)+')
 33
 34def ucfirst(s):
 35    return s[0].upper() + s[1:].lower()
 36
 37def words(s):
 38    return s.split(' ')
 39
 40
 41def is_wiki_word(text):
 42    return wiki_word_re.match(text)
 43
 44        
 45def wiki_word(node):
 46    orig = text = node.astext()
 47    # handle module/plugin links -- link to code
 48    if is_wiki_word(text):
 49        node['refuri'] = text
 50    else:
 51        if '.' in text:
 52            parts = text.split('.')
 53            link = 'http://python-nose.googlecode.com/svn/trunk'
 54            for p in parts:
 55                # stop at class names
 56                if p[0].upper() == p[0]:
 57                    break
 58                link += '/' + p        
 59            node['refuri'] = link
 60            return True
 61        node['refuri'] = ''.join(map(ucfirst, words(text)))
 62    print "Unknown ref %s -> %s" % (orig, node['refuri'])
 63    del node['refname']
 64    node.resolved = True
 65    return True
 66wiki_word.priority = 100
 67
 68class WWReader(Reader):
 69    unknown_reference_resolvers = (wiki_word,)
 70
 71    
 72class WikiWriter(Writer):
 73    def translate(self):
 74        visitor = WikiVisitor(self.document)
 75        self.document.walkabout(visitor)
 76        self.output = visitor.astext()
 77
 78        
 79class WikiVisitor(SparseNodeVisitor):
 80    def __init__(self, document):
 81        SparseNodeVisitor.__init__(self, document)
 82        self.list_depth = 0
 83        self.list_item_prefix = None
 84        self.indent = self.old_indent = ''
 85        self.output = []
 86        self.preformat = False
 87        
 88    def astext(self):
 89        return ''.join(self.output)
 90
 91    def visit_Text(self, node):
 92        #print "Text", node
 93        data = node.astext()
 94        if not self.preformat:
 95            data = data.lstrip('\n\r')
 96            data = data.replace('\r', '')
 97            data = data.replace('\n', ' ')
 98        self.output.append(data)
 99    
100    def visit_bullet_list(self, node):
101        self.list_depth += 1
102        self.list_item_prefix = (' ' * self.list_depth) + '* '
103
104    def depart_bullet_list(self, node):
105        self.list_depth -= 1
106        if self.list_depth == 0:
107            self.list_item_prefix = None
108        else:
109            (' ' * self.list_depth) + '* '
110        self.output.append('\n\n')
111                           
112    def visit_list_item(self, node):
113        self.old_indent = self.indent
114        self.indent = self.list_item_prefix
115
116    def depart_list_item(self, node):
117        self.indent = self.old_indent
118        
119    def visit_literal_block(self, node):
120        self.output.extend(['{{{', '\n'])
121        self.preformat = True
122
123    def depart_literal_block(self, node):
124        self.output.extend(['\n', '}}}', '\n\n'])
125        self.preformat = False
126
127    def visit_doctest_block(self, node):
128        self.output.extend(['{{{', '\n'])
129        self.preformat = True
130
131    def depart_doctest_block(self, node):
132        self.output.extend(['\n', '}}}', '\n\n'])
133        self.preformat = False
134        
135    def visit_paragraph(self, node):
136        self.output.append(self.indent)
137        
138    def depart_paragraph(self, node):
139        self.output.append('\n\n')
140        if self.indent == self.list_item_prefix:
141            # we're in a sub paragraph of a list item
142            self.indent = ' ' * self.list_depth
143        
144    def visit_reference(self, node):
145        if node.has_key('refuri'):
146            href = node['refuri']
147        elif node.has_key('refid'):
148            href = '#' + node['refid']
149        else:
150            href = None
151        self.output.append('[' + href + ' ')
152
153    def depart_reference(self, node):
154        self.output.append(']')
155
156    def visit_subtitle(self, node):
157        self.output.append('=== ')
158
159    def depart_subtitle(self, node):
160        self.output.append(' ===\n\n')
161        self.list_depth = 0
162        self.indent = ''
163        
164    def visit_title(self, node):
165        self.output.append('== ')
166
167    def depart_title(self, node):
168        self.output.append(' ==\n\n')
169        self.list_depth = 0
170        self.indent = ''
171        
172    def visit_title_reference(self, node):
173        self.output.append("`")
174
175    def depart_title_reference(self, node):
176        self.output.append("`")
177
178    def visit_emphasis(self, node):
179        self.output.append('*')
180
181    def depart_emphasis(self, node):
182        self.output.append('*')
183        
184    def visit_literal(self, node):
185        self.output.append('`')
186        
187    def depart_literal(self, node):
188        self.output.append('`')
189
190
191def runcmd(cmd):
192    print cmd
193    (status,output) = getstatusoutput(cmd)
194    if status != success:
195        raise Exception(output)
196
197
198def section(doc, name):
199    m = re.search(r'(%s\n%s.*?)\n[^\n-]{3,}\n-{3,}\n' %
200                  (name, '-' * len(name)), doc, re.DOTALL)
201    if m:
202        return m.groups()[0]
203    raise Exception('Section %s not found' % name)
204
205
206def wikirst(doc):
207    return publish_string(doc, reader=WWReader(), writer=WikiWriter())
208
209
210def plugin_interface():
211    """use pudge browser to generate interface docs
212    from nose.plugins.base.PluginInterface
213    """
214    b = pudge.browser.Browser(['nose.plugins.base'], None)
215    m = b.modules()[0]
216    intf = list([ c for c in m.classes() if c.name ==
217                  'IPluginInterface'])[0]
218    doc = wikirst(intf.doc())
219    methods = [ m for m in intf.routines() if not m.name.startswith('_') ]
220    methods.sort(lambda a, b: cmp(a.name, b.name))
221    mdoc = []
222    for m in methods:
223        # FIXME fix the arg list so literal os.environ is not in there
224        mdoc.append('*%s%s*\n\n' %  (m.name, formatargspec(m.obj)))
225        # FIXME this is resulting in poorly formatted doc sections
226        mdoc.append(' ' + m.doc().replace('\n', '\n '))
227        mdoc.append('\n\n')
228    doc = doc + ''.join(mdoc)
229    return doc
230
231
232def example_plugin():
233    # FIXME dump whole example plugin code from setup.py and plug.py
234    # into python source sections
235    root = os.path.abspath(os.path.join(os.path.dirname(__file__),
236                                        '..'))
237    exp = os.path.join(root, 'examples', 'plugin')
238    setup = file(os.path.join(exp, 'setup.py'), 'r').read()
239    plug = file(os.path.join(exp, 'plug.py'), 'r').read()
240
241    wik = "*%s:*\n{{{\n%s\n}}}\n"
242    return wik % ('setup.py', setup) + wik % ('plug.py', plug)
243
244
245def tools():
246    top = wikirst(nose.tools.__doc__)
247    b = pudge.browser.Browser(['nose.tools'], None)
248    m = b.modules()[0]
249    funcs = [ (f.name, f.formatargs().replace('(self, ', '('), f.doc())
250              for f in m.routines() ]
251    funcs.sort()
252    mdoc = [top, '\n\n']
253    for name, args, doc in funcs:
254        mdoc.append("*%s%s*\n\n" % (name, args))
255        mdoc.append(' ' + doc.replace('\n', '\n '))
256        mdoc.append('\n\n')
257    return ''.join(mdoc)
258
259
260def usage():
261    conf = Config(plugins=BuiltinPluginManager())
262    usage_text = conf.help(nose.main.__doc__).replace('mkwiki.py', 'nosetests')
263    out = '{{{\n%s\n}}}\n' % usage_text
264    return out
265
266
267def mkwiki(path):
268    #
269    # Pages to publish and the docstring(s) to load for that page
270    #
271
272    pages = {  #'SandBox': wikirst(section(nose.__doc__, 'Writing tests'))
273        'WritingTests': wikirst(section(nose.__doc__, 'Writing tests')),
274        'NoseFeatures': wikirst(section(nose.__doc__, 'Features')),
275        'WritingPlugins': wikirst(nose.plugins.__doc__),
276        'PluginInterface': plugin_interface(),
277        'ErrorClassPlugin': wikirst(errorclass.__doc__),
278        'TestingTools': tools(),
279        'FindingAndRunningTests': wikirst(
280            section(nose.__doc__, 'Finding and running tests')),
281        # FIXME finish example plugin doc... add some explanation
282        'ExamplePlugin': example_plugin(),
283        
284        'NosetestsUsage': usage(),
285        }
286
287    current = os.getcwd()
288    w = Wiki(path)
289    for page, doc in pages.items():
290        print "====== %s ======" % page
291        w.update_docs(page, doc)
292        print "====== %s ======" % page
293    os.chdir(current)
294
295
296class Wiki(object):
297    doc_re = re.compile(r'(.*?)' + div, re.DOTALL)
298    
299    def __init__(self, path):
300        self.path = path
301        self.newpages = []
302        os.chdir(path)
303        runcmd('svn up')
304
305    def filename(self, page):
306        if not page.endswith('.wiki'):
307            page = page + '.wiki'
308        return page
309        
310    def get_page(self, page):
311        headers = []
312        content = []
313
314        try:
315            fh = file(self.filename(page), 'r')
316            in_header = True
317            for line in fh:
318                if in_header:
319                    if line.startswith('#'):
320                        headers.append(line)
321                    else:
322                        in_header = False
323                        content.append(line)
324                else:
325                    content.append(line)
326            fh.close()            
327            return (headers, ''.join(content))
328        except IOError:
329            self.newpages.append(page)
330            return ('', '')
331
332    def set_docs(self, page, headers, page_src, docs):
333        wikified = docs + div
334        if not page_src:
335            new_src = wikified + warning
336            print "! Adding new page"
337        else:
338            m = self.doc_re.search(page_src)
339            if m:
340                print "! Updating doc section"
341                new_src = self.doc_re.sub(wikified, page_src, 1)
342            else:
343                print "! Adding new doc section"
344                new_src = wikified + page_src
345        if new_src == page_src:
346            print "! No changes"
347            return        
348        # Restore any headers (lines marked by # at start of file)
349        if headers:
350            new_src = ''.join(headers) + '\n' + new_src
351        fh = file(self.filename(page), 'w')
352        fh.write(new_src)
353        fh.close()
354        
355    def update_docs(self, page, doc):
356        headers, current = self.get_page(page)
357        self.set_docs(page, headers, current, doc)
358        if page in self.newpages:
359            runcmd('svn add %s' % self.filename(page))
360
361            
362def findwiki(root):
363    if not root or root is '/': # not likely to work on windows
364        raise ValueError("wiki path not found")
365    if not os.path.isdir(root):
366        return findwiki(os.path.dirname(root))
367    entries = os.listdir(root)
368    if 'wiki' in entries:
369        return os.path.join(root, 'wiki')
370    return findwiki(os.path.dirname(root))
371
372
373def main():
374    path = findwiki(os.path.abspath(__file__))
375    mkwiki(path)
376
377    
378if __name__ == '__main__':
379    main()