Giter Club home page Giter Club logo

yamale's Introduction

Yamale (ya·ma·lē)

⚠️ Ensure that your schema definitions come from internal or trusted sources. Yamale does not protect against intentionally malicious schemas.

Yamale

A schema and validator for YAML.

What's YAML? See the current spec here and an introduction to the syntax here.

Build Status PyPI

Requirements

  • Python 3.8+
  • PyYAML
  • ruamel.yaml (optional)

Install

pip

$ pip install yamale

NOTE: Some platforms, e.g., Mac OS, may ship with only Python 2 and may not have pip installed. Installation of Python 3 should also install pip. To preserve any system dependencies on default software, consider installing Python 3 as a local package. Please note replacing system-provided Python may disrupt other software. Mac OS users may wish to investigate MacPorts, homebrew, or building Python 3 from source; in all three cases, Apple's Command Line Tools (CLT) for Xcode may be required. See also developers, below.

Manual

  1. Download Yamale from: https://github.com/23andMe/Yamale/archive/master.zip
  2. Unzip somewhere temporary
  3. Run python setup.py install (may have to prepend sudo)

Usage

Command line

Yamale can be run from the command line to validate one or many YAML files. Yamale will search the directory you supply (current directory is default) for YAML files. Each YAML file it finds it will look in the same directory as that file for its schema, if there is no schema Yamale will keep looking up the directory tree until it finds one. If Yamale can not find a schema it will tell you.

Usage:

usage: yamale [-h] [-s SCHEMA] [-n CPU_NUM] [-p PARSER] [--no-strict] [PATH]

Validate yaml files.

positional arguments:
  PATH                  folder to validate. Default is current directory.

optional arguments:
  -h, --help            show this help message and exit
  -s SCHEMA, --schema SCHEMA
                        filename of schema. Default is schema.yaml.
  -n CPU_NUM, --cpu-num CPU_NUM
                        number of CPUs to use. Default is 4.
  -p PARSER, --parser PARSER
                        YAML library to load files. Choices are "ruamel" or
                        "pyyaml" (default).
  --no-strict           Disable strict mode, unexpected elements in the data
                        will be accepted.

API

There are several ways to feed Yamale schema and data files. The simplest way is to let Yamale take care of reading and parsing your YAML files.

All you need to do is supply the files' path:

# Import Yamale and make a schema object:
import yamale
schema = yamale.make_schema('./schema.yaml')

# Create a Data object
data = yamale.make_data('./data.yaml')

# Validate data against the schema. Throws a ValueError if data is invalid.
yamale.validate(schema, data)

You can pass a string of YAML to make_schema() and make_data() instead of passing a file path by using the content= parameter:

data = yamale.make_data(content="""
name: Bill
age: 26
height: 6.2
awesome: True
""")

If data is valid, nothing will happen. However, if data is invalid Yamale will throw a YamaleError with a message containing all the invalid nodes:

try:
    yamale.validate(schema, data)
    print('Validation success! 👍')
except ValueError as e:
    print('Validation failed!\n%s' % str(e))
    exit(1)

and an array of ValidationResult.

try:
    yamale.validate(schema, data)
    print('Validation success! 👍')
except YamaleError as e:
    print('Validation failed!\n')
    for result in e.results:
        print("Error validating data '%s' with '%s'\n\t" % (result.data, result.schema))
        for error in result.errors:
            print('\t%s' % error)
    exit(1)

You can also specify an optional parser if you'd like to use the ruamel.yaml (YAML 1.2 support) instead:

# Import Yamale and make a schema object, make sure ruamel.yaml is installed already.
import yamale
schema = yamale.make_schema('./schema.yaml', parser='ruamel')

# Create a Data object
data = yamale.make_data('./data.yaml', parser='ruamel')

# Validate data against the schema same as before.
yamale.validate(schema, data)

Schema

⚠️ Ensure that your schema definitions come from internal or trusted sources. Yamale does not protect against intentionally malicious schemas.

To use Yamale you must make a schema. A schema is a valid YAML file with one or more documents inside. Each node terminates in a string which contains valid Yamale syntax. For example, str() represents a String validator.

A basic schema:

name: str()
age: int(max=200)
height: num()
awesome: bool()

And some YAML that validates:

name: Bill
age: 26
height: 6.2
awesome: True

Take a look at the Examples section for more complex schema ideas.

Includes

Schema files may contain more than one YAML document (nodes separated by ---). The first document found will be the base schema. Any additional documents will be treated as Includes. Includes allow you to define a valid structure once and use it several times. They also allow you to do recursion.

A schema with an Include validator:

person1: include('person')
person2: include('person')
---
person:
    name: str()
    age: int()

Some valid YAML:

person1:
    name: Bill
    age: 70

person2:
    name: Jill
    age: 20

Every root node not in the first YAML document will be treated like an include:

person: include('friend')
group: include('family')
---
friend:
    name: str()
family:
    name: str()

Is equivalent to:

person: include('friend')
group: include('family')
---
friend:
    name: str()
---
family:
    name: str()
Recursion

You can get recursion using the Include validator.

This schema:

person: include('human')
---
human:
    name: str()
    age: int()
    friend: include('human', required=False)

Will validate this data:

person:
    name: Bill
    age: 50
    friend:
        name: Jill
        age: 20
        friend:
            name: Will
            age: 10
Adding external includes

After you construct a schema you can add extra, external include definitions by calling schema.add_include(dict). This method takes a dictionary and adds each key as another include.

Strict mode

By default Yamale will provide errors for extra elements present in lists and maps that are not covered by the schema. With strict mode disabled (using the --no-strict command line option), additional elements will not cause any errors. In the API, strict mode can be toggled by passing the strict=True/False flag to the validate function.

It is possible to mix strict and non-strict mode by setting the strict=True/False flag in the include validator, setting the option only for the included validators.

Validators

Here are all the validators Yamale knows about. Every validator takes a required keyword telling Yamale whether or not that node must exist. By default every node is required. Example: str(required=False)

You can also require that an optional value is not None by using the none keyword. By default Yamale will accept None as a valid value for a key that's not required. Reject None values with none=False in any validator. Example: str(required=False, none=False).

Some validators take keywords and some take arguments, some take both. For instance the enum() validator takes one or more constants as arguments and the required keyword: enum('a string', 1, False, required=False)

