summaryrefslogtreecommitdiff
path: root/sphinx/config.py
diff options
context:
space:
mode:
Diffstat (limited to 'sphinx/config.py')
-rw-r--r--sphinx/config.py109
1 files changed, 75 insertions, 34 deletions
diff --git a/sphinx/config.py b/sphinx/config.py
index 4222dbac9..451aa8b0d 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -13,12 +13,21 @@ import re
from os import path, getenv
from six import PY2, PY3, iteritems, string_types, binary_type, text_type, integer_types
+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
from sphinx.util.pycompat import execfile_, NoneType
-from sphinx.util.i18n import format_date
+
+if False:
+ # For type annotation
+ from typing import Any, Callable, Dict, Iterable, Iterator, List, Tuple # NOQA
+ from sphinx.util.tags import Tags # NOQA
+
+logger = logging.getLogger(__name__)
nonascii_re = re.compile(br'[\x80-\xff]')
copyright_year_re = re.compile(r'^((\d{4}-)?)(\d{4})(?=[ ,])')
@@ -35,6 +44,13 @@ CONFIG_PERMITTED_TYPE_WARNING = "The config value `{name}' has type `{current.__
CONFIG_TYPE_WARNING = "The config value `{name}' has type `{current.__name__}', " \
"defaults to `{default.__name__}'."
+if PY3:
+ unicode = str # special alias for static typing...
+
+ConfigValue = NamedTuple('ConfigValue', [('name', str),
+ ('value', Any),
+ ('rebuild', Union[bool, unicode])])
+
class ENUM(object):
"""represents the config value should be a one of candidates.
@@ -43,13 +59,15 @@ class ENUM(object):
app.add_config_value('latex_show_urls', 'no', None, ENUM('no', 'footnote', 'inline'))
"""
def __init__(self, *candidates):
+ # type: (unicode) -> None
self.candidates = candidates
def match(self, value):
+ # type: (unicode) -> bool
return value in self.candidates
-string_classes = [text_type]
+string_classes = [text_type] # type: List
if PY2:
string_classes.append(binary_type) # => [str, unicode]
@@ -114,15 +132,13 @@ class Config(object):
tls_verify = (True, 'env'),
tls_cacerts = (None, 'env'),
-
- # pre-initialized confval for HTML builder
- html_translator_class = (None, 'html', string_classes),
- )
+ ) # type: Dict[unicode, Tuple]
def __init__(self, dirname, filename, overrides, tags):
+ # type: (unicode, unicode, Dict, Tags) -> None
self.overrides = overrides
self.values = Config.config_values.copy()
- config = {}
+ config = {} # type: Dict[unicode, Any]
if dirname is not None:
config_file = path.join(dirname, filename)
config['__file__'] = config_file
@@ -140,14 +156,14 @@ class Config(object):
self._raw_config = config
# these two must be preinitialized because extensions can add their
# own config values
- self.setup = config.get('setup', None)
+ self.setup = config.get('setup', None) # type: Callable
if 'extensions' in overrides:
if isinstance(overrides['extensions'], string_types):
config['extensions'] = overrides.pop('extensions').split(',')
else:
config['extensions'] = overrides.pop('extensions')
- self.extensions = config.get('extensions', [])
+ self.extensions = config.get('extensions', []) # type: List[unicode]
# correct values of copyright year that are not coherent with
# the SOURCE_DATE_EPOCH environment variable (if set)
@@ -155,10 +171,11 @@ class Config(object):
if getenv('SOURCE_DATE_EPOCH') is not None:
for k in ('copyright', 'epub_copyright'):
if k in config:
- config[k] = copyright_year_re.sub('\g<1>%s' % format_date('%Y'),
+ config[k] = copyright_year_re.sub(r'\g<1>%s' % format_date('%Y'),
config[k])
- def check_types(self, warn):
+ def check_types(self):
+ # type: () -> None
# check all values for deviation from the default value's type, since
# that can result in TypeErrors all over the place
# NB. since config values might use l_() we have to wait with calling
@@ -177,7 +194,7 @@ class Config(object):
current = self[name]
if isinstance(permitted, ENUM):
if not permitted.match(current):
- warn(CONFIG_ENUM_WARNING.format(
+ logger.warning(CONFIG_ENUM_WARNING.format(
name=name, current=current, candidates=permitted.candidates))
else:
if type(current) is type(default):
@@ -192,30 +209,32 @@ class Config(object):
continue # at least we share a non-trivial base class
if permitted:
- warn(CONFIG_PERMITTED_TYPE_WARNING.format(
+ logger.warning(CONFIG_PERMITTED_TYPE_WARNING.format(
name=name, current=type(current),
permitted=str([cls.__name__ for cls in permitted])))
else:
- warn(CONFIG_TYPE_WARNING.format(
+ logger.warning(CONFIG_TYPE_WARNING.format(
name=name, current=type(current), default=type(default)))
- def check_unicode(self, warn):
+ def check_unicode(self):
+ # type: () -> None
# check all string values for non-ASCII characters in bytestrings,
# since that can result in UnicodeErrors all over the place
for name, value in iteritems(self._raw_config):
if isinstance(value, binary_type) and nonascii_re.search(value):
- warn('the config value %r is set to a string with non-ASCII '
- 'characters; this can lead to Unicode errors occurring. '
- 'Please use Unicode strings, e.g. %r.' % (name, u'Content'))
+ logger.warning('the config value %r is set to a string with non-ASCII '
+ 'characters; this can lead to Unicode errors occurring. '
+ 'Please use Unicode strings, e.g. %r.', name, u'Content')
def convert_overrides(self, name, value):
+ # type: (unicode, Any) -> Any
if not isinstance(value, string_types):
return value
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(',')
@@ -223,19 +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, warn):
- """Initialize some limited config variables before loading extensions"""
- variables = ['needs_sphinx', 'suppress_warnings', 'html_translator_class']
+ def pre_init_values(self):
+ # type: () -> None
+ """
+ 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:
@@ -243,9 +265,10 @@ class Config(object):
elif name in self._raw_config:
self.__dict__[name] = self._raw_config[name]
except ValueError as exc:
- warn(exc)
+ logger.warning("%s", exc)
- def init_values(self, warn):
+ def init_values(self):
+ # type: () -> None
config = self._raw_config
for valname, value in iteritems(self.overrides):
try:
@@ -254,38 +277,56 @@ class Config(object):
config.setdefault(realvalname, {})[key] = value
continue
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
if isinstance(value, string_types):
config[valname] = self.convert_overrides(valname, value)
else:
config[valname] = value
except ValueError as exc:
- warn(exc)
+ logger.warning("%s", exc)
for name in config:
if name in self.values:
self.__dict__[name] = config[name]
- if isinstance(self.source_suffix, string_types):
- self.source_suffix = [self.source_suffix]
+ if isinstance(self.source_suffix, string_types): # type: ignore
+ self.source_suffix = [self.source_suffix] # type: ignore
def __getattr__(self, name):
+ # type: (unicode) -> Any
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)
return default
def __getitem__(self, name):
+ # type: (unicode) -> unicode
return getattr(self, name)
def __setitem__(self, name, value):
+ # type: (unicode, Any) -> None
setattr(self, name, value)
def __delitem__(self, name):
+ # type: (unicode) -> None
delattr(self, name)
def __contains__(self, name):
+ # type: (unicode) -> bool
return name in self.values
+
+ def __iter__(self):
+ # type: () -> Iterable[ConfigValue]
+ for name, value in iteritems(self.values):
+ yield ConfigValue(name, getattr(self, name), value[1]) # type: ignore
+
+ def add(self, name, default, rebuild, types):
+ # type: (unicode, Any, Union[bool, unicode], Any) -> None
+ self.values[name] = (default, rebuild, types)
+
+ def filter(self, rebuild):
+ # type: (str) -> Iterator[ConfigValue]
+ return (value for value in self if value.rebuild == rebuild) # type: ignore