mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Enable mypy 'strict optional' for 19 modules (#11422)
Co-authored-by: Adam Turner <9087854+aa-turner@users.noreply.github.com>
This commit is contained in:
parent
f4a47f1401
commit
4de540efb6
@ -309,42 +309,23 @@ disallow_any_generics = true
|
||||
module = [
|
||||
"sphinx.builders.html",
|
||||
"sphinx.builders.latex",
|
||||
"sphinx.builders.linkcheck",
|
||||
"sphinx.domains",
|
||||
"sphinx.domains.c",
|
||||
"sphinx.domains.cpp",
|
||||
"sphinx.domains.javascript",
|
||||
"sphinx.domains.python",
|
||||
"sphinx.domains.std",
|
||||
"sphinx.environment",
|
||||
"sphinx.environment.adapters.toctree",
|
||||
"sphinx.ext.apidoc",
|
||||
"sphinx.ext.autodoc",
|
||||
"sphinx.ext.autodoc.mock",
|
||||
"sphinx.ext.autodoc.importer",
|
||||
"sphinx.ext.autodoc.preserve_defaults",
|
||||
"sphinx.ext.autosummary",
|
||||
"sphinx.ext.autosummary.generate",
|
||||
"sphinx.ext.doctest",
|
||||
"sphinx.ext.graphviz",
|
||||
"sphinx.ext.inheritance_diagram",
|
||||
"sphinx.ext.intersphinx",
|
||||
"sphinx.ext.imgmath",
|
||||
"sphinx.ext.linkcode",
|
||||
"sphinx.ext.mathjax",
|
||||
"sphinx.ext.napoleon",
|
||||
"sphinx.ext.napoleon.docstring",
|
||||
"sphinx.pycode.parser",
|
||||
"sphinx.registry",
|
||||
"sphinx.testing.util",
|
||||
"sphinx.transforms.i18n",
|
||||
"sphinx.transforms.post_transforms.images",
|
||||
"sphinx.util.cfamily",
|
||||
"sphinx.util.docfields",
|
||||
"sphinx.util.docutils",
|
||||
"sphinx.writers.latex",
|
||||
"sphinx.writers.text",
|
||||
"sphinx.writers.xml",
|
||||
]
|
||||
strict_optional = false
|
||||
|
||||
|
@ -160,7 +160,10 @@ class BuildInfo:
|
||||
raise ValueError(__('build info file is broken: %r') % exc) from exc
|
||||
|
||||
def __init__(
|
||||
self, config: Config = None, tags: Tags = None, config_categories: list[str] = [],
|
||||
self,
|
||||
config: Config | None = None,
|
||||
tags: Tags | None = None,
|
||||
config_categories: list[str] = [],
|
||||
) -> None:
|
||||
self.config_hash = ''
|
||||
self.tags_hash = ''
|
||||
@ -217,7 +220,7 @@ class StandaloneHTMLBuilder(Builder):
|
||||
imgpath: str = None
|
||||
domain_indices: list[DOMAIN_INDEX_TYPE] = []
|
||||
|
||||
def __init__(self, app: Sphinx, env: BuildEnvironment = None) -> None:
|
||||
def __init__(self, app: Sphinx, env: BuildEnvironment | None = None) -> None:
|
||||
super().__init__(app, env)
|
||||
|
||||
# CSS files
|
||||
@ -1016,7 +1019,7 @@ class StandaloneHTMLBuilder(Builder):
|
||||
|
||||
# --------- these are overwritten by the serialization builder
|
||||
|
||||
def get_target_uri(self, docname: str, typ: str = None) -> str:
|
||||
def get_target_uri(self, docname: str, typ: str | None = None) -> str:
|
||||
return quote(docname) + self.link_suffix
|
||||
|
||||
def handle_page(self, pagename: str, addctx: dict, templatename: str = 'page.html',
|
||||
|
@ -174,7 +174,7 @@ def _add_uri(app: Sphinx, uri: str, node: nodes.Element,
|
||||
try:
|
||||
lineno = get_node_line(node)
|
||||
except ValueError:
|
||||
lineno = None
|
||||
lineno = -1
|
||||
|
||||
if uri not in hyperlinks:
|
||||
hyperlinks[uri] = Hyperlink(uri, docname, app.env.doc2path(docname), lineno)
|
||||
@ -184,7 +184,7 @@ class Hyperlink(NamedTuple):
|
||||
uri: str
|
||||
docname: str
|
||||
docpath: str
|
||||
lineno: int | None
|
||||
lineno: int
|
||||
|
||||
|
||||
class HyperlinkAvailabilityChecker:
|
||||
@ -374,7 +374,7 @@ class HyperlinkAvailabilityCheckWorker(Thread):
|
||||
# - Attempt HTTP HEAD before HTTP GET unless page content is required.
|
||||
# - Follow server-issued HTTP redirects.
|
||||
# - Respect server-issued HTTP 429 back-offs.
|
||||
error_message = None
|
||||
error_message = ''
|
||||
status_code = -1
|
||||
response_url = retry_after = ''
|
||||
for retrieval_method, kwargs in _retrieval_methods(self.check_anchors, anchor):
|
||||
|
@ -32,6 +32,7 @@ class XMLBuilder(Builder):
|
||||
allow_parallel = True
|
||||
|
||||
_writer_class: type[XMLWriter] | type[PseudoXMLWriter] = XMLWriter
|
||||
writer: XMLWriter | PseudoXMLWriter
|
||||
default_translator_class = XMLTranslator
|
||||
|
||||
def init(self) -> None:
|
||||
|
@ -226,8 +226,8 @@ class Domain:
|
||||
for rolename in obj.roles:
|
||||
self._role2type.setdefault(rolename, []).append(name)
|
||||
self._type2role[name] = obj.roles[0] if obj.roles else ''
|
||||
self.objtypes_for_role: Callable[[str], list[str]] = self._role2type.get
|
||||
self.role_for_objtype: Callable[[str], str] = self._type2role.get
|
||||
self.objtypes_for_role = self._role2type.get
|
||||
self.role_for_objtype = self._type2role.get
|
||||
|
||||
def setup(self) -> None:
|
||||
"""Set up domain object."""
|
||||
|
@ -2013,7 +2013,9 @@ class ASTFunctionParameter(ASTBase):
|
||||
self.arg = arg
|
||||
self.ellipsis = ellipsis
|
||||
|
||||
def get_id(self, version: int, objectType: str = None, symbol: Symbol = None) -> str:
|
||||
def get_id(
|
||||
self, version: int, objectType: str | None = None, symbol: Symbol | None = None,
|
||||
) -> str:
|
||||
# this is not part of the normal name mangling in C++
|
||||
if symbol:
|
||||
# the anchor will be our parent
|
||||
@ -3114,8 +3116,8 @@ class ASTType(ASTBase):
|
||||
def trailingReturn(self) -> ASTType:
|
||||
return self.decl.trailingReturn
|
||||
|
||||
def get_id(self, version: int, objectType: str = None,
|
||||
symbol: Symbol = None) -> str:
|
||||
def get_id(self, version: int, objectType: str | None = None,
|
||||
symbol: Symbol | None = None) -> str:
|
||||
if version == 1:
|
||||
res = []
|
||||
if objectType: # needs the name
|
||||
@ -3211,7 +3213,9 @@ class ASTTemplateParamConstrainedTypeWithInit(ASTBase):
|
||||
def isPack(self) -> bool:
|
||||
return self.type.isPack
|
||||
|
||||
def get_id(self, version: int, objectType: str = None, symbol: Symbol = None) -> str:
|
||||
def get_id(
|
||||
self, version: int, objectType: str | None = None, symbol: Symbol | None = None,
|
||||
) -> str:
|
||||
# this is not part of the normal name mangling in C++
|
||||
assert version >= 2
|
||||
if symbol:
|
||||
@ -3250,8 +3254,8 @@ class ASTTypeWithInit(ASTBase):
|
||||
def isPack(self) -> bool:
|
||||
return self.type.isPack
|
||||
|
||||
def get_id(self, version: int, objectType: str = None,
|
||||
symbol: Symbol = None) -> str:
|
||||
def get_id(self, version: int, objectType: str | None = None,
|
||||
symbol: Symbol | None = None) -> str:
|
||||
if objectType != 'member':
|
||||
return self.type.get_id(version, objectType)
|
||||
if version == 1:
|
||||
@ -3279,8 +3283,8 @@ class ASTTypeUsing(ASTBase):
|
||||
self.name = name
|
||||
self.type = type
|
||||
|
||||
def get_id(self, version: int, objectType: str = None,
|
||||
symbol: Symbol = None) -> str:
|
||||
def get_id(self, version: int, objectType: str | None = None,
|
||||
symbol: Symbol | None = None) -> str:
|
||||
if version == 1:
|
||||
raise NoOldIdError()
|
||||
return symbol.get_full_nested_name().get_id(version)
|
||||
@ -3319,8 +3323,8 @@ class ASTConcept(ASTBase):
|
||||
def name(self) -> ASTNestedName:
|
||||
return self.nestedName
|
||||
|
||||
def get_id(self, version: int, objectType: str = None,
|
||||
symbol: Symbol = None) -> str:
|
||||
def get_id(self, version: int, objectType: str | None = None,
|
||||
symbol: Symbol | None = None) -> str:
|
||||
if version == 1:
|
||||
raise NoOldIdError()
|
||||
return symbol.get_full_nested_name().get_id(version)
|
||||
@ -3628,7 +3632,9 @@ class ASTTemplateParamType(ASTTemplateParam):
|
||||
def get_identifier(self) -> ASTIdentifier:
|
||||
return self.data.get_identifier()
|
||||
|
||||
def get_id(self, version: int, objectType: str = None, symbol: Symbol = None) -> str:
|
||||
def get_id(
|
||||
self, version: int, objectType: str | None = None, symbol: Symbol | None = None,
|
||||
) -> str:
|
||||
# this is not part of the normal name mangling in C++
|
||||
assert version >= 2
|
||||
if symbol:
|
||||
@ -3715,7 +3721,9 @@ class ASTTemplateParamNonType(ASTTemplateParam):
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_id(self, version: int, objectType: str = None, symbol: Symbol = None) -> str:
|
||||
def get_id(
|
||||
self, version: int, objectType: str | None = None, symbol: Symbol | None = None,
|
||||
) -> str:
|
||||
assert version >= 2
|
||||
# this is not part of the normal name mangling in C++
|
||||
if symbol:
|
||||
@ -3836,7 +3844,9 @@ class ASTTemplateIntroductionParameter(ASTBase):
|
||||
def get_identifier(self) -> ASTIdentifier:
|
||||
return self.identifier
|
||||
|
||||
def get_id(self, version: int, objectType: str = None, symbol: Symbol = None) -> str:
|
||||
def get_id(
|
||||
self, version: int, objectType: str | None = None, symbol: Symbol | None = None,
|
||||
) -> str:
|
||||
assert version >= 2
|
||||
# this is not part of the normal name mangling in C++
|
||||
if symbol:
|
||||
@ -4933,7 +4943,7 @@ class Symbol:
|
||||
Symbol.debug_indent -= 2
|
||||
|
||||
def add_name(self, nestedName: ASTNestedName,
|
||||
templatePrefix: ASTTemplateDeclarationPrefix = None) -> Symbol:
|
||||
templatePrefix: ASTTemplateDeclarationPrefix | None = None) -> Symbol:
|
||||
if Symbol.debug_lookup:
|
||||
Symbol.debug_indent += 1
|
||||
Symbol.debug_print("add_name:")
|
||||
@ -6543,7 +6553,7 @@ class DefinitionParser(BaseParser):
|
||||
header = "Error in declarator or parameters-and-qualifiers"
|
||||
raise self._make_multi_error(prevErrors, header) from e
|
||||
|
||||
def _parse_initializer(self, outer: str = None, allowFallback: bool = True,
|
||||
def _parse_initializer(self, outer: str | None = None, allowFallback: bool = True,
|
||||
) -> ASTInitializer:
|
||||
# initializer # global vars
|
||||
# -> brace-or-equal-initializer
|
||||
@ -6592,7 +6602,7 @@ class DefinitionParser(BaseParser):
|
||||
value = self._parse_expression_fallback(fallbackEnd, parser, allow=allowFallback)
|
||||
return ASTInitializer(value)
|
||||
|
||||
def _parse_type(self, named: bool | str, outer: str = None) -> ASTType:
|
||||
def _parse_type(self, named: bool | str, outer: str | None = None) -> ASTType:
|
||||
"""
|
||||
named=False|'maybe'|True: 'maybe' is e.g., for function objects which
|
||||
doesn't need to name the arguments
|
||||
@ -7571,8 +7581,8 @@ class CPPNamespacePopObject(SphinxDirective):
|
||||
|
||||
class AliasNode(nodes.Element):
|
||||
def __init__(self, sig: str, aliasOptions: dict,
|
||||
env: BuildEnvironment = None,
|
||||
parentKey: LookupKey = None) -> None:
|
||||
env: BuildEnvironment | None = None,
|
||||
parentKey: LookupKey | None = None) -> None:
|
||||
super().__init__()
|
||||
self.sig = sig
|
||||
self.aliasOptions = aliasOptions
|
||||
|
@ -143,7 +143,7 @@ class JSObject(ObjectDescription[Tuple[str, str]]):
|
||||
domain.note_object(fullname, self.objtype, node_id, location=signode)
|
||||
|
||||
if 'noindexentry' not in self.options:
|
||||
indextext = self.get_index_text(mod_name, name_obj)
|
||||
indextext = self.get_index_text(mod_name, name_obj) # type: ignore[arg-type]
|
||||
if indextext:
|
||||
self.indexnode['entries'].append(('single', indextext, node_id, '', None))
|
||||
|
||||
@ -438,11 +438,13 @@ class JavaScriptDomain(Domain):
|
||||
searches.reverse()
|
||||
|
||||
newname = None
|
||||
object_ = None
|
||||
for search_name in searches:
|
||||
if search_name in self.objects:
|
||||
newname = search_name
|
||||
object_ = self.objects[search_name]
|
||||
|
||||
return newname, self.objects.get(newname)
|
||||
return newname, object_
|
||||
|
||||
def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
|
||||
typ: str, target: str, node: pending_xref, contnode: Element,
|
||||
@ -463,7 +465,7 @@ class JavaScriptDomain(Domain):
|
||||
name, obj = self.find_obj(env, mod_name, prefix, target, None, 1)
|
||||
if not obj:
|
||||
return []
|
||||
return [('js:' + self.role_for_objtype(obj[2]),
|
||||
return [('js:' + self.role_for_objtype(obj[2]), # type: ignore[operator]
|
||||
make_refnode(builder, fromdocname, obj[0], obj[1], contnode, name))]
|
||||
|
||||
def get_objects(self) -> Iterator[tuple[str, str, str, str, str, int]]:
|
||||
|
@ -250,6 +250,8 @@ class ReSTDomain(Domain):
|
||||
typ: str, target: str, node: pending_xref, contnode: Element,
|
||||
) -> Element | None:
|
||||
objtypes = self.objtypes_for_role(typ)
|
||||
if not objtypes:
|
||||
return None
|
||||
for objtype in objtypes:
|
||||
result = self.objects.get((objtype, target))
|
||||
if result:
|
||||
@ -266,9 +268,10 @@ class ReSTDomain(Domain):
|
||||
result = self.objects.get((objtype, target))
|
||||
if result:
|
||||
todocname, node_id = result
|
||||
results.append(('rst:' + self.role_for_objtype(objtype),
|
||||
make_refnode(builder, fromdocname, todocname, node_id,
|
||||
contnode, target + ' ' + objtype)))
|
||||
results.append(
|
||||
('rst:' + self.role_for_objtype(objtype), # type: ignore[operator]
|
||||
make_refnode(builder, fromdocname, todocname, node_id,
|
||||
contnode, target + ' ' + objtype)))
|
||||
return results
|
||||
|
||||
def get_objects(self) -> Iterator[tuple[str, str, str, str, str, int]]:
|
||||
|
@ -212,7 +212,7 @@ class TocTree:
|
||||
if sub_toc_node.get('hidden', False) and not includehidden:
|
||||
continue
|
||||
for i, entry in enumerate(
|
||||
_entries_from_toctree(sub_toc_node, [refdoc] + parents,
|
||||
_entries_from_toctree(sub_toc_node, [refdoc or ''] + parents,
|
||||
subtree=True),
|
||||
start=sub_toc_node.parent.index(sub_toc_node) + 1,
|
||||
):
|
||||
|
@ -117,7 +117,7 @@ class MockFinder(MetaPathFinder):
|
||||
self.mocked_modules: list[str] = []
|
||||
|
||||
def find_spec(self, fullname: str, path: Sequence[bytes | str] | None,
|
||||
target: ModuleType = None) -> ModuleSpec | None:
|
||||
target: ModuleType | None = None) -> ModuleSpec | None:
|
||||
for modname in self.modnames:
|
||||
# check if fullname is (or is a descendant of) one of our targets
|
||||
if modname == fullname or fullname.startswith(modname + '.'):
|
||||
|
@ -72,6 +72,7 @@ def update_defvalue(app: Sphinx, obj: Any, bound_method: bool) -> None:
|
||||
|
||||
try:
|
||||
function = get_function_def(obj)
|
||||
assert function is not None # for mypy
|
||||
if function.args.defaults or function.args.kw_defaults:
|
||||
sig = inspect.signature(obj)
|
||||
defaults = list(function.args.defaults)
|
||||
@ -90,7 +91,7 @@ def update_defvalue(app: Sphinx, obj: Any, bound_method: bool) -> None:
|
||||
value = ast_unparse(default)
|
||||
parameters[i] = param.replace(default=DefaultValue(value))
|
||||
else:
|
||||
default = kw_defaults.pop(0)
|
||||
default = kw_defaults.pop(0) # type: ignore[assignment]
|
||||
value = get_default_value(lines, default)
|
||||
if value is None:
|
||||
value = ast_unparse(default)
|
||||
|
@ -192,7 +192,7 @@ class TestGroup:
|
||||
def __init__(self, name: str) -> None:
|
||||
self.name = name
|
||||
self.setup: list[TestCode] = []
|
||||
self.tests: list[list[TestCode]] = []
|
||||
self.tests: list[list[TestCode] | tuple[TestCode, None]] = []
|
||||
self.cleanup: list[TestCode] = []
|
||||
|
||||
def add_code(self, code: TestCode, prepend: bool = False) -> None:
|
||||
@ -206,10 +206,13 @@ class TestGroup:
|
||||
elif code.type == 'doctest':
|
||||
self.tests.append([code])
|
||||
elif code.type == 'testcode':
|
||||
self.tests.append([code, None])
|
||||
# "testoutput" may replace the second element
|
||||
self.tests.append((code, None))
|
||||
elif code.type == 'testoutput':
|
||||
if self.tests and len(self.tests[-1]) == 2:
|
||||
self.tests[-1][1] = code
|
||||
if self.tests:
|
||||
latest_test = self.tests[-1]
|
||||
if len(latest_test) == 2:
|
||||
self.tests[-1] = [latest_test[0], code]
|
||||
else:
|
||||
raise RuntimeError(__('invalid TestCode type'))
|
||||
|
||||
@ -233,7 +236,7 @@ class TestCode:
|
||||
|
||||
|
||||
class SphinxDocTestRunner(doctest.DocTestRunner):
|
||||
def summarize(self, out: Callable, verbose: bool = None, # type: ignore
|
||||
def summarize(self, out: Callable, verbose: bool | None = None, # type: ignore
|
||||
) -> tuple[int, int]:
|
||||
string_io = StringIO()
|
||||
old_stdout = sys.stdout
|
||||
@ -339,7 +342,7 @@ Doctest summary
|
||||
if self.total_failures or self.setup_failures or self.cleanup_failures:
|
||||
self.app.statuscode = 1
|
||||
|
||||
def write(self, build_docnames: Iterable[str], updated_docnames: Sequence[str],
|
||||
def write(self, build_docnames: Iterable[str] | None, updated_docnames: Sequence[str],
|
||||
method: str = 'update') -> None:
|
||||
if build_docnames is None:
|
||||
build_docnames = sorted(self.env.all_docs)
|
||||
@ -361,7 +364,7 @@ Doctest summary
|
||||
return filename
|
||||
|
||||
@staticmethod
|
||||
def get_line_number(node: Node) -> int | None:
|
||||
def get_line_number(node: Node) -> int:
|
||||
"""Get the real line number or admit we don't know."""
|
||||
# TODO: Work out how to store or calculate real (file-relative)
|
||||
# line numbers for doctest blocks in docstrings.
|
||||
@ -370,7 +373,7 @@ Doctest summary
|
||||
# not the file. This is correct where it is set, in
|
||||
# `docutils.nodes.Node.setup_child`, but Sphinx should report
|
||||
# relative to the file, not the docstring.
|
||||
return None
|
||||
return None # type: ignore[return-value]
|
||||
if node.line is not None:
|
||||
# TODO: find the root cause of this off by one error.
|
||||
return node.line - 1
|
||||
@ -438,12 +441,12 @@ Doctest summary
|
||||
group.add_code(code)
|
||||
if self.config.doctest_global_setup:
|
||||
code = TestCode(self.config.doctest_global_setup,
|
||||
'testsetup', filename=None, lineno=0)
|
||||
'testsetup', filename='<global_setup>', lineno=0)
|
||||
for group in groups.values():
|
||||
group.add_code(code, prepend=True)
|
||||
if self.config.doctest_global_cleanup:
|
||||
code = TestCode(self.config.doctest_global_cleanup,
|
||||
'testcleanup', filename=None, lineno=0)
|
||||
'testcleanup', filename='<global_cleanup>', lineno=0)
|
||||
for group in groups.values():
|
||||
group.add_code(code)
|
||||
if not groups:
|
||||
|
@ -298,6 +298,7 @@ def render_dot_html(self: HTML5Translator, node: graphviz, code: str, options: d
|
||||
self.body.append('<p class="warning">%s</p>' % alt)
|
||||
self.body.append('</object></div>\n')
|
||||
else:
|
||||
assert outfn is not None
|
||||
with open(outfn + '.map', encoding='utf-8') as mapfile:
|
||||
imgmap = ClickableMapDefinition(outfn + '.map', mapfile.read(), dot=code)
|
||||
if imgmap.clickable:
|
||||
|
@ -25,6 +25,7 @@ def doctree_read(app: Sphinx, doctree: Node) -> None:
|
||||
if not callable(env.config.linkcode_resolve):
|
||||
raise LinkcodeError(
|
||||
"Function `linkcode_resolve` is not given in conf.py")
|
||||
assert resolve_target is not None # for mypy
|
||||
|
||||
domain_keys = {
|
||||
'py': ['module', 'fullname'],
|
||||
|
@ -377,7 +377,7 @@ def _process_docstring(app: Sphinx, what: str, name: str, obj: Any,
|
||||
|
||||
"""
|
||||
result_lines = lines
|
||||
docstring: GoogleDocstring = None
|
||||
docstring: GoogleDocstring
|
||||
if app.config.napoleon_numpy_docstring:
|
||||
docstring = NumpyDocstring(result_lines, app.config, app, what, name,
|
||||
obj, options)
|
||||
@ -390,7 +390,7 @@ def _process_docstring(app: Sphinx, what: str, name: str, obj: Any,
|
||||
|
||||
|
||||
def _skip_member(app: Sphinx, what: str, name: str, obj: Any,
|
||||
skip: bool, options: Any) -> bool:
|
||||
skip: bool, options: Any) -> bool | None:
|
||||
"""Determine if private and special class members are included in docs.
|
||||
|
||||
The following settings in conf.py determine if private and special class
|
||||
|
@ -5,7 +5,7 @@ from __future__ import annotations
|
||||
import locale
|
||||
from gettext import NullTranslations, translation
|
||||
from os import path
|
||||
from typing import Any, Callable
|
||||
from typing import Any, Callable, Iterable
|
||||
|
||||
|
||||
class _TranslationProxy:
|
||||
@ -90,7 +90,7 @@ translators: dict[tuple[str, str], NullTranslations] = {}
|
||||
|
||||
|
||||
def init(
|
||||
locale_dirs: list[str | None],
|
||||
locale_dirs: Iterable[str | None],
|
||||
language: str | None,
|
||||
catalog: str = 'sphinx',
|
||||
namespace: str = 'general',
|
||||
|
@ -145,7 +145,7 @@ class TokenProcessor:
|
||||
|
||||
return self.current
|
||||
|
||||
def fetch_until(self, condition: Any) -> list[Token]:
|
||||
def fetch_until(self, condition: Any) -> list[Token | None]:
|
||||
"""Fetch tokens until specified token appeared.
|
||||
|
||||
.. note:: This also handles parenthesis well.
|
||||
@ -176,7 +176,7 @@ class AfterCommentParser(TokenProcessor):
|
||||
super().__init__(lines)
|
||||
self.comment: str | None = None
|
||||
|
||||
def fetch_rvalue(self) -> list[Token]:
|
||||
def fetch_rvalue(self) -> list[Token | None]:
|
||||
"""Fetch right-hand value of assignment."""
|
||||
tokens = []
|
||||
while self.fetch_token():
|
||||
@ -191,7 +191,7 @@ class AfterCommentParser(TokenProcessor):
|
||||
tokens += self.fetch_until(DEDENT)
|
||||
elif self.current == [OP, ';']: # NoQA: SIM114
|
||||
break
|
||||
elif self.current.kind not in (OP, NAME, NUMBER, STRING):
|
||||
elif self.current and self.current.kind not in {OP, NAME, NUMBER, STRING}:
|
||||
break
|
||||
|
||||
return tokens
|
||||
@ -199,7 +199,7 @@ class AfterCommentParser(TokenProcessor):
|
||||
def parse(self) -> None:
|
||||
"""Parse the code and obtain comment after assignment."""
|
||||
# skip lvalue (or whole of AnnAssign)
|
||||
while not self.fetch_token().match([OP, '='], NEWLINE, COMMENT):
|
||||
while (tok := self.fetch_token()) and not tok.match([OP, '='], NEWLINE, COMMENT):
|
||||
assert self.current
|
||||
|
||||
# skip rvalue (if exists)
|
||||
@ -207,7 +207,7 @@ class AfterCommentParser(TokenProcessor):
|
||||
self.fetch_rvalue()
|
||||
|
||||
if self.current == COMMENT:
|
||||
self.comment = self.current.value
|
||||
self.comment = self.current.value # type: ignore[union-attr]
|
||||
|
||||
|
||||
class VariableCommentPicker(ast.NodeVisitor):
|
||||
@ -502,22 +502,23 @@ class DefinitionFinder(TokenProcessor):
|
||||
def parse_definition(self, typ: str) -> None:
|
||||
"""Parse AST of definition."""
|
||||
name = self.fetch_token()
|
||||
self.context.append(name.value)
|
||||
self.context.append(name.value) # type: ignore[union-attr]
|
||||
funcname = '.'.join(self.context)
|
||||
|
||||
if self.decorator:
|
||||
start_pos = self.decorator.start[0]
|
||||
self.decorator = None
|
||||
else:
|
||||
start_pos = name.start[0]
|
||||
start_pos = name.start[0] # type: ignore[union-attr]
|
||||
|
||||
self.fetch_until([OP, ':'])
|
||||
if self.fetch_token().match(COMMENT, NEWLINE):
|
||||
if self.fetch_token().match(COMMENT, NEWLINE): # type: ignore[union-attr]
|
||||
self.fetch_until(INDENT)
|
||||
self.indents.append((typ, funcname, start_pos))
|
||||
else:
|
||||
# one-liner
|
||||
self.add_definition(funcname, (typ, start_pos, name.end[0]))
|
||||
self.add_definition(funcname,
|
||||
(typ, start_pos, name.end[0])) # type: ignore[union-attr]
|
||||
self.context.pop()
|
||||
|
||||
def finalize_block(self) -> None:
|
||||
@ -525,11 +526,11 @@ class DefinitionFinder(TokenProcessor):
|
||||
definition = self.indents.pop()
|
||||
if definition[0] != 'other':
|
||||
typ, funcname, start_pos = definition
|
||||
end_pos = self.current.end[0] - 1
|
||||
end_pos = self.current.end[0] - 1 # type: ignore[union-attr]
|
||||
while emptyline_re.match(self.get_line(end_pos)):
|
||||
end_pos -= 1
|
||||
|
||||
self.add_definition(funcname, (typ, start_pos, end_pos))
|
||||
self.add_definition(funcname, (typ, start_pos, end_pos)) # type: ignore[arg-type]
|
||||
self.context.pop()
|
||||
|
||||
|
||||
|
@ -111,6 +111,7 @@ class SphinxTestApp(application.Sphinx):
|
||||
docutilsconf: str | None = None,
|
||||
parallel: int = 0,
|
||||
) -> None:
|
||||
assert srcdir is not None
|
||||
|
||||
self.docutils_conf_path = srcdir / 'docutils.conf'
|
||||
if docutilsconf is not None:
|
||||
|
@ -24,7 +24,7 @@ from sphinx.util.nodes import apply_source_workaround, is_smartquotable
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from sphinx.application import Sphinx
|
||||
from sphinx.domain.std import StandardDomain
|
||||
from sphinx.domains.std import StandardDomain
|
||||
from sphinx.environment import BuildEnvironment
|
||||
|
||||
|
||||
@ -163,7 +163,7 @@ class AutoNumbering(SphinxTransform):
|
||||
default_priority = 210
|
||||
|
||||
def apply(self, **kwargs: Any) -> None:
|
||||
domain: StandardDomain = self.env.get_domain('std')
|
||||
domain: StandardDomain = self.env.domains['std']
|
||||
|
||||
for node in self.document.findall(nodes.Element):
|
||||
if (domain.is_enumerable_node(node) and
|
||||
|
@ -300,7 +300,7 @@ class _NodeUpdater:
|
||||
__('inconsistent term references in translated message.' +
|
||||
' original: {0}, translated: {1}'))
|
||||
|
||||
xref_reftarget_map = {}
|
||||
xref_reftarget_map: dict[tuple[str, str, str] | None, dict[str, Any]] = {}
|
||||
|
||||
def get_ref_key(node: addnodes.pending_xref) -> tuple[str, str, str] | None:
|
||||
case = node["refdomain"], node["reftype"]
|
||||
@ -393,10 +393,10 @@ class Locale(SphinxTransform):
|
||||
for _id in node['ids']:
|
||||
parts = split_term_classifiers(msgstr)
|
||||
patch = publish_msgstr(
|
||||
self.app, parts[0], source, node.line, self.config, settings,
|
||||
self.app, parts[0] or '', source, node.line, self.config, settings,
|
||||
)
|
||||
updater.patch = make_glossary_term(
|
||||
self.env, patch, parts[1], source, node.line, _id, self.document,
|
||||
self.env, patch, parts[1] or '', source, node.line, _id, self.document,
|
||||
)
|
||||
processed = True
|
||||
|
||||
@ -497,7 +497,7 @@ class Locale(SphinxTransform):
|
||||
if 'index' in self.config.gettext_additional_targets:
|
||||
# Extract and translate messages for index entries.
|
||||
for node, entries in traverse_translatable_index(self.document):
|
||||
new_entries: list[tuple[str, str, str, str, str]] = []
|
||||
new_entries: list[tuple[str, str, str, str, str | None]] = []
|
||||
for type, msg, tid, main, _key in entries:
|
||||
msg_parts = split_index_msg(type, msg)
|
||||
msgstr_parts = []
|
||||
|
@ -117,6 +117,7 @@ class DataURIExtractor(BaseImageConverter):
|
||||
|
||||
def handle(self, node: nodes.image) -> None:
|
||||
image = parse_data_uri(node['uri'])
|
||||
assert image is not None
|
||||
ext = get_image_extension(image.mimetype)
|
||||
if ext is None:
|
||||
logger.warning(__('Unknown image format: %s...'), node['uri'][:32],
|
||||
@ -140,7 +141,7 @@ class DataURIExtractor(BaseImageConverter):
|
||||
def get_filename_for(filename: str, mimetype: str) -> str:
|
||||
basename = os.path.basename(filename)
|
||||
basename = re.sub(CRITICAL_PATH_CHAR_RE, "_", basename)
|
||||
return os.path.splitext(basename)[0] + get_image_extension(mimetype)
|
||||
return os.path.splitext(basename)[0] + (get_image_extension(mimetype) or '')
|
||||
|
||||
|
||||
class ImageConverter(BaseImageConverter):
|
||||
@ -202,8 +203,12 @@ class ImageConverter(BaseImageConverter):
|
||||
if not self.available:
|
||||
return False
|
||||
else:
|
||||
rule = self.get_conversion_rule(node)
|
||||
return bool(rule)
|
||||
try:
|
||||
self.get_conversion_rule(node)
|
||||
except ValueError:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def get_conversion_rule(self, node: nodes.image) -> tuple[str, str]:
|
||||
for candidate in self.guess_mimetypes(node):
|
||||
@ -212,7 +217,7 @@ class ImageConverter(BaseImageConverter):
|
||||
if rule in self.conversion_rules:
|
||||
return rule
|
||||
|
||||
return None
|
||||
raise ValueError('No conversion rule found')
|
||||
|
||||
def is_available(self) -> bool:
|
||||
"""Return the image converter is available or not."""
|
||||
@ -222,7 +227,8 @@ class ImageConverter(BaseImageConverter):
|
||||
if '?' in node['candidates']:
|
||||
return []
|
||||
elif '*' in node['candidates']:
|
||||
return [guess_mimetype(node['uri'])]
|
||||
guessed = guess_mimetype(node['uri'])
|
||||
return [guessed] if guessed is not None else []
|
||||
else:
|
||||
return node['candidates'].keys()
|
||||
|
||||
|
@ -94,7 +94,8 @@ class ASTBaseBase:
|
||||
return False
|
||||
return True
|
||||
|
||||
__hash__: Callable[[], int] = None
|
||||
# Defining __hash__ = None is not strictly needed when __eq__ is defined.
|
||||
__hash__ = None # type: ignore[assignment]
|
||||
|
||||
def clone(self) -> Any:
|
||||
return deepcopy(self)
|
||||
@ -346,8 +347,7 @@ class BaseParser:
|
||||
def matched_text(self) -> str:
|
||||
if self.last_match is not None:
|
||||
return self.last_match.group()
|
||||
else:
|
||||
return None
|
||||
return ''
|
||||
|
||||
def read_rest(self) -> str:
|
||||
rv = self.definition[self.pos:]
|
||||
|
@ -5,16 +5,18 @@ be domain-specifically transformed to a more appealing presentation.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import contextlib
|
||||
from typing import TYPE_CHECKING, Any, List, Tuple, cast
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.nodes import Node
|
||||
from docutils.nodes import Element, Node
|
||||
from docutils.parsers.rst.states import Inliner
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.environment import BuildEnvironment
|
||||
from sphinx.locale import __
|
||||
from sphinx.util import logging
|
||||
from sphinx.util.nodes import get_node_line
|
||||
from sphinx.util.typing import TextlikeNode
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@ -52,8 +54,15 @@ class Field:
|
||||
is_grouped = False
|
||||
is_typed = False
|
||||
|
||||
def __init__(self, name: str, names: tuple[str, ...] = (), label: str = None,
|
||||
has_arg: bool = True, rolename: str = None, bodyrolename: str = None) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
names: tuple[str, ...] = (),
|
||||
label: str = '',
|
||||
has_arg: bool = True,
|
||||
rolename: str = '',
|
||||
bodyrolename: str = '',
|
||||
) -> None:
|
||||
self.name = name
|
||||
self.names = names
|
||||
self.label = label
|
||||
@ -63,8 +72,8 @@ class Field:
|
||||
|
||||
def make_xref(self, rolename: str, domain: str, target: str,
|
||||
innernode: type[TextlikeNode] = addnodes.literal_emphasis,
|
||||
contnode: Node = None, env: BuildEnvironment = None,
|
||||
inliner: Inliner = None, location: Node = None) -> Node:
|
||||
contnode: Node | None = None, env: BuildEnvironment | None = None,
|
||||
inliner: Inliner | None = None, location: Element | None = None) -> Node:
|
||||
# note: for backwards compatibility env is last, but not optional
|
||||
assert env is not None
|
||||
assert (inliner is None) == (location is None), (inliner, location)
|
||||
@ -83,23 +92,33 @@ class Field:
|
||||
refnode += contnode or innernode(target, target)
|
||||
env.get_domain(domain).process_field_xref(refnode)
|
||||
return refnode
|
||||
lineno = logging.get_source_line(location)[1]
|
||||
lineno = -1
|
||||
if location is not None:
|
||||
with contextlib.suppress(ValueError):
|
||||
lineno = get_node_line(location)
|
||||
ns, messages = role(rolename, target, target, lineno, inliner, {}, [])
|
||||
return nodes.inline(target, '', *ns)
|
||||
|
||||
def make_xrefs(self, rolename: str, domain: str, target: str,
|
||||
innernode: type[TextlikeNode] = addnodes.literal_emphasis,
|
||||
contnode: Node = None, env: BuildEnvironment = None,
|
||||
inliner: Inliner = None, location: Node = None) -> list[Node]:
|
||||
contnode: Node | None = None, env: BuildEnvironment | None = None,
|
||||
inliner: Inliner | None = None, location: Element | None = None,
|
||||
) -> list[Node]:
|
||||
return [self.make_xref(rolename, domain, target, innernode, contnode,
|
||||
env, inliner, location)]
|
||||
|
||||
def make_entry(self, fieldarg: str, content: list[Node]) -> tuple[str, list[Node]]:
|
||||
return (fieldarg, content)
|
||||
|
||||
def make_field(self, types: dict[str, list[Node]], domain: str,
|
||||
item: tuple, env: BuildEnvironment = None,
|
||||
inliner: Inliner = None, location: Node = None) -> nodes.field:
|
||||
def make_field(
|
||||
self,
|
||||
types: dict[str, list[Node]],
|
||||
domain: str,
|
||||
item: tuple,
|
||||
env: BuildEnvironment | None = None,
|
||||
inliner: Inliner | None = None,
|
||||
location: Element | None = None,
|
||||
) -> nodes.field:
|
||||
fieldarg, content = item
|
||||
fieldname = nodes.field_name('', self.label)
|
||||
if fieldarg:
|
||||
@ -135,14 +154,20 @@ class GroupedField(Field):
|
||||
is_grouped = True
|
||||
list_type = nodes.bullet_list
|
||||
|
||||
def __init__(self, name: str, names: tuple[str, ...] = (), label: str = None,
|
||||
rolename: str = None, can_collapse: bool = False) -> None:
|
||||
def __init__(self, name: str, names: tuple[str, ...] = (), label: str = '',
|
||||
rolename: str = '', can_collapse: bool = False) -> None:
|
||||
super().__init__(name, names, label, True, rolename)
|
||||
self.can_collapse = can_collapse
|
||||
|
||||
def make_field(self, types: dict[str, list[Node]], domain: str,
|
||||
items: tuple, env: BuildEnvironment = None,
|
||||
inliner: Inliner = None, location: Node = None) -> nodes.field:
|
||||
def make_field(
|
||||
self,
|
||||
types: dict[str, list[Node]],
|
||||
domain: str,
|
||||
items: tuple,
|
||||
env: BuildEnvironment | None = None,
|
||||
inliner: Inliner | None = None,
|
||||
location: Element | None = None,
|
||||
) -> nodes.field:
|
||||
fieldname = nodes.field_name('', self.label)
|
||||
listnode = self.list_type()
|
||||
for fieldarg, content in items:
|
||||
@ -184,16 +209,29 @@ class TypedField(GroupedField):
|
||||
"""
|
||||
is_typed = True
|
||||
|
||||
def __init__(self, name: str, names: tuple[str, ...] = (), typenames: tuple[str, ...] = (),
|
||||
label: str = None, rolename: str = None, typerolename: str = None,
|
||||
can_collapse: bool = False) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
names: tuple[str, ...] = (),
|
||||
typenames: tuple[str, ...] = (),
|
||||
label: str = '',
|
||||
rolename: str = '',
|
||||
typerolename: str = '',
|
||||
can_collapse: bool = False,
|
||||
) -> None:
|
||||
super().__init__(name, names, label, rolename, can_collapse)
|
||||
self.typenames = typenames
|
||||
self.typerolename = typerolename
|
||||
|
||||
def make_field(self, types: dict[str, list[Node]], domain: str,
|
||||
items: tuple, env: BuildEnvironment = None,
|
||||
inliner: Inliner = None, location: Node = None) -> nodes.field:
|
||||
def make_field(
|
||||
self,
|
||||
types: dict[str, list[Node]],
|
||||
domain: str,
|
||||
items: tuple,
|
||||
env: BuildEnvironment | None = None,
|
||||
inliner: Inliner | None = None,
|
||||
location: Element | None = None,
|
||||
) -> nodes.field:
|
||||
def handle_item(fieldarg: str, content: str) -> nodes.paragraph:
|
||||
par = nodes.paragraph()
|
||||
par.extend(self.make_xrefs(self.rolename, domain, fieldarg,
|
||||
@ -251,7 +289,7 @@ class DocFieldTransformer:
|
||||
"""Transform a single field list *node*."""
|
||||
typemap = self.typemap
|
||||
|
||||
entries: list[nodes.field | tuple[Field, Any, Node]] = []
|
||||
entries: list[nodes.field | tuple[Field, Any, Element]] = []
|
||||
groupindices: dict[str, int] = {}
|
||||
types: dict[str, dict] = {}
|
||||
|
||||
@ -292,7 +330,7 @@ class DocFieldTransformer:
|
||||
target = content[0].astext()
|
||||
xrefs = typed_field.make_xrefs(
|
||||
typed_field.typerolename,
|
||||
self.directive.domain,
|
||||
self.directive.domain or '',
|
||||
target,
|
||||
contnode=content[0],
|
||||
env=self.directive.state.document.settings.env,
|
||||
@ -362,7 +400,8 @@ class DocFieldTransformer:
|
||||
fieldtypes = types.get(fieldtype.name, {})
|
||||
env = self.directive.state.document.settings.env
|
||||
inliner = self.directive.state.inliner
|
||||
new_list += fieldtype.make_field(fieldtypes, self.directive.domain, items,
|
||||
domain = self.directive.domain or ''
|
||||
new_list += fieldtype.make_field(fieldtypes, domain, items,
|
||||
env=env, inliner=inliner, location=location)
|
||||
|
||||
node.replace_self(new_list)
|
||||
|
@ -143,7 +143,7 @@ def patched_get_language() -> Generator[None, None, None]:
|
||||
"""
|
||||
from docutils.languages import get_language
|
||||
|
||||
def patched_get_language(language_code: str, reporter: Reporter = None) -> Any:
|
||||
def patched_get_language(language_code: str, reporter: Reporter | None = None) -> Any:
|
||||
return get_language(language_code)
|
||||
|
||||
try:
|
||||
@ -167,7 +167,7 @@ def patched_rst_get_language() -> Generator[None, None, None]:
|
||||
"""
|
||||
from docutils.parsers.rst.languages import get_language
|
||||
|
||||
def patched_get_language(language_code: str, reporter: Reporter = None) -> Any:
|
||||
def patched_get_language(language_code: str, reporter: Reporter | None = None) -> Any:
|
||||
return get_language(language_code)
|
||||
|
||||
try:
|
||||
@ -378,7 +378,7 @@ def switch_source_input(state: State, content: StringList) -> Generator[None, No
|
||||
get_source_and_line = state.memo.reporter.get_source_and_line # type: ignore
|
||||
|
||||
# replace it by new one
|
||||
state_machine = StateMachine([], None)
|
||||
state_machine = StateMachine([], None) # type: ignore[arg-type]
|
||||
state_machine.input_lines = content
|
||||
state.memo.reporter.get_source_and_line = state_machine.get_source_and_line # type: ignore # noqa: E501
|
||||
|
||||
@ -492,12 +492,12 @@ class SphinxRole:
|
||||
"""Reference to the :class:`.Config` object."""
|
||||
return self.env.config
|
||||
|
||||
def get_source_info(self, lineno: int = None) -> tuple[str, int]:
|
||||
def get_source_info(self, lineno: int | None = None) -> tuple[str, int]:
|
||||
if lineno is None:
|
||||
lineno = self.lineno
|
||||
return self.inliner.reporter.get_source_and_line(lineno) # type: ignore
|
||||
|
||||
def set_source_info(self, node: Node, lineno: int = None) -> None:
|
||||
def set_source_info(self, node: Node, lineno: int | None = None) -> None:
|
||||
node.source, node.line = self.get_source_info(lineno)
|
||||
|
||||
def get_location(self) -> str:
|
||||
|
@ -4,7 +4,7 @@ from __future__ import annotations
|
||||
|
||||
import base64
|
||||
from os import path
|
||||
from typing import TYPE_CHECKING, NamedTuple
|
||||
from typing import TYPE_CHECKING, NamedTuple, overload
|
||||
|
||||
import imagesize
|
||||
|
||||
@ -51,6 +51,16 @@ def get_image_size(filename: str) -> tuple[int, int] | None:
|
||||
return None
|
||||
|
||||
|
||||
@overload
|
||||
def guess_mimetype(filename: PathLike[str] | str, default: str) -> str:
|
||||
...
|
||||
|
||||
|
||||
@overload
|
||||
def guess_mimetype(filename: PathLike[str] | str, default: None = None) -> str | None:
|
||||
...
|
||||
|
||||
|
||||
def guess_mimetype(
|
||||
filename: PathLike[str] | str = '',
|
||||
default: str | None = None,
|
||||
|
@ -19,7 +19,6 @@ from sphinx.util import logging
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from sphinx.builders import Builder
|
||||
from sphinx.domain import IndexEntry
|
||||
from sphinx.environment import BuildEnvironment
|
||||
from sphinx.util.tags import Tags
|
||||
|
||||
@ -300,7 +299,7 @@ def get_prev_node(node: Node) -> Node | None:
|
||||
|
||||
def traverse_translatable_index(
|
||||
doctree: Element,
|
||||
) -> Iterable[tuple[Element, list[IndexEntry]]]:
|
||||
) -> Iterable[tuple[Element, list[tuple[str, str, str, str, str | None]]]]:
|
||||
"""Traverse translatable index node from a document tree."""
|
||||
matcher = NodeMatcher(addnodes.index, inline=False)
|
||||
for node in doctree.findall(matcher): # type: addnodes.index
|
||||
|
@ -38,6 +38,9 @@ class Cell:
|
||||
def __hash__(self) -> int:
|
||||
return hash((self.col, self.row))
|
||||
|
||||
def __bool__(self) -> bool:
|
||||
return self.text != '' and self.col is not None and self.row is not None
|
||||
|
||||
def wrap(self, width: int) -> None:
|
||||
self.wrapped = my_wrap(self.text, width)
|
||||
|
||||
@ -88,7 +91,7 @@ class Table:
|
||||
+--------+--------+
|
||||
|
||||
"""
|
||||
def __init__(self, colwidth: list[int] = None) -> None:
|
||||
def __init__(self, colwidth: list[int] | None = None) -> None:
|
||||
self.lines: list[list[Cell]] = []
|
||||
self.separator = 0
|
||||
self.colwidth: list[int] = (colwidth if colwidth is not None else [])
|
||||
@ -140,7 +143,7 @@ class Table:
|
||||
def _ensure_has_column(self, col: int) -> None:
|
||||
for line in self.lines:
|
||||
while len(line) < col:
|
||||
line.append(None)
|
||||
line.append(Cell())
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "\n".join(repr(line) for line in self.lines)
|
||||
@ -150,6 +153,8 @@ class Table:
|
||||
``self.colwidth`` or ``self.measured_widths``).
|
||||
This takes into account cells spanning multiple columns.
|
||||
"""
|
||||
if cell.row is None or cell.col is None:
|
||||
raise ValueError('Cell co-ordinates have not been set')
|
||||
width = 0
|
||||
for i in range(self[cell.row, cell.col].colspan):
|
||||
width += source[cell.col + i]
|
||||
@ -173,6 +178,8 @@ class Table:
|
||||
cell.wrap(width=self.cell_width(cell, self.colwidth))
|
||||
if not cell.wrapped:
|
||||
continue
|
||||
if cell.row is None or cell.col is None:
|
||||
raise ValueError('Cell co-ordinates have not been set')
|
||||
width = math.ceil(max(column_width(x) for x in cell.wrapped) / cell.colspan)
|
||||
for col in range(cell.col, cell.col + cell.colspan):
|
||||
self.measured_widths[col] = max(self.measured_widths[col], width)
|
||||
@ -358,7 +365,7 @@ class TextWriter(writers.Writer):
|
||||
settings_spec = ('No options here.', '', ())
|
||||
settings_defaults: dict[str, Any] = {}
|
||||
|
||||
output: str = None
|
||||
output: str
|
||||
|
||||
def __init__(self, builder: TextBuilder) -> None:
|
||||
super().__init__()
|
||||
@ -391,7 +398,7 @@ class TextTranslator(SphinxTranslator):
|
||||
self.list_counter: list[int] = []
|
||||
self.sectionlevel = 0
|
||||
self.lineblocklevel = 0
|
||||
self.table: Table = None
|
||||
self.table: Table
|
||||
|
||||
def add_text(self, text: str) -> None:
|
||||
self.states[-1].append((-1, text))
|
||||
@ -400,7 +407,9 @@ class TextTranslator(SphinxTranslator):
|
||||
self.states.append([])
|
||||
self.stateindent.append(indent)
|
||||
|
||||
def end_state(self, wrap: bool = True, end: list[str] = [''], first: str = None) -> None:
|
||||
def end_state(
|
||||
self, wrap: bool = True, end: list[str] | None = [''], first: str | None = None,
|
||||
) -> None:
|
||||
content = self.states.pop()
|
||||
maxindent = sum(self.stateindent)
|
||||
indent = self.stateindent.pop()
|
||||
@ -843,17 +852,17 @@ class TextTranslator(SphinxTranslator):
|
||||
self.stateindent.pop()
|
||||
self.entry.text = text
|
||||
self.table.add_cell(self.entry)
|
||||
self.entry = None
|
||||
del self.entry
|
||||
|
||||
def visit_table(self, node: Element) -> None:
|
||||
if self.table:
|
||||
if hasattr(self, 'table'):
|
||||
raise NotImplementedError('Nested tables are not supported.')
|
||||
self.new_state(0)
|
||||
self.table = Table()
|
||||
|
||||
def depart_table(self, node: Element) -> None:
|
||||
self.add_text(str(self.table))
|
||||
self.table = None
|
||||
del self.table
|
||||
self.end_state(wrap=False)
|
||||
|
||||
def visit_acks(self, node: Element) -> None:
|
||||
|
@ -10,6 +10,8 @@ from sphinx.builders import Builder
|
||||
|
||||
|
||||
class XMLWriter(BaseXMLWriter):
|
||||
output: str
|
||||
|
||||
def __init__(self, builder: Builder) -> None:
|
||||
super().__init__()
|
||||
self.builder = builder
|
||||
@ -34,7 +36,7 @@ class PseudoXMLWriter(BaseXMLWriter):
|
||||
config_section = 'pseudoxml writer'
|
||||
config_section_dependencies = ('writers',)
|
||||
|
||||
output = None
|
||||
output: str
|
||||
"""Final translated form of `document`."""
|
||||
|
||||
def __init__(self, builder: Builder) -> None:
|
||||
|
@ -125,13 +125,12 @@ def test_reporting_with_autodoc(app, status, warning, capfd):
|
||||
written = []
|
||||
app.builder._warn_out = written.append
|
||||
app.builder.build_all()
|
||||
lines = '\n'.join(written).replace(os.sep, '/').split('\n')
|
||||
failures = [l for l in lines if l.startswith('File')]
|
||||
expected = [
|
||||
'File "dir/inner.rst", line 1, in default',
|
||||
'File "dir/bar.py", line ?, in default',
|
||||
'File "foo.py", line ?, in default',
|
||||
'File "index.rst", line 4, in default',
|
||||
]
|
||||
for location in expected:
|
||||
assert location in failures
|
||||
|
||||
failures = [line.replace(os.sep, '/')
|
||||
for line in '\n'.join(written).splitlines()
|
||||
if line.startswith('File')]
|
||||
|
||||
assert 'File "dir/inner.rst", line 1, in default' in failures
|
||||
assert 'File "dir/bar.py", line ?, in default' in failures
|
||||
assert 'File "foo.py", line ?, in default' in failures
|
||||
assert 'File "index.rst", line 4, in default' in failures
|
||||
|
Loading…
Reference in New Issue
Block a user