String - str(min=int, max=int, equals=string, starts_with=string, ends_with=string, matches=regex, exclude=string, ignore_case=False, multiline=False, dotall=False)

Validates strings.

  • keywords
    • min: len(string) >= min
    • max: len(string) <= max
    • equals: string == value (add ignore_case=True for case-insensitive checking)
    • starts_with: Accepts only strings starting with given value (add ignore_case=True for case-insensitive checking)
    • matches: Validates the string against a given regex. Similar to the regex() validator, you can use ignore_case, multiline and dotall)
    • ends_with: Accepts only strings ending with given value (add ignore_case=True for case-insensitive checking)
    • exclude: Rejects strings that contains any character in the excluded value
    • ignore_case: Validates strings in a case-insensitive manner.
    • multiline: ^ and $ in a pattern match at the beginning and end of each line in a string in addition to matching at the beginning and end of the entire string. (A pattern matches at the beginning of a string even in multiline mode; see below for a workaround.); only allowed in conjunction with a matches keyword.
    • dotall: . in a pattern matches newline characters in a validated string in addition to matching every character that isn't a newline.; only allowed in conjunction with a matches keyword.

Examples:

  • str(max=10, exclude='?!'): Allows only strings less than 11 characters that don't contain ? or !.

Regex - regex([patterns], name=string, ignore_case=False, multiline=False, dotall=False)

Validates strings against one or more regular expressions.

  • arguments: one or more Python regular expression patterns
  • keywords:
    • name: A friendly description for the patterns.
    • ignore_case: Validates strings in a case-insensitive manner.
    • multiline: ^ and $ in a pattern match at the beginning and end of each line in a string in addition to matching at the beginning and end of the entire string. (A pattern matches at the beginning of a string even in multiline mode; see below for a workaround.)
    • dotall: . in a pattern matches newline characters in a validated string in addition to matching every character that isn't a newline.

Examples:

  • regex('^[^?!]{,10}$'): Allows only strings less than 11 characters that don't contain ? or !.
  • regex(r'^(\d+)(\s\1)+$', name='repeated natural'): Allows only strings that contain two or more identical digit sequences, each separated by a whitespace character. Non-matching strings like sugar are rejected with a message like 'sugar' is not a repeated natural.
  • regex('.*^apples$', multiline=True, dotall=True): Allows the string apples as well as multiline strings that contain the line apples.

Integer - int(min=int, max=int)

Validates integers.

  • keywords
    • min: int >= min
    • max: int <= max

Number - num(min=float, max=float)

Validates integers and floats.

  • keywords
    • min: num >= min
    • max: num <= max

Boolean - bool()

Validates booleans.

Null - null()

Validates null values.

Enum - enum([primitives])

Validates from a list of constants.

  • arguments: constants to test equality with

Examples:

  • enum('a string', 1, False): a value can be either 'a string', 1 or False

Day - day(min=date, max=date)

Validates a date in the form of YYYY-MM-DD.

  • keywords
    • min: date >= min
    • max: date <= max

Examples:

  • day(min='2001-01-01', max='2100-01-01'): Only allows dates between 2001-01-01 and 2100-01-01.

Timestamp - timestamp(min=time, max=time)

Validates a timestamp in the form of YYYY-MM-DD HH:MM:SS.

  • keywords
    • min: time >= min
    • max: time <= max

Examples:

  • timestamp(min='2001-01-01 01:00:00', max='2100-01-01 23:00:00'): Only allows times between 2001-01-01 01:00:00 and 2100-01-01 23:00:00.

List - list([validators], min=int, max=int)

Validates lists. If one or more validators are passed to list() only nodes that pass at least one of those validators will be accepted.

  • arguments: one or more validators to test values with
  • keywords
    • min: len(list) >= min
    • max: len(list) <= max

Examples:

  • list(): Validates any list
  • list(include('custom'), int(), min=4): Only validates lists that contain the custom include or integers and contains a minimum of 4 items.

Map - map([validators], key=validator, min=int, max=int)

Validates maps. Use when you want a node to contain freeform data. Similar to List, Map takes one or more validators to run against the values of its nodes, and only nodes that pass at least one of those validators will be accepted. By default, only the values of nodes are validated and the keys aren't checked.

  • arguments: one or more validators to test values with
  • keywords
    • key: A validator for the keys of the map.
    • min: len(map) >= min
    • max: len(map) <= max

Examples:

  • map(): Validates any map
  • map(str(), int()): Only validates maps whose values are strings or integers.
  • map(str(), key=int()): Only validates maps whose keys are integers and values are strings. 1: one would be valid but '1': one would not.
  • map(str(), min=1): Only validates a non-empty map.

IP Address - ip()

Validates IPv4 and IPv6 addresses.

  • keywords
    • version: 4 or 6; explicitly force IPv4 or IPv6 validation

Examples:

  • ip(): Allows any valid IPv4 or IPv6 address
  • ip(version=4): Allows any valid IPv4 address
  • ip(version=6): Allows any valid IPv6 address

MAC Address - mac()

Validates MAC addresses.

Examples:

  • mac(): Allows any valid MAC address

SemVer (Semantic Versioning) - semver()

Validates Semantic Versioning strings.

Examples:

  • semver(): Allows any valid SemVer string

Any - any([validators])

Validates against a union of types. Use when a node must contain one and only one of several types. It is valid if at least one of the listed validators is valid. If no validators are given, accept any value.

  • arguments: validators to test values with (if none is given, allow any value; if one or more are given, one must be present)

Examples:

  • any(int(), null()): Validates either an integer or a null value.
  • any(num(), include('vector')): Validates either a number or an included 'vector' type.
  • any(str(min=3, max=3),str(min=5, max=5),str(min=7, max=7)): validates to a string that is exactly 3, 5, or 7 characters long
  • any(): Allows any value.

Subset - subset([validators], allow_empty=False)

Validates against a subset of types. Unlike the Any validator, this validators allows one or more of several types. As such, it automatically validates against a list. It is valid if all values can be validated against at least one validator.

  • arguments: validators to test with (at least one; if none is given, a ValueError exception will be raised)
  • keywords:
    • allow_empty: Allow the subset to be empty (and is, therefore, also optional). This overrides the required flag.

Examples:

  • subset(int(), str()): Validators against an integer, a string, or a list of either.
  • subset(int(), str(), allow_empty=True): Same as above, but allows the empty set and makes the subset optional.

