diff --git a/readthedocs/config/config.py b/readthedocs/config/config.py index 9b7c1a0c782..884945418f0 100644 --- a/readthedocs/config/config.py +++ b/readthedocs/config/config.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # pylint: disable=too-many-lines """Build configuration for rtd.""" @@ -37,6 +36,7 @@ validate_string, ) + __all__ = ( 'ALL', 'load', @@ -44,6 +44,7 @@ 'BuildConfigV2', 'ConfigError', 'ConfigOptionNotSupportedError', + 'ConfigFileNotFound', 'InvalidConfig', 'PIP', 'SETUPTOOLS', @@ -58,7 +59,7 @@ VERSION_INVALID = 'version-invalid' CONFIG_SYNTAX_INVALID = 'config-syntax-invalid' CONFIG_REQUIRED = 'config-required' -CONF_FILE_REQUIRED = 'conf-file-required' +CONFIG_FILE_REQUIRED = 'config-file-required' PYTHON_INVALID = 'python-invalid' SUBMODULES_INVALID = 'submodules-invalid' INVALID_KEYS_COMBINATION = 'invalid-keys-combination' @@ -87,6 +88,17 @@ def __init__(self, message, code): super().__init__(message) +class ConfigFileNotFound(ConfigError): + + """Error when we can't find a configuration file.""" + + def __init__(self, directory): + super().__init__( + f"Configuration file not found in: {directory}", + CONFIG_FILE_REQUIRED, + ) + + class ConfigOptionNotSupportedError(ConfigError): """Error for unsupported configuration options in a version.""" @@ -298,7 +310,6 @@ class BuildConfigV1(BuildConfigBase): """Version 1 of the configuration file.""" - CONF_FILE_REQUIRED_MESSAGE = 'Missing key "conf_file"' PYTHON_INVALID_MESSAGE = '"python" section must be a mapping.' PYTHON_EXTRA_REQUIREMENTS_INVALID_MESSAGE = ( '"python.extra_requirements" section must be a list.' @@ -1104,14 +1115,14 @@ def load(path, env_config): filename = find_one(path, CONFIG_FILENAME_REGEX) if not filename: - raise ConfigError('No configuration file found', code=CONFIG_REQUIRED) + raise ConfigFileNotFound(path) with open(filename, 'r') as configuration_file: try: config = parse(configuration_file.read()) except ParseError as error: raise ConfigError( 'Parse error in {filename}: {message}'.format( - filename=filename, + filename=os.path.relpath(filename, path), message=str(error), ), code=CONFIG_SYNTAX_INVALID, diff --git a/readthedocs/config/tests/test_config.py b/readthedocs/config/tests/test_config.py index 3ad5cfe8a9d..404cf691211 100644 --- a/readthedocs/config/tests/test_config.py +++ b/readthedocs/config/tests/test_config.py @@ -15,14 +15,17 @@ BuildConfigV1, BuildConfigV2, ConfigError, + ConfigFileNotFound, ConfigOptionNotSupportedError, InvalidConfig, load, ) from readthedocs.config.config import ( + CONFIG_FILE_REQUIRED, CONFIG_FILENAME_REGEX, CONFIG_NOT_SUPPORTED, CONFIG_REQUIRED, + CONFIG_SYNTAX_INVALID, INVALID_KEY, PYTHON_INVALID, VERSION_INVALID, @@ -73,9 +76,9 @@ def get_build_config(config, env_config=None, source_file='readthedocs.yml'): def test_load_no_config_file(tmpdir, files): apply_fs(tmpdir, files) base = str(tmpdir) - with raises(ConfigError) as e: + with raises(ConfigFileNotFound) as e: load(base, {}) - assert e.value.code == CONFIG_REQUIRED + assert e.value.code == CONFIG_FILE_REQUIRED def test_load_empty_config_file(tmpdir): @@ -136,6 +139,27 @@ def test_load_unknow_version(tmpdir): assert excinfo.value.code == VERSION_INVALID +def test_load_raise_exception_invalid_syntax(tmpdir): + apply_fs( + tmpdir, { + 'readthedocs.yml': textwrap.dedent(''' + version: 2 + python: + install: + - method: pip + path: . + # bad indentation here + extra_requirements: + - build + '''), + }, + ) + base = str(tmpdir) + with raises(ConfigError) as excinfo: + load(base, {}) + assert excinfo.value.code == CONFIG_SYNTAX_INVALID + + def test_yaml_extension(tmpdir): """Make sure loading the 'readthedocs' file with a 'yaml' extension.""" apply_fs( diff --git a/readthedocs/doc_builder/config.py b/readthedocs/doc_builder/config.py index 9e002d561d7..5f57a4402b1 100644 --- a/readthedocs/doc_builder/config.py +++ b/readthedocs/doc_builder/config.py @@ -1,10 +1,8 @@ -# -*- coding: utf-8 -*- - """An API to load config from a readthedocs.yml file.""" from os import path -from readthedocs.config import BuildConfigV1, ConfigError, InvalidConfig +from readthedocs.config import BuildConfigV1, ConfigFileNotFound from readthedocs.config import load as load_config from readthedocs.projects.models import ProjectConfigurationError @@ -59,10 +57,9 @@ def load_yaml_config(version): path=checkout_path, env_config=env_config, ) - except InvalidConfig: - # This is a subclass of ConfigError, so has to come first - raise - except ConfigError: + except ConfigFileNotFound: + # Dafault to use v1 with some defaults from the web interface + # if we don't find a configuration file. config = BuildConfigV1( env_config=env_config, raw_config={},