Merge pull request #5539 from tk0miya/master

Fix mypy violations (and merge 1.8 branch to master)
This commit is contained in:
Takeshi KOMIYA 2018-10-16 10:23:25 +09:00 committed by GitHub
commit c97441e2af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 118 additions and 105 deletions

View File

@ -68,6 +68,15 @@ Features added
Bugs fixed
----------
* #5490: latex: enumerated list causes a crash with recommonmark
* #5492: sphinx-build fails to build docs w/ Python < 3.5.2
* #3704: latex: wrong ``\label`` positioning for figures with a legend
* #5496: C++, fix assertion when a symbol is declared more than twice.
* #5493: gettext: crashed with broken template
* #5495: csv-table directive with file option in included file is broken (refs:
#4821)
* #5498: autodoc: unable to find type hints for a ``functools.partial``
Testing
--------

View File

@ -104,7 +104,13 @@ But, sometimes, the change of interface are needed for some reasons. In such
cases, we've marked them as deprecated. And they are kept during the two
major versions (for more details, please see :ref:`deprecation-policy`).
The following is a list of deprecated interface.
The following is a list of deprecated interfaces.
.. tabularcolumns:: |>{\raggedright}\Y{.4}|>{\centering}\Y{.1}|>{\centering}\Y{.12}|>{\raggedright\arraybackslash}\Y{.38}|
.. |LaTeXHyphenate| raw:: latex
\hspace{0pt}
.. list-table:: deprecated APIs
:header-rows: 1
@ -112,8 +118,8 @@ The following is a list of deprecated interface.
:widths: 40, 10, 10, 40
* - Target
- Deprecated
- (will be) Removed
- |LaTeXHyphenate|\ Deprecated
- (will be) Removed
- Alternatives
* - ``suffix`` argument of ``BuildEnvironment.doc2path()``

View File

@ -22,6 +22,7 @@ from six import StringIO
from sphinx.builders import Builder
from sphinx.domains.python import pairindextypes
from sphinx.errors import ThemeError
from sphinx.locale import __
from sphinx.util import split_index_msg, logging, status_iterator
from sphinx.util.console import bold # type: ignore
@ -247,11 +248,14 @@ class MessageCatalogBuilder(I18nBuilder):
for template in status_iterator(files, __('reading templates... '), "purple", # type: ignore # NOQA
len(files), self.app.verbosity):
try:
with open(template, 'r', encoding='utf-8') as f: # type: ignore
context = f.read()
for line, meth, msg in extract_translations(context):
origin = MsgOrigin(template, line)
self.catalogs['sphinx'].add(msg, origin)
except Exception as exc:
raise ThemeError('%s: %r' % (template, exc))
def build(self, docnames, summary=None, method='update'):
# type: (Iterable[unicode], unicode, unicode) -> None

View File

@ -374,7 +374,7 @@ class StandaloneHTMLBuilder(Builder):
if filename and '://' not in filename:
filename = posixpath.join('_static', filename)
self.script_files.append(JavaScript(filename, **kwargs)) # type: ignore
self.script_files.append(JavaScript(filename, **kwargs))
@property
def default_translator_class(self):

View File

@ -214,7 +214,7 @@ class QtHelpBuilder(StandaloneHTMLBuilder):
def keyword_item(self, name, ref):
# type: (unicode, Any) -> unicode
matchobj = _idpattern.match(name) # type: ignore
matchobj = _idpattern.match(name)
if matchobj:
groupdict = matchobj.groupdict()
shortname = groupdict['title']

View File

@ -492,7 +492,7 @@ def valid_dir(d):
if not path.isdir(dir):
return False
if set(['Makefile', 'make.bat']) & set(os.listdir(dir)): # type: ignore
if set(['Makefile', 'make.bat']) & set(os.listdir(dir)):
return False
if d['sep']:
@ -508,7 +508,7 @@ def valid_dir(d):
d['dot'] + 'templates',
d['master'] + d['suffix'],
]
if set(reserved_names) & set(os.listdir(dir)): # type: ignore
if set(reserved_names) & set(os.listdir(dir)):
return False
return True

View File

