Add SphinxDirective as a helper

This commit is contained in:
Takeshi KOMIYA
2018-03-28 22:09:38 +09:00
parent 39ce9d9ab9
commit 5ff7bc6dc6
5 changed files with 67 additions and 51 deletions

View File

@@ -16,6 +16,7 @@ from docutils.parsers.rst import Directive, directives, roles
from sphinx import addnodes
from sphinx.util.docfields import DocFieldTransformer
from sphinx.util.docutils import SphinxDirective
# import all directives sphinx provides
from sphinx.directives.code import ( # noqa
@@ -33,6 +34,7 @@ if False:
# For type annotation
from typing import Any, Dict, List # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.config import Config # NOQA
from sphinx.environment import BuildEnvironment # NOQA
@@ -41,7 +43,7 @@ nl_escape_re = re.compile(r'\\\n')
strip_backslash_re = re.compile(r'\\(.)')
class ObjectDescription(Directive):
class ObjectDescription(SphinxDirective):
"""
Directive to describe a class, function or similar object. Not used
directly, but subclassed (in domain-specific directives) to add custom
@@ -135,7 +137,6 @@ class ObjectDescription(Directive):
self.domain, self.objtype = self.name.split(':', 1)
else:
self.domain, self.objtype = '', self.name
self.env = self.state.document.settings.env # type: BuildEnvironment
self.indexnode = addnodes.index(entries=[])
node = addnodes.desc()
@@ -187,7 +188,7 @@ class ObjectDescription(Directive):
DescDirective = ObjectDescription
class DefaultRole(Directive):
class DefaultRole(SphinxDirective):
"""
Set the default interpreted text role. Overridden from docutils.
"""
@@ -212,7 +213,7 @@ class DefaultRole(Directive):
line=self.lineno)
return messages + [error]
roles._roles[''] = role
self.state.document.settings.env.temp_data['default_role'] = role_name
self.env.temp_data['default_role'] = role_name
return messages
@@ -229,7 +230,6 @@ class DefaultDomain(Directive):
def run(self):
# type: () -> List[nodes.Node]
env = self.state.document.settings.env
domain_name = self.arguments[0].lower()
# if domain_name not in env.domains:
# # try searching by label
@@ -237,7 +237,7 @@ class DefaultDomain(Directive):
# if domain.label.lower() == domain_name:
# domain_name = domain.name
# break
env.temp_data['default_domain'] = env.domains.get(domain_name)
self.env.temp_data['default_domain'] = self.env.domains.get(domain_name)
return []

View File

@@ -12,7 +12,7 @@ import sys
from difflib import unified_diff
from docutils import nodes
from docutils.parsers.rst import Directive, directives
from docutils.parsers.rst import directives
from docutils.statemachine import ViewList
from six import text_type
@@ -20,6 +20,7 @@ from sphinx import addnodes
from sphinx.locale import __
from sphinx.util import logging
from sphinx.util import parselinenos
from sphinx.util.docutils import SphinxDirective
from sphinx.util.nodes import set_source_info
if False:
@@ -31,7 +32,7 @@ if False:
logger = logging.getLogger(__name__)
class Highlight(Directive):
class Highlight(SphinxDirective):
"""
Directive to set the highlighting language for code blocks, as well
as the threshold for line numbers.
@@ -77,7 +78,7 @@ def dedent_lines(lines, dedent, location=None):
def container_wrapper(directive, literal_node, caption):
# type: (Directive, nodes.Node, unicode) -> nodes.container
# type: (SphinxDirective, nodes.Node, unicode) -> nodes.container
container_node = nodes.container('', literal_block=True,
classes=['literal-block-wrapper'])
parsed = nodes.Element()
@@ -95,7 +96,7 @@ def container_wrapper(directive, literal_node, caption):
return container_node
class CodeBlock(Directive):
class CodeBlock(SphinxDirective):
"""
Directive for a code block with special highlighting or line numbering
settings.
@@ -372,7 +373,7 @@ class LiteralIncludeReader(object):
return lines
class LiteralInclude(Directive):
class LiteralInclude(SphinxDirective):
"""
Like ``.. include:: :literal:``, but only warns if the include file is
not found, and does not raise errors. Also has several options for
@@ -412,19 +413,17 @@ class LiteralInclude(Directive):
if not document.settings.file_insertion_enabled:
return [document.reporter.warning('File insertion disabled',
line=self.lineno)]
env = document.settings.env
# convert options['diff'] to absolute path
if 'diff' in self.options:
_, path = env.relfn2path(self.options['diff'])
_, path = self.env.relfn2path(self.options['diff'])
self.options['diff'] = path
try:
location = self.state_machine.get_source_and_line(self.lineno)
rel_filename, filename = env.relfn2path(self.arguments[0])
env.note_dependency(rel_filename)
rel_filename, filename = self.env.relfn2path(self.arguments[0])
self.env.note_dependency(rel_filename)
reader = LiteralIncludeReader(filename, self.options, env.config)
reader = LiteralIncludeReader(filename, self.options, self.config)
text, lines = reader.read(location=location)
retnode = nodes.literal_block(text, text, source=filename)

View File

@@ -8,7 +8,7 @@
"""
from docutils import nodes
from docutils.parsers.rst import Directive, directives
from docutils.parsers.rst import directives
from docutils.parsers.rst.directives.admonitions import BaseAdmonition
from docutils.parsers.rst.directives.misc import Class
from docutils.parsers.rst.directives.misc import Include as BaseInclude
@@ -18,6 +18,7 @@ from sphinx import addnodes, locale
from sphinx.deprecation import DeprecatedDict, RemovedInSphinx30Warning
from sphinx.locale import _
from sphinx.util import url_re, docname_join
from sphinx.util.docutils import SphinxDirective
from sphinx.util.matching import patfilter
from sphinx.util.nodes import explicit_title_re, set_source_info, \
process_index_entry
@@ -49,7 +50,7 @@ def int_or_nothing(argument):
return int(argument)
class TocTree(Directive):
class TocTree(SphinxDirective):
"""
Directive to notify Sphinx about the hierarchical structure of the docs,
and to include a table-of-contents like tree in the current document.
@@ -72,8 +73,7 @@ class TocTree(Directive):
def run(self):
# type: () -> List[nodes.Node]
env = self.state.document.settings.env
suffixes = env.config.source_suffix
suffixes = self.config.source_suffix
glob = 'glob' in self.options
ret = []
@@ -81,17 +81,17 @@ class TocTree(Directive):
# and title may be None if the document's title is to be used
entries = [] # type: List[Tuple[unicode, unicode]]
includefiles = []
all_docnames = env.found_docs.copy()
all_docnames = self.env.found_docs.copy()
# don't add the currently visited file in catch-all patterns
all_docnames.remove(env.docname)
all_docnames.remove(self.env.docname)
for entry in self.content:
if not entry:
continue
# look for explicit titles ("Some Title <document>")
explicit = explicit_title_re.match(entry)
if glob and ('*' in entry or '?' in entry or '[' in entry) and not explicit:
patname = docname_join(env.docname, entry)
docnames = sorted(patfilter(all_docnames, patname))
patname = docname_join(self.env.docname, entry)
docnames = sorted(patfilter(all_docnames, patname)) # type: ignore
for docname in docnames:
all_docnames.remove(docname) # don't include it again
entries.append((None, docname))
@@ -114,20 +114,20 @@ class TocTree(Directive):
docname = docname[:-len(suffix)]
break
# absolutize filenames
docname = docname_join(env.docname, docname)
docname = docname_join(self.env.docname, docname)
if url_re.match(ref) or ref == 'self':
entries.append((title, ref))
elif docname not in env.found_docs:
elif docname not in self.env.found_docs:
ret.append(self.state.document.reporter.warning(
'toctree contains reference to nonexisting '
'document %r' % docname, line=self.lineno))
env.note_reread()
self.env.note_reread()
else:
all_docnames.discard(docname)
entries.append((title, docname))
includefiles.append(docname)
subnode = addnodes.toctree()
subnode['parent'] = env.docname
subnode['parent'] = self.env.docname
# entries contains all entries (self references, external links etc.)
if 'reversed' in self.options:
entries.reverse()
@@ -149,7 +149,7 @@ class TocTree(Directive):
return ret
class Author(Directive):
class Author(SphinxDirective):
"""
Directive to give the name of the author of the current document
or section. Shown in the output only if the show_authors option is on.
@@ -162,8 +162,7 @@ class Author(Directive):
def run(self):
# type: () -> List[nodes.Node]
env = self.state.document.settings.env
if not env.config.show_authors:
if not self.config.show_authors:
return []
para = nodes.paragraph(translatable=False)
emph = nodes.emphasis()
@@ -183,7 +182,7 @@ class Author(Directive):
return [para] + messages
class Index(Directive):
class Index(SphinxDirective):
"""
Directive to add entries to the index.
"""
@@ -196,8 +195,7 @@ class Index(Directive):
def run(self):
# type: () -> List[nodes.Node]
arguments = self.arguments[0].split('\n')
env = self.state.document.settings.env
targetid = 'index-%s' % env.new_serialno('index')
targetid = 'index-%s' % self.env.new_serialno('index')
targetnode = nodes.target('', '', ids=[targetid])
self.state.document.note_explicit_target(targetnode)
indexnode = addnodes.index()
@@ -209,7 +207,7 @@ class Index(Directive):
return [indexnode, targetnode]
class VersionChange(Directive):
class VersionChange(SphinxDirective):
"""
Directive to describe a change/addition/deprecation in a specific version.
"""
@@ -252,9 +250,8 @@ class VersionChange(Directive):
classes=['versionmodified']),
translatable=False)
node.append(para)
env = self.state.document.settings.env
# XXX should record node.source as well
env.note_versionchange(node['type'], node['version'], node, node.line)
self.env.note_versionchange(node['type'], node['version'], node, node.line)
return [node] + messages
@@ -265,7 +262,7 @@ class SeeAlso(BaseAdmonition):
node_class = addnodes.seealso
class TabularColumns(Directive):
class TabularColumns(SphinxDirective):
"""
Directive to give an explicit tabulary column definition to LaTeX.
"""
@@ -283,7 +280,7 @@ class TabularColumns(Directive):
return [node]
class Centered(Directive):
class Centered(SphinxDirective):
"""
Directive to create a centered line of bold text.
"""
@@ -304,7 +301,7 @@ class Centered(Directive):
return [subnode] + messages
class Acks(Directive):
class Acks(SphinxDirective):
"""
Directive for a list of names.
"""
@@ -326,7 +323,7 @@ class Acks(Directive):
return [node]
class HList(Directive):
class HList(SphinxDirective):
"""
Directive for a list that gets compacted horizontally.
"""
@@ -363,7 +360,7 @@ class HList(Directive):
return [newnode]
class Only(Directive):
class Only(SphinxDirective):
"""
Directive to only include text if the given tag(s) are enabled.
"""
@@ -421,7 +418,7 @@ class Only(Directive):
self.state.memo.section_level = surrounding_section_level
class Include(BaseInclude):
class Include(BaseInclude, SphinxDirective):
"""
Like the standard "Include" directive, but interprets absolute paths
"correctly", i.e. relative to source directory.
@@ -429,14 +426,13 @@ class Include(BaseInclude):
def run(self):
# type: () -> List[nodes.Node]
env = self.state.document.settings.env
if self.arguments[0].startswith('<') and \
self.arguments[0].endswith('>'):
# docutils "standard" includes, do not do path processing
return BaseInclude.run(self)
rel_filename, filename = env.relfn2path(self.arguments[0])
rel_filename, filename = self.env.relfn2path(self.arguments[0])
self.arguments[0] = filename
env.note_included(filename)
self.env.note_included(filename)
return BaseInclude.run(self)

View File

@@ -12,6 +12,7 @@ from docutils.parsers.rst import directives
from docutils.parsers.rst.directives import images, html, tables
from sphinx import addnodes
from sphinx.util.docutils import SphinxDirective
from sphinx.util.nodes import set_source_info
if False:
@@ -44,16 +45,15 @@ class Figure(images.Figure):
return [figure_node]
class Meta(html.Meta):
class Meta(html.Meta, SphinxDirective):
def run(self):
# type: () -> List[nodes.Node]
env = self.state.document.settings.env
result = html.Meta.run(self)
for node in result:
if (isinstance(node, nodes.pending) and
isinstance(node.details['nodes'][0], html.MetaBody.meta)):
meta = node.details['nodes'][0]
meta.source = env.doc2path(env.docname)
meta.source = self.env.doc2path(self.env.docname)
meta.line = self.lineno
meta.rawcontent = meta['content']

View File

@@ -20,7 +20,7 @@ from distutils.version import LooseVersion
import docutils
from docutils import nodes
from docutils.languages import get_language
from docutils.parsers.rst import directives, roles, convert_directive_function
from docutils.parsers.rst import Directive, directives, roles, convert_directive_function
from docutils.statemachine import StateMachine
from docutils.utils import Reporter
@@ -36,6 +36,7 @@ if False:
# For type annotation
from typing import Any, Callable, Generator, Iterator, List, Set, Tuple # NOQA
from docutils.statemachine import State, ViewList # NOQA
from sphinx.config import Config # NOQA
from sphinx.environment import BuildEnvironment # NOQA
from sphinx.io import SphinxFileInput # NOQA
@@ -273,6 +274,26 @@ def switch_source_input(state, content):
state.memo.reporter.get_source_and_line = get_source_and_line
class SphinxDirective(Directive):
"""A base class for Directives.
Compared with ``docutils.parsers.rst.Directive``, this class improves
accessibility to Sphinx APIs.
"""
@property
def env(self):
# type: () -> BuildEnvironment
"""Reference to the :class:`.BuildEnvironment` object."""
return self.state.document.settings.env
@property
def config(self):
# type: () -> Config
"""Reference to the :class:`.Config` object."""
return self.env.config
# cache a vanilla instance of nodes.document
# Used in new_document() function
__document_cache__ = None # type: nodes.document