Skip to content

Commit

Permalink
feat: add support for more complex types
Browse files Browse the repository at this point in the history
  • Loading branch information
jnoortheen committed Apr 17, 2020
1 parent 214cc2b commit 584e530
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 8 deletions.
6 changes: 3 additions & 3 deletions arger/arger.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
from argparse import ArgumentParser
from typing import Any, Callable, Dict, Optional

from .parser.classes import Option
from .structs import Command
from .types import F
from arger.parser.classes import Option
from arger.structs import Command
from arger.types import F


def _add_args(parser, args: Dict[str, Option]):
Expand Down
9 changes: 6 additions & 3 deletions arger/parser/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,13 @@ def __init__(self, *args, **kwargs):
if match_types(typ, list):
origin = inner_type
else: # tuple
# origin = inner_type[0] if len(set(inner_type)) == 1 else str
origin = str
self.container_type = typ
self.container_type = lambda x: tuple(x)
if isclass(origin) and issubclass(origin, Enum):
kwargs.setdefault("choices", [e.name for e in typ])
origin = str
self.container_type = lambda x: typ[x]

kwargs["type"] = origin
super().__init__(*args, **kwargs)
Expand All @@ -43,8 +46,8 @@ def __call__(self, parser, namespace, values, option_string=None):
# items = _copy_items(items)
# items.append(self.const)
# setattr(namespace, self.dest, items)
if self.container_type and match_types(self.container_type, tuple):
values = tuple(values)
if self.container_type:
values = self.container_type(values)
setattr(namespace, self.dest, values)


Expand Down
9 changes: 9 additions & 0 deletions arger/parser/classes.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import argparse
import inspect
from typing import Any, List, Optional

from arger.parser.utils import generate_flags
Expand Down Expand Up @@ -75,7 +76,15 @@ def __init__(
def add(self, parser: argparse.ArgumentParser):
return parser.add_argument(*self.flags, **self.kwargs)

@property
def _kwargs_repr_(self):
return {
k: f"`{val.__name__}" if inspect.isclass(val) else val
for k, val in self.kwargs.items()
}

def __repr__(self):
"""helps during tests"""
return f"{self.__class__.__name__}: {self.flags}, {repr(self.kwargs)}"

def set_flags(self, option_generator, name: str):
Expand Down
4 changes: 2 additions & 2 deletions arger/types.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from typing import Any, Callable, TypeVar


F = TypeVar("F", bound=Callable[..., Any])
F = TypeVar("F", bound=Callable[..., Any]) # decorator

UNDEFINED = object()
UNDEFINED = object() # singleton
"""sometimes the value could be None. we need this to distinguish such values."""


Expand Down
52 changes: 52 additions & 0 deletions tests/test_arguments.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from decimal import Decimal
from typing import List, Tuple

import pytest

from arger import Arger
from arger.parser.classes import Argument
from arger.parser.funcs import Param


@pytest.fixture
def argument(name, tp, hlp='') -> Argument:
return Argument(**(Param(name, tp, hlp))._asdict())


@pytest.fixture
def arger(argument):
arg = Arger()
argument.add(arg)
return arg


@pytest.mark.parametrize(
'name, tp, input, expected',
[
# simple types
('an_int', int, '20', 20),
('a_float', float, '25', 25.0),
('a_deci', Decimal, '25', 25.0),
('a_cmplx', complex, '4+8j', 4 + 8j),
('a_str', str, 'new-str', 'new-str'),
('bool', bool, '--bool', True),
('bool', False, '--no-bool', False),
# container types
('a_tuple', tuple, '1 2 3', ('1', '2', '3')),
('a_tuple', Tuple[int, ...], '1 2 3', (1, 2, 3)),
('a_tuple', Tuple[str, ...], '1 2 3', ('1', '2', '3')),
('a_tuple', Tuple[float, ...], '1 2 3', (1.0, 2.0, 3.0)),
('a_tuple', Tuple[str, int], '1 2', ('1', 2)),
('a_tuple', Tuple[int, float, Decimal, complex, str], '1 2', ('1', 2)),
('a_list', list, '1 2 3', ['1', '2', '3']),
('a_list', List, '1 2 3', ['1', '2', '3']),
('a_list', List[str], '1 2 3', ['1', '2', '3']),
('a_list', List[int], '1 2 3', [1, 2, 3]),
('a_list', List[Decimal], '1 2 3', [Decimal(1), Decimal(2), Decimal(3)]),
('a_list', set, '1 2 3', {'1', '2', '3'}),
],
)
def test_arguments(arger, argument, input, expected, name):
# parses input
ns = arger.parse_args(input.split())
assert getattr(ns, name) == expected

0 comments on commit 584e530

Please sign in to comment.