Convert directives in builtin extensions to class API.

This commit is contained in:
Georg Brandl 2009-02-18 21:48:00 +01:00
parent 6bfed75113
commit 3724f9020a
4 changed files with 167 additions and 112 deletions

View File

@ -23,6 +23,7 @@ from docutils import nodes
from docutils.parsers.rst import directives
from sphinx.builders import Builder
from sphinx.util.compat import Directive
from sphinx.util.console import bold
blankline_re = re.compile(r'^\s*<BLANKLINE>', re.MULTILINE)
@ -30,63 +31,77 @@ doctestopt_re = re.compile(r'#\s*doctest:.+$', re.MULTILINE)
# set up the necessary directives
def test_directive(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
# use ordinary docutils nodes for test code: they get special attributes
# so that our builder recognizes them, and the other builders are happy.
code = '\n'.join(content)
test = None
if name == 'doctest':
if '<BLANKLINE>' in code:
# convert <BLANKLINE>s to ordinary blank lines for presentation
test = code
code = blankline_re.sub('', code)
if doctestopt_re.search(code):
if not test:
class TestDirective(Directive):
"""
Base class for doctest-related directives.
"""
has_content = True
required_arguments = 0
optional_arguments = 1
final_argument_whitespace = True
def run(self):
# use ordinary docutils nodes for test code: they get special attributes
# so that our builder recognizes them, and the other builders are happy.
code = '\n'.join(self.content)
test = None
if self.name == 'doctest':
if '<BLANKLINE>' in code:
# convert <BLANKLINE>s to ordinary blank lines for presentation
test = code
code = doctestopt_re.sub('', code)
nodetype = nodes.literal_block
if name == 'testsetup' or 'hide' in options:
nodetype = nodes.comment
if arguments:
groups = [x.strip() for x in arguments[0].split(',')]
else:
groups = ['default']
node = nodetype(code, code, testnodetype=name, groups=groups)
node.line = lineno
if test is not None:
# only save if it differs from code
node['test'] = test
if name == 'testoutput':
# don't try to highlight output
node['language'] = 'none'
node['options'] = {}
if name in ('doctest', 'testoutput') and 'options' in options:
# parse doctest-like output comparison flags
option_strings = options['options'].replace(',', ' ').split()
for option in option_strings:
if (option[0] not in '+-' or option[1:] not in
doctest.OPTIONFLAGS_BY_NAME):
# XXX warn?
continue
flag = doctest.OPTIONFLAGS_BY_NAME[option[1:]]
node['options'][flag] = (option[0] == '+')
return [node]
code = blankline_re.sub('', code)
if doctestopt_re.search(code):
if not test:
test = code
code = doctestopt_re.sub('', code)
nodetype = nodes.literal_block
if self.name == 'testsetup' or 'hide' in self.options:
nodetype = nodes.comment
if self.arguments:
groups = [x.strip() for x in self.arguments[0].split(',')]
else:
groups = ['default']
node = nodetype(code, code, testnodetype=self.name, groups=groups)
node.line = self.lineno
if test is not None:
# only save if it differs from code
node['test'] = test
if self.name == 'testoutput':
# don't try to highlight output
node['language'] = 'none'
node['options'] = {}
if self.name in ('doctest', 'testoutput') and 'options' in self.options:
# parse doctest-like output comparison flags
option_strings = self.options['options'].replace(',', ' ').split()
for option in option_strings:
if (option[0] not in '+-' or option[1:] not in
doctest.OPTIONFLAGS_BY_NAME):
# XXX warn?
continue
flag = doctest.OPTIONFLAGS_BY_NAME[option[1:]]
node['options'][flag] = (option[0] == '+')
return [node]
# need to have individual functions for each directive due to different
# options they accept
class TestsetupDirective(TestDirective):
option_spec = {}
def testsetup_directive(*args):
return test_directive(*args)
class DoctestDirective(TestDirective):
option_spec = {
'hide': directives.flag,
'options': directives.unchanged,
}
def doctest_directive(*args):
return test_directive(*args)
class TestcodeDirective(TestDirective):
option_spec = {
'hide': directives.flag,
}
def testcode_directive(*args):
return test_directive(*args)
def testoutput_directive(*args):
return test_directive(*args)
class TestoutputDirective(TestDirective):
option_spec = {
'hide': directives.flag,
'options': directives.unchanged,
}
parser = doctest.DocTestParser()
@ -334,13 +349,10 @@ Doctest summary
def setup(app):
app.add_directive('testsetup', testsetup_directive, 1, (0, 1, 1))
app.add_directive('doctest', doctest_directive, 1, (0, 1, 1),
hide=directives.flag, options=directives.unchanged)
app.add_directive('testcode', testcode_directive, 1, (0, 1, 1),
hide=directives.flag)
app.add_directive('testoutput', testoutput_directive, 1, (0, 1, 1),
hide=directives.flag, options=directives.unchanged)
app.add_directive('testsetup', TestsetupDirective)
app.add_directive('doctest', DoctestDirective)
app.add_directive('testcode', TestcodeDirective)
app.add_directive('testoutput', TestoutputDirective)
app.add_builder(DocTestBuilder)
# this config value adds to sys.path
app.add_config_value('doctest_path', [], False)

View File

@ -22,17 +22,27 @@
from docutils import nodes
from sphinx.util.compat import Directive
class ifconfig(nodes.Element): pass
def ifconfig_directive(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
node = ifconfig()
node.line = lineno
node['expr'] = arguments[0]
state.nested_parse(content, content_offset, node)
return [node]
class IfConfig(Directive):
has_content = True
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = True
option_spec = {}
def run(self):
node = ifconfig()
node.document = self.state.document
node.line = self.lineno
node['expr'] = self.arguments[0]
self.state.nested_parse(self.content, self.content_offset, node)
return [node]
def process_ifconfig_nodes(app, doctree, docname):
@ -58,5 +68,5 @@ def process_ifconfig_nodes(app, doctree, docname):
def setup(app):
app.add_node(ifconfig)
app.add_directive('ifconfig', ifconfig_directive, 1, (1, 0, 1))
app.add_directive('ifconfig', IfConfig)
app.connect('doctree-resolved', process_ifconfig_nodes)

View File

@ -12,6 +12,8 @@
from docutils import nodes, utils
from docutils.parsers.rst import directives
from sphinx.util.compat import Directive
class math(nodes.Inline, nodes.TextElement):
pass
@ -45,22 +47,33 @@ def eq_role(role, rawtext, text, lineno, inliner, options={}, content=[]):
node['docname'] = inliner.document.settings.env.docname
return [node], []
def math_directive(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
latex = '\n'.join(content)
if arguments and arguments[0]:
latex = arguments[0] + '\n\n' + latex
node = displaymath()
node['latex'] = latex
node['label'] = options.get('label', None)
node['nowrap'] = 'nowrap' in options
node['docname'] = state.document.settings.env.docname
ret = [node]
if node['label']:
tnode = nodes.target('', '', ids=['equation-' + node['label']])
state.document.note_explicit_target(tnode)
ret.insert(0, tnode)
return ret
class MathDirective(Directive):
has_content = True
required_arguments = 0
optional_arguments = 1
final_argument_whitespace = True
option_spec = {
'label': directives.unchanged,
'nowrap': directives.flag,
}
def run(self):
latex = '\n'.join(self.content)
if self.arguments and self.arguments[0]:
latex = self.arguments[0] + '\n\n' + latex
node = displaymath()
node['latex'] = latex
node['label'] = self.options.get('label', None)
node['nowrap'] = 'nowrap' in self.options
node['docname'] = self.state.document.settings.env.docname
ret = [node]
if node['label']:
tnode = nodes.target('', '', ids=['equation-' + node['label']])
self.state.document.note_explicit_target(tnode)
ret.insert(0, tnode)
return ret
def latex_visit_math(self, node):
@ -134,6 +147,5 @@ def setup(app, htmlinlinevisitors, htmldisplayvisitors):
html=(html_visit_eqref, html_depart_eqref))
app.add_role('math', math_role)
app.add_role('eq', eq_role)
app.add_directive('math', math_directive, 1, (0, 1, 1),
label=directives.unchanged, nowrap=directives.flag)
app.add_directive('math', MathDirective)
app.connect('doctree-resolved', number_equations)

View File

@ -14,42 +14,63 @@
from docutils import nodes
from sphinx.util.compat import make_admonition
from sphinx.util.compat import Directive, make_admonition
class todo_node(nodes.Admonition, nodes.Element): pass
class todolist(nodes.General, nodes.Element): pass
def todo_directive(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
env = state.document.settings.env
class Todo(Directive):
"""
A todo entry, displayed (if configured) in the form of an admonition.
"""
targetid = "todo-%s" % env.index_num
env.index_num += 1
targetnode = nodes.target('', '', ids=[targetid])
has_content = True
required_arguments = 0
optional_arguments = 0
final_argument_whitespace = False
option_spec = {}
ad = make_admonition(todo_node, name, [_('Todo')], options, content, lineno,
content_offset, block_text, state, state_machine)
def run(self):
env = self.state.document.settings.env
# Attach a list of all todos to the environment,
# the todolist works with the collected todo nodes
if not hasattr(env, 'todo_all_todos'):
env.todo_all_todos = []
env.todo_all_todos.append({
'docname': env.docname,
'lineno': lineno,
'todo': ad[0].deepcopy(),
'target': targetnode,
})
targetid = "todo-%s" % env.index_num
env.index_num += 1
targetnode = nodes.target('', '', ids=[targetid])
return [targetnode] + ad
ad = make_admonition(todo_node, self.name, [_('Todo')], self.options,
self.content, self.lineno, self.content_offset,
self.block_text, self.state, self.state_machine)
# Attach a list of all todos to the environment,
# the todolist works with the collected todo nodes
if not hasattr(env, 'todo_all_todos'):
env.todo_all_todos = []
env.todo_all_todos.append({
'docname': env.docname,
'lineno': self.lineno,
'todo': ad[0].deepcopy(),
'target': targetnode,
})
return [targetnode] + ad
def todolist_directive(name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine):
# Simply insert an empty todolist node which will be replaced later
# when process_todo_nodes is called
return [todolist('')]
class TodoList(Directive):
"""
A list of all todo entries.
"""
has_content = False
required_arguments = 0
optional_arguments = 0
final_argument_whitespace = False
option_spec = {}
def run(self):
# Simply insert an empty todolist node which will be replaced later
# when process_todo_nodes is called
return [todolist('')]
def process_todo_nodes(app, doctree, fromdocname):
@ -119,8 +140,8 @@ def setup(app):
latex=(visit_todo_node, depart_todo_node),
text=(visit_todo_node, depart_todo_node))
app.add_directive('todo', todo_directive, 1, (0, 0, 1))
app.add_directive('todolist', todolist_directive, 0, (0, 0, 0))
app.add_directive('todo', Todo)
app.add_directive('todolist', TodoList)
app.connect('doctree-resolved', process_todo_nodes)
app.connect('env-purge-doc', purge_todos)