diff options
Diffstat (limited to 'sphinx/util/docfields.py')
-rw-r--r-- | sphinx/util/docfields.py | 148 |
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) |