Initialize i18n module earlier

This commit is contained in:
Takeshi KOMIYA 2017-03-09 12:23:23 +09:00
parent 5ee4c396bc
commit 15114e596d
3 changed files with 89 additions and 78 deletions

View File

@ -38,6 +38,7 @@ from sphinx.domains.std import GenericObject, Target, StandardDomain
from sphinx.deprecation import RemovedInSphinx17Warning, RemovedInSphinx20Warning from sphinx.deprecation import RemovedInSphinx17Warning, RemovedInSphinx20Warning
from sphinx.environment import BuildEnvironment from sphinx.environment import BuildEnvironment
from sphinx.io import SphinxStandaloneReader from sphinx.io import SphinxStandaloneReader
from sphinx.locale import _
from sphinx.roles import XRefRole from sphinx.roles import XRefRole
from sphinx.util import pycompat # noqa: F401 from sphinx.util import pycompat # noqa: F401
from sphinx.util import import_object from sphinx.util import import_object
@ -189,14 +190,18 @@ class Sphinx(object):
self.config.check_unicode() 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 initialize i18n and loading
# extensions
self.config.pre_init_values() self.config.pre_init_values()
# set up translation infrastructure
self._init_i18n()
# 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__:
raise VersionRequirementError( raise VersionRequirementError(
'This project needs at least Sphinx v%s and therefore cannot ' _('This project needs at least Sphinx v%s and therefore cannot '
'be built with this version.' % self.config.needs_sphinx) 'be built with this version.') % self.config.needs_sphinx)
# set confdir to srcdir if -C given (!= no confdir); a few pieces # set confdir to srcdir if -C given (!= no confdir); a few pieces
# of code expect a confdir to be set # of code expect a confdir to be set
@ -224,9 +229,9 @@ class Sphinx(object):
self.config.setup(self) self.config.setup(self)
else: else:
raise ConfigError( raise ConfigError(
"'setup' that is specified in the conf.py has not been " + _("'setup' that is specified in the conf.py has not been "
"callable. Please provide a callable `setup` function " + "callable. Please provide a callable `setup` function "
"in order to behave as a sphinx extension conf.py itself." "in order to behave as a sphinx extension conf.py itself.")
) )
# now that we know all config values, collect them from conf.py # now that we know all config values, collect them from conf.py
@ -236,23 +241,22 @@ class Sphinx(object):
if self.config.needs_extensions: if self.config.needs_extensions:
for extname, needs_ver in self.config.needs_extensions.items(): for extname, needs_ver in self.config.needs_extensions.items():
if extname not in self._extensions: if extname not in self._extensions:
logger.warning('needs_extensions config value specifies a ' logger.warning(_('needs_extensions config value specifies a '
'version requirement for extension %s, but it is ' 'version requirement for extension %s, but it is '
'not loaded', extname) 'not loaded'), extname)
continue continue
has_ver = self._extension_metadata[extname]['version'] has_ver = self._extension_metadata[extname]['version']
if has_ver == 'unknown version' or needs_ver > has_ver: if has_ver == 'unknown version' or needs_ver > has_ver:
raise VersionRequirementError( raise VersionRequirementError(
'This project needs the extension %s at least in ' _('This project needs the extension %s at least in '
'version %s and therefore cannot be built with the ' 'version %s and therefore cannot be built with the '
'loaded version (%s).' % (extname, needs_ver, has_ver)) 'loaded version (%s).') % (extname, needs_ver, has_ver))
# check primary_domain if requested # check primary_domain if requested
if self.config.primary_domain and self.config.primary_domain not in self.domains: if self.config.primary_domain and self.config.primary_domain not in self.domains:
logger.warning('primary_domain %r not found, ignored.', self.config.primary_domain) logger.warning(_('primary_domain %r not found, ignored.'),
self.config.primary_domain)
# set up translation infrastructure
self._init_i18n()
# check all configuration values for permissible types # check all configuration values for permissible types
self.config.check_types() self.config.check_types()
# set up source_parsers # set up source_parsers
@ -286,7 +290,7 @@ class Sphinx(object):
if self.config.language is not None: if self.config.language is not None:
if has_translation or self.config.language == 'en': if has_translation or self.config.language == 'en':
# "en" never needs to be translated # "en" never needs to be translated
logger.info('done') logger.info(_('done'))
else: else:
logger.info('not available for built-in messages') logger.info('not available for built-in messages')
@ -307,28 +311,28 @@ class Sphinx(object):
self.env.domains[domain] = self.domains[domain](self.env) self.env.domains[domain] = self.domains[domain](self.env)
else: else:
try: try:
logger.info(bold('loading pickled environment... '), nonl=True) logger.info(bold(_('loading pickled environment... ')), nonl=True)
self.env = BuildEnvironment.frompickle( self.env = BuildEnvironment.frompickle(
self.srcdir, self.config, path.join(self.doctreedir, ENV_PICKLE_FILENAME)) self.srcdir, self.config, path.join(self.doctreedir, ENV_PICKLE_FILENAME))
self.env.domains = {} self.env.domains = {}
for domain in self.domains.keys(): for domain in self.domains.keys():
# this can raise if the data version doesn't fit # this can raise if the data version doesn't fit
self.env.domains[domain] = self.domains[domain](self.env) self.env.domains[domain] = self.domains[domain](self.env)
logger.info('done') logger.info(_('done'))
except Exception as err: except Exception as err:
if isinstance(err, IOError) and err.errno == ENOENT: if isinstance(err, IOError) and err.errno == ENOENT:
logger.info('not yet created') logger.info(_('not yet created'))
else: else:
logger.info('failed: %s', err) logger.info(_('failed: %s'), err)
self._init_env(freshenv=True) self._init_env(freshenv=True)
def _init_builder(self, buildername): def _init_builder(self, buildername):
# type: (unicode) -> None # type: (unicode) -> None
if buildername is None: if buildername is None:
print('No builder selected, using default: html', file=self._status) logger.info(_('No builder selected, using default: html'))
buildername = 'html' buildername = 'html'
if buildername not in self.builderclasses: if buildername not in self.builderclasses:
raise SphinxError('Builder name %s not registered' % buildername) raise SphinxError(_('Builder name %s not registered') % buildername)
builderclass = self.builderclasses[buildername] builderclass = self.builderclasses[buildername]
self.builder = builderclass(self) self.builder = builderclass(self)
@ -355,13 +359,13 @@ class Sphinx(object):
self.builder.build_update() self.builder.build_update()
status = (self.statuscode == 0 and status = (self.statuscode == 0 and
'succeeded' or 'finished with problems') _('succeeded') or _('finished with problems'))
if self._warncount: if self._warncount:
logger.info(bold('build %s, %s warning%s.' % logger.info(bold(_('build %s, %s warning%s.') %
(status, self._warncount, (status, self._warncount,
self._warncount != 1 and 's' or ''))) self._warncount != 1 and 's' or '')))
else: else:
logger.info(bold('build %s.' % status)) logger.info(bold(_('build %s.') % status))
except Exception as err: except Exception as err:
# delete the saved env to force a fresh build next time # delete the saved env to force a fresh build next time
envfile = path.join(self.doctreedir, ENV_PICKLE_FILENAME) envfile = path.join(self.doctreedir, ENV_PICKLE_FILENAME)
@ -472,20 +476,20 @@ 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:
logger.warning('the extension %r was already merged with Sphinx since version %s; ' logger.warning(_('the extension %r was already merged with Sphinx since '
'this extension is ignored.', 'version %s; 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:
mod = __import__(extension, None, None, ['setup']) mod = __import__(extension, None, None, ['setup'])
except ImportError as err: except ImportError as err:
logger.verbose('Original exception:\n' + traceback.format_exc()) logger.verbose(_('Original exception:\n') + traceback.format_exc())
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'):
logger.warning('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:
@ -493,9 +497,9 @@ class Sphinx(object):
except VersionRequirementError as err: except VersionRequirementError as err:
# add the extension name to the version required # add the extension name to the version required
raise VersionRequirementError( raise VersionRequirementError(
'The %s extension used by this project needs at least ' _('The %s extension used by this project needs at least '
'Sphinx v%s; it therefore cannot be built with this ' 'Sphinx v%s; it therefore cannot be built with this '
'version.' % (extension, err)) 'version.') % (extension, err))
if ext_meta is None: if ext_meta is None:
ext_meta = {} ext_meta = {}
# special-case for compatibility # special-case for compatibility
@ -505,9 +509,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:
logger.warning('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
@ -529,7 +533,7 @@ class Sphinx(object):
def _validate_event(self, event): def _validate_event(self, event):
# type: (unicode) -> None # type: (unicode) -> None
if event not in self._events: if event not in self._events:
raise ExtensionError('Unknown event name: %s' % event) raise ExtensionError(_('Unknown event name: %s') % event)
def connect(self, event, callback): def connect(self, event, callback):
# type: (unicode, Callable) -> int # type: (unicode, Callable) -> int
@ -560,7 +564,7 @@ class Sphinx(object):
pass pass
results = [] results = []
if event in self._listeners: if event in self._listeners:
for _, callback in iteritems(self._listeners[event]): for listener_id, callback in iteritems(self._listeners[event]):
results.append(callback(self, *args)) results.append(callback(self, *args))
return results return results
@ -577,12 +581,11 @@ class Sphinx(object):
# type: (Type[Builder]) -> None # type: (Type[Builder]) -> None
logger.debug('[app] adding builder: %r', builder) logger.debug('[app] adding builder: %r', builder)
if not hasattr(builder, 'name'): if not hasattr(builder, 'name'):
raise ExtensionError('Builder class %s has no "name" attribute' raise ExtensionError(_('Builder class %s has no "name" attribute')
% builder) % builder)
if builder.name in self.builderclasses: if builder.name in self.builderclasses:
raise ExtensionError( raise ExtensionError(_('Builder %r already exists (in module %s)') %
'Builder %r already exists (in module %s)' % ( (builder.name, self.builderclasses[builder.name].__module__))
builder.name, self.builderclasses[builder.name].__module__))
self.builderclasses[builder.name] = builder self.builderclasses[builder.name] = builder
def add_config_value(self, name, default, rebuild, types=()): def add_config_value(self, name, default, rebuild, types=()):
@ -590,7 +593,7 @@ class Sphinx(object):
logger.debug('[app] adding config value: %r', logger.debug('[app] adding config value: %r',
(name, default, rebuild) + ((types,) if types else ())) # type: ignore (name, default, rebuild) + ((types,) if types else ())) # type: ignore
if name in self.config: if name in self.config:
raise ExtensionError('Config value %r already present' % name) raise ExtensionError(_('Config value %r already present') % name)
if rebuild in (False, True): if rebuild in (False, True):
rebuild = rebuild and 'env' or '' rebuild = rebuild and 'env' or ''
self.config.add(name, default, rebuild, types) self.config.add(name, default, rebuild, types)
@ -604,7 +607,7 @@ class Sphinx(object):
def set_translator(self, name, translator_class): def set_translator(self, name, translator_class):
# type: (unicode, Any) -> None # type: (unicode, Any) -> None
logger.info(bold('A Translator for the %s builder is changed.' % name)) logger.info(bold(_('A Translator for the %s builder is changed.') % name))
self._translators[name] = translator_class self._translators[name] = translator_class
def add_node(self, node, **kwds): def add_node(self, node, **kwds):
@ -612,8 +615,8 @@ class Sphinx(object):
logger.debug('[app] adding node: %r', (node, kwds)) logger.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__):
logger.warning('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__])
@ -621,8 +624,8 @@ class Sphinx(object):
try: try:
visit, depart = val visit, depart = val
except ValueError: except ValueError:
raise ExtensionError('Value for key %r must be a ' raise ExtensionError(_('Value for key %r must be a '
'(visit, depart) function tuple' % key) '(visit, depart) function tuple') % key)
translator = self._translators.get(key) translator = self._translators.get(key)
translators = [] translators = []
if translator is not None: if translator is not None:
@ -665,8 +668,8 @@ class Sphinx(object):
return convert_directive_function(obj) return convert_directive_function(obj)
else: else:
if content or arguments or options: if content or arguments or options:
raise ExtensionError('when adding directive classes, no ' raise ExtensionError(_('when adding directive classes, no '
'additional arguments may be given') 'additional arguments may be given'))
return obj return obj
def add_directive(self, name, obj, content=None, arguments=None, **options): def add_directive(self, name, obj, content=None, arguments=None, **options):
@ -674,8 +677,8 @@ class Sphinx(object):
logger.debug('[app] adding directive: %r', logger.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:
logger.warning('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(
@ -685,8 +688,8 @@ class Sphinx(object):
# type: (unicode, Any) -> None # type: (unicode, Any) -> None
logger.debug('[app] adding role: %r', (name, role)) logger.debug('[app] adding role: %r', (name, role))
if name in roles._roles: if name in roles._roles:
logger.warning('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)
@ -697,8 +700,8 @@ class Sphinx(object):
# register_canonical_role # register_canonical_role
logger.debug('[app] adding generic role: %r', (name, nodeclass)) logger.debug('[app] adding generic role: %r', (name, nodeclass))
if name in roles._roles: if name in roles._roles:
logger.warning('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)
@ -708,17 +711,17 @@ class Sphinx(object):
# type: (Type[Domain]) -> None # type: (Type[Domain]) -> None
logger.debug('[app] adding domain: %r', domain) logger.debug('[app] adding domain: %r', domain)
if domain.name in self.domains: if domain.name in self.domains:
raise ExtensionError('domain %s already registered' % domain.name) raise ExtensionError(_('domain %s already registered') % domain.name)
self.domains[domain.name] = domain self.domains[domain.name] = domain
def override_domain(self, domain): def override_domain(self, domain):
# type: (Type[Domain]) -> None # type: (Type[Domain]) -> None
logger.debug('[app] overriding domain: %r', domain) logger.debug('[app] overriding domain: %r', domain)
if domain.name not in self.domains: if domain.name not in self.domains:
raise ExtensionError('domain %s not yet registered' % domain.name) raise ExtensionError(_('domain %s not yet registered') % domain.name)
if not issubclass(domain, self.domains[domain.name]): if not issubclass(domain, self.domains[domain.name]):
raise ExtensionError('new domain not a subclass of registered %s ' raise ExtensionError(_('new domain not a subclass of registered %s '
'domain' % domain.name) 'domain') % domain.name)
self.domains[domain.name] = domain self.domains[domain.name] = domain
def add_directive_to_domain(self, domain, name, obj, def add_directive_to_domain(self, domain, name, obj,
@ -727,7 +730,7 @@ class Sphinx(object):
logger.debug('[app] adding directive to domain: %r', logger.debug('[app] adding directive to domain: %r',
(domain, name, obj, content, arguments, options)) (domain, name, obj, content, arguments, options))
if domain not in self.domains: if domain not in self.domains:
raise ExtensionError('domain %s not yet registered' % domain) raise ExtensionError(_('domain %s not yet registered') % domain)
self.domains[domain].directives[name] = \ self.domains[domain].directives[name] = \
self._directive_helper(obj, content, arguments, **options) self._directive_helper(obj, content, arguments, **options)
@ -735,14 +738,14 @@ class Sphinx(object):
# type: (unicode, unicode, Any) -> None # type: (unicode, unicode, Any) -> None
logger.debug('[app] adding role to domain: %r', (domain, name, role)) logger.debug('[app] adding role to domain: %r', (domain, name, role))
if domain not in self.domains: if domain not in self.domains:
raise ExtensionError('domain %s not yet registered' % domain) raise ExtensionError(_('domain %s not yet registered') % domain)
self.domains[domain].roles[name] = role self.domains[domain].roles[name] = role
def add_index_to_domain(self, domain, index): def add_index_to_domain(self, domain, index):
# type: (unicode, Type[Index]) -> None # type: (unicode, Type[Index]) -> None
logger.debug('[app] adding index to domain: %r', (domain, index)) logger.debug('[app] adding index to domain: %r', (domain, index))
if domain not in self.domains: if domain not in self.domains:
raise ExtensionError('domain %s not yet registered' % domain) raise ExtensionError(_('domain %s not yet registered') % domain)
self.domains[domain].indices.append(index) self.domains[domain].indices.append(index)
def add_object_type(self, directivename, rolename, indextemplate='', def add_object_type(self, directivename, rolename, indextemplate='',
@ -844,8 +847,8 @@ class Sphinx(object):
# type: (unicode, Parser) -> None # type: (unicode, Parser) -> None
logger.debug('[app] adding search source_parser: %r, %r', suffix, parser) logger.debug('[app] adding search source_parser: %r, %r', suffix, parser)
if suffix in self._additional_source_parsers: if suffix in self._additional_source_parsers:
logger.warning('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

@ -16,7 +16,7 @@ from six import PY2, PY3, iteritems, string_types, binary_type, text_type, integ
from typing import Any, NamedTuple, Union from typing import Any, NamedTuple, Union
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 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
@ -233,8 +233,8 @@ class Config(object):
else: else:
defvalue = self.values[name][0] defvalue = self.values[name][0]
if isinstance(defvalue, dict): if isinstance(defvalue, dict):
raise ValueError('cannot override dictionary config setting %r, ' raise ValueError(_('cannot override dictionary config setting %r, '
'ignoring (use %r to set individual elements)' % 'ignoring (use %r to set individual elements)') %
(name, name + '.key=value')) (name, name + '.key=value'))
elif isinstance(defvalue, list): elif isinstance(defvalue, list):
return value.split(',') return value.split(',')
@ -242,20 +242,22 @@ class Config(object):
try: try:
return int(value) return int(value)
except ValueError: except ValueError:
raise ValueError('invalid number %r for config value %r, ignoring' % raise ValueError(_('invalid number %r for config value %r, ignoring') %
(value, name)) (value, name))
elif hasattr(defvalue, '__call__'): elif hasattr(defvalue, '__call__'):
return value return value
elif defvalue is not None and not isinstance(defvalue, string_types): elif defvalue is not None and not isinstance(defvalue, string_types):
raise ValueError('cannot override config setting %r with unsupported ' raise ValueError(_('cannot override config setting %r with unsupported '
'type, ignoring' % name) 'type, ignoring') % name)
else: else:
return value return value
def pre_init_values(self): def pre_init_values(self):
# type: () -> None # type: () -> None
"""Initialize some limited config variables before loading extensions""" """
variables = ['needs_sphinx', 'suppress_warnings'] Initialize some limited config variables before initialize i18n and loading extensions
"""
variables = ['needs_sphinx', 'suppress_warnings', 'language', 'locale_dirs']
for name in variables: for name in variables:
try: try:
if name in self.overrides: if name in self.overrides:
@ -275,7 +277,7 @@ class Config(object):
config.setdefault(realvalname, {})[key] = value config.setdefault(realvalname, {})[key] = value
continue continue
elif valname not in self.values: elif valname not in self.values:
logger.warning('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)
@ -294,7 +296,7 @@ class Config(object):
if name.startswith('_'): if name.startswith('_'):
raise AttributeError(name) raise AttributeError(name)
if name not in self.values: if name not in self.values:
raise AttributeError('No such config value: %s' % name) raise AttributeError(_('No such config value: %s') % name)
default = self.values[name][0] default = self.values[name][0]
if hasattr(default, '__call__'): if hasattr(default, '__call__'):
return default(self) return default(self)

View File

@ -229,11 +229,17 @@ translators = {} # type: Dict[unicode, Any]
if PY3: if PY3:
def _(message): def _(message):
# type: (unicode) -> unicode # type: (unicode) -> unicode
try:
return translators['sphinx'].gettext(message) return translators['sphinx'].gettext(message)
except KeyError:
return message
else: else:
def _(message): def _(message):
# type: (unicode) -> unicode # type: (unicode) -> unicode
try:
return translators['sphinx'].ugettext(message) return translators['sphinx'].ugettext(message)
except KeyError:
return message
def init(locale_dirs, language, catalog='sphinx'): def init(locale_dirs, language, catalog='sphinx'):