@ -424,7 +424,7 @@ def correct_copyright_year(app, config):
for k in ('copyright', 'epub_copyright'):
if k in config:
replace = r'\g<1>%s' % format_date('%Y')
config[k] = copyright_year_re.sub(replace, config[k]) # type: ignore
config[k] = copyright_year_re.sub(replace, config[k])
def check_confval_types(app, config):

View File

@ -8,7 +8,6 @@
"""
import re
from contextlib import contextmanager
from docutils import nodes
from docutils.parsers.rst import directives
@ -380,7 +379,6 @@ class Include(BaseInclude, SphinxDirective):
def run(self):
# type: () -> List[nodes.Node]
current_filename = self.env.doc2path(self.env.docname)
if self.arguments[0].startswith('<') and \
self.arguments[0].endswith('>'):
# docutils "standard" includes, do not do path processing
@ -388,29 +386,9 @@ class Include(BaseInclude, SphinxDirective):
rel_filename, filename = self.env.relfn2path(self.arguments[0])
self.arguments[0] = filename
self.env.note_included(filename)
with patched_warnings(self, current_filename):
return BaseInclude.run(self)
@contextmanager
def patched_warnings(directive, parent_filename):
# type: (BaseInclude, unicode) -> Generator[None, None, None]
"""Add includee filename to the warnings during inclusion."""
try:
original = directive.state_machine.insert_input
def insert_input(input_lines, source):
# type: (Any, unicode) -> None
source += ' <included from %s>' % parent_filename
original(input_lines, source)
# patch insert_input() temporarily
directive.state_machine.insert_input = insert_input
yield
finally:
directive.state_machine.insert_input = original
def setup(app):
# type: (Sphinx) -> Dict[unicode, Any]
directives.register_directive('toctree', TocTree)

View File

@ -83,7 +83,7 @@ class CObject(ObjectDescription):
def _parse_type(self, node, ctype):
# type: (nodes.Node, unicode) -> None
# add cross-ref nodes for all words
for part in [_f for _f in wsplit_re.split(ctype) if _f]: # type: ignore
for part in [_f for _f in wsplit_re.split(ctype) if _f]:
tnode = nodes.Text(part, part)
if part[0] in string.ascii_letters + '_' and \
part not in self.stopwords:
@ -98,10 +98,10 @@ class CObject(ObjectDescription):
def _parse_arglist(self, arglist):
# type: (unicode) -> Iterator[unicode]
while True:
m = c_funcptr_arg_sig_re.match(arglist) # type: ignore
m = c_funcptr_arg_sig_re.match(arglist)
if m:
yield m.group()
arglist = c_funcptr_arg_sig_re.sub('', arglist) # type: ignore
arglist = c_funcptr_arg_sig_re.sub('', arglist)
if ',' in arglist:
_, arglist = arglist.split(',', 1)
else:
@ -118,9 +118,9 @@ class CObject(ObjectDescription):
# type: (unicode, addnodes.desc_signature) -> unicode
"""Transform a C signature into RST nodes."""
# first try the function pointer signature regex, it's more specific
m = c_funcptr_sig_re.match(sig) # type: ignore
m = c_funcptr_sig_re.match(sig)
if m is None:
m = c_sig_re.match(sig) # type: ignore
m = c_sig_re.match(sig)
if m is None:
raise ValueError('no match')
rettype, name, arglist, const = m.groups()
@ -162,7 +162,7 @@ class CObject(ObjectDescription):
arg = arg.strip()
param = addnodes.desc_parameter('', '', noemph=True)
try:
m = c_funcptr_arg_sig_re.match(arg) # type: ignore
m = c_funcptr_arg_sig_re.match(arg)
if m:
self._parse_type(param, m.group(1) + '(')
param += nodes.emphasis(m.group(2), m.group(2))

View File

@ -4010,14 +4010,20 @@ class Symbol:
noDecl = []
withDecl = []
dupDecl = []
for s in symbols:
if s.declaration is None:
noDecl.append(s)
elif s.isRedeclaration:
dupDecl.append(s)
else:
withDecl.append(s)
if Symbol.debug_lookup:
print(" #noDecl: ", len(noDecl))
print(" #withDecl:", len(withDecl))
print(" #dupDecl: ", len(dupDecl))
if len(dupDecl) > 0:
assert len(withDecl) > 0
# assert len(noDecl) <= 1 # we should fill in symbols when they are there
# TODO: enable assertion when we at some point find out how to do cleanup
# With partial builds we may start with a large symbol tree stripped of declarations.

View File

@ -157,7 +157,7 @@ class PyXrefMixin:
if split_contnode:
contnode = nodes.Text(sub_target)
if delims_re.match(sub_target): # type: ignore
if delims_re.match(sub_target):
results.append(contnode or innernode(sub_target, sub_target))
else:
results.append(self.make_xref(rolename, domain, sub_target,
@ -252,7 +252,7 @@ class PyObject(ObjectDescription):
* it is stripped from the displayed name if present
* it is added to the full name (return value) if not present
"""
m = py_sig_re.match(sig) # type: ignore
m = py_sig_re.match(sig)
if m is None:
raise ValueError
name_prefix, name, arglist, retann = m.groups()

