Use sphinx.util.logging instead app.warn()

This commit is contained in:
Takeshi KOMIYA
2016-12-20 23:21:30 +09:00
parent 6d4e645409
commit 85dcd7baa8
14 changed files with 130 additions and 136 deletions

View File

@@ -177,11 +177,11 @@ class Sphinx(object):
self.tags = Tags(tags) self.tags = Tags(tags)
self.config = Config(confdir, CONFIG_FILENAME, self.config = Config(confdir, CONFIG_FILENAME,
confoverrides or {}, self.tags) confoverrides or {}, self.tags)
self.config.check_unicode(self.warn) self.config.check_unicode()
# defer checking types until i18n has been initialized # defer checking types until i18n has been initialized
# initialize some limited config variables before loading extensions # initialize some limited config variables before loading extensions
self.config.pre_init_values(self.warn) self.config.pre_init_values()
# check the Sphinx version if requested # check the Sphinx version if requested
if self.config.needs_sphinx and self.config.needs_sphinx > sphinx.__display_version__: if self.config.needs_sphinx and self.config.needs_sphinx > sphinx.__display_version__:
@@ -227,7 +227,7 @@ class Sphinx(object):
) )
# now that we know all config values, collect them from conf.py # now that we know all config values, collect them from conf.py
self.config.init_values(self.warn) self.config.init_values()
# check extension versions if requested # check extension versions if requested
if self.config.needs_extensions: if self.config.needs_extensions:
@@ -251,7 +251,7 @@ class Sphinx(object):
# set up translation infrastructure # set up translation infrastructure
self._init_i18n() self._init_i18n()
# check all configuration values for permissible types # check all configuration values for permissible types
self.config.check_types(self.warn) self.config.check_types()
# set up source_parsers # set up source_parsers
self._init_source_parsers() self._init_source_parsers()
# set up the build environment # set up the build environment
@@ -528,9 +528,9 @@ class Sphinx(object):
if extension in self._extensions: if extension in self._extensions:
return return
if extension in EXTENSION_BLACKLIST: if extension in EXTENSION_BLACKLIST:
self.warn('the extension %r was already merged with Sphinx since version %s; ' logger.warning('the extension %r was already merged with Sphinx since version %s; '
'this extension is ignored.' % ( 'this extension is ignored.',
extension, EXTENSION_BLACKLIST[extension])) extension, EXTENSION_BLACKLIST[extension])
return return
self._setting_up_extension.append(extension) self._setting_up_extension.append(extension)
try: try:
@@ -540,8 +540,8 @@ class Sphinx(object):
raise ExtensionError('Could not import extension %s' % extension, raise ExtensionError('Could not import extension %s' % extension,
err) err)
if not hasattr(mod, 'setup'): if not hasattr(mod, 'setup'):
self.warn('extension %r has no setup() function; is it really ' logger.warning('extension %r has no setup() function; is it really '
'a Sphinx extension module?' % extension) 'a Sphinx extension module?', extension)
ext_meta = None ext_meta = None
else: else:
try: try:
@@ -561,9 +561,9 @@ class Sphinx(object):
if not ext_meta.get('version'): if not ext_meta.get('version'):
ext_meta['version'] = 'unknown version' ext_meta['version'] = 'unknown version'
except Exception: except Exception:
self.warn('extension %r returned an unsupported object from ' logger.warning('extension %r returned an unsupported object from '
'its setup() function; it should return None or a ' 'its setup() function; it should return None or a '
'metadata dictionary' % extension) 'metadata dictionary', extension)
ext_meta = {'version': 'unknown version'} ext_meta = {'version': 'unknown version'}
self._extensions[extension] = mod self._extensions[extension] = mod
self._extension_metadata[extension] = ext_meta self._extension_metadata[extension] = ext_meta
@@ -668,9 +668,9 @@ class Sphinx(object):
self.debug('[app] adding node: %r', (node, kwds)) self.debug('[app] adding node: %r', (node, kwds))
if not kwds.pop('override', False) and \ if not kwds.pop('override', False) and \
hasattr(nodes.GenericNodeVisitor, 'visit_' + node.__name__): hasattr(nodes.GenericNodeVisitor, 'visit_' + node.__name__):
self.warn('while setting up extension %s: node class %r is ' logger.warning('while setting up extension %s: node class %r is '
'already registered, its visitors will be overridden' % 'already registered, its visitors will be overridden',
(self._setting_up_extension, node.__name__), self._setting_up_extension, node.__name__,
type='app', subtype='add_node') type='app', subtype='add_node')
nodes._add_node_class_names([node.__name__]) nodes._add_node_class_names([node.__name__])
for key, val in iteritems(kwds): for key, val in iteritems(kwds):
@@ -722,9 +722,9 @@ class Sphinx(object):
self.debug('[app] adding directive: %r', self.debug('[app] adding directive: %r',
(name, obj, content, arguments, options)) (name, obj, content, arguments, options))
if name in directives._directives: if name in directives._directives:
self.warn('while setting up extension %s: directive %r is ' logger.warning('while setting up extension %s: directive %r is '
'already registered, it will be overridden' % 'already registered, it will be overridden',
(self._setting_up_extension[-1], name), self._setting_up_extension[-1], name,
type='app', subtype='add_directive') type='app', subtype='add_directive')
directives.register_directive( directives.register_directive(
name, self._directive_helper(obj, content, arguments, **options)) name, self._directive_helper(obj, content, arguments, **options))
@@ -733,9 +733,9 @@ class Sphinx(object):
# type: (unicode, Any) -> None # type: (unicode, Any) -> None
self.debug('[app] adding role: %r', (name, role)) self.debug('[app] adding role: %r', (name, role))
if name in roles._roles: if name in roles._roles:
self.warn('while setting up extension %s: role %r is ' logger.warning('while setting up extension %s: role %r is '
'already registered, it will be overridden' % 'already registered, it will be overridden',
(self._setting_up_extension[-1], name), self._setting_up_extension[-1], name,
type='app', subtype='add_role') type='app', subtype='add_role')
roles.register_local_role(name, role) roles.register_local_role(name, role)
@@ -745,9 +745,9 @@ class Sphinx(object):
# register_canonical_role # register_canonical_role
self.debug('[app] adding generic role: %r', (name, nodeclass)) self.debug('[app] adding generic role: %r', (name, nodeclass))
if name in roles._roles: if name in roles._roles:
self.warn('while setting up extension %s: role %r is ' logger.warning('while setting up extension %s: role %r is '
'already registered, it will be overridden' % 'already registered, it will be overridden',
(self._setting_up_extension[-1], name), self._setting_up_extension[-1], name,
type='app', subtype='add_generic_role') type='app', subtype='add_generic_role')
role = roles.GenericRole(name, nodeclass) role = roles.GenericRole(name, nodeclass)
roles.register_local_role(name, role) roles.register_local_role(name, role)
@@ -891,9 +891,9 @@ class Sphinx(object):
# type: (unicode, Parser) -> None # type: (unicode, Parser) -> None
self.debug('[app] adding search source_parser: %r, %r', suffix, parser) self.debug('[app] adding search source_parser: %r, %r', suffix, parser)
if suffix in self._additional_source_parsers: if suffix in self._additional_source_parsers:
self.warn('while setting up extension %s: source_parser for %r is ' logger.warning('while setting up extension %s: source_parser for %r is '
'already registered, it will be overridden' % 'already registered, it will be overridden',
(self._setting_up_extension[-1], suffix), self._setting_up_extension[-1], suffix,
type='app', subtype='add_source_parser') type='app', subtype='add_source_parser')
self._additional_source_parsers[suffix] = parser self._additional_source_parsers[suffix] = parser