Include - include(include_name)

Validates included structures. Must supply the name of a valid include.

  • arguments: single name of a defined include, surrounded by quotes.

Examples:

  • include('person')

Custom validators

It is also possible to add your own custom validators. This is an advanced topic, but here is an example of adding a Date validator and using it in a schema as date()

import yamale
import datetime
from yamale.validators import DefaultValidators, Validator

class Date(Validator):
    """ Custom Date validator """
    tag = 'date'

    def _is_valid(self, value):
        return isinstance(value, datetime.date)

validators = DefaultValidators.copy()  # This is a dictionary
validators[Date.tag] = Date
schema = yamale.make_schema('./schema.yaml', validators=validators)
# Then use `schema` as normal

Examples

⚠️ Ensure that your schema definitions come from internal or trusted sources. Yamale does not protect against intentionally malicious schemas.

Using keywords

Schema:

optional: str(required=False)
optional_min: int(min=1, required=False)
min: num(min=1.5)
max: int(max=100)

Valid Data:

optional_min: 10
min: 1.6
max: 100

Includes and recursion

Schema:

customerA: include('customer')
customerB: include('customer')
recursion: include('recurse')
---
customer:
    name: str()
    age: int()
    custom: include('custom_type')

custom_type:
    integer: int()

recurse:
    level: int()
    again: include('recurse', required=False)

Valid Data:

customerA:
    name: bob
    age: 900
    custom:
        integer: 1
customerB:
    name: jill
    age: 1
    custom:
        integer: 3
recursion:
    level: 1
    again:
        level: 2
        again:
            level: 3
            again:
                level: 4

Lists

Schema:

list_with_two_types: list(str(), include('variant'))
questions: list(include('question'))
---
variant:
  rsid: str()
  name: str()

question:
  choices: list(include('choices'))
  questions: list(include('question'), required=False)

choices:
  id: str()

Valid Data:

list_with_two_types:
  - 'some'
  - rsid: 'rs123'
    name: 'some SNP'
  - 'thing'
  - rsid: 'rs312'
    name: 'another SNP'
questions:
  - choices:
      - id: 'id_str'
      - id: 'id_str1'
    questions:
      - choices:
        - id: 'id_str'
        - id: 'id_str1'

The data is a list of items without a keyword at the top level

Schema:

list(include('human'), min=2, max=2)

---
human:
  name: str()
  age: int(max=200)
  height: num()
  awesome: bool()

Valid Data:

- name: Bill
  age: 26
  height: 6.2
  awesome: True

- name: Adrian
  age: 23
  height: 6.3
  awesome: True

Writing Tests

To validate YAML files when you run your program's tests use Yamale's YamaleTestCase

Example:

class TestYaml(YamaleTestCase):
    base_dir = os.path.dirname(os.path.realpath(__file__))
    schema = 'schema.yaml'
    yaml = 'data.yaml'
    # or yaml = ['data-*.yaml', 'some_data.yaml']

    def runTest(self):
        self.assertTrue(self.validate())

base_dir: String path to prepend to all other paths. This is optional.

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.

Developers

Linting + Formatting

Yamale is formatted with ruff. There is a github action enforcing ruff formatting and linting rules. You can run this locally via make lint or by installing the pre-commit hooks via make install-hooks

Testing

Yamale uses Tox to run its tests against multiple Python versions. To run tests, first checkout Yamale, install Tox, then run make test in Yamale's root directory. You may also have to install the correct Python versions to test with as well.

NOTE on Python versions: tox.ini specifies the lowest and highest versions of Python supported by Yamale. Unless your development environment is configured to support testing against multiple Python versions, one or more of the test branches may fail. One method of enabling testing against multiple versions of Python is to install pyenv and tox-pyenv and to use pyenv install and pyenv local to ensure that tox is able to locate appropriate Pythons.

Releasing

Yamale uses Github Actions to upload new tags to PyPi. To release a new version:

  1. Make a commit with the new version number in yamale/VERSION.
  2. Run tests for good luck.
  3. Run make release.

Github Actions will take care of the rest.

yamale's People

Contributors

abourree avatar andreynautilus avatar antfitch avatar blopker avatar cblakkan avatar davidak avatar dpasichnyi avatar drmull avatar europ avatar haudren avatar idantene avatar jesse-r-s-hines avatar jonschumacher avatar jsoref avatar kalefranz avatar kiblik avatar lgtm-migrator avatar peterwhittaker avatar phidica avatar salu133445 avatar sarahc23 avatar scriptsrc avatar sigma67 avatar simonw avatar sirykd avatar supertylerc avatar tapaswenipathak avatar thiagowfx avatar tjlaboss avatar yu-anchen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

yamale's Issues

Bug:map( key=validator ) dont work at all, it just say true

To reproduce paste this in terminal

echo "
map1: include('test1')
map2: include('test2')
---
test1: map(str(), key=int())
test2: map(str(), key=regex('a*'))
" > test-schema.yaml;

# Actually anything passed into the key validator doesnt do anything
echo "
map1:
  key: <- this key isnt an integer,
map2:
  key: <- this key is not a valid regex match
" > test.yaml;
yamale -s test-schema.yaml test.yaml --strict

This command should fail because the key validator should not validate, but it is always passing.

Adding possibility to include validators

Hello,
I think it could be interesting to be able to include not only nodes but also validators in Yamale. For example:

# [schema.yaml]

key1: include('freq_val_1')
key2: include('freq_val_2')

--- # Frequently used validators
freq_val_1: enum('str_1', 'str_2', 'str_3', 'str_4', 'str_5')
freq_val_2: map(include('a_node_with_some_very_long_name_1'), include('a_node_with_some_very_long_name_2'))
# etc...

--- # Included nodes
a_node_with_some_very_long_name_1:
  n: int()
a_node_with_some_very_long_name_2:
  x: num()

I can do a PR if you are interested. In this case all suggestions are welcome.

Root level constraint.

How do we express this in yamale? I want the constraints on topic-1 and topic-2 to be enforced only when yaml file has target-1.

target-1: 
  topic-1:
     a: str()
     b: str()
  topic-2:
     c: int()
     d: int() 

target-2: 
  topic-3:
     a: str()
     b: str()
  topic-4:
     c: int()
     d: int()

Add interface to support shared type definitions.

