diff --git a/CHANGES b/CHANGES index 94cfc0de6..5e8d7cb3b 100644 --- a/CHANGES +++ b/CHANGES @@ -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 -------- diff --git a/doc/extdev/index.rst b/doc/extdev/index.rst index fe4c4ff88..b75afc40c 100644 --- a/doc/extdev/index.rst +++ b/doc/extdev/index.rst @@ -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()`` diff --git a/sphinx/builders/gettext.py b/sphinx/builders/gettext.py index b95b4819f..fbec9aae2 100644 --- a/sphinx/builders/gettext.py +++ b/sphinx/builders/gettext.py @@ -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): - 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) + 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 diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index be871b33c..9689b34ff 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -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): diff --git a/sphinx/builders/qthelp.py b/sphinx/builders/qthelp.py index 22ccae5f0..74d0d7c98 100644 --- a/sphinx/builders/qthelp.py +++ b/sphinx/builders/qthelp.py @@ -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'] diff --git a/sphinx/cmd/quickstart.py b/sphinx/cmd/quickstart.py index 89b49cf5a..d919af720 100644 --- a/sphinx/cmd/quickstart.py +++ b/sphinx/cmd/quickstart.py @@ -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 diff --git a/sphinx/config.py b/sphinx/config.py index 2f6051425..c3a81291f 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -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): diff --git a/sphinx/directives/other.py b/sphinx/directives/other.py index e3de4ff62..ab3cfc37c 100644 --- a/sphinx/directives/other.py +++ b/sphinx/directives/other.py @@ -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,27 +386,7 @@ 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 += ' ' % 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 + return BaseInclude.run(self) def setup(app): diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index e6b370d62..1104475f2 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -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)) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 64db2ce9b..df3dfb578 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -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. diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index 1fdd05780..77083cc3c 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -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() diff --git a/sphinx/domains/rst.py b/sphinx/domains/rst.py index 8d81314f6..026d2a59d 100644 --- a/sphinx/domains/rst.py +++ b/sphinx/domains/rst.py @@ -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() diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py index 0fee0e54b..2bbc92f88 100644 --- a/sphinx/domains/std.py +++ b/sphinx/domains/std.py @@ -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)) diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index 26a9b14fb..8411e1ac4 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -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]] diff --git a/sphinx/ext/autosummary/__init__.py b/sphinx/ext/autosummary/__init__.py index fab5d3039..10adfc9ce 100644 --- a/sphinx/ext/autosummary/__init__.py +++ b/sphinx/ext/autosummary/__init__.py @@ -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: diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py index 5cb0d7416..20a06aeb4 100644 --- a/sphinx/ext/autosummary/generate.py +++ b/sphinx/ext/autosummary/generate.py @@ -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 diff --git a/sphinx/ext/coverage.py b/sphinx/ext/coverage.py index c4078a928..76b721adf 100644 --- a/sphinx/ext/coverage.py +++ b/sphinx/ext/coverage.py @@ -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: diff --git a/sphinx/ext/graphviz.py b/sphinx/ext/graphviz.py index f7f97213c..57d63f120 100644 --- a/sphinx/ext/graphviz.py +++ b/sphinx/ext/graphviz.py @@ -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): diff --git a/sphinx/ext/imgmath.py b/sphinx/ext/imgmath.py index 5be272ab1..cbd38b84c 100644 --- a/sphinx/ext/imgmath.py +++ b/sphinx/ext/imgmath.py @@ -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) diff --git a/sphinx/ext/inheritance_diagram.py b/sphinx/ext/inheritance_diagram.py index b6b9c8535..3815f6ba1 100644 --- a/sphinx/ext/inheritance_diagram.py +++ b/sphinx/ext/inheritance_diagram.py @@ -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 diff --git a/sphinx/ext/napoleon/docstring.py b/sphinx/ext/napoleon/docstring.py index 68e73b27e..f64a7cdce 100644 --- a/sphinx/ext/napoleon/docstring.py +++ b/sphinx/ext/napoleon/docstring.py @@ -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():] diff --git a/sphinx/highlighting.py b/sphinx/highlighting.py index 1775f8ddb..e61a891a0 100644 --- a/sphinx/highlighting.py +++ b/sphinx/highlighting.py @@ -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) diff --git a/sphinx/locale/__init__.py b/sphinx/locale/__init__.py index a193ac1d1..a09447cb1 100644 --- a/sphinx/locale/__init__.py +++ b/sphinx/locale/__init__.py @@ -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 diff --git a/sphinx/pycode/__init__.py b/sphinx/pycode/__init__.py index 03e8ffb61..3bd49a305 100644 --- a/sphinx/pycode/__init__.py +++ b/sphinx/pycode/__init__.py @@ -33,8 +33,8 @@ class ModuleAnalyzer: def for_string(cls, string, modname, srcname=''): # 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: diff --git a/sphinx/registry.py b/sphinx/registry.py index 0e7f9bd37..763a3ce54 100644 --- a/sphinx/registry.py +++ b/sphinx/registry.py @@ -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): diff --git a/sphinx/roles.py b/sphinx/roles.py index f50159c66..fb01a876b 100644 --- a/sphinx/roles.py +++ b/sphinx/roles.py @@ -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() diff --git a/sphinx/search/__init__.py b/sphinx/search/__init__.py index d02779fa2..4ae96fb57 100644 --- a/sphinx/search/__init__.py +++ b/sphinx/search/__init__.py @@ -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 diff --git a/sphinx/testing/util.py b/sphinx/testing/util.py index 3e39f3ca8..cd2073aaa 100644 --- a/sphinx/testing/util.py +++ b/sphinx/testing/util.py @@ -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): diff --git a/sphinx/transforms/post_transforms/compat.py b/sphinx/transforms/post_transforms/compat.py index c67f7149f..9a03ff6b5 100644 --- a/sphinx/transforms/post_transforms/compat.py +++ b/sphinx/transforms/post_transforms/compat.py @@ -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 diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py index 5a094877e..8bb18debd 100644 --- a/sphinx/util/__init__.py +++ b/sphinx/util/__init__.py @@ -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]) diff --git a/sphinx/util/docutils.py b/sphinx/util/docutils.py index f1fd66167..9ce4087ac 100644 --- a/sphinx/util/docutils.py +++ b/sphinx/util/docutils.py @@ -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): diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py index 1133ba8fa..541d5f5c5 100644 --- a/sphinx/util/inspect.py +++ b/sphinx/util/inspect.py @@ -322,10 +322,14 @@ class Signature: raise try: - self.annotations = typing.get_type_hints(subject) # type: ignore + 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', diff --git a/sphinx/util/logging.py b/sphinx/util/logging.py index e7aecdc97..a5bd7cd05 100644 --- a/sphinx/util/logging.py +++ b/sphinx/util/logging.py @@ -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): diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py index 78cd34f46..ff73819ee 100644 --- a/sphinx/util/nodes.py +++ b/sphinx/util/nodes.py @@ -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 diff --git a/sphinx/util/rst.py b/sphinx/util/rst.py index 0e49f991a..c46389d7e 100644 --- a/sphinx/util/rst.py +++ b/sphinx/util/rst.py @@ -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 diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index ee7f5d7bd..f9af2a1f8 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -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): diff --git a/tests/roots/test-latex-labels/index.rst b/tests/roots/test-latex-labels/index.rst index 5859fb6d2..c55ee0b04 100644 --- a/tests/roots/test-latex-labels/index.rst +++ b/tests/roots/test-latex-labels/index.rst @@ -16,6 +16,8 @@ figures labeled figure + with a legend + code-blocks ----------- diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index 8e285019f..5da8a90ee 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -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