From 9fc3c943c87fc876da6e0a75f621e15a2201cbdd Mon Sep 17 00:00:00 2001 From: Cordell Blakkan Date: Tue, 19 Mar 2024 15:12:59 -0400 Subject: [PATCH 1/4] Ruff codebase --- ruff.toml | 2 + setup.py | 50 +-- yamale/command_line.py | 73 ++-- yamale/readers/tests/test_bad_file.py | 6 +- yamale/readers/tests/test_yaml.py | 34 +- yamale/readers/yaml_reader.py | 11 +- yamale/schema/datapath.py | 5 +- yamale/schema/schema.py | 48 +-- yamale/schema/validationresults.py | 8 +- yamale/syntax/parser.py | 21 +- yamale/syntax/tests/test_parser.py | 47 ++- yamale/tests/__init__.py | 2 +- yamale/tests/test_command_line.py | 116 +++--- yamale/tests/test_functional.py | 390 ++++++++------------- yamale/tests/test_meta_test.py | 47 +-- yamale/util.py | 3 +- yamale/validators/base.py | 19 +- yamale/validators/constraints.py | 54 +-- yamale/validators/tests/test_constraint.py | 145 ++++---- yamale/validators/tests/test_validate.py | 140 ++++---- yamale/validators/validators.py | 88 +++-- yamale/version.py | 4 +- yamale/yamale.py | 10 +- yamale/yamale_error.py | 2 +- yamale/yamale_testcase.py | 3 +- 25 files changed, 627 insertions(+), 701 deletions(-) create mode 100644 ruff.toml diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 0000000..46e6544 --- /dev/null +++ b/ruff.toml @@ -0,0 +1,2 @@ +line-length = 121 +indent-width = 4 diff --git a/setup.py b/setup.py index 838c944..147d7b6 100755 --- a/setup.py +++ b/setup.py @@ -1,38 +1,38 @@ #!/usr/bin/env python from setuptools import setup, find_packages -readme = open('README.md', encoding='utf-8').read() -license = open('LICENSE', encoding='utf-8').read() -version = open('yamale/VERSION', encoding='utf-8').read().strip() +readme = open("README.md", encoding="utf-8").read() +license = open("LICENSE", encoding="utf-8").read() +version = open("yamale/VERSION", encoding="utf-8").read().strip() setup( - name='yamale', + name="yamale", version=version, - url='https://github.com/23andMe/Yamale', - author='Bo Lopker', - author_email='blopker@23andme.com', - description='A schema and validator for YAML.', + url="https://github.com/23andMe/Yamale", + author="Bo Lopker", + author_email="blopker@23andme.com", + description="A schema and validator for YAML.", long_description=readme, - long_description_content_type='text/markdown', - license='MIT', + long_description_content_type="text/markdown", + license="MIT", packages=find_packages(), include_package_data=True, - install_requires=['pyyaml'], - python_requires='>=3.8', + install_requires=["pyyaml"], + python_requires=">=3.8", entry_points={ - 'console_scripts': ['yamale=yamale.command_line:main'], + "console_scripts": ["yamale=yamale.command_line:main"], }, classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'Operating System :: OS Independent', - 'License :: OSI Approved :: MIT License', - 'Programming Language :: Python', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.12', - ] + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Operating System :: OS Independent", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + ], ) diff --git a/yamale/command_line.py b/yamale/command_line.py index 1193cb8..77fba4f 100644 --- a/yamale/command_line.py +++ b/yamale/command_line.py @@ -2,10 +2,10 @@ # -*- coding: utf-8 -*- """ - Validate yaml files and check them against their schemas. Designed to be used outside of Vagrant. +Validate yaml files and check them against their schemas. Designed to be used outside of Vagrant. - Just install Yamale: - pip install yamale +Just install Yamale: + pip install yamale """ import argparse @@ -37,9 +37,9 @@ def _validate(schema_path, data_path, parser, strict, _raise_error): def _find_data_path_schema(data_path, schema_name): - """ Starts in the data file folder and recursively looks - in parents for `schema_name` """ - if not data_path or data_path == os.path.abspath(os.sep) or data_path == '.': + """Starts in the data file folder and recursively looks + in parents for `schema_name`""" + if not data_path or data_path == os.path.abspath(os.sep) or data_path == ".": return None directory = os.path.dirname(data_path) path = glob.glob(os.path.join(directory, schema_name)) @@ -49,8 +49,8 @@ def _find_data_path_schema(data_path, schema_name): def _find_schema(data_path, schema_name): - """ Checks if `schema_name` is a valid file, if not - searches in `data_path` for it. """ + """Checks if `schema_name` is a valid file, if not + searches in `data_path` for it.""" if os.path.isfile(schema_name): return schema_name @@ -65,7 +65,7 @@ def _find_schema(data_path, schema_name): def _validate_single(yaml_path, schema_name, parser, strict): - print('Validating %s...' % yaml_path) + print("Validating %s..." % yaml_path) s = _find_schema(yaml_path, schema_name) if not s: raise ValueError("Invalid schema name for '{}' or schema not found.".format(schema_name)) @@ -76,29 +76,26 @@ def _validate_dir(root, schema_name, cpus, parser, strict): pool = Pool(processes=cpus) res = [] error_messages = [] - print('Finding yaml files...') + print("Finding yaml files...") for root, dirs, files in os.walk(root): for f in files: - if (f.endswith('.yaml') or f.endswith('.yml')) and f != schema_name: + if (f.endswith(".yaml") or f.endswith(".yml")) and f != schema_name: d = os.path.join(root, f) s = _find_schema(d, schema_name) if s: - res.append(pool.apply_async(_validate, - (s, d, parser, strict, False))) + res.append(pool.apply_async(_validate, (s, d, parser, strict, False))) else: - print('No schema found for: %s' % d) + print("No schema found for: %s" % d) - print('Found %s yaml files.' % len(res)) - print('Validating...') + print("Found %s yaml files." % len(res)) + print("Validating...") for r in res: sub_results = r.get(timeout=300) - error_messages.extend([str(sub_result) - for sub_result in sub_results - if not sub_result.isValid()]) + error_messages.extend([str(sub_result) for sub_result in sub_results if not sub_result.isValid()]) pool.close() pool.join() if error_messages: - raise ValueError('\n----\n'.join(set(error_messages))) + raise ValueError("\n----\n".join(set(error_messages))) def _router(root, schema_name, cpus, parser, strict=True): @@ -110,29 +107,33 @@ def _router(root, schema_name, cpus, parser, strict=True): def main(): - parser = argparse.ArgumentParser(description='Validate yaml files.') - parser.add_argument('path', metavar='PATH', default='./', nargs='?', - help='folder to validate. Default is current directory.') - parser.add_argument('-V', '--version', action='version', version=__version__) - parser.add_argument('-s', '--schema', default='schema.yaml', - help='filename of schema. Default is schema.yaml.') - parser.add_argument('-n', '--cpu-num', default=4, type=int, - help='number of CPUs to use. Default is 4.') - parser.add_argument('-p', '--parser', default='pyyaml', - help='YAML library to load files. Choices are "ruamel" or "pyyaml" (default).') - parser.add_argument('--no-strict', action='store_true', - help='Disable strict mode, unexpected elements in the data will be accepted.') + parser = argparse.ArgumentParser(description="Validate yaml files.") + parser.add_argument( + "path", metavar="PATH", default="./", nargs="?", help="folder to validate. Default is current directory." + ) + parser.add_argument("-V", "--version", action="version", version=__version__) + parser.add_argument("-s", "--schema", default="schema.yaml", help="filename of schema. Default is schema.yaml.") + parser.add_argument("-n", "--cpu-num", default=4, type=int, help="number of CPUs to use. Default is 4.") + parser.add_argument( + "-p", + "--parser", + default="pyyaml", + help='YAML library to load files. Choices are "ruamel" or "pyyaml" (default).', + ) + parser.add_argument( + "--no-strict", action="store_true", help="Disable strict mode, unexpected elements in the data will be accepted." + ) args = parser.parse_args() try: _router(args.path, args.schema, args.cpu_num, args.parser, not args.no_strict) except (SyntaxError, NameError, TypeError, ValueError) as e: - print('Validation failed!\n%s' % str(e)) + print("Validation failed!\n%s" % str(e)) exit(1) try: - print('Validation success! 👍') + print("Validation success! 👍") except UnicodeEncodeError: - print('Validation success!') + print("Validation success!") -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/yamale/readers/tests/test_bad_file.py b/yamale/readers/tests/test_bad_file.py index 2fc680b..656972b 100644 --- a/yamale/readers/tests/test_bad_file.py +++ b/yamale/readers/tests/test_bad_file.py @@ -2,10 +2,10 @@ from pytest import raises from .. import parse_yaml -parsers = ['pyyaml', 'PyYAML', 'ruamel'] +parsers = ["pyyaml", "PyYAML", "ruamel"] -@pytest.mark.parametrize('parser', parsers) +@pytest.mark.parametrize("parser", parsers) def test_reader_error(parser): with raises(IOError): - parse_yaml('wat', parser) + parse_yaml("wat", parser) diff --git a/yamale/readers/tests/test_yaml.py b/yamale/readers/tests/test_yaml.py index b7f1f81..cafea33 100644 --- a/yamale/readers/tests/test_yaml.py +++ b/yamale/readers/tests/test_yaml.py @@ -3,22 +3,22 @@ from .. import yaml_reader from yamale.tests import get_fixture -parsers = ['pyyaml', 'PyYAML', 'ruamel'] -TYPES = get_fixture('types.yaml') -NESTED = get_fixture('nested.yaml') -KEYWORDS = get_fixture('keywords.yaml') +parsers = ["pyyaml", "PyYAML", "ruamel"] +TYPES = get_fixture("types.yaml") +NESTED = get_fixture("nested.yaml") +KEYWORDS = get_fixture("keywords.yaml") -@pytest.mark.parametrize('parser', parsers) -@pytest.mark.parametrize('use_string', [True, False]) +@pytest.mark.parametrize("parser", parsers) +@pytest.mark.parametrize("use_string", [True, False]) def test_parse(parser, use_string): if use_string: - with io.open(TYPES, encoding='utf-8') as f: + with io.open(TYPES, encoding="utf-8") as f: content = f.read() a = yaml_reader.parse_yaml(parser=parser, content=content)[0] else: a = yaml_reader.parse_yaml(TYPES, parser)[0] - assert a['string'] == 'str()' + assert a["string"] == "str()" def test_parse_validates_arguments(): @@ -28,22 +28,22 @@ def test_parse_validates_arguments(): yaml_reader.parse_yaml(path=None, content=None) -@pytest.mark.parametrize('parser', parsers) +@pytest.mark.parametrize("parser", parsers) def test_types(parser): t = yaml_reader.parse_yaml(TYPES, parser)[0] - assert t['string'] == 'str()' - assert t['number'] == 'num()' - assert t['boolean'] == 'bool()' - assert t['integer'] == 'int()' + assert t["string"] == "str()" + assert t["number"] == "num()" + assert t["boolean"] == "bool()" + assert t["integer"] == "int()" -@pytest.mark.parametrize('parser', parsers) +@pytest.mark.parametrize("parser", parsers) def test_keywords(parser): t = yaml_reader.parse_yaml(KEYWORDS, parser)[0] - assert t['optional_min'] == 'int(min=1, required=False)' + assert t["optional_min"] == "int(min=1, required=False)" -@pytest.mark.parametrize('parser', parsers) +@pytest.mark.parametrize("parser", parsers) def test_nested(parser): t = yaml_reader.parse_yaml(NESTED, parser)[0] - assert t['list'][-1]['string'] == 'str()' + assert t["list"][-1]["string"] == "str()" diff --git a/yamale/readers/yaml_reader.py b/yamale/readers/yaml_reader.py index 59696b3..d160046 100644 --- a/yamale/readers/yaml_reader.py +++ b/yamale/readers/yaml_reader.py @@ -4,6 +4,7 @@ def _pyyaml(f): import yaml + try: Loader = yaml.CSafeLoader except AttributeError: # System does not have libyaml @@ -13,17 +14,15 @@ def _pyyaml(f): def _ruamel(f): from ruamel.yaml import YAML - yaml = YAML(typ='safe') + + yaml = YAML(typ="safe") return list(yaml.load_all(f)) -_parsers = { - 'pyyaml': _pyyaml, - 'ruamel': _ruamel -} +_parsers = {"pyyaml": _pyyaml, "ruamel": _ruamel} -def parse_yaml(path=None, parser='pyyaml', content=None): +def parse_yaml(path=None, parser="pyyaml", content=None): try: parse = _parsers[parser.lower()] except KeyError: diff --git a/yamale/schema/datapath.py b/yamale/schema/datapath.py index 1f6b6d8..6302b6f 100644 --- a/yamale/schema/datapath.py +++ b/yamale/schema/datapath.py @@ -1,5 +1,4 @@ class DataPath(object): - def __init__(self, *path): self._path = path @@ -9,7 +8,7 @@ def __add__(self, other): return dp def __str__(self): - return '.'.join(map(str, (self._path))) + return ".".join(map(str, (self._path))) def __repr__(self): - return 'DataPath({})'.format(repr(self._path)) + return "DataPath({})".format(repr(self._path)) diff --git a/yamale/schema/schema.py b/yamale/schema/schema.py index 8395a94..fe0972c 100644 --- a/yamale/schema/schema.py +++ b/yamale/schema/schema.py @@ -10,21 +10,18 @@ class Schema(object): Still acts like a dict. """ - def __init__(self, schema_dict, name='', validators=None, includes=None): + def __init__(self, schema_dict, name="", validators=None, includes=None): self.validators = validators or val.DefaultValidators self.dict = schema_dict self.name = name - self._schema = self._process_schema(DataPath(), - schema_dict, - self.validators) + self._schema = self._process_schema(DataPath(), schema_dict, self.validators) # if this schema is included it shares the includes with the top level # schema self.includes = {} if includes is None else includes def add_include(self, type_dict): for include_name, custom_type in type_dict.items(): - t = Schema(custom_type, name=include_name, - validators=self.validators, includes=self.includes) + t = Schema(custom_type, name=include_name, validators=self.validators, includes=self.includes) self.includes[include_name] = t def _process_schema(self, path, schema_data, validators): @@ -33,13 +30,9 @@ def _process_schema(self, path, schema_data, validators): """ if util.is_map(schema_data) or util.is_list(schema_data): for key, data in util.get_iter(schema_data): - schema_data[key] = self._process_schema(path + DataPath(key), - data, - validators) + schema_data[key] = self._process_schema(path + DataPath(key), data, validators) else: - schema_data = self._parse_schema_item(path, - schema_data, - validators) + schema_data = self._parse_schema_item(path, schema_data, validators) return schema_data def _parse_schema_item(self, path, expression, validators): @@ -47,7 +40,7 @@ def _parse_schema_item(self, path, expression, validators): return syntax.parse(expression, validators) except SyntaxError as e: # Tack on some more context and rethrow. - error = str(e) + ' at node \'%s\'' % str(path) + error = str(e) + " at node '%s'" % str(path) raise SyntaxError(error) def validate(self, data, data_name, strict): @@ -70,7 +63,7 @@ def _validate_item(self, validator, data, path, strict, key): if isinstance(validator, val.Validator) and validator.is_optional: return errors # SHUT DOWN EVERYTHING - errors.append('%s: Required field missing' % path) + errors.append("%s: Required field missing" % path) return errors return self._validate(validator, data_item, path, strict) @@ -84,16 +77,11 @@ def _validate(self, validator, data, path, strict): """ if util.is_list(validator) or util.is_map(validator): - return self._validate_static_map_list(validator, - data, - path, - strict) + return self._validate_static_map_list(validator, data, path, strict) errors = [] # Optional field with optional value? Who cares. - if (data is None and - validator.is_optional and - validator.can_be_none): + if data is None and validator.is_optional and validator.can_be_none: return errors errors += self._validate_primitive(validator, data, path) @@ -129,14 +117,10 @@ def _validate_static_map_list(self, validator, data, path, strict): validator_keys = set(util.get_keys(validator)) for key in data_keys - validator_keys: error_path = path + DataPath(key) - errors += ['%s: Unexpected element' % error_path] + errors += ["%s: Unexpected element" % error_path] for key, sub_validator in util.get_iter(validator): - errors += self._validate_item(sub_validator, - data, - path, - strict, - key) + errors += self._validate_item(sub_validator, data, path, strict, key) return errors def _validate_map_list(self, validator, data, path, strict): @@ -162,13 +146,9 @@ def _validate_map_list(self, validator, data, path, strict): def _validate_include(self, validator, data, path, strict): include_schema = self.includes.get(validator.include_name) if not include_schema: - return [('Include \'%s\' has not been defined.' - % validator.include_name)] + return [("Include '%s' has not been defined." % validator.include_name)] strict = strict if validator.strict is None else validator.strict - return include_schema._validate(include_schema._schema, - data, - path, - strict) + return include_schema._validate(include_schema._schema, data, path, strict) def _validate_any(self, validator, data, path, strict): if not validator.validators: @@ -219,6 +199,6 @@ def _validate_primitive(self, validator, data, path): errors = validator.validate(data) for i, error in enumerate(errors): - errors[i] = ('%s: ' % path) + error + errors[i] = ("%s: " % path) + error return errors diff --git a/yamale/schema/validationresults.py b/yamale/schema/validationresults.py index c1ab3ed..03a3b0d 100644 --- a/yamale/schema/validationresults.py +++ b/yamale/schema/validationresults.py @@ -3,7 +3,7 @@ def __init__(self, errors): self.errors = errors def __str__(self): - return '\n'.join(self.errors) + return "\n".join(self.errors) def isValid(self): return len(self.errors) == 0 @@ -24,7 +24,7 @@ def __str__(self): head_line_bits.append("'{}'".format(self.data)) if self.schema: head_line_bits.append("with schema '{}'".format(self.schema)) - head_line = ' '.join(head_line_bits) - head_line += '\n\t' - error_str = head_line + '\n\t'.join(self.errors) + head_line = " ".join(head_line_bits) + head_line += "\n\t" + error_str = head_line + "\n\t".join(self.errors) return error_str diff --git a/yamale/syntax/parser.py b/yamale/syntax/parser.py index 8b89231..ae71ef9 100644 --- a/yamale/syntax/parser.py +++ b/yamale/syntax/parser.py @@ -2,7 +2,7 @@ from .. import validators as val -safe_globals = ('True', 'False', 'None') +safe_globals = ("True", "False", "None") safe_builtins = dict((f, __builtins__[f]) for f in safe_globals) @@ -11,9 +11,9 @@ def _validate_expr(call_node, validators): try: func_name = call_node.func.id except AttributeError: - raise SyntaxError('Schema expressions must be enclosed by a validator.') + raise SyntaxError("Schema expressions must be enclosed by a validator.") if func_name not in validators: - raise SyntaxError('Not a registered validator: \'%s\'. ' % func_name) + raise SyntaxError("Not a registered validator: '%s'. " % func_name) # Validate that all args are constant literals, validator names, or other call nodes arg_values = call_node.args + [kw.value for kw in call_node.keywords] for arg in arg_values: @@ -25,22 +25,15 @@ def _validate_expr(call_node, validators): elif isinstance(base_arg, ast.Call): _validate_expr(base_arg, validators) else: - raise SyntaxError( - 'Argument values must either be constant literals, or else ' - 'reference other validators.') + raise SyntaxError("Argument values must either be constant literals, or else " "reference other validators.") def parse(validator_string, validators=None): validators = validators or val.DefaultValidators try: - tree = ast.parse(validator_string, mode='eval') + tree = ast.parse(validator_string, mode="eval") _validate_expr(tree.body, validators) # evaluate with access to a limited global scope only - return eval(compile(tree, '', 'eval'), - {'__builtins__': safe_builtins}, - validators) + return eval(compile(tree, "", "eval"), {"__builtins__": safe_builtins}, validators) except (SyntaxError, NameError, TypeError) as e: - raise SyntaxError( - 'Invalid schema expression: \'%s\'. ' % validator_string + - str(e) - ) + raise SyntaxError("Invalid schema expression: '%s'. " % validator_string + str(e)) diff --git a/yamale/syntax/tests/test_parser.py b/yamale/syntax/tests/test_parser.py index 2cce518..405e9af 100644 --- a/yamale/syntax/tests/test_parser.py +++ b/yamale/syntax/tests/test_parser.py @@ -2,41 +2,50 @@ from .. import parser as par from yamale.validators.validators import ( - Validator, String, Regex, Number, Integer, Boolean, List, Day, Timestamp, - Ip, Mac) + Validator, + String, + Regex, + Number, + Integer, + Boolean, + List, + Day, + Timestamp, + Ip, + Mac, +) def test_eval(): - assert eval('String()') == String() + assert eval("String()") == String() def test_types(): - assert par.parse('String()') == String() - assert par.parse('str()') == String() - assert par.parse('regex()') == Regex() - assert par.parse('num()') == Number() - assert par.parse('int()') == Integer() - assert par.parse('day()') == Day() - assert par.parse('timestamp()') == Timestamp() - assert par.parse('bool()') == Boolean() - assert par.parse('list(str())') == List(String()) - assert par.parse('ip()') == Ip() - assert par.parse('mac()') == Mac() + assert par.parse("String()") == String() + assert par.parse("str()") == String() + assert par.parse("regex()") == Regex() + assert par.parse("num()") == Number() + assert par.parse("int()") == Integer() + assert par.parse("day()") == Day() + assert par.parse("timestamp()") == Timestamp() + assert par.parse("bool()") == Boolean() + assert par.parse("list(str())") == List(String()) + assert par.parse("ip()") == Ip() + assert par.parse("mac()") == Mac() def test_custom_type(): - class my_validator(Validator): pass - assert par.parse('custom()', {'custom': my_validator}) == my_validator() + assert par.parse("custom()", {"custom": my_validator}) == my_validator() def test_required(): - assert par.parse('str(required=True)').is_required - assert par.parse('str(required=False)').is_optional + assert par.parse("str(required=True)").is_required + assert par.parse("str(required=False)").is_optional def test_syntax_error(): with raises(SyntaxError): - par.parse('eval()') + par.parse("eval()") diff --git a/yamale/tests/__init__.py b/yamale/tests/__init__.py index 84c2bec..3c16abb 100644 --- a/yamale/tests/__init__.py +++ b/yamale/tests/__init__.py @@ -3,4 +3,4 @@ def get_fixture(relative): script_dir = os.path.dirname(__file__) - return os.path.join(script_dir, 'fixtures/', relative) + return os.path.join(script_dir, "fixtures/", relative) diff --git a/yamale/tests/test_command_line.py b/yamale/tests/test_command_line.py index eeb0bf8..675cb13 100644 --- a/yamale/tests/test_command_line.py +++ b/yamale/tests/test_command_line.py @@ -8,108 +8,128 @@ dir_path = os.path.dirname(os.path.realpath(__file__)) -parsers = ['pyyaml', 'PyYAML', 'ruamel'] +parsers = ["pyyaml", "PyYAML", "ruamel"] @contextlib.contextmanager def scoped_chandge_dir(new_dir): cwd = os.getcwd() os.chdir(new_dir) - try: yield - finally: os.chdir(cwd) + try: + yield + finally: + os.chdir(cwd) -@pytest.mark.parametrize('parser', parsers) +@pytest.mark.parametrize("parser", parsers) def test_bad_yaml(parser): with pytest.raises(ValueError) as e: command_line._router( - 'yamale/tests/command_line_fixtures/yamls/bad.yaml', - 'schema.yaml', 1, parser) + "yamale/tests/command_line_fixtures/yamls/bad.yaml", + "schema.yaml", + 1, + parser, + ) assert "map.bad: '12.5' is not a str." in e.value.message -@pytest.mark.parametrize('parser', parsers) +@pytest.mark.parametrize("parser", parsers) def test_required_keys_yaml(parser): with pytest.raises(ValueError) as e: command_line._router( - 'yamale/tests/command_line_fixtures/yamls/required_keys_bad.yaml', - 'required_keys_schema.yaml', 1, parser) + "yamale/tests/command_line_fixtures/yamls/required_keys_bad.yaml", + "required_keys_schema.yaml", + 1, + parser, + ) assert "map.key: Required field missing" in e.value.message -@pytest.mark.parametrize('parser', parsers) +@pytest.mark.parametrize("parser", parsers) def test_good_yaml(parser): - command_line._router( - 'yamale/tests/command_line_fixtures/yamls/good.yaml', - 'schema.yaml', 1, parser) + command_line._router("yamale/tests/command_line_fixtures/yamls/good.yaml", "schema.yaml", 1, parser) -@pytest.mark.parametrize('parser', parsers) +@pytest.mark.parametrize("parser", parsers) def test_good_relative_yaml(parser): command_line._router( - 'yamale/tests/command_line_fixtures/yamls/good.yaml', - '../schema_dir/external.yaml', 1, parser) + "yamale/tests/command_line_fixtures/yamls/good.yaml", + "../schema_dir/external.yaml", + 1, + parser, + ) -@pytest.mark.parametrize('parser', parsers) +@pytest.mark.parametrize("parser", parsers) def test_good_relative_schema_in_subfolder(parser): - with scoped_chandge_dir('yamale/tests/command_line_fixtures/schema_dir'): - command_line._router( - '../yamls/good.yaml', - 'external.yaml', 1, parser) + with scoped_chandge_dir("yamale/tests/command_line_fixtures/schema_dir"): + command_line._router("../yamls/good.yaml", "external.yaml", 1, parser) -@pytest.mark.parametrize('parser', parsers) +@pytest.mark.parametrize("parser", parsers) def test_external_glob_schema(parser): command_line._router( - 'yamale/tests/command_line_fixtures/yamls/good.yaml', - os.path.join(dir_path, 'command_line_fixtures/schema_dir/ex*.yaml'), 1, parser) + "yamale/tests/command_line_fixtures/yamls/good.yaml", + os.path.join(dir_path, "command_line_fixtures/schema_dir/ex*.yaml"), + 1, + parser, + ) def test_empty_schema_file(): - with pytest.raises(ValueError, match='is an empty file!'): + with pytest.raises(ValueError, match="is an empty file!"): command_line._router( - 'yamale/tests/command_line_fixtures/empty_schema/data.yaml', - 'empty_schema.yaml' , 1, 'PyYAML') + "yamale/tests/command_line_fixtures/empty_schema/data.yaml", + "empty_schema.yaml", + 1, + "PyYAML", + ) def test_external_schema(): command_line._router( - 'yamale/tests/command_line_fixtures/yamls/good.yaml', - os.path.join(dir_path, 'command_line_fixtures/schema_dir/external.yaml'), 1, 'PyYAML') + "yamale/tests/command_line_fixtures/yamls/good.yaml", + os.path.join(dir_path, "command_line_fixtures/schema_dir/external.yaml"), + 1, + "PyYAML", + ) def test_bad_dir(): with pytest.raises(ValueError): - command_line._router( - 'yamale/tests/command_line_fixtures/yamls', - 'schema.yaml', 4, 'PyYAML') + command_line._router("yamale/tests/command_line_fixtures/yamls", "schema.yaml", 4, "PyYAML") def test_bad_strict(): with pytest.raises(ValueError) as e: command_line._router( - 'yamale/tests/command_line_fixtures/yamls/required_keys_extra_element.yaml', - 'required_keys_schema.yaml', - 4, 'PyYAML', strict=True) + "yamale/tests/command_line_fixtures/yamls/required_keys_extra_element.yaml", + "required_keys_schema.yaml", + 4, + "PyYAML", + strict=True, + ) assert "map.key2: Unexpected element" in e.value.message def test_bad_issue_54(): with pytest.raises(yamale_error.YamaleError) as e: command_line._router( - 'yamale/tests/fixtures/nested_issue_54.yaml', - 'nested.yaml', - 4, 'PyYAML', strict=True) - assert 'string: Required field missing' in e.value.message - assert 'number: Required field missing' in e.value.message - assert 'integer: Required field missing' in e.value.message - assert 'boolean: Required field missing' in e.value.message - assert 'date: Required field missing' in e.value.message - assert 'datetime: Required field missing' in e.value.message - assert 'nest: Required field missing' in e.value.message - assert 'list: Required field missing' in e.value.message + "yamale/tests/fixtures/nested_issue_54.yaml", + "nested.yaml", + 4, + "PyYAML", + strict=True, + ) + assert "string: Required field missing" in e.value.message + assert "number: Required field missing" in e.value.message + assert "integer: Required field missing" in e.value.message + assert "boolean: Required field missing" in e.value.message + assert "date: Required field missing" in e.value.message + assert "datetime: Required field missing" in e.value.message + assert "nest: Required field missing" in e.value.message + assert "list: Required field missing" in e.value.message -def test_nested_schema_issue_69(): - command_line._router('yamale/tests/command_line_fixtures/nestedYaml','schema.yaml', 1, 'PyYAML') +def test_nested_schema_issue_69(): + command_line._router("yamale/tests/command_line_fixtures/nestedYaml", "schema.yaml", 1, "PyYAML") diff --git a/yamale/tests/test_functional.py b/yamale/tests/test_functional.py index c35bcae..97c8fd1 100644 --- a/yamale/tests/test_functional.py +++ b/yamale/tests/test_functional.py @@ -6,287 +6,218 @@ from . import get_fixture from .. import validators as val -types = { - 'schema': 'types.yaml', - 'bad': 'types_bad_data.yaml', - 'good': 'types_good_data.yaml' -} +types = {"schema": "types.yaml", "bad": "types_bad_data.yaml", "good": "types_good_data.yaml"} -nested = { - 'schema': 'nested.yaml', - 'bad': 'nested_bad_data.yaml', - 'good': 'nested_good_data.yaml' -} +nested = {"schema": "nested.yaml", "bad": "nested_bad_data.yaml", "good": "nested_good_data.yaml"} -custom = { - 'schema': 'custom_types.yaml', - 'bad': 'custom_types_bad.yaml', - 'good': 'custom_types_good.yaml' -} +custom = {"schema": "custom_types.yaml", "bad": "custom_types_bad.yaml", "good": "custom_types_good.yaml"} -keywords = { - 'schema': 'keywords.yaml', - 'bad': 'keywords_bad.yaml', - 'good': 'keywords_good.yaml' -} +keywords = {"schema": "keywords.yaml", "bad": "keywords_bad.yaml", "good": "keywords_good.yaml"} -lists = { - 'schema': 'lists.yaml', - 'bad': 'lists_bad.yaml', - 'bad2': 'lists_bad2.yaml', - 'good': 'lists_good.yaml' -} +lists = {"schema": "lists.yaml", "bad": "lists_bad.yaml", "bad2": "lists_bad2.yaml", "good": "lists_good.yaml"} -maps = { - 'schema': 'map.yaml', - 'bad': 'map_bad.yaml', - 'bad2': 'map_bad2.yaml', - 'good': 'map_good.yaml' -} +maps = {"schema": "map.yaml", "bad": "map_bad.yaml", "bad2": "map_bad2.yaml", "good": "map_good.yaml"} -anys = { - 'schema': 'any.yaml', - 'bad': 'any_bad.yaml', - 'good': 'any_good.yaml' -} +anys = {"schema": "any.yaml", "bad": "any_bad.yaml", "good": "any_good.yaml"} -list_include = { - 'schema': 'list_include.yaml', - 'good': 'list_include_good.yaml' -} +list_include = {"schema": "list_include.yaml", "good": "list_include_good.yaml"} -issue_22 = { - 'schema': 'issue_22.yaml', - 'good': 'issue_22_good.yaml' -} +issue_22 = {"schema": "issue_22.yaml", "good": "issue_22_good.yaml"} -issue_50 = { - 'schema': 'issue_50.yaml', - 'good': 'issue_50_good.yaml' -} +issue_50 = {"schema": "issue_50.yaml", "good": "issue_50_good.yaml"} -regexes = { - 'schema': 'regex.yaml', - 'bad': 'regex_bad.yaml', - 'good': 'regex_good.yaml' -} +regexes = {"schema": "regex.yaml", "bad": "regex_bad.yaml", "good": "regex_good.yaml"} -ips = { - 'schema': 'ip.yaml', - 'bad': 'ip_bad.yaml', - 'good': 'ip_good.yaml' -} +ips = {"schema": "ip.yaml", "bad": "ip_bad.yaml", "good": "ip_good.yaml"} -macs = { - 'schema': 'mac.yaml', - 'bad': 'mac_bad.yaml', - 'good': 'mac_good.yaml' -} +macs = {"schema": "mac.yaml", "bad": "mac_bad.yaml", "good": "mac_good.yaml"} -nested_map = { - 'schema': 'nested_map.yaml', - 'good': 'nested_map_good.yaml' -} +nested_map = {"schema": "nested_map.yaml", "good": "nested_map_good.yaml"} -top_level_map = { - 'schema': 'top_level_map.yaml', - 'good': 'top_level_map_good.yaml' -} +top_level_map = {"schema": "top_level_map.yaml", "good": "top_level_map_good.yaml"} include_validator = { - 'schema': 'include_validator.yaml', - 'good': 'include_validator_good.yaml', - 'bad': 'include_validator_bad.yaml' + "schema": "include_validator.yaml", + "good": "include_validator_good.yaml", + "bad": "include_validator_bad.yaml", } -strict_map = { - 'schema': 'strict_map.yaml', - 'good': 'strict_map_good.yaml', - 'bad': 'strict_map_bad.yaml' -} +strict_map = {"schema": "strict_map.yaml", "good": "strict_map_good.yaml", "bad": "strict_map_bad.yaml"} mixed_strict_map = { - 'schema': 'mixed_strict_map.yaml', - 'good': 'mixed_strict_map_good.yaml', - 'bad': 'mixed_strict_map_bad.yaml' + "schema": "mixed_strict_map.yaml", + "good": "mixed_strict_map_good.yaml", + "bad": "mixed_strict_map_bad.yaml", } -strict_list = { - 'schema': 'strict_list.yaml', - 'good': 'strict_list_good.yaml', - 'bad': 'strict_list_bad.yaml' -} +strict_list = {"schema": "strict_list.yaml", "good": "strict_list_good.yaml", "bad": "strict_list_bad.yaml"} -nested_map2 = { - 'schema': 'nested_map2.yaml', - 'good': 'nested_map2_good.yaml', - 'bad': 'nested_map2_bad.yaml' -} +nested_map2 = {"schema": "nested_map2.yaml", "good": "nested_map2_good.yaml", "bad": "nested_map2_bad.yaml"} -static_list = { - 'schema': 'static_list.yaml', - 'good': 'static_list_good.yaml', - 'bad': 'static_list_bad.yaml' -} +static_list = {"schema": "static_list.yaml", "good": "static_list_good.yaml", "bad": "static_list_bad.yaml"} -nested_issue_54 = { - 'schema': 'nested.yaml', - 'bad': 'nested_issue_54.yaml', - 'good': 'nested_good_data.yaml' -} +nested_issue_54 = {"schema": "nested.yaml", "bad": "nested_issue_54.yaml", "good": "nested_good_data.yaml"} map_key_constraint = { - 'schema': 'map_key_constraint.yaml', - 'good': 'map_key_constraint_good.yaml', - 'bad_base': 'map_key_constraint_bad_base.yaml', - 'bad_nest': 'map_key_constraint_bad_nest.yaml', - 'bad_nest_con': 'map_key_constraint_bad_nest_con.yaml', + "schema": "map_key_constraint.yaml", + "good": "map_key_constraint_good.yaml", + "bad_base": "map_key_constraint_bad_base.yaml", + "bad_nest": "map_key_constraint_bad_nest.yaml", + "bad_nest_con": "map_key_constraint_bad_nest_con.yaml", } numeric_bool_coercion = { - 'schema': 'numeric_bool_coercion.yaml', - 'good': 'numeric_bool_coercion_good.yaml', - 'bad': 'numeric_bool_coercion_bad.yaml', + "schema": "numeric_bool_coercion.yaml", + "good": "numeric_bool_coercion_good.yaml", + "bad": "numeric_bool_coercion_bad.yaml", } subset = { - 'schema': 'subset.yaml', - 'good': 'subset_good.yaml', - 'good2': 'subset_good2.yaml', - 'bad': 'subset_bad.yaml', - 'bad2': 'subset_bad2.yaml', - 'bad3': 'subset_bad3.yaml' + "schema": "subset.yaml", + "good": "subset_good.yaml", + "good2": "subset_good2.yaml", + "bad": "subset_bad.yaml", + "bad2": "subset_bad2.yaml", + "bad3": "subset_bad3.yaml", } -subset_empty = { - 'schema': 'subset_empty.yaml', - 'good': 'subset_empty_good.yaml', - 'good2': 'subset_empty_good2.yaml' -} +subset_empty = {"schema": "subset_empty.yaml", "good": "subset_empty_good.yaml", "good2": "subset_empty_good2.yaml"} -subset_nodef = { - 'schema': 'subset_nodef.yaml' -} +subset_nodef = {"schema": "subset_nodef.yaml"} test_data = [ - types, nested, custom, - keywords, lists, maps, - anys, list_include, issue_22, - issue_50, regexes, ips, macs, - nested_map, top_level_map, - include_validator, strict_map, - mixed_strict_map, strict_list, - nested_map2, static_list, + types, + nested, + custom, + keywords, + lists, + maps, + anys, + list_include, + issue_22, + issue_50, + regexes, + ips, + macs, + nested_map, + top_level_map, + include_validator, + strict_map, + mixed_strict_map, + strict_list, + nested_map2, + static_list, nested_issue_54, map_key_constraint, numeric_bool_coercion, - subset, subset_empty + subset, + subset_empty, ] for d in test_data: for key in d.keys(): - if key == 'schema': + if key == "schema": d[key] = yamale.make_schema(get_fixture(d[key])) else: d[key] = yamale.make_data(get_fixture(d[key])) def test_tests(): - """ Make sure the test runner is working.""" + """Make sure the test runner is working.""" assert 1 + 1 == 2 def test_flat_make_schema(): - assert isinstance(types['schema']._schema['string'], val.String) + assert isinstance(types["schema"]._schema["string"], val.String) def test_nested_schema(): - nested_schema = nested['schema']._schema - assert isinstance(nested_schema['string'], val.String) - assert isinstance(nested_schema['list'], (list, tuple)) - assert isinstance(nested_schema['list'][0], val.String) + nested_schema = nested["schema"]._schema + assert isinstance(nested_schema["string"], val.String) + assert isinstance(nested_schema["list"], (list, tuple)) + assert isinstance(nested_schema["list"][0], val.String) -@pytest.mark.parametrize('data_map', test_data) +@pytest.mark.parametrize("data_map", test_data) def test_good(data_map): for k, v in data_map.items(): - if k.startswith('good'): - yamale.validate(data_map['schema'], data_map[k]) + if k.startswith("good"): + yamale.validate(data_map["schema"], data_map[k]) def test_bad_validate(): - assert count_exception_lines(types['schema'], types['bad']) == 9 + assert count_exception_lines(types["schema"], types["bad"]) == 9 def test_bad_nested(): - assert count_exception_lines(nested['schema'], nested['bad']) == 2 + assert count_exception_lines(nested["schema"], nested["bad"]) == 2 def test_bad_nested_issue_54(): exp = [ - 'string: Required field missing', - 'number: Required field missing', - 'integer: Required field missing', - 'boolean: Required field missing', - 'date: Required field missing', - 'datetime: Required field missing', - 'nest: Required field missing', - 'list: Required field missing' + "string: Required field missing", + "number: Required field missing", + "integer: Required field missing", + "boolean: Required field missing", + "date: Required field missing", + "datetime: Required field missing", + "nest: Required field missing", + "list: Required field missing", ] - match_exception_lines(nested_issue_54['schema'], nested_issue_54['bad'], exp) + match_exception_lines(nested_issue_54["schema"], nested_issue_54["bad"], exp) + def test_bad_custom(): - assert count_exception_lines(custom['schema'], custom['bad']) == 1 + assert count_exception_lines(custom["schema"], custom["bad"]) == 1 def test_bad_lists(): - assert count_exception_lines(lists['schema'], lists['bad']) == 6 + assert count_exception_lines(lists["schema"], lists["bad"]) == 6 def test_bad2_lists(): - assert count_exception_lines(lists['schema'], lists['bad2']) == 2 + assert count_exception_lines(lists["schema"], lists["bad2"]) == 2 def test_bad_maps(): - assert count_exception_lines(maps['schema'], maps['bad']) == 7 + assert count_exception_lines(maps["schema"], maps["bad"]) == 7 + def test_bad_maps2(): - assert count_exception_lines(maps['schema'], maps['bad2']) == 1 + assert count_exception_lines(maps["schema"], maps["bad2"]) == 1 + def test_bad_keywords(): - assert count_exception_lines(keywords['schema'], keywords['bad']) == 9 + assert count_exception_lines(keywords["schema"], keywords["bad"]) == 9 def test_bad_anys(): - assert count_exception_lines(anys['schema'], anys['bad']) == 5 + assert count_exception_lines(anys["schema"], anys["bad"]) == 5 def test_bad_regexes(): - assert count_exception_lines(regexes['schema'], regexes['bad']) == 4 + assert count_exception_lines(regexes["schema"], regexes["bad"]) == 4 def test_bad_include_validator(): exp = ["key1: 'a_string' is not a int."] - match_exception_lines(include_validator['schema'], - include_validator['bad'], - exp) + match_exception_lines(include_validator["schema"], include_validator["bad"], exp) def test_bad_schema(): with pytest.raises(SyntaxError) as excinfo: - yamale.make_schema(get_fixture('bad_schema.yaml')) - assert 'fixtures/bad_schema.yaml' in str(excinfo.value) + yamale.make_schema(get_fixture("bad_schema.yaml")) + assert "fixtures/bad_schema.yaml" in str(excinfo.value) def test_empty_schema(): with pytest.raises(ValueError) as excinfo: - yamale.make_schema(get_fixture('empty_schema.yaml')) - assert 'empty_schema.yaml is an empty file!' in str(excinfo.value) + yamale.make_schema(get_fixture("empty_schema.yaml")) + assert "empty_schema.yaml is an empty file!" in str(excinfo.value) @pytest.mark.parametrize( - "schema_filename", - ['bad_schema_rce.yaml', 'bad_schema_rce2.yaml', 'bad_schema_rce3.yaml', 'bad_schema_rce4.yaml'] + "schema_filename", ["bad_schema_rce.yaml", "bad_schema_rce2.yaml", "bad_schema_rce3.yaml", "bad_schema_rce4.yaml"] ) def test_vulnerable_schema(schema_filename): with pytest.raises(SyntaxError) as excinfo: @@ -296,60 +227,42 @@ def test_vulnerable_schema(schema_filename): def test_list_is_not_a_map(): exp = [" : '[1, 2]' is not a map"] - match_exception_lines(strict_map['schema'], - strict_list['good'], - exp) + match_exception_lines(strict_map["schema"], strict_list["good"], exp) def test_bad_strict_map(): - exp = ['extra: Unexpected element'] - match_exception_lines(strict_map['schema'], - strict_map['bad'], - exp, - strict=True) + exp = ["extra: Unexpected element"] + match_exception_lines(strict_map["schema"], strict_map["bad"], exp, strict=True) def test_bad_strict_list(): - exp = ['2: Unexpected element'] - match_exception_lines(strict_list['schema'], - strict_list['bad'], - exp, - strict=True) + exp = ["2: Unexpected element"] + match_exception_lines(strict_list["schema"], strict_list["bad"], exp, strict=True) def test_bad_mixed_strict_map(): - exp = ['field3.extra: Unexpected element'] - match_exception_lines(mixed_strict_map['schema'], - mixed_strict_map['bad'], - exp) + exp = ["field3.extra: Unexpected element"] + match_exception_lines(mixed_strict_map["schema"], mixed_strict_map["bad"], exp) def test_bad_nested_map2(): - exp = ['field1.field1_1: Required field missing'] - match_exception_lines(nested_map2['schema'], - nested_map2['bad'], - exp) + exp = ["field1.field1_1: Required field missing"] + match_exception_lines(nested_map2["schema"], nested_map2["bad"], exp) def test_bad_static_list(): - exp = ['0: Required field missing'] - match_exception_lines(static_list['schema'], - static_list['bad'], - exp) + exp = ["0: Required field missing"] + match_exception_lines(static_list["schema"], static_list["bad"], exp) def test_bad_map_key_constraint_base(): exp = [": Key error - 'bad' is not a int."] - match_exception_lines(map_key_constraint['schema'], - map_key_constraint['bad_base'], - exp) + match_exception_lines(map_key_constraint["schema"], map_key_constraint["bad_base"], exp) def test_bad_map_key_constraint_nest(): exp = ["1.0: Key error - '100' is not a str."] - match_exception_lines(map_key_constraint['schema'], - map_key_constraint['bad_nest'], - exp) + match_exception_lines(map_key_constraint["schema"], map_key_constraint["bad_nest"], exp) def test_bad_map_key_constraint_nest_con(): @@ -357,9 +270,7 @@ def test_bad_map_key_constraint_nest_con(): "1.0: Key error - '100' is not a str.", "1.0: Key error - 'baz' contains excluded character 'z'", ] - match_exception_lines(map_key_constraint['schema'], - map_key_constraint['bad_nest_con'], - exp) + match_exception_lines(map_key_constraint["schema"], map_key_constraint["bad_nest_con"], exp) def test_bad_numeric_bool_coercion(): @@ -369,67 +280,58 @@ def test_bad_numeric_bool_coercion(): "numbers.0: 'False' is not a num.", "numbers.1: 'True' is not a num.", ] - match_exception_lines(numeric_bool_coercion['schema'], - numeric_bool_coercion['bad'], - exp) + match_exception_lines(numeric_bool_coercion["schema"], numeric_bool_coercion["bad"], exp) + def test_bad_subset(): - exp = [ - "subset_list: 'subset' may not be an empty set." - ] - match_exception_lines(subset['schema'], - subset['bad'], - exp) + exp = ["subset_list: 'subset' may not be an empty set."] + match_exception_lines(subset["schema"], subset["bad"], exp) + def test_bad_subset2(): - exp = [ - "subset_list: '[1]' is not a int.", - "subset_list: '[1]' is not a str." - ] - match_exception_lines(subset['schema'], - subset['bad2'], - exp) + exp = ["subset_list: '[1]' is not a int.", "subset_list: '[1]' is not a str."] + match_exception_lines(subset["schema"], subset["bad2"], exp) + def test_bad_subset3(): - exp = [ - "subset_list: '{'a': 1}' is not a int.", - "subset_list: '{'a': 1}' is not a str." - ] - match_exception_lines(subset['schema'], - subset['bad3'], - exp) + exp = ["subset_list: '{'a': 1}' is not a int.", "subset_list: '{'a': 1}' is not a str."] + match_exception_lines(subset["schema"], subset["bad3"], exp) + def test_nodef_subset_schema(): with pytest.raises(ValueError) as e: - yamale.make_schema(get_fixture(subset_nodef['schema'])) + yamale.make_schema(get_fixture(subset_nodef["schema"])) assert "'subset' requires at least one validator!" in str(e.value) -@pytest.mark.parametrize("use_schema_string,use_data_string,expected_message_re", [ - (False, False, "^Error validating data '.*?' with schema '.*?'\n\t"), - (True, False, "^Error validating data '.*?'\n\t"), - (False, True, "^Error validating data with schema '.*?'\n\t"), - (True, True, "^Error validating data\n\t"), -]) + +@pytest.mark.parametrize( + "use_schema_string,use_data_string,expected_message_re", + [ + (False, False, "^Error validating data '.*?' with schema '.*?'\n\t"), + (True, False, "^Error validating data '.*?'\n\t"), + (False, True, "^Error validating data with schema '.*?'\n\t"), + (True, True, "^Error validating data\n\t"), + ], +) def test_validate_errors(use_schema_string, use_data_string, expected_message_re): - schema_path = get_fixture('types.yaml') - data_path = get_fixture('types_bad_data.yaml') + schema_path = get_fixture("types.yaml") + data_path = get_fixture("types_bad_data.yaml") if use_schema_string: - with io.open(schema_path, encoding='utf-8') as f: + with io.open(schema_path, encoding="utf-8") as f: schema = yamale.make_schema(content=f.read()) else: schema = yamale.make_schema(schema_path) if use_data_string: - with io.open(data_path, encoding='utf-8') as f: + with io.open(data_path, encoding="utf-8") as f: data = yamale.make_data(content=f.read()) else: data = yamale.make_data(data_path) with pytest.raises(yamale.yamale_error.YamaleError) as excinfo: yamale.validate(schema, data) - assert re.match(expected_message_re, excinfo.value.message, re.MULTILINE), \ - 'Message {} should match {}'.format( - excinfo.value.message, expected_message_re - ) + assert re.match(expected_message_re, excinfo.value.message, re.MULTILINE), "Message {} should match {}".format( + excinfo.value.message, expected_message_re + ) def match_exception_lines(schema, data, expected, strict=False): diff --git a/yamale/tests/test_meta_test.py b/yamale/tests/test_meta_test.py index 5759a82..93ab5e2 100644 --- a/yamale/tests/test_meta_test.py +++ b/yamale/tests/test_meta_test.py @@ -9,8 +9,8 @@ class TestAllYaml(YamaleTestCase): base_dir = data_folder - schema = 'meta_test_fixtures/schema.yaml' - yaml = 'meta_test_fixtures/data1.yaml' + schema = "meta_test_fixtures/schema.yaml" + yaml = "meta_test_fixtures/data1.yaml" def runTest(self): self.assertTrue(self.validate()) @@ -18,8 +18,8 @@ def runTest(self): class TestBadYaml(YamaleTestCase): base_dir = data_folder - schema = 'meta_test_fixtures/schema_bad.yaml' - yaml = 'meta_test_fixtures/data*.yaml' + schema = "meta_test_fixtures/schema_bad.yaml" + yaml = "meta_test_fixtures/data*.yaml" def runTest(self): self.assertRaises(ValueError, self.validate) @@ -27,11 +27,13 @@ def runTest(self): class TestMapYaml(YamaleTestCase): base_dir = data_folder - schema = 'meta_test_fixtures/schema.yaml' - yaml = ['meta_test_fixtures/data1.yaml', - 'meta_test_fixtures/some_data.yaml', - # Make sure schema doesn't validate itself - 'meta_test_fixtures/schema.yaml'] + schema = "meta_test_fixtures/schema.yaml" + yaml = [ + "meta_test_fixtures/data1.yaml", + "meta_test_fixtures/some_data.yaml", + # Make sure schema doesn't validate itself + "meta_test_fixtures/schema.yaml", + ] def runTest(self): self.assertTrue(self.validate()) @@ -47,9 +49,10 @@ def runTest(self): class Card(Validator): - """ Custom validator for testing purpose """ - tag = 'card' - card_regex = re.compile(r'^(10|[2-9JQKA])[SHDC]$') + """Custom validator for testing purpose""" + + tag = "card" + card_regex = re.compile(r"^(10|[2-9JQKA])[SHDC]$") def _is_valid(self, value): return re.match(self.card_regex, value) @@ -57,30 +60,30 @@ def _is_valid(self, value): class TestCustomValidator(YamaleTestCase): base_dir = data_folder - schema = 'meta_test_fixtures/schema_custom.yaml' - yaml = 'meta_test_fixtures/data_custom.yaml' + schema = "meta_test_fixtures/schema_custom.yaml" + yaml = "meta_test_fixtures/data_custom.yaml" def runTest(self): validators = DefaultValidators.copy() - validators['card'] = Card + validators["card"] = Card self.assertTrue(self.validate(validators)) class TestCustomValidatorWithIncludes(YamaleTestCase): base_dir = data_folder - schema = 'meta_test_fixtures/schema_custom_with_include.yaml' - yaml = 'meta_test_fixtures/data_custom_with_include.yaml' + schema = "meta_test_fixtures/schema_custom_with_include.yaml" + yaml = "meta_test_fixtures/data_custom_with_include.yaml" def runTest(self): validators = DefaultValidators.copy() - validators['card'] = Card + validators["card"] = Card self.assertTrue(self.validate(validators)) class TestBadRequiredYaml(YamaleTestCase): base_dir = data_folder - schema = 'meta_test_fixtures/schema_required_bad.yaml' - yaml = 'meta_test_fixtures/data_required_bad.yaml' + schema = "meta_test_fixtures/schema_required_bad.yaml" + yaml = "meta_test_fixtures/data_required_bad.yaml" def runTest(self): self.assertRaises(ValueError, self.validate) @@ -88,8 +91,8 @@ def runTest(self): class TestGoodRequiredYaml(YamaleTestCase): base_dir = data_folder - schema = 'meta_test_fixtures/schema_required_good.yaml' - yaml = 'meta_test_fixtures/data_required_good.yaml' + schema = "meta_test_fixtures/schema_required_good.yaml" + yaml = "meta_test_fixtures/data_required_good.yaml" def runTest(self): self.assertTrue(self.validate()) diff --git a/yamale/util.py b/yamale/util.py index e450f2b..45c4ab3 100644 --- a/yamale/util.py +++ b/yamale/util.py @@ -16,6 +16,7 @@ def to_unicode(s): return unicode(s) except NameError: + def isstr(s): return isinstance(s, str) @@ -63,7 +64,7 @@ def get_subclasses(cls, _subclasses_yielded=None): _subclasses_yielded = set() # If the passed class is old- rather than new-style, raise an exception. - if not hasattr(cls, '__subclasses__'): + if not hasattr(cls, "__subclasses__"): raise TypeError('Old-style class "%s" unsupported.' % cls.__name__) # For each direct subclass of this class diff --git a/yamale/validators/base.py b/yamale/validators/base.py index 343843b..c36ec7d 100644 --- a/yamale/validators/base.py +++ b/yamale/validators/base.py @@ -1,5 +1,6 @@ class Validator(object): """Base class for all Validators""" + constraints = [] value_type = None @@ -8,15 +9,13 @@ def __init__(self, *args, **kwargs): self.kwargs = kwargs # Is field required? Default is True - self.is_required = bool(kwargs.pop('required', True)) + self.is_required = bool(kwargs.pop("required", True)) # Can value be None if field is optional? Default is True - self._value_can_be_none = bool(kwargs.pop('none', True)) + self._value_can_be_none = bool(kwargs.pop("none", True)) # Construct all constraints - self._constraints_inst = self._create_constraints(self.constraints, - self.value_type, - kwargs) + self._constraints_inst = self._create_constraints(self.constraints, self.value_type, kwargs) def _create_constraints(self, constraint_classes, value_type, kwargs): constraints = [] @@ -39,7 +38,7 @@ def can_be_none(self): def _is_valid(self, value): """Validators must implement this. Return True if value is valid.""" - raise NotImplementedError('You need to override this function') + raise NotImplementedError("You need to override this function") def get_name(self): return self.tag @@ -74,14 +73,12 @@ def is_valid(self, value): def fail(self, value): """Override to define a custom fail message""" - return '\'%s\' is not a %s.' % (value, self.get_name()) + return "'%s' is not a %s." % (value, self.get_name()) def __repr__(self): - return '%s(%s, %s)' % (self.__class__.__name__, self.args, self.kwargs) + return "%s(%s, %s)" % (self.__class__.__name__, self.args, self.kwargs) def __eq__(self, other): # Validators are equal if they have the same args and kwargs. - eq = [isinstance(other, self.__class__), - self.args == other.args, - self.kwargs == other.kwargs] + eq = [isinstance(other, self.__class__), self.args == other.args, self.kwargs == other.kwargs] return all(eq) diff --git a/yamale/validators/constraints.py b/yamale/validators/constraints.py index 3065a49..72575a3 100644 --- a/yamale/validators/constraints.py +++ b/yamale/validators/constraints.py @@ -36,15 +36,15 @@ def get_kwarg(self, kwargs, key, kwtype): try: # Try to convert value # Is this value one of the datetime types? if kwtype == datetime.date: - time = datetime.datetime.strptime(value, '%Y-%m-%d') + time = datetime.datetime.strptime(value, "%Y-%m-%d") return datetime.date(time.year, time.month, time.day) if kwtype == datetime.datetime: - return datetime.datetime.strptime(value, '%Y-%m-%d %H:%M:%S') + return datetime.datetime.strptime(value, "%Y-%m-%d %H:%M:%S") return kwtype(value) except (TypeError, ValueError): - raise SyntaxError('%s is not a %s' % (key, kwtype)) + raise SyntaxError("%s is not a %s" % (key, kwtype)) def is_valid(self, value): if not self.is_active: @@ -56,14 +56,14 @@ def is_valid(self, value): return None def _fail(self, value): - return '\'%s\' violates %s.' % (value, self.__class__.__name__) + return "'%s' violates %s." % (value, self.__class__.__name__) class Min(Constraint): - fail = '%s is less than %s' + fail = "%s is less than %s" def __init__(self, value_type, kwargs): - self.keywords = {'min': value_type} + self.keywords = {"min": value_type} super(Min, self).__init__(value_type, kwargs) def _is_valid(self, value): @@ -74,10 +74,10 @@ def _fail(self, value): class Max(Constraint): - fail = '%s is greater than %s' + fail = "%s is greater than %s" def __init__(self, value_type, kwargs): - self.keywords = {'max': value_type} + self.keywords = {"max": value_type} super(Max, self).__init__(value_type, kwargs) def _is_valid(self, value): @@ -88,8 +88,8 @@ def _fail(self, value): class LengthMin(Constraint): - keywords = {'min': int} - fail = 'Length of %s is less than %s' + keywords = {"min": int} + fail = "Length of %s is less than %s" def _is_valid(self, value): return self.min <= len(value) @@ -99,8 +99,8 @@ def _fail(self, value): class LengthMax(Constraint): - keywords = {'max': int} - fail = 'Length of %s is greater than %s' + keywords = {"max": int} + fail = "Length of %s is greater than %s" def _is_valid(self, value): return self.max >= len(value) @@ -110,8 +110,8 @@ def _fail(self, value): class Key(Constraint): - keywords = {'key': Validator} - fail = 'Key error - %s' + keywords = {"key": Validator} + fail = "Key error - %s" def _is_valid(self, value): for k in value.keys(): @@ -127,8 +127,8 @@ def _fail(self, value): class StringEquals(Constraint): - keywords = {'equals': str, 'ignore_case': bool} - fail = '%s does not equal %s' + keywords = {"equals": str, "ignore_case": bool} + fail = "%s does not equal %s" def _is_valid(self, value): # Check if the function has only been called due to ignore_case @@ -148,8 +148,8 @@ def _fail(self, value): class StringStartsWith(Constraint): - keywords = {'starts_with': str, 'ignore_case': bool} - fail = '%s does not start with %s' + keywords = {"starts_with": str, "ignore_case": bool} + fail = "%s does not start with %s" def _is_valid(self, value): # Check if the function has only been called due to ignore_case @@ -173,8 +173,8 @@ def _fail(self, value): class StringEndsWith(Constraint): - keywords = {'ends_with': str, 'ignore_case': bool} - fail = '%s does not end with %s' + keywords = {"ends_with": str, "ignore_case": bool} + fail = "%s does not end with %s" def _is_valid(self, value): # Check if the function has only been called due to ignore_case @@ -198,10 +198,10 @@ def _fail(self, value): class StringMatches(Constraint): - keywords = {'matches': str} - fail = '%s is not a regex match.' + keywords = {"matches": str} + fail = "%s is not a regex match." - _regex_flags = {'ignore_case': re.I, 'multiline': re.M, 'dotall': re.S} + _regex_flags = {"ignore_case": re.I, "multiline": re.M, "dotall": re.S} def __init__(self, value_type, kwargs): self._flags = 0 @@ -222,8 +222,8 @@ def _fail(self, value): class CharacterExclude(Constraint): - keywords = {'exclude': str, 'ignore_case': bool} - fail = '\'%s\' contains excluded character \'%s\'' + keywords = {"exclude": str, "ignore_case": bool} + fail = "'%s' contains excluded character '%s'" def _is_valid(self, value): # Check if the function has only been called due to ignore_case @@ -251,8 +251,8 @@ def _fail(self, value): class IpVersion(Constraint): - keywords = {'version': int} - fail = 'IP version of %s is not %s' + keywords = {"version": int} + fail = "IP version of %s is not %s" def _is_valid(self, value): try: diff --git a/yamale/validators/tests/test_constraint.py b/yamale/validators/tests/test_constraint.py index f5984de..9cb775e 100644 --- a/yamale/validators/tests/test_constraint.py +++ b/yamale/validators/tests/test_constraint.py @@ -4,23 +4,23 @@ def test_length_min(): v = val.String(min=2) - assert v.is_valid('abcd') - assert v.is_valid('ab') - assert not v.is_valid('a') + assert v.is_valid("abcd") + assert v.is_valid("ab") + assert not v.is_valid("a") def test_length_max(): v = val.String(max=3) - assert v.is_valid('abc') - assert v.is_valid('ab') - assert not v.is_valid('abcd') + assert v.is_valid("abc") + assert v.is_valid("ab") + assert not v.is_valid("abcd") def test_number_max(): - v = val.Number(min=.5) + v = val.Number(min=0.5) assert v.is_valid(4) - assert v.is_valid(.5) - assert not v.is_valid(.1) + assert v.is_valid(0.5) + assert not v.is_valid(0.1) def test_number_min(): @@ -59,100 +59,101 @@ def test_day_max(): def test_str_equals(): - v = val.String(equals='abcd') - assert v.is_valid('abcd') - assert not v.is_valid('abcde') - assert not v.is_valid('c') + v = val.String(equals="abcd") + assert v.is_valid("abcd") + assert not v.is_valid("abcde") + assert not v.is_valid("c") def test_str_equals_ignore_case(): - v = val.String(equals='abcd', ignore_case=True) - assert v.is_valid('abCd') - assert not v.is_valid('abcde') - assert not v.is_valid('C') + v = val.String(equals="abcd", ignore_case=True) + assert v.is_valid("abCd") + assert not v.is_valid("abcde") + assert not v.is_valid("C") def test_str_starts_with(): - v = val.String(starts_with='abc') - assert v.is_valid('abcd') - assert not v.is_valid('bcd') - assert not v.is_valid('c') + v = val.String(starts_with="abc") + assert v.is_valid("abcd") + assert not v.is_valid("bcd") + assert not v.is_valid("c") def test_str_starts_with_ignore_case(): - v = val.String(starts_with='abC', ignore_case=True) - assert v.is_valid('abCde') - assert v.is_valid('abcde') - assert not v.is_valid('bcd') - assert not v.is_valid('C') + v = val.String(starts_with="abC", ignore_case=True) + assert v.is_valid("abCde") + assert v.is_valid("abcde") + assert not v.is_valid("bcd") + assert not v.is_valid("C") def test_str_ends_with(): - v = val.String(ends_with='abcd') - assert v.is_valid('abcd') - assert not v.is_valid('abcde') - assert not v.is_valid('c') + v = val.String(ends_with="abcd") + assert v.is_valid("abcd") + assert not v.is_valid("abcde") + assert not v.is_valid("c") def test_str_ends_with_ignore_case(): - v = val.String(ends_with='abC', ignore_case=True) - assert v.is_valid('xyzabC') - assert v.is_valid('xyzabc') - assert not v.is_valid('cde') - assert not v.is_valid('C') + v = val.String(ends_with="abC", ignore_case=True) + assert v.is_valid("xyzabC") + assert v.is_valid("xyzabc") + assert not v.is_valid("cde") + assert not v.is_valid("C") def test_str_matches(): - v = val.String(matches=r'^(abc)\1?de$') - assert v.is_valid('abcabcde') - assert not v.is_valid('abcabcabcde') - assert not v.is_valid('\12') + v = val.String(matches=r"^(abc)\1?de$") + assert v.is_valid("abcabcde") + assert not v.is_valid("abcabcabcde") + assert not v.is_valid("\12") - v = val.String(matches=r'[a-z0-9]{3,}s\s$', ignore_case=True) - assert v.is_valid('b33S\v') - assert v.is_valid('B33s\t') - assert not v.is_valid(' b33s ') - assert not v.is_valid('b33s ') + v = val.String(matches=r"[a-z0-9]{3,}s\s$", ignore_case=True) + assert v.is_valid("b33S\v") + assert v.is_valid("B33s\t") + assert not v.is_valid(" b33s ") + assert not v.is_valid("b33s ") - v = val.String(matches=r'A.+\d$', ignore_case=False, multiline=True) - assert v.is_valid('A_-3\n\n') - assert not v.is_valid('a!!!!!5\n\n') + v = val.String(matches=r"A.+\d$", ignore_case=False, multiline=True) + assert v.is_valid("A_-3\n\n") + assert not v.is_valid("a!!!!!5\n\n") - v = val.String(matches=r'.*^Ye.*s\.', ignore_case=True, multiline=True, dotall=True) - assert v.is_valid('YEeeEEEEeeeeS.') - assert v.is_valid('What?\nYes!\nBEES.\nOK.') - assert not v.is_valid('YES-TA-TOES?') - assert not v.is_valid('\n\nYaes.') + v = val.String(matches=r".*^Ye.*s\.", ignore_case=True, multiline=True, dotall=True) + assert v.is_valid("YEeeEEEEeeeeS.") + assert v.is_valid("What?\nYes!\nBEES.\nOK.") + assert not v.is_valid("YES-TA-TOES?") + assert not v.is_valid("\n\nYaes.") def test_char_exclude(): - v = val.String(exclude='abcd') - assert v.is_valid('efg') - assert not v.is_valid('abc') - assert not v.is_valid('c') + v = val.String(exclude="abcd") + assert v.is_valid("efg") + assert not v.is_valid("abc") + assert not v.is_valid("c") def test_char_exclude_igonre_case(): - v = val.String(exclude='abcd', ignore_case=True) - assert v.is_valid('efg') - assert v.is_valid('Efg') - assert not v.is_valid('abc') - assert not v.is_valid('Def') - assert not v.is_valid('c') + v = val.String(exclude="abcd", ignore_case=True) + assert v.is_valid("efg") + assert v.is_valid("Efg") + assert not v.is_valid("abc") + assert not v.is_valid("Def") + assert not v.is_valid("c") def test_ip4(): v = val.Ip(version=4) - assert v.is_valid('192.168.1.1') - assert v.is_valid('192.168.1.255') - assert v.is_valid('192.168.3.1/24') - assert not v.is_valid('2001:db8::') - assert not v.is_valid('2001:db8::/64') + assert v.is_valid("192.168.1.1") + assert v.is_valid("192.168.1.255") + assert v.is_valid("192.168.3.1/24") + assert not v.is_valid("2001:db8::") + assert not v.is_valid("2001:db8::/64") + def test_ip6(): v = val.Ip(version=6) - assert not v.is_valid('192.168.1.1') - assert not v.is_valid('192.168.1.255') - assert not v.is_valid('192.168.3.1/24') - assert v.is_valid('2001:db8::') - assert v.is_valid('2001:db8::/64') + assert not v.is_valid("192.168.1.1") + assert not v.is_valid("192.168.1.255") + assert not v.is_valid("192.168.3.1/24") + assert v.is_valid("2001:db8::") + assert v.is_valid("2001:db8::/64") diff --git a/yamale/validators/tests/test_validate.py b/yamale/validators/tests/test_validate.py index efb8fd9..a3370fe 100644 --- a/yamale/validators/tests/test_validate.py +++ b/yamale/validators/tests/test_validate.py @@ -12,61 +12,61 @@ def test_validator_defaults(): def test_equality(): assert val.String() == val.String() - assert val.String(hello='wat') == val.String(hello='wat') - assert val.String(hello='wat') != val.String(hello='nope') - assert val.Boolean('yep') != val.Boolean('nope') + assert val.String(hello="wat") == val.String(hello="wat") + assert val.String(hello="wat") != val.String(hello="nope") + assert val.Boolean("yep") != val.Boolean("nope") def test_integer(): v = val.Integer() assert v.is_valid(1) - assert not v.is_valid('1') + assert not v.is_valid("1") assert not v.is_valid(1.34) def test_string(): v = val.String() - assert v.is_valid('1') + assert v.is_valid("1") assert not v.is_valid(1) def test_regex(): - v = val.Regex(r'^(abc)\1?de$', name='test regex') - assert v.is_valid('abcabcde') - assert not v.is_valid('abcabcabcde') - assert not v.is_valid('\12') - assert v.fail('woopz') == '\'woopz\' is not a test regex.' - - v = val.Regex(r'[a-z0-9]{3,}s\s$', ignore_case=True) - assert v.is_valid('b33S\v') - assert v.is_valid('B33s\t') - assert not v.is_valid(' b33s ') - assert not v.is_valid('b33s ') - assert v.fail('fdsa') == '\'fdsa\' is not a regex match.' - - v = val.Regex(r'A.+\d$', ignore_case=False, multiline=True) - assert v.is_valid('A_-3\n\n') - assert not v.is_valid('a!!!!!5\n\n') - - v = val.Regex(r'.*^Ye.*s\.', ignore_case=True, multiline=True, dotall=True) - assert v.is_valid('YEeeEEEEeeeeS.') - assert v.is_valid('What?\nYes!\nBEES.\nOK.') - assert not v.is_valid('YES-TA-TOES?') - assert not v.is_valid('\n\nYaes.') + v = val.Regex(r"^(abc)\1?de$", name="test regex") + assert v.is_valid("abcabcde") + assert not v.is_valid("abcabcabcde") + assert not v.is_valid("\12") + assert v.fail("woopz") == "'woopz' is not a test regex." + + v = val.Regex(r"[a-z0-9]{3,}s\s$", ignore_case=True) + assert v.is_valid("b33S\v") + assert v.is_valid("B33s\t") + assert not v.is_valid(" b33s ") + assert not v.is_valid("b33s ") + assert v.fail("fdsa") == "'fdsa' is not a regex match." + + v = val.Regex(r"A.+\d$", ignore_case=False, multiline=True) + assert v.is_valid("A_-3\n\n") + assert not v.is_valid("a!!!!!5\n\n") + + v = val.Regex(r".*^Ye.*s\.", ignore_case=True, multiline=True, dotall=True) + assert v.is_valid("YEeeEEEEeeeeS.") + assert v.is_valid("What?\nYes!\nBEES.\nOK.") + assert not v.is_valid("YES-TA-TOES?") + assert not v.is_valid("\n\nYaes.") def test_number(): v = val.Number() assert v.is_valid(1) assert v.is_valid(1.3425235) - assert not v.is_valid('str') + assert not v.is_valid("str") def test_boolean(): v = val.Boolean() assert v.is_valid(True) assert v.is_valid(False) - assert not v.is_valid('') + assert not v.is_valid("") assert not v.is_valid(0) @@ -74,7 +74,7 @@ def test_date(): v = val.Day() assert v.is_valid(date(2015, 1, 1)) assert v.is_valid(datetime(2015, 1, 1, 1)) - assert not v.is_valid('') + assert not v.is_valid("") assert not v.is_valid(0) @@ -82,7 +82,7 @@ def test_datetime(): v = val.Timestamp() assert v.is_valid(datetime(2015, 1, 1, 1)) assert not v.is_valid(date(2015, 1, 1)) - assert not v.is_valid('') + assert not v.is_valid("") assert not v.is_valid(0) @@ -90,55 +90,57 @@ def test_list(): v = val.List() assert v.is_valid([]) assert v.is_valid(()) - assert not v.is_valid('') + assert not v.is_valid("") assert not v.is_valid(0) def test_null(): v = val.Null() assert v.is_valid(None) - assert not v.is_valid('None') + assert not v.is_valid("None") assert not v.is_valid(0) - assert not v.is_valid(float('nan')) + assert not v.is_valid(float("nan")) assert not v.is_valid({}) + def test_ip(): v = val.Ip() - assert v.is_valid('192.168.1.1') - assert v.is_valid('192.168.1.255') - assert v.is_valid('192.168.3.1/24') - assert v.is_valid('2001:db8::') - assert v.is_valid('2001:db8::/64') - assert not v.is_valid('257.192.168.1') - assert not v.is_valid('192.168.1.256') - assert not v.is_valid('2001:db8::/129') - assert not v.is_valid('2001:dg8::/127') - assert not v.is_valid('asdf') + assert v.is_valid("192.168.1.1") + assert v.is_valid("192.168.1.255") + assert v.is_valid("192.168.3.1/24") + assert v.is_valid("2001:db8::") + assert v.is_valid("2001:db8::/64") + assert not v.is_valid("257.192.168.1") + assert not v.is_valid("192.168.1.256") + assert not v.is_valid("2001:db8::/129") + assert not v.is_valid("2001:dg8::/127") + assert not v.is_valid("asdf") + def test_mac(): v = val.Mac() - assert v.is_valid('12:34:56:78:90:ab') - assert v.is_valid('1234:5678:90ab') - assert v.is_valid('12-34-56-78-90-ab') - assert v.is_valid('1234-5678-90ab') - - assert v.is_valid('12:34:56:78:90:AB') - assert v.is_valid('1234:5678:90AB') - assert v.is_valid('12-34-56-78-90-AB') - assert v.is_valid('1234-5678-90AB') - - assert v.is_valid('ab:cd:ef:12:34:56') - assert v.is_valid('abcd:ef12:3456') - assert v.is_valid('ab-cd-ef-12-34-56') - assert v.is_valid('abcd-ef12-3456') - - assert v.is_valid('AB:CD:EF:12:34:56') - assert v.is_valid('ABCD:EF12:3456') - assert v.is_valid('AB-CD-EF-12-34-56') - assert v.is_valid('ABCD-EF12-3456') - - assert not v.is_valid('qwertyuiop') - assert not v.is_valid('qw-er-ty-12-34-56') - assert not v.is_valid('ab:cd:ef:12:34:56:78') - assert not v.is_valid('abcdefghijkl') - assert not v.is_valid('1234567890ax') + assert v.is_valid("12:34:56:78:90:ab") + assert v.is_valid("1234:5678:90ab") + assert v.is_valid("12-34-56-78-90-ab") + assert v.is_valid("1234-5678-90ab") + + assert v.is_valid("12:34:56:78:90:AB") + assert v.is_valid("1234:5678:90AB") + assert v.is_valid("12-34-56-78-90-AB") + assert v.is_valid("1234-5678-90AB") + + assert v.is_valid("ab:cd:ef:12:34:56") + assert v.is_valid("abcd:ef12:3456") + assert v.is_valid("ab-cd-ef-12-34-56") + assert v.is_valid("abcd-ef12-3456") + + assert v.is_valid("AB:CD:EF:12:34:56") + assert v.is_valid("ABCD:EF12:3456") + assert v.is_valid("AB-CD-EF-12-34-56") + assert v.is_valid("ABCD-EF12-3456") + + assert not v.is_valid("qwertyuiop") + assert not v.is_valid("qw-er-ty-12-34-56") + assert not v.is_valid("ab:cd:ef:12:34:56:78") + assert not v.is_valid("abcdefghijkl") + assert not v.is_valid("1234567890ax") diff --git a/yamale/validators/validators.py b/yamale/validators/validators.py index e5fc4e4..d72478e 100644 --- a/yamale/validators/validators.py +++ b/yamale/validators/validators.py @@ -14,14 +14,17 @@ class String(Validator): """String validator""" - tag = 'str' - constraints = [con.LengthMin, - con.LengthMax, - con.CharacterExclude, - con.StringEquals, - con.StringStartsWith, - con.StringEndsWith, - con.StringMatches] + + tag = "str" + constraints = [ + con.LengthMin, + con.LengthMax, + con.CharacterExclude, + con.StringEquals, + con.StringStartsWith, + con.StringEndsWith, + con.StringMatches, + ] def _is_valid(self, value): return util.isstr(value) @@ -29,8 +32,9 @@ def _is_valid(self, value): class Number(Validator): """Number/float validator""" + value_type = float - tag = 'num' + tag = "num" constraints = [con.Min, con.Max] def _is_valid(self, value): @@ -39,8 +43,9 @@ def _is_valid(self, value): class Integer(Validator): """Integer validator""" + value_type = int - tag = 'int' + tag = "int" constraints = [con.Min, con.Max] def _is_valid(self, value): @@ -49,7 +54,8 @@ def _is_valid(self, value): class Boolean(Validator): """Boolean validator""" - tag = 'bool' + + tag = "bool" def _is_valid(self, value): return isinstance(value, bool) @@ -57,7 +63,8 @@ def _is_valid(self, value): class Enum(Validator): """Enum validator""" - tag = 'enum' + + tag = "enum" def __init__(self, *args, **kwargs): super(Enum, self).__init__(*args, **kwargs) @@ -67,13 +74,14 @@ def _is_valid(self, value): return value in self.enums def fail(self, value): - return '\'%s\' not in %s' % (value, self.enums) + return "'%s' not in %s" % (value, self.enums) class Day(Validator): """Day validator. Format: YYYY-MM-DD""" + value_type = date - tag = 'day' + tag = "day" constraints = [con.Min, con.Max] def _is_valid(self, value): @@ -82,8 +90,9 @@ def _is_valid(self, value): class Timestamp(Validator): """Timestamp validator. Format: YYYY-MM-DD HH:MM:SS""" + value_type = datetime - tag = 'timestamp' + tag = "timestamp" constraints = [con.Min, con.Max] def _is_valid(self, value): @@ -92,7 +101,8 @@ def _is_valid(self, value): class Map(Validator): """Map and dict validator""" - tag = 'map' + + tag = "map" constraints = [con.LengthMin, con.LengthMax, con.Key] def __init__(self, *args, **kwargs): @@ -105,7 +115,8 @@ def _is_valid(self, value): class List(Validator): """List validator""" - tag = 'list' + + tag = "list" constraints = [con.LengthMin, con.LengthMax] def __init__(self, *args, **kwargs): @@ -118,11 +129,12 @@ def _is_valid(self, value): class Include(Validator): """Include validator""" - tag = 'include' + + tag = "include" def __init__(self, *args, **kwargs): self.include_name = args[0] - self.strict = kwargs.pop('strict', None) + self.strict = kwargs.pop("strict", None) super(Include, self).__init__(*args, **kwargs) def _is_valid(self, value): @@ -134,7 +146,8 @@ def get_name(self): class Any(Validator): """Any of several types validator""" - tag = 'any' + + tag = "any" def __init__(self, *args, **kwargs): self.validators = [val for val in args if isinstance(val, Validator)] @@ -143,23 +156,25 @@ def __init__(self, *args, **kwargs): def _is_valid(self, value): return True + class Subset(Validator): """Subset of several types validator""" - tag = 'subset' + + tag = "subset" def __init__(self, *args, **kwargs): super(Subset, self).__init__(*args, **kwargs) - self._allow_empty_set = bool(kwargs.pop('allow_empty', False)) + self._allow_empty_set = bool(kwargs.pop("allow_empty", False)) self.validators = [val for val in args if isinstance(val, Validator)] if not self.validators: - raise ValueError('\'%s\' requires at least one validator!' % self.tag) + raise ValueError("'%s' requires at least one validator!" % self.tag) def _is_valid(self, value): return self.can_be_none or value is not None def fail(self, value): # Called in case `_is_valid` returns False - return '\'%s\' may not be an empty set.' % self.get_name() + return "'%s' may not be an empty set." % self.get_name() @property def is_optional(self): @@ -172,8 +187,9 @@ def can_be_none(self): class Null(Validator): """Validates null""" + value_type = None - tag = 'null' + tag = "null" def _is_valid(self, value): return value is None @@ -181,18 +197,18 @@ def _is_valid(self, value): class Regex(Validator): """Regular expression validator""" - tag = 'regex' - _regex_flags = {'ignore_case': re.I, 'multiline': re.M, 'dotall': re.S} + + tag = "regex" + _regex_flags = {"ignore_case": re.I, "multiline": re.M, "dotall": re.S} def __init__(self, *args, **kwargs): - self.regex_name = kwargs.pop('name', None) + self.regex_name = kwargs.pop("name", None) flags = 0 for k, v in util.get_iter(self._regex_flags): flags |= v if kwargs.pop(k, False) else 0 - self.regexes = [re.compile(arg, flags) - for arg in args if util.isstr(arg)] + self.regexes = [re.compile(arg, flags) for arg in args if util.isstr(arg)] super(Regex, self).__init__(*args, **kwargs) def _is_valid(self, value): @@ -204,7 +220,8 @@ def get_name(self): class Ip(Validator): """IP address validator""" - tag = 'ip' + + tag = "ip" constraints = [con.IpVersion] def _is_valid(self, value): @@ -220,15 +237,14 @@ def ip_address(self, value): class Mac(Regex): """MAC address validator""" - tag = 'mac' + + tag = "mac" def __init__(self, *args, **kwargs): super(Mac, self).__init__(*args, **kwargs) self.regexes = [ - re.compile( - "[0-9a-fA-F]{2}([-:]?)[0-9a-fA-F]{2}(\\1[0-9a-fA-F]{2}){4}$"), - re.compile( - "[0-9a-fA-F]{4}([-:]?)[0-9a-fA-F]{4}(\\1[0-9a-fA-F]{4})$"), + re.compile("[0-9a-fA-F]{2}([-:]?)[0-9a-fA-F]{2}(\\1[0-9a-fA-F]{2}){4}$"), + re.compile("[0-9a-fA-F]{4}([-:]?)[0-9a-fA-F]{4}(\\1[0-9a-fA-F]{4})$"), ] diff --git a/yamale/version.py b/yamale/version.py index 2a969a6..33d70e5 100644 --- a/yamale/version.py +++ b/yamale/version.py @@ -1,7 +1,7 @@ import os.path -FNAME = 'VERSION' +FNAME = "VERSION" root = os.path.dirname(os.path.abspath(__file__)) -with open(os.path.join(root, FNAME), 'r', encoding='utf-8') as f: +with open(os.path.join(root, FNAME), "r", encoding="utf-8") as f: __version__ = f.read().strip() diff --git a/yamale/yamale.py b/yamale/yamale.py index f563585..5a3b9bf 100644 --- a/yamale/yamale.py +++ b/yamale/yamale.py @@ -3,13 +3,14 @@ from .yamale_error import YamaleError -def make_schema(path=None, parser='PyYAML', validators=None, content=None): +def make_schema(path=None, parser="PyYAML", validators=None, content=None): # validators = None means use default. # Import readers here so we can get version information in setup.py. from . import readers + raw_schemas = readers.parse_yaml(path, parser, content=content) if not raw_schemas: - raise ValueError('{} is an empty file!'.format(path)) + raise ValueError("{} is an empty file!".format(path)) # First document is the base schema try: s = Schema(raw_schemas[0], path, validators=validators) @@ -17,15 +18,16 @@ def make_schema(path=None, parser='PyYAML', validators=None, content=None): for raw_schema in raw_schemas[1:]: s.add_include(raw_schema) except (TypeError, SyntaxError) as e: - error = 'Schema error in file %s\n' % path + error = "Schema error in file %s\n" % path error += str(e) raise SyntaxError(error) return s -def make_data(path=None, parser='PyYAML', content=None): +def make_data(path=None, parser="PyYAML", content=None): from . import readers + raw_data = readers.parse_yaml(path, parser, content=content) if len(raw_data) == 0: return [({}, path)] diff --git a/yamale/yamale_error.py b/yamale/yamale_error.py index 3d56043..0359633 100644 --- a/yamale/yamale_error.py +++ b/yamale/yamale_error.py @@ -1,5 +1,5 @@ class YamaleError(ValueError): def __init__(self, results): - super(YamaleError, self).__init__('\n'.join([str(x) for x in list(filter(lambda x: not x.isValid(), results))])) + super(YamaleError, self).__init__("\n".join([str(x) for x in list(filter(lambda x: not x.isValid(), results))])) self.message = self.args[0] self.results = results diff --git a/yamale/yamale_testcase.py b/yamale/yamale_testcase.py index c749c67..21ba6d7 100644 --- a/yamale/yamale_testcase.py +++ b/yamale/yamale_testcase.py @@ -8,7 +8,7 @@ class YamaleTestCase(TestCase): - """ TestCase for easily validating YAML in your own tests. + """TestCase for easily validating YAML in your own tests. `schema`: String of path to the schema file to use. One schema file per test case. `yaml`: String or list of yaml files to validate. Accepts globs. `base_dir`: String path to prepend to all other paths. This is optional. @@ -46,4 +46,3 @@ def validate(self, validators=None): if not result.isValid(): raise ValueError(result) return True - From 93599170d14aa0958175da07fe47d843cad05ab4 Mon Sep 17 00:00:00 2001 From: Cordell Blakkan Date: Tue, 19 Mar 2024 15:13:53 -0400 Subject: [PATCH 2/4] Add ruff commit to git-blame-ignore-revs --- .git-blame-ignore-revs | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .git-blame-ignore-revs diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000..3188dff --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,2 @@ +# Run ruff on codebase +9fc3c943c87fc876da6e0a75f621e15a2201cbdd From e314521e8bc4d418aa079946d33a2664391adab7 Mon Sep 17 00:00:00 2001 From: Cordell Blakkan Date: Tue, 19 Mar 2024 15:16:58 -0400 Subject: [PATCH 3/4] Prepare for 5.1.0 release --- yamale/VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yamale/VERSION b/yamale/VERSION index 0062ac9..831446c 100644 --- a/yamale/VERSION +++ b/yamale/VERSION @@ -1 +1 @@ -5.0.0 +5.1.0 From 06547bff1050ec0a7c0c4a3157147c325cef7bf6 Mon Sep 17 00:00:00 2001 From: Cordell Blakkan Date: Tue, 19 Mar 2024 22:04:27 -0400 Subject: [PATCH 4/4] Fix function name typo --- yamale/tests/test_command_line.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yamale/tests/test_command_line.py b/yamale/tests/test_command_line.py index 675cb13..f728445 100644 --- a/yamale/tests/test_command_line.py +++ b/yamale/tests/test_command_line.py @@ -12,7 +12,7 @@ @contextlib.contextmanager -def scoped_chandge_dir(new_dir): +def scoped_change_dir(new_dir): cwd = os.getcwd() os.chdir(new_dir) try: @@ -62,7 +62,7 @@ def test_good_relative_yaml(parser): @pytest.mark.parametrize("parser", parsers) def test_good_relative_schema_in_subfolder(parser): - with scoped_chandge_dir("yamale/tests/command_line_fixtures/schema_dir"): + with scoped_change_dir("yamale/tests/command_line_fixtures/schema_dir"): command_line._router("../yamls/good.yaml", "external.yaml", 1, parser)