summaryrefslogtreecommitdiff
path: root/sphinx/writers/manpage.py
diff options
context:
space:
mode:
Diffstat (limited to 'sphinx/writers/manpage.py')
-rw-r--r--sphinx/writers/manpage.py273
1 files changed, 139 insertions, 134 deletions
diff --git a/sphinx/writers/manpage.py b/sphinx/writers/manpage.py
index 45a800533..8d311acbc 100644
--- a/sphinx/writers/manpage.py
+++ b/sphinx/writers/manpage.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
"""
sphinx.writers.manpage
~~~~~~~~~~~~~~~~~~~~~~
@@ -9,23 +8,27 @@
:license: BSD, see LICENSE for details.
"""
+import warnings
+from typing import Iterable, cast
+
from docutils import nodes
from docutils.writers.manpage import (
- MACRO_DEF,
Writer,
Translator as BaseTranslator
)
-import sphinx.util.docutils
from sphinx import addnodes
+from sphinx.builders import Builder
+from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.locale import admonitionlabels, _
from sphinx.util import logging
+from sphinx.util.docutils import SphinxTranslator
from sphinx.util.i18n import format_date
+from sphinx.util.nodes import NodeMatcher
if False:
# For type annotation
- from typing import Any # NOQA
- from sphinx.builders import Builder # NOQA
+ from typing import Any, Dict # NOQA
logger = logging.getLogger(__name__)
@@ -33,20 +36,20 @@ logger = logging.getLogger(__name__)
class ManualPageWriter(Writer):
def __init__(self, builder):
# type: (Builder) -> None
- Writer.__init__(self)
+ super().__init__()
self.builder = builder
def translate(self):
# type: () -> None
transform = NestedInlineTransform(self.document)
transform.apply()
- visitor = self.builder.create_translator(self.builder, self.document)
- self.visitor = visitor
+ visitor = self.builder.create_translator(self.document, self.builder)
+ self.visitor = cast(ManualPageTranslator, visitor)
self.document.walkabout(visitor)
- self.output = visitor.astext()
+ self.output = self.visitor.astext()
-class NestedInlineTransform(object):
+class NestedInlineTransform:
"""
Flatten nested inline nodes:
@@ -61,33 +64,38 @@ class NestedInlineTransform(object):
# type: (nodes.document) -> None
self.document = document
- def apply(self):
- # type: () -> None
- def is_inline(node):
- # type: (nodes.Node) -> bool
- return isinstance(node, (nodes.literal, nodes.emphasis, nodes.strong))
-
- for node in self.document.traverse(is_inline):
- if any(is_inline(subnode) for subnode in node):
+ def apply(self, **kwargs):
+ # type: (Any) -> None
+ matcher = NodeMatcher(nodes.literal, nodes.emphasis, nodes.strong)
+ for node in self.document.traverse(matcher): # type: nodes.TextElement
+ if any(matcher(subnode) for subnode in node):
pos = node.parent.index(node)
for subnode in reversed(node[1:]):
node.remove(subnode)
- if is_inline(subnode):
+ if matcher(subnode):
node.parent.insert(pos + 1, subnode)
else:
- newnode = node.__class__('', subnode, **node.attributes)
+ newnode = node.__class__('', '', subnode, **node.attributes)
node.parent.insert(pos + 1, newnode)
-class ManualPageTranslator(BaseTranslator):
+class ManualPageTranslator(SphinxTranslator, BaseTranslator):
"""
Custom translator.
"""
- def __init__(self, builder, *args, **kwds):
- # type: (Builder, Any, Any) -> None
- BaseTranslator.__init__(self, *args, **kwds)
- self.builder = builder
+ _docinfo = {} # type: Dict[str, Any]
+
+ def __init__(self, *args):
+ # type: (Any) -> None
+ if isinstance(args[0], nodes.document) and isinstance(args[1], Builder):
+ document, builder = args
+ else:
+ warnings.warn('The order of arguments for ManualPageTranslator has been changed. '
+ 'Please give "document" as 1st and "builder" as 2nd.',
+ RemovedInSphinx40Warning, stacklevel=2)
+ builder, document = args
+ super().__init__(document, builder)
self.in_productionlist = 0
@@ -95,35 +103,31 @@ class ManualPageTranslator(BaseTranslator):
self.section_level = -1
# docinfo set by man_pages config value
- self._docinfo['title'] = self.document.settings.title
- self._docinfo['subtitle'] = self.document.settings.subtitle
- if self.document.settings.authors:
+ self._docinfo['title'] = self.settings.title
+ self._docinfo['subtitle'] = self.settings.subtitle
+ if self.settings.authors:
# don't set it if no author given
- self._docinfo['author'] = self.document.settings.authors
- self._docinfo['manual_section'] = self.document.settings.section
+ self._docinfo['author'] = self.settings.authors
+ self._docinfo['manual_section'] = self.settings.section
# docinfo set by other config values
self._docinfo['title_upper'] = self._docinfo['title'].upper()
- if builder.config.today:
- self._docinfo['date'] = builder.config.today
+ if self.config.today:
+ self._docinfo['date'] = self.config.today
else:
- self._docinfo['date'] = format_date(builder.config.today_fmt or _('%b %d, %Y'),
- language=builder.config.language)
- self._docinfo['copyright'] = builder.config.copyright
- self._docinfo['version'] = builder.config.version
- self._docinfo['manual_group'] = builder.config.project
-
- # In docutils < 0.11 self.append_header() was never called
- if sphinx.util.docutils.__version_info__ < (0, 11):
- self.body.append(MACRO_DEF)
+ self._docinfo['date'] = format_date(self.config.today_fmt or _('%b %d, %Y'),
+ language=self.config.language)
+ self._docinfo['copyright'] = self.config.copyright
+ self._docinfo['version'] = self.config.version
+ self._docinfo['manual_group'] = self.config.project
# Overwrite admonition label translations with our own
for label, translation in admonitionlabels.items():
- self.language.labels[label] = self.deunicode(translation)
+ self.language.labels[label] = self.deunicode(translation) # type: ignore
# overwritten -- added quotes around all .TH arguments
def header(self):
- # type: () -> unicode
+ # type: () -> str
tmpl = (".TH \"%(title_upper)s\" \"%(manual_section)s\""
" \"%(date)s\" \"%(version)s\" \"%(manual_group)s\"\n"
".SH NAME\n"
@@ -131,177 +135,175 @@ class ManualPageTranslator(BaseTranslator):
return tmpl % self._docinfo
def visit_start_of_file(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def depart_start_of_file(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def visit_desc(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.visit_definition_list(node)
def depart_desc(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.depart_definition_list(node)
def visit_desc_signature(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.visit_definition_list_item(node)
self.visit_term(node)
def depart_desc_signature(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.depart_term(node)
def visit_desc_signature_line(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def depart_desc_signature_line(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append(' ')
def visit_desc_addname(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def depart_desc_addname(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def visit_desc_type(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def depart_desc_type(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def visit_desc_returns(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append(' -> ')
def depart_desc_returns(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def visit_desc_name(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def depart_desc_name(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def visit_desc_parameterlist(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('(')
self.first_param = 1
def depart_desc_parameterlist(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append(')')
def visit_desc_parameter(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
if not self.first_param:
self.body.append(', ')
else:
self.first_param = 0
def depart_desc_parameter(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def visit_desc_optional(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('[')
def depart_desc_optional(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append(']')
def visit_desc_annotation(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def depart_desc_annotation(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def visit_desc_content(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.visit_definition(node)
def depart_desc_content(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.depart_definition(node)
def visit_versionmodified(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.visit_paragraph(node)
def depart_versionmodified(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.depart_paragraph(node)
# overwritten -- don't make whole of term bold if it includes strong node
def visit_term(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
if node.traverse(nodes.strong):
self.body.append('\n')
else:
- BaseTranslator.visit_term(self, node)
+ super().visit_term(node)
# overwritten -- we don't want source comments to show up
- def visit_comment(self, node):
- # type: (nodes.Node) -> None
+ def visit_comment(self, node): # type: ignore
+ # type: (nodes.Element) -> None
raise nodes.SkipNode
# overwritten -- added ensure_eol()
def visit_footnote(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.ensure_eol()
- BaseTranslator.visit_footnote(self, node)
+ super().visit_footnote(node)
# overwritten -- handle footnotes rubric
def visit_rubric(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.ensure_eol()
- if len(node.children) == 1:
- rubtitle = node.children[0].astext()
- if rubtitle in ('Footnotes', _('Footnotes')):
- self.body.append('.SH ' + self.deunicode(rubtitle).upper() +
- '\n')
- raise nodes.SkipNode
+ if len(node) == 1 and node.astext() in ('Footnotes', _('Footnotes')):
+ self.body.append('.SH ' + self.deunicode(node.astext()).upper() + '\n')
+ raise nodes.SkipNode
else:
self.body.append('.sp\n')
def depart_rubric(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def visit_seealso(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.visit_admonition(node, 'seealso')
def depart_seealso(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.depart_admonition(node)
def visit_productionlist(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.ensure_eol()
names = []
self.in_productionlist += 1
self.body.append('.sp\n.nf\n')
- for production in node:
+ productionlist = cast(Iterable[addnodes.production], node)
+ for production in productionlist:
names.append(production['tokenname'])
maxlen = max(len(name) for name in names)
lastname = None
- for production in node:
+ for production in productionlist:
if production['tokenname']:
lastname = production['tokenname'].ljust(maxlen)
self.body.append(self.defs['strong'][0])
@@ -317,16 +319,16 @@ class ManualPageTranslator(BaseTranslator):
raise nodes.SkipNode
def visit_production(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def depart_production(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
# overwritten -- don't emit a warning for images
def visit_image(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
if 'alt' in node.attributes:
self.body.append(_('[image: %s]') % node['alt'] + '\n')
self.body.append(_('[image]') + '\n')
@@ -334,11 +336,11 @@ class ManualPageTranslator(BaseTranslator):
# overwritten -- don't visit inner marked up nodes
def visit_reference(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append(self.defs['reference'][0])
# avoid repeating escaping code... fine since
# visit_Text calls astext() and only works on that afterwards
- self.visit_Text(node)
+ self.visit_Text(node) # type: ignore
self.body.append(self.defs['reference'][1])
uri = node.get('refuri', '')
@@ -356,115 +358,118 @@ class ManualPageTranslator(BaseTranslator):
raise nodes.SkipNode
def visit_number_reference(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
text = nodes.Text(node.get('title', '#'))
self.visit_Text(text)
raise nodes.SkipNode
def visit_centered(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.ensure_eol()
self.body.append('.sp\n.ce\n')
def depart_centered(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.body.append('\n.ce 0\n')
def visit_compact_paragraph(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def depart_compact_paragraph(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def visit_download_reference(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def depart_download_reference(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def visit_toctree(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
raise nodes.SkipNode
def visit_index(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
raise nodes.SkipNode
def visit_tabular_col_spec(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
raise nodes.SkipNode
def visit_glossary(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def depart_glossary(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def visit_acks(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
+ bullet_list = cast(nodes.bullet_list, node[0])
+ list_items = cast(Iterable[nodes.list_item], bullet_list)
self.ensure_eol()
- self.body.append(', '.join(n.astext()
- for n in node.children[0].children) + '.')
+ bullet_list = cast(nodes.bullet_list, node[0])
+ list_items = cast(Iterable[nodes.list_item], bullet_list)
+ self.body.append(', '.join(n.astext() for n in list_items) + '.')
self.body.append('\n')
raise nodes.SkipNode
def visit_hlist(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.visit_bullet_list(node)
def depart_hlist(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.depart_bullet_list(node)
def visit_hlistcol(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def depart_hlistcol(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def visit_literal_emphasis(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
return self.visit_emphasis(node)
def depart_literal_emphasis(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
return self.depart_emphasis(node)
def visit_literal_strong(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
return self.visit_strong(node)
def depart_literal_strong(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
return self.depart_strong(node)
def visit_abbreviation(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def depart_abbreviation(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def visit_manpage(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
return self.visit_strong(node)
def depart_manpage(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
return self.depart_strong(node)
# overwritten: handle section titles better than in 0.6 release
def visit_title(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
if isinstance(node.parent, addnodes.seealso):
self.body.append('.IP "')
return
@@ -476,47 +481,47 @@ class ManualPageTranslator(BaseTranslator):
self.body.append('.SH %s\n' %
self.deunicode(node.astext().upper()))
raise nodes.SkipNode
- return BaseTranslator.visit_title(self, node)
+ return super().visit_title(node)
def depart_title(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
if isinstance(node.parent, addnodes.seealso):
self.body.append('"\n')
return
- return BaseTranslator.depart_title(self, node)
+ return super().depart_title(node)
def visit_raw(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
if 'manpage' in node.get('format', '').split():
self.body.append(node.astext())
raise nodes.SkipNode
def visit_meta(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
raise nodes.SkipNode
def visit_inline(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def depart_inline(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def visit_math(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def depart_math(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
pass
def visit_math_block(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.visit_centered(node)
def depart_math_block(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.Element) -> None
self.depart_centered(node)
def unknown_visit(self, node):