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

View File

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

View File

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

View File

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