Skip to content

Commit

Permalink
Merge pull request #10 from patch/types
Browse files Browse the repository at this point in the history
Fixed a bug with default collection type hints
  • Loading branch information
eladrich authored Feb 10, 2022
2 parents 25fdce1 + 96d4385 commit 02767e9
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 8 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ The `pyrallis.field` behaves like the regular `dataclasses.field` with an additi


# TODOs:
- [ ] Fix error with default Dict and List
- [x] Fix error with default Dict and List
> Underlying error: No decoding function for type ~KT, consider using pyrallis.decode.register
- [x] Refine the `--help` command
> For example the `options` argument is confusing there
Expand Down
12 changes: 7 additions & 5 deletions pyrallis/parsers/decoding.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
is_union,
is_enum,
ParsingError,
format_error
format_error,
has_generic_arg
)

logger = getLogger(__name__)
Expand Down Expand Up @@ -130,30 +131,31 @@ def get_decoding_fn(t: Type[T]) -> Callable[[Any], T]:
elif is_dict(t):
logger.debug(f"Decoding a Dict field: {t}")
args = get_type_arguments(t)
if len(args) != 2:
if args is None or len(args) != 2 or has_generic_arg(args):
args = (Any, Any)
return decode_dict(*args)

elif is_set(t):
logger.debug(f"Decoding a Set field: {t}")
args = get_type_arguments(t)
if len(args) != 1:
if args is None or len(args) != 1 or has_generic_arg(args):
args = (Any,)
return decode_set(args[0])

elif is_tuple(t):
logger.debug(f"Decoding a Tuple field: {t}")
args = get_type_arguments(t)
if args is None:
args = []
return decode_tuple(*args)

elif is_list(t): # NOTE: Looks like can't be written with a dictionary
logger.debug(f"Decoding a List field: {t}")
args = get_type_arguments(t)
if not args:
if args is None or len(args) != 1 or has_generic_arg(args):
# Using a `List` or `list` annotation, so we don't know what do decode the
# items into!
args = (Any,)
assert len(args) == 1
decode_fn = decode_list(args[0])

return decode_fn
Expand Down
14 changes: 14 additions & 0 deletions pyrallis/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,20 @@ def format_error(e: Exception):
return f'Exception: {e}'


def is_generic_arg(arg):
try:
return arg.__name__ in ['KT', 'VT', 'T']
except Exception:
return False


def has_generic_arg(args):
for arg in args:
if is_generic_arg(arg):
return True
return False


CONFIG_ARG = 'config_path'

if __name__ == "__main__":
Expand Down
28 changes: 26 additions & 2 deletions tests/test_lists.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
from dataclasses import dataclass, field
from typing import *

import pytest

from .testutils import *


Expand Down Expand Up @@ -80,3 +78,29 @@ class SomeClass(TestSetup):
arguments = "--a " + format_list_using_brackets(value)
result = SomeClass.setup(arguments)
assert result == SomeClass(a=value)


@parametrize(
"item_type, type_hint, value, arg",
[
(list, List, [1, 2, 3], '[1, 2, 3]'),
(set, Set, {1, 2, 3}, '[1, 2, 3]'),
(tuple, Tuple, (1, 2, 3), '[1, 2, 3]'),
(dict, Dict, {1: 2}, '{1: 2}')
],
)
def test_collection_no_type(item_type, type_hint, value, arg):
@dataclass
class ContainerHint(TestSetup):
a: type_hint

c = ContainerHint.setup(f"--a '{arg}'")
assert c.a == value

@dataclass
class ContainerType(TestSetup):
a: item_type

c = ContainerType.setup(f"--a '{arg}'")
assert c.a == value

0 comments on commit 02767e9

Please sign in to comment.