Merge pull request #4800 from stsewd/remove-support-for-mult-confs
Remove support for multiple configurations in one fileghowardsit
commit
164800694a
|
@ -36,7 +36,6 @@ __all__ = (
|
|||
'ConfigError',
|
||||
'ConfigOptionNotSupportedError',
|
||||
'InvalidConfig',
|
||||
'ProjectConfig',
|
||||
)
|
||||
|
||||
ALL = 'all'
|
||||
|
@ -110,12 +109,10 @@ class InvalidConfig(ConfigError):
|
|||
|
||||
message_template = 'Invalid "{key}": {error}'
|
||||
|
||||
def __init__(self, key, code, error_message, source_file=None,
|
||||
source_position=None):
|
||||
def __init__(self, key, code, error_message, source_file=None):
|
||||
self.key = key
|
||||
self.code = code
|
||||
self.source_file = source_file
|
||||
self.source_position = source_position
|
||||
message = self.message_template.format(
|
||||
key=key,
|
||||
code=code,
|
||||
|
@ -149,11 +146,10 @@ class BuildConfigBase(object):
|
|||
]
|
||||
version = None
|
||||
|
||||
def __init__(self, env_config, raw_config, source_file, source_position):
|
||||
def __init__(self, env_config, raw_config, source_file):
|
||||
self.env_config = env_config
|
||||
self.raw_config = raw_config
|
||||
self.source_file = source_file
|
||||
self.source_position = source_position
|
||||
if os.path.isdir(self.source_file):
|
||||
self.base_path = self.source_file
|
||||
else:
|
||||
|
@ -165,10 +161,7 @@ class BuildConfigBase(object):
|
|||
def error(self, key, message, code):
|
||||
"""Raise an error related to ``key``."""
|
||||
if not os.path.isdir(self.source_file):
|
||||
source = '{file} [{pos}]'.format(
|
||||
file=os.path.relpath(self.source_file, self.base_path),
|
||||
pos=self.source_position,
|
||||
)
|
||||
source = os.path.relpath(self.source_file, self.base_path)
|
||||
error_message = '{source}: {message}'.format(
|
||||
source=source,
|
||||
message=message,
|
||||
|
@ -180,7 +173,6 @@ class BuildConfigBase(object):
|
|||
code=code,
|
||||
error_message=error_message,
|
||||
source_file=self.source_file,
|
||||
source_position=self.source_position,
|
||||
)
|
||||
|
||||
@contextmanager
|
||||
|
@ -194,7 +186,6 @@ class BuildConfigBase(object):
|
|||
code=error.code,
|
||||
error_message=str(error),
|
||||
source_file=self.source_file,
|
||||
source_position=self.source_position,
|
||||
)
|
||||
|
||||
def pop(self, name, container, default, raise_ex):
|
||||
|
@ -1058,16 +1049,6 @@ class BuildConfigV2(BuildConfigBase):
|
|||
return Submodules(**self._config['submodules'])
|
||||
|
||||
|
||||
class ProjectConfig(list):
|
||||
|
||||
"""Wrapper for multiple build configs."""
|
||||
|
||||
def validate(self):
|
||||
"""Validates each configuration build."""
|
||||
for build in self:
|
||||
build.validate()
|
||||
|
||||
|
||||
def load(path, env_config):
|
||||
"""
|
||||
Load a project configuration and the top-most build config for a given path.
|
||||
|
@ -1083,10 +1064,9 @@ def load(path, env_config):
|
|||
'No configuration file found',
|
||||
code=CONFIG_REQUIRED
|
||||
)
|
||||
build_configs = []
|
||||
with open(filename, 'r') as configuration_file:
|
||||
try:
|
||||
configs = parse(configuration_file.read())
|
||||
config = parse(configuration_file.read())
|
||||
except ParseError as error:
|
||||
raise ConfigError(
|
||||
'Parse error in {filename}: {message}'.format(
|
||||
|
@ -1095,23 +1075,19 @@ def load(path, env_config):
|
|||
),
|
||||
code=CONFIG_SYNTAX_INVALID,
|
||||
)
|
||||
for i, config in enumerate(configs):
|
||||
allow_v2 = env_config.get('allow_v2')
|
||||
if allow_v2:
|
||||
version = config.get('version', 1)
|
||||
else:
|
||||
version = 1
|
||||
build_config = get_configuration_class(version)(
|
||||
env_config,
|
||||
config,
|
||||
source_file=filename,
|
||||
source_position=i,
|
||||
)
|
||||
build_configs.append(build_config)
|
||||
allow_v2 = env_config.get('allow_v2')
|
||||
if allow_v2:
|
||||
version = config.get('version', 1)
|
||||
else:
|
||||
version = 1
|
||||
build_config = get_configuration_class(version)(
|
||||
env_config,
|
||||
config,
|
||||
source_file=filename,
|
||||
)
|
||||
|
||||
project_config = ProjectConfig(build_configs)
|
||||
project_config.validate()
|
||||
return project_config
|
||||
build_config.validate()
|
||||
return build_config
|
||||
|
||||
|
||||
def get_configuration_class(version):
|
||||
|
|
|
@ -17,18 +17,17 @@ class ParseError(Exception):
|
|||
|
||||
def parse(stream):
|
||||
"""
|
||||
Take file-like object and return a list of project configurations.
|
||||
Take file-like object and return a project configuration.
|
||||
|
||||
The files need be valid YAML and only contain mappings as documents.
|
||||
The file need be valid YAML and only contain mappings as document.
|
||||
Everything else raises a ``ParseError``.
|
||||
"""
|
||||
try:
|
||||
configs = list(yaml.safe_load_all(stream))
|
||||
config = yaml.safe_load(stream)
|
||||
except yaml.YAMLError as error:
|
||||
raise ParseError('YAML: {message}'.format(message=error))
|
||||
if not configs:
|
||||
if not isinstance(config, dict):
|
||||
raise ParseError('Expected mapping')
|
||||
if not config:
|
||||
raise ParseError('Empty config')
|
||||
for config in configs:
|
||||
if not isinstance(config, dict):
|
||||
raise ParseError('Expected mapping')
|
||||
return configs
|
||||
return config
|
||||
|
|
|
@ -17,7 +17,6 @@ from readthedocs.config import (
|
|||
ConfigError,
|
||||
ConfigOptionNotSupportedError,
|
||||
InvalidConfig,
|
||||
ProjectConfig,
|
||||
load,
|
||||
)
|
||||
from readthedocs.config.config import (
|
||||
|
@ -81,13 +80,11 @@ type: sphinx
|
|||
}
|
||||
|
||||
|
||||
def get_build_config(config, env_config=None, source_file='readthedocs.yml',
|
||||
source_position=0):
|
||||
def get_build_config(config, env_config=None, source_file='readthedocs.yml'):
|
||||
return BuildConfigV1(
|
||||
env_config or {},
|
||||
config,
|
||||
source_file=source_file,
|
||||
source_position=source_position,
|
||||
)
|
||||
|
||||
|
||||
|
@ -130,10 +127,7 @@ def test_load_empty_config_file(tmpdir):
|
|||
def test_minimal_config(tmpdir):
|
||||
apply_fs(tmpdir, minimal_config_dir)
|
||||
base = str(tmpdir)
|
||||
config = load(base, env_config)
|
||||
assert isinstance(config, ProjectConfig)
|
||||
assert len(config) == 1
|
||||
build = config[0]
|
||||
build = load(base, env_config)
|
||||
assert isinstance(build, BuildConfigV1)
|
||||
|
||||
|
||||
|
@ -144,10 +138,7 @@ def test_load_version1(tmpdir):
|
|||
''')
|
||||
})
|
||||
base = str(tmpdir)
|
||||
config = load(base, get_env_config({'allow_v2': True}))
|
||||
assert isinstance(config, ProjectConfig)
|
||||
assert len(config) == 1
|
||||
build = config[0]
|
||||
build = load(base, get_env_config({'allow_v2': True}))
|
||||
assert isinstance(build, BuildConfigV1)
|
||||
|
||||
|
||||
|
@ -158,10 +149,7 @@ def test_load_version2(tmpdir):
|
|||
''')
|
||||
})
|
||||
base = str(tmpdir)
|
||||
config = load(base, get_env_config({'allow_v2': True}))
|
||||
assert isinstance(config, ProjectConfig)
|
||||
assert len(config) == 1
|
||||
build = config[0]
|
||||
build = load(base, get_env_config({'allow_v2': True}))
|
||||
assert isinstance(build, BuildConfigV2)
|
||||
|
||||
|
||||
|
@ -182,31 +170,18 @@ def test_yaml_extension(tmpdir):
|
|||
apply_fs(tmpdir, yaml_extension_config_dir)
|
||||
base = str(tmpdir)
|
||||
config = load(base, env_config)
|
||||
assert len(config) == 1
|
||||
assert isinstance(config, BuildConfigV1)
|
||||
|
||||
|
||||
def test_build_config_has_source_file(tmpdir):
|
||||
base = str(apply_fs(tmpdir, minimal_config_dir))
|
||||
build = load(base, env_config)[0]
|
||||
build = load(base, env_config)
|
||||
assert build.source_file == os.path.join(base, 'readthedocs.yml')
|
||||
assert build.source_position == 0
|
||||
|
||||
|
||||
def test_build_config_has_source_position(tmpdir):
|
||||
base = str(apply_fs(tmpdir, multiple_config_dir))
|
||||
builds = load(base, env_config)
|
||||
assert len(builds) == 2
|
||||
first, second = filter(
|
||||
lambda b: not b.source_file.endswith('nested/readthedocs.yml'),
|
||||
builds,
|
||||
)
|
||||
assert first.source_position == 0
|
||||
assert second.source_position == 1
|
||||
|
||||
|
||||
def test_build_config_has_list_with_single_empty_value(tmpdir):
|
||||
base = str(apply_fs(tmpdir, config_with_explicit_empty_list))
|
||||
build = load(base, env_config)[0]
|
||||
build = load(base, env_config)
|
||||
assert isinstance(build, BuildConfigV1)
|
||||
assert build.formats == []
|
||||
|
||||
|
@ -216,7 +191,6 @@ def test_config_requires_name():
|
|||
{'output_base': ''},
|
||||
{},
|
||||
source_file='readthedocs.yml',
|
||||
source_position=0,
|
||||
)
|
||||
with raises(InvalidConfig) as excinfo:
|
||||
build.validate()
|
||||
|
@ -229,7 +203,6 @@ def test_build_requires_valid_name():
|
|||
{'output_base': ''},
|
||||
{'name': 'with/slashes'},
|
||||
source_file='readthedocs.yml',
|
||||
source_position=0,
|
||||
)
|
||||
with raises(InvalidConfig) as excinfo:
|
||||
build.validate()
|
||||
|
@ -553,7 +526,6 @@ def test_valid_build_config():
|
|||
env_config,
|
||||
minimal_config,
|
||||
source_file='readthedocs.yml',
|
||||
source_position=0,
|
||||
)
|
||||
build.validate()
|
||||
assert build.name == 'docs'
|
||||
|
@ -575,7 +547,6 @@ class TestValidateBase(object):
|
|||
get_env_config(),
|
||||
{'base': '../docs'},
|
||||
source_file=source_file,
|
||||
source_position=0,
|
||||
)
|
||||
build.validate()
|
||||
assert build.base == str(tmpdir.join('docs'))
|
||||
|
@ -596,7 +567,6 @@ class TestValidateBase(object):
|
|||
get_env_config(),
|
||||
{'base': 1},
|
||||
source_file=str(tmpdir.join('readthedocs.yml')),
|
||||
source_position=0,
|
||||
)
|
||||
with raises(InvalidConfig) as excinfo:
|
||||
build.validate()
|
||||
|
@ -609,7 +579,6 @@ class TestValidateBase(object):
|
|||
get_env_config(),
|
||||
{'base': 'docs'},
|
||||
source_file=str(tmpdir.join('readthedocs.yml')),
|
||||
source_position=0,
|
||||
)
|
||||
with raises(InvalidConfig) as excinfo:
|
||||
build.validate()
|
||||
|
@ -625,7 +594,6 @@ class TestValidateBuild(object):
|
|||
get_env_config(),
|
||||
{'build': {'image': 3.0}},
|
||||
source_file=str(tmpdir.join('readthedocs.yml')),
|
||||
source_position=0,
|
||||
)
|
||||
with raises(InvalidConfig) as excinfo:
|
||||
build.validate()
|
||||
|
@ -641,7 +609,6 @@ class TestValidateBuild(object):
|
|||
'python': {'version': '3.3'},
|
||||
},
|
||||
source_file=str(tmpdir.join('readthedocs.yml')),
|
||||
source_position=0,
|
||||
)
|
||||
build.validate_build()
|
||||
with raises(InvalidConfig) as excinfo:
|
||||
|
@ -658,7 +625,6 @@ class TestValidateBuild(object):
|
|||
'python': {'version': '3.3'},
|
||||
},
|
||||
source_file=str(tmpdir.join('readthedocs.yml')),
|
||||
source_position=0,
|
||||
)
|
||||
build.validate_build()
|
||||
build.validate_python()
|
||||
|
@ -669,7 +635,6 @@ class TestValidateBuild(object):
|
|||
get_env_config(),
|
||||
{'build': {'image': 'latest'}},
|
||||
source_file=str(tmpdir.join('readthedocs.yml')),
|
||||
source_position=0,
|
||||
)
|
||||
build.validate()
|
||||
assert build.build.image == 'readthedocs/build:latest'
|
||||
|
@ -680,7 +645,6 @@ class TestValidateBuild(object):
|
|||
get_env_config(),
|
||||
{},
|
||||
source_file=str(tmpdir.join('readthedocs.yml')),
|
||||
source_position=0,
|
||||
)
|
||||
build.validate()
|
||||
assert build.build.image == 'readthedocs/build:2.0'
|
||||
|
@ -696,7 +660,6 @@ class TestValidateBuild(object):
|
|||
get_env_config({'defaults': defaults}),
|
||||
{'build': {'image': 'latest'}},
|
||||
source_file=str(tmpdir.join('readthedocs.yml')),
|
||||
source_position=0,
|
||||
)
|
||||
build.validate()
|
||||
assert build.build.image == image
|
||||
|
@ -786,7 +749,6 @@ def test_build_validate_calls_all_subvalidators(tmpdir):
|
|||
{},
|
||||
{},
|
||||
source_file=str(tmpdir.join('readthedocs.yml')),
|
||||
source_position=0,
|
||||
)
|
||||
with patch.multiple(
|
||||
BuildConfigV1,
|
||||
|
@ -802,20 +764,6 @@ def test_build_validate_calls_all_subvalidators(tmpdir):
|
|||
BuildConfigV1.validate_output_base.assert_called_with()
|
||||
|
||||
|
||||
def test_validate_project_config():
|
||||
with patch.object(BuildConfigV1, 'validate') as build_validate:
|
||||
project = ProjectConfig([
|
||||
BuildConfigV1(
|
||||
env_config,
|
||||
minimal_config,
|
||||
source_file='readthedocs.yml',
|
||||
source_position=0,
|
||||
),
|
||||
])
|
||||
project.validate()
|
||||
assert build_validate.call_count == 1
|
||||
|
||||
|
||||
def test_load_calls_validate(tmpdir):
|
||||
apply_fs(tmpdir, minimal_config_dir)
|
||||
base = str(tmpdir)
|
||||
|
@ -896,13 +844,12 @@ def test_as_dict(tmpdir):
|
|||
|
||||
class TestBuildConfigV2(object):
|
||||
|
||||
def get_build_config(self, config, env_config=None,
|
||||
source_file='readthedocs.yml', source_position=0):
|
||||
def get_build_config(
|
||||
self, config, env_config=None, source_file='readthedocs.yml'):
|
||||
return BuildConfigV2(
|
||||
env_config or {},
|
||||
config,
|
||||
source_file=source_file,
|
||||
source_position=source_position,
|
||||
)
|
||||
|
||||
def test_version(self):
|
||||
|
|
|
@ -28,36 +28,35 @@ def test_parse_bad_type():
|
|||
def test_parse_single_config():
|
||||
buf = StringIO(u'base: path')
|
||||
config = parse(buf)
|
||||
assert isinstance(config, list)
|
||||
assert len(config) == 1
|
||||
assert config[0]['base'] == 'path'
|
||||
assert isinstance(config, dict)
|
||||
assert config['base'] == 'path'
|
||||
|
||||
|
||||
def test_parse_null_value():
|
||||
buf = StringIO(u'base: null')
|
||||
config = parse(buf)
|
||||
assert config[0]['base'] is None
|
||||
assert config['base'] is None
|
||||
|
||||
|
||||
def test_parse_empty_value():
|
||||
buf = StringIO(u'base:')
|
||||
config = parse(buf)
|
||||
assert config[0]['base'] is None
|
||||
assert config['base'] is None
|
||||
|
||||
|
||||
def test_parse_empty_string_value():
|
||||
buf = StringIO(u'base: ""')
|
||||
config = parse(buf)
|
||||
assert config[0]['base'] == ''
|
||||
assert config['base'] == ''
|
||||
|
||||
|
||||
def test_parse_empty_list():
|
||||
buf = StringIO(u'base: []')
|
||||
config = parse(buf)
|
||||
assert config[0]['base'] == []
|
||||
assert config['base'] == []
|
||||
|
||||
|
||||
def test_parse_multiple_configs_in_one_file():
|
||||
def test_do_not_parse_multiple_configs_in_one_file():
|
||||
buf = StringIO(
|
||||
u'''
|
||||
base: path
|
||||
|
@ -67,8 +66,5 @@ name: second
|
|||
nested:
|
||||
works: true
|
||||
''')
|
||||
configs = parse(buf)
|
||||
assert isinstance(configs, list)
|
||||
assert len(configs) == 2
|
||||
assert configs[0]['base'] == 'path'
|
||||
assert configs[1]['nested'] == {'works': True}
|
||||
with raises(ParseError):
|
||||
parse(buf)
|
||||
|
|
|
@ -65,7 +65,7 @@ def load_yaml_config(version):
|
|||
config = load_config(
|
||||
path=checkout_path,
|
||||
env_config=env_config,
|
||||
)[0]
|
||||
)
|
||||
except InvalidConfig:
|
||||
# This is a subclass of ConfigError, so has to come first
|
||||
raise
|
||||
|
@ -74,7 +74,6 @@ def load_yaml_config(version):
|
|||
env_config=env_config,
|
||||
raw_config={},
|
||||
source_file=checkout_path,
|
||||
source_position=0,
|
||||
)
|
||||
config.validate()
|
||||
return config
|
||||
|
|
|
@ -13,7 +13,7 @@ from django_dynamic_fixture import get
|
|||
from mock import MagicMock, PropertyMock, patch
|
||||
|
||||
from readthedocs.builds.models import Version
|
||||
from readthedocs.config import ALL, BuildConfigV1, InvalidConfig, ProjectConfig
|
||||
from readthedocs.config import ALL, BuildConfigV1, InvalidConfig
|
||||
from readthedocs.config.tests.utils import apply_fs
|
||||
from readthedocs.doc_builder.config import load_yaml_config
|
||||
from readthedocs.doc_builder.environments import LocalBuildEnvironment
|
||||
|
@ -27,9 +27,7 @@ def create_load(config=None):
|
|||
"""
|
||||
Mock out the function of the build load function.
|
||||
|
||||
This will create a ProjectConfig list of BuildConfigV1 objects and validate
|
||||
them. The default load function iterates over files and builds up a list of
|
||||
objects. Instead of mocking all of this, just mock the end result.
|
||||
This will create a BuildConfigV1 object and validate it.
|
||||
"""
|
||||
if config is None:
|
||||
config = {}
|
||||
|
@ -41,14 +39,11 @@ def create_load(config=None):
|
|||
}
|
||||
if env_config is not None:
|
||||
env_config_defaults.update(env_config)
|
||||
yaml_config = ProjectConfig([
|
||||
BuildConfigV1(
|
||||
env_config_defaults,
|
||||
config,
|
||||
source_file='readthedocs.yml',
|
||||
source_position=0,
|
||||
),
|
||||
])
|
||||
yaml_config = BuildConfigV1(
|
||||
env_config_defaults,
|
||||
config,
|
||||
source_file='readthedocs.yml',
|
||||
)
|
||||
yaml_config.validate()
|
||||
return yaml_config
|
||||
|
||||
|
|
Loading…
Reference in New Issue