Fix #3926: Add `autodoc_warningiserror to suppress the behavior of -W` option

This commit is contained in:
Takeshi KOMIYA 2017-07-15 19:27:04 +09:00
parent bb79a0c4d0
commit bfd71cd77b
5 changed files with 71 additions and 2 deletions

View File

@ -13,6 +13,9 @@ Deprecated
Features added Features added
-------------- --------------
* #3926: Add ``autodoc_warningiserror`` to suppress the behavior of ``-W``
option during importing target modules on autodoc
Bugs fixed Bugs fixed
---------- ----------

View File

@ -386,6 +386,13 @@ There are also new config values that you can set:
This config value only requires to declare the top-level modules that This config value only requires to declare the top-level modules that
should be mocked. should be mocked.
.. confval:: autodoc_warningiserror
This value controls the behavior of :option:`sphinx-build -W` during
importing modules.
If ``False`` is given, autodoc forcely suppresses the error if the imported
module emits warnings. By default, ``True``.
Docstring preprocessing Docstring preprocessing
----------------------- -----------------------

View File

@ -654,7 +654,8 @@ class Documenter(object):
logger.debug('[autodoc] import %s', self.modname) logger.debug('[autodoc] import %s', self.modname)
with warnings.catch_warnings(): with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=ImportWarning) warnings.filterwarnings("ignore", category=ImportWarning)
__import__(self.modname) with logging.skip_warningiserror(not self.env.config.autodoc_warningiserror):
__import__(self.modname)
parent = None parent = None
obj = self.module = sys.modules[self.modname] obj = self.module = sys.modules[self.modname]
logger.debug('[autodoc] => %r', obj) logger.debug('[autodoc] => %r', obj)
@ -1883,6 +1884,7 @@ def setup(app):
app.add_config_value('autodoc_default_flags', [], True) app.add_config_value('autodoc_default_flags', [], True)
app.add_config_value('autodoc_docstring_signature', True, True) app.add_config_value('autodoc_docstring_signature', True, True)
app.add_config_value('autodoc_mock_imports', [], True) app.add_config_value('autodoc_mock_imports', [], True)
app.add_config_value('autodoc_warningiserror', True, True)
app.add_event('autodoc-process-docstring') app.add_event('autodoc-process-docstring')
app.add_event('autodoc-process-signature') app.add_event('autodoc-process-signature')
app.add_event('autodoc-skip-member') app.add_event('autodoc-skip-member')

View File

@ -249,6 +249,27 @@ def pending_logging():
memhandler.flushTo(logger) memhandler.flushTo(logger)
@contextmanager
def skip_warningiserror(skip=True):
# type: (bool) -> Generator
"""contextmanager to skip WarningIsErrorFilter for a while."""
logger = logging.getLogger()
if skip is False:
yield
else:
try:
disabler = DisableWarningIsErrorFilter()
for handler in logger.handlers:
# use internal method; filters.insert() directly to install disabler
# before WarningIsErrorFilter
handler.filters.insert(0, disabler)
yield
finally:
for handler in logger.handlers:
handler.removeFilter(disabler)
class LogCollector(object): class LogCollector(object):
def __init__(self): def __init__(self):
# type: () -> None # type: () -> None
@ -330,7 +351,10 @@ class WarningIsErrorFilter(logging.Filter):
def filter(self, record): def filter(self, record):
# type: (logging.LogRecord) -> bool # type: (logging.LogRecord) -> bool
if self.app.warningiserror: if getattr(record, 'skip_warningsiserror', False):
# disabled by DisableWarningIsErrorFilter
return True
elif self.app.warningiserror:
location = getattr(record, 'location', '') location = getattr(record, 'location', '')
if location: if location:
raise SphinxWarning(location + ":" + record.msg % record.args) raise SphinxWarning(location + ":" + record.msg % record.args)
@ -340,6 +364,15 @@ class WarningIsErrorFilter(logging.Filter):
return True return True
class DisableWarningIsErrorFilter(logging.Filter):
"""Disable WarningIsErrorFilter if this filter installed."""
def filter(self, record):
# type: (logging.LogRecord) -> bool
record.skip_warningsiserror = True # type: ignore
return True
class WarningLogRecordTranslator(logging.Filter): class WarningLogRecordTranslator(logging.Filter):
"""Converts a log record to one Sphinx expects """Converts a log record to one Sphinx expects

View File

@ -272,3 +272,27 @@ def test_output_with_unencodable_char(app, status, warning):
status.seek(0) status.seek(0)
logger.info(u"unicode \u206d...") logger.info(u"unicode \u206d...")
assert status.getvalue() == "unicode ?...\n" assert status.getvalue() == "unicode ?...\n"
def test_skip_warningiserror(app, status, warning):
logging.setup(app, status, warning)
logger = logging.getLogger(__name__)
app.warningiserror = True
with logging.skip_warningiserror():
logger.warning('message')
# if False, warning raises SphinxWarning exception
with pytest.raises(SphinxWarning):
with logging.skip_warningiserror(False):
logger.warning('message')
# It also works during pending_warnings.
with logging.pending_warnings():
with logging.skip_warningiserror():
logger.warning('message')
with pytest.raises(SphinxWarning):
with logging.pending_warnings():
with logging.skip_warningiserror(False):
logger.warning('message')