View File

@ -77,7 +77,7 @@ def parse_directive(d):
if not dir.startswith('.'):
# Assume it is a directive without syntax
return (dir, '')
m = dir_sig_re.match(dir) # type: ignore
m = dir_sig_re.match(dir)
if not m:
return (dir, '')
parsed_dir, parsed_args = m.groups()

View File

@ -155,10 +155,10 @@ class Cmdoption(ObjectDescription):
# type: (unicode, addnodes.desc_signature) -> unicode
"""Transform an option description into RST nodes."""
count = 0
firstname = ''
firstname = '' # type: unicode
for potential_option in sig.split(', '):
potential_option = potential_option.strip()
m = option_desc_re.match(potential_option) # type: ignore
m = option_desc_re.match(potential_option)
if not m:
logger.warning(__('Malformed option description %r, should '
'look like "opt", "-opt args", "--opt args", '
@ -387,7 +387,7 @@ def token_xrefs(text):
# type: (unicode) -> List[nodes.Node]
retnodes = []
pos = 0
for m in token_re.finditer(text): # type: ignore
for m in token_re.finditer(text):
if m.start() > pos:
txt = text[pos:m.start()]
retnodes.append(nodes.Text(txt, txt))

View File

@ -300,7 +300,7 @@ class Documenter:
# an autogenerated one
try:
explicit_modname, path, base, args, retann = \
py_ext_sig_re.match(self.name).groups() # type: ignore
py_ext_sig_re.match(self.name).groups()
except AttributeError:
logger.warning(__('invalid signature for auto%s (%r)') % (self.objtype, self.name),
type='autodoc')
@ -314,7 +314,7 @@ class Documenter:
modname = None
parents = []
self.modname, self.objpath = self.resolve_name(modname, parents, path, base)
self.modname, self.objpath = self.resolve_name(modname, parents, path, base) # type: ignore # NOQA
if not self.modname:
return False
@ -934,7 +934,7 @@ class DocstringSignatureMixin:
if not doclines:
continue
# match first line of docstring against signature RE
match = py_ext_sig_re.match(doclines[0]) # type: ignore
match = py_ext_sig_re.match(doclines[0])
if not match:
continue
exmod, path, base, args, retann = match.groups()
@ -951,7 +951,7 @@ class DocstringSignatureMixin:
result = args, retann
# don't look any further
break
return result
return result # type: ignore
def get_doc(self, encoding=None, ignore=1):
# type: (unicode, int) -> List[List[unicode]]

View File

@ -423,7 +423,7 @@ def mangle_signature(sig, max_chars=30):
opt_re = re.compile(r"^(.*, |)([a-zA-Z0-9_*]+)=")
while s:
m = opt_re.search(s) # type: ignore
m = opt_re.search(s)
if not m:
# The rest are arguments
args = s.split(', ')
@ -481,7 +481,7 @@ def extract_summary(doc, document):
summary = doc[0].strip()
else:
# Try to find the "first sentence", which may span multiple lines
sentences = periods_re.split(" ".join(doc)) # type: ignore
sentences = periods_re.split(" ".join(doc))
if len(sentences) == 1:
summary = sentences[0].strip()
else:

View File

@ -302,11 +302,11 @@ def find_autosummary_in_lines(lines, module=None, filename=None):
template = None
current_module = module
in_autosummary = False
base_indent = ""
base_indent = "" # type: unicode
for line in lines:
if in_autosummary:
m = toctree_arg_re.match(line) # type: ignore
m = toctree_arg_re.match(line)
if m:
toctree = m.group(1)
if filename:
@ -314,7 +314,7 @@ def find_autosummary_in_lines(lines, module=None, filename=None):
toctree)
continue
m = template_arg_re.match(line) # type: ignore
m = template_arg_re.match(line)
if m:
template = m.group(1).strip()
continue
@ -322,7 +322,7 @@ def find_autosummary_in_lines(lines, module=None, filename=None):
if line.strip().startswith(':'):
continue # skip options
m = autosummary_item_re.match(line) # type: ignore
m = autosummary_item_re.match(line)
if m:
name = m.group(1).strip()
if name.startswith('~'):
@ -338,7 +338,7 @@ def find_autosummary_in_lines(lines, module=None, filename=None):
in_autosummary = False
m = autosummary_re.match(line) # type: ignore
m = autosummary_re.match(line)
if m:
in_autosummary = True
base_indent = m.group(1)
@ -346,7 +346,7 @@ def find_autosummary_in_lines(lines, module=None, filename=None):
template = None
continue
m = automodule_re.search(line) # type: ignore
m = automodule_re.search(line)
if m:
current_module = m.group(1).strip()
# recurse into the automodule docstring
@ -354,7 +354,7 @@ def find_autosummary_in_lines(lines, module=None, filename=None):
current_module, filename=filename))
continue
m = module_re.match(line) # type: ignore
m = module_re.match(line)
if m:
current_module = m.group(2)
continue

