summaryrefslogtreecommitdiff
path: root/sphinx/util/docfields.py
diff options
context:
space:
mode:
Diffstat (limited to 'sphinx/util/docfields.py')
-rw-r--r--sphinx/util/docfields.py148
1 files changed, 78 insertions, 70 deletions
diff --git a/sphinx/util/docfields.py b/sphinx/util/docfields.py
index 94968e148..063a89795 100644
--- a/sphinx/util/docfields.py
+++ b/sphinx/util/docfields.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
"""
sphinx.util.docfields
~~~~~~~~~~~~~~~~~~~~~
@@ -9,7 +8,8 @@
:copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
-from __future__ import absolute_import
+
+from typing import List, Tuple, cast
from docutils import nodes
@@ -17,18 +17,19 @@ from sphinx import addnodes
if False:
# For type annotation
- from typing import Any, Dict, List, Tuple # NOQA
+ from typing import Any, Dict, Type, Union # NOQA
from sphinx.domains import Domain # NOQA
from sphinx.environment import BuildEnvironment # NOQA
+ from sphinx.util.typing import TextlikeNode # NOQA
def _is_single_paragraph(node):
- # type: (nodes.Node) -> bool
+ # type: (nodes.field_body) -> bool
"""True if the node only contains one paragraph (and system messages)."""
if len(node) == 0:
return False
elif len(node) > 1:
- for subnode in node[1:]:
+ for subnode in node[1:]: # type: nodes.Node
if not isinstance(subnode, nodes.system_message):
return False
if isinstance(node[0], nodes.paragraph):
@@ -36,7 +37,7 @@ def _is_single_paragraph(node):
return False
-class Field(object):
+class Field:
"""A doc field that is never grouped. It can have an argument or not, the
argument can be linked using a specified *rolename*. Field should be used
for doc fields that usually don't occur more than once.
@@ -54,7 +55,7 @@ class Field(object):
def __init__(self, name, names=(), label=None, has_arg=True, rolename=None,
bodyrolename=None):
- # type: (unicode, Tuple[unicode, ...], unicode, bool, unicode, unicode) -> None
+ # type: (str, Tuple[str, ...], str, bool, str, str) -> None
self.name = name
self.names = names
self.label = label
@@ -63,10 +64,10 @@ class Field(object):
self.bodyrolename = bodyrolename
def make_xref(self,
- rolename, # type: unicode
- domain, # type: unicode
- target, # type: unicode
- innernode=addnodes.literal_emphasis, # type: nodes.Node
+ rolename, # type: str
+ domain, # type: str
+ target, # type: str
+ innernode=addnodes.literal_emphasis, # type: Type[TextlikeNode]
contnode=None, # type: nodes.Node
env=None, # type: BuildEnvironment
):
@@ -77,14 +78,14 @@ class Field(object):
reftype=rolename, reftarget=target)
refnode += contnode or innernode(target, target)
if env:
- env.domains[domain].process_field_xref(refnode)
+ env.get_domain(domain).process_field_xref(refnode)
return refnode
def make_xrefs(self,
- rolename, # type: unicode
- domain, # type: unicode
- target, # type: unicode
- innernode=addnodes.literal_emphasis, # type: nodes.Node
+ rolename, # type: str
+ domain, # type: str
+ target, # type: str
+ innernode=addnodes.literal_emphasis, # type: Type[TextlikeNode]
contnode=None, # type: nodes.Node
env=None, # type: BuildEnvironment
):
@@ -92,12 +93,12 @@ class Field(object):
return [self.make_xref(rolename, domain, target, innernode, contnode, env)]
def make_entry(self, fieldarg, content):
- # type: (List, unicode) -> Tuple[List, unicode]
+ # type: (str, List[nodes.Node]) -> Tuple[str, List[nodes.Node]]
return (fieldarg, content)
def make_field(self,
- types, # type: Dict[unicode, List[nodes.Node]]
- domain, # type: unicode
+ types, # type: Dict[str, List[nodes.Node]]
+ domain, # type: str
item, # type: Tuple
env=None, # type: BuildEnvironment
):
@@ -137,13 +138,13 @@ class GroupedField(Field):
def __init__(self, name, names=(), label=None, rolename=None,
can_collapse=False):
- # type: (unicode, Tuple[unicode, ...], unicode, unicode, bool) -> None
- Field.__init__(self, name, names, label, True, rolename)
+ # type: (str, Tuple[str, ...], str, str, bool) -> None
+ super().__init__(name, names, label, True, rolename)
self.can_collapse = can_collapse
def make_field(self,
- types, # type: Dict[unicode, List[nodes.Node]]
- domain, # type: unicode
+ types, # type: Dict[str, List[nodes.Node]]
+ domain, # type: str
items, # type: Tuple
env=None, # type: BuildEnvironment
):
@@ -159,7 +160,8 @@ class GroupedField(Field):
listnode += nodes.list_item('', par)
if len(items) == 1 and self.can_collapse:
- fieldbody = nodes.field_body('', listnode[0][0])
+ list_item = cast(nodes.list_item, listnode[0])
+ fieldbody = nodes.field_body('', list_item[0])
return nodes.field('', fieldname, fieldbody)
fieldbody = nodes.field_body('', listnode)
@@ -189,20 +191,20 @@ class TypedField(GroupedField):
def __init__(self, name, names=(), typenames=(), label=None,
rolename=None, typerolename=None, can_collapse=False):
- # type: (unicode, Tuple[unicode, ...], Tuple[unicode, ...], unicode, unicode, unicode, bool) -> None # NOQA
- GroupedField.__init__(self, name, names, label, rolename, can_collapse)
+ # type: (str, Tuple[str, ...], Tuple[str, ...], str, str, str, bool) -> None
+ super().__init__(name, names, label, rolename, can_collapse)
self.typenames = typenames
self.typerolename = typerolename
def make_field(self,
- types, # type: Dict[unicode, List[nodes.Node]]
- domain, # type: unicode
+ types, # type: Dict[str, List[nodes.Node]]
+ domain, # type: str
items, # type: Tuple
env=None, # type: BuildEnvironment
):
# type: (...) -> nodes.field
def handle_item(fieldarg, content):
- # type: (unicode, unicode) -> nodes.paragraph
+ # type: (str, str) -> nodes.paragraph
par = nodes.paragraph()
par.extend(self.make_xrefs(self.rolename, domain, fieldarg,
addnodes.literal_strong, env=env))
@@ -213,7 +215,7 @@ class TypedField(GroupedField):
# inconsistencies later when references are resolved
fieldtype = types.pop(fieldarg)
if len(fieldtype) == 1 and isinstance(fieldtype[0], nodes.Text):
- typename = u''.join(n.astext() for n in fieldtype)
+ typename = fieldtype[0].astext()
par.extend(self.make_xrefs(self.typerolename, domain, typename,
addnodes.literal_emphasis, env=env))
else:
@@ -226,7 +228,7 @@ class TypedField(GroupedField):
fieldname = nodes.field_name('', self.label)
if len(items) == 1 and self.can_collapse:
fieldarg, content = items[0]
- bodynode = handle_item(fieldarg, content)
+ bodynode = handle_item(fieldarg, content) # type: nodes.Node
else:
bodynode = self.list_type()
for fieldarg, content in items:
@@ -235,11 +237,12 @@ class TypedField(GroupedField):
return nodes.field('', fieldname, fieldbody)
-class DocFieldTransformer(object):
+class DocFieldTransformer:
"""
Transforms field lists in "doc field" syntax into better-looking
equivalents, using the field type definitions given on a domain.
"""
+ typemap = None # type: Dict[str, Tuple[Field, bool]]
def __init__(self, directive):
# type: (Any) -> None
@@ -250,18 +253,19 @@ class DocFieldTransformer(object):
self.typemap = directive._doc_field_type_map
def preprocess_fieldtypes(self, types):
- # type: (List) -> Dict[unicode, Tuple[Any, bool]]
+ # type: (List[Field]) -> Dict[str, Tuple[Field, bool]]
typemap = {}
for fieldtype in types:
for name in fieldtype.names:
typemap[name] = fieldtype, False
if fieldtype.is_typed:
- for name in fieldtype.typenames:
- typemap[name] = fieldtype, True
+ typed_field = cast(TypedField, fieldtype)
+ for name in typed_field.typenames:
+ typemap[name] = typed_field, True
return typemap
def transform_all(self, node):
- # type: (nodes.Node) -> None
+ # type: (addnodes.desc_content) -> None
"""Transform all field list children of a node."""
# don't traverse, only handle field lists that are immediate children
for child in node:
@@ -269,58 +273,62 @@ class DocFieldTransformer(object):
self.transform(child)
def transform(self, node):
- # type: (nodes.Node) -> None
+ # type: (nodes.field_list) -> None
"""Transform a single field list *node*."""
typemap = self.typemap
- entries = []
- groupindices = {} # type: Dict[unicode, int]
- types = {} # type: Dict[unicode, Dict]
+ entries = [] # type: List[Union[nodes.field, Tuple[Field, Any]]]
+ groupindices = {} # type: Dict[str, int]
+ types = {} # type: Dict[str, Dict]
# step 1: traverse all fields and collect field types and content
- for field in node:
- fieldname, fieldbody = field
+ for field in cast(List[nodes.field], node):
+ assert len(field) == 2
+ field_name = cast(nodes.field_name, field[0])
+ field_body = cast(nodes.field_body, field[1])
try:
# split into field type and argument
- fieldtype, fieldarg = fieldname.astext().split(None, 1)
+ fieldtype_name, fieldarg = field_name.astext().split(None, 1)
except ValueError:
# maybe an argument-less field type?
- fieldtype, fieldarg = fieldname.astext(), ''
- typedesc, is_typefield = typemap.get(fieldtype, (None, None))
+ fieldtype_name, fieldarg = field_name.astext(), ''
+ typedesc, is_typefield = typemap.get(fieldtype_name, (None, None))
# collect the content, trying not to keep unnecessary paragraphs
- if _is_single_paragraph(fieldbody):
- content = fieldbody.children[0].children
+ if _is_single_paragraph(field_body):
+ paragraph = cast(nodes.paragraph, field_body[0])
+ content = paragraph.children
else:
- content = fieldbody.children
+ content = field_body.children
# sort out unknown fields
if typedesc is None or typedesc.has_arg != bool(fieldarg):
# either the field name is unknown, or the argument doesn't
# match the spec; capitalize field name and be done with it
- new_fieldname = fieldtype[0:1].upper() + fieldtype[1:]
+ new_fieldname = fieldtype_name[0:1].upper() + fieldtype_name[1:]
if fieldarg:
new_fieldname += ' ' + fieldarg
- fieldname[0] = nodes.Text(new_fieldname)
+ field_name[0] = nodes.Text(new_fieldname)
entries.append(field)
# but if this has a type then we can at least link it
if (typedesc and is_typefield and content and
len(content) == 1 and isinstance(content[0], nodes.Text)):
+ typed_field = cast(TypedField, typedesc)
target = content[0].astext()
- xrefs = typedesc.make_xrefs(
- typedesc.typerolename,
+ xrefs = typed_field.make_xrefs(
+ typed_field.typerolename,
self.directive.domain,
target,
contnode=content[0],
)
- if _is_single_paragraph(fieldbody):
- fieldbody.children[0].clear()
- fieldbody.children[0].extend(xrefs)
+ if _is_single_paragraph(field_body):
+ paragraph = cast(nodes.paragraph, field_body[0])
+ paragraph.clear()
+ paragraph.extend(xrefs)
else:
- fieldbody.clear()
- fieldbody += nodes.paragraph()
- fieldbody[0].extend(xrefs)
+ field_body.clear()
+ field_body += nodes.paragraph('', '', *xrefs)
continue
@@ -347,27 +355,27 @@ class DocFieldTransformer(object):
[nodes.Text(argtype)]
fieldarg = argname
- translatable_content = nodes.inline(fieldbody.rawsource,
+ translatable_content = nodes.inline(field_body.rawsource,
translatable=True)
- translatable_content.document = fieldbody.parent.document
- translatable_content.source = fieldbody.parent.source
- translatable_content.line = fieldbody.parent.line
+ translatable_content.document = field_body.parent.document
+ translatable_content.source = field_body.parent.source
+ translatable_content.line = field_body.parent.line
translatable_content += content
# grouped entries need to be collected in one entry, while others
# get one entry per field
if typedesc.is_grouped:
if typename in groupindices:
- group = entries[groupindices[typename]]
+ group = cast(Tuple[Field, List], entries[groupindices[typename]])
else:
groupindices[typename] = len(entries)
- group = [typedesc, []]
+ group = (typedesc, [])
entries.append(group)
- entry = typedesc.make_entry(fieldarg, [translatable_content])
- group[1].append(entry)
+ new_entry = typedesc.make_entry(fieldarg, [translatable_content])
+ group[1].append(new_entry)
else:
- entry = typedesc.make_entry(fieldarg, [translatable_content])
- entries.append([typedesc, entry])
+ new_entry = typedesc.make_entry(fieldarg, [translatable_content])
+ entries.append((typedesc, new_entry))
# step 2: all entries are collected, construct the new field list
new_list = nodes.field_list()
@@ -376,10 +384,10 @@ class DocFieldTransformer(object):
# pass-through old field
new_list += entry
else:
- fieldtype, content = entry
+ fieldtype, items = entry
fieldtypes = types.get(fieldtype.name, {})
env = self.directive.state.document.settings.env
new_list += fieldtype.make_field(fieldtypes, self.directive.domain,
- content, env=env)
+ items, env=env)
node.replace_self(new_list)