Redefining a shared type in many different schemas could be improved by allowing a shared type definition yaml file. It would be nice to have an interface for specifying a shared type definition file.

Conditional with strict

I can't validate a schema like this:
Data 1:

id: example
type: type1
value: 10

Data 2:

id: example
type: type2
value: value

Schema:

any(include('v1'), include('v2), include('v3'))
---
v1:
  id: str()
---
v2:
 type: regex('^type1$')
 value: int()
---
v3:
 type: regex('^type2$')
 value: str()

I was only able to do that when I duplicated schema like this:

any(include('v2'), include('v3'))
---
v2:
 id: str()
 type: regex('^type1$')
 value: int()
---
v3:
 id: str()
 type: regex('^type2$')
 value: str()

This occurs because validator in any operator are validated separately.
I would like to make a conditional schema validator, defined by the type field

Typo in lists example in README

In the README example on lists, the schema has

question:
  choices: list(include('choices'))
  questions: list(include('question'), required=False)

and the example data has

questions:
  - choices:
      - id: 'id_str'
      - id: 'id_str1'
    questions:
      - choices:
        - id: 'id_str'
        - id: 'id_str1'

I'm not very familiar with YAML syntax, but i think there is a typo (singular vs plural). Either question: in the schema should be questions: or the outer questions: in the data should be question:.

map/list with all optional fields

Hi,

When introducing 2.0 I changed the behavior of maps/lists with all optional keys as discussed in #60 (comment) . Now I stumbled on a project having a problem with this new behavior: canada-ca/ore-ero#702

This gave me a bit of a bad feeling so I took another look at the issue and now I think I can fix it so it works like in pre 2.0 without too much trouble.

Its a bit of a mess since if we do this we make another backwards incompatible change in regards with 2.0 but since 2.0 haven't been out long maybe it could be viewed as an regression. Otherwise I suppose we have to bump the version again...

I don't know, what do you think @mildebrandt? Do you want a PR for this?

Python validation versions

In README, we said that Yamale is validated with Python 2.7 and 3.4.
Before PR #81, tox.ini said Python 2.7 and 3.6
Since PR #81, tox.ini said Pythng 2.7 and lastest Python 3

Do we need to validate Yamale on Python 3 older than latest ?
If yes, should we validate from 3.4 to latest ? Or only 3.4 and latest ? Or only 3.4 ?

Bug validating scientific notation

Hello,
I am encountering an issue when I have a float written in the scientific notation. For example, under python3:

# [data.yaml]
key1: 1.0e-6
key2: 1e-6
# [schema.yaml]
key1: num()
key2: num()

When I try to validate with the command yamale data.yaml, I get the error:

ValueError: 
Error validating data /local/home/mrigal/Work/parsing_yaml/issues/issue1/data.yaml with schema schema.yaml
	key2: '1e-6' is not a num.

For some reason it seems that PyYAML converts 1e-6 to a string (but not 1.0e-6). If I modify the source file yamale/readers/yaml_reader.py to load the module ruamel.yaml instead of yaml, it works fine:

from __future__ import absolute_import
from ruamel.yaml import YAML

yaml=YAML(typ="safe")

def parse_file(file_name):
    with open(file_name) as f:
        return list(yaml.load_all(f))

empty schema generated IndexError exception

hello,
assume the simple block of code below:

schema = yamale.make_schema('schema.yml')

where:
schema.yml is an empty file

this generates the following exception

File "....../lib/python3.7/site-packages/yamale/yamale.py", line 17, in make_schema
s = Schema(raw_schemas[0], path, validators=validators)
IndexError: list index out of range

I think would make sense to add a simple check to a non-empty yml schema.

if you think this make sense, let me know if you will accept a pull request for it. I can help to fix it.

The tool is great and I enjoy it

thnx
Antonio

Validate List of maps

No way to validate a list of maps

yaml:

childs:
  - name: foo
    age: 3
  - name: bar
    age: 5

not working schema

childs: list(map())
childs: list(include(childs))
---
childs:
  name: str()
  age: int()

Add meta_test_fixtures to the MANIFEST

Via email:

I'm using yamale for some time and eventually tried
to make debian package for it. I use the following command line:

$python setup.py --command-packages=stdeb.command bdist_deb

It results me errors, because it doesn't copy files from
yamale/tests/meta_test_fixtures to deb_dist/ directory as other needed
files. I made the following change and it began to work:

$ git diff
diff --git a/MANIFEST.in b/MANIFEST.in
index 91b9824..440b84f 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,2 +1,3 @@
   include *.md LICENSE
   include yamale/tests/fixtures/*.yaml
+include yamale/tests/meta_test_fixtures/*.yaml

I'm using f5444a3602e07871c4700efe6aaf75c51addb384 revision of yamale
sources.

I hope this will be useful for you.

Validate struct of dict

How dow I validate this yaml structure:

people:
  personA:
    name: Some Name
    age: Some Age
  personB:
    name: Some Other Name
    age: Some Other Age

The problem is that this is not a list, it will be parsed into a dict structure. In python this would then be ["people"]["personA"]["age"] for instance.

empty yml to validate not detected

hello,
assume the simple block of code below (may be not a bug)

schema = yamale.make_schema('schema.yml')
cfg = yamale.make_data('config.yml')

try:
    yamale.validate(schema, cfg)
except ValueError as e:
    log.critical(e)
    sys.exit(1)

where:
schema.yml is the yaml schema used to validate a config.yml file

Now let's assume in the schema.yml there a line like:
log_file: str(required=True)

Now if I provide an empty config.yml file, the validation succeed with no exception.
So in this way any config.yml is considered valid.

I think would make sense to add a simple check to a non-empty yml file to validate.

if you think this make sense, let me know if you will accept a pull request for it. I can help to fix it.

The tool is great and I enjoy it

thnx
Antonio

Possibility to export to standard json schema

Hi,

we are considering using this tool for our yaml schemas validation but we rely on being able to produce a standard json schema from a yamale schema for different extensions we use that needs the schema to be supplied in json.

Would it be possible to do this? From YAML to JSON we have no issue at all, but given than yamale introduces new semantics we cannot do it easily ourselves.

Thanks!

dict key type validation

Would it be possible to also add validation of key type in dict (={key:val}), please?

I am missing this functionality as almost all validators work along jsonschema principles. There (by definition) is every key string. But not so in YAML (https://yaml.org/spec/1.2/spec.html#id2764196)... The key itself could be almost everything (unique).

(If I am missing something obvious in yamale docs, excuse me in advance.)

feature suggestions

Hello:

If possible, please add the following features to yamale:

  1. Unions. Usage should look like this:

key: union(int(), include('my_type'), string(), ... )

  1. min/max length specifications in list()

  2. An exclude=True option in map() so that keys that are not in the required or optional keyset of the schema will be rejected

  3. Schema Extensions/Merges - this is different from includes in that it does not add an included schema as a subschema, but extends a current schema. For example:

Foo:
a: int()
b: str()
d: list(str())
Bar(Foo):
b: map()
c: num()

Bar's full schema when Foo is resolved will be (b is overriden in Bar)

Bar(Foo):
a: int()
d: list(str())
b: map()
c: num()

Custom key validation

Hello,

is it possible to validate custom key ('abc' or 'bcd' or whatever str()) as below:

Data:

abc:
  type: 1
  option: a
bcd:
  type: 2
  option: b

Schema:

???: include('profile')
---
profile:
  type: int()
  option: str()

Implement set() validator

if I want to have a list of unique strings like this:

list:
  - test
  - test

there's no way to validate the list entries are unique.

Validating unamed list

Esteemed maintainer,

I am not sure how one would validate an unamed list:of maps

  • id: 1
  • id: 2

Ability to specify schema and data without a filename

Currently you use the library like this:

import yamale
schema = yamale.make_schema('./schema.yaml')

# Create a Data object
data = yamale.make_data('./data.yaml')

# Validate data against the schema. Throws a ValueError if data is invalid.
yamale.validate(schema, data)

It would be really useful if there was an option to provide the schema and data as Python strings instead - that way the could be loaded from a database or from an incoming HTTP request.

Something like this could work

import yamale

schema = yamale.make_schema(content="""
name: str()
age: int(max=200)
height: num()
awesome: bool()
""")

data = yamale.make_data(content="""
name: Bill
age: 26
height: 6.2
awesome: True
""")

yamale.validate(schema, data)

Unable to validate list of values

Hi,

We trying to validate list of values of spec.initContainers.resources but we are getting following error while validating list of values

Error:

root@devenv:/media/sf_test_devenv# yamale -s schema.yaml data.yaml
Validating /media/sf_test_devenv/data.yaml...

Error!
Schema: schema.yaml
Data file: /media/sf_test_devenv/data.yaml
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/yamale/command_line.py", line 29, in _validate
    yamale.validate(schema, data)
  File "/usr/local/lib/python2.7/dist-packages/yamale/yamale.py", line 39, in validate
    schema.validate(d)
  File "/usr/local/lib/python2.7/dist-packages/yamale/schema/schema.py", line 56, in validate
    raise ValueError(error_str)
ValueError:
Error validating data /media/sf_test_devenv/data.yaml with schema schema.yaml
        spec.template.spec.initContainers.0.resources.requests.cpu: Required field missing
        spec.template.spec.initContainers.0.resources.requests.memory: Required field missing
        spec.template.spec.initContainers.0.resources.limits.memory: Required field missing
        spec.template.spec.initContainers.0.resources.limits.cpu: Required field missing
        spec.template.spec.initContainers.1.resources.requests.cpu: Required field missing
        spec.template.spec.initContainers.1.resources.requests.memory: Required field missing
        spec.template.spec.initContainers.1.resources.limits.memory: Required field missing
        spec.template.spec.initContainers.1.resources.limits.cpu: Required field missing

Traceback (most recent call last):
  File "/usr/local/bin/yamale", line 11, in <module>
    load_entry_point('yamale==1.10.0', 'console_scripts', 'yamale')()
  File "/usr/local/lib/python2.7/dist-packages/yamale/command_line.py", line 112, in main
    _router(args.path, args.schema, args.cpu_num, args.parser)
  File "/usr/local/lib/python2.7/dist-packages/yamale/command_line.py", line 96, in _router
    _validate_single(root, schema_name, parser)
  File "/usr/local/lib/python2.7/dist-packages/yamale/command_line.py", line 68, in _validate_single
    _validate(s, yaml_path, parser)
  File "/usr/local/lib/python2.7/dist-packages/yamale/command_line.py", line 36, in _validate
    raise ValueError('Validation failed!')
ValueError: Validation failed!
root@devenv:/media/sf_test_devenv#

Data.yaml


##---
# Source: SampleApp/templates/deployment.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: SampleApp
  labels:
    app: SampleApp
    chart: SampleApp-0.0.0-snapshot
spec:
  replicas: 2
  selector:
    matchLabels:
      app: SampleApp
      release: RELEASE-NAME
  template:
    metadata:
      labels:
        app: SampleApp
        release: RELEASE-NAME
    spec:
      initContainers:
      - name: SampleApp-create-db
        image: "${DOCKER_NAME}/service-db-maria:0.0.18"
        imagePullPolicy: IfNotPresent
        resources:
            limits:
              cpu: 1
              memory: 512Mi
            requests:
              cpu: 50m
              memory: 128Mi
            
      - name: SampleApp-migrate-db
        image: "${DOCKER_REGISTRY}/SampleApp-migrate:${BUILD_VERSION}"
        resources:
            limits:
              cpu: 1
              memory: 512Mi
            requests:
              cpu: 50m
              memory: 128Mi
      containers:
        - name: SampleApp
          image: "${DOCKER_REGISTRY}/SampleApp:0.0.0-snapshot"
          imagePullPolicy: IfNotPresent
          env:
          - name: LOGGING_LEVEL
            value: "info"
          ports:
            - containerPort: 80
            - containerPort: 6060
          resources:
            limits:
              cpu: 1
              memory: 512Mi
            requests:
              cpu: 50m
              memory: 128Mi

Schema.yaml

spec: 
  template:
    spec: 
      initContainers: list(include('resourcesDefinition'))
---
resourcesDefinition:
  resources: 
    limits:
      memory: any(str(), int())
      cpu: any(str(), int())
    requests:
      cpu: any(str(), int())
      memory: any(str(), int())

null() validator can be confusing with none=False

Hi,

I found that the null() validator can be confusing with none=False. Here is an example.

# Data
key: ~
# Schema
null(required=False, none=False)  # -> this will pass without throwing errors

One possible way is to state it clear in the documentation that none=False will simply be ignored for a null() validator. The other way is to raise an error during constructing the validator if none=False is set for a null validator.

Thanks.

[Feature request] "in" validator

Hi,

first of all thanks for the work on this. It's very easy to use. I do have a feature request though.

It would be great if it were possible to add a validator that checks if a value is within a list provided by the user.

A simple example to illustrate what I'm talking about:

Schema

map(include('category'))
---
category:
  default: in('category.tags')
  tags: list(str(), int())

Data

rating:
  default: Unrated
  # default: must be within the user-defined tags
  # default: "LMAO" would be invalid for example
  tags:
    - Unrated
    - 1
    - 2
    - 3
    - 4
    - 5

Thank you

Add support for custom error messages

Hello! I think it would be really nice to be able to get additional information out of the failed validation so that I can display a useful error message to the end user of my software.

Example:

Error validating data config.yml with schema .config.schema
        api_key: Required field missing
        log_level: 'test' is not a regex match.
        log_dir: '1' is not a str.

What I would like to be able to do:

Configuration is not valid!
        api_key: Must be a valid API key
        log_level: Must be one of [DEBUG, INFO, WARNING, CRITICAL].
        log_dir: Must be a valid directory.

Or maybe being able to get info out of the exception. Maybe something like this?

class SchemaValidationError(Exception):
    def __init__(self, message, errors):
        super().__init__(message)
        self.errors = errors

...

raise SchemaValidationError(
    "Error validating data config.yml with schema .config.schema",
    errors=[
        { "key": "api_key", "reason": "Required field missing" },
        { "key": "log_level", "reason": "'test' is not a regex match" },
        { "key": "log_dir", "reason": "'1' is not a str" }
    ]
)

Document `key: list(include('custom'), str())` format

Would this be something you'd be willing to accept functionality to be able to validate?

# config.yaml
apps:
  - id: com.android.foo
    url: http://example.com/com.android.foo.apk
  - id: com.android.bar
    versioncode: 123
  - com.android.baz

with this:

# schema.yaml
apps: list(include('app'), str())

---
app:
  id: str()
  versioncode: int(required=False)
  url: str(required=False)

Validate multiple alternative schemas

I'm trying to build a validator for a configuration file format that has multiple mutually incompatible versions (similar to docker-compose files for example).

Example of how I had hoped to express this:

# The following line does *not* work
any(include('v1_config'), include('v2_config'))
---
v1_config:
  version: int(min=1, max=1, required=False)
  settings:
    v1_setting_a: str()
    v1_setting_b: str(required=False)
---
v2_config:
  version: int(min=2, max=2)
  settings:
    v2_setting_c: str()
    v2_setting_d: int()

Is there any way to express what I'm trying to do here, or is that simply not supported (yet)?

Conditional with strict

I can't validate a schema like this:
Data 1:

id: example
type: type1
value: 10

Data 2:

id: example
type: type2
value: value

Schema:

any(include('v1'), include('v2), include('v3'))
---
v1:
  id: str()
---
v2:
 type: regex('^type1')
 value: int()
---
v3:
 type: regex('^type2')
 value: str()

I was only able to do that when I duplicated schema like this:

any(include('v2), include('v3'))
---
v2:
 id: str()
 type: regex('^type1')
 value: int()
---
v3:
 id: str()
 type: regex('^type2')
 value: str()

This occurs because validator in any operator are validated separatedely.
I would like to make a conditional schema validator, defined by the type field

schema file in current directory overrules schemas in deeper directories

When yamale is run on the command line in a file tree of yaml files and schema files, the program wrongly validates against the schema file in the current directory rather than matching the schema files deeper in the file tree.

Example:

cwd/
   +- schema.yaml
   +- data.yml
   +- subdir/
       +- schema.yaml
       +- data2.yml

When yamale is run from cwd, both data.yml and data2.yml are validated against cwd/schema.yaml; if the command is called from another directory (outside or higher in the tree) the file data2.yml is validated against cwd/subdir/schema.yaml.

The reason is that the glob in _find_schema will try to match the schema file name without a path prefix, which will match a schema file in the current directory.

path = glob.glob(schema_name)

This should probably be changed to match the directory name of the data file plus the name of the schema.

Can't Validate Keys with Dots in Them

It seems that I can't validate a YAML file if it has dots (.) in the key. Looks like it's because of the flattening of keys later on, but interestingly you can specify a key with dots in the schema, and Yamale will translate the schema keys with dots in them to underscores.

I worked around this in a super hacky way locally with the following:

from yamale import util

def sub(dic):
    new = {}
    for k, v in util.get_iter(dic):
        if util.is_iter(v):
            v = sub(v)
        if util.isstr(k):
            new[k.replace('.', '_')] = v
    return new


def parse_file(file_name, parser):
    try:
        parse = _parsers[parser.lower()]
    except KeyError:
        raise NameError('Parser "' + parser + '" is not supported\nAvailable parsers are listed below:\nPyYAML\nruamel')
    parsed = parse(file_name)
    new = []
    for x in parsed:
        new.append(sub(x))
    return new

This pretty much just replaces . with _ in parsed YAML files, which is being done elsewhere anyway when things get flattened out. I'm not sure if the flattening functionality that does the dot replacement could be removed with this patch, but maybe it could be.

Would you be open to a cleaned up and tested version of this in a PR? If so, I can work on it and submit it.

My primary focus is networking, so this is useful when I need to validate a document with IP addresses or MAC addresses as a key.

Calling Custom Validator using Yamale commandline

Hi,

I understood that Custom validators can be written. We can call custom validator with python command.
Is there a way so that we can use custom validator directly with Yamale command (yamale --schema <file_schema> ).

Regards
Hemant

ValueError when attempting to validate list of validator and validator

Hi all;

In an issue similar (I think) to #22 we're running into situations where we have schemas with optional lists - i.e.

example1:
  logic:
    or:
      - thing1
      - thing2
example2:
  logic:
    - or:
      - thing3
      - thing4
      - thing5

validating against:

example1: include('spec-1', required=False)
example2: include('spec-2', required=False)
---
spec-1:
  logic: >
    any(
      include('or-spec', required=False),
      required=False
    )
spec-2:
  logic: >
    any(
      list(
        include('or-spec', required=False),
        required=False
      ),
      required=False
    )
blended-spec:
  logic: >
    any(
      list(
        include('or-spec', required=False),
        required=False
      ),
      include('or-spec', required=False),
      required=False
    )
or-spec:
  or: any(list(str(required=True)), str(required=False))

I can create validators for either example1 or example2 (spec-1 and spec-2 respectively) but when I try to combine those together (blended-spec) I end up with the below stack trace. I'm starting to convince myself that it's not me, but I'd say it's still 50/50 - my YAML comprehension is really truly bad. Any advice would be greatly appreciated!

Traceback (most recent call last):
  File "/Users/hhorsey/.pyenv/versions/idsm3/lib/python3.7/site-packages/yamale/util.py", line 81, in get_value
    return reduce(getitem, path, dic)[last_key]
TypeError: list indices must be integers or slices, not str

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/hhorsey/repos/tracked/buildstockbatch-dev/buildstockbatch/base.py", line 294, in validate_project_schema
    return yamale.validate(schema, data)
  File "/Users/hhorsey/.pyenv/versions/idsm3/lib/python3.7/site-packages/yamale/yamale.py", line 39, in validate
    schema.validate(d)
  File "/Users/hhorsey/.pyenv/versions/idsm3/lib/python3.7/site-packages/yamale/schema/schema.py", line 49, in validate
    errors += self._validate(validator, data, key=key, includes=self.includes)
  File "/Users/hhorsey/.pyenv/versions/idsm3/lib/python3.7/site-packages/yamale/schema/schema.py", line 83, in _validate
    return self._validate_item(validator, data_item, position, includes)
  File "/Users/hhorsey/.pyenv/versions/idsm3/lib/python3.7/site-packages/yamale/schema/schema.py", line 104, in _validate_item
    includes, position)
  File "/Users/hhorsey/.pyenv/versions/idsm3/lib/python3.7/site-packages/yamale/schema/schema.py", line 150, in _validate_include
    errors += include_schema._validate(validator, data, includes=includes, key=key, position=pos)
  File "/Users/hhorsey/.pyenv/versions/idsm3/lib/python3.7/site-packages/yamale/schema/schema.py", line 83, in _validate
    return self._validate_item(validator, data_item, position, includes)
  File "/Users/hhorsey/.pyenv/versions/idsm3/lib/python3.7/site-packages/yamale/schema/schema.py", line 112, in _validate_item
    includes, position)
  File "/Users/hhorsey/.pyenv/versions/idsm3/lib/python3.7/site-packages/yamale/schema/schema.py", line 163, in _validate_any
    err = self._validate_item(v, data, pos, includes)
  File "/Users/hhorsey/.pyenv/versions/idsm3/lib/python3.7/site-packages/yamale/schema/schema.py", line 104, in _validate_item
    includes, position)
  File "/Users/hhorsey/.pyenv/versions/idsm3/lib/python3.7/site-packages/yamale/schema/schema.py", line 150, in _validate_include
    errors += include_schema._validate(validator, data, includes=includes, key=key, position=pos)
  File "/Users/hhorsey/.pyenv/versions/idsm3/lib/python3.7/site-packages/yamale/schema/schema.py", line 75, in _validate
    data_item = util.get_value(data, key)
  File "/Users/hhorsey/.pyenv/versions/idsm3/lib/python3.7/site-packages/yamale/util.py", line 83, in get_value
    return reduce(getitem, path, dic)[int(last_key)]
ValueError: invalid literal for int() with base 10: 'or'

I'm testing against 1.10.0. Thanks so much!

Ry

strict mode?

Keys that do not fit the schema are ignored - so I could have a typo in an optional key and never know about it. It would be nice to have a strict mode where you get a warning that keys were found that are not in the schema.

Regression in v1.5.2

Hello,
It looks like there was a regression in v1.5.2. Could you please look over it.

# Clean
$ pip uninstall yamale

# Test is OK with Yamale v1.5.0
$ pip install yamale==1.5.0
$ python regression_test.py
DONE!


# Test fails with Yamale v1.5.2
$ pip install --upgrade yamale==1.5.2
$ python regression_test.py
Traceback (most recent call last):
  File "/workspace/android/mission-impossible-android/.venv/lib/python3.4/site-packages/yamale/schema/util.py", line 65, in get_value
    return reduce(getitem, path, dic)[last_key]
TypeError: string indices must be integers

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "regression_test.py", line 12, in <module>
    yamale.validate(schema, data)
  File "/workspace/android/mission-impossible-android/.venv/lib/python3.4/site-packages/yamale/yamale.py", line 30, in validate
    schema.validate(d)
  File "/workspace/android/mission-impossible-android/.venv/lib/python3.4/site-packages/yamale/schema/schema.py", line 44, in validate
    errors += self._validate(validator, data, key=key, includes=self.includes)
  File "/workspace/android/mission-impossible-android/.venv/lib/python3.4/site-packages/yamale/schema/schema.py", line 76, in _validate
    return self._validate_item(validator, data_item, position, includes)
  File "/workspace/android/mission-impossible-android/.venv/lib/python3.4/site-packages/yamale/schema/schema.py", line 100, in _validate_item
    includes, position)
  File "/workspace/android/mission-impossible-android/.venv/lib/python3.4/site-packages/yamale/schema/schema.py", line 122, in _validate_map_list
    err = self._validate(v, data, key, pos, includes)
  File "/workspace/android/mission-impossible-android/.venv/lib/python3.4/site-packages/yamale/schema/schema.py", line 76, in _validate
    return self._validate_item(validator, data_item, position, includes)
  File "/workspace/android/mission-impossible-android/.venv/lib/python3.4/site-packages/yamale/schema/schema.py", line 96, in _validate_item
    includes, position)
  File "/workspace/android/mission-impossible-android/.venv/lib/python3.4/site-packages/yamale/schema/schema.py", line 142, in _validate_include
    errors += include_schema._validate(validator, data, includes=includes, key=key, position=pos)
  File "/workspace/android/mission-impossible-android/.venv/lib/python3.4/site-packages/yamale/schema/schema.py", line 68, in _validate
    data_item = util.get_value(data, key)
  File "/workspace/android/mission-impossible-android/.venv/lib/python3.4/site-packages/yamale/schema/util.py", line 67, in get_value
    return reduce(getitem, path, dic)[int(last_key)]
ValueError: invalid literal for int() with base 10: 'id'

This is the schema that is no longer working.

apps: >
  list(
    str(),
    include('app_fdroid_latest'),
  )

---

app_fdroid_latest:
  id: str()
  repository: str(required=False)
  app_type: enum('system', 'privileged', 'user', required=False)

Thank you,

include schema if a reference is valid

Hello

I'd like include sub schema if a field is set to true.

Does custom validator can walk into full data tree?
If yes, how to?

my data

a: true
b:
  c: 10

expecting schema

a: bool()
b: includeIf('a', 'isTrue', 'b')
---
isTrue: enum(True)
---
b:
  c: int()

Regards,

Arnaud

IP and MAC type validation

I think leveraging the netaddress module for IP and MAC address validation would be useful. I get that you do not have time for anything but bugs, so I figured I post this feature request and possibly get to it when I have time myself. Maybe even someone else might want to take a stab at this as well.

any() inside included element causes error

The following schema results in an error while parsing the schema:

test_1: include('test_2')

---
test_2: any(str(), int())

Relevant part of stack trace:

File "/usr/local/lib/python2.7/site-packages/yamale/yamale.py", line 17, in make_schema
s.add_include(raw_schema)
File "/usr/local/lib/python2.7/site-packages/yamale/schema/schema.py", line 20, in add_include
validators=self.validators)
File "/usr/local/lib/python2.7/site-packages/yamale/schema/schema.py", line 14, in init
self._schema = self._process_schema(schema_dict, self.validators)
File "/usr/local/lib/python2.7/site-packages/yamale/schema/schema.py", line 36, in _process_schema
raise SyntaxError(str(e) + ' at node '%s' in file %s' % (key, self.name))
SyntaxError: Invalid schema expression: 'i'. name 'i' is not defined at node '11' in file test_2

This is with Python 2.7 if that matters

So, if I want an element to contain one of two possible types of value, I need to do something like this instead and do some additional validation in the code that at least one of them is present:

test_1: include('test_2')

---
test_2:
  test_3: str(required=False)
  test_4: int(required=False)

Not sure if this is intended. Am I missing something or is there no neater way to do this?
Thanks!

Updates to string validator

Right now the string validator accepts an exclude argument: str(min=int, max=int, exclude=string)
@matstrand and I believe that "reject" is a better kwarg name.
It would also be more useful if the exclude kwarg accepted a regex pattern instead of just a literal string of characters.

Cleaner command line error output

Currently, when yamale is used from the command line and an error is found the output is difficult to parse since schema.validate() will raise and then command_line.main() will also raise. The raise() from command_line.main() doesn't actually add anything new and the raise() from validate() includes the python stack it makes it hard to find the actual problem - I'm only really interested in what yamale found, not the methods it called to find them.

To deal with this locally, I made some very simple changes that I'd be happy to contribute back if you're interested. In command_line.main(), I simply wrapped the call to _router():

try:
    _router(args.path, args.schema, args.cpu_num, args.parser, args.strict)
except ValueError:
    # Already processed
    pass
else:
    print('Validation success! 👍')

To get rid of the python stack from the output, I changed one line in command_line._validate() from
error += traceback.format_exc() to error += str(e).

With those changes, I was able to change this output

Validating /Users/televi/foo.yaml...

Error!
Schema: bar.yaml
Data file: /Users/televi/foo.yaml
Traceback (most recent call last):
  File "/Users/televi/.venv/lib/python3.6/site-packages/yamale/command_line.py", line 29, in _validate
    yamale.validate(schema, data, strict)
  File "/Users/televi/.venv/lib/python3.6/site-packages/yamale/yamale.py", line 38, in validate
    schema.validate(d, path, strict)
  File "/Users/televi/.venv/lib/python3.6/site-packages/yamale/schema/schema.py", line 65, in validate
    raise ValueError(error_str)
ValueError:
Error validating data /Users/televi/foo.yaml with schema bar.yaml
	borg: 'None' is not a str.

Traceback (most recent call last):
  File "/Users/televi/.venv/lib/python3.6/site-packages/yamale/command_line.py", line 29, in _validate
    yamale.validate(schema, data, strict)
  File "/Users/televi/.venv/lib/python3.6/site-packages/yamale/yamale.py", line 38, in validate
    schema.validate(d, path, strict)
  File "/Users/televi/.venv/lib/python3.6/site-packages/yamale/schema/schema.py", line 65, in validate
    raise ValueError(error_str)
ValueError:
Error validating data /Users/televi/foo.yaml with schema bar.yaml
	borg: 'None' is not a str.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/televi/.venv/bin/yamale", line 11, in <module>
    load_entry_point('yamale==2.0', 'console_scripts', 'yamale')()
  File "/Users/televi/.venv/lib/python3.6/site-packages/yamale/command_line.py", line 115, in main
    _router(args.path, args.schema, args.cpu_num, args.parser, args.strict)
  File "/Users/televi/.venv/lib/python3.6/site-packages/yamale/command_line.py", line 97, in _router
    _validate_single(root, schema_name, parser, strict)
  File "/Users/televi/.venv/lib/python3.6/site-packages/yamale/command_line.py", line 68, in _validate_single
    _validate(s, yaml_path, parser, strict)
  File "/Users/televi/.venv/lib/python3.6/site-packages/yamale/command_line.py", line 36, in _validate
    raise ValueError('Validation failed!')
ValueError: Validation failed!

to this output

Validating /Users/televi/foo.yaml...

Error!
Schema: bar.yaml
Data file: /Users/televi/foo.yaml

Error validating data /Users/televi/foo.yaml with schema bar.yaml
	borg: 'None' is not a str.

Custom validators require `eval`

Version: 2.0.1
OS: Windows 10
Python: 3.8.1

I was having issues making a Custom Validator. I tried to implement the Date example and noticed that the error was a string. This was the important part of the error:

date: 'datetime.date(2020,3,20)' is not a date.

When I used eval it worked correctly. Such as:

class Date(Validator):
    # ...
    
    def _is_valid(self, value):
        #  here
        return isinstance(eval(value), datetime.date)

I can make a PR to update the docs. But, is this (using eval) the intended behavior?

Other than eval, the user could instead extract the components of and apply them.

class Date(Validator):
    # ...
    
    def _is_valid(self, value):
        val = value.replace('datetime.date(', '').replace(')', '').split(',')
        return isinstance(datetime.date(val[0], val[1], val[2]), datetime.date)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.