Merge branch '2.0'

This commit is contained in:
Takeshi KOMIYA 2019-06-30 01:08:48 +09:00
commit c18dcf08f9
11 changed files with 361 additions and 507 deletions

View File

@ -243,21 +243,23 @@ inserting them into the page source under a suitable :rst:dir:`py:module`,
These work exactly like :rst:dir:`autoclass` etc., These work exactly like :rst:dir:`autoclass` etc.,
but do not offer the options used for automatic member documentation. but do not offer the options used for automatic member documentation.
:rst:dir:`autodata` and :rst:dir:`autoattribute` support :rst:dir:`autodata` and :rst:dir:`autoattribute` support the ``annotation``
the ``annotation`` option. option. The option controls how the value of variable is shown. If specified
Without this option, the representation of the object without arguments, only the name of the variable will be printed, and its value
will be shown in the documentation. is not shown::
When the option is given without arguments,
only the name of the object will be printed::
.. autodata:: CD_DRIVE .. autodata:: CD_DRIVE
:annotation: :annotation:
You can tell sphinx what should be printed after the name:: If the option specified with arguments, it is printed after the name as a value
of the variable::
.. autodata:: CD_DRIVE .. autodata:: CD_DRIVE
:annotation: = your CD device name :annotation: = your CD device name
By default, without ``annotation`` option, Sphinx tries to obtain the value of
the variable and print it after the name.
For module data members and class attributes, documentation can either be put For module data members and class attributes, documentation can either be put
into a comment with special formatting (using a ``#:`` to start the comment into a comment with special formatting (using a ``#:`` to start the comment
instead of just ``#``), or in a docstring *after* the definition. Comments instead of just ``#``), or in a docstring *after* the definition. Comments

View File

@ -10,21 +10,22 @@
""" """
import copy import copy
from typing import NamedTuple from typing import Any, Callable, Dict, Iterable, List, NamedTuple, Tuple, Type, Union
from docutils import nodes
from docutils.nodes import Element, Node, system_message
from docutils.parsers.rst.states import Inliner
from sphinx.addnodes import pending_xref
from sphinx.errors import SphinxError from sphinx.errors import SphinxError
from sphinx.locale import _ from sphinx.locale import _
from sphinx.roles import XRefRole
from sphinx.util.typing import RoleFunction
if False: if False:
# For type annotation # For type annotation
from typing import Any, Callable, Dict, Iterable, List, Tuple, Type, Union # NOQA from sphinx.builders import Builder
from docutils import nodes # NOQA from sphinx.environment import BuildEnvironment
from docutils.parsers.rst.states import Inliner # NOQA
from sphinx import addnodes # NOQA
from sphinx.builders import Builder # NOQA
from sphinx.environment import BuildEnvironment # NOQA
from sphinx.roles import XRefRole # NOQA
from sphinx.util.typing import RoleFunction # NOQA
class ObjType: class ObjType:
@ -46,8 +47,7 @@ class ObjType:
'searchprio': 1, 'searchprio': 1,
} }
def __init__(self, lname, *roles, **attrs): def __init__(self, lname: str, *roles, **attrs) -> None:
# type: (str, Any, Any) -> None
self.lname = lname self.lname = lname
self.roles = roles # type: Tuple self.roles = roles # type: Tuple
self.attrs = self.known_attrs.copy() # type: Dict self.attrs = self.known_attrs.copy() # type: Dict
@ -82,15 +82,14 @@ class Index:
localname = None # type: str localname = None # type: str
shortname = None # type: str shortname = None # type: str
def __init__(self, domain): def __init__(self, domain: "Domain") -> None:
# type: (Domain) -> None
if self.name is None or self.localname is None: if self.name is None or self.localname is None:
raise SphinxError('Index subclass %s has no valid name or localname' raise SphinxError('Index subclass %s has no valid name or localname'
% self.__class__.__name__) % self.__class__.__name__)
self.domain = domain self.domain = domain
def generate(self, docnames=None): def generate(self, docnames: Iterable[str] = None
# type: (Iterable[str]) -> Tuple[List[Tuple[str, List[IndexEntry]]], bool] ) -> Tuple[List[Tuple[str, List[IndexEntry]]], bool]:
"""Get entries for the index. """Get entries for the index.
If ``docnames`` is given, restrict to entries referring to these If ``docnames`` is given, restrict to entries referring to these
@ -181,7 +180,7 @@ class Domain:
#: role name -> a warning message if reference is missing #: role name -> a warning message if reference is missing
dangling_warnings = {} # type: Dict[str, str] dangling_warnings = {} # type: Dict[str, str]
#: node_class -> (enum_node_type, title_getter) #: node_class -> (enum_node_type, title_getter)
enumerable_nodes = {} # type: Dict[Type[nodes.Node], Tuple[str, Callable]] enumerable_nodes = {} # type: Dict[Type[Node], Tuple[str, Callable]]
#: data value for a fresh environment #: data value for a fresh environment
initial_data = {} # type: Dict initial_data = {} # type: Dict
@ -190,8 +189,7 @@ class Domain:
#: data version, bump this when the format of `self.data` changes #: data version, bump this when the format of `self.data` changes
data_version = 0 data_version = 0
def __init__(self, env): def __init__(self, env: "BuildEnvironment") -> None:
# type: (BuildEnvironment) -> None
self.env = env # type: BuildEnvironment self.env = env # type: BuildEnvironment
self._role_cache = {} # type: Dict[str, Callable] self._role_cache = {} # type: Dict[str, Callable]
self._directive_cache = {} # type: Dict[str, Callable] self._directive_cache = {} # type: Dict[str, Callable]
@ -220,8 +218,7 @@ class Domain:
self.objtypes_for_role = self._role2type.get # type: Callable[[str], List[str]] self.objtypes_for_role = self._role2type.get # type: Callable[[str], List[str]]
self.role_for_objtype = self._type2role.get # type: Callable[[str], str] self.role_for_objtype = self._type2role.get # type: Callable[[str], str]
def add_object_type(self, name, objtype): def add_object_type(self, name: str, objtype: ObjType) -> None:
# type: (str, ObjType) -> None
"""Add an object type.""" """Add an object type."""
self.object_types[name] = objtype self.object_types[name] = objtype
if objtype.roles: if objtype.roles:
@ -232,8 +229,7 @@ class Domain:
for role in objtype.roles: for role in objtype.roles:
self._role2type.setdefault(role, []).append(name) self._role2type.setdefault(role, []).append(name)
def role(self, name): def role(self, name: str) -> RoleFunction:
# type: (str) -> RoleFunction
"""Return a role adapter function that always gives the registered """Return a role adapter function that always gives the registered
role its full name ('domain:name') as the first argument. role its full name ('domain:name') as the first argument.
""" """
@ -243,15 +239,15 @@ class Domain:
return None return None
fullname = '%s:%s' % (self.name, name) fullname = '%s:%s' % (self.name, name)
def role_adapter(typ, rawtext, text, lineno, inliner, options={}, content=[]): def role_adapter(typ: str, rawtext: str, text: str, lineno: int,
# type: (str, str, str, int, Inliner, Dict, List[str]) -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA inliner: Inliner, options: Dict = {}, content: List[str] = []
) -> Tuple[List[Node], List[system_message]]:
return self.roles[name](fullname, rawtext, text, lineno, return self.roles[name](fullname, rawtext, text, lineno,
inliner, options, content) inliner, options, content)
self._role_cache[name] = role_adapter self._role_cache[name] = role_adapter
return role_adapter return role_adapter
def directive(self, name): def directive(self, name: str) -> Callable:
# type: (str) -> Callable
"""Return a directive adapter class that always gives the registered """Return a directive adapter class that always gives the registered
directive its full name ('domain:name') as ``self.name``. directive its full name ('domain:name') as ``self.name``.
""" """
@ -263,8 +259,7 @@ class Domain:
BaseDirective = self.directives[name] BaseDirective = self.directives[name]
class DirectiveAdapter(BaseDirective): # type: ignore class DirectiveAdapter(BaseDirective): # type: ignore
def run(self): def run(self) -> List[Node]:
# type: () -> List[nodes.Node]
self.name = fullname self.name = fullname
return super().run() return super().run()
self._directive_cache[name] = DirectiveAdapter self._directive_cache[name] = DirectiveAdapter
@ -272,13 +267,11 @@ class Domain:
# methods that should be overwritten # methods that should be overwritten
def clear_doc(self, docname): def clear_doc(self, docname: str) -> None:
# type: (str) -> None
"""Remove traces of a document in the domain-specific inventories.""" """Remove traces of a document in the domain-specific inventories."""
pass pass
def merge_domaindata(self, docnames, otherdata): def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None:
# type: (List[str], Dict) -> None
"""Merge in data regarding *docnames* from a different domaindata """Merge in data regarding *docnames* from a different domaindata
inventory (coming from a subprocess in parallel builds). inventory (coming from a subprocess in parallel builds).
""" """
@ -286,26 +279,24 @@ class Domain:
'to be able to do parallel builds!' % 'to be able to do parallel builds!' %
self.__class__) self.__class__)
def process_doc(self, env, docname, document): def process_doc(self, env: "BuildEnvironment", docname: str,
# type: (BuildEnvironment, str, nodes.document) -> None document: nodes.document) -> None:
"""Process a document after it is read by the environment.""" """Process a document after it is read by the environment."""
pass pass
def check_consistency(self): def check_consistency(self) -> None:
# type: () -> None
"""Do consistency checks (**experimental**).""" """Do consistency checks (**experimental**)."""
pass pass
def process_field_xref(self, pnode): def process_field_xref(self, pnode: pending_xref) -> None:
# type: (addnodes.pending_xref) -> None
"""Process a pending xref created in a doc field. """Process a pending xref created in a doc field.
For example, attach information about the current scope. For example, attach information about the current scope.
""" """
pass pass
def resolve_xref(self, env, fromdocname, builder, def resolve_xref(self, env: "BuildEnvironment", fromdocname: str, builder: "Builder",
typ, target, node, contnode): typ: str, target: str, node: pending_xref, contnode: Element
# type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA ) -> Element:
"""Resolve the pending_xref *node* with the given *typ* and *target*. """Resolve the pending_xref *node* with the given *typ* and *target*.
This method should return a new node, to replace the xref node, This method should return a new node, to replace the xref node,
@ -321,8 +312,9 @@ class Domain:
""" """
pass pass
def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode): def resolve_any_xref(self, env: "BuildEnvironment", fromdocname: str, builder: "Builder",
# type: (BuildEnvironment, str, Builder, str, addnodes.pending_xref, nodes.Element) -> List[Tuple[str, nodes.Element]] # NOQA target: str, node: pending_xref, contnode: Element
) -> List[Tuple[str, Element]]:
"""Resolve the pending_xref *node* with the given *target*. """Resolve the pending_xref *node* with the given *target*.
The reference comes from an "any" or similar role, which means that we The reference comes from an "any" or similar role, which means that we
@ -338,8 +330,7 @@ class Domain:
""" """
raise NotImplementedError raise NotImplementedError
def get_objects(self): def get_objects(self) -> Iterable[Tuple[str, str, str, str, str, int]]:
# type: () -> Iterable[Tuple[str, str, str, str, str, int]]
"""Return an iterable of "object descriptions". """Return an iterable of "object descriptions".
Object descriptions are tuples with six items: Object descriptions are tuples with six items:
@ -374,20 +365,17 @@ class Domain:
""" """
return [] return []
def get_type_name(self, type, primary=False): def get_type_name(self, type: ObjType, primary: bool = False) -> str:
# type: (ObjType, bool) -> str
"""Return full name for given ObjType.""" """Return full name for given ObjType."""
if primary: if primary:
return type.lname return type.lname
return _('%s %s') % (self.label, type.lname) return _('%s %s') % (self.label, type.lname)
def get_enumerable_node_type(self, node): def get_enumerable_node_type(self, node: Node) -> str:
# type: (nodes.Node) -> str
"""Get type of enumerable nodes (experimental).""" """Get type of enumerable nodes (experimental)."""
enum_node_type, _ = self.enumerable_nodes.get(node.__class__, (None, None)) enum_node_type, _ = self.enumerable_nodes.get(node.__class__, (None, None))
return enum_node_type return enum_node_type
def get_full_qualified_name(self, node): def get_full_qualified_name(self, node: Element) -> str:
# type: (nodes.Element) -> str
"""Return full qualified name for given node.""" """Return full qualified name for given node."""
return None return None

View File

@ -10,24 +10,23 @@
import re import re
import string import string
from typing import Any, Dict, Iterator, List, Tuple
from docutils import nodes from docutils import nodes
from docutils.nodes import Element
from sphinx import addnodes from sphinx import addnodes
from sphinx.addnodes import pending_xref, desc_signature
from sphinx.application import Sphinx
from sphinx.builders import Builder
from sphinx.directives import ObjectDescription from sphinx.directives import ObjectDescription
from sphinx.domains import Domain, ObjType from sphinx.domains import Domain, ObjType
from sphinx.environment import BuildEnvironment
from sphinx.locale import _ from sphinx.locale import _
from sphinx.roles import XRefRole from sphinx.roles import XRefRole
from sphinx.util.docfields import Field, TypedField from sphinx.util.docfields import Field, TypedField
from sphinx.util.nodes import make_refnode from sphinx.util.nodes import make_refnode
if False:
# For type annotation
from typing import Any, Dict, Iterator, List, Tuple # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.builders import Builder # NOQA
from sphinx.environment import BuildEnvironment # NOQA
# RE to split at word boundaries # RE to split at word boundaries
wsplit_re = re.compile(r'(\W+)') wsplit_re = re.compile(r'(\W+)')
@ -79,23 +78,20 @@ class CObject(ObjectDescription):
'struct', '_Bool', 'struct', '_Bool',
} }
def _parse_type(self, node, ctype): def _parse_type(self, node: Element, ctype: str) -> None:
# type: (nodes.Element, str) -> None
# add cross-ref nodes for all words # add cross-ref nodes for all words
for part in [_f for _f in wsplit_re.split(ctype) if _f]: for part in [_f for _f in wsplit_re.split(ctype) if _f]:
tnode = nodes.Text(part, part) tnode = nodes.Text(part, part)
if part[0] in string.ascii_letters + '_' and \ if part[0] in string.ascii_letters + '_' and \
part not in self.stopwords: part not in self.stopwords:
pnode = addnodes.pending_xref( pnode = pending_xref('', refdomain='c', reftype='type', reftarget=part,
'', refdomain='c', reftype='type', reftarget=part,
modname=None, classname=None) modname=None, classname=None)
pnode += tnode pnode += tnode
node += pnode node += pnode
else: else:
node += tnode node += tnode
def _parse_arglist(self, arglist): def _parse_arglist(self, arglist: str) -> Iterator[str]:
# type: (str) -> Iterator[str]
while True: while True:
m = c_funcptr_arg_sig_re.match(arglist) m = c_funcptr_arg_sig_re.match(arglist)
if m: if m:
@ -113,8 +109,7 @@ class CObject(ObjectDescription):
yield arglist yield arglist
break break
def handle_signature(self, sig, signode): def handle_signature(self, sig: str, signode: desc_signature) -> str:
# type: (str, addnodes.desc_signature) -> str
"""Transform a C signature into RST nodes.""" """Transform a C signature into RST nodes."""
# first try the function pointer signature regex, it's more specific # first try the function pointer signature regex, it's more specific
m = c_funcptr_sig_re.match(sig) m = c_funcptr_sig_re.match(sig)
@ -183,8 +178,7 @@ class CObject(ObjectDescription):
signode += addnodes.desc_addname(const, const) signode += addnodes.desc_addname(const, const)
return fullname return fullname
def get_index_text(self, name): def get_index_text(self, name: str) -> str:
# type: (str) -> str
if self.objtype == 'function': if self.objtype == 'function':
return _('%s (C function)') % name return _('%s (C function)') % name
elif self.objtype == 'member': elif self.objtype == 'member':
@ -198,8 +192,7 @@ class CObject(ObjectDescription):
else: else:
return '' return ''
def add_target_and_index(self, name, sig, signode): def add_target_and_index(self, name: str, sig: str, signode: desc_signature) -> None:
# type: (str, str, addnodes.desc_signature) -> None
# for C API items we add a prefix since names are usually not qualified # for C API items we add a prefix since names are usually not qualified
# by a module name and so easily clash with e.g. section titles # by a module name and so easily clash with e.g. section titles
targetname = 'c.' + name targetname = 'c.' + name
@ -221,23 +214,21 @@ class CObject(ObjectDescription):
self.indexnode['entries'].append(('single', indextext, self.indexnode['entries'].append(('single', indextext,
targetname, '', None)) targetname, '', None))
def before_content(self): def before_content(self) -> None:
# type: () -> None
self.typename_set = False self.typename_set = False
if self.name == 'c:type': if self.name == 'c:type':
if self.names: if self.names:
self.env.ref_context['c:type'] = self.names[0] self.env.ref_context['c:type'] = self.names[0]
self.typename_set = True self.typename_set = True
def after_content(self): def after_content(self) -> None:
# type: () -> None
if self.typename_set: if self.typename_set:
self.env.ref_context.pop('c:type', None) self.env.ref_context.pop('c:type', None)
class CXRefRole(XRefRole): class CXRefRole(XRefRole):
def process_link(self, env, refnode, has_explicit_title, title, target): def process_link(self, env: BuildEnvironment, refnode: Element,
# type: (BuildEnvironment, nodes.Element, bool, str, str) -> Tuple[str, str] has_explicit_title: bool, title: str, target: str) -> Tuple[str, str]:
if not has_explicit_title: if not has_explicit_title:
target = target.lstrip('~') # only has a meaning for the title target = target.lstrip('~') # only has a meaning for the title
# if the first character is a tilde, don't display the module/class # if the first character is a tilde, don't display the module/class
@ -280,22 +271,20 @@ class CDomain(Domain):
'objects': {}, # fullname -> docname, objtype 'objects': {}, # fullname -> docname, objtype
} # type: Dict[str, Dict[str, Tuple[str, Any]]] } # type: Dict[str, Dict[str, Tuple[str, Any]]]
def clear_doc(self, docname): def clear_doc(self, docname: str) -> None:
# type: (str) -> None
for fullname, (fn, _l) in list(self.data['objects'].items()): for fullname, (fn, _l) in list(self.data['objects'].items()):
if fn == docname: if fn == docname:
del self.data['objects'][fullname] del self.data['objects'][fullname]
def merge_domaindata(self, docnames, otherdata): def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None:
# type: (List[str], Dict) -> None
# XXX check duplicates # XXX check duplicates
for fullname, (fn, objtype) in otherdata['objects'].items(): for fullname, (fn, objtype) in otherdata['objects'].items():
if fn in docnames: if fn in docnames:
self.data['objects'][fullname] = (fn, objtype) self.data['objects'][fullname] = (fn, objtype)
def resolve_xref(self, env, fromdocname, builder, def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
typ, target, node, contnode): typ: str, target: str, node: pending_xref, contnode: Element
# type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA ) -> Element:
# strip pointer asterisk # strip pointer asterisk
target = target.rstrip(' *') target = target.rstrip(' *')
# becase TypedField can generate xrefs # becase TypedField can generate xrefs
@ -307,9 +296,9 @@ class CDomain(Domain):
return make_refnode(builder, fromdocname, obj[0], 'c.' + target, return make_refnode(builder, fromdocname, obj[0], 'c.' + target,
contnode, target) contnode, target)
def resolve_any_xref(self, env, fromdocname, builder, target, def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
node, contnode): target: str, node: pending_xref, contnode: Element
# type: (BuildEnvironment, str, Builder, str, addnodes.pending_xref, nodes.Element) -> List[Tuple[str, nodes.Element]] # NOQA ) -> List[Tuple[str, Element]]:
# strip pointer asterisk # strip pointer asterisk
target = target.rstrip(' *') target = target.rstrip(' *')
if target not in self.data['objects']: if target not in self.data['objects']:
@ -319,14 +308,12 @@ class CDomain(Domain):
make_refnode(builder, fromdocname, obj[0], 'c.' + target, make_refnode(builder, fromdocname, obj[0], 'c.' + target,
contnode, target))] contnode, target))]
def get_objects(self): def get_objects(self) -> Iterator[Tuple[str, str, str, str, str, int]]:
# type: () -> Iterator[Tuple[str, str, str, str, str, int]]
for refname, (docname, type) in list(self.data['objects'].items()): for refname, (docname, type) in list(self.data['objects'].items()):
yield (refname, refname, type, docname, 'c.' + refname, 1) yield (refname, refname, type, docname, 'c.' + refname, 1)
def setup(app): def setup(app: Sphinx) -> Dict[str, Any]:
# type: (Sphinx) -> Dict[str, Any]
app.add_domain(CDomain) app.add_domain(CDomain)
return { return {

View File

@ -9,9 +9,11 @@
""" """
from collections import namedtuple from collections import namedtuple
from typing import Any, Dict, List
from typing import cast from typing import cast
from docutils import nodes from docutils import nodes
from docutils.nodes import Node
from sphinx import addnodes from sphinx import addnodes
from sphinx.domains import Domain from sphinx.domains import Domain
@ -21,9 +23,8 @@ from sphinx.util.docutils import SphinxDirective
if False: if False:
# For type annotation # For type annotation
from typing import Any, Dict, List # NOQA from sphinx.application import Sphinx
from sphinx.application import Sphinx # NOQA from sphinx.environment import BuildEnvironment
from sphinx.environment import BuildEnvironment # NOQA
versionlabels = { versionlabels = {
@ -54,8 +55,7 @@ class VersionChange(SphinxDirective):
final_argument_whitespace = True final_argument_whitespace = True
option_spec = {} # type: Dict option_spec = {} # type: Dict
def run(self): def run(self) -> List[Node]:
# type: () -> List[nodes.Node]
node = addnodes.versionmodified() node = addnodes.versionmodified()
node.document = self.state.document node.document = self.state.document
self.set_source_info(node) self.set_source_info(node)
@ -93,7 +93,7 @@ class VersionChange(SphinxDirective):
domain = cast(ChangeSetDomain, self.env.get_domain('changeset')) domain = cast(ChangeSetDomain, self.env.get_domain('changeset'))
domain.note_changeset(node) domain.note_changeset(node)
ret = [node] # type: List[nodes.Node] ret = [node] # type: List[Node]
ret += messages ret += messages
return ret return ret
@ -108,15 +108,13 @@ class ChangeSetDomain(Domain):
'changes': {}, # version -> list of ChangeSet 'changes': {}, # version -> list of ChangeSet
} # type: Dict } # type: Dict
def clear_doc(self, docname): def clear_doc(self, docname: str) -> None:
# type: (str) -> None
for version, changes in self.data['changes'].items(): for version, changes in self.data['changes'].items():
for changeset in changes[:]: for changeset in changes[:]:
if changeset.docname == docname: if changeset.docname == docname:
changes.remove(changeset) changes.remove(changeset)
def merge_domaindata(self, docnames, otherdata): def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None:
# type: (List[str], Dict) -> None
# XXX duplicates? # XXX duplicates?
for version, otherchanges in otherdata['changes'].items(): for version, otherchanges in otherdata['changes'].items():
changes = self.data['changes'].setdefault(version, []) changes = self.data['changes'].setdefault(version, [])
@ -124,12 +122,10 @@ class ChangeSetDomain(Domain):
if changeset.docname in docnames: if changeset.docname in docnames:
changes.append(changeset) changes.append(changeset)
def process_doc(self, env, docname, document): def process_doc(self, env: "BuildEnvironment", docname: str, document: nodes.document) -> None: # NOQA
# type: (BuildEnvironment, str, nodes.document) -> None
pass # nothing to do here. All changesets are registered on calling directive. pass # nothing to do here. All changesets are registered on calling directive.
def note_changeset(self, node): def note_changeset(self, node: addnodes.versionmodified) -> None:
# type: (addnodes.versionmodified) -> None
version = node['version'] version = node['version']
module = self.env.ref_context.get('py:module') module = self.env.ref_context.get('py:module')
objname = self.env.temp_data.get('object') objname = self.env.temp_data.get('object')
@ -137,13 +133,11 @@ class ChangeSetDomain(Domain):
module, objname, node.astext()) module, objname, node.astext())
self.data['changes'].setdefault(version, []).append(changeset) self.data['changes'].setdefault(version, []).append(changeset)
def get_changesets_for(self, version): def get_changesets_for(self, version: str) -> List[ChangeSet]:
# type: (str) -> List[ChangeSet]
return self.data['changes'].get(version, []) return self.data['changes'].get(version, [])
def setup(app): def setup(app: "Sphinx") -> Dict[str, Any]:
# type: (Sphinx) -> Dict[str, Any]
app.add_domain(ChangeSetDomain) app.add_domain(ChangeSetDomain)
app.add_directive('deprecated', VersionChange) app.add_directive('deprecated', VersionChange)
app.add_directive('versionadded', VersionChange) app.add_directive('versionadded', VersionChange)

View File

@ -8,11 +8,13 @@
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
from typing import Any, Dict, List, Set, Tuple
from typing import cast from typing import cast
from docutils import nodes from docutils import nodes
from docutils.nodes import Element
from sphinx import addnodes from sphinx.addnodes import pending_xref
from sphinx.domains import Domain from sphinx.domains import Domain
from sphinx.locale import __ from sphinx.locale import __
from sphinx.transforms import SphinxTransform from sphinx.transforms import SphinxTransform
@ -21,10 +23,10 @@ from sphinx.util.nodes import copy_source_info, make_refnode
if False: if False:
# For type annotation # For type annotation
from typing import Any, Dict, List, Set, Tuple, Union # NOQA from sphinx.application import Sphinx
from sphinx.application import Sphinx # NOQA from sphinx.builders import Builder
from sphinx.builders import Builder # NOQA from sphinx.environment import BuildEnvironment
from sphinx.environment import BuildEnvironment # NOQA
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -40,17 +42,14 @@ class CitationDomain(Domain):
} }
@property @property
def citations(self): def citations(self) -> Dict[str, Tuple[str, str, int]]:
# type: () -> Dict[str, Tuple[str, str, int]]
return self.data.setdefault('citations', {}) return self.data.setdefault('citations', {})
@property @property
def citation_refs(self): def citation_refs(self) -> Dict[str, Set[str]]:
# type: () -> Dict[str, Set[str]]
return self.data.setdefault('citation_refs', {}) return self.data.setdefault('citation_refs', {})
def clear_doc(self, docname): def clear_doc(self, docname: str) -> None:
# type: (str) -> None
for key, (fn, _l, lineno) in list(self.citations.items()): for key, (fn, _l, lineno) in list(self.citations.items()):
if fn == docname: if fn == docname:
del self.citations[key] del self.citations[key]
@ -60,8 +59,7 @@ class CitationDomain(Domain):
elif docname in docnames: elif docname in docnames:
docnames.remove(docname) docnames.remove(docname)
def merge_domaindata(self, docnames, otherdata): def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None:
# type: (List[str], Dict) -> None
# XXX duplicates? # XXX duplicates?
for key, data in otherdata['citations'].items(): for key, data in otherdata['citations'].items():
if data[0] in docnames: if data[0] in docnames:
@ -72,8 +70,7 @@ class CitationDomain(Domain):
if docname in docnames: if docname in docnames:
citation_refs.add(docname) citation_refs.add(docname)
def note_citation(self, node): def note_citation(self, node: nodes.citation) -> None:
# type: (nodes.citation) -> None
label = node[0].astext() label = node[0].astext()
if label in self.citations: if label in self.citations:
path = self.env.doc2path(self.citations[label][0]) path = self.env.doc2path(self.citations[label][0])
@ -81,20 +78,19 @@ class CitationDomain(Domain):
location=node, type='ref', subtype='citation') location=node, type='ref', subtype='citation')
self.citations[label] = (node['docname'], node['ids'][0], node.line) self.citations[label] = (node['docname'], node['ids'][0], node.line)
def note_citation_reference(self, node): def note_citation_reference(self, node: pending_xref) -> None:
# type: (addnodes.pending_xref) -> None
docnames = self.citation_refs.setdefault(node['reftarget'], set()) docnames = self.citation_refs.setdefault(node['reftarget'], set())
docnames.add(self.env.docname) docnames.add(self.env.docname)
def check_consistency(self): def check_consistency(self) -> None:
# type: () -> None
for name, (docname, labelid, lineno) in self.citations.items(): for name, (docname, labelid, lineno) in self.citations.items():
if name not in self.citation_refs: if name not in self.citation_refs:
logger.warning(__('Citation [%s] is not referenced.'), name, logger.warning(__('Citation [%s] is not referenced.'), name,
type='ref', subtype='citation', location=(docname, lineno)) type='ref', subtype='citation', location=(docname, lineno))
def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): def resolve_xref(self, env: "BuildEnvironment", fromdocname: str, builder: "Builder",
# type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA typ: str, target: str, node: pending_xref, contnode: Element
) -> Element:
docname, labelid, lineno = self.citations.get(target, ('', '', 0)) docname, labelid, lineno = self.citations.get(target, ('', '', 0))
if not docname: if not docname:
return None return None
@ -102,8 +98,9 @@ class CitationDomain(Domain):
return make_refnode(builder, fromdocname, docname, return make_refnode(builder, fromdocname, docname,
labelid, contnode) labelid, contnode)
def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode): def resolve_any_xref(self, env: "BuildEnvironment", fromdocname: str, builder: "Builder",
# type: (BuildEnvironment, str, Builder, str, addnodes.pending_xref, nodes.Element) -> List[Tuple[str, nodes.Element]] # NOQA target: str, node: pending_xref, contnode: Element
) -> List[Tuple[str, Element]]:
refnode = self.resolve_xref(env, fromdocname, builder, 'ref', target, node, contnode) refnode = self.resolve_xref(env, fromdocname, builder, 'ref', target, node, contnode)
if refnode is None: if refnode is None:
return [] return []
@ -115,8 +112,7 @@ class CitationDefinitionTransform(SphinxTransform):
"""Mark citation definition labels as not smartquoted.""" """Mark citation definition labels as not smartquoted."""
default_priority = 619 default_priority = 619
def apply(self, **kwargs): def apply(self, **kwargs) -> None:
# type: (Any) -> None
domain = cast(CitationDomain, self.env.get_domain('citation')) domain = cast(CitationDomain, self.env.get_domain('citation'))
for node in self.document.traverse(nodes.citation): for node in self.document.traverse(nodes.citation):
# register citation node to domain # register citation node to domain
@ -135,12 +131,11 @@ class CitationReferenceTransform(SphinxTransform):
""" """
default_priority = 619 default_priority = 619
def apply(self, **kwargs): def apply(self, **kwargs) -> None:
# type: (Any) -> None
domain = cast(CitationDomain, self.env.get_domain('citation')) domain = cast(CitationDomain, self.env.get_domain('citation'))
for node in self.document.traverse(nodes.citation_reference): for node in self.document.traverse(nodes.citation_reference):
target = node.astext() target = node.astext()
ref = addnodes.pending_xref(target, refdomain='citation', reftype='ref', ref = pending_xref(target, refdomain='citation', reftype='ref',
reftarget=target, refwarn=True, reftarget=target, refwarn=True,
support_smartquotes=False, support_smartquotes=False,
ids=node["ids"], ids=node["ids"],
@ -153,8 +148,7 @@ class CitationReferenceTransform(SphinxTransform):
domain.note_citation_reference(ref) domain.note_citation_reference(ref)
def setup(app): def setup(app: "Sphinx") -> Dict[str, Any]:
# type: (Sphinx) -> Dict[str, Any]
app.add_domain(CitationDomain) app.add_domain(CitationDomain)
app.add_transform(CitationDefinitionTransform) app.add_transform(CitationDefinitionTransform)
app.add_transform(CitationReferenceTransform) app.add_transform(CitationReferenceTransform)

View File

@ -8,26 +8,26 @@
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
from typing import Any, Dict, Iterator, List, Tuple
from docutils import nodes from docutils import nodes
from docutils.nodes import Element, Node
from docutils.parsers.rst import directives from docutils.parsers.rst import directives
from sphinx import addnodes from sphinx import addnodes
from sphinx.addnodes import desc_signature, pending_xref
from sphinx.application import Sphinx
from sphinx.builders import Builder
from sphinx.directives import ObjectDescription from sphinx.directives import ObjectDescription
from sphinx.domains import Domain, ObjType from sphinx.domains import Domain, ObjType
from sphinx.domains.python import _pseudo_parse_arglist from sphinx.domains.python import _pseudo_parse_arglist
from sphinx.environment import BuildEnvironment
from sphinx.locale import _ from sphinx.locale import _
from sphinx.roles import XRefRole from sphinx.roles import XRefRole
from sphinx.util.docfields import Field, GroupedField, TypedField from sphinx.util.docfields import Field, GroupedField, TypedField
from sphinx.util.docutils import SphinxDirective from sphinx.util.docutils import SphinxDirective
from sphinx.util.nodes import make_refnode from sphinx.util.nodes import make_refnode
if False:
# For type annotation
from typing import Any, Dict, Iterator, List, Tuple # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.builders import Builder # NOQA
from sphinx.environment import BuildEnvironment # NOQA
class JSObject(ObjectDescription): class JSObject(ObjectDescription):
""" """
@ -44,8 +44,7 @@ class JSObject(ObjectDescription):
#: based on directive nesting #: based on directive nesting
allow_nesting = False allow_nesting = False
def handle_signature(self, sig, signode): def handle_signature(self, sig: str, signode: desc_signature) -> Tuple[str, str]:
# type: (str, addnodes.desc_signature) -> Tuple[str, str]
"""Breaks down construct signatures """Breaks down construct signatures
Parses out prefix and argument list from construct definition. The Parses out prefix and argument list from construct definition. The
@ -98,8 +97,8 @@ class JSObject(ObjectDescription):
_pseudo_parse_arglist(signode, arglist) _pseudo_parse_arglist(signode, arglist)
return fullname, prefix return fullname, prefix
def add_target_and_index(self, name_obj, sig, signode): def add_target_and_index(self, name_obj: Tuple[str, str], sig: str,
# type: (Tuple[str, str], str, addnodes.desc_signature) -> None signode: desc_signature) -> None:
mod_name = self.env.ref_context.get('js:module') mod_name = self.env.ref_context.get('js:module')
fullname = (mod_name and mod_name + '.' or '') + name_obj[0] fullname = (mod_name and mod_name + '.' or '') + name_obj[0]
if fullname not in self.state.document.ids: if fullname not in self.state.document.ids:
@ -122,8 +121,7 @@ class JSObject(ObjectDescription):
fullname.replace('$', '_S_'), fullname.replace('$', '_S_'),
'', None)) '', None))
def get_index_text(self, objectname, name_obj): def get_index_text(self, objectname: str, name_obj: Tuple[str, str]) -> str:
# type: (str, Tuple[str, str]) -> str
name, obj = name_obj name, obj = name_obj
if self.objtype == 'function': if self.objtype == 'function':
if not obj: if not obj:
@ -137,8 +135,7 @@ class JSObject(ObjectDescription):
return _('%s (%s attribute)') % (name, obj) return _('%s (%s attribute)') % (name, obj)
return '' return ''
def before_content(self): def before_content(self) -> None:
# type: () -> None
"""Handle object nesting before content """Handle object nesting before content
:py:class:`JSObject` represents JavaScript language constructs. For :py:class:`JSObject` represents JavaScript language constructs. For
@ -174,8 +171,7 @@ class JSObject(ObjectDescription):
objects = self.env.ref_context.setdefault('js:objects', []) objects = self.env.ref_context.setdefault('js:objects', [])
objects.append(prefix) objects.append(prefix)
def after_content(self): def after_content(self) -> None:
# type: () -> None
"""Handle object de-nesting after content """Handle object de-nesting after content
If this class is a nestable object, removing the last nested class prefix If this class is a nestable object, removing the last nested class prefix
@ -246,12 +242,11 @@ class JSModule(SphinxDirective):
'noindex': directives.flag 'noindex': directives.flag
} }
def run(self): def run(self) -> List[Node]:
# type: () -> List[nodes.Node]
mod_name = self.arguments[0].strip() mod_name = self.arguments[0].strip()
self.env.ref_context['js:module'] = mod_name self.env.ref_context['js:module'] = mod_name
noindex = 'noindex' in self.options noindex = 'noindex' in self.options
ret = [] # type: List[nodes.Node] ret = [] # type: List[Node]
if not noindex: if not noindex:
self.env.domaindata['js']['modules'][mod_name] = self.env.docname self.env.domaindata['js']['modules'][mod_name] = self.env.docname
# Make a duplicate entry in 'objects' to facilitate searching for # Make a duplicate entry in 'objects' to facilitate searching for
@ -269,8 +264,8 @@ class JSModule(SphinxDirective):
class JSXRefRole(XRefRole): class JSXRefRole(XRefRole):
def process_link(self, env, refnode, has_explicit_title, title, target): def process_link(self, env: BuildEnvironment, refnode: Element,
# type: (BuildEnvironment, nodes.Element, bool, str, str) -> Tuple[str, str] has_explicit_title: bool, title: str, target: str) -> Tuple[str, str]:
# basically what sphinx.domains.python.PyXRefRole does # basically what sphinx.domains.python.PyXRefRole does
refnode['js:object'] = env.ref_context.get('js:object') refnode['js:object'] = env.ref_context.get('js:object')
refnode['js:module'] = env.ref_context.get('js:module') refnode['js:module'] = env.ref_context.get('js:module')
@ -322,8 +317,7 @@ class JavaScriptDomain(Domain):
'modules': {}, # mod_name -> docname 'modules': {}, # mod_name -> docname
} # type: Dict[str, Dict[str, Tuple[str, str]]] } # type: Dict[str, Dict[str, Tuple[str, str]]]
def clear_doc(self, docname): def clear_doc(self, docname: str) -> None:
# type: (str) -> None
for fullname, (pkg_docname, _l) in list(self.data['objects'].items()): for fullname, (pkg_docname, _l) in list(self.data['objects'].items()):
if pkg_docname == docname: if pkg_docname == docname:
del self.data['objects'][fullname] del self.data['objects'][fullname]
@ -331,8 +325,7 @@ class JavaScriptDomain(Domain):
if pkg_docname == docname: if pkg_docname == docname:
del self.data['modules'][mod_name] del self.data['modules'][mod_name]
def merge_domaindata(self, docnames, otherdata): def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None:
# type: (List[str], Dict) -> None
# XXX check duplicates # XXX check duplicates
for fullname, (fn, objtype) in otherdata['objects'].items(): for fullname, (fn, objtype) in otherdata['objects'].items():
if fn in docnames: if fn in docnames:
@ -341,8 +334,8 @@ class JavaScriptDomain(Domain):
if pkg_docname in docnames: if pkg_docname in docnames:
self.data['modules'][mod_name] = pkg_docname self.data['modules'][mod_name] = pkg_docname
def find_obj(self, env, mod_name, prefix, name, typ, searchorder=0): def find_obj(self, env: BuildEnvironment, mod_name: str, prefix: str, name: str,
# type: (BuildEnvironment, str, str, str, str, int) -> Tuple[str, Tuple[str, str]] typ: str, searchorder: int = 0) -> Tuple[str, Tuple[str, str]]:
if name[-2:] == '()': if name[-2:] == '()':
name = name[:-2] name = name[:-2]
objects = self.data['objects'] objects = self.data['objects']
@ -366,9 +359,9 @@ class JavaScriptDomain(Domain):
return newname, objects.get(newname) return newname, objects.get(newname)
def resolve_xref(self, env, fromdocname, builder, typ, target, node, def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
contnode): typ: str, target: str, node: pending_xref, contnode: Element
# type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA ) -> Element:
mod_name = node.get('js:module') mod_name = node.get('js:module')
prefix = node.get('js:object') prefix = node.get('js:object')
searchorder = node.hasattr('refspecific') and 1 or 0 searchorder = node.hasattr('refspecific') and 1 or 0
@ -378,9 +371,9 @@ class JavaScriptDomain(Domain):
return make_refnode(builder, fromdocname, obj[0], return make_refnode(builder, fromdocname, obj[0],
name.replace('$', '_S_'), contnode, name) name.replace('$', '_S_'), contnode, name)
def resolve_any_xref(self, env, fromdocname, builder, target, node, def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
contnode): target: str, node: pending_xref, contnode: Element
# type: (BuildEnvironment, str, Builder, str, addnodes.pending_xref, nodes.Element) -> List[Tuple[str, nodes.Element]] # NOQA ) -> List[Tuple[str, Element]]:
mod_name = node.get('js:module') mod_name = node.get('js:module')
prefix = node.get('js:object') prefix = node.get('js:object')
name, obj = self.find_obj(env, mod_name, prefix, target, None, 1) name, obj = self.find_obj(env, mod_name, prefix, target, None, 1)
@ -390,14 +383,12 @@ class JavaScriptDomain(Domain):
make_refnode(builder, fromdocname, obj[0], make_refnode(builder, fromdocname, obj[0],
name.replace('$', '_S_'), contnode, name))] name.replace('$', '_S_'), contnode, name))]
def get_objects(self): def get_objects(self) -> Iterator[Tuple[str, str, str, str, str, int]]:
# type: () -> Iterator[Tuple[str, str, str, str, str, int]]
for refname, (docname, type) in list(self.data['objects'].items()): for refname, (docname, type) in list(self.data['objects'].items()):
yield refname, refname, type, docname, \ yield refname, refname, type, docname, \
refname.replace('$', '_S_'), 1 refname.replace('$', '_S_'), 1
def get_full_qualified_name(self, node): def get_full_qualified_name(self, node: Element) -> str:
# type: (nodes.Element) -> str
modname = node.get('js:module') modname = node.get('js:module')
prefix = node.get('js:object') prefix = node.get('js:object')
target = node.get('reftarget') target = node.get('reftarget')
@ -407,8 +398,7 @@ class JavaScriptDomain(Domain):
return '.'.join(filter(None, [modname, prefix, target])) return '.'.join(filter(None, [modname, prefix, target]))
def setup(app): def setup(app: Sphinx) -> Dict[str, Any]:
# type: (Sphinx) -> Dict[str, Any]
app.add_domain(JavaScriptDomain) app.add_domain(JavaScriptDomain)
return { return {

View File

@ -9,12 +9,16 @@
""" """
import warnings import warnings
from typing import Any, Dict, Iterable, List, Tuple
from docutils import nodes from docutils import nodes
from docutils.nodes import Element, Node, system_message
from docutils.nodes import make_id from docutils.nodes import make_id
from sphinx.addnodes import pending_xref
from sphinx.deprecation import RemovedInSphinx40Warning from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.domains import Domain from sphinx.domains import Domain
from sphinx.environment import BuildEnvironment
from sphinx.locale import __ from sphinx.locale import __
from sphinx.roles import XRefRole from sphinx.roles import XRefRole
from sphinx.util import logging from sphinx.util import logging
@ -22,18 +26,16 @@ from sphinx.util.nodes import make_refnode
if False: if False:
# For type annotation # For type annotation
from typing import Any, Dict, Iterable, List, Tuple # NOQA from sphinx.application import Sphinx
from sphinx import addnodes # NOQA from sphinx.builders import Builder
from sphinx.application import Sphinx # NOQA
from sphinx.builders import Builder # NOQA
from sphinx.environment import BuildEnvironment # NOQA
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class MathReferenceRole(XRefRole): class MathReferenceRole(XRefRole):
def result_nodes(self, document, env, node, is_ref): def result_nodes(self, document: nodes.document, env: BuildEnvironment, node: Element,
# type: (nodes.document, BuildEnvironment, nodes.Element, bool) -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA is_ref: bool) -> Tuple[List[Node], List[system_message]]:
node['refdomain'] = 'math' node['refdomain'] = 'math'
return [node], [] return [node], []
@ -58,12 +60,10 @@ class MathDomain(Domain):
} }
@property @property
def equations(self): def equations(self) -> Dict[str, Tuple[str, int]]:
# type: () -> Dict[str, Tuple[str, int]]
return self.data.setdefault('objects', {}) # labelid -> (docname, eqno) return self.data.setdefault('objects', {}) # labelid -> (docname, eqno)
def note_equation(self, docname, labelid, location=None): def note_equation(self, docname: str, labelid: str, location: Any = None) -> None:
# type: (str, str, Any) -> None
if labelid in self.equations: if labelid in self.equations:
other = self.equations[labelid][0] other = self.equations[labelid][0]
logger.warning(__('duplicate label of equation %s, other instance in %s') % logger.warning(__('duplicate label of equation %s, other instance in %s') %
@ -71,31 +71,27 @@ class MathDomain(Domain):
self.equations[labelid] = (docname, self.env.new_serialno('eqno') + 1) self.equations[labelid] = (docname, self.env.new_serialno('eqno') + 1)
def get_equation_number_for(self, labelid): def get_equation_number_for(self, labelid: str) -> int:
# type: (str) -> int
if labelid in self.equations: if labelid in self.equations:
return self.equations[labelid][1] return self.equations[labelid][1]
else: else:
return None return None
def process_doc(self, env, docname, document): def process_doc(self, env: BuildEnvironment, docname: str,
# type: (BuildEnvironment, str, nodes.document) -> None document: nodes.document) -> None:
def math_node(node): def math_node(node: Node) -> bool:
# type: (nodes.Node) -> bool
return isinstance(node, (nodes.math, nodes.math_block)) return isinstance(node, (nodes.math, nodes.math_block))
self.data['has_equations'][docname] = any(document.traverse(math_node)) self.data['has_equations'][docname] = any(document.traverse(math_node))
def clear_doc(self, docname): def clear_doc(self, docname: str) -> None:
# type: (str) -> None
for equation_id, (doc, eqno) in list(self.equations.items()): for equation_id, (doc, eqno) in list(self.equations.items()):
if doc == docname: if doc == docname:
del self.equations[equation_id] del self.equations[equation_id]
self.data['has_equations'].pop(docname, None) self.data['has_equations'].pop(docname, None)
def merge_domaindata(self, docnames, otherdata): def merge_domaindata(self, docnames: Iterable[str], otherdata: Dict) -> None:
# type: (Iterable[str], Dict) -> None
for labelid, (doc, eqno) in otherdata['objects'].items(): for labelid, (doc, eqno) in otherdata['objects'].items():
if doc in docnames: if doc in docnames:
self.equations[labelid] = (doc, eqno) self.equations[labelid] = (doc, eqno)
@ -103,8 +99,9 @@ class MathDomain(Domain):
for docname in docnames: for docname in docnames:
self.data['has_equations'][docname] = otherdata['has_equations'][docname] self.data['has_equations'][docname] = otherdata['has_equations'][docname]
def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: "Builder",
# type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA typ: str, target: str, node: pending_xref, contnode: Element
) -> Element:
assert typ in ('eq', 'numref') assert typ in ('eq', 'numref')
docname, number = self.equations.get(target, (None, None)) docname, number = self.equations.get(target, (None, None))
if docname: if docname:
@ -131,20 +128,19 @@ class MathDomain(Domain):
else: else:
return None return None
def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode): def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: "Builder",
# type: (BuildEnvironment, str, Builder, str, addnodes.pending_xref, nodes.Element) -> List[Tuple[str, nodes.Element]] # NOQA target: str, node: pending_xref, contnode: Element
) -> List[Tuple[str, Element]]:
refnode = self.resolve_xref(env, fromdocname, builder, 'eq', target, node, contnode) refnode = self.resolve_xref(env, fromdocname, builder, 'eq', target, node, contnode)
if refnode is None: if refnode is None:
return [] return []
else: else:
return [('eq', refnode)] return [('eq', refnode)]
def get_objects(self): def get_objects(self) -> List:
# type: () -> List
return [] return []
def add_equation(self, env, docname, labelid): def add_equation(self, env: BuildEnvironment, docname: str, labelid: str) -> int:
# type: (BuildEnvironment, str, str) -> int
warnings.warn('MathDomain.add_equation() is deprecated.', warnings.warn('MathDomain.add_equation() is deprecated.',
RemovedInSphinx40Warning) RemovedInSphinx40Warning)
if labelid in self.equations: if labelid in self.equations:
@ -156,20 +152,17 @@ class MathDomain(Domain):
self.equations[labelid] = (docname, eqno) self.equations[labelid] = (docname, eqno)
return eqno return eqno
def get_next_equation_number(self, docname): def get_next_equation_number(self, docname: str) -> int:
# type: (str) -> int
warnings.warn('MathDomain.get_next_equation_number() is deprecated.', warnings.warn('MathDomain.get_next_equation_number() is deprecated.',
RemovedInSphinx40Warning) RemovedInSphinx40Warning)
targets = [eq for eq in self.equations.values() if eq[0] == docname] targets = [eq for eq in self.equations.values() if eq[0] == docname]
return len(targets) + 1 return len(targets) + 1
def has_equations(self): def has_equations(self) -> bool:
# type: () -> bool
return any(self.data['has_equations'].values()) return any(self.data['has_equations'].values())
def setup(app): def setup(app: "Sphinx") -> Dict[str, Any]:
# type: (Sphinx) -> Dict[str, Any]
app.add_domain(MathDomain) app.add_domain(MathDomain)
app.add_role('eq', MathReferenceRole(warn_dangling=True)) app.add_role('eq', MathReferenceRole(warn_dangling=True))

View File

@ -10,29 +10,29 @@
import re import re
import warnings import warnings
from typing import Any, Dict, Iterable, Iterator, List, Tuple, Type
from typing import cast from typing import cast
from docutils import nodes from docutils import nodes
from docutils.nodes import Element, Node
from docutils.parsers.rst import directives from docutils.parsers.rst import directives
from sphinx import addnodes from sphinx import addnodes
from sphinx.addnodes import pending_xref, desc_signature
from sphinx.application import Sphinx
from sphinx.builders import Builder
from sphinx.deprecation import RemovedInSphinx40Warning from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.directives import ObjectDescription from sphinx.directives import ObjectDescription
from sphinx.domains import Domain, ObjType, Index, IndexEntry from sphinx.domains import Domain, ObjType, Index, IndexEntry
from sphinx.environment import BuildEnvironment
from sphinx.locale import _, __ from sphinx.locale import _, __
from sphinx.roles import XRefRole from sphinx.roles import XRefRole
from sphinx.util import logging from sphinx.util import logging
from sphinx.util.docfields import Field, GroupedField, TypedField from sphinx.util.docfields import Field, GroupedField, TypedField
from sphinx.util.docutils import SphinxDirective from sphinx.util.docutils import SphinxDirective
from sphinx.util.nodes import make_refnode from sphinx.util.nodes import make_refnode
from sphinx.util.typing import TextlikeNode
if False:
# For type annotation
from typing import Any, Dict, Iterable, Iterator, List, Tuple, Type # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.builders import Builder # NOQA
from sphinx.environment import BuildEnvironment # NOQA
from sphinx.util.typing import TextlikeNode # NOQA
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -58,8 +58,7 @@ pairindextypes = {
} }
def _pseudo_parse_arglist(signode, arglist): def _pseudo_parse_arglist(signode: desc_signature, arglist: str) -> None:
# type: (addnodes.desc_signature, str) -> None
""""Parse" a list of arguments separated by commas. """"Parse" a list of arguments separated by commas.
Arguments can have "optional" annotations given by enclosing them in Arguments can have "optional" annotations given by enclosing them in
@ -67,7 +66,7 @@ def _pseudo_parse_arglist(signode, arglist):
string literal (e.g. default argument value). string literal (e.g. default argument value).
""" """
paramlist = addnodes.desc_parameterlist() paramlist = addnodes.desc_parameterlist()
stack = [paramlist] # type: List[nodes.Element] stack = [paramlist] # type: List[Element]
try: try:
for argument in arglist.split(','): for argument in arglist.split(','):
argument = argument.strip() argument = argument.strip()
@ -110,15 +109,9 @@ def _pseudo_parse_arglist(signode, arglist):
# This override allows our inline type specifiers to behave like :class: link # This override allows our inline type specifiers to behave like :class: link
# when it comes to handling "." and "~" prefixes. # when it comes to handling "." and "~" prefixes.
class PyXrefMixin: class PyXrefMixin:
def make_xref(self, def make_xref(self, rolename: str, domain: str, target: str,
rolename, # type: str innernode: Type[TextlikeNode] = nodes.emphasis,
domain, # type: str contnode: Node = None, env: BuildEnvironment = None) -> Node:
target, # type: str
innernode=nodes.emphasis, # type: Type[TextlikeNode]
contnode=None, # type: nodes.Node
env=None, # type: BuildEnvironment
):
# type: (...) -> nodes.Node
result = super().make_xref(rolename, domain, target, # type: ignore result = super().make_xref(rolename, domain, target, # type: ignore
innernode, contnode, env) innernode, contnode, env)
result['refspecific'] = True result['refspecific'] = True
@ -133,15 +126,9 @@ class PyXrefMixin:
break break
return result return result
def make_xrefs(self, def make_xrefs(self, rolename: str, domain: str, target: str,
rolename, # type: str innernode: Type[TextlikeNode] = nodes.emphasis,
domain, # type: str contnode: Node = None, env: BuildEnvironment = None) -> List[Node]:
target, # type: str
innernode=nodes.emphasis, # type: Type[TextlikeNode]
contnode=None, # type: nodes.Node
env=None, # type: BuildEnvironment
):
# type: (...) -> List[nodes.Node]
delims = r'(\s*[\[\]\(\),](?:\s*or\s)?\s*|\s+or\s+)' delims = r'(\s*[\[\]\(\),](?:\s*or\s)?\s*|\s+or\s+)'
delims_re = re.compile(delims) delims_re = re.compile(delims)
sub_targets = re.split(delims, target) sub_targets = re.split(delims, target)
@ -163,9 +150,9 @@ class PyXrefMixin:
class PyField(PyXrefMixin, Field): class PyField(PyXrefMixin, Field):
def make_xref(self, rolename, domain, target, def make_xref(self, rolename: str, domain: str, target: str,
innernode=nodes.emphasis, contnode=None, env=None): innernode: Type[TextlikeNode] = nodes.emphasis,
# type: (str, str, str, Type[TextlikeNode], nodes.Node, BuildEnvironment) -> nodes.Node # NOQA contnode: Node = None, env: BuildEnvironment = None) -> Node:
if rolename == 'class' and target == 'None': if rolename == 'class' and target == 'None':
# None is not a type, so use obj role instead. # None is not a type, so use obj role instead.
rolename = 'obj' rolename = 'obj'
@ -178,9 +165,9 @@ class PyGroupedField(PyXrefMixin, GroupedField):
class PyTypedField(PyXrefMixin, TypedField): class PyTypedField(PyXrefMixin, TypedField):
def make_xref(self, rolename, domain, target, def make_xref(self, rolename: str, domain: str, target: str,
innernode=nodes.emphasis, contnode=None, env=None): innernode: Type[TextlikeNode] = nodes.emphasis,
# type: (str, str, str, Type[TextlikeNode], nodes.Node, BuildEnvironment) -> nodes.Node # NOQA contnode: Node = None, env: BuildEnvironment = None) -> Node:
if rolename == 'class' and target == 'None': if rolename == 'class' and target == 'None':
# None is not a type, so use obj role instead. # None is not a type, so use obj role instead.
rolename = 'obj' rolename = 'obj'
@ -222,22 +209,19 @@ class PyObject(ObjectDescription):
allow_nesting = False allow_nesting = False
def get_signature_prefix(self, sig): def get_signature_prefix(self, sig: str) -> str:
# type: (str) -> str
"""May return a prefix to put before the object name in the """May return a prefix to put before the object name in the
signature. signature.
""" """
return '' return ''
def needs_arglist(self): def needs_arglist(self) -> bool:
# type: () -> bool
"""May return true if an empty argument list is to be generated even if """May return true if an empty argument list is to be generated even if
the document contains none. the document contains none.
""" """
return False return False
def handle_signature(self, sig, signode): def handle_signature(self, sig: str, signode: desc_signature) -> Tuple[str, str]:
# type: (str, addnodes.desc_signature) -> Tuple[str, str]
"""Transform a Python signature into RST nodes. """Transform a Python signature into RST nodes.
Return (fully qualified name of the thing, classname if any). Return (fully qualified name of the thing, classname if any).
@ -311,13 +295,12 @@ class PyObject(ObjectDescription):
return fullname, prefix return fullname, prefix
def get_index_text(self, modname, name): def get_index_text(self, modname: str, name: Tuple[str, str]) -> str:
# type: (str, Tuple[str, str]) -> str
"""Return the text for the index entry of the object.""" """Return the text for the index entry of the object."""
raise NotImplementedError('must be implemented in subclasses') raise NotImplementedError('must be implemented in subclasses')
def add_target_and_index(self, name_cls, sig, signode): def add_target_and_index(self, name_cls: Tuple[str, str], sig: str,
# type: (Tuple[str, str], str, addnodes.desc_signature) -> None signode: desc_signature) -> None:
modname = self.options.get('module', self.env.ref_context.get('py:module')) modname = self.options.get('module', self.env.ref_context.get('py:module'))
fullname = (modname and modname + '.' or '') + name_cls[0] fullname = (modname and modname + '.' or '') + name_cls[0]
# note target # note target
@ -336,8 +319,7 @@ class PyObject(ObjectDescription):
self.indexnode['entries'].append(('single', indextext, self.indexnode['entries'].append(('single', indextext,
fullname, '', None)) fullname, '', None))
def before_content(self): def before_content(self) -> None:
# type: () -> None
"""Handle object nesting before content """Handle object nesting before content
:py:class:`PyObject` represents Python language constructs. For :py:class:`PyObject` represents Python language constructs. For
@ -370,8 +352,7 @@ class PyObject(ObjectDescription):
modules.append(self.env.ref_context.get('py:module')) modules.append(self.env.ref_context.get('py:module'))
self.env.ref_context['py:module'] = self.options['module'] self.env.ref_context['py:module'] = self.options['module']
def after_content(self): def after_content(self) -> None:
# type: () -> None
"""Handle object de-nesting after content """Handle object de-nesting after content
If this class is a nestable object, removing the last nested class prefix If this class is a nestable object, removing the last nested class prefix
@ -402,19 +383,16 @@ class PyModulelevel(PyObject):
Description of an object on module level (functions, data). Description of an object on module level (functions, data).
""" """
def run(self): def run(self) -> List[Node]:
# type: () -> List[nodes.Node]
warnings.warn('PyClassmember is deprecated.', warnings.warn('PyClassmember is deprecated.',
RemovedInSphinx40Warning) RemovedInSphinx40Warning)
return super().run() return super().run()
def needs_arglist(self): def needs_arglist(self) -> bool:
# type: () -> bool
return self.objtype == 'function' return self.objtype == 'function'
def get_index_text(self, modname, name_cls): def get_index_text(self, modname: str, name_cls: Tuple[str, str]) -> str:
# type: (str, Tuple[str, str]) -> str
if self.objtype == 'function': if self.objtype == 'function':
if not modname: if not modname:
return _('%s() (built-in function)') % name_cls[0] return _('%s() (built-in function)') % name_cls[0]
@ -435,19 +413,16 @@ class PyFunction(PyObject):
'async': directives.flag, 'async': directives.flag,
}) })
def get_signature_prefix(self, sig): def get_signature_prefix(self, sig: str) -> str:
# type: (str) -> str
if 'async' in self.options: if 'async' in self.options:
return 'async ' return 'async '
else: else:
return '' return ''
def needs_arglist(self): def needs_arglist(self) -> bool:
# type: () -> bool
return True return True
def get_index_text(self, modname, name_cls): def get_index_text(self, modname: str, name_cls: Tuple[str, str]) -> str:
# type: (str, Tuple[str, str]) -> str
name, cls = name_cls name, cls = name_cls
if modname: if modname:
return _('%s() (in module %s)') % (name, modname) return _('%s() (in module %s)') % (name, modname)
@ -458,8 +433,7 @@ class PyFunction(PyObject):
class PyVariable(PyObject): class PyVariable(PyObject):
"""Description of a variable.""" """Description of a variable."""
def get_index_text(self, modname, name_cls): def get_index_text(self, modname: str, name_cls: Tuple[str, str]) -> str:
# type: (str, Tuple[str, str]) -> str
name, cls = name_cls name, cls = name_cls
if modname: if modname:
return _('%s (in module %s)') % (name, modname) return _('%s (in module %s)') % (name, modname)
@ -474,12 +448,10 @@ class PyClasslike(PyObject):
allow_nesting = True allow_nesting = True
def get_signature_prefix(self, sig): def get_signature_prefix(self, sig: str) -> str:
# type: (str) -> str
return self.objtype + ' ' return self.objtype + ' '
def get_index_text(self, modname, name_cls): def get_index_text(self, modname: str, name_cls: Tuple[str, str]) -> str:
# type: (str, Tuple[str, str]) -> str
if self.objtype == 'class': if self.objtype == 'class':
if not modname: if not modname:
return _('%s (built-in class)') % name_cls[0] return _('%s (built-in class)') % name_cls[0]
@ -495,27 +467,23 @@ class PyClassmember(PyObject):
Description of a class member (methods, attributes). Description of a class member (methods, attributes).
""" """
def run(self): def run(self) -> List[Node]:
# type: () -> List[nodes.Node]
warnings.warn('PyClassmember is deprecated.', warnings.warn('PyClassmember is deprecated.',
RemovedInSphinx40Warning) RemovedInSphinx40Warning)
return super().run() return super().run()
def needs_arglist(self): def needs_arglist(self) -> bool:
# type: () -> bool
return self.objtype.endswith('method') return self.objtype.endswith('method')
def get_signature_prefix(self, sig): def get_signature_prefix(self, sig: str) -> str:
# type: (str) -> str
if self.objtype == 'staticmethod': if self.objtype == 'staticmethod':
return 'static ' return 'static '
elif self.objtype == 'classmethod': elif self.objtype == 'classmethod':
return 'classmethod ' return 'classmethod '
return '' return ''
def get_index_text(self, modname, name_cls): def get_index_text(self, modname: str, name_cls: Tuple[str, str]) -> str:
# type: (str, Tuple[str, str]) -> str
name, cls = name_cls name, cls = name_cls
add_modules = self.env.config.add_module_names add_modules = self.env.config.add_module_names
if self.objtype == 'method': if self.objtype == 'method':
@ -584,15 +552,13 @@ class PyMethod(PyObject):
'staticmethod': directives.flag, 'staticmethod': directives.flag,
}) })
def needs_arglist(self): def needs_arglist(self) -> bool:
# type: () -> bool
if 'property' in self.options: if 'property' in self.options:
return False return False
else: else:
return True return True
def get_signature_prefix(self, sig): def get_signature_prefix(self, sig: str) -> str:
# type: (str) -> str
prefix = [] prefix = []
if 'abstractmethod' in self.options: if 'abstractmethod' in self.options:
prefix.append('abstract') prefix.append('abstract')
@ -610,8 +576,7 @@ class PyMethod(PyObject):
else: else:
return '' return ''
def get_index_text(self, modname, name_cls): def get_index_text(self, modname: str, name_cls: Tuple[str, str]) -> str:
# type: (str, Tuple[str, str]) -> str
name, cls = name_cls name, cls = name_cls
try: try:
clsname, methname = name.rsplit('.', 1) clsname, methname = name.rsplit('.', 1)
@ -638,8 +603,7 @@ class PyClassMethod(PyMethod):
option_spec = PyObject.option_spec.copy() option_spec = PyObject.option_spec.copy()
def run(self): def run(self) -> List[Node]:
# type: () -> List[nodes.Node]
self.name = 'py:method' self.name = 'py:method'
self.options['classmethod'] = True self.options['classmethod'] = True
@ -651,8 +615,7 @@ class PyStaticMethod(PyMethod):
option_spec = PyObject.option_spec.copy() option_spec = PyObject.option_spec.copy()
def run(self): def run(self) -> List[Node]:
# type: () -> List[nodes.Node]
self.name = 'py:method' self.name = 'py:method'
self.options['staticmethod'] = True self.options['staticmethod'] = True
@ -662,8 +625,7 @@ class PyStaticMethod(PyMethod):
class PyAttribute(PyObject): class PyAttribute(PyObject):
"""Description of an attribute.""" """Description of an attribute."""
def get_index_text(self, modname, name_cls): def get_index_text(self, modname: str, name_cls: Tuple[str, str]) -> str:
# type: (str, Tuple[str, str]) -> str
name, cls = name_cls name, cls = name_cls
try: try:
clsname, attrname = name.rsplit('.', 1) clsname, attrname = name.rsplit('.', 1)
@ -682,14 +644,12 @@ class PyDecoratorMixin:
""" """
Mixin for decorator directives. Mixin for decorator directives.
""" """
def handle_signature(self, sig, signode): def handle_signature(self, sig: str, signode: desc_signature) -> Tuple[str, str]:
# type: (str, addnodes.desc_signature) -> Tuple[str, str]
ret = super().handle_signature(sig, signode) # type: ignore ret = super().handle_signature(sig, signode) # type: ignore
signode.insert(0, addnodes.desc_addname('@', '@')) signode.insert(0, addnodes.desc_addname('@', '@'))
return ret return ret
def needs_arglist(self): def needs_arglist(self) -> bool:
# type: () -> bool
return False return False
@ -697,8 +657,7 @@ class PyDecoratorFunction(PyDecoratorMixin, PyModulelevel):
""" """
Directive to mark functions meant to be used as decorators. Directive to mark functions meant to be used as decorators.
""" """
def run(self): def run(self) -> List[Node]:
# type: () -> List[nodes.Node]
# a decorator function is a function after all # a decorator function is a function after all
self.name = 'py:function' self.name = 'py:function'
return super().run() return super().run()
@ -708,8 +667,7 @@ class PyDecoratorMethod(PyDecoratorMixin, PyClassmember):
""" """
Directive to mark methods meant to be used as decorators. Directive to mark methods meant to be used as decorators.
""" """
def run(self): def run(self) -> List[Node]:
# type: () -> List[nodes.Node]
self.name = 'py:method' self.name = 'py:method'
return super().run() return super().run()
@ -730,14 +688,13 @@ class PyModule(SphinxDirective):
'deprecated': directives.flag, 'deprecated': directives.flag,
} }
def run(self): def run(self) -> List[Node]:
# type: () -> List[nodes.Node]
domain = cast(PythonDomain, self.env.get_domain('py')) domain = cast(PythonDomain, self.env.get_domain('py'))
modname = self.arguments[0].strip() modname = self.arguments[0].strip()
noindex = 'noindex' in self.options noindex = 'noindex' in self.options
self.env.ref_context['py:module'] = modname self.env.ref_context['py:module'] = modname
ret = [] # type: List[nodes.Node] ret = [] # type: List[Node]
if not noindex: if not noindex:
# note module to the domain # note module to the domain
domain.note_module(modname, domain.note_module(modname,
@ -771,8 +728,7 @@ class PyCurrentModule(SphinxDirective):
final_argument_whitespace = False final_argument_whitespace = False
option_spec = {} # type: Dict option_spec = {} # type: Dict
def run(self): def run(self) -> List[Node]:
# type: () -> List[nodes.Node]
modname = self.arguments[0].strip() modname = self.arguments[0].strip()
if modname == 'None': if modname == 'None':
self.env.ref_context.pop('py:module', None) self.env.ref_context.pop('py:module', None)
@ -782,8 +738,8 @@ class PyCurrentModule(SphinxDirective):
class PyXRefRole(XRefRole): class PyXRefRole(XRefRole):
def process_link(self, env, refnode, has_explicit_title, title, target): def process_link(self, env: BuildEnvironment, refnode: Element,
# type: (BuildEnvironment, nodes.Element, bool, str, str) -> Tuple[str, str] has_explicit_title: bool, title: str, target: str) -> Tuple[str, str]:
refnode['py:module'] = env.ref_context.get('py:module') refnode['py:module'] = env.ref_context.get('py:module')
refnode['py:class'] = env.ref_context.get('py:class') refnode['py:class'] = env.ref_context.get('py:class')
if not has_explicit_title: if not has_explicit_title:
@ -813,8 +769,8 @@ class PythonModuleIndex(Index):
localname = _('Python Module Index') localname = _('Python Module Index')
shortname = _('modules') shortname = _('modules')
def generate(self, docnames=None): def generate(self, docnames: Iterable[str] = None
# type: (Iterable[str]) -> Tuple[List[Tuple[str, List[IndexEntry]]], bool] ) -> Tuple[List[Tuple[str, List[IndexEntry]]], bool]:
content = {} # type: Dict[str, List[IndexEntry]] content = {} # type: Dict[str, List[IndexEntry]]
# list of prefixes to ignore # list of prefixes to ignore
ignores = None # type: List[str] ignores = None # type: List[str]
@ -928,12 +884,10 @@ class PythonDomain(Domain):
] ]
@property @property
def objects(self): def objects(self) -> Dict[str, Tuple[str, str]]:
# type: () -> Dict[str, Tuple[str, str]]
return self.data.setdefault('objects', {}) # fullname -> docname, objtype return self.data.setdefault('objects', {}) # fullname -> docname, objtype
def note_object(self, name, objtype, location=None): def note_object(self, name: str, objtype: str, location: Any = None) -> None:
# type: (str, str, Any) -> None
"""Note a python object for cross reference. """Note a python object for cross reference.
.. versionadded:: 2.1 .. versionadded:: 2.1
@ -946,20 +900,17 @@ class PythonDomain(Domain):
self.objects[name] = (self.env.docname, objtype) self.objects[name] = (self.env.docname, objtype)
@property @property
def modules(self): def modules(self) -> Dict[str, Tuple[str, str, str, bool]]:
# type: () -> Dict[str, Tuple[str, str, str, bool]]
return self.data.setdefault('modules', {}) # modname -> docname, synopsis, platform, deprecated # NOQA return self.data.setdefault('modules', {}) # modname -> docname, synopsis, platform, deprecated # NOQA
def note_module(self, name, synopsis, platform, deprecated): def note_module(self, name: str, synopsis: str, platform: str, deprecated: bool) -> None:
# type: (str, str, str, bool) -> None
"""Note a python module for cross reference. """Note a python module for cross reference.
.. versionadded:: 2.1 .. versionadded:: 2.1
""" """
self.modules[name] = (self.env.docname, synopsis, platform, deprecated) self.modules[name] = (self.env.docname, synopsis, platform, deprecated)
def clear_doc(self, docname): def clear_doc(self, docname: str) -> None:
# type: (str) -> None
for fullname, (fn, _l) in list(self.objects.items()): for fullname, (fn, _l) in list(self.objects.items()):
if fn == docname: if fn == docname:
del self.objects[fullname] del self.objects[fullname]
@ -967,8 +918,7 @@ class PythonDomain(Domain):
if fn == docname: if fn == docname:
del self.modules[modname] del self.modules[modname]
def merge_domaindata(self, docnames, otherdata): def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None:
# type: (List[str], Dict) -> None
# XXX check duplicates? # XXX check duplicates?
for fullname, (fn, objtype) in otherdata['objects'].items(): for fullname, (fn, objtype) in otherdata['objects'].items():
if fn in docnames: if fn in docnames:
@ -977,8 +927,8 @@ class PythonDomain(Domain):
if data[0] in docnames: if data[0] in docnames:
self.modules[modname] = data self.modules[modname] = data
def find_obj(self, env, modname, classname, name, type, searchmode=0): def find_obj(self, env: BuildEnvironment, modname: str, classname: str,
# type: (BuildEnvironment, str, str, str, str, int) -> List[Tuple[str, Any]] name: str, type: str, searchmode: int = 0) -> List[Tuple[str, Any]]:
"""Find a Python object for "name", perhaps using the given module """Find a Python object for "name", perhaps using the given module
and/or classname. Returns a list of (name, object entry) tuples. and/or classname. Returns a list of (name, object entry) tuples.
""" """
@ -1040,9 +990,9 @@ class PythonDomain(Domain):
matches.append((newname, self.objects[newname])) matches.append((newname, self.objects[newname]))
return matches return matches
def resolve_xref(self, env, fromdocname, builder, def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
type, target, node, contnode): type: str, target: str, node: pending_xref, contnode: Element
# type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA ) -> Element:
modname = node.get('py:module') modname = node.get('py:module')
clsname = node.get('py:class') clsname = node.get('py:class')
searchmode = node.hasattr('refspecific') and 1 or 0 searchmode = node.hasattr('refspecific') and 1 or 0
@ -1061,12 +1011,12 @@ class PythonDomain(Domain):
else: else:
return make_refnode(builder, fromdocname, obj[0], name, contnode, name) return make_refnode(builder, fromdocname, obj[0], name, contnode, name)
def resolve_any_xref(self, env, fromdocname, builder, target, def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
node, contnode): target: str, node: pending_xref, contnode: Element
# type: (BuildEnvironment, str, Builder, str, addnodes.pending_xref, nodes.Element) -> List[Tuple[str, nodes.Element]] # NOQA ) -> List[Tuple[str, Element]]:
modname = node.get('py:module') modname = node.get('py:module')
clsname = node.get('py:class') clsname = node.get('py:class')
results = [] # type: List[Tuple[str, nodes.Element]] results = [] # type: List[Tuple[str, Element]]
# always search in "refspecific" mode with the :any: role # always search in "refspecific" mode with the :any: role
matches = self.find_obj(env, modname, clsname, target, None, 1) matches = self.find_obj(env, modname, clsname, target, None, 1)
@ -1081,8 +1031,8 @@ class PythonDomain(Domain):
contnode, name))) contnode, name)))
return results return results
def _make_module_refnode(self, builder, fromdocname, name, contnode): def _make_module_refnode(self, builder: Builder, fromdocname: str, name: str,
# type: (Builder, str, str, nodes.Node) -> nodes.Element contnode: Node) -> Element:
# get additional info for modules # get additional info for modules
docname, synopsis, platform, deprecated = self.modules[name] docname, synopsis, platform, deprecated = self.modules[name]
title = name title = name
@ -1095,16 +1045,14 @@ class PythonDomain(Domain):
return make_refnode(builder, fromdocname, docname, return make_refnode(builder, fromdocname, docname,
'module-' + name, contnode, title) 'module-' + name, contnode, title)
def get_objects(self): def get_objects(self) -> Iterator[Tuple[str, str, str, str, str, int]]:
# type: () -> Iterator[Tuple[str, str, str, str, str, int]]
for modname, info in self.modules.items(): for modname, info in self.modules.items():
yield (modname, modname, 'module', info[0], 'module-' + modname, 0) yield (modname, modname, 'module', info[0], 'module-' + modname, 0)
for refname, (docname, type) in self.objects.items(): for refname, (docname, type) in self.objects.items():
if type != 'module': # modules are already handled if type != 'module': # modules are already handled
yield (refname, refname, type, docname, refname, 1) yield (refname, refname, type, docname, refname, 1)
def get_full_qualified_name(self, node): def get_full_qualified_name(self, node: Element) -> str:
# type: (nodes.Element) -> str
modname = node.get('py:module') modname = node.get('py:module')
clsname = node.get('py:class') clsname = node.get('py:class')
target = node.get('reftarget') target = node.get('reftarget')
@ -1114,8 +1062,7 @@ class PythonDomain(Domain):
return '.'.join(filter(None, [modname, clsname, target])) return '.'.join(filter(None, [modname, clsname, target]))
def setup(app): def setup(app: Sphinx) -> Dict[str, Any]:
# type: (Sphinx) -> Dict[str, Any]
app.add_domain(PythonDomain) app.add_domain(PythonDomain)
return { return {

View File

@ -9,26 +9,24 @@
""" """
import re import re
from typing import Any, Dict, Iterator, List, Tuple
from typing import cast from typing import cast
from docutils.nodes import Element
from docutils.parsers.rst import directives from docutils.parsers.rst import directives
from sphinx import addnodes from sphinx import addnodes
from sphinx.addnodes import desc_signature, pending_xref
from sphinx.application import Sphinx
from sphinx.builders import Builder
from sphinx.directives import ObjectDescription from sphinx.directives import ObjectDescription
from sphinx.domains import Domain, ObjType from sphinx.domains import Domain, ObjType
from sphinx.environment import BuildEnvironment
from sphinx.locale import _, __ from sphinx.locale import _, __
from sphinx.roles import XRefRole from sphinx.roles import XRefRole
from sphinx.util import logging from sphinx.util import logging
from sphinx.util.nodes import make_refnode from sphinx.util.nodes import make_refnode
if False:
# For type annotation
from typing import Any, Dict, Iterator, List, Tuple # NOQA
from docutils import nodes # NOQA
from sphinx.application import Sphinx # NOQA
from sphinx.builders import Builder # NOQA
from sphinx.environment import BuildEnvironment # NOQA
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -40,8 +38,7 @@ class ReSTMarkup(ObjectDescription):
Description of generic reST markup. Description of generic reST markup.
""" """
def add_target_and_index(self, name, sig, signode): def add_target_and_index(self, name: str, sig: str, signode: desc_signature) -> None:
# type: (str, str, addnodes.desc_signature) -> None
targetname = self.objtype + '-' + name targetname = self.objtype + '-' + name
if targetname not in self.state.document.ids: if targetname not in self.state.document.ids:
signode['names'].append(targetname) signode['names'].append(targetname)
@ -57,13 +54,11 @@ class ReSTMarkup(ObjectDescription):
self.indexnode['entries'].append(('single', indextext, self.indexnode['entries'].append(('single', indextext,
targetname, '', None)) targetname, '', None))
def get_index_text(self, objectname, name): def get_index_text(self, objectname: str, name: str) -> str:
# type: (str, str) -> str
return '' return ''
def parse_directive(d): def parse_directive(d: str) -> Tuple[str, str]:
# type: (str) -> Tuple[str, str]
"""Parse a directive signature. """Parse a directive signature.
Returns (directive, arguments) string tuple. If no arguments are given, Returns (directive, arguments) string tuple. If no arguments are given,
@ -87,8 +82,7 @@ class ReSTDirective(ReSTMarkup):
""" """
Description of a reST directive. Description of a reST directive.
""" """
def handle_signature(self, sig, signode): def handle_signature(self, sig: str, signode: desc_signature) -> str:
# type: (str, addnodes.desc_signature) -> str
name, args = parse_directive(sig) name, args = parse_directive(sig)
desc_name = '.. %s::' % name desc_name = '.. %s::' % name
signode += addnodes.desc_name(desc_name, desc_name) signode += addnodes.desc_name(desc_name, desc_name)
@ -96,18 +90,15 @@ class ReSTDirective(ReSTMarkup):
signode += addnodes.desc_addname(args, args) signode += addnodes.desc_addname(args, args)
return name return name
def get_index_text(self, objectname, name): def get_index_text(self, objectname: str, name: str) -> str:
# type: (str, str) -> str
return _('%s (directive)') % name return _('%s (directive)') % name
def before_content(self): def before_content(self) -> None:
# type: () -> None
if self.names: if self.names:
directives = self.env.ref_context.setdefault('rst:directives', []) directives = self.env.ref_context.setdefault('rst:directives', [])
directives.append(self.names[0]) directives.append(self.names[0])
def after_content(self): def after_content(self) -> None:
# type: () -> None
directives = self.env.ref_context.setdefault('rst:directives', []) directives = self.env.ref_context.setdefault('rst:directives', [])
if directives: if directives:
directives.pop() directives.pop()
@ -122,8 +113,7 @@ class ReSTDirectiveOption(ReSTMarkup):
'type': directives.unchanged, 'type': directives.unchanged,
}) })
def handle_signature(self, sig, signode): def handle_signature(self, sig: str, signode: desc_signature) -> str:
# type: (str, addnodes.desc_signature) -> str
try: try:
name, argument = re.split(r'\s*:\s+', sig.strip(), 1) name, argument = re.split(r'\s*:\s+', sig.strip(), 1)
except ValueError: except ValueError:
@ -137,8 +127,7 @@ class ReSTDirectiveOption(ReSTMarkup):
signode += addnodes.desc_annotation(text, text) signode += addnodes.desc_annotation(text, text)
return name return name
def add_target_and_index(self, name, sig, signode): def add_target_and_index(self, name: str, sig: str, signode: desc_signature) -> None:
# type: (str, str, addnodes.desc_signature) -> None
directive_name = self.current_directive directive_name = self.current_directive
targetname = '-'.join([self.objtype, self.current_directive, name]) targetname = '-'.join([self.objtype, self.current_directive, name])
if targetname not in self.state.document.ids: if targetname not in self.state.document.ids:
@ -162,8 +151,7 @@ class ReSTDirectiveOption(ReSTMarkup):
self.indexnode['entries'].append(('single', text, targetname, '', key)) self.indexnode['entries'].append(('single', text, targetname, '', key))
@property @property
def current_directive(self): def current_directive(self) -> str:
# type: () -> str
directives = self.env.ref_context.get('rst:directives') directives = self.env.ref_context.get('rst:directives')
if directives: if directives:
return directives[-1] return directives[-1]
@ -175,13 +163,11 @@ class ReSTRole(ReSTMarkup):
""" """
Description of a reST role. Description of a reST role.
""" """
def handle_signature(self, sig, signode): def handle_signature(self, sig: str, signode: desc_signature) -> str:
# type: (str, addnodes.desc_signature) -> str
signode += addnodes.desc_name(':%s:' % sig, ':%s:' % sig) signode += addnodes.desc_name(':%s:' % sig, ':%s:' % sig)
return sig return sig
def get_index_text(self, objectname, name): def get_index_text(self, objectname: str, name: str) -> str:
# type: (str, str) -> str
return _('%s (role)') % name return _('%s (role)') % name
@ -209,12 +195,10 @@ class ReSTDomain(Domain):
} # type: Dict[str, Dict[Tuple[str, str], str]] } # type: Dict[str, Dict[Tuple[str, str], str]]
@property @property
def objects(self): def objects(self) -> Dict[Tuple[str, str], str]:
# type: () -> Dict[Tuple[str, str], str]
return self.data.setdefault('objects', {}) # (objtype, fullname) -> docname return self.data.setdefault('objects', {}) # (objtype, fullname) -> docname
def note_object(self, objtype, name, location=None): def note_object(self, objtype: str, name: str, location: Any = None) -> None:
# type: (str, str, Any) -> None
if (objtype, name) in self.objects: if (objtype, name) in self.objects:
docname = self.objects[objtype, name] docname = self.objects[objtype, name]
logger.warning(__('duplicate description of %s %s, other instance in %s') % logger.warning(__('duplicate description of %s %s, other instance in %s') %
@ -222,21 +206,20 @@ class ReSTDomain(Domain):
self.objects[objtype, name] = self.env.docname self.objects[objtype, name] = self.env.docname
def clear_doc(self, docname): def clear_doc(self, docname: str) -> None:
# type: (str) -> None
for (typ, name), doc in list(self.objects.items()): for (typ, name), doc in list(self.objects.items()):
if doc == docname: if doc == docname:
del self.objects[typ, name] del self.objects[typ, name]
def merge_domaindata(self, docnames, otherdata): def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None:
# type: (List[str], Dict) -> None
# XXX check duplicates # XXX check duplicates
for (typ, name), doc in otherdata['objects'].items(): for (typ, name), doc in otherdata['objects'].items():
if doc in docnames: if doc in docnames:
self.objects[typ, name] = doc self.objects[typ, name] = doc
def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
# type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA typ: str, target: str, node: pending_xref, contnode: Element
) -> Element:
objtypes = self.objtypes_for_role(typ) objtypes = self.objtypes_for_role(typ)
for objtype in objtypes: for objtype in objtypes:
todocname = self.objects.get((objtype, target)) todocname = self.objects.get((objtype, target))
@ -246,9 +229,10 @@ class ReSTDomain(Domain):
contnode, target + ' ' + objtype) contnode, target + ' ' + objtype)
return None return None
def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode): def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
# type: (BuildEnvironment, str, Builder, str, addnodes.pending_xref, nodes.Element) -> List[Tuple[str, nodes.Element]] # NOQA target: str, node: pending_xref, contnode: Element
results = [] # type: List[Tuple[str, nodes.Element]] ) -> List[Tuple[str, Element]]:
results = [] # type: List[Tuple[str, Element]]
for objtype in self.object_types: for objtype in self.object_types:
todocname = self.objects.get((objtype, target)) todocname = self.objects.get((objtype, target))
if todocname: if todocname:
@ -258,14 +242,12 @@ class ReSTDomain(Domain):
contnode, target + ' ' + objtype))) contnode, target + ' ' + objtype)))
return results return results
def get_objects(self): def get_objects(self) -> Iterator[Tuple[str, str, str, str, str, int]]:
# type: () -> Iterator[Tuple[str, str, str, str, str, int]]
for (typ, name), docname in self.data['objects'].items(): for (typ, name), docname in self.data['objects'].items():
yield name, name, typ, docname, typ + '-' + name, 1 yield name, name, typ, docname, typ + '-' + name, 1
def setup(app): def setup(app: Sphinx) -> Dict[str, Any]:
# type: (Sphinx) -> Dict[str, Any]
app.add_domain(ReSTDomain) app.add_domain(ReSTDomain)
return { return {

View File

@ -12,13 +12,16 @@ import re
import unicodedata import unicodedata
import warnings import warnings
from copy import copy from copy import copy
from typing import Any, Callable, Dict, Iterable, Iterator, List, Optional, Tuple, Type, Union
from typing import cast from typing import cast
from docutils import nodes from docutils import nodes
from docutils.parsers.rst import directives from docutils.nodes import Element, Node, system_message
from docutils.parsers.rst import Directive, directives
from docutils.statemachine import StringList from docutils.statemachine import StringList
from sphinx import addnodes from sphinx import addnodes
from sphinx.addnodes import desc_signature, pending_xref
from sphinx.deprecation import RemovedInSphinx40Warning from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.directives import ObjectDescription from sphinx.directives import ObjectDescription
from sphinx.domains import Domain, ObjType from sphinx.domains import Domain, ObjType
@ -27,15 +30,13 @@ from sphinx.roles import XRefRole
from sphinx.util import ws_re, logging, docname_join from sphinx.util import ws_re, logging, docname_join
from sphinx.util.docutils import SphinxDirective from sphinx.util.docutils import SphinxDirective
from sphinx.util.nodes import clean_astext, make_refnode from sphinx.util.nodes import clean_astext, make_refnode
from sphinx.util.typing import RoleFunction
if False: if False:
# For type annotation # For type annotation
from typing import Any, Callable, Dict, Iterable, Iterator, List, Tuple, Type, Union # NOQA from sphinx.application import Sphinx
from docutils.parsers.rst import Directive # NOQA from sphinx.builders import Builder
from sphinx.application import Sphinx # NOQA from sphinx.environment import BuildEnvironment
from sphinx.builders import Builder # NOQA
from sphinx.environment import BuildEnvironment # NOQA
from sphinx.util.typing import RoleFunction # NOQA
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -51,10 +52,9 @@ class GenericObject(ObjectDescription):
A generic x-ref directive registered with Sphinx.add_object_type(). A generic x-ref directive registered with Sphinx.add_object_type().
""" """
indextemplate = '' indextemplate = ''
parse_node = None # type: Callable[[GenericObject, BuildEnvironment, str, addnodes.desc_signature], str] # NOQA parse_node = None # type: Callable[[GenericObject, BuildEnvironment, str, desc_signature], str] # NOQA
def handle_signature(self, sig, signode): def handle_signature(self, sig: str, signode: desc_signature) -> str:
# type: (str, addnodes.desc_signature) -> str
if self.parse_node: if self.parse_node:
name = self.parse_node(self.env, sig, signode) name = self.parse_node(self.env, sig, signode)
else: else:
@ -64,8 +64,7 @@ class GenericObject(ObjectDescription):
name = ws_re.sub('', sig) name = ws_re.sub('', sig)
return name return name
def add_target_and_index(self, name, sig, signode): def add_target_and_index(self, name: str, sig: str, signode: desc_signature) -> None:
# type: (str, str, addnodes.desc_signature) -> None
targetname = '%s-%s' % (self.objtype, name) targetname = '%s-%s' % (self.objtype, name)
signode['ids'].append(targetname) signode['ids'].append(targetname)
self.state.document.note_explicit_target(signode) self.state.document.note_explicit_target(signode)
@ -93,8 +92,8 @@ class EnvVarXRefRole(XRefRole):
Cross-referencing role for environment variables (adds an index entry). Cross-referencing role for environment variables (adds an index entry).
""" """
def result_nodes(self, document, env, node, is_ref): def result_nodes(self, document: nodes.document, env: "BuildEnvironment", node: Element,
# type: (nodes.document, BuildEnvironment, nodes.Element, bool) -> Tuple[List[nodes.Node], List[nodes.system_message]] # NOQA is_ref: bool) -> Tuple[List[Node], List[system_message]]:
if not is_ref: if not is_ref:
return [node], [] return [node], []
varname = node['reftarget'] varname = node['reftarget']
@ -121,8 +120,7 @@ class Target(SphinxDirective):
final_argument_whitespace = True final_argument_whitespace = True
option_spec = {} # type: Dict option_spec = {} # type: Dict
def run(self): def run(self) -> List[Node]:
# type: () -> List[nodes.Node]
# normalize whitespace in fullname like XRefRole does # normalize whitespace in fullname like XRefRole does
fullname = ws_re.sub(' ', self.arguments[0].strip()) fullname = ws_re.sub(' ', self.arguments[0].strip())
targetname = '%s-%s' % (self.name, fullname) targetname = '%s-%s' % (self.name, fullname)
@ -154,8 +152,7 @@ class Cmdoption(ObjectDescription):
Description of a command-line option (.. option). Description of a command-line option (.. option).
""" """
def handle_signature(self, sig, signode): def handle_signature(self, sig: str, signode: desc_signature) -> str:
# type: (str, addnodes.desc_signature) -> str
"""Transform an option description into RST nodes.""" """Transform an option description into RST nodes."""
count = 0 count = 0
firstname = '' firstname = ''
@ -183,8 +180,7 @@ class Cmdoption(ObjectDescription):
raise ValueError raise ValueError
return firstname return firstname
def add_target_and_index(self, firstname, sig, signode): def add_target_and_index(self, firstname: str, sig: str, signode: desc_signature) -> None:
# type: (str, str, addnodes.desc_signature) -> None
currprogram = self.env.ref_context.get('std:program') currprogram = self.env.ref_context.get('std:program')
for optname in signode.get('allnames', []): for optname in signode.get('allnames', []):
targetname = optname.replace('/', '-') targetname = optname.replace('/', '-')
@ -220,8 +216,7 @@ class Program(SphinxDirective):
final_argument_whitespace = True final_argument_whitespace = True
option_spec = {} # type: Dict option_spec = {} # type: Dict
def run(self): def run(self) -> List[Node]:
# type: () -> List[nodes.Node]
program = ws_re.sub('-', self.arguments[0].strip()) program = ws_re.sub('-', self.arguments[0].strip())
if program == 'None': if program == 'None':
self.env.ref_context.pop('std:program', None) self.env.ref_context.pop('std:program', None)
@ -231,21 +226,20 @@ class Program(SphinxDirective):
class OptionXRefRole(XRefRole): class OptionXRefRole(XRefRole):
def process_link(self, env, refnode, has_explicit_title, title, target): def process_link(self, env: "BuildEnvironment", refnode: Element, has_explicit_title: bool,
# type: (BuildEnvironment, nodes.Element, bool, str, str) -> Tuple[str, str] title: str, target: str) -> Tuple[str, str]:
refnode['std:program'] = env.ref_context.get('std:program') refnode['std:program'] = env.ref_context.get('std:program')
return title, target return title, target
def split_term_classifiers(line): def split_term_classifiers(line: str) -> List[Optional[str]]:
# type: (str) -> List[Union[str, None]]
# split line into a term and classifiers. if no classifier, None is used.. # split line into a term and classifiers. if no classifier, None is used..
parts = re.split(' +: +', line) + [None] parts = re.split(' +: +', line) + [None]
return parts return parts
def make_glossary_term(env, textnodes, index_key, source, lineno, new_id=None): def make_glossary_term(env: "BuildEnvironment", textnodes: Iterable[Node], index_key: str,
# type: (BuildEnvironment, Iterable[nodes.Node], str, str, int, str) -> nodes.term source: str, lineno: int, new_id: str = None) -> nodes.term:
# get a text-only representation of the term and register it # get a text-only representation of the term and register it
# as a cross-reference target # as a cross-reference target
term = nodes.term('', '', *textnodes) term = nodes.term('', '', *textnodes)
@ -291,8 +285,7 @@ class Glossary(SphinxDirective):
'sorted': directives.flag, 'sorted': directives.flag,
} }
def run(self): def run(self) -> List[Node]:
# type: () -> List[nodes.Node]
node = addnodes.glossary() node = addnodes.glossary()
node.document = self.state.document node.document = self.state.document
@ -398,16 +391,15 @@ class Glossary(SphinxDirective):
return messages + [node] return messages + [node]
def token_xrefs(text): def token_xrefs(text: str) -> List[Node]:
# type: (str) -> List[nodes.Node]
retnodes = [] # type: List[nodes.Node] retnodes = [] # type: List[nodes.Node]
pos = 0 pos = 0
for m in token_re.finditer(text): for m in token_re.finditer(text):
if m.start() > pos: if m.start() > pos:
txt = text[pos:m.start()] txt = text[pos:m.start()]
retnodes.append(nodes.Text(txt, txt)) retnodes.append(nodes.Text(txt, txt))
refnode = addnodes.pending_xref( refnode = pending_xref(m.group(1), reftype='token', refdomain='std',
m.group(1), reftype='token', refdomain='std', reftarget=m.group(1)) reftarget=m.group(1))
refnode += nodes.literal(m.group(1), m.group(1), classes=['xref']) refnode += nodes.literal(m.group(1), m.group(1), classes=['xref'])
retnodes.append(refnode) retnodes.append(refnode)
pos = m.end() pos = m.end()
@ -427,8 +419,7 @@ class ProductionList(SphinxDirective):
final_argument_whitespace = True final_argument_whitespace = True
option_spec = {} # type: Dict option_spec = {} # type: Dict
def run(self): def run(self) -> List[Node]:
# type: () -> List[nodes.Node]
domain = cast(StandardDomain, self.env.get_domain('std')) domain = cast(StandardDomain, self.env.get_domain('std'))
node = addnodes.productionlist() # type: nodes.Element node = addnodes.productionlist() # type: nodes.Element
i = 0 i = 0
@ -533,8 +524,7 @@ class StandardDomain(Domain):
nodes.container: ('code-block', None), nodes.container: ('code-block', None),
} # type: Dict[Type[nodes.Node], Tuple[str, Callable]] } # type: Dict[Type[nodes.Node], Tuple[str, Callable]]
def __init__(self, env): def __init__(self, env: "BuildEnvironment") -> None:
# type: (BuildEnvironment) -> None
super().__init__(env) super().__init__(env)
# set up enumerable nodes # set up enumerable nodes
@ -543,27 +533,22 @@ class StandardDomain(Domain):
self.enumerable_nodes[node] = settings self.enumerable_nodes[node] = settings
@property @property
def objects(self): def objects(self) -> Dict[Tuple[str, str], Tuple[str, str]]:
# type: () -> Dict[Tuple[str, str], Tuple[str, str]]
return self.data.setdefault('objects', {}) # (objtype, name) -> docname, labelid return self.data.setdefault('objects', {}) # (objtype, name) -> docname, labelid
@property @property
def progoptions(self): def progoptions(self) -> Dict[Tuple[str, str], Tuple[str, str]]:
# type: () -> Dict[Tuple[str, str], Tuple[str, str]]
return self.data.setdefault('progoptions', {}) # (program, name) -> docname, labelid return self.data.setdefault('progoptions', {}) # (program, name) -> docname, labelid
@property @property
def labels(self): def labels(self) -> Dict[str, Tuple[str, str, str]]:
# type: () -> Dict[str, Tuple[str, str, str]]
return self.data.setdefault('labels', {}) # labelname -> docname, labelid, sectionname return self.data.setdefault('labels', {}) # labelname -> docname, labelid, sectionname
@property @property
def anonlabels(self): def anonlabels(self) -> Dict[str, Tuple[str, str]]:
# type: () -> Dict[str, Tuple[str, str]]
return self.data.setdefault('anonlabels', {}) # labelname -> docname, labelid return self.data.setdefault('anonlabels', {}) # labelname -> docname, labelid
def clear_doc(self, docname): def clear_doc(self, docname: str) -> None:
# type: (str) -> None
key = None # type: Any key = None # type: Any
for key, (fn, _l) in list(self.progoptions.items()): for key, (fn, _l) in list(self.progoptions.items()):
if fn == docname: if fn == docname:
@ -578,8 +563,7 @@ class StandardDomain(Domain):
if fn == docname: if fn == docname:
del self.anonlabels[key] del self.anonlabels[key]
def merge_domaindata(self, docnames, otherdata): def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None:
# type: (List[str], Dict) -> None
# XXX duplicates? # XXX duplicates?
for key, data in otherdata['progoptions'].items(): for key, data in otherdata['progoptions'].items():
if data[0] in docnames: if data[0] in docnames:
@ -594,8 +578,7 @@ class StandardDomain(Domain):
if data[0] in docnames: if data[0] in docnames:
self.anonlabels[key] = data self.anonlabels[key] = data
def process_doc(self, env, docname, document): def process_doc(self, env: "BuildEnvironment", docname: str, document: nodes.document) -> None: # NOQA
# type: (BuildEnvironment, str, nodes.document) -> None
for name, explicit in document.nametypes.items(): for name, explicit in document.nametypes.items():
if not explicit: if not explicit:
continue continue
@ -636,17 +619,15 @@ class StandardDomain(Domain):
continue continue
self.labels[name] = docname, labelid, sectname self.labels[name] = docname, labelid, sectname
def add_object(self, objtype, name, docname, labelid): def add_object(self, objtype: str, name: str, docname: str, labelid: str) -> None:
# type: (str, str, str, str) -> None
self.objects[objtype, name] = (docname, labelid) self.objects[objtype, name] = (docname, labelid)
def add_program_option(self, program, name, docname, labelid): def add_program_option(self, program: str, name: str, docname: str, labelid: str) -> None:
# type: (str, str, str, str) -> None
self.progoptions[program, name] = (docname, labelid) self.progoptions[program, name] = (docname, labelid)
def build_reference_node(self, fromdocname, builder, docname, labelid, def build_reference_node(self, fromdocname: str, builder: "Builder", docname: str,
sectname, rolename, **options): labelid: str, sectname: str, rolename: str, **options
# type: (str, Builder, str, str, str, str, Any) -> nodes.Element ) -> Element:
nodeclass = options.pop('nodeclass', nodes.reference) nodeclass = options.pop('nodeclass', nodes.reference)
newnode = nodeclass('', '', internal=True, **options) newnode = nodeclass('', '', internal=True, **options)
innernode = nodes.inline(sectname, sectname) innernode = nodes.inline(sectname, sectname)
@ -659,7 +640,7 @@ class StandardDomain(Domain):
# set more info in contnode; in case the # set more info in contnode; in case the
# get_relative_uri call raises NoUri, # get_relative_uri call raises NoUri,
# the builder will then have to resolve these # the builder will then have to resolve these
contnode = addnodes.pending_xref('') contnode = pending_xref('')
contnode['refdocname'] = docname contnode['refdocname'] = docname
contnode['refsectname'] = sectname contnode['refsectname'] = sectname
newnode['refuri'] = builder.get_relative_uri( newnode['refuri'] = builder.get_relative_uri(
@ -669,8 +650,8 @@ class StandardDomain(Domain):
newnode.append(innernode) newnode.append(innernode)
return newnode return newnode
def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): def resolve_xref(self, env: "BuildEnvironment", fromdocname: str, builder: "Builder",
# type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA typ: str, target: str, node: pending_xref, contnode: Element) -> Element:
if typ == 'ref': if typ == 'ref':
resolver = self._resolve_ref_xref resolver = self._resolve_ref_xref
elif typ == 'numref': elif typ == 'numref':
@ -691,8 +672,9 @@ class StandardDomain(Domain):
return resolver(env, fromdocname, builder, typ, target, node, contnode) return resolver(env, fromdocname, builder, typ, target, node, contnode)
def _resolve_ref_xref(self, env, fromdocname, builder, typ, target, node, contnode): def _resolve_ref_xref(self, env: "BuildEnvironment", fromdocname: str,
# type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA builder: "Builder", typ: str, target: str, node: pending_xref,
contnode: Element) -> Element:
if node['refexplicit']: if node['refexplicit']:
# reference to anonymous label; the reference uses # reference to anonymous label; the reference uses
# the supplied link caption # the supplied link caption
@ -708,8 +690,9 @@ class StandardDomain(Domain):
return self.build_reference_node(fromdocname, builder, return self.build_reference_node(fromdocname, builder,
docname, labelid, sectname, 'ref') docname, labelid, sectname, 'ref')
def _resolve_numref_xref(self, env, fromdocname, builder, typ, target, node, contnode): def _resolve_numref_xref(self, env: "BuildEnvironment", fromdocname: str,
# type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA builder: "Builder", typ: str, target: str,
node: pending_xref, contnode: Element) -> Element:
if target in self.labels: if target in self.labels:
docname, labelid, figname = self.labels.get(target, ('', '', '')) docname, labelid, figname = self.labels.get(target, ('', '', ''))
else: else:
@ -769,8 +752,9 @@ class StandardDomain(Domain):
nodeclass=addnodes.number_reference, nodeclass=addnodes.number_reference,
title=title) title=title)
def _resolve_keyword_xref(self, env, fromdocname, builder, typ, target, node, contnode): def _resolve_keyword_xref(self, env: "BuildEnvironment", fromdocname: str,
# type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA builder: "Builder", typ: str, target: str,
node: pending_xref, contnode: Element) -> Element:
# keywords are oddballs: they are referenced by named labels # keywords are oddballs: they are referenced by named labels
docname, labelid, _ = self.labels.get(target, ('', '', '')) docname, labelid, _ = self.labels.get(target, ('', '', ''))
if not docname: if not docname:
@ -778,8 +762,9 @@ class StandardDomain(Domain):
return make_refnode(builder, fromdocname, docname, return make_refnode(builder, fromdocname, docname,
labelid, contnode) labelid, contnode)
def _resolve_doc_xref(self, env, fromdocname, builder, typ, target, node, contnode): def _resolve_doc_xref(self, env: "BuildEnvironment", fromdocname: str,
# type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA builder: "Builder", typ: str, target: str,
node: pending_xref, contnode: Element) -> Element:
# directly reference to document by source name; can be absolute or relative # directly reference to document by source name; can be absolute or relative
refdoc = node.get('refdoc', fromdocname) refdoc = node.get('refdoc', fromdocname)
docname = docname_join(refdoc, node['reftarget']) docname = docname_join(refdoc, node['reftarget'])
@ -794,8 +779,9 @@ class StandardDomain(Domain):
innernode = nodes.inline(caption, caption, classes=['doc']) innernode = nodes.inline(caption, caption, classes=['doc'])
return make_refnode(builder, fromdocname, docname, None, innernode) return make_refnode(builder, fromdocname, docname, None, innernode)
def _resolve_option_xref(self, env, fromdocname, builder, typ, target, node, contnode): def _resolve_option_xref(self, env: "BuildEnvironment", fromdocname: str,
# type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA builder: "Builder", typ: str, target: str,
node: pending_xref, contnode: Element) -> Element:
progname = node.get('std:program') progname = node.get('std:program')
target = target.strip() target = target.strip()
docname, labelid = self.progoptions.get((progname, target), ('', '')) docname, labelid = self.progoptions.get((progname, target), ('', ''))
@ -815,8 +801,9 @@ class StandardDomain(Domain):
return make_refnode(builder, fromdocname, docname, return make_refnode(builder, fromdocname, docname,
labelid, contnode) labelid, contnode)
def _resolve_obj_xref(self, env, fromdocname, builder, typ, target, node, contnode): def _resolve_obj_xref(self, env: "BuildEnvironment", fromdocname: str,
# type: (BuildEnvironment, str, Builder, str, str, addnodes.pending_xref, nodes.Element) -> nodes.Element # NOQA builder: "Builder", typ: str, target: str,
node: pending_xref, contnode: Element) -> Element:
objtypes = self.objtypes_for_role(typ) or [] objtypes = self.objtypes_for_role(typ) or []
for objtype in objtypes: for objtype in objtypes:
if (objtype, target) in self.objects: if (objtype, target) in self.objects:
@ -829,8 +816,9 @@ class StandardDomain(Domain):
return make_refnode(builder, fromdocname, docname, return make_refnode(builder, fromdocname, docname,
labelid, contnode) labelid, contnode)
def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode): def resolve_any_xref(self, env: "BuildEnvironment", fromdocname: str,
# type: (BuildEnvironment, str, Builder, str, addnodes.pending_xref, nodes.Element) -> List[Tuple[str, nodes.Element]] # NOQA builder: "Builder", target: str, node: pending_xref,
contnode: Element) -> List[Tuple[str, Element]]:
results = [] # type: List[Tuple[str, nodes.Element]] results = [] # type: List[Tuple[str, nodes.Element]]
ltarget = target.lower() # :ref: lowercases its target automatically ltarget = target.lower() # :ref: lowercases its target automatically
for role in ('ref', 'option'): # do not try "keyword" for role in ('ref', 'option'): # do not try "keyword"
@ -851,8 +839,7 @@ class StandardDomain(Domain):
labelid, contnode))) labelid, contnode)))
return results return results
def get_objects(self): def get_objects(self) -> Iterator[Tuple[str, str, str, str, str, int]]:
# type: () -> Iterator[Tuple[str, str, str, str, str, int]]
# handle the special 'doc' reference here # handle the special 'doc' reference here
for doc in self.env.all_docs: for doc in self.env.all_docs:
yield (doc, clean_astext(self.env.titles[doc]), 'doc', doc, '', -1) yield (doc, clean_astext(self.env.titles[doc]), 'doc', doc, '', -1)
@ -873,17 +860,14 @@ class StandardDomain(Domain):
if name not in non_anon_labels: if name not in non_anon_labels:
yield (name, name, 'label', docname, labelid, -1) yield (name, name, 'label', docname, labelid, -1)
def get_type_name(self, type, primary=False): def get_type_name(self, type: ObjType, primary: bool = False) -> str:
# type: (ObjType, bool) -> str
# never prepend "Default" # never prepend "Default"
return type.lname return type.lname
def is_enumerable_node(self, node): def is_enumerable_node(self, node: Node) -> bool:
# type: (nodes.Node) -> bool
return node.__class__ in self.enumerable_nodes return node.__class__ in self.enumerable_nodes
def get_numfig_title(self, node): def get_numfig_title(self, node: Node) -> str:
# type: (nodes.Node) -> str
"""Get the title of enumerable nodes to refer them using its title""" """Get the title of enumerable nodes to refer them using its title"""
if self.is_enumerable_node(node): if self.is_enumerable_node(node):
elem = cast(nodes.Element, node) elem = cast(nodes.Element, node)
@ -897,11 +881,9 @@ class StandardDomain(Domain):
return None return None
def get_enumerable_node_type(self, node): def get_enumerable_node_type(self, node: Node) -> str:
# type: (nodes.Node) -> str
"""Get type of enumerable nodes.""" """Get type of enumerable nodes."""
def has_child(node, cls): def has_child(node: Element, cls: Type) -> bool:
# type: (nodes.Element, Type) -> bool
return any(isinstance(child, cls) for child in node) return any(isinstance(child, cls) for child in node)
if isinstance(node, nodes.section): if isinstance(node, nodes.section):
@ -915,8 +897,8 @@ class StandardDomain(Domain):
figtype, _ = self.enumerable_nodes.get(node.__class__, (None, None)) figtype, _ = self.enumerable_nodes.get(node.__class__, (None, None))
return figtype return figtype
def get_fignumber(self, env, builder, figtype, docname, target_node): def get_fignumber(self, env: "BuildEnvironment", builder: "Builder",
# type: (BuildEnvironment, Builder, str, str, nodes.Element) -> Tuple[int, ...] figtype: str, docname: str, target_node: Element) -> Tuple[int, ...]:
if figtype == 'section': if figtype == 'section':
if builder.name == 'latex': if builder.name == 'latex':
return tuple() return tuple()
@ -938,8 +920,7 @@ class StandardDomain(Domain):
# Maybe it is defined in orphaned document. # Maybe it is defined in orphaned document.
raise ValueError raise ValueError
def get_full_qualified_name(self, node): def get_full_qualified_name(self, node: Element) -> str:
# type: (nodes.Element) -> str
if node.get('reftype') == 'option': if node.get('reftype') == 'option':
progname = node.get('std:program') progname = node.get('std:program')
command = ws_re.split(node.get('reftarget')) command = ws_re.split(node.get('reftarget'))
@ -953,24 +934,20 @@ class StandardDomain(Domain):
else: else:
return None return None
def note_citations(self, env, docname, document): def note_citations(self, env: "BuildEnvironment", docname: str, document: nodes.document) -> None: # NOQA
# type: (BuildEnvironment, str, nodes.document) -> None
warnings.warn('StandardDomain.note_citations() is deprecated.', warnings.warn('StandardDomain.note_citations() is deprecated.',
RemovedInSphinx40Warning) RemovedInSphinx40Warning)
def note_citation_refs(self, env, docname, document): def note_citation_refs(self, env: "BuildEnvironment", docname: str, document: nodes.document) -> None: # NOQA
# type: (BuildEnvironment, str, nodes.document) -> None
warnings.warn('StandardDomain.note_citation_refs() is deprecated.', warnings.warn('StandardDomain.note_citation_refs() is deprecated.',
RemovedInSphinx40Warning) RemovedInSphinx40Warning)
def note_labels(self, env, docname, document): def note_labels(self, env: "BuildEnvironment", docname: str, document: nodes.document) -> None: # NOQA
# type: (BuildEnvironment, str, nodes.document) -> None
warnings.warn('StandardDomain.note_labels() is deprecated.', warnings.warn('StandardDomain.note_labels() is deprecated.',
RemovedInSphinx40Warning) RemovedInSphinx40Warning)
def setup(app): def setup(app: "Sphinx") -> Dict[str, Any]:
# type: (Sphinx) -> Dict[str, Any]
app.add_domain(StandardDomain) app.add_domain(StandardDomain)
return { return {

View File

@ -270,7 +270,7 @@ def format_date(format: str, date: datetime = None, language: str = None) -> str
if source_date_epoch is not None: if source_date_epoch is not None:
date = datetime.utcfromtimestamp(float(source_date_epoch)) date = datetime.utcfromtimestamp(float(source_date_epoch))
else: else:
date = datetime.now() date = datetime.utcnow()
result = [] result = []
tokens = date_format_re.split(format) tokens = date_format_re.split(format)