Skip to content

Commit c79dbe4

Browse files
authored
Merge pull request #5627 from stsewd/move-file-validations-out-of-the-config-module
Move file validations out of the config module
2 parents 92816b0 + 1204b62 commit c79dbe4

File tree

4 files changed

+17
-178
lines changed

4 files changed

+17
-178
lines changed

readthedocs/config/config.py

+8-9
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@
3030
validate_bool,
3131
validate_choice,
3232
validate_dict,
33-
validate_directory,
34-
validate_file,
33+
validate_path,
3534
validate_list,
3635
validate_string,
3736
)
@@ -499,7 +498,7 @@ def validate_conda(self):
499498
with self.catch_validation_error('conda.file'):
500499
if 'file' not in raw_conda:
501500
raise ValidationError('file', VALUE_NOT_FOUND)
502-
conda_environment = validate_file(
501+
conda_environment = validate_path(
503502
raw_conda['file'],
504503
self.base_path,
505504
)
@@ -516,7 +515,7 @@ def validate_requirements_file(self):
516515
if not requirements_file:
517516
return None
518517
with self.catch_validation_error('requirements_file'):
519-
requirements_file = validate_file(
518+
requirements_file = validate_path(
520519
requirements_file,
521520
self.base_path,
522521
)
@@ -685,7 +684,7 @@ def validate_conda(self):
685684
conda = {}
686685
with self.catch_validation_error('conda.environment'):
687686
environment = self.pop_config('conda.environment', raise_ex=True)
688-
conda['environment'] = validate_file(environment, self.base_path)
687+
conda['environment'] = validate_path(environment, self.base_path)
689688
return conda
690689

691690
def validate_build(self):
@@ -791,15 +790,15 @@ def validate_python_install(self, index):
791790
if 'requirements' in raw_install:
792791
requirements_key = key + '.requirements'
793792
with self.catch_validation_error(requirements_key):
794-
requirements = validate_file(
793+
requirements = validate_path(
795794
self.pop_config(requirements_key),
796795
self.base_path,
797796
)
798797
python_install['requirements'] = requirements
799798
elif 'path' in raw_install:
800799
path_key = key + '.path'
801800
with self.catch_validation_error(path_key):
802-
path = validate_directory(
801+
path = validate_path(
803802
self.pop_config(path_key),
804803
self.base_path,
805804
)
@@ -876,7 +875,7 @@ def validate_mkdocs(self):
876875
with self.catch_validation_error('mkdocs.configuration'):
877876
configuration = self.pop_config('mkdocs.configuration', None)
878877
if configuration is not None:
879-
configuration = validate_file(configuration, self.base_path)
878+
configuration = validate_path(configuration, self.base_path)
880879
mkdocs['configuration'] = configuration
881880

882881
with self.catch_validation_error('mkdocs.fail_on_warning'):
@@ -923,7 +922,7 @@ def validate_sphinx(self):
923922
configuration,
924923
)
925924
if configuration is not None:
926-
configuration = validate_file(configuration, self.base_path)
925+
configuration = validate_path(configuration, self.base_path)
927926
sphinx['configuration'] = configuration
928927

929928
with self.catch_validation_error('sphinx.fail_on_warning'):

readthedocs/config/tests/test_config.py

+1-93
Original file line numberDiff line numberDiff line change
@@ -860,16 +860,6 @@ def test_conda_check_valid(self, tmpdir):
860860
build.validate()
861861
assert build.conda.environment == 'environment.yml'
862862

863-
def test_conda_check_invalid(self, tmpdir):
864-
apply_fs(tmpdir, {'environment.yml': ''})
865-
build = self.get_build_config(
866-
{'conda': {'environment': 'no_existing_environment.yml'}},
867-
source_file=str(tmpdir.join('readthedocs.yml')),
868-
)
869-
with raises(InvalidConfig) as excinfo:
870-
build.validate()
871-
assert excinfo.value.key == 'conda.environment'
872-
873863
@pytest.mark.parametrize('value', [3, [], 'invalid'])
874864
def test_conda_check_invalid_value(self, value):
875865
build = self.get_build_config({'conda': value})
@@ -1057,22 +1047,6 @@ def test_python_install_check_default(self, tmpdir):
10571047
assert install[0].method == PIP
10581048
assert install[0].extra_requirements == []
10591049

1060-
def test_python_install_path_check_invalid(self, tmpdir):
1061-
build = self.get_build_config(
1062-
{
1063-
'python': {
1064-
'install': [{
1065-
'path': 'noexists',
1066-
'method': 'pip',
1067-
}],
1068-
},
1069-
},
1070-
source_file=str(tmpdir.join('readthedocs.yml')),
1071-
)
1072-
with raises(InvalidConfig) as excinfo:
1073-
build.validate()
1074-
assert excinfo.value.key == 'python.install.0.path'
1075-
10761050
@pytest.mark.parametrize('value', ['invalid', 'apt'])
10771051
def test_python_install_method_check_invalid(self, value, tmpdir):
10781052
build = self.get_build_config(
@@ -1108,26 +1082,6 @@ def test_python_install_requirements_check_valid(self, tmpdir):
11081082
assert isinstance(install[0], PythonInstallRequirements)
11091083
assert install[0].requirements == 'requirements.txt'
11101084

1111-
def test_python_install_requirements_check_invalid(self, tmpdir):
1112-
apply_fs(tmpdir, {'requirements.txt': ''})
1113-
requirements_file = 'invalid'
1114-
build = self.get_build_config(
1115-
{
1116-
'python': {
1117-
'install': [{
1118-
'path': '.',
1119-
'requirements': requirements_file,
1120-
}],
1121-
},
1122-
},
1123-
source_file=str(tmpdir.join('readthedocs.yml')),
1124-
)
1125-
with raises(InvalidConfig) as excinfo:
1126-
build.validate()
1127-
assert excinfo.value.key == 'python.install.0.requirements'
1128-
error_msg = 'path {} does not exist'.format(requirements_file)
1129-
assert error_msg in str(excinfo.value)
1130-
11311085
def test_python_install_requirements_does_not_allow_null(self, tmpdir):
11321086
build = self.get_build_config(
11331087
{
@@ -1378,7 +1332,7 @@ def test_python_install_extra_requirements_allow_empty(self, tmpdir):
13781332
{
13791333
'python': {
13801334
'install': [{
1381-
'path': '',
1335+
'path': '.',
13821336
'method': 'pip',
13831337
'extra_requirements': [],
13841338
}],
@@ -1427,32 +1381,6 @@ def test_python_install_several_respects_order(self, tmpdir):
14271381

14281382
assert install[2].requirements == 'three.txt'
14291383

1430-
def test_python_install_reports_correct_invalid_index(self, tmpdir):
1431-
apply_fs(tmpdir, {
1432-
'one': {},
1433-
'two': {},
1434-
})
1435-
build = self.get_build_config(
1436-
{
1437-
'python': {
1438-
'install': [{
1439-
'path': 'one',
1440-
'method': 'pip',
1441-
'extra_requirements': [],
1442-
}, {
1443-
'path': 'two',
1444-
'method': 'setuptools',
1445-
}, {
1446-
'requirements': 'three.txt',
1447-
}],
1448-
},
1449-
},
1450-
source_file=str(tmpdir.join('readthedocs.yml')),
1451-
)
1452-
with raises(InvalidConfig) as excinfo:
1453-
build.validate()
1454-
assert excinfo.value.key == 'python.install.2.requirements'
1455-
14561384
@pytest.mark.parametrize('value', [True, False])
14571385
def test_python_system_packages_check_valid(self, value):
14581386
build = self.get_build_config({
@@ -1565,16 +1493,6 @@ def test_sphinx_configuration_check_valid(self, tmpdir):
15651493
build.validate()
15661494
assert build.sphinx.configuration == 'conf.py'
15671495

1568-
def test_sphinx_configuration_check_invalid(self, tmpdir):
1569-
apply_fs(tmpdir, {'conf.py': ''})
1570-
build = self.get_build_config(
1571-
{'sphinx': {'configuration': 'invalid.py'}},
1572-
source_file=str(tmpdir.join('readthedocs.yml')),
1573-
)
1574-
with raises(InvalidConfig) as excinfo:
1575-
build.validate()
1576-
assert excinfo.value.key == 'sphinx.configuration'
1577-
15781496
def test_sphinx_cant_be_used_with_mkdocs(self, tmpdir):
15791497
apply_fs(tmpdir, {'conf.py': ''})
15801498
build = self.get_build_config(
@@ -1677,16 +1595,6 @@ def test_mkdocs_configuration_check_valid(self, tmpdir):
16771595
assert build.doctype == 'mkdocs'
16781596
assert build.sphinx is None
16791597

1680-
def test_mkdocs_configuration_check_invalid(self, tmpdir):
1681-
apply_fs(tmpdir, {'mkdocs.yml': ''})
1682-
build = self.get_build_config(
1683-
{'mkdocs': {'configuration': 'invalid.yml'}},
1684-
source_file=str(tmpdir.join('readthedocs.yml')),
1685-
)
1686-
with raises(InvalidConfig) as excinfo:
1687-
build.validate()
1688-
assert excinfo.value.key == 'mkdocs.configuration'
1689-
16901598
def test_mkdocs_configuration_allow_null(self):
16911599
build = self.get_build_config(
16921600
{'mkdocs': {'configuration': None}},

readthedocs/config/tests/test_validation.py

+2-46
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
1+
import os
2+
13
from mock import patch
24
from pytest import raises
35

46
from readthedocs.config.validation import (
57
INVALID_BOOL,
68
INVALID_CHOICE,
7-
INVALID_DIRECTORY,
8-
INVALID_FILE,
99
INVALID_LIST,
10-
INVALID_PATH,
1110
INVALID_STRING,
1211
ValidationError,
1312
validate_bool,
1413
validate_choice,
15-
validate_directory,
16-
validate_file,
1714
validate_list,
1815
validate_path,
1916
validate_string,
@@ -80,42 +77,6 @@ def test_it_rejects_string_types(self):
8077
assert excinfo.value.code == INVALID_LIST
8178

8279

83-
class TestValidateDirectory:
84-
85-
def test_it_uses_validate_path(self, tmpdir):
86-
patcher = patch('readthedocs.config.validation.validate_path')
87-
with patcher as validate_path:
88-
path = str(tmpdir.mkdir('a directory'))
89-
validate_path.return_value = path
90-
validate_directory(path, str(tmpdir))
91-
validate_path.assert_called_with(path, str(tmpdir))
92-
93-
def test_it_rejects_files(self, tmpdir):
94-
tmpdir.join('file').write('content')
95-
with raises(ValidationError) as excinfo:
96-
validate_directory('file', str(tmpdir))
97-
assert excinfo.value.code == INVALID_DIRECTORY
98-
99-
100-
class TestValidateFile:
101-
102-
def test_it_uses_validate_path(self, tmpdir):
103-
patcher = patch('readthedocs.config.validation.validate_path')
104-
with patcher as validate_path:
105-
path = tmpdir.join('a file')
106-
path.write('content')
107-
path = str(path)
108-
validate_path.return_value = path
109-
validate_file(path, str(tmpdir))
110-
validate_path.assert_called_with(path, str(tmpdir))
111-
112-
def test_it_rejects_directories(self, tmpdir):
113-
tmpdir.mkdir('directory')
114-
with raises(ValidationError) as excinfo:
115-
validate_file('directory', str(tmpdir))
116-
assert excinfo.value.code == INVALID_FILE
117-
118-
11980
class TestValidatePath:
12081

12182
def test_it_accepts_relative_path(self, tmpdir):
@@ -140,11 +101,6 @@ def test_it_only_accepts_strings(self):
140101
validate_path(None, '')
141102
assert excinfo.value.code == INVALID_STRING
142103

143-
def test_it_rejects_non_existent_path(self, tmpdir):
144-
with raises(ValidationError) as excinfo:
145-
validate_path('does not exist', str(tmpdir))
146-
assert excinfo.value.code == INVALID_PATH
147-
148104

149105
class TestValidateString:
150106

readthedocs/config/validation.py

+6-30
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
"""Validations for the RTD configuration file."""
2+
23
import os
34

45

56
INVALID_BOOL = 'invalid-bool'
67
INVALID_CHOICE = 'invalid-choice'
78
INVALID_LIST = 'invalid-list'
89
INVALID_DICT = 'invalid-dictionary'
9-
INVALID_DIRECTORY = 'invalid-directory'
10-
INVALID_FILE = 'invalid-file'
1110
INVALID_PATH = 'invalid-path'
1211
INVALID_STRING = 'invalid-string'
1312
VALUE_NOT_FOUND = 'value-not-found'
@@ -20,8 +19,6 @@ class ValidationError(Exception):
2019
messages = {
2120
INVALID_BOOL: 'expected one of (0, 1, true, false), got {value}',
2221
INVALID_CHOICE: 'expected one of ({choices}), got {value}',
23-
INVALID_DIRECTORY: '{value} is not a directory',
24-
INVALID_FILE: '{value} is not a file',
2522
INVALID_DICT: '{value} is not a dictionary',
2623
INVALID_PATH: 'path {value} does not exist',
2724
INVALID_STRING: 'expected string',
@@ -77,35 +74,14 @@ def validate_bool(value):
7774
return bool(value)
7875

7976

80-
def validate_directory(value, base_path):
81-
"""Check that ``value`` is a directory."""
82-
path = os.path.join(
83-
base_path,
84-
validate_path(value, base_path)
85-
)
86-
if not os.path.isdir(path):
87-
raise ValidationError(value, INVALID_DIRECTORY)
88-
return os.path.relpath(path, base_path)
89-
90-
91-
def validate_file(value, base_path):
92-
"""Check that ``value`` is a file."""
93-
path = os.path.join(
94-
base_path,
95-
validate_path(value, base_path)
96-
)
97-
if not os.path.isfile(path):
98-
raise ValidationError(value, INVALID_FILE)
99-
return os.path.relpath(path, base_path)
100-
101-
10277
def validate_path(value, base_path):
103-
"""Check that ``value`` is an existent file in ``base_path``."""
78+
"""Check that ``value`` is a valid path name and normamlize it."""
10479
string_value = validate_string(value)
105-
pathed_value = os.path.join(base_path, string_value)
106-
if not os.path.exists(pathed_value):
80+
if not string_value:
10781
raise ValidationError(value, INVALID_PATH)
108-
return os.path.relpath(pathed_value, base_path)
82+
full_path = os.path.join(base_path, string_value)
83+
rel_path = os.path.relpath(full_path, base_path)
84+
return rel_path
10985

11086

11187
def validate_string(value):

0 commit comments

Comments
 (0)