View File

@ -100,7 +100,7 @@ class CoverageBuilder(Builder):
# Fetch all the info from the header files
c_objects = self.env.domaindata['c']['objects']
for filename in self.c_sourcefiles:
undoc = set()
undoc = set() # type: Set[Tuple[unicode, unicode]]
with open(filename, 'r') as f:
for line in f:
for key, regex in self.c_regexes:

View File

@ -59,7 +59,7 @@ class ClickableMapDefinition:
def parse(self, dot=None):
# type: (unicode) -> None
matched = self.maptag_re.match(self.content[0]) # type: ignore
matched = self.maptag_re.match(self.content[0])
if not matched:
raise GraphvizError('Invalid clickable map file found: %s' % self.filename)
@ -72,7 +72,7 @@ class ClickableMapDefinition:
self.content[0] = self.content[0].replace('%3', self.id)
for line in self.content:
if self.href_re.search(line): # type: ignore
if self.href_re.search(line):
self.clickable.append(line)
def generate_clickable_map(self):

View File

@ -189,7 +189,7 @@ def convert_dvi_to_png(dvipath, builder):
depth = None
if builder.config.imgmath_use_preview:
for line in stdout.splitlines():
matched = depth_re.match(line) # type: ignore
matched = depth_re.match(line)
if matched:
depth = int(matched.group(1))
write_png_depth(filename, depth)

View File

@ -77,7 +77,7 @@ def try_import(objname):
__import__(objname)
return sys.modules.get(objname) # type: ignore
except (ImportError, ValueError): # ValueError,py27 -> ImportError,py3
matched = module_sig_re.match(objname) # type: ignore
matched = module_sig_re.match(objname)
if not matched:
return None
@ -88,7 +88,7 @@ def try_import(objname):
return None
try:
__import__(modname)
return getattr(sys.modules.get(modname), attrname, None)
return getattr(sys.modules.get(modname), attrname, None) # type: ignore
except (ImportError, ValueError): # ValueError,py27 -> ImportError,py3
return None

View File

