Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Collapse error messages #224

Merged
merged 4 commits into from
Feb 11, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 93 additions & 6 deletions src/Nirum/Targets/Python.hs
Original file line number Diff line number Diff line change
Expand Up @@ -740,7 +740,7 @@ compileTypeDeclaration src d@TypeDeclaration { typename = typename'
insertStandardImport "typing"
insertThirdPartyImports
[ ("nirum.validate", ["validate_unboxed_type"])
, ("nirum.deserialize", ["deserialize_unboxed_type"])
, ("nirum.deserialize", ["deserialize_meta"])
]
pyVer <- getPythonVersion
return $ toStrict $ renderMarkup $ [compileText|
Expand Down Expand Up @@ -792,7 +792,13 @@ class #{className}(object):
%{ of Python3 }
def __nirum_deserialize__(cls: type, value: typing.Any) -> '#{className}':
%{ endcase }
return deserialize_unboxed_type(cls, value)
inner_type = cls.__nirum_get_inner_type__()
deserializer = getattr(inner_type, '__nirum_deserialize__', None)
if deserializer:
value = deserializer(value)
else:
value = deserialize_meta(inner_type, value)
return cls(value=value)

%{ case pyVer }
%{ of Python2 }
Expand Down Expand Up @@ -861,7 +867,7 @@ compileTypeDeclaration src d@TypeDeclaration { typename = typename'
(\ (n, t, _) -> [qq|'{n}': {t}|]) nameTypeTriples ",\n "
importTypingForPython3
insertThirdPartyImports [ ("nirum.validate", ["validate_record_type"])
, ("nirum.deserialize", ["deserialize_record_type"])
, ("nirum.deserialize", ["deserialize_meta"])
]
insertThirdPartyImportsA [ ( "nirum.constructs"
, [("name_dict_type", "NameDict")]
Expand Down Expand Up @@ -932,7 +938,38 @@ class $className(object):

@classmethod
def __nirum_deserialize__($clsType, value){ ret className }:
return deserialize_record_type(cls, value)
if '_type' not in value:
raise ValueError('"_type" field is missing.')
if not cls.__nirum_record_behind_name__ == value['_type']:
raise ValueError(
'%s expect "_type" equal to "%s"'
', but found %s.' % (
typing._type_repr(cls),
cls.__nirum_record_behind_name__,
value['_type']
)
)
args = dict()
behind_names = cls.__nirum_field_names__.behind_names
field_types = cls.__nirum_field_types__
if callable(field_types):
field_types = field_types()
# old compiler could generate non-callable dictionary
errors = set()
for attribute_name, item in value.items():
if attribute_name == '_type':
continue
if attribute_name in behind_names:
name = behind_names[attribute_name]
else:
name = attribute_name
try:
args[name] = deserialize_meta(field_types[name], item)
except ValueError as e:
errors.add('%s: %s' % (attribute_name, str(e)))
if errors:
raise ValueError('\\n'.join(sorted(errors)))
return cls(**args)

def __hash__(self){ret "int"}:
return hash(($hashText,))
Expand Down Expand Up @@ -983,7 +1020,7 @@ compileTypeDeclaration src
(\ (t, b) -> [qq|$t = '{b}'|]) enumMembers' "\n "
importTypingForPython3
insertStandardImport "enum"
insertThirdPartyImports [ ("nirum.deserialize", ["deserialize_union_type"])
insertThirdPartyImports [ ("nirum.deserialize", ["deserialize_meta"])
]
insertThirdPartyImportsA [ ( "nirum.constructs"
, [("name_dict_type", "NameDict")]
Expand Down Expand Up @@ -1025,7 +1062,57 @@ class $className({T.intercalate "," $ compileExtendClasses annotations}):
def __nirum_deserialize__(
{arg "cls" "type"}, value
){ ret className }:
return deserialize_union_type(cls, value)
if '_type' not in value:
raise ValueError('"_type" field is missing.')
if '_tag' not in value:
raise ValueError('"_tag" field is missing.')
if not hasattr(cls, '__nirum_tag__'):
for sub_cls in cls.__subclasses__():
if sub_cls.__nirum_tag__.value == value['_tag']:
cls = sub_cls
break
else:
raise ValueError(
'%r is not deserialzable tag of `%s`' % (
value, typing._type_repr(cls)
)
)
if not cls.__nirum_union_behind_name__ == value['_type']:
raise ValueError(
'%s expect "_type" equal to "%s", but found %s' % (
typing._type_repr(cls),
cls.__nirum_union_behind_name__,
value['_type']
)
)
if not cls.__nirum_tag__.value == value['_tag']:
raise ValueError(
'%s expect "_tag" equal to "%s", but found %s' % (
typing._type_repr(cls),
cls.__nirum_tag__.value,
cls
)
)
args = dict()
behind_names = cls.__nirum_tag_names__.behind_names
errors = set()
for attribute_name, item in value.items():
if attribute_name in ('_type', '_tag'):
continue
if attribute_name in behind_names:
name = behind_names[attribute_name]
else:
name = attribute_name
tag_types = cls.__nirum_tag_types__
if callable(tag_types): # old compiler could generate non-callable map
tag_types = dict(tag_types())
try:
args[name] = deserialize_meta(tag_types[name], item)
except ValueError as e:
errors.add('%s: %s' % (attribute_name, str(e)))
if errors:
raise ValueError('\\n'.join(sorted(errors)))
return cls(**args)


$tagCodes'
Expand Down
26 changes: 26 additions & 0 deletions test/python/primitive_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,14 @@ def test_record():
Point1.__nirum_deserialize__({'x': 3, 'top': 14})
with raises(ValueError):
Point1.__nirum_deserialize__({'_type': 'foo'})
with raises(ValueError) as e:
Point1.__nirum_deserialize__({'_type': 'point1',
'left': 'a',
'top': 'b'})
assert str(e.value) == '''\
left: invalid literal for int() with base 10: 'a'
top: invalid literal for int() with base 10: 'b'\
'''
with raises(TypeError):
Point1(left=1, top='a')
with raises(TypeError):
Expand Down Expand Up @@ -250,6 +258,24 @@ def test_union():
assert agnostic_name != CultureAgnosticName(fullname=u'wrong')
with raises(TypeError):
CultureAgnosticName(fullname=1)
name = MixedName.__nirum_deserialize__({
'_type': 'mixed_name',
'_tag': 'east_asian_name',
'family_name': u'foo',
'given_name': u'bar',
})
assert isinstance(name, MixedName.EastAsianName)
with raises(ValueError) as e:
MixedName.__nirum_deserialize__({
'_type': 'mixed_name',
'_tag': 'east_asian_name',
'family_name': 404,
'given_name': 503,
})
assert str(e.value) == '''\
family_name: '404' is not a string.
given_name: '503' is not a string.\
'''


def test_union_with_special_case():
Expand Down