diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py index 7448e2682..3a592a257 100644 --- a/sphinx/builders/__init__.py +++ b/sphinx/builders/__init__.py @@ -161,8 +161,8 @@ class Builder(object): if candidate: break else: - logger.warn_node('no matching candidate for image URI %r' % node['uri'], - node) + logger.warning('no matching candidate for image URI %r', node['uri'], + location=node) continue node['uri'] = candidate else: diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 52d520a05..cfdbc99d9 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -4885,7 +4885,7 @@ class CPPDomain(Domain): class Warner(object): def warn(self, msg): if emitWarnings: - logger.warn_node(msg, node) + logger.warning(msg, location=node) warner = Warner() parser = DefinitionParser(target, warner, env.config) try: diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index 1da874ea2..886b1f863 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -787,10 +787,9 @@ class PythonDomain(Domain): if not matches: return None elif len(matches) > 1: - logger.warn_node( - 'more than one target found for cross-reference ' - '%r: %s' % (target, ', '.join(match[0] for match in matches)), - node) + logger.warning('more than one target found for cross-reference %r: %s', + target, ', '.join(match[0] for match in matches), + location=node) name, obj = matches[0] if obj[1] == 'module': diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py index eb355ecf2..c351a15af 100644 --- a/sphinx/domains/std.py +++ b/sphinx/domains/std.py @@ -573,8 +573,8 @@ class StandardDomain(Domain): label = node[0].astext() if label in self.data['citations']: path = env.doc2path(self.data['citations'][label][0]) - logger.warn_node('duplicate citation %s, other instance in %s' % - (label, path), node) + logger.warning('duplicate citation %s, other instance in %s', label, path, + location=node) self.data['citations'][label] = (docname, node['ids'][0]) def note_labels(self, env, docname, document): @@ -596,8 +596,9 @@ class StandardDomain(Domain): # link and object descriptions continue if name in labels: - logger.warn_node('duplicate label %s, ' % name + 'other instance ' - 'in ' + env.doc2path(labels[name][0]), node) + logger.warning('duplicate label %s, ' % name + 'other instance ' + 'in ' + env.doc2path(labels[name][0]), + location=node) anonlabels[name] = docname, labelid if node.tagname == 'section': sectname = clean_astext(node[0]) # node[0] == title node @@ -688,7 +689,7 @@ class StandardDomain(Domain): return None if env.config.numfig is False: - logger.warn_node('numfig is disabled. :numref: is ignored.', node) + logger.warning('numfig is disabled. :numref: is ignored.', location=node) return contnode target_node = env.get_doctree(docname).ids.get(labelid) @@ -701,7 +702,8 @@ class StandardDomain(Domain): if fignumber is None: return contnode except ValueError: - logger.warn_node("no number is assigned for %s: %s" % (figtype, labelid), node) + logger.warning("no number is assigned for %s: %s", figtype, labelid, + location=node) return contnode try: @@ -711,7 +713,7 @@ class StandardDomain(Domain): title = env.config.numfig_format.get(figtype, '') if figname is None and '%{name}' in title: - logger.warn_node('the link has no caption: %s' % title, node) + logger.warning('the link has no caption: %s', title, location=node) return contnode else: fignum = '.'.join(map(str, fignumber)) @@ -725,10 +727,10 @@ class StandardDomain(Domain): # old style format (cf. "Fig.%s") newtitle = title % fignum except KeyError as exc: - logger.warn_node('invalid numfig_format: %s (%r)' % (title, exc), node) + logger.warning('invalid numfig_format: %s (%r)', title, exc, location=node) return contnode except TypeError: - logger.warn_node('invalid numfig_format: %s' % title, node) + logger.warning('invalid numfig_format: %s', title, location=node) return contnode return self.build_reference_node(fromdocname, builder, diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py index 4b6e9bf31..1fb9ec19e 100644 --- a/sphinx/environment/__init__.py +++ b/sphinx/environment/__init__.py @@ -902,8 +902,8 @@ class BuildEnvironment(object): rel_filename, filename = self.relfn2path(targetname, docname) self.dependencies[docname].add(rel_filename) if not os.access(filename, os.R_OK): - logger.warn_node('download file not readable: %s' % filename, - node) + logger.warning('download file not readable: %s', filename, + location=node) continue uniquename = self.dlfiles.add_file(docname, filename) node['filename'] = uniquename @@ -921,8 +921,8 @@ class BuildEnvironment(object): if mimetype not in candidates: globbed.setdefault(mimetype, []).append(new_imgpath) except (OSError, IOError) as err: - logger.warn_node('image file %s not readable: %s' % - (filename, err), node) + logger.warning('image file %s not readable: %s', filename, err, + location=node) for key, files in iteritems(globbed): candidates[key] = sorted(files, key=len)[0] # select by similarity @@ -934,13 +934,13 @@ class BuildEnvironment(object): node['candidates'] = candidates = {} imguri = node['uri'] if imguri.startswith('data:'): - logger.warn_node('image data URI found. some builders might not support', node, - type='image', subtype='data_uri') + logger.warning('image data URI found. some builders might not support', + location=node, type='image', subtype='data_uri') candidates['?'] = imguri continue elif imguri.find('://') != -1: - logger.warn_node('nonlocal image URI found: %s' % imguri, node, - type='image', subtype='nonlocal_uri') + logger.warning('nonlocal image URI found: %s', imguri, + location=node, type='image', subtype='nonlocal_uri') candidates['?'] = imguri continue rel_imgpath, full_imgpath = self.relfn2path(imguri, docname) @@ -969,8 +969,8 @@ class BuildEnvironment(object): for imgpath in itervalues(candidates): self.dependencies[docname].add(imgpath) if not os.access(path.join(self.srcdir, imgpath), os.R_OK): - logger.warn_node('image file not readable: %s' % imgpath, - node) + logger.warning('image file not readable: %s', imgpath, + location=node) continue self.images.add_file(docname, imgpath) @@ -1183,7 +1183,8 @@ class BuildEnvironment(object): (node['refdomain'], typ) else: msg = '%r reference target not found: %%(target)s' % typ - logger.warn_node(msg % {'target': target}, node, type='ref', subtype=typ) + logger.warning(msg % {'target': target}, + location=node, type='ref', subtype=typ) def _resolve_doc_reference(self, builder, refdoc, node, contnode): # type: (Builder, unicode, nodes.Node, nodes.Node) -> nodes.Node @@ -1234,9 +1235,9 @@ class BuildEnvironment(object): return None if len(results) > 1: nice_results = ' or '.join(':%s:' % r[0] for r in results) - logger.warn_node('more than one target found for \'any\' cross-' - 'reference %r: could be %s' % (target, nice_results), - node) + logger.warning('more than one target found for \'any\' cross-' + 'reference %r: could be %s', target, nice_results, + location=node) res_role, newnode = results[0] # Override "any" class with the actual role type to get the styling # approximately correct. diff --git a/sphinx/environment/managers/indexentries.py b/sphinx/environment/managers/indexentries.py index 43e3b4c83..ef9c84d02 100644 --- a/sphinx/environment/managers/indexentries.py +++ b/sphinx/environment/managers/indexentries.py @@ -55,7 +55,7 @@ class IndexEntries(EnvironmentManager): for entry in node['entries']: split_index_msg(entry[0], entry[1]) except ValueError as exc: - logger.warn_node(str(exc), node) + logger.warning(str(exc), location=node) node.parent.remove(node) else: for entry in node['entries']: diff --git a/sphinx/environment/managers/toctree.py b/sphinx/environment/managers/toctree.py index 64937b7fa..1df3f0999 100644 --- a/sphinx/environment/managers/toctree.py +++ b/sphinx/environment/managers/toctree.py @@ -317,15 +317,13 @@ class Toctree(EnvironmentManager): refnode.children = [nodes.Text(title)] if not toc.children: # empty toc means: no titles will show up in the toctree - logger.warn_node( - 'toctree contains reference to document %r that ' - 'doesn\'t have a title: no link will be generated' - % ref, toctreenode) + logger.warning('toctree contains reference to document %r that ' + 'doesn\'t have a title: no link will be generated', + ref, location=toctreenode) except KeyError: # this is raised if the included file does not exist - logger.warn_node( - 'toctree contains reference to nonexisting document %r' - % ref, toctreenode) + logger.warning('toctree contains reference to nonexisting document %r', + ref, location=toctreenode) else: # if titles_only is given, only keep the main title and # sub-toctrees diff --git a/sphinx/ext/autosectionlabel.py b/sphinx/ext/autosectionlabel.py index 78cb3c042..d45ba66a6 100644 --- a/sphinx/ext/autosectionlabel.py +++ b/sphinx/ext/autosectionlabel.py @@ -26,8 +26,9 @@ def register_sections_as_label(app, document): sectname = clean_astext(node[0]) if name in labels: - logger.warn_node('duplicate label %s, ' % name + 'other instance ' - 'in ' + app.env.doc2path(labels[name][0]), node) + logger.warning('duplicate label %s, ' % name + 'other instance ' + 'in ' + app.env.doc2path(labels[name][0]), + location=node) anonlabels[name] = docname, labelid labels[name] = docname, labelid, sectname diff --git a/sphinx/ext/todo.py b/sphinx/ext/todo.py index e8d8201df..f575e7462 100644 --- a/sphinx/ext/todo.py +++ b/sphinx/ext/todo.py @@ -100,7 +100,8 @@ def process_todos(app, doctree): }) if env.config.todo_emit_warnings: - logger.warn_node("TODO entry found: %s" % node[1].astext(), node) + logger.warning("TODO entry found: %s", node[1].astext(), + location=node) class TodoList(Directive): diff --git a/sphinx/transforms/__init__.py b/sphinx/transforms/__init__.py index fad0d0038..4eb65af6e 100644 --- a/sphinx/transforms/__init__.py +++ b/sphinx/transforms/__init__.py @@ -181,7 +181,7 @@ class AutoIndexUpgrader(Transform): if 'entries' in node and any(len(entry) == 4 for entry in node['entries']): msg = ('4 column based index found. ' 'It might be a bug of extensions you use: %r' % node['entries']) - logger.warn_node(msg, node) + logger.warning(msg, location=node) for i, entry in enumerate(node['entries']): if len(entry) == 4: node['entries'][i] = entry + (None,) diff --git a/sphinx/transforms/i18n.py b/sphinx/transforms/i18n.py index addd617d4..8663c573d 100644 --- a/sphinx/transforms/i18n.py +++ b/sphinx/transforms/i18n.py @@ -274,8 +274,8 @@ class Locale(Transform): old_foot_refs = node.traverse(is_autonumber_footnote_ref) new_foot_refs = patch.traverse(is_autonumber_footnote_ref) if len(old_foot_refs) != len(new_foot_refs): - logger.warn_node('inconsistent footnote references in ' - 'translated message', node) + logger.warning('inconsistent footnote references in translated message', + location=node) old_foot_namerefs = {} # type: Dict[unicode, List[nodes.footnote_reference]] for r in old_foot_refs: old_foot_namerefs.setdefault(r.get('refname'), []).append(r) @@ -309,7 +309,8 @@ class Locale(Transform): old_refs = node.traverse(is_refnamed_ref) new_refs = patch.traverse(is_refnamed_ref) if len(old_refs) != len(new_refs): - logger.warn_node('inconsistent references in translated message', node) + logger.warning('inconsistent references in translated message', + location=node) old_ref_names = [r['refname'] for r in old_refs] new_ref_names = [r['refname'] for r in new_refs] orphans = list(set(old_ref_names) - set(new_ref_names)) @@ -337,7 +338,8 @@ class Locale(Transform): new_refs = patch.traverse(is_refnamed_footnote_ref) refname_ids_map = {} if len(old_refs) != len(new_refs): - logger.warn_node('inconsistent references in translated message', node) + logger.warning('inconsistent references in translated message', + location=node) for old in old_refs: refname_ids_map[old["refname"]] = old["ids"] for new in new_refs: @@ -352,7 +354,8 @@ class Locale(Transform): new_refs = patch.traverse(addnodes.pending_xref) xref_reftarget_map = {} if len(old_refs) != len(new_refs): - logger.warn_node('inconsistent term references in translated message', node) + logger.warning('inconsistent term references in translated message', + location=node) def get_ref_key(node): # type: (nodes.Node) -> Tuple[unicode, unicode, unicode] diff --git a/sphinx/util/logging.py b/sphinx/util/logging.py index 95b14be9a..ecec2649a 100644 --- a/sphinx/util/logging.py +++ b/sphinx/util/logging.py @@ -16,6 +16,7 @@ from contextlib import contextmanager from collections import defaultdict from six import PY2, StringIO +from docutils import nodes from docutils.utils import get_source_line from sphinx.errors import SphinxWarning @@ -92,23 +93,6 @@ class SphinxWarningLogRecord(logging.LogRecord): class SphinxLoggerAdapter(logging.LoggerAdapter): """LoggerAdapter allowing ``type`` and ``subtype`` keywords.""" - def warn_node(self, message, node, **kwargs): - # type: (unicode, nodes.Node, Any) -> None - """Emit a warning for specific node. - - :param message: a message of warning - :param node: a node related with the warning - """ - (source, line) = get_source_line(node) - if source and line: - kwargs['location'] = "%s:%s" % (source, line) - elif source: - kwargs['location'] = "%s:" % source - elif line: - kwargs['location'] = ":%s" % line - - self.warning(message, **kwargs) - def log(self, level, msg, *args, **kwargs): # type: (Union[int, str], unicode, Any, Any) -> None if isinstance(level, int): @@ -374,6 +358,16 @@ class WarningLogRecordTranslator(logging.Filter): record.location = '%s' % self.app.env.doc2path(docname) else: record.location = None + elif isinstance(location, nodes.Node): + (source, line) = get_source_line(location) + if source and line: + record.location = "%s:%s" % (source, line) + elif source: + record.location = "%s:" % source + elif line: + record.location = ":%s" % line + else: + record.location = None elif location and ':' not in location: record.location = '%s' % self.app.env.doc2path(location) diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py index a5aa58444..4c574c242 100644 --- a/sphinx/util/nodes.py +++ b/sphinx/util/nodes.py @@ -362,8 +362,8 @@ def process_only_nodes(doctree, tags): try: ret = tags.eval_condition(node['expr']) except Exception as err: - logger.warn_node('exception while evaluating only ' - 'directive expression: %s' % err, node) + logger.warning('exception while evaluating only directive expression: %s', err, + location=node) node.replace_self(node.children or nodes.comment()) else: if ret: diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py index 0daaffd82..6a455cdb4 100644 --- a/sphinx/writers/html.py +++ b/sphinx/writers/html.py @@ -302,7 +302,7 @@ class HTMLTranslator(BaseTranslator): if figtype: if len(node['ids']) == 0: msg = 'Any IDs not assigned for %s node' % node.tagname - logger.warn_node(msg, node) + logger.warning(msg, location=node) else: append_fignumber(figtype, node['ids'][0]) @@ -525,8 +525,8 @@ class HTMLTranslator(BaseTranslator): if not ('width' in node and 'height' in node): size = get_image_size(os.path.join(self.builder.srcdir, olduri)) if size is None: - logger.warn_node('Could not obtain image size. ' - ':scale: option is ignored.', node) + logger.warning('Could not obtain image size. :scale: option is ignored.', + location=node) else: if 'width' not in node: node['width'] = str(size[0]) diff --git a/tests/test_util_logging.py b/tests/test_util_logging.py index 59c899369..0fc7277a7 100644 --- a/tests/test_util_logging.py +++ b/tests/test_util_logging.py @@ -199,30 +199,24 @@ def test_warning_location(app, status, warning): assert 'index.txt:10: WARNING: message2' in warning.getvalue() logger.warning('message3', location=None) - assert '\x1b[31mWARNING: message3' in warning.getvalue() # \x1b[31m = darkred - - -@with_app() -def test_warn_node(app, status, warning): - logging.setup(app, status, warning) - logger = logging.getLogger(__name__) + assert colorize('darkred', 'WARNING: message3') in warning.getvalue() node = nodes.Node() node.source, node.line = ('index.txt', 10) - logger.warn_node('message1', node) - assert 'index.txt:10: WARNING: message1' in warning.getvalue() + logger.warning('message4', location=node) + assert 'index.txt:10: WARNING: message4' in warning.getvalue() node.source, node.line = ('index.txt', None) - logger.warn_node('message2', node) - assert 'index.txt:: WARNING: message2' in warning.getvalue() + logger.warning('message5', location=node) + assert 'index.txt:: WARNING: message5' in warning.getvalue() node.source, node.line = (None, 10) - logger.warn_node('message3', node) - assert ':10: WARNING: message3' in warning.getvalue() + logger.warning('message6', location=node) + assert ':10: WARNING: message6' in warning.getvalue() node.source, node.line = (None, None) - logger.warn_node('message4', node) - assert '\x1b[31mWARNING: message4' in warning.getvalue() # \x1b[31m = darkred + logger.warning('message7', location=node) + assert colorize('darkred', 'WARNING: message7') in warning.getvalue() @with_app()