Skip to content

Commit

Permalink
Fix #5: add optional validators to map()
Browse files Browse the repository at this point in the history
  • Loading branch information
blopker committed May 1, 2014
1 parent 50f71a5 commit 3ad6fb2
Show file tree
Hide file tree
Showing 12 changed files with 71 additions and 29 deletions.
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,6 @@ Validates integers and floats.
### Boolean - `bool()`
Validates booleans.

### Map - `map()`
Validates maps. Use when you want a node to contain freeform data.

### Enum - `enum([primitives])`
Validates from a list of constants.
- arguments: constants to test equality with
Expand All @@ -177,6 +174,14 @@ Examples:
- `list()`: Validates any list
- `list(str(), int())`: Only validates lists that contain strings or integers.

### Map - `map([validators])`
Validates maps. Use when you want a node to contain freeform data. Similar to `List`, `Map` also takes a number of validators to
run against it's children nodes. A child validates if at least one validator passes.

Examples:
- `map()`: Validates any map
- `map(str(), int())`: Only validates maps whose children are strings or integers.

### 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.
Expand Down
2 changes: 1 addition & 1 deletion yamale/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from .yamale import make_schema, make_data, validate


VERSION = (1, 0, 1, 'final', 0)
VERSION = (1, 0, 2, 'final', 0)


# Dynamically calculate the version based on VERSION.
Expand Down
17 changes: 12 additions & 5 deletions yamale/schema/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,18 +82,24 @@ def _validate(self, validator, data, key, position=None, includes=None):
if isinstance(validator, val.Include):
errors += self._validate_include(validator, data_item, includes, position)

elif isinstance(validator, val.List):
errors += self._validate_list(validator, data_item, includes, position)
elif isinstance(validator, (val.Map, val.List)):
errors += self._validate_map_list(validator, data_item, includes, position)

return errors

def _validate_list(self, validator, data, includes, pos):

def _validate_map_list(self, validator, data, includes, pos):
errors = []

if not validator.validators:
return errors # No validators, user just wanted a list.
return errors # No validators, user just wanted a map.

if isinstance(validator, val.List):
keys = range(len(data))
else:
keys = data.keys()

for key in range(len(data)):
for key in keys:
sub_errors = []
for v in validator.validators:
err = self._validate(v, data, key, pos, includes)
Expand All @@ -107,6 +113,7 @@ def _validate_list(self, validator, data, includes, pos):

return errors


def _validate_include(self, validator, data, includes, pos):
errors = []

Expand Down
2 changes: 1 addition & 1 deletion yamale/tests/fixtures/keywords_bad.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ optional: 'bad!?'
optional_min: 9
min_example: 1.3
max_example: NOT AN INT
boolean: True
boolean: "True"
1 change: 1 addition & 0 deletions yamale/tests/fixtures/map.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
map: map(str(), int())
3 changes: 3 additions & 0 deletions yamale/tests/fixtures/map_bad.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
map:
bad: 12.5
not: []
3 changes: 3 additions & 0 deletions yamale/tests/fixtures/map_good.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
map:
good: "hello"
yes: 5
1 change: 1 addition & 0 deletions yamale/tests/fixtures/types.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ integer: int()
boolean: bool()
list: list()
enum: enum('one', True, 1)
map: map()
13 changes: 7 additions & 6 deletions yamale/tests/fixtures/types_bad_data.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
string: "hello"
number: 12.1
integer: "NOT AN INT"
boolean: True
list: []
enum: 2
string: 1
number: "nan"
integer: 1.4
boolean: 0
list: "list?"
enum: False
map: 'hello'
3 changes: 3 additions & 0 deletions yamale/tests/fixtures/types_good_data.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ integer: 2
boolean: True
list: ['hi']
enum: 1
map:
hello: 1
another: "hi"
40 changes: 27 additions & 13 deletions yamale/tests/functional_tests.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from nose.tools import raises

from . import get_fixture
import yamale as sch
from .. import validators as val
Expand Down Expand Up @@ -34,7 +32,13 @@
'good': 'lists_good.yaml'
}

test_data = [types, nested, custom, keywords, lists]
maps = {
'schema': 'map.yaml',
'bad': 'map_bad.yaml',
'good': 'map_good.yaml'
}

test_data = [types, nested, custom, keywords, lists, maps]

for d in test_data:
for key in d.keys():
Expand Down Expand Up @@ -69,26 +73,36 @@ def good_gen(data_map):
sch.validate(data_map['schema'], data_map['good'])


@raises(ValueError)
def test_bad_validate():
sch.validate(types['schema'], types['bad'])
assert count_exception_lines(types['schema'], types['bad']) == 9


@raises(ValueError)
def test_bad_nested():
sch.validate(nested['schema'], nested['bad'])
assert count_exception_lines(nested['schema'], nested['bad']) == 3


@raises(ValueError)
def test_bad_custom():
assert sch.validate(custom['schema'], custom['bad'])
assert count_exception_lines(custom['schema'], custom['bad']) == 3


@raises(ValueError)
def test_bad_lists():
assert sch.validate(lists['schema'], lists['bad'])
assert count_exception_lines(lists['schema'], lists['bad']) == 4


def test_bad_maps():
assert count_exception_lines(maps['schema'], maps['bad']) == 6


@raises(ValueError)
def test_bad_keywords():
assert sch.validate(keywords['schema'], keywords['bad'])
assert count_exception_lines(keywords['schema'], keywords['bad']) == 6


def count_exception_lines(schema, data):
try:
sch.validate(schema, data)
except ValueError as exp:
count = len(exp.message.split('\n'))
print(exp.message)
print(count)
return count
raise Exception("Data valid")
4 changes: 4 additions & 0 deletions yamale/validators/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ class Map(Validator):
"""Map and dict validator"""
tag = 'map'

def __init__(self, *args, **kwargs):
super(Map, self).__init__(*args, **kwargs)
self.validators = [val for val in args if isinstance(val, Validator)]

def _is_valid(self, value):
return isinstance(value, Mapping)

Expand Down

0 comments on commit 3ad6fb2

Please sign in to comment.