Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions sphinx/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,15 +255,16 @@ def __init__(
self.statuscode = 0

# read config
overrides = confoverrides or {}
self.tags = Tags(tags)
if confdir is None:
# set confdir to srcdir if -C given (!= no confdir); a few pieces
# of code expect a confdir to be set
self.confdir = self.srcdir
self.config = Config({}, confoverrides or {})
self.config = Config({}, overrides)
else:
self.confdir = _StrPath(confdir).resolve()
self.config = Config.read(self.confdir, confoverrides or {}, self.tags)
self.config = Config.read(self.confdir, overrides=overrides, tags=self.tags)
self.config._verbosity = -1 if self.quiet else self.verbosity

# set up translation infrastructure
Expand Down
49 changes: 25 additions & 24 deletions sphinx/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,32 +351,17 @@ def verbosity(self) -> int:
def read(
cls: type[Config],
confdir: str | os.PathLike[str],
overrides: dict[str, Any] | None = None,
tags: Tags | None = None,
*,
overrides: dict[str, Any],
tags: Tags,
) -> Config:
"""Create a Config object from configuration file."""
filename = Path(confdir, CONFIG_FILENAME)
if not filename.is_file():
raise ConfigError(
__("config directory doesn't contain a conf.py file (%s)") % confdir
)
namespace = eval_config_file(filename, tags)

# Note: Old sphinx projects have been configured as "language = None" because
# sphinx-quickstart previously generated this by default.
# To keep compatibility, they should be fallback to 'en' for a while
# (This conversion should not be removed before 2025-01-01).
if namespace.get('language', ...) is None:
logger.warning(
__(
"Invalid configuration value found: 'language = None'. "
'Update your configuration to a valid language code. '
"Falling back to 'en' (English)."
)
)
namespace['language'] = 'en'

return cls(namespace, overrides)
return _read_conf_py(filename, overrides=overrides, tags=tags)

def convert_overrides(self, name: str, value: str) -> Any:
opt = self._options[name]
Expand Down Expand Up @@ -589,12 +574,28 @@ def __setstate__(self, state: dict[str, Any]) -> None:
self.__dict__.update(state)


def eval_config_file(
filename: str | os.PathLike[str], tags: Tags | None
) -> dict[str, Any]:
"""Evaluate a config file."""
filename = Path(filename)
def _read_conf_py(conf_path: Path, *, overrides: dict[str, Any], tags: Tags) -> Config:
"""Create a Config object from a conf.py file."""
namespace = eval_config_file(conf_path, tags)

# Note: Old sphinx projects have been configured as "language = None" because
# sphinx-quickstart previously generated this by default.
# To keep compatibility, they should be fallback to 'en' for a while
# (This conversion should not be removed before 2025-01-01).
if namespace.get('language', ...) is None:
logger.warning(
__(
"Invalid configuration value found: 'language = None'. "
'Update your configuration to a valid language code. '
"Falling back to 'en' (English)."
)
)
namespace['language'] = 'en'
return Config(namespace, overrides)


def eval_config_file(filename: Path, tags: Tags) -> dict[str, Any]:
"""Evaluate a config file."""
namespace: dict[str, Any] = {
'__file__': str(filename),
'tags': tags,
Expand Down
17 changes: 9 additions & 8 deletions tests/test_config/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
)
from sphinx.deprecation import RemovedInSphinx90Warning
from sphinx.errors import ConfigError, ExtensionError, VersionRequirementError
from sphinx.util.tags import Tags

if TYPE_CHECKING:
from collections.abc import Iterable
Expand Down Expand Up @@ -139,7 +140,7 @@ def test_core_config(app: SphinxTestApp) -> None:

def test_config_not_found(tmp_path):
with pytest.raises(ConfigError):
Config.read(tmp_path)
Config.read(tmp_path, overrides={}, tags=Tags())


@pytest.mark.parametrize('protocol', list(range(pickle.HIGHEST_PROTOCOL)))
Expand Down Expand Up @@ -394,12 +395,12 @@ def test_errors_warnings(logger, tmp_path):
# test the error for syntax errors in the config file
(tmp_path / 'conf.py').write_text('project = \n', encoding='ascii')
with pytest.raises(ConfigError) as excinfo:
Config.read(tmp_path, {}, None)
Config.read(tmp_path, overrides={}, tags=Tags())
assert 'conf.py' in str(excinfo.value)

# test the automatic conversion of 2.x only code in configs
(tmp_path / 'conf.py').write_text('project = u"Jägermeister"\n', encoding='utf8')
cfg = Config.read(tmp_path, {}, None)
cfg = Config.read(tmp_path, overrides={}, tags=Tags())
assert cfg.project == 'Jägermeister'
assert logger.called is False

Expand Down Expand Up @@ -440,7 +441,7 @@ def test_config_eol(logger, tmp_path):
configfile = tmp_path / 'conf.py'
for eol in (b'\n', b'\r\n'):
configfile.write_bytes(b'project = "spam"' + eol)
cfg = Config.read(tmp_path, {}, None)
cfg = Config.read(tmp_path, overrides={}, tags=Tags())
assert cfg.project == 'spam'
assert logger.called is False

Expand Down Expand Up @@ -678,7 +679,7 @@ def test_conf_py_language_none(tmp_path):
(tmp_path / 'conf.py').write_text('language = None', encoding='utf-8')

# When we load conf.py into a Config object
cfg = Config.read(tmp_path, {}, None)
cfg = Config.read(tmp_path, overrides={}, tags=Tags())

# Then the language is coerced to English
assert cfg.language == 'en'
Expand All @@ -691,7 +692,7 @@ def test_conf_py_language_none_warning(logger, tmp_path):
(tmp_path / 'conf.py').write_text('language = None', encoding='utf-8')

# When we load conf.py into a Config object
Config.read(tmp_path, {}, None)
Config.read(tmp_path, overrides={}, tags=Tags())

# Then a warning is raised
assert logger.warning.called
Expand All @@ -708,7 +709,7 @@ def test_conf_py_no_language(tmp_path):
(tmp_path / 'conf.py').touch()

# When we load conf.py into a Config object
cfg = Config.read(tmp_path, {}, None)
cfg = Config.read(tmp_path, overrides={}, tags=Tags())

# Then the language is coerced to English
assert cfg.language == 'en'
Expand All @@ -720,7 +721,7 @@ def test_conf_py_nitpick_ignore_list(tmp_path):
(tmp_path / 'conf.py').touch()

# When we load conf.py into a Config object
cfg = Config.read(tmp_path, {}, None)
cfg = Config.read(tmp_path, overrides={}, tags=Tags())

# Then the default nitpick_ignore[_regex] is an empty list
assert cfg.nitpick_ignore == []
Expand Down
Loading