Skip to content

Commit

Permalink
Disallow conversion when Unions are used as target. Version 0.6.1
Browse files Browse the repository at this point in the history
  • Loading branch information
Dobiasd committed Feb 14, 2019
1 parent 84425b1 commit 049bd37
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 4 deletions.
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setuptools.setup(
name="undictify",
version="0.6.0",
version="0.6.1",
author="Tobias Hermann",
author_email="[email protected]",
description="Type-checked function calls at runtime",
Expand Down
15 changes: 13 additions & 2 deletions undictify/_unpack.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ def _get_value(func: WrappedOrFunc[TypeT], value: Any, log_name: str,
return _get_dict_value(func, value, skip_superfluous, convert_types)

allowed_types = list(map(_unwrap_decorator_type, _get_union_types(func) \
if _is_optional_type(func) \
if _is_optional_type(func) or _is_union_type(func) \
else [func]))

if func is inspect.Parameter.empty and log_name != 'self':
Expand All @@ -208,6 +208,14 @@ def _get_value(func: WrappedOrFunc[TypeT], value: Any, log_name: str,
if convert_types:
if _is_optional_type(func):
func = _get_optional_type(func)
if _is_union_type(func):
raise TypeError(f'The convert flag must be set to False '
f'when Unions are used to avoid '
f'ambiguities.'
f'Thus {value} is not converted '
f'from type {_get_type_name(value_type)} '
f'into type {_get_type_name(func)} '
f'for key {log_name}.')
try:
if isinstance(value, str) and func is bool:
return _string_to_bool(value)
Expand Down Expand Up @@ -347,6 +355,9 @@ def _get_type_name(the_type: Callable[..., TypeT]) -> str:
"""Return a printable name of a type."""
if _is_optional_type(the_type):
return f'Optional[{str(_get_optional_type(the_type).__name__)}]'
if _is_union_type(the_type):
union_type_names = [t.__name__ for t in _get_union_types(the_type)]
return f'Union[{", ".join(union_type_names)}]'
if _is_list_type(the_type):
return f'List[{str(_get_list_type_elem_type(the_type).__name__)}]'
return the_type.__name__
Expand All @@ -369,7 +380,7 @@ def _isinstanceofone(value: Callable[..., TypeT], types: List[Callable[..., Type
if _isinstanceofone(value, _get_union_types(the_type)):
return True
try:
if _is_instance(value, the_type): # type: ignore
if type(value) == the_type: # pylint: disable=unidiomatic-typecheck
return True
except TypeError:
pass
Expand Down
15 changes: 14 additions & 1 deletion undictify/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -890,7 +890,20 @@ def test_ok(self) -> None:
**json.loads(object_repr))
self.assertEqual('hi', with_union.val)

def test_not_ok(self) -> None:
def test_not_ok_builtin(self) -> None:
"""Valid JSON string, but invalid target class."""
object_repr = '{"val": true}'
with self.assertRaises(TypeError):
type_checked_call()(WithUnionOfBuiltIns)(**json.loads(object_repr))

def test_convert_builtin(self) -> None:
"""Disallow conversion when Unions are used as target."""
object_repr = '{"val": true}'
with self.assertRaises(TypeError):
type_checked_call(convert=True)(WithUnionOfBuiltIns)(
**json.loads(object_repr))

def test_not_ok_class(self) -> None:
"""Valid JSON string, but invalid target class."""
object_repr = '{"val": "hi"}'
with self.assertRaises(TypeError):
Expand Down

0 comments on commit 049bd37

Please sign in to comment.