@ -239,7 +239,7 @@ class GoogleDocstring(UnicodeMixin):
_name, _type, _desc = before, '', after # type: unicode, unicode, unicode
if parse_type:
match = _google_typed_arg_regex.match(before) # type: ignore
match = _google_typed_arg_regex.match(before)
if match:
_name = match.group(1)
_type = match.group(2)
@ -500,9 +500,9 @@ class GoogleDocstring(UnicodeMixin):
# type: (List[unicode]) -> bool
if not lines:
return False
if _bullet_list_regex.match(lines[0]): # type: ignore
if _bullet_list_regex.match(lines[0]):
return True
if _enumerated_list_regex.match(lines[0]): # type: ignore
if _enumerated_list_regex.match(lines[0]):
return True
if len(lines) < 2 or lines[0].endswith('::'):
return False
@ -576,7 +576,7 @@ class GoogleDocstring(UnicodeMixin):
section = self._consume_section_header()
self._is_in_section = True
self._section_indent = self._get_current_indent()
if _directive_regex.match(section): # type: ignore
if _directive_regex.match(section):
lines = [section] + self._consume_to_next_section()
else:
lines = self._sections[section.lower()](section)
@ -704,7 +704,7 @@ class GoogleDocstring(UnicodeMixin):
fields = self._consume_fields(parse_type=False, prefer_type=True)
lines = [] # type: List[unicode]
for _name, _type, _desc in fields:
m = self._name_rgx.match(_type).groupdict() # type: ignore
m = self._name_rgx.match(_type).groupdict()
if m['role']:
_type = m['name']
_type = ' ' + _type if _type else ''
@ -766,9 +766,9 @@ class GoogleDocstring(UnicodeMixin):
# type: (unicode) -> Tuple[unicode, unicode, unicode]
before_colon = []
after_colon = []
colon = ''
colon = '' # type: unicode
found_colon = False
for i, source in enumerate(_xref_regex.split(line)): # type: ignore
for i, source in enumerate(_xref_regex.split(line)):
if found_colon:
after_colon.append(source)
else:
@ -962,7 +962,7 @@ class NumpyDocstring(GoogleDocstring):
section, underline = self._line_iter.peek(2)
section = section.lower()
if section in self._sections and isinstance(underline, string_types):
return bool(_numpy_section_regex.match(underline)) # type: ignore
return bool(_numpy_section_regex.match(underline))
elif self._directive_sections:
if _directive_regex.match(section):
for directive_section in self._directive_sections:
@ -996,7 +996,7 @@ class NumpyDocstring(GoogleDocstring):
def parse_item_name(text):
# type: (unicode) -> Tuple[unicode, unicode]
"""Match ':role:`name`' or 'name'"""
m = self._name_rgx.match(text) # type: ignore
m = self._name_rgx.match(text)
if m:
g = m.groups()
if g[1] is None:
@ -1020,7 +1020,7 @@ class NumpyDocstring(GoogleDocstring):
if not line.strip():
continue
m = self._name_rgx.match(line) # type: ignore
m = self._name_rgx.match(line)
if m and line[m.end():].strip().startswith(':'):
push_item(current_func, rest)
current_func, line = line[:m.end()], line[m.end():]

View File

@ -149,8 +149,8 @@ class PygmentsBridge:
# trim doctest options if wanted
if isinstance(lexer, PythonConsoleLexer) and self.trim_doctest_flags:
source = doctest.blankline_re.sub('', source) # type: ignore
source = doctest.doctestopt_re.sub('', source) # type: ignore
source = doctest.blankline_re.sub('', source)
source = doctest.doctestopt_re.sub('', source)
# highlight via Pygments
formatter = self.get_formatter(**kwargs)

View File

@ -44,7 +44,7 @@ class _TranslationProxy(UserString):
if not args:
# not called with "function" and "arguments", but a plain string
return text_type(func)
return object.__new__(cls) # type: ignore
return object.__new__(cls)
def __getnewargs__(self):
# type: () -> Tuple

View File

@ -33,8 +33,8 @@ class ModuleAnalyzer:
def for_string(cls, string, modname, srcname='<string>'):
# type: (unicode, unicode, unicode) -> ModuleAnalyzer
if isinstance(string, bytes):
return cls(BytesIO(string), modname, srcname) # type: ignore
return cls(StringIO(string), modname, srcname, decoded=True) # type: ignore
return cls(BytesIO(string), modname, srcname)
return cls(StringIO(string), modname, srcname, decoded=True)
@classmethod
def for_file(cls, filename, modname):
@ -43,7 +43,7 @@ class ModuleAnalyzer:
return cls.cache['file', filename]
try:
with open(filename, 'rb') as f:
obj = cls(f, modname, filename) # type: ignore
obj = cls(f, modname, filename)
cls.cache['file', filename] = obj
except Exception as err:
if '.egg/' in filename:

View File

@ -358,7 +358,7 @@ class SphinxComponentRegistry:
# type: (unicode, Type[nodes.NodeVisitor], bool) -> None
logger.debug('[app] Change of translator for the %s builder.' % name)
if name in self.translators and not override:
raise ExtensionError(__('Translatoro for %r already exists') % name)
raise ExtensionError(__('Translator for %r already exists') % name)
self.translators[name] = translator
def add_translation_handlers(self, node, **kwargs):

View File

@ -260,7 +260,7 @@ def menusel_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
text = utils.unescape(text)
if typ == 'menuselection':
text = text.replace('-->', u'\N{TRIANGULAR BULLET}')
spans = _amp_re.split(text) # type: ignore
spans = _amp_re.split(text)
node = nodes.inline(rawtext=rawtext)
for i, span in enumerate(spans):
@ -342,7 +342,7 @@ _abbr_re = re.compile(r'\((.*)\)$', re.S)
def abbr_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
# type: (unicode, unicode, unicode, int, Inliner, Dict, List[unicode]) -> Tuple[List[nodes.Node], List[nodes.Node]] # NOQA
text = utils.unescape(text)
m = _abbr_re.search(text) # type: ignore
m = _abbr_re.search(text)
if m is None:
return [addnodes.abbreviation(text, text, **options)], []
abbr = text[:m.start()].strip()

View File

