diff --git a/CHANGES b/CHANGES index d59dc9f52..4d5dc1623 100644 --- a/CHANGES +++ b/CHANGES @@ -57,6 +57,7 @@ Deprecated * ``sphinx.util.pycompat.convert_with_2to3()`` * ``sphinx.util.pycompat.execfile_()`` * ``sphinx.util.smartypants`` +* ``sphinx.util.typing.DirectiveOption`` Features added -------------- diff --git a/doc/extdev/deprecated.rst b/doc/extdev/deprecated.rst index 96bc84ff3..3fdb55e2c 100644 --- a/doc/extdev/deprecated.rst +++ b/doc/extdev/deprecated.rst @@ -72,6 +72,11 @@ The following is a list of deprecated interfaces. - 6.0 - ``docutils.utils.smartyquotes`` + * - ``sphinx.util.typing.DirectiveOption`` + - 4.0 + - 6.0 + - N/A + * - pending_xref node for viewcode extension - 3.5 - 5.0 diff --git a/sphinx/directives/__init__.py b/sphinx/directives/__init__.py index d4c82c9f3..f40144809 100644 --- a/sphinx/directives/__init__.py +++ b/sphinx/directives/__init__.py @@ -21,7 +21,7 @@ from sphinx.deprecation import RemovedInSphinx50Warning, deprecated_alias from sphinx.util import docutils from sphinx.util.docfields import DocFieldTransformer, Field, TypedField from sphinx.util.docutils import SphinxDirective -from sphinx.util.typing import DirectiveOption +from sphinx.util.typing import OptionSpec if TYPE_CHECKING: from sphinx.application import Sphinx @@ -58,9 +58,9 @@ class ObjectDescription(SphinxDirective, Generic[T]): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True - option_spec = { + option_spec: OptionSpec = { 'noindex': directives.flag, - } # type: Dict[str, DirectiveOption] + } # types of doc fields that this directive handles, see sphinx.util.docfields doc_field_types = [] # type: List[Field] @@ -251,7 +251,7 @@ class DefaultDomain(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = False - option_spec = {} # type: Dict + option_spec: OptionSpec = {} def run(self) -> List[Node]: domain_name = self.arguments[0].lower() diff --git a/sphinx/directives/code.py b/sphinx/directives/code.py index f5cd92b82..04b81cef5 100644 --- a/sphinx/directives/code.py +++ b/sphinx/directives/code.py @@ -22,6 +22,7 @@ from sphinx.directives import optional_int from sphinx.locale import __ from sphinx.util import logging, parselinenos from sphinx.util.docutils import SphinxDirective +from sphinx.util.typing import OptionSpec if TYPE_CHECKING: from sphinx.application import Sphinx @@ -39,7 +40,7 @@ class Highlight(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = False - option_spec = { + option_spec: OptionSpec = { 'force': directives.flag, 'linenothreshold': directives.positive_int, } @@ -103,7 +104,7 @@ class CodeBlock(SphinxDirective): required_arguments = 0 optional_arguments = 1 final_argument_whitespace = False - option_spec = { + option_spec: OptionSpec = { 'force': directives.flag, 'linenos': directives.flag, 'dedent': optional_int, @@ -379,7 +380,7 @@ class LiteralInclude(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True - option_spec = { + option_spec: OptionSpec = { 'dedent': optional_int, 'linenos': directives.flag, 'lineno-start': int, diff --git a/sphinx/directives/other.py b/sphinx/directives/other.py index 7dd6252e2..1daa3c4a5 100644 --- a/sphinx/directives/other.py +++ b/sphinx/directives/other.py @@ -23,6 +23,7 @@ from sphinx.util import docname_join, url_re from sphinx.util.docutils import SphinxDirective from sphinx.util.matching import Matcher, patfilter from sphinx.util.nodes import explicit_title_re +from sphinx.util.typing import OptionSpec if TYPE_CHECKING: from sphinx.application import Sphinx @@ -162,7 +163,7 @@ class Author(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True - option_spec = {} # type: Dict + option_spec: OptionSpec = {} def run(self) -> List[Node]: if not self.config.show_authors: @@ -202,7 +203,7 @@ class TabularColumns(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True - option_spec = {} # type: Dict + option_spec: OptionSpec = {} def run(self) -> List[Node]: node = addnodes.tabular_col_spec() @@ -219,7 +220,7 @@ class Centered(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True - option_spec = {} # type: Dict + option_spec: OptionSpec = {} def run(self) -> List[Node]: if not self.arguments: @@ -241,7 +242,7 @@ class Acks(SphinxDirective): required_arguments = 0 optional_arguments = 0 final_argument_whitespace = False - option_spec = {} # type: Dict + option_spec: OptionSpec = {} def run(self) -> List[Node]: node = addnodes.acks() @@ -262,7 +263,7 @@ class HList(SphinxDirective): required_arguments = 0 optional_arguments = 0 final_argument_whitespace = False - option_spec = { + option_spec: OptionSpec = { 'columns': int, } @@ -298,7 +299,7 @@ class Only(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True - option_spec = {} # type: Dict + option_spec: OptionSpec = {} def run(self) -> List[Node]: node = addnodes.only() diff --git a/sphinx/directives/patches.py b/sphinx/directives/patches.py index 1c3cfd853..b4c978474 100644 --- a/sphinx/directives/patches.py +++ b/sphinx/directives/patches.py @@ -25,6 +25,7 @@ from sphinx.util import logging from sphinx.util.docutils import SphinxDirective from sphinx.util.nodes import set_source_info from sphinx.util.osutil import SEP, os_path, relpath +from sphinx.util.typing import OptionSpec if TYPE_CHECKING: from sphinx.application import Sphinx @@ -140,7 +141,7 @@ class Code(SphinxDirective): This is compatible with docutils' :rst:dir:`code` directive. """ optional_arguments = 1 - option_spec = { + option_spec: OptionSpec = { 'class': directives.class_option, 'force': directives.flag, 'name': directives.unchanged, @@ -184,7 +185,7 @@ class MathDirective(SphinxDirective): required_arguments = 0 optional_arguments = 1 final_argument_whitespace = True - option_spec = { + option_spec: OptionSpec = { 'label': directives.unchanged, 'name': directives.unchanged, 'class': directives.class_option, diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index 0fecbad6d..1b7a5fb60 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -39,6 +39,7 @@ from sphinx.util.cfamily import (ASTAttribute, ASTBaseBase, ASTBaseParenExprList from sphinx.util.docfields import Field, TypedField from sphinx.util.docutils import SphinxDirective from sphinx.util.nodes import make_refnode +from sphinx.util.typing import OptionSpec logger = logging.getLogger(__name__) T = TypeVar('T') @@ -3095,7 +3096,7 @@ class CObject(ObjectDescription[ASTDeclaration]): names=('rtype',)), ] - option_spec = { + option_spec: OptionSpec = { 'noindexentry': directives.flag, } @@ -3335,7 +3336,7 @@ class CNamespaceObject(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True - option_spec = {} # type: Dict + option_spec: OptionSpec = {} def run(self) -> List[Node]: rootSymbol = self.env.domaindata['c']['root_symbol'] @@ -3365,7 +3366,7 @@ class CNamespacePushObject(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True - option_spec = {} # type: Dict + option_spec: OptionSpec = {} def run(self) -> List[Node]: if self.arguments[0].strip() in ('NULL', '0', 'nullptr'): @@ -3396,7 +3397,7 @@ class CNamespacePopObject(SphinxDirective): required_arguments = 0 optional_arguments = 0 final_argument_whitespace = True - option_spec = {} # type: Dict + option_spec: OptionSpec = {} def run(self) -> List[Node]: stack = self.env.temp_data.get('c:namespace_stack', None) @@ -3550,10 +3551,10 @@ class AliasTransform(SphinxTransform): class CAliasObject(ObjectDescription): - option_spec = { + option_spec: OptionSpec = { 'maxdepth': directives.nonnegative_int, 'noroot': directives.flag, - } # type: Dict + } def run(self) -> List[Node]: """ diff --git a/sphinx/domains/changeset.py b/sphinx/domains/changeset.py index 33234d6e0..23a3375ce 100644 --- a/sphinx/domains/changeset.py +++ b/sphinx/domains/changeset.py @@ -17,6 +17,7 @@ from sphinx import addnodes from sphinx.domains import Domain from sphinx.locale import _ from sphinx.util.docutils import SphinxDirective +from sphinx.util.typing import OptionSpec if TYPE_CHECKING: from sphinx.application import Sphinx @@ -53,7 +54,7 @@ class VersionChange(SphinxDirective): required_arguments = 1 optional_arguments = 1 final_argument_whitespace = True - option_spec = {} # type: Dict + option_spec: OptionSpec = {} def run(self) -> List[Node]: node = addnodes.versionmodified() diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 3bd764de5..9637654c9 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -39,6 +39,7 @@ from sphinx.util.cfamily import (ASTAttribute, ASTBaseBase, ASTBaseParenExprList from sphinx.util.docfields import Field, GroupedField from sphinx.util.docutils import SphinxDirective from sphinx.util.nodes import make_refnode +from sphinx.util.typing import OptionSpec logger = logging.getLogger(__name__) T = TypeVar('T') @@ -6707,7 +6708,7 @@ class CPPObject(ObjectDescription[ASTDeclaration]): names=('returns', 'return')), ] - option_spec = { + option_spec: OptionSpec = { 'noindexentry': directives.flag, 'tparam-line-spec': directives.flag, } @@ -6973,7 +6974,7 @@ class CPPNamespaceObject(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True - option_spec = {} # type: Dict + option_spec: OptionSpec = {} def run(self) -> List[Node]: rootSymbol = self.env.domaindata['cpp']['root_symbol'] @@ -7004,7 +7005,7 @@ class CPPNamespacePushObject(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True - option_spec = {} # type: Dict + option_spec: OptionSpec = {} def run(self) -> List[Node]: if self.arguments[0].strip() in ('NULL', '0', 'nullptr'): @@ -7036,7 +7037,7 @@ class CPPNamespacePopObject(SphinxDirective): required_arguments = 0 optional_arguments = 0 final_argument_whitespace = True - option_spec = {} # type: Dict + option_spec: OptionSpec = {} def run(self) -> List[Node]: stack = self.env.temp_data.get('cpp:namespace_stack', None) @@ -7213,10 +7214,10 @@ class AliasTransform(SphinxTransform): class CPPAliasObject(ObjectDescription): - option_spec = { + option_spec: OptionSpec = { 'maxdepth': directives.nonnegative_int, 'noroot': directives.flag, - } # type: Dict + } def run(self) -> List[Node]: """ diff --git a/sphinx/domains/index.py b/sphinx/domains/index.py index fd1a76613..9ecfae439 100644 --- a/sphinx/domains/index.py +++ b/sphinx/domains/index.py @@ -20,6 +20,7 @@ from sphinx.environment import BuildEnvironment from sphinx.util import logging, split_index_msg from sphinx.util.docutils import ReferenceRole, SphinxDirective from sphinx.util.nodes import process_index_entry +from sphinx.util.typing import OptionSpec if TYPE_CHECKING: from sphinx.application import Sphinx @@ -67,7 +68,7 @@ class IndexDirective(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True - option_spec = { + option_spec: OptionSpec = { 'name': directives.unchanged, } diff --git a/sphinx/domains/javascript.py b/sphinx/domains/javascript.py index f612fb914..b34cff509 100644 --- a/sphinx/domains/javascript.py +++ b/sphinx/domains/javascript.py @@ -28,6 +28,7 @@ from sphinx.util import logging from sphinx.util.docfields import Field, GroupedField, TypedField from sphinx.util.docutils import SphinxDirective from sphinx.util.nodes import make_id, make_refnode +from sphinx.util.typing import OptionSpec logger = logging.getLogger(__name__) @@ -47,7 +48,7 @@ class JSObject(ObjectDescription[Tuple[str, str]]): #: based on directive nesting allow_nesting = False - option_spec = { + option_spec: OptionSpec = { 'noindex': directives.flag, 'noindexentry': directives.flag, } @@ -253,7 +254,7 @@ class JSModule(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = False - option_spec = { + option_spec: OptionSpec = { 'noindex': directives.flag } diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index 40a67f82c..a1e892f4c 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -38,7 +38,7 @@ from sphinx.util.docfields import Field, GroupedField, TypedField from sphinx.util.docutils import SphinxDirective from sphinx.util.inspect import signature_from_str from sphinx.util.nodes import find_pending_xref_condition, make_id, make_refnode -from sphinx.util.typing import TextlikeNode +from sphinx.util.typing import OptionSpec, TextlikeNode logger = logging.getLogger(__name__) @@ -357,7 +357,7 @@ class PyObject(ObjectDescription[Tuple[str, str]]): :cvar allow_nesting: Class is an object that allows for nested namespaces :vartype allow_nesting: bool """ - option_spec = { + option_spec: OptionSpec = { 'noindex': directives.flag, 'noindexentry': directives.flag, 'module': directives.unchanged, @@ -575,7 +575,7 @@ class PyObject(ObjectDescription[Tuple[str, str]]): class PyFunction(PyObject): """Description of a function.""" - option_spec = PyObject.option_spec.copy() + option_spec: OptionSpec = PyObject.option_spec.copy() option_spec.update({ 'async': directives.flag, }) @@ -629,7 +629,7 @@ class PyDecoratorFunction(PyFunction): class PyVariable(PyObject): """Description of a variable.""" - option_spec = PyObject.option_spec.copy() + option_spec: OptionSpec = PyObject.option_spec.copy() option_spec.update({ 'type': directives.unchanged, 'value': directives.unchanged, @@ -662,7 +662,7 @@ class PyClasslike(PyObject): Description of a class-like object (classes, interfaces, exceptions). """ - option_spec = PyObject.option_spec.copy() + option_spec: OptionSpec = PyObject.option_spec.copy() option_spec.update({ 'final': directives.flag, }) @@ -689,7 +689,7 @@ class PyClasslike(PyObject): class PyMethod(PyObject): """Description of a method.""" - option_spec = PyObject.option_spec.copy() + option_spec: OptionSpec = PyObject.option_spec.copy() option_spec.update({ 'abstractmethod': directives.flag, 'async': directives.flag, @@ -750,7 +750,7 @@ class PyMethod(PyObject): class PyClassMethod(PyMethod): """Description of a classmethod.""" - option_spec = PyObject.option_spec.copy() + option_spec: OptionSpec = PyObject.option_spec.copy() def run(self) -> List[Node]: self.name = 'py:method' @@ -762,7 +762,7 @@ class PyClassMethod(PyMethod): class PyStaticMethod(PyMethod): """Description of a staticmethod.""" - option_spec = PyObject.option_spec.copy() + option_spec: OptionSpec = PyObject.option_spec.copy() def run(self) -> List[Node]: self.name = 'py:method' @@ -790,7 +790,7 @@ class PyDecoratorMethod(PyMethod): class PyAttribute(PyObject): """Description of an attribute.""" - option_spec = PyObject.option_spec.copy() + option_spec: OptionSpec = PyObject.option_spec.copy() option_spec.update({ 'type': directives.unchanged, 'value': directives.unchanged, @@ -857,7 +857,7 @@ class PyModule(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = False - option_spec = { + option_spec: OptionSpec = { 'platform': lambda x: x, 'synopsis': lambda x: x, 'noindex': directives.flag, @@ -921,7 +921,7 @@ class PyCurrentModule(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = False - option_spec = {} # type: Dict + option_spec: OptionSpec = {} def run(self) -> List[Node]: modname = self.arguments[0].strip() diff --git a/sphinx/domains/rst.py b/sphinx/domains/rst.py index 07bf46b75..6b4f1c0ff 100644 --- a/sphinx/domains/rst.py +++ b/sphinx/domains/rst.py @@ -25,6 +25,7 @@ from sphinx.locale import _, __ from sphinx.roles import XRefRole from sphinx.util import logging from sphinx.util.nodes import make_id, make_refnode +from sphinx.util.typing import OptionSpec logger = logging.getLogger(__name__) @@ -117,7 +118,7 @@ class ReSTDirectiveOption(ReSTMarkup): """ Description of an option for reST directive. """ - option_spec = ReSTMarkup.option_spec.copy() + option_spec: OptionSpec = ReSTMarkup.option_spec.copy() option_spec.update({ 'type': directives.unchanged, }) diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py index 8b10c8547..274c29c87 100644 --- a/sphinx/domains/std.py +++ b/sphinx/domains/std.py @@ -30,7 +30,7 @@ from sphinx.roles import XRefRole from sphinx.util import docname_join, logging, ws_re from sphinx.util.docutils import SphinxDirective from sphinx.util.nodes import clean_astext, make_id, make_refnode -from sphinx.util.typing import RoleFunction +from sphinx.util.typing import OptionSpec, RoleFunction if TYPE_CHECKING: from sphinx.application import Sphinx @@ -132,7 +132,7 @@ class Target(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True - option_spec = {} # type: Dict + option_spec: OptionSpec = {} def run(self) -> List[Node]: # normalize whitespace in fullname like XRefRole does @@ -265,7 +265,7 @@ class Program(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True - option_spec = {} # type: Dict + option_spec: OptionSpec = {} def run(self) -> List[Node]: program = ws_re.sub('-', self.arguments[0].strip()) @@ -329,7 +329,7 @@ class Glossary(SphinxDirective): required_arguments = 0 optional_arguments = 0 final_argument_whitespace = False - option_spec = { + option_spec: OptionSpec = { 'sorted': directives.flag, } @@ -482,7 +482,7 @@ class ProductionList(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True - option_spec = {} # type: Dict + option_spec: OptionSpec = {} def run(self) -> List[Node]: domain = cast(StandardDomain, self.env.get_domain('std')) diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index 0b5709301..2abe41234 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -33,7 +33,7 @@ from sphinx.util import inspect, logging from sphinx.util.docstrings import extract_metadata, prepare_docstring from sphinx.util.inspect import (evaluate_signature, getdoc, object_description, safe_getattr, stringify_signature) -from sphinx.util.typing import get_type_hints, restify +from sphinx.util.typing import OptionSpec, get_type_hints, restify from sphinx.util.typing import stringify as stringify_typehint if TYPE_CHECKING: @@ -309,7 +309,9 @@ class Documenter: #: true if the generated content may contain titles titles_allowed = False - option_spec = {'noindex': bool_option} # type: Dict[str, Callable] + option_spec: OptionSpec = { + 'noindex': bool_option + } def get_attr(self, obj: Any, name: str, *defargs: Any) -> Any: """getattr() override for types such as Zope interfaces.""" @@ -970,7 +972,7 @@ class ModuleDocumenter(Documenter): content_indent = '' titles_allowed = True - option_spec = { + option_spec: OptionSpec = { 'members': members_option, 'undoc-members': bool_option, 'noindex': bool_option, 'inherited-members': inherited_members_option, 'show-inheritance': bool_option, 'synopsis': identity, @@ -978,7 +980,7 @@ class ModuleDocumenter(Documenter): 'member-order': member_order_option, 'exclude-members': exclude_members_option, 'private-members': members_option, 'special-members': members_option, 'imported-members': bool_option, 'ignore-module-all': bool_option - } # type: Dict[str, Callable] + } def __init__(self, *args: Any) -> None: super().__init__(*args) @@ -1419,13 +1421,13 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: """ objtype = 'class' member_order = 20 - option_spec = { + option_spec: OptionSpec = { 'members': members_option, 'undoc-members': bool_option, 'noindex': bool_option, 'inherited-members': inherited_members_option, 'show-inheritance': bool_option, 'member-order': member_order_option, 'exclude-members': exclude_members_option, 'private-members': members_option, 'special-members': members_option, - } # type: Dict[str, Callable] + } _signature_class = None # type: Any _signature_method_name = None # type: str @@ -1874,7 +1876,7 @@ class DataDocumenter(GenericAliasMixin, NewTypeMixin, TypeVarMixin, objtype = 'data' member_order = 40 priority = -10 - option_spec = dict(ModuleLevelDocumenter.option_spec) + option_spec: OptionSpec = dict(ModuleLevelDocumenter.option_spec) option_spec["annotation"] = annotation_option option_spec["no-value"] = bool_option @@ -2358,7 +2360,7 @@ class AttributeDocumenter(GenericAliasMixin, NewTypeMixin, SlotsMixin, # type: """ objtype = 'attribute' member_order = 60 - option_spec = dict(ModuleLevelDocumenter.option_spec) + option_spec: OptionSpec = dict(ModuleLevelDocumenter.option_spec) option_spec["annotation"] = annotation_option option_spec["no-value"] = bool_option diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index 1a0fd2409..3cb5bc798 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -85,6 +85,7 @@ from sphinx.util import logging, rst from sphinx.util.docutils import (NullReporter, SphinxDirective, SphinxRole, new_document, switch_source_input) from sphinx.util.matching import Matcher +from sphinx.util.typing import OptionSpec from sphinx.writers.html import HTMLTranslator logger = logging.getLogger(__name__) @@ -225,7 +226,7 @@ class Autosummary(SphinxDirective): optional_arguments = 0 final_argument_whitespace = False has_content = True - option_spec = { + option_spec: OptionSpec = { 'caption': directives.unchanged_required, 'toctree': directives.unchanged, 'nosignatures': directives.flag, diff --git a/sphinx/ext/doctest.py b/sphinx/ext/doctest.py index e48fca248..e7ee8c67d 100644 --- a/sphinx/ext/doctest.py +++ b/sphinx/ext/doctest.py @@ -31,6 +31,7 @@ from sphinx.util import logging from sphinx.util.console import bold # type: ignore from sphinx.util.docutils import SphinxDirective from sphinx.util.osutil import relpath +from sphinx.util.typing import OptionSpec if TYPE_CHECKING: from sphinx.application import Sphinx @@ -150,15 +151,19 @@ class TestDirective(SphinxDirective): class TestsetupDirective(TestDirective): - option_spec = {'skipif': directives.unchanged_required} # type: Dict + option_spec: OptionSpec = { + 'skipif': directives.unchanged_required + } class TestcleanupDirective(TestDirective): - option_spec = {'skipif': directives.unchanged_required} # type: Dict + option_spec: OptionSpec = { + 'skipif': directives.unchanged_required + } class DoctestDirective(TestDirective): - option_spec = { + option_spec: OptionSpec = { 'hide': directives.flag, 'no-trim-doctest-flags': directives.flag, 'options': directives.unchanged, @@ -169,7 +174,7 @@ class DoctestDirective(TestDirective): class TestcodeDirective(TestDirective): - option_spec = { + option_spec: OptionSpec = { 'hide': directives.flag, 'no-trim-doctest-flags': directives.flag, 'pyversion': directives.unchanged_required, @@ -179,7 +184,7 @@ class TestcodeDirective(TestDirective): class TestoutputDirective(TestDirective): - option_spec = { + option_spec: OptionSpec = { 'hide': directives.flag, 'no-trim-doctest-flags': directives.flag, 'options': directives.unchanged, diff --git a/sphinx/ext/graphviz.py b/sphinx/ext/graphviz.py index f10285086..f10edfaae 100644 --- a/sphinx/ext/graphviz.py +++ b/sphinx/ext/graphviz.py @@ -30,6 +30,7 @@ from sphinx.util.fileutil import copy_asset from sphinx.util.i18n import search_image_for_language from sphinx.util.nodes import set_source_info from sphinx.util.osutil import ensuredir +from sphinx.util.typing import OptionSpec from sphinx.writers.html import HTMLTranslator from sphinx.writers.latex import LaTeXTranslator from sphinx.writers.manpage import ManualPageTranslator @@ -113,7 +114,7 @@ class Graphviz(SphinxDirective): required_arguments = 0 optional_arguments = 1 final_argument_whitespace = False - option_spec = { + option_spec: OptionSpec = { 'alt': directives.unchanged, 'align': align_spec, 'caption': directives.unchanged, @@ -181,7 +182,7 @@ class GraphvizSimple(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = False - option_spec = { + option_spec: OptionSpec = { 'alt': directives.unchanged, 'align': align_spec, 'caption': directives.unchanged, diff --git a/sphinx/ext/ifconfig.py b/sphinx/ext/ifconfig.py index df8545028..0e42984da 100644 --- a/sphinx/ext/ifconfig.py +++ b/sphinx/ext/ifconfig.py @@ -28,6 +28,7 @@ import sphinx from sphinx.application import Sphinx from sphinx.util.docutils import SphinxDirective from sphinx.util.nodes import nested_parse_with_titles +from sphinx.util.typing import OptionSpec class ifconfig(nodes.Element): @@ -40,7 +41,7 @@ class IfConfig(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True - option_spec = {} # type: Dict + option_spec: OptionSpec = {} def run(self) -> List[Node]: node = ifconfig() diff --git a/sphinx/ext/inheritance_diagram.py b/sphinx/ext/inheritance_diagram.py index 63a171087..62b80ac39 100644 --- a/sphinx/ext/inheritance_diagram.py +++ b/sphinx/ext/inheritance_diagram.py @@ -53,6 +53,7 @@ from sphinx.ext.graphviz import (figure_wrapper, graphviz, render_dot_html, rend render_dot_texinfo) from sphinx.util import md5 from sphinx.util.docutils import SphinxDirective +from sphinx.util.typing import OptionSpec from sphinx.writers.html import HTMLTranslator from sphinx.writers.latex import LaTeXTranslator from sphinx.writers.texinfo import TexinfoTranslator @@ -331,7 +332,7 @@ class InheritanceDiagram(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True - option_spec = { + option_spec: OptionSpec = { 'parts': int, 'private-bases': directives.flag, 'caption': directives.unchanged, diff --git a/sphinx/ext/todo.py b/sphinx/ext/todo.py index e0fdd31e0..e7a2cb51a 100644 --- a/sphinx/ext/todo.py +++ b/sphinx/ext/todo.py @@ -27,6 +27,7 @@ from sphinx.errors import NoUri from sphinx.locale import _, __ from sphinx.util import logging, texescape from sphinx.util.docutils import SphinxDirective, new_document +from sphinx.util.typing import OptionSpec from sphinx.writers.html import HTMLTranslator from sphinx.writers.latex import LaTeXTranslator @@ -51,7 +52,7 @@ class Todo(BaseAdmonition, SphinxDirective): required_arguments = 0 optional_arguments = 0 final_argument_whitespace = False - option_spec = { + option_spec: OptionSpec = { 'class': directives.class_option, 'name': directives.unchanged, } @@ -110,7 +111,7 @@ class TodoList(SphinxDirective): required_arguments = 0 optional_arguments = 0 final_argument_whitespace = False - option_spec = {} # type: Dict + option_spec: OptionSpec = {} def run(self) -> List[Node]: # Simply insert an empty todolist node which will be replaced later diff --git a/sphinx/util/typing.py b/sphinx/util/typing.py index afd2f805a..128fbd542 100644 --- a/sphinx/util/typing.py +++ b/sphinx/util/typing.py @@ -16,6 +16,8 @@ from typing import Any, Callable, Dict, Generator, List, Optional, Tuple, TypeVa from docutils import nodes from docutils.parsers.rst.states import Inliner +from sphinx.deprecation import RemovedInSphinx60Warning, deprecated_alias + if sys.version_info > (3, 7): from typing import ForwardRef else: @@ -40,9 +42,6 @@ if False: from typing import Type # NOQA # for python3.5.1 -# An entry of Directive.option_spec -DirectiveOption = Callable[[str], Any] - # Text like nodes which are initialized with text and rawsource TextlikeNode = Union[nodes.Text, nodes.TextElement] @@ -56,6 +55,9 @@ PathMatcher = Callable[[str], bool] RoleFunction = Callable[[str, str, str, int, Inliner, Dict[str, Any], List[str]], Tuple[List[nodes.Node], List[nodes.system_message]]] +# A option spec for directive +OptionSpec = Dict[str, Callable[[Optional[str]], Any]] + # title getter functions for enumerable nodes (see sphinx.domains.std) TitleGetter = Callable[[nodes.Node], str] @@ -405,3 +407,10 @@ def _stringify_py36(annotation: Any) -> str: return 'Union[%s]' % param_str return qualname + + +deprecated_alias('sphinx.util.typing', + { + 'DirectiveOption': Callable[[str], Any], + }, + RemovedInSphinx60Warning)