123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- """
- Adds a sphinx directive that can be used to automatically document a plugin.
- this::
- .. autoplugin :: nose.plugins.foo
- :plugin: Pluggy
-
- produces::
- .. automodule :: nose.plugins.foo
-
- Options
- -------
- .. cmdoption :: --foo=BAR, --fooble=BAR
- Do the foo thing to the new thing.
- Plugin
- ------
- .. autoclass :: nose.plugins.foo.Pluggy
- :members:
- Source
- ------
- .. include :: path/to/nose/plugins/foo.py
- :literal:
- """
- import os
- try:
- from docutils import nodes, utils
- from docutils.statemachine import ViewList
- from docutils.parsers.rst import directives
- except ImportError:
- pass # won't run anyway
- from nose.util import resolve_name
- from nose.plugins.base import Plugin
- from nose.plugins.manager import BuiltinPluginManager
- from nose.config import Config
- from nose.core import TestProgram
- from inspect import isclass
- def autoplugin_directive(dirname, arguments, options, content, lineno,
- content_offset, block_text, state, state_machine):
- mod_name = arguments[0]
- mod = resolve_name(mod_name)
- plug_name = options.get('plugin', None)
- if plug_name:
- obj = getattr(mod, plug_name)
- else:
- for entry in dir(mod):
- obj = getattr(mod, entry)
- if isclass(obj) and issubclass(obj, Plugin) and obj is not Plugin:
- plug_name = '%s.%s' % (mod_name, entry)
- break
-
- # mod docstring
- rst = ViewList()
- rst.append('.. automodule :: %s\n' % mod_name, '<autodoc>')
- rst.append('', '<autodoc>')
-
- # options
- rst.append('Options', '<autodoc>')
- rst.append('-------', '<autodoc>')
- rst.append('', '<autodoc>')
- plug = obj()
- opts = OptBucket()
- plug.options(opts, {})
- for opt in opts:
- rst.append(opt.options(), '<autodoc>')
- rst.append(' \n', '<autodoc>')
- rst.append(' ' + opt.help + '\n', '<autodoc>')
- rst.append('\n', '<autodoc>')
-
- # plugin class
- rst.append('Plugin', '<autodoc>')
- rst.append('------', '<autodoc>')
- rst.append('', '<autodoc>')
-
- rst.append('.. autoclass :: %s\n' % plug_name, '<autodoc>')
- rst.append(' :members:\n', '<autodoc>')
- rst.append(' :show-inheritance:\n', '<autodoc>')
- rst.append('', '<autodoc>')
-
- # source
- rst.append('Source', '<autodoc>')
- rst.append('------', '<autodoc>')
- rst.append(
- '.. include :: %s\n' % utils.relative_path(
- state_machine.document['source'],
- os.path.abspath(mod.__file__.replace('.pyc', '.py'))),
- '<autodoc>')
- rst.append(' :literal:\n', '<autodoc>')
- rst.append('', '<autodoc>')
-
- node = nodes.section()
- node.document = state.document
- surrounding_title_styles = state.memo.title_styles
- surrounding_section_level = state.memo.section_level
- state.memo.title_styles = []
- state.memo.section_level = 0
- state.nested_parse(rst, 0, node, match_titles=1)
- state.memo.title_styles = surrounding_title_styles
- state.memo.section_level = surrounding_section_level
- return node.children
- def autohelp_directive(dirname, arguments, options, content, lineno,
- content_offset, block_text, state, state_machine):
- """produces rst from nose help"""
- config = Config(parserClass=OptBucket,
- plugins=BuiltinPluginManager())
- parser = config.getParser(TestProgram.usage())
- rst = ViewList()
- for line in parser.format_help().split('\n'):
- rst.append(line, '<autodoc>')
- rst.append('Options', '<autodoc>')
- rst.append('-------', '<autodoc>')
- rst.append('', '<autodoc>')
- for opt in parser:
- rst.append(opt.options(), '<autodoc>')
- rst.append(' \n', '<autodoc>')
- rst.append(' ' + opt.help + '\n', '<autodoc>')
- rst.append('\n', '<autodoc>')
- node = nodes.section()
- node.document = state.document
- surrounding_title_styles = state.memo.title_styles
- surrounding_section_level = state.memo.section_level
- state.memo.title_styles = []
- state.memo.section_level = 0
- state.nested_parse(rst, 0, node, match_titles=1)
- state.memo.title_styles = surrounding_title_styles
- state.memo.section_level = surrounding_section_level
- return node.children
-
- class OptBucket(object):
- def __init__(self, doc=None, prog='nosetests'):
- self.opts = []
- self.doc = doc
- self.prog = prog
- def __iter__(self):
- return iter(self.opts)
- def format_help(self):
- return self.doc.replace('%prog', self.prog).replace(':\n', '::\n')
-
- def add_option(self, *arg, **kw):
- self.opts.append(Opt(*arg, **kw))
- class Opt(object):
- def __init__(self, *arg, **kw):
- self.opts = arg
- self.action = kw.pop('action', None)
- self.default = kw.pop('default', None)
- self.metavar = kw.pop('metavar', None)
- self.help = kw.pop('help', None)
- def options(self):
- buf = []
- for optstring in self.opts:
- desc = optstring
- if self.action not in ('store_true', 'store_false'):
- desc += '=%s' % self.meta(optstring)
- buf.append(desc)
- return '.. cmdoption :: ' + ', '.join(buf)
- def meta(self, optstring):
- # FIXME optparser default metavar?
- return self.metavar or 'DEFAULT'
-
- def setup(app):
- app.add_directive('autoplugin',
- autoplugin_directive, 1, (1, 0, 1),
- plugin=directives.unchanged)
- app.add_directive('autohelp', autohelp_directive, 0, (0, 0, 1))
|