@ -85,7 +85,7 @@ var Stemmer = function() {
at white spaces, which should be enough for most languages except CJK
languages.
"""
return self._word_re.findall(input) # type: ignore
return self._word_re.findall(input)
def stem(self, word):
# type: (unicode) -> unicode

View File

@ -194,7 +194,7 @@ def remove_unicode_literals(s):
# type: (unicode) -> unicode
warnings.warn('remove_unicode_literals() is deprecated.',
RemovedInSphinx40Warning)
return _unicode_literals_re.sub(lambda x: x.group(1) or x.group(2), s) # type: ignore
return _unicode_literals_re.sub(lambda x: x.group(1) or x.group(2), s)
def find_files(root, suffix=None):

View File

@ -10,7 +10,6 @@
"""
import warnings
from typing import TYPE_CHECKING
from docutils import nodes
from docutils.writers.docutils_xml import XMLTranslator
@ -20,7 +19,8 @@ from sphinx.deprecation import RemovedInSphinx30Warning
from sphinx.transforms import SphinxTransform
from sphinx.util import logging
if TYPE_CHECKING:
if False:
# For type annotation
from typing import Any, Callable, Dict, Iterable, List, Tuple # NOQA
from docutils.parsers.rst.states import Inliner # NOQA
from docutils.writers.html4css1 import Writer # NOQA

View File

@ -377,7 +377,7 @@ def detect_encoding(readline):
except UnicodeDecodeError:
return None
matches = _coding_re.findall(line_string) # type: ignore
matches = _coding_re.findall(line_string)
if not matches:
return None
return get_normal_name(matches[0])

View File

@ -225,13 +225,13 @@ class sphinx_domains:
class WarningStream:
def write(self, text):
# type: (unicode) -> None
matched = report_re.search(text) # type: ignore
matched = report_re.search(text)
if not matched:
logger.warning(text.rstrip("\r\n"))
else:
location, type, level = matched.groups()
message = report_re.sub('', text).rstrip() # type: ignore
logger.log(type, message, location=location)
message = report_re.sub('', text).rstrip()
logger.log(type, message, location=location) # type: ignore
class LoggingReporter(Reporter):

View File

@ -322,10 +322,14 @@ class Signature:
raise
try:
if ispartial(subject):
# get_type_hints() does not support partial objects
self.annotations = {} # type: Dict[str, Any]
else:
self.annotations = typing.get_type_hints(subject) # type: ignore
except Exception as exc:
if (3, 5, 0) <= sys.version_info < (3, 5, 3) and isinstance(exc, AttributeError):
# python 3.5.2 raises ValueError for partial objects.
# python 3.5.2 raises ValueError for classmethod-ized partial objects.
self.annotations = {}
else:
logger.warning('Invalid type annotation found on %r. Ignored: %r',

View File

@ -153,7 +153,7 @@ class SphinxLoggerAdapter(logging.LoggerAdapter):
def handle(self, record):
# type: (logging.LogRecord) -> None
self.logger.handle(record) # type: ignore
self.logger.handle(record)
class WarningStreamHandler(logging.StreamHandler):
@ -411,7 +411,8 @@ class SphinxLogRecordTranslator(logging.Filter):
def filter(self, record): # type: ignore
# type: (SphinxWarningLogRecord) -> bool
if isinstance(record, logging.LogRecord):
record.__class__ = self.LogRecordClass # force subclassing to handle location
# force subclassing to handle location
record.__class__ = self.LogRecordClass # type: ignore
location = getattr(record, 'location', None)
if isinstance(location, tuple):

View File

@ -335,7 +335,7 @@ def clean_astext(node):
def split_explicit_title(text):
# type: (unicode) -> Tuple[bool, unicode, unicode]
"""Split role content into title and target, if given."""
match = explicit_title_re.match(text) # type: ignore
match = explicit_title_re.match(text)
if match:
return True, match.group(1), match.group(2)
return False, text, text

View File

@ -30,7 +30,7 @@ logger = logging.getLogger(__name__)
def escape(text):
# type: (unicode) -> unicode
text = symbols_re.sub(r'\\\1', text) # type: ignore
text = symbols_re.sub(r'\\\1', text)
text = re.sub(r'^\.', r'\.', text) # escape a dot at top
return text

View File

@ -1679,7 +1679,6 @@ class LaTeXTranslator(nodes.NodeVisitor):
def visit_figure(self, node):
# type: (nodes.Node) -> None
labels = self.hypertarget_to(node)
if self.table:
# TODO: support align option
if 'width' in node:
@ -1691,7 +1690,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.body.append('\\begin{sphinxfigure-in-table}\n\\centering\n')
if any(isinstance(child, nodes.caption) for child in node):
self.body.append('\\capstart')
self.context.append(labels + '\\end{sphinxfigure-in-table}\\relax\n')
self.context.append('\\end{sphinxfigure-in-table}\\relax\n')
elif node.get('align', '') in ('left', 'right'):
length = None
if 'width' in node:
@ -1700,7 +1699,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
length = self.latex_image_length(node[0]['width'])
self.body.append('\\begin{wrapfigure}{%s}{%s}\n\\centering' %
(node['align'] == 'right' and 'r' or 'l', length or '0pt'))
self.context.append(labels + '\\end{wrapfigure}\n')
self.context.append('\\end{wrapfigure}\n')
elif self.in_minipage:
self.body.append('\n\\begin{center}')
self.context.append('\\end{center}\n')
@ -1709,7 +1708,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
self.elements['figure_align'])
if any(isinstance(child, nodes.caption) for child in node):
self.body.append('\\capstart\n')
self.context.append(labels + '\\end{figure}\n')
self.context.append('\\end{figure}\n')
def depart_figure(self, node):
# type: (nodes.Node) -> None
@ -1730,6 +1729,9 @@ class LaTeXTranslator(nodes.NodeVisitor):
def depart_caption(self, node):
# type: (nodes.Node) -> None
self.body.append('}')
if isinstance(node.parent, nodes.figure):
labels = self.hypertarget_to(node.parent)
self.body.append(labels)
self.in_caption -= 1
def visit_legend(self, node):

View File

@ -16,6 +16,8 @@ figures
labeled figure
with a legend
code-blocks
-----------

View File

@ -1296,7 +1296,8 @@ def test_latex_labels(app, status, warning):
r'\label{\detokenize{index:figure1}}'
r'\end{figure}' in result)
assert (r'\caption{labeled figure}'
r'\label{\detokenize{index:figure3}}'
'\\label{\detokenize{index:figure3}}\n'
'\\begin{sphinxlegend}\nwith a legend\n\\end{sphinxlegend}\n'
r'\end{figure}' in result)
# code-blocks