Merge branch '2229_suppress_warnings'

This commit is contained in:
Takeshi KOMIYA 2016-03-03 20:40:38 +09:00
commit 3939c4d684
12 changed files with 110 additions and 19 deletions

View File

@ -18,6 +18,8 @@ Features added
* Define ``\crossref`` macro to redefine the style of references * Define ``\crossref`` macro to redefine the style of references
* #2301: Texts in the classic html theme should be hyphenated. * #2301: Texts in the classic html theme should be hyphenated.
* #2355: Define ``\termref`` macro to redefine the style of ``term`` roles. * #2355: Define ``\termref`` macro to redefine the style of ``term`` roles.
* Add :confval:`suppress_warnings` to suppress arbitrary warning message (experimental)
* #2229: Fix no warning is given for unknown options
Bugs fixed Bugs fixed
---------- ----------

View File

@ -212,6 +212,26 @@ General configuration
.. versionadded:: 0.5 .. versionadded:: 0.5
.. confval:: suppress_warnings
A list of warning types to suppress arbitrary warning messages.
Sphinx supports following warning types:
* ref.term
* ref.ref
* ref.numref
* ref.keyword
* ref.option
* ref.citation
* ref.doc
You can choose from these types.
Now, this option should be considered *experimental*.
.. versionadded:: 1.4
.. confval:: needs_sphinx .. confval:: needs_sphinx
If set to a ``major.minor`` version string like ``'1.1'``, Sphinx will If set to a ``major.minor`` version string like ``'1.1'``, Sphinx will

View File

@ -41,6 +41,7 @@ from sphinx.util import pycompat # noqa: imported for side-effects
from sphinx.util import import_object from sphinx.util import import_object
from sphinx.util.tags import Tags from sphinx.util.tags import Tags
from sphinx.util.osutil import ENOENT from sphinx.util.osutil import ENOENT
from sphinx.util.logging import is_suppressed_warning
from sphinx.util.console import bold, lightgray, darkgray, darkgreen, \ from sphinx.util.console import bold, lightgray, darkgray, darkgreen, \
term_width_line term_width_line
@ -318,7 +319,7 @@ class Sphinx(object):
wfile.flush() wfile.flush()
self.messagelog.append(message) self.messagelog.append(message)
def warn(self, message, location=None, prefix='WARNING: '): def warn(self, message, location=None, prefix='WARNING: ', type=None, subtype=None):
"""Emit a warning. """Emit a warning.
If *location* is given, it should either be a tuple of (docname, lineno) If *location* is given, it should either be a tuple of (docname, lineno)
@ -326,12 +327,17 @@ class Sphinx(object):
*prefix* usually should not be changed. *prefix* usually should not be changed.
*type* and *subtype* are used to suppress warnings with :confval:`suppress_warnings`.
.. note:: .. note::
For warnings emitted during parsing, you should use For warnings emitted during parsing, you should use
:meth:`.BuildEnvironment.warn` since that will collect all :meth:`.BuildEnvironment.warn` since that will collect all
warnings during parsing for later output. warnings during parsing for later output.
""" """
if is_suppressed_warning(type, subtype, self.config.suppress_warnings):
return
if isinstance(location, tuple): if isinstance(location, tuple):
docname, lineno = location docname, lineno = location
if docname: if docname:

View File

@ -260,12 +260,12 @@ class Builder(object):
# while reading, collect all warnings from docutils # while reading, collect all warnings from docutils
warnings = [] warnings = []
self.env.set_warnfunc(lambda *args: warnings.append(args)) self.env.set_warnfunc(lambda *args, **kwargs: warnings.append((args, kwargs)))
updated_docnames = set(self.env.update(self.config, self.srcdir, updated_docnames = set(self.env.update(self.config, self.srcdir,
self.doctreedir, self.app)) self.doctreedir, self.app))
self.env.set_warnfunc(self.warn) self.env.set_warnfunc(self.warn)
for warning in warnings: for warning, kwargs in warnings:
self.warn(*warning) self.warn(*warning, **kwargs)
doccount = len(updated_docnames) doccount = len(updated_docnames)
self.info(bold('looking for now-outdated files... '), nonl=1) self.info(bold('looking for now-outdated files... '), nonl=1)
@ -350,7 +350,7 @@ class Builder(object):
self.info('done') self.info('done')
warnings = [] warnings = []
self.env.set_warnfunc(lambda *args: warnings.append(args)) self.env.set_warnfunc(lambda *args, **kwargs: warnings.append((args, kwargs)))
if self.parallel_ok: if self.parallel_ok:
# number of subprocesses is parallel-1 because the main process # number of subprocesses is parallel-1 because the main process
# is busy loading doctrees and doing write_doc_serialized() # is busy loading doctrees and doing write_doc_serialized()
@ -366,13 +366,16 @@ class Builder(object):
doctree = self.env.get_and_resolve_doctree(docname, self) doctree = self.env.get_and_resolve_doctree(docname, self)
self.write_doc_serialized(docname, doctree) self.write_doc_serialized(docname, doctree)
self.write_doc(docname, doctree) self.write_doc(docname, doctree)
for warning in warnings: for warning, kwargs in warnings:
self.warn(*warning) self.warn(*warning, **kwargs)
def _write_parallel(self, docnames, warnings, nproc): def _write_parallel(self, docnames, warnings, nproc):
def write_process(docs): def write_process(docs):
local_warnings = [] local_warnings = []
self.env.set_warnfunc(lambda *args: local_warnings.append(args))
def warnfunc(*args, **kwargs):
local_warnings.append((args, kwargs))
self.env.set_warnfunc(warnfunc)
for docname, doctree in docs: for docname, doctree in docs:
self.write_doc(docname, doctree) self.write_doc(docname, doctree)
return local_warnings return local_warnings
@ -402,8 +405,8 @@ class Builder(object):
self.info(bold('waiting for workers...')) self.info(bold('waiting for workers...'))
tasks.join() tasks.join()
for warning in warnings: for warning, kwargs in warnings:
self.warn(*warning) self.warn(*warning, **kwargs)
def prepare_writing(self, docnames): def prepare_writing(self, docnames):
"""A place where you can add logic before :meth:`write_doc` is run""" """A place where you can add logic before :meth:`write_doc` is run"""

View File

@ -76,6 +76,7 @@ class Config(object):
templates_path = ([], 'html'), templates_path = ([], 'html'),
template_bridge = (None, 'html', string_classes), template_bridge = (None, 'html', string_classes),
keep_warnings = (False, 'env'), keep_warnings = (False, 'env'),
suppress_warnings = ([], 'env'),
modindex_common_prefix = ([], 'html'), modindex_common_prefix = ([], 'html'),
rst_epilog = (None, 'env', string_classes), rst_epilog = (None, 'env', string_classes),
rst_prolog = (None, 'env', string_classes), rst_prolog = (None, 'env', string_classes),

View File

@ -447,7 +447,7 @@ class StandardDomain(Domain):
'productionlist': ProductionList, 'productionlist': ProductionList,
} }
roles = { roles = {
'option': OptionXRefRole(), 'option': OptionXRefRole(warn_dangling=True),
'envvar': EnvVarXRefRole(), 'envvar': EnvVarXRefRole(),
# links to tokens in grammar productions # links to tokens in grammar productions
'token': XRefRole(), 'token': XRefRole(),
@ -485,6 +485,7 @@ class StandardDomain(Domain):
'the label must precede a section header)', 'the label must precede a section header)',
'numref': 'undefined label: %(target)s', 'numref': 'undefined label: %(target)s',
'keyword': 'unknown keyword: %(target)s', 'keyword': 'unknown keyword: %(target)s',
'option': 'unknown option: %(target)s',
} }
enumerable_nodes = { # node_class -> (figtype, title_getter) enumerable_nodes = { # node_class -> (figtype, title_getter)

View File

@ -242,7 +242,7 @@ class BuildEnvironment:
self.versioning_condition = condition self.versioning_condition = condition
self.versioning_compare = compare self.versioning_compare = compare
def warn(self, docname, msg, lineno=None): def warn(self, docname, msg, lineno=None, **kwargs):
"""Emit a warning. """Emit a warning.
This differs from using ``app.warn()`` in that the warning may not This differs from using ``app.warn()`` in that the warning may not
@ -250,11 +250,11 @@ class BuildEnvironment:
the update of the environment. the update of the environment.
""" """
# strange argument order is due to backwards compatibility # strange argument order is due to backwards compatibility
self._warnfunc(msg, (docname, lineno)) self._warnfunc(msg, (docname, lineno), **kwargs)
def warn_node(self, msg, node): def warn_node(self, msg, node, **kwargs):
"""Like :meth:`warn`, but with source information taken from *node*.""" """Like :meth:`warn`, but with source information taken from *node*."""
self._warnfunc(msg, '%s:%s' % get_source_line(node)) self._warnfunc(msg, '%s:%s' % get_source_line(node), **kwargs)
def clear_doc(self, docname): def clear_doc(self, docname):
"""Remove all traces of a source file in the inventory.""" """Remove all traces of a source file in the inventory."""
@ -576,7 +576,7 @@ class BuildEnvironment:
def read_process(docs): def read_process(docs):
self.app = app self.app = app
self.warnings = [] self.warnings = []
self.set_warnfunc(lambda *args: self.warnings.append(args)) self.set_warnfunc(lambda *args, **kwargs: self.warnings.append((args, kwargs)))
for docname in docs: for docname in docs:
self.read_doc(docname, app) self.read_doc(docname, app)
# allow pickling self to send it back # allow pickling self to send it back
@ -603,8 +603,8 @@ class BuildEnvironment:
app.info(bold('waiting for workers...')) app.info(bold('waiting for workers...'))
tasks.join() tasks.join()
for warning in warnings: for warning, kwargs in warnings:
self._warnfunc(*warning) self._warnfunc(*warning, **kwargs)
def check_dependents(self, already): def check_dependents(self, already):
to_rewrite = self.assign_section_numbers() + self.assign_figure_numbers() to_rewrite = self.assign_section_numbers() + self.assign_figure_numbers()
@ -1540,7 +1540,7 @@ class BuildEnvironment:
(node['refdomain'], typ) (node['refdomain'], typ)
else: else:
msg = '%r reference target not found: %%(target)s' % typ msg = '%r reference target not found: %%(target)s' % typ
self.warn_node(msg % {'target': target}, node) self.warn_node(msg % {'target': target}, node, type='ref', subtype=typ)
def _resolve_doc_reference(self, builder, node, contnode): def _resolve_doc_reference(self, builder, node, contnode):
# directly reference to document by source name; # directly reference to document by source name;

29
sphinx/util/logging.py Normal file
View File

@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
"""
sphinx.util.logging
~~~~~~~~~~~~~~~~~~~
Logging utility functions for Sphinx.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
def is_suppressed_warning(type, subtype, suppress_warnings):
"""Check the warning is suppressed or not."""
if type is None:
return False
for warning_type in suppress_warnings:
if '.' in warning_type:
target, subtarget = warning_type.split('.', 1)
else:
target, subtarget = warning_type, None
if target == type:
if (subtype is None or subtarget is None or
subtarget == subtype or subtarget == '*'):
return True
return False

View File

@ -40,6 +40,7 @@ HTML_WARNINGS = ENV_WARNINGS + """\
%(root)s/images.txt:20: WARNING: no matching candidate for image URI u'foo.\\*' %(root)s/images.txt:20: WARNING: no matching candidate for image URI u'foo.\\*'
%(root)s/markup.txt:271: WARNING: Could not lex literal_block as "c". Highlighting skipped. %(root)s/markup.txt:271: WARNING: Could not lex literal_block as "c". Highlighting skipped.
%(root)s/footnote.txt:60: WARNING: citation not found: missing %(root)s/footnote.txt:60: WARNING: citation not found: missing
%(root)s/markup.txt:160: WARNING: unknown option: &option
""" """
if PY3: if PY3:

View File

@ -24,6 +24,7 @@ from test_build_html import ENV_WARNINGS
LATEX_WARNINGS = ENV_WARNINGS + """\ LATEX_WARNINGS = ENV_WARNINGS + """\
%(root)s/markup.txt:160: WARNING: unknown option: &option
%(root)s/footnote.txt:60: WARNING: citation not found: missing %(root)s/footnote.txt:60: WARNING: citation not found: missing
%(root)s/images.txt:20: WARNING: no matching candidate for image URI u'foo.\\*' %(root)s/images.txt:20: WARNING: no matching candidate for image URI u'foo.\\*'
%(root)s/markup.txt:271: WARNING: Could not lex literal_block as "c". Highlighting skipped. %(root)s/markup.txt:271: WARNING: Could not lex literal_block as "c". Highlighting skipped.

View File

@ -23,6 +23,7 @@ from test_build_html import ENV_WARNINGS
TEXINFO_WARNINGS = ENV_WARNINGS + """\ TEXINFO_WARNINGS = ENV_WARNINGS + """\
%(root)s/markup.txt:160: WARNING: unknown option: &option
%(root)s/footnote.txt:60: WARNING: citation not found: missing %(root)s/footnote.txt:60: WARNING: citation not found: missing
%(root)s/images.txt:20: WARNING: no matching candidate for image URI u'foo.\\*' %(root)s/images.txt:20: WARNING: no matching candidate for image URI u'foo.\\*'
%(root)s/images.txt:29: WARNING: no matching candidate for image URI u'svgimg.\\*' %(root)s/images.txt:29: WARNING: no matching candidate for image URI u'svgimg.\\*'

View File

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
"""
test_util_logging
~~~~~~~~~~~~~~~~~
Test logging util.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from __future__ import print_function
from sphinx.util.logging import is_suppressed_warning
def test_is_suppressed_warning():
suppress_warnings = ["ref", "files.*", "rest.duplicated_labels"]
assert is_suppressed_warning(None, None, suppress_warnings) is False
assert is_suppressed_warning("ref", None, suppress_warnings) is True
assert is_suppressed_warning("ref", "numref", suppress_warnings) is True
assert is_suppressed_warning("ref", "option", suppress_warnings) is True
assert is_suppressed_warning("files", "image", suppress_warnings) is True
assert is_suppressed_warning("files", "stylesheet", suppress_warnings) is True
assert is_suppressed_warning("rest", "syntax", suppress_warnings) is False
assert is_suppressed_warning("rest", "duplicated_labels", suppress_warnings) is True