Skip to content

Commit

Permalink
Merge pull request #65 from smarie/dev
Browse files Browse the repository at this point in the history
Correct handling of None in TupleNode + Extended support from typing.List to typing.Iterable
  • Loading branch information
RussBaz committed Mar 7, 2018
2 parents b5a8899 + 4ae2ea7 commit 9a1b230
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 28 deletions.
40 changes: 31 additions & 9 deletions enforce/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,12 @@ def __init__(self, variable_length=False, **kwargs):
super().__init__(typing.Tuple, is_sequence=True, is_container=True, **kwargs)

def validate_data(self, validator, data, sticky=False):
# fix for https://github.com/RussBaz/enforce/issues/62 (1/4)
if data is None:
# unfortunately this is not enough to stop propagating to children...
# ... so the None case is also handled in map_data and validate_children
return ValidationResult(valid=False, data=data, type_name=extract_type_name(type(data)))

covariant = self.covariant or validator.settings.covariant
contravariant = self.contravariant or validator.settings.contravariant

Expand All @@ -341,7 +347,11 @@ def validate_data(self, validator, data, sticky=False):
return ValidationResult(valid=False, data=data, type_name=extract_type_name(input_type))

def validate_children(self, validator, propagated_data):
if self.variable_length:
# fix for https://github.com/RussBaz/enforce/issues/62 (3/4)
if propagated_data is None:
# yield a sequence of one element: a single failure
yield [ValidationResult(valid=False, data=propagated_data, type_name=extract_type_name(propagated_data))]
elif self.variable_length:
child = self.children[0]

children_validation_results = []
Expand All @@ -356,10 +366,14 @@ def validate_children(self, validator, propagated_data):

def map_data(self, validator, self_validation_result):
data = self_validation_result.data
output = []
for element in data:
output.append(element)
return output
# fix for https://github.com/RussBaz/enforce/issues/62 (2/4)
if data is not None:
output = []
for element in data:
output.append(element)
return output
else:
return None

def reduce_data(self, validator, child_validation_results, self_validation_result):
return tuple(result.data for result in child_validation_results)
Expand All @@ -369,12 +383,20 @@ def get_actual_data_type(self, self_validation_result, child_validation_results,
Returns a name of an actual type of given data
"""
actual_type = self_validation_result.type_name
child_types = list(result.type_name for result in child_validation_results) or []

actual_type = TYPE_NAME_ALIASES.get(actual_type, actual_type)
# fix for https://github.com/RussBaz/enforce/issues/62 (4/4)
if actual_type == extract_type_name(type(None)):
# None - No need to check children
actual_type = TYPE_NAME_ALIASES.get(actual_type, actual_type)

if child_types:
actual_type = actual_type + '[' + ', '.join(child_types) + ']'
else:
# Append children type information
child_types = list(result.type_name for result in child_validation_results) or []

actual_type = TYPE_NAME_ALIASES.get(actual_type, actual_type)

if child_types:
actual_type = actual_type + '[' + ', '.join(child_types) + ']'

return actual_type

Expand Down
42 changes: 23 additions & 19 deletions enforce/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,12 +137,15 @@ def _parse_bytes(node, hint, validator, parsers):


def _parse_generic(node, hint, validator, parsers):
if issubclass(hint, typing.List):
yield _parse_list(node, hint, validator, parsers)
elif issubclass(hint, typing.Mapping):
yield _parse_dict(node, hint, validator, parsers)
elif issubclass(hint, typing.Set):
yield _parse_set(node, hint, validator, parsers)
# Fixes https://github.com/RussBaz/enforce/issues/{47, 51, 52}
# Mapping need to be checked BEFORE Iterable (since a Mapping is an Iterable)
if issubclass(hint, typing.Mapping):
yield _parse_mapping(node, hint, validator, parsers)
elif issubclass(hint, typing.Iterable):
yield _parse_iterable(node, hint, validator, parsers)
# Not needed anymore: a Set is just an Iterable
# elif issubclass(hint, typing.Set):
# yield _parse_set(node, hint, validator, parsers)
else:
new_node = yield nodes.GenericNode(
hint,
Expand All @@ -153,31 +156,32 @@ def _parse_generic(node, hint, validator, parsers):
yield _yield_parsing_result(node, new_node)


def _parse_list(node, hint, validator, parsers):
def _parse_iterable(node, hint, validator, parsers):
new_node = yield nodes.SimpleNode(hint.__extra__)
validator.all_nodes.append(new_node)

# add its type as child
# We need to index first element only as Lists always have 1 argument
# We need to index first element only as Iterable always have 1 argument
if hint.__args__:
yield get_parser(new_node, hint.__args__[0], validator, parsers)

yield _yield_parsing_result(node, new_node)


def _parse_set(node, hint, validator, parsers):
new_node = yield nodes.SimpleNode(hint.__extra__)
validator.all_nodes.append(new_node)

# add its type as child
# We need to index first element only as Sets always have 1 argument
if hint.__args__:
yield get_parser(new_node, hint.__args__[0], validator, parsers)

yield _yield_parsing_result(node, new_node)
# -- Not useful anymore: a Set is an Iterable
# def _parse_set(node, hint, validator, parsers):
# new_node = yield nodes.SimpleNode(hint.__extra__)
# validator.all_nodes.append(new_node)
#
# # add its type as child
# # We need to index first element only as Sets always have 1 argument
# if hint.__args__:
# yield get_parser(new_node, hint.__args__[0], validator, parsers)
#
# yield _yield_parsing_result(node, new_node)


def _parse_dict(node, hint, validator, parsers):
def _parse_mapping(node, hint, validator, parsers):
hint_args = hint.__args__

if hint_args:
Expand Down

0 comments on commit 9a1b230

Please sign in to comment.