summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Lehmann <mail@robertlehmann.de>2014-10-30 15:46:02 +0100
committerRobert Lehmann <mail@robertlehmann.de>2014-10-30 15:46:02 +0100
commitcfe0b5cc6dcf0484ca75d08671d3be706e122c89 (patch)
tree7a9baff241c3c59d7c25c310829fa5760f502f45
parent1017e6815c5a199afcbe239c91818c5ff48b94eb (diff)
downloadsphinx-cfe0b5cc6dcf0484ca75d08671d3be706e122c89.tar.gz
Checked configuration values for their types. Fixes #1150.
-rw-r--r--sphinx/application.py3
-rw-r--r--sphinx/config.py27
-rw-r--r--sphinx/errors.py10
-rw-r--r--tests/test_config.py8
4 files changed, 44 insertions, 4 deletions
diff --git a/sphinx/application.py b/sphinx/application.py
index 630bff36..5f6b92f5 100644
--- a/sphinx/application.py
+++ b/sphinx/application.py
@@ -124,6 +124,7 @@ class Sphinx(object):
self.config = Config(confdir, CONFIG_FILENAME,
confoverrides or {}, self.tags)
self.config.check_unicode(self.warn)
+ # defer checking types until i18n has been initialized
# set confdir to srcdir if -C given (!= no confdir); a few pieces
# of code expect a confdir to be set
@@ -172,6 +173,8 @@ class Sphinx(object):
# set up translation infrastructure
self._init_i18n()
+ # check all configuration values for permissible types
+ self.config.check_types(self.warn)
# set up the build environment
self._init_env(freshenv)
# set up the builder
diff --git a/sphinx/config.py b/sphinx/config.py
index cf6bed08..be30d1ea 100644
--- a/sphinx/config.py
+++ b/sphinx/config.py
@@ -14,7 +14,7 @@ from os import path
from six import PY3, iteritems, string_types, binary_type, integer_types
-from sphinx.errors import ConfigError
+from sphinx.errors import ConfigError, ConfigWarning
from sphinx.locale import l_
from sphinx.util.osutil import make_filename, cd
from sphinx.util.pycompat import execfile_
@@ -249,6 +249,30 @@ class Config(object):
self.setup = config.get('setup', None)
self.extensions = config.get('extensions', [])
+ def check_types(self, warn):
+ # 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
+ # this method until i18n is initialized
+ for name in self._raw_config:
+ if name not in Config.config_values:
+ continue # we don't know a default value
+ default, dummy_rebuild = Config.config_values[name]
+ if hasattr(default, '__call__'):
+ default = default(self) # could invoke l_()
+ if default is None:
+ continue
+ current = self[name]
+ if type(current) is type(default):
+ continue
+ common_bases = (
+ set(type(current).__bases__) & set(type(default).__bases__))
+ common_bases.discard(object)
+ if common_bases:
+ continue # at least we share a non-trivial base class
+ warn("the config value %r has type `%s', defaults to `%s.'"
+ % (name, type(current).__name__, type(default).__name__))
+
def check_unicode(self, warn):
# check all string values for non-ASCII characters in bytestrings,
# since that can result in UnicodeErrors all over the place
@@ -296,7 +320,6 @@ class Config(object):
for name in config:
if name in self.values:
self.__dict__[name] = config[name]
- del self._raw_config
def __getattr__(self, name):
if name.startswith('_'):
diff --git a/sphinx/errors.py b/sphinx/errors.py
index 3d7a5eb4..4e9828dc 100644
--- a/sphinx/errors.py
+++ b/sphinx/errors.py
@@ -3,8 +3,8 @@
sphinx.errors
~~~~~~~~~~~~~
- Contains SphinxError and a few subclasses (in an extra module to avoid
- circular import problems).
+ Contains SphinxError, a few subclasses (in an extra module to avoid
+ circular import problems), and related classes.
:copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
@@ -75,3 +75,9 @@ class SphinxParallelError(Exception):
def __str__(self):
return traceback.format_exception_only(
self.orig_exc.__class__, self.orig_exc)[0].strip()
+
+class ConfigWarning(UserWarning):
+ """
+ Base category for warnings about dubious configuration values.
+ """
+ pass
diff --git a/tests/test_config.py b/tests/test_config.py
index 0dcf3fa3..c11c0721 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -133,3 +133,11 @@ def test_config_eol(tmpdir):
cfg = Config(tmpdir, 'conf.py', {}, None)
cfg.init_values(lambda warning: 1/0)
assert cfg.project == u'spam'
+
+
+@with_app(confoverrides={'master_doc': 123})
+def test_check_types(app, status, warning):
+ # WARNING: the config value 'master_doc' has type `int', defaults to `str.'
+ assert any(buf.startswith('WARNING:')
+ and 'master_doc' in buf and 'int' in buf and 'str' in buf
+ for buf in warning.buflist)