-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Move config module from rtd-build repo (#4242)
* Add pytest-describe dependencie * Import tests from validation.py * Downgrade pytest to 3.2.5 pytest-describe is incompatible with the current pytest (3.6.1) had to downgrade to 3.2.5 * Move validation.py * Fix tests Change the pathed module * Linter * Change import
- Loading branch information
Showing
4 changed files
with
265 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
# -*- coding: utf-8 -*- | ||
from __future__ import division, print_function, unicode_literals | ||
|
||
import os | ||
|
||
from mock import patch | ||
from pytest import raises | ||
from six import text_type | ||
|
||
from readthedocs.config.validation import ( | ||
INVALID_BOOL, INVALID_CHOICE, INVALID_DIRECTORY, INVALID_FILE, INVALID_LIST, | ||
INVALID_PATH, INVALID_STRING, ValidationError, validate_bool, | ||
validate_choice, validate_directory, validate_file, validate_list, | ||
validate_path, validate_string) | ||
|
||
|
||
def describe_validate_bool(): | ||
def it_accepts_true(): | ||
assert validate_bool(True) is True | ||
|
||
def it_accepts_false(): | ||
assert validate_bool(False) is False | ||
|
||
def it_accepts_0(): | ||
assert validate_bool(0) is False | ||
|
||
def it_accepts_1(): | ||
assert validate_bool(1) is True | ||
|
||
def it_fails_on_string(): | ||
with raises(ValidationError) as excinfo: | ||
validate_bool('random string') | ||
assert excinfo.value.code == INVALID_BOOL | ||
|
||
|
||
def describe_validate_choice(): | ||
|
||
def it_accepts_valid_choice(): | ||
result = validate_choice('choice', ('choice', 'another_choice')) | ||
assert result is 'choice' | ||
|
||
with raises(ValidationError) as excinfo: | ||
validate_choice('c', 'abc') | ||
assert excinfo.value.code == INVALID_LIST | ||
|
||
def it_rejects_invalid_choice(): | ||
with raises(ValidationError) as excinfo: | ||
validate_choice('not-a-choice', ('choice', 'another_choice')) | ||
assert excinfo.value.code == INVALID_CHOICE | ||
|
||
|
||
def describe_validate_list(): | ||
|
||
def it_accepts_list_types(): | ||
result = validate_list(['choice', 'another_choice']) | ||
assert result == ['choice', 'another_choice'] | ||
|
||
result = validate_list(('choice', 'another_choice')) | ||
assert result == ['choice', 'another_choice'] | ||
|
||
def iterator(): | ||
yield 'choice' | ||
|
||
result = validate_list(iterator()) | ||
assert result == ['choice'] | ||
|
||
with raises(ValidationError) as excinfo: | ||
validate_choice('c', 'abc') | ||
assert excinfo.value.code == INVALID_LIST | ||
|
||
def it_rejects_string_types(): | ||
with raises(ValidationError) as excinfo: | ||
result = validate_list('choice') | ||
assert excinfo.value.code == INVALID_LIST | ||
|
||
|
||
def describe_validate_directory(): | ||
|
||
def it_uses_validate_path(tmpdir): | ||
patcher = patch('readthedocs.config.validation.validate_path') | ||
with patcher as validate_path: | ||
path = text_type(tmpdir.mkdir('a directory')) | ||
validate_path.return_value = path | ||
validate_directory(path, str(tmpdir)) | ||
validate_path.assert_called_with(path, str(tmpdir)) | ||
|
||
def it_rejects_files(tmpdir): | ||
tmpdir.join('file').write('content') | ||
with raises(ValidationError) as excinfo: | ||
validate_directory('file', str(tmpdir)) | ||
assert excinfo.value.code == INVALID_DIRECTORY | ||
|
||
|
||
def describe_validate_file(): | ||
|
||
def it_uses_validate_path(tmpdir): | ||
patcher = patch('readthedocs.config.validation.validate_path') | ||
with patcher as validate_path: | ||
path = tmpdir.join('a file') | ||
path.write('content') | ||
path = str(path) | ||
validate_path.return_value = path | ||
validate_file(path, str(tmpdir)) | ||
validate_path.assert_called_with(path, str(tmpdir)) | ||
|
||
def it_rejects_directories(tmpdir): | ||
tmpdir.mkdir('directory') | ||
with raises(ValidationError) as excinfo: | ||
validate_file('directory', str(tmpdir)) | ||
assert excinfo.value.code == INVALID_FILE | ||
|
||
|
||
def describe_validate_path(): | ||
|
||
def it_accepts_relative_path(tmpdir): | ||
tmpdir.mkdir('a directory') | ||
validate_path('a directory', str(tmpdir)) | ||
|
||
def it_accepts_files(tmpdir): | ||
tmpdir.join('file').write('content') | ||
validate_path('file', str(tmpdir)) | ||
|
||
def it_accepts_absolute_path(tmpdir): | ||
path = str(tmpdir.mkdir('a directory')) | ||
validate_path(path, 'does not matter') | ||
|
||
def it_returns_absolute_path(tmpdir): | ||
tmpdir.mkdir('a directory') | ||
path = validate_path('a directory', str(tmpdir)) | ||
assert path == os.path.abspath(path) | ||
|
||
def it_only_accepts_strings(): | ||
with raises(ValidationError) as excinfo: | ||
validate_path(None, '') | ||
assert excinfo.value.code == INVALID_STRING | ||
|
||
def it_rejects_non_existent_path(tmpdir): | ||
with raises(ValidationError) as excinfo: | ||
validate_path('does not exist', str(tmpdir)) | ||
assert excinfo.value.code == INVALID_PATH | ||
|
||
|
||
def describe_validate_string(): | ||
|
||
def it_accepts_unicode(): | ||
result = validate_string(u'Unicöde') | ||
assert isinstance(result, text_type) | ||
|
||
def it_accepts_nonunicode(): | ||
result = validate_string('Unicode') | ||
assert isinstance(result, text_type) | ||
|
||
def it_rejects_float(): | ||
with raises(ValidationError) as excinfo: | ||
validate_string(123.456) | ||
assert excinfo.value.code == INVALID_STRING | ||
|
||
def it_rejects_none(): | ||
with raises(ValidationError) as excinfo: | ||
validate_string(None) | ||
assert excinfo.value.code == INVALID_STRING |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
"""Validations for the RTD configuration file.""" | ||
from __future__ import division, print_function, unicode_literals | ||
|
||
import os | ||
|
||
from six import string_types, text_type | ||
|
||
INVALID_BOOL = 'invalid-bool' | ||
INVALID_CHOICE = 'invalid-choice' | ||
INVALID_LIST = 'invalid-list' | ||
INVALID_DIRECTORY = 'invalid-directory' | ||
INVALID_FILE = 'invalid-file' | ||
INVALID_PATH = 'invalid-path' | ||
INVALID_STRING = 'invalid-string' | ||
|
||
|
||
class ValidationError(Exception): | ||
|
||
"""Base error for validations.""" | ||
|
||
messages = { | ||
INVALID_BOOL: 'expected one of (0, 1, true, false), got {value}', | ||
INVALID_CHOICE: 'expected one of ({choices}), got {value}', | ||
INVALID_DIRECTORY: '{value} is not a directory', | ||
INVALID_FILE: '{value} is not a file', | ||
INVALID_PATH: 'path {value} does not exist', | ||
INVALID_STRING: 'expected string', | ||
INVALID_LIST: 'expected list', | ||
} | ||
|
||
def __init__(self, value, code, format_kwargs=None): | ||
self.value = value | ||
self.code = code | ||
defaults = { | ||
'value': value, | ||
} | ||
if format_kwargs is not None: | ||
defaults.update(format_kwargs) | ||
message = self.messages[code].format(**defaults) | ||
super(ValidationError, self).__init__(message) | ||
|
||
|
||
def validate_list(value): | ||
"""Check if ``value`` is an iterable.""" | ||
if isinstance(value, str): | ||
raise ValidationError(value, INVALID_LIST) | ||
if not hasattr(value, '__iter__'): | ||
raise ValidationError(value, INVALID_LIST) | ||
return list(value) | ||
|
||
|
||
def validate_choice(value, choices): | ||
"""Check that ``value`` is in ``choices``.""" | ||
choices = validate_list(choices) | ||
if value not in choices: | ||
raise ValidationError(value, INVALID_CHOICE, { | ||
'choices': ', '.join(map(str, choices)) | ||
}) | ||
return value | ||
|
||
|
||
def validate_bool(value): | ||
"""Check that ``value`` is an boolean value.""" | ||
if value not in (0, 1, False, True): | ||
raise ValidationError(value, INVALID_BOOL) | ||
return bool(value) | ||
|
||
|
||
def validate_directory(value, base_path): | ||
"""Check that ``value`` is a directory.""" | ||
path = validate_path(value, base_path) | ||
if not os.path.isdir(path): | ||
raise ValidationError(value, INVALID_DIRECTORY) | ||
return path | ||
|
||
|
||
def validate_file(value, base_path): | ||
"""Check that ``value`` is a file.""" | ||
path = validate_path(value, base_path) | ||
if not os.path.isfile(path): | ||
raise ValidationError(value, INVALID_FILE) | ||
return path | ||
|
||
|
||
def validate_path(value, base_path): | ||
"""Check that ``value`` is an existent file in ``base_path``.""" | ||
string_value = validate_string(value) | ||
pathed_value = os.path.join(base_path, string_value) | ||
final_value = os.path.abspath(pathed_value) | ||
if not os.path.exists(final_value): | ||
raise ValidationError(value, INVALID_PATH) | ||
return final_value | ||
|
||
|
||
def validate_string(value): | ||
"""Check that ``value`` is a string type.""" | ||
if not isinstance(value, string_types): | ||
raise ValidationError(value, INVALID_STRING) | ||
return text_type(value) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters