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.environment import BuildEnvironment
from sphinx.io import SphinxStandaloneReader
from sphinx.locale import _
from sphinx.roles import XRefRole
from sphinx.util import pycompat # noqa: F401
from sphinx.util import import_object
@ -189,14 +190,18 @@ class Sphinx(object):
self.config.check_unicode()
# 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()
# set up translation infrastructure
self._init_i18n()
# check the Sphinx version if requested
if self.config.needs_sphinx and self.config.needs_sphinx > sphinx.__display_version__:
raise VersionRequirementError(
'This project needs at least Sphinx v%s and therefore cannot '
'be built with this version.' % self.config.needs_sphinx)
_('This project needs at least Sphinx v%s and therefore cannot '
'be built with this version.') % self.config.needs_sphinx)
# set confdir to srcdir if -C given (!= no confdir); a few pieces
# of code expect a confdir to be set
@ -224,9 +229,9 @@ class Sphinx(object):
self.config.setup(self)
else:
raise ConfigError(
"'setup' that is specified in the conf.py has not been " +
"callable. Please provide a callable `setup` function " +
"in order to behave as a sphinx extension conf.py itself."
_("'setup' that is specified in the conf.py has not been "
"callable. Please provide a callable `setup` function "
"in order to behave as a sphinx extension conf.py itself.")
)
# now that we know all config values, collect them from conf.py
@ -236,23 +241,22 @@ class Sphinx(object):
if self.config.needs_extensions:
for extname, needs_ver in self.config.needs_extensions.items():
if extname not in self._extensions:
logger.warning('needs_extensions config value specifies a '
'version requirement for extension %s, but it is '
'not loaded', extname)
logger.warning(_('needs_extensions config value specifies a '
'version requirement for extension %s, but it is '
'not loaded'), extname)
continue
has_ver = self._extension_metadata[extname]['version']
if has_ver == 'unknown version' or needs_ver > has_ver:
raise VersionRequirementError(
'This project needs the extension %s at least in '
'version %s and therefore cannot be built with the '
'loaded version (%s).' % (extname, needs_ver, has_ver))
_('This project needs the extension %s at least in '
'version %s and therefore cannot be built with the '
'loaded version (%s).') % (extname, needs_ver, has_ver))
# check primary_domain if requested
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
self.config.check_types()
# set up source_parsers
@ -286,7 +290,7 @@ class Sphinx(object):
if self.config.language is not None:
if has_translation or self.config.language == 'en':
# "en" never needs to be translated
logger.info('done')
logger.info(_('done'))
else:
logger.info('not available for built-in messages')
@ -307,28 +311,28 @@ class Sphinx(object):
self.env.domains[domain] = self.domains[domain](self.env)
else:
try:
logger.info(bold('loading pickled environment... '), nonl=True)
logger.info(bold(_('loading pickled environment... ')), nonl=True)
self.env = BuildEnvironment.frompickle(
self.srcdir, self.config, path.join(self.doctreedir, ENV_PICKLE_FILENAME))
self.env.domains = {}
for domain in self.domains.keys():
# this can raise if the data version doesn't fit
self.env.domains[domain] = self.domains[domain](self.env)
logger.info('done')
logger.info(_('done'))
except Exception as err:
if isinstance(err, IOError) and err.errno == ENOENT:
logger.info('not yet created')
logger.info(_('not yet created'))
else:
logger.info('failed: %s', err)
logger.info(_('failed: %s'), err)
self._init_env(freshenv=True)
def _init_builder(self, buildername):
# type: (unicode) -> 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'
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]
self.builder = builderclass(self)
@ -355,13 +359,13 @@ class Sphinx(object):
self.builder.build_update()
status = (self.statuscode == 0 and
'succeeded' or 'finished with problems')
_('succeeded') or _('finished with problems'))
if self._warncount:
logger.info(bold('build %s, %s warning%s.' %
logger.info(bold(_('build %s, %s warning%s.') %
(status, self._warncount,
self._warncount != 1 and 's' or '')))
else:
logger.info(bold('build %s.' % status))
logger.info(bold(_('build %s.') % status))
except Exception as err:
# delete the saved env to force a fresh build next time
envfile = path.join(self.doctreedir, ENV_PICKLE_FILENAME)
@ -472,20 +476,20 @@ class Sphinx(object):
if extension in self._extensions:
return
if extension in EXTENSION_BLACKLIST:
logger.warning('the extension %r was already merged with Sphinx since version %s; '
'this extension is ignored.',
logger.warning(_('the extension %r was already merged with Sphinx since '
'version %s; this extension is ignored.'),
extension, EXTENSION_BLACKLIST[extension])
return
self._setting_up_extension.append(extension)
try:
mod = __import__(extension, None, None, ['setup'])
except ImportError as err:
logger.verbose('Original exception:\n' + traceback.format_exc())
raise ExtensionError('Could not import extension %s' % extension,
logger.verbose(_('Original exception:\n') + traceback.format_exc())
raise ExtensionError(_('Could not import extension %s') % extension,
err)
if not hasattr(mod, 'setup'):
logger.warning('extension %r has no setup() function; is it really '
'a Sphinx extension module?', extension)
logger.warning(_('extension %r has no setup() function; is it really '
'a Sphinx extension module?'), extension)
ext_meta = None
else:
try:
@ -493,9 +497,9 @@ class Sphinx(object):
except VersionRequirementError as err:
# add the extension name to the version required
raise VersionRequirementError(
'The %s extension used by this project needs at least '
'Sphinx v%s; it therefore cannot be built with this '
'version.' % (extension, err))
_('The %s extension used by this project needs at least '
'Sphinx v%s; it therefore cannot be built with this '
'version.') % (extension, err))
if ext_meta is None:
ext_meta = {}
# special-case for compatibility
@ -505,9 +509,9 @@ class Sphinx(object):
if not ext_meta.get('version'):
ext_meta['version'] = 'unknown version'
except Exception:
logger.warning('extension %r returned an unsupported object from '
'its setup() function; it should return None or a '
'metadata dictionary', extension)
logger.warning(_('extension %r returned an unsupported object from '
'its setup() function; it should return None or a '
'metadata dictionary'), extension)
ext_meta = {'version': 'unknown version'}
self._extensions[extension] = mod
self._extension_metadata[extension] = ext_meta
@ -529,7 +533,7 @@ class Sphinx(object):
def _validate_event(self, event):
# type: (unicode) -> None
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):
# type: (unicode, Callable) -> int
@ -560,7 +564,7 @@ class Sphinx(object):
pass
results = []
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))
return results
@ -577,12 +581,11 @@ class Sphinx(object):
# type: (Type[Builder]) -> None
logger.debug('[app] adding builder: %r', builder)
if not hasattr(builder, 'name'):
raise ExtensionError('Builder class %s has no "name" attribute'
raise ExtensionError(_('Builder class %s has no "name" attribute')
% builder)
if builder.name in self.builderclasses:
raise ExtensionError(
'Builder %r already exists (in module %s)' % (
builder.name, self.builderclasses[builder.name].__module__))
raise ExtensionError(_('Builder %r already exists (in module %s)') %
(builder.name, self.builderclasses[builder.name].__module__))
self.builderclasses[builder.name] = builder
def add_config_value(self, name, default, rebuild, types=()):
@ -590,7 +593,7 @@ class Sphinx(object):
logger.debug('[app] adding config value: %r',
(name, default, rebuild) + ((types,) if types else ())) # type: ignore
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):
rebuild = rebuild and 'env' or ''
self.config.add(name, default, rebuild, types)
@ -604,7 +607,7 @@ class Sphinx(object):
def set_translator(self, name, translator_class):
# 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
def add_node(self, node, **kwds):
@ -612,8 +615,8 @@ class Sphinx(object):
logger.debug('[app] adding node: %r', (node, kwds))
if not kwds.pop('override', False) and \
hasattr(nodes.GenericNodeVisitor, 'visit_' + node.__name__):
logger.warning('while setting up extension %s: node class %r is '
'already registered, its visitors will be overridden',
logger.warning(_('while setting up extension %s: node class %r is '
'already registered, its visitors will be overridden'),
self._setting_up_extension, node.__name__,
type='app', subtype='add_node')
nodes._add_node_class_names([node.__name__])
@ -621,8 +624,8 @@ class Sphinx(object):
try:
visit, depart = val
except ValueError:
raise ExtensionError('Value for key %r must be a '
'(visit, depart) function tuple' % key)
raise ExtensionError(_('Value for key %r must be a '
'(visit, depart) function tuple') % key)
translator = self._translators.get(key)
translators = []
if translator is not None:
@ -665,8 +668,8 @@ class Sphinx(object):
return convert_directive_function(obj)
else:
if content or arguments or options:
raise ExtensionError('when adding directive classes, no '
'additional arguments may be given')
raise ExtensionError(_('when adding directive classes, no '
'additional arguments may be given'))
return obj
def add_directive(self, name, obj, content=None, arguments=None, **options):
@ -674,8 +677,8 @@ class Sphinx(object):
logger.debug('[app] adding directive: %r',
(name, obj, content, arguments, options))
if name in directives._directives:
logger.warning('while setting up extension %s: directive %r is '
'already registered, it will be overridden',
logger.warning(_('while setting up extension %s: directive %r is '
'already registered, it will be overridden'),
self._setting_up_extension[-1], name,
type='app', subtype='add_directive')
directives.register_directive(
@ -685,8 +688,8 @@ class Sphinx(object):
# type: (unicode, Any) -> None
logger.debug('[app] adding role: %r', (name, role))
if name in roles._roles:
logger.warning('while setting up extension %s: role %r is '
'already registered, it will be overridden',
logger.warning(_('while setting up extension %s: role %r is '
'already registered, it will be overridden'),
self._setting_up_extension[-1], name,
type='app', subtype='add_role')
roles.register_local_role(name, role)
@ -697,8 +700,8 @@ class Sphinx(object):
# register_canonical_role
logger.debug('[app] adding generic role: %r', (name, nodeclass))
if name in roles._roles:
logger.warning('while setting up extension %s: role %r is '
'already registered, it will be overridden',
logger.warning(_('while setting up extension %s: role %r is '
'already registered, it will be overridden'),
self._setting_up_extension[-1], name,
type='app', subtype='add_generic_role')
role = roles.GenericRole(name, nodeclass)
@ -708,17 +711,17 @@ class Sphinx(object):
# type: (Type[Domain]) -> None
logger.debug('[app] adding domain: %r', domain)
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
def override_domain(self, domain):
# type: (Type[Domain]) -> None
logger.debug('[app] overriding domain: %r', domain)
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]):
raise ExtensionError('new domain not a subclass of registered %s '
'domain' % domain.name)
raise ExtensionError(_('new domain not a subclass of registered %s '
'domain') % domain.name)
self.domains[domain.name] = domain
def add_directive_to_domain(self, domain, name, obj,
@ -727,7 +730,7 @@ class Sphinx(object):
logger.debug('[app] adding directive to domain: %r',
(domain, name, obj, content, arguments, options))
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._directive_helper(obj, content, arguments, **options)
@ -735,14 +738,14 @@ class Sphinx(object):
# type: (unicode, unicode, Any) -> None
logger.debug('[app] adding role to domain: %r', (domain, name, role))
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
def add_index_to_domain(self, domain, index):
# type: (unicode, Type[Index]) -> None
logger.debug('[app] adding index to domain: %r', (domain, index))
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)
def add_object_type(self, directivename, rolename, indextemplate='',
@ -844,8 +847,8 @@ class Sphinx(object):
# type: (unicode, Parser) -> None
logger.debug('[app] adding search source_parser: %r, %r', suffix, parser)
if suffix in self._additional_source_parsers:
logger.warning('while setting up extension %s: source_parser for %r is '
'already registered, it will be overridden',
logger.warning(_('while setting up extension %s: source_parser for %r is '
'already registered, it will be overridden'),
self._setting_up_extension[-1], suffix,
type='app', subtype='add_source_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 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.osutil import cd
@ -233,8 +233,8 @@ class Config(object):
else:
defvalue = self.values[name][0]
if isinstance(defvalue, dict):
raise ValueError('cannot override dictionary config setting %r, '
'ignoring (use %r to set individual elements)' %
raise ValueError(_('cannot override dictionary config setting %r, '
'ignoring (use %r to set individual elements)') %
(name, name + '.key=value'))
elif isinstance(defvalue, list):
return value.split(',')
@ -242,20 +242,22 @@ class Config(object):
try:
return int(value)
except ValueError:
raise ValueError('invalid number %r for config value %r, ignoring' %
raise ValueError(_('invalid number %r for config value %r, ignoring') %
(value, name))
elif hasattr(defvalue, '__call__'):
return value
elif defvalue is not None and not isinstance(defvalue, string_types):
raise ValueError('cannot override config setting %r with unsupported '
'type, ignoring' % name)
raise ValueError(_('cannot override config setting %r with unsupported '
'type, ignoring') % name)
else:
return value
def pre_init_values(self):
# 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:
try:
if name in self.overrides:
@ -275,7 +277,7 @@ class Config(object):
config.setdefault(realvalname, {})[key] = value
continue
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
if isinstance(value, string_types):
config[valname] = self.convert_overrides(valname, value)
@ -294,7 +296,7 @@ class Config(object):
if name.startswith('_'):
raise AttributeError(name)
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]
if hasattr(default, '__call__'):
return default(self)

View File

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