View File

@@ -19,7 +19,7 @@ except ImportError:
from docutils import nodes from docutils import nodes
from sphinx.util import i18n, path_stabilize from sphinx.util import i18n, path_stabilize, logging
from sphinx.util.osutil import SEP, relative_uri from sphinx.util.osutil import SEP, relative_uri
from sphinx.util.i18n import find_catalog from sphinx.util.i18n import find_catalog
from sphinx.util.console import bold, darkgreen # type: ignore from sphinx.util.console import bold, darkgreen # type: ignore
@@ -284,13 +284,9 @@ class Builder(object):
self.info(bold('building [%s]' % self.name) + ': ' + summary) self.info(bold('building [%s]' % self.name) + ': ' + summary)
# while reading, collect all warnings from docutils # while reading, collect all warnings from docutils
warnings = [] with logging.pending_logging():
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)
for warning, kwargs in warnings:
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)
@@ -376,25 +372,23 @@ class Builder(object):
self.info('done') self.info('done')
warnings = [] # type: List[Tuple[Tuple, Dict]] warnings = [] # type: List[Tuple[Tuple, Dict]]
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()
warnings = []
self._write_parallel(sorted(docnames), warnings, self._write_parallel(sorted(docnames), warnings,
nproc=self.app.parallel - 1) nproc=self.app.parallel - 1)
else: else:
self._write_serial(sorted(docnames), warnings) self._write_serial(sorted(docnames))
self.env.set_warnfunc(self.warn)
def _write_serial(self, docnames, warnings): def _write_serial(self, docnames):
# type: (Sequence[unicode], List[Tuple[Tuple, Dict]]) -> None # type: (Sequence[unicode]) -> None
with logging.pending_logging():
for docname in self.app.status_iterator( for docname in self.app.status_iterator(
docnames, 'writing output... ', darkgreen, len(docnames)): docnames, 'writing output... ', darkgreen, len(docnames)):
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, kwargs in warnings:
self.warn(*warning, **kwargs)
def _write_parallel(self, docnames, warnings, nproc): def _write_parallel(self, docnames, warnings, nproc):
# type: (Iterable[unicode], List[Tuple[Tuple, Dict]], int) -> None # type: (Iterable[unicode], List[Tuple[Tuple, Dict]], int) -> None

View File

@@ -38,8 +38,7 @@ class ChangesBuilder(Builder):
def init(self): def init(self):
# type: () -> None # type: () -> None
self.create_template_bridge() self.create_template_bridge()
Theme.init_themes(self.confdir, self.config.html_theme_path, Theme.init_themes(self.confdir, self.config.html_theme_path)
warn=self.warn)
self.theme = Theme('default') self.theme = Theme('default')
self.templates.init(self, self.theme) self.templates.init(self, self.theme)

View File

@@ -159,10 +159,9 @@ class StandaloneHTMLBuilder(Builder):
def init_templates(self): def init_templates(self):
# type: () -> None # type: () -> None
Theme.init_themes(self.confdir, self.config.html_theme_path, Theme.init_themes(self.confdir, self.config.html_theme_path)
warn=self.warn)
themename, themeoptions = self.get_theme_config() themename, themeoptions = self.get_theme_config()
self.theme = Theme(themename, warn=self.warn) self.theme = Theme(themename)
self.theme_options = themeoptions.copy() self.theme_options = themeoptions.copy()
self.create_template_bridge() self.create_template_bridge()
self.templates.init(self, self.theme) self.templates.init(self, self.theme)
@@ -314,8 +313,7 @@ class StandaloneHTMLBuilder(Builder):
lufmt = self.config.html_last_updated_fmt lufmt = self.config.html_last_updated_fmt
if lufmt is not None: if lufmt is not None:
self.last_updated = format_date(lufmt or _('%b %d, %Y'), self.last_updated = format_date(lufmt or _('%b %d, %Y'),
language=self.config.language, language=self.config.language)
warn=self.warn)
else: else:
self.last_updated = None self.last_updated = None

View File

@@ -16,6 +16,7 @@ from six import PY2, PY3, iteritems, string_types, binary_type, text_type, integ
from sphinx.errors import ConfigError from sphinx.errors import ConfigError
from sphinx.locale import l_ from sphinx.locale import l_
from sphinx.util import logging
from sphinx.util.i18n import format_date from sphinx.util.i18n import format_date
from sphinx.util.osutil import cd from sphinx.util.osutil import cd
from sphinx.util.pycompat import execfile_, NoneType from sphinx.util.pycompat import execfile_, NoneType
@@ -25,6 +26,8 @@ if False:
from typing import Any, Callable, Tuple # NOQA from typing import Any, Callable, Tuple # NOQA
from sphinx.util.tags import Tags # NOQA from sphinx.util.tags import Tags # NOQA
logger = logging.getLogger(__name__)
nonascii_re = re.compile(br'[\x80-\xff]') nonascii_re = re.compile(br'[\x80-\xff]')
copyright_year_re = re.compile(r'^((\d{4}-)?)(\d{4})(?=[ ,])') copyright_year_re = re.compile(r'^((\d{4}-)?)(\d{4})(?=[ ,])')
@@ -166,8 +169,8 @@ class Config(object):
config[k] = copyright_year_re.sub('\g<1>%s' % format_date('%Y'), # type: ignore # NOQA config[k] = copyright_year_re.sub('\g<1>%s' % format_date('%Y'), # type: ignore # NOQA
config[k]) config[k])
def check_types(self, warn): def check_types(self):
# type: (Callable) -> None # type: () -> None
# check all values for deviation from the default value's type, since # check all values for deviation from the default value's type, since
# that can result in TypeErrors all over the place # that can result in TypeErrors all over the place
# NB. since config values might use l_() we have to wait with calling # NB. since config values might use l_() we have to wait with calling
@@ -186,7 +189,7 @@ class Config(object):
current = self[name] current = self[name]
if isinstance(permitted, ENUM): if isinstance(permitted, ENUM):
if not permitted.match(current): if not permitted.match(current):
warn(CONFIG_ENUM_WARNING.format( logger.warning(CONFIG_ENUM_WARNING.format(
name=name, current=current, candidates=permitted.candidates)) name=name, current=current, candidates=permitted.candidates))
else: else:
if type(current) is type(default): if type(current) is type(default):
@@ -201,20 +204,20 @@ class Config(object):
continue # at least we share a non-trivial base class continue # at least we share a non-trivial base class
if permitted: if permitted:
warn(CONFIG_PERMITTED_TYPE_WARNING.format( logger.warning(CONFIG_PERMITTED_TYPE_WARNING.format(
name=name, current=type(current), name=name, current=type(current),
permitted=str([cls.__name__ for cls in permitted]))) permitted=str([cls.__name__ for cls in permitted])))
else: else:
warn(CONFIG_TYPE_WARNING.format( logger.warning(CONFIG_TYPE_WARNING.format(
name=name, current=type(current), default=type(default))) name=name, current=type(current), default=type(default)))
def check_unicode(self, warn): def check_unicode(self):
# type: (Callable) -> None # type: () -> None
# check all string values for non-ASCII characters in bytestrings, # check all string values for non-ASCII characters in bytestrings,
# since that can result in UnicodeErrors all over the place # since that can result in UnicodeErrors all over the place
for name, value in iteritems(self._raw_config): for name, value in iteritems(self._raw_config):
if isinstance(value, binary_type) and nonascii_re.search(value): # type: ignore if isinstance(value, binary_type) and nonascii_re.search(value): # type: ignore
warn('the config value %r is set to a string with non-ASCII ' logger.warning('the config value %r is set to a string with non-ASCII '
'characters; this can lead to Unicode errors occurring. ' 'characters; this can lead to Unicode errors occurring. '
'Please use Unicode strings, e.g. %r.' % (name, u'Content')) 'Please use Unicode strings, e.g. %r.' % (name, u'Content'))
@@ -244,8 +247,8 @@ class Config(object):
else: else:
return value return value
def pre_init_values(self, warn): def pre_init_values(self):
# type: (Callable) -> None # type: () -> None
"""Initialize some limited config variables before loading extensions""" """Initialize some limited config variables before loading extensions"""
variables = ['needs_sphinx', 'suppress_warnings', 'html_translator_class'] variables = ['needs_sphinx', 'suppress_warnings', 'html_translator_class']
for name in variables: for name in variables:
@@ -255,10 +258,10 @@ class Config(object):
elif name in self._raw_config: elif name in self._raw_config:
self.__dict__[name] = self._raw_config[name] self.__dict__[name] = self._raw_config[name]
except ValueError as exc: except ValueError as exc:
warn(exc) logger.warning("%s" % exc)
def init_values(self, warn): def init_values(self):
# type: (Callable) -> None # type: () -> None
config = self._raw_config config = self._raw_config
for valname, value in iteritems(self.overrides): for valname, value in iteritems(self.overrides):
try: try:
@@ -267,14 +270,14 @@ class Config(object):
config.setdefault(realvalname, {})[key] = value # type: ignore config.setdefault(realvalname, {})[key] = value # type: ignore
continue continue
elif valname not in self.values: elif valname not in self.values:
warn('unknown config value %r in override, ignoring' % valname) logger.warning('unknown config value %r in override, ignoring' % valname)
continue continue
if isinstance(value, string_types): if isinstance(value, string_types):
config[valname] = self.convert_overrides(valname, value) config[valname] = self.convert_overrides(valname, value)
else: else:
config[valname] = value config[valname] = value
except ValueError as exc: except ValueError as exc:
warn(exc) logger.warning("%s" % exc)
for name in config: for name in config:
if name in self.values: if name in self.values:
self.__dict__[name] = config[name] self.__dict__[name] = config[name]

View File

@@ -11,6 +11,7 @@
from six import text_type from six import text_type
from sphinx.util import logging
from sphinx.util.pycompat import htmlescape from sphinx.util.pycompat import htmlescape
from sphinx.util.texescape import tex_hl_escape_map_new from sphinx.util.texescape import tex_hl_escape_map_new
from sphinx.ext import doctest from sphinx.ext import doctest
@@ -26,6 +27,8 @@ from pygments.styles import get_style_by_name
from pygments.util import ClassNotFound from pygments.util import ClassNotFound
from sphinx.pygments_styles import SphinxStyle, NoneStyle from sphinx.pygments_styles import SphinxStyle, NoneStyle
logger = logging.getLogger(__name__)
lexers = dict( lexers = dict(
none = TextLexer(stripnl=False), none = TextLexer(stripnl=False),
python = PythonLexer(stripnl=False), python = PythonLexer(stripnl=False),
@@ -92,7 +95,7 @@ class PygmentsBridge(object):
return '\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n' + \ return '\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n' + \
source + '\\end{Verbatim}\n' source + '\\end{Verbatim}\n'
def highlight_block(self, source, lang, opts=None, warn=None, force=False, **kwargs): def highlight_block(self, source, lang, opts=None, location=None, force=False, **kwargs):
if not isinstance(source, text_type): if not isinstance(source, text_type):
source = source.decode() source = source.decode()
@@ -120,11 +123,9 @@ class PygmentsBridge(object):
try: try:
lexer = lexers[lang] = get_lexer_by_name(lang, **(opts or {})) lexer = lexers[lang] = get_lexer_by_name(lang, **(opts or {}))
except ClassNotFound: except ClassNotFound:
if warn: logger.warning('Pygments lexer name %r is not known', lang,
warn('Pygments lexer name %r is not known' % lang) location=location)
lexer = lexers['none'] lexer = lexers['none']
else:
raise
else: else:
lexer.add_filter('raiseonerror') lexer.add_filter('raiseonerror')
@@ -137,17 +138,16 @@ class PygmentsBridge(object):
formatter = self.get_formatter(**kwargs) formatter = self.get_formatter(**kwargs)
try: try:
hlsource = highlight(source, lexer, formatter) hlsource = highlight(source, lexer, formatter)
except ErrorToken as exc: except ErrorToken:
# this is most probably not the selected language, # this is most probably not the selected language,
# so let it pass unhighlighted # so let it pass unhighlighted
if lang == 'default': if lang == 'default':
pass # automatic highlighting failed. pass # automatic highlighting failed.
elif warn:
warn('Could not lex literal_block as "%s". '
'Highlighting skipped.' % lang,
type='misc', subtype='highlighting_failure')
else: else:
raise exc logger.warning('Could not lex literal_block as "%s". '
'Highlighting skipped.', lang,
type='misc', subtype='highlighting_failure',
location=location)
hlsource = highlight(source, lexers['none'], formatter) hlsource = highlight(source, lexers['none'], formatter)
if self.dest == 'html': if self.dest == 'html':
return hlsource return hlsource

View File

@@ -26,6 +26,9 @@ except ImportError:
from sphinx import package_dir from sphinx import package_dir
from sphinx.errors import ThemeError from sphinx.errors import ThemeError
from sphinx.util import logging
logger = logging.getLogger(__name__)
if False: if False:
# For type annotation # For type annotation
@@ -43,8 +46,8 @@ class Theme(object):
themepath = [] # type: List[unicode] themepath = [] # type: List[unicode]
@classmethod @classmethod
def init_themes(cls, confdir, theme_path, warn=None): def init_themes(cls, confdir, theme_path):
# type: (unicode, unicode, Callable) -> None # type: (unicode, unicode) -> None
"""Search all theme paths for available themes.""" """Search all theme paths for available themes."""
cls.themepath = list(theme_path) cls.themepath = list(theme_path)
cls.themepath.append(path.join(package_dir, 'themes')) cls.themepath.append(path.join(package_dir, 'themes'))
@@ -62,9 +65,8 @@ class Theme(object):
tname = theme[:-4] tname = theme[:-4]
tinfo = zfile tinfo = zfile
except Exception: except Exception:
if warn: logger.warning('file %r on theme path is not a valid '
warn('file %r on theme path is not a valid ' 'zipfile or contains no theme', theme)
'zipfile or contains no theme' % theme)
continue continue
else: else:
if not path.isfile(path.join(themedir, theme, THEMECONF)): if not path.isfile(path.join(themedir, theme, THEMECONF)):
@@ -105,8 +107,8 @@ class Theme(object):
cls.themes[name] = (path.join(themedir, name), None) cls.themes[name] = (path.join(themedir, name), None)
return return
def __init__(self, name, warn=None): def __init__(self, name):
# type: (unicode, Callable) -> None # type: (unicode) -> None
if name not in self.themes: if name not in self.themes:
self.load_extra_theme(name) self.load_extra_theme(name)
if name not in self.themes: if name not in self.themes:
@@ -162,7 +164,7 @@ class Theme(object):
raise ThemeError('no theme named %r found, inherited by %r' % raise ThemeError('no theme named %r found, inherited by %r' %
(inherit, name)) (inherit, name))
else: else:
self.base = Theme(inherit, warn=warn) self.base = Theme(inherit)
def get_confstr(self, section, name, default=NODEFAULT): def get_confstr(self, section, name, default=NODEFAULT):
# type: (unicode, unicode, Any) -> Any # type: (unicode, unicode, Any) -> Any

View File

@@ -34,7 +34,6 @@ class DefaultSubstitutions(Transform):
def apply(self): def apply(self):
# type: () -> None # type: () -> None
env = self.document.settings.env
config = self.document.settings.env.config config = self.document.settings.env.config
# only handle those not otherwise defined in the document # only handle those not otherwise defined in the document
to_handle = default_substitutions - set(self.document.substitution_defs) to_handle = default_substitutions - set(self.document.substitution_defs)
@@ -45,7 +44,7 @@ class DefaultSubstitutions(Transform):
if refname == 'today' and not text: if refname == 'today' and not text:
# special handling: can also specify a strftime format # special handling: can also specify a strftime format
text = format_date(config.today_fmt or _('%b %d, %Y'), text = format_date(config.today_fmt or _('%b %d, %Y'),
language=config.language, warn=env.warn) language=config.language)
ref.replace_self(nodes.Text(text, text)) ref.replace_self(nodes.Text(text, text))

View File

@@ -22,9 +22,12 @@ from babel.messages.pofile import read_po
from babel.messages.mofile import write_mo from babel.messages.mofile import write_mo
from sphinx.errors import SphinxError from sphinx.errors import SphinxError
from sphinx.util import logging
from sphinx.util.osutil import SEP, walk from sphinx.util.osutil import SEP, walk
from sphinx.deprecation import RemovedInSphinx16Warning from sphinx.deprecation import RemovedInSphinx16Warning
logger = logging.getLogger(__name__)
if False: if False:
# For type annotation # For type annotation
from typing import Callable # NOQA from typing import Callable # NOQA
@@ -171,8 +174,8 @@ date_format_mappings = {
} }
def babel_format_date(date, format, locale, warn=None, formatter=babel.dates.format_date): def babel_format_date(date, format, locale, formatter=babel.dates.format_date):
# type: (datetime, unicode, unicode, Callable, Callable) -> unicode # type: (datetime, unicode, unicode, Callable) -> unicode
if locale is None: if locale is None:
locale = 'en' locale = 'en'
@@ -187,15 +190,13 @@ def babel_format_date(date, format, locale, warn=None, formatter=babel.dates.for
# fallback to English # fallback to English
return formatter(date, format, locale='en') return formatter(date, format, locale='en')
except AttributeError: except AttributeError:
if warn: logger.warning('Invalid date format. Quote the string by single quote '
warn('Invalid date format. Quote the string by single quote ' 'if you want to output it directly: %s', format)
'if you want to output it directly: %s' % format)
return format return format
def format_date(format, date=None, language=None, warn=None): def format_date(format, date=None, language=None):
# type: (str, datetime, unicode, Callable) -> unicode # type: (str, datetime, unicode) -> unicode
if format is None: if format is None:
format = 'medium' format = 'medium'
@@ -213,7 +214,7 @@ def format_date(format, date=None, language=None, warn=None):
warnings.warn('LDML format support will be dropped at Sphinx-1.6', warnings.warn('LDML format support will be dropped at Sphinx-1.6',
RemovedInSphinx16Warning) RemovedInSphinx16Warning)
return babel_format_date(date, format, locale=language, warn=warn, return babel_format_date(date, format, locale=language,
formatter=babel.dates.format_datetime) formatter=babel.dates.format_datetime)
else: else:
# consider the format as ustrftime's and try to convert it to babel's # consider the format as ustrftime's and try to convert it to babel's

View File

@@ -364,11 +364,10 @@ class HTMLTranslator(BaseTranslator):
else: else:
opts = {} opts = {}
def warner(msg, **kwargs):
self.builder.warn(msg, (self.builder.current_docname, node.line), **kwargs)
highlighted = self.highlighter.highlight_block( highlighted = self.highlighter.highlight_block(
node.rawsource, lang, opts=opts, warn=warner, linenos=linenos, node.rawsource, lang, opts=opts, linenos=linenos,
**highlight_args) location=(self.builder.current_docname, node.line), **highlight_args
)
starttag = self.starttag(node, 'div', suffix='', starttag = self.starttag(node, 'div', suffix='',
CLASS='highlight-%s' % lang) CLASS='highlight-%s' % lang)
self.body.append(starttag + highlighted + '</div>\n') self.body.append(starttag + highlighted + '</div>\n')

View File

@@ -2155,12 +2155,10 @@ class LaTeXTranslator(nodes.NodeVisitor):
else: else:
opts = {} opts = {}
def warner(msg, **kwargs): hlcode = self.highlighter.highlight_block(
# type: (unicode) -> None code, lang, opts=opts, linenos=linenos,
self.builder.warn(msg, (self.curfilestack[-1], node.line), **kwargs) location=(self.curfilestack[-1], node.line), **highlight_args
hlcode = self.highlighter.highlight_block(code, lang, opts=opts, )
warn=warner, linenos=linenos,
**highlight_args)
# workaround for Unicode issue # workaround for Unicode issue
hlcode = hlcode.replace(u'', u'@texteuro[]') hlcode = hlcode.replace(u'', u'@texteuro[]')
if self.in_footnote: if self.in_footnote:

View File

@@ -40,10 +40,10 @@ with "\\?": b?'here: >>>(\\\\|/)xbb<<<'
""" """
HTML_WARNINGS = ENV_WARNINGS + """\ HTML_WARNINGS = ENV_WARNINGS + """\
%(root)s/index.rst:\\d+: WARNING: no matching candidate for image URI u'foo.\\*'
%(root)s/index.rst:\\d+: WARNING: Could not lex literal_block as "c". Highlighting skipped.
%(root)s/index.rst:\\d+: WARNING: unknown option: &option %(root)s/index.rst:\\d+: WARNING: unknown option: &option
%(root)s/index.rst:\\d+: WARNING: citation not found: missing %(root)s/index.rst:\\d+: WARNING: citation not found: missing
%(root)s/index.rst:\\d+: WARNING: no matching candidate for image URI u'foo.\\*'
%(root)s/index.rst:\\d+: WARNING: Could not lex literal_block as "c". Highlighting skipped.
""" """
if PY3: if PY3:

View File

@@ -87,7 +87,8 @@ def test_extension_values(app, status, warning):
@with_tempdir @with_tempdir
def test_errors_warnings(dir): @mock.patch("sphinx.config.logger")
def test_errors_warnings(dir, logger):
# test the error for syntax errors in the config file # test the error for syntax errors in the config file
(dir / 'conf.py').write_text(u'project = \n', encoding='ascii') (dir / 'conf.py').write_text(u'project = \n', encoding='ascii')
raises_msg(ConfigError, 'conf.py', Config, dir, 'conf.py', {}, None) raises_msg(ConfigError, 'conf.py', Config, dir, 'conf.py', {}, None)
@@ -97,8 +98,9 @@ def test_errors_warnings(dir):
u'# -*- coding: utf-8\n\nproject = u"Jägermeister"\n', u'# -*- coding: utf-8\n\nproject = u"Jägermeister"\n',
encoding='utf-8') encoding='utf-8')
cfg = Config(dir, 'conf.py', {}, None) cfg = Config(dir, 'conf.py', {}, None)
cfg.init_values(lambda warning: 1/0) cfg.init_values()
assert cfg.project == u'Jägermeister' assert cfg.project == u'Jägermeister'
assert logger.called is False
# test the warning for bytestrings with non-ascii content # test the warning for bytestrings with non-ascii content
# bytestrings with non-ascii content are a syntax error in python3 so we # bytestrings with non-ascii content are a syntax error in python3 so we
@@ -108,13 +110,10 @@ def test_errors_warnings(dir):
(dir / 'conf.py').write_text( (dir / 'conf.py').write_text(
u'# -*- coding: latin-1\nproject = "fooä"\n', encoding='latin-1') u'# -*- coding: latin-1\nproject = "fooä"\n', encoding='latin-1')
cfg = Config(dir, 'conf.py', {}, None) cfg = Config(dir, 'conf.py', {}, None)
warned = [False]
def warn(msg): assert logger.warning.called is False
warned[0] = True cfg.check_unicode()
assert logger.warning.called is True
cfg.check_unicode(warn)
assert warned[0]
@with_tempdir @with_tempdir
@@ -152,14 +151,16 @@ def test_needs_sphinx():
@with_tempdir @with_tempdir
def test_config_eol(tmpdir): @mock.patch("sphinx.config.logger")
def test_config_eol(tmpdir, logger):
# test config file's eol patterns: LF, CRLF # test config file's eol patterns: LF, CRLF
configfile = tmpdir / 'conf.py' configfile = tmpdir / 'conf.py'
for eol in (b'\n', b'\r\n'): for eol in (b'\n', b'\r\n'):
configfile.write_bytes(b'project = "spam"' + eol) configfile.write_bytes(b'project = "spam"' + eol)
cfg = Config(tmpdir, 'conf.py', {}, None) cfg = Config(tmpdir, 'conf.py', {}, None)
cfg.init_values(lambda warning: 1/0) cfg.init_values()
assert cfg.project == u'spam' assert cfg.project == u'spam'
assert logger.called is False
@with_app(confoverrides={'master_doc': 123, @with_app(confoverrides={'master_doc': 123,

View File

@@ -9,9 +9,9 @@
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
import mock
from pygments.lexer import RegexLexer from pygments.lexer import RegexLexer
from pygments.token import Text, Name from pygments.token import Text, Name
from pygments.filters import ErrorToken
from pygments.formatters.html import HtmlFormatter from pygments.formatters.html import HtmlFormatter
from sphinx.highlighting import PygmentsBridge from sphinx.highlighting import PygmentsBridge
@@ -89,7 +89,8 @@ def test_trim_doctest_flags():
PygmentsBridge.html_formatter = HtmlFormatter PygmentsBridge.html_formatter = HtmlFormatter
def test_default_highlight(): @mock.patch('sphinx.highlighting.logger')
def test_default_highlight(logger):
bridge = PygmentsBridge('html') bridge = PygmentsBridge('html')
# default: highlights as python3 # default: highlights as python3
@@ -107,8 +108,7 @@ def test_default_highlight():
'<span class="s2">&quot;Hello sphinx world&quot;</span>\n</pre></div>\n') '<span class="s2">&quot;Hello sphinx world&quot;</span>\n</pre></div>\n')
# python3: raises error if highlighting failed # python3: raises error if highlighting failed
try:
ret = bridge.highlight_block('reST ``like`` text', 'python3') ret = bridge.highlight_block('reST ``like`` text', 'python3')
assert False, "highlight_block() does not raise any exceptions" logger.warning.assert_called_with('Could not lex literal_block as "%s". '
except ErrorToken: 'Highlighting skipped.', 'python3',
pass # raise parsing error type='misc', subtype='highlighting_failure', location=None)