Skip to content

Commit

Permalink
refactor: merge classes that handle Argument and Option creation
Browse files Browse the repository at this point in the history
  • Loading branch information
jnoortheen committed Nov 4, 2020
1 parent 351979f commit 023877d
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 74 deletions.
86 changes: 43 additions & 43 deletions arger/funcs.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# pylint: disable = protected-access

import argparse
import inspect
import typing as tp
Expand Down Expand Up @@ -30,6 +32,7 @@ def generate(self, param_name: str) -> tp.Iterator[str]:

class Argument:
flags: tp.Tuple[str, ...] = ()
kind: inspect._ParameterKind

def __init__(
self,
Expand Down Expand Up @@ -65,11 +68,23 @@ def __repr__(self):
"""helps during tests"""
return f"<{self.__class__.__name__}: {self.flags}, {repr(self.kwargs)}>"

def set_dest(self, name: str, typ: tp.Any):
self.flags = (name,)
self.update_type(typ)
def update(
self,
param: inspect.Parameter,
option_generator: tp.Optional[FlagsGenerator] = None,
):
self.kind = param.kind
self._update(param, option_generator)

def _update(
self,
param: inspect.Parameter,
_: tp.Optional[FlagsGenerator] = None,
):
self.flags = (param.name,)
self._update_type(param.annotation)

def update_type(self, typ: tp.Any):
def _update_type(self, typ: tp.Any):
"""Update type externally."""
if 'type' not in self.kwargs and typ is not _EMPTY:
self.kwargs['type'] = typ
Expand Down Expand Up @@ -99,17 +114,16 @@ def __init__(self, *flags: str, **kwargs):
super().__init__(**kwargs)
self.flags = flags

def set_flags(
def _update(
self,
name: str,
typ: tp.Any,
default: tp.Any = _EMPTY,
param: inspect.Parameter,
option_generator: tp.Optional[FlagsGenerator] = None,
):
self.kwargs.setdefault('dest', name)
self.kind = param.kind
self.kwargs.setdefault('dest', param.name)
if not self.flags and option_generator is not None:
self.flags = tuple(option_generator.generate(name))
self.update_default(typ, default)
self.flags = tuple(option_generator.generate(param.name))
self.update_default(param.annotation, param.default)

def update_default(self, typ: tp.Any, default: tp.Any = _EMPTY):
"""Update type and default externally"""
Expand All @@ -124,7 +138,7 @@ def update_default(self, typ: tp.Any, default: tp.Any = _EMPTY):
elif default is not None and typ is _EMPTY:
typ = type(default)

self.update_type(typ)
self._update_type(typ)


class ParsedFunc:
Expand All @@ -150,12 +164,7 @@ def __init__(self, func: tp.Optional[tp.Callable]):
self.args = OrderedDict()
for param in sign.parameters.values():
param_doc = docstr.params.get(param.name)
if param.default is param.empty:
self.args[param.name] = create_argument(param, param_doc)
else:
self.args[param.name] = create_option(
param, param_doc, option_generator
)
self.args[param.name] = create_argument(param, param_doc, option_generator)

def dispatch(self, ns: argparse.Namespace) -> tp.Any:
if self.fn:
Expand All @@ -175,35 +184,26 @@ def dispatch(self, ns: argparse.Namespace) -> tp.Any:
return None


def create_option(
def create_argument(
param: inspect.Parameter,
pdoc: tp.Optional[ParamDocTp],
option_generator: FlagsGenerator,
):
) -> Argument:
hlp = pdoc.doc if pdoc else ""
if isinstance(param.default, Option):
param.default.kwargs.setdefault('help', hlp)
param.default.set_flags(param.name, param.annotation, option_generator)
return param.default

if isinstance(param.default, Argument):
param.default.kwargs.setdefault('help', hlp)
param.default.set_dest(param.name, param.annotation)
return param.default

option = Option(help=hlp)
option.set_flags(param.name, param.annotation, param.default, option_generator)
return option


def create_argument(param: inspect.Parameter, doc: tp.Optional[ParamDocTp]) -> Argument:
kwargs = dict(help=doc.doc if doc else "")
if param.kind == inspect.Parameter.VAR_POSITIONAL:
kwargs.setdefault("nargs", "*")
if param.annotation is not _EMPTY:
kwargs.setdefault("type", param.annotation)
arg = Argument(**kwargs)
arg.set_dest(param.name, param.annotation)

if isinstance(param.default, (Argument, Option)):
arg = param.default
elif param.default is _EMPTY:
arg = Argument()
if param.kind == inspect.Parameter.VAR_POSITIONAL:
arg.kwargs.setdefault("nargs", "*")
if param.annotation is not _EMPTY:
arg.kwargs.setdefault("type", param.annotation)
else:
arg = Option()

arg.kwargs.setdefault('help', hlp)
arg.update(param, option_generator)
return arg


Expand Down
2 changes: 1 addition & 1 deletion arger/main.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# pylint: disable = W0212 ; protected member
# pylint: disable = protected-access

import argparse as ap
import copy
Expand Down
2 changes: 2 additions & 0 deletions arger/typing_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ def is_tuple(tp):


def cast(tp, val) -> Any:
# https://github.com/contains-io/typingplus/blob/master/typingplus.py
# for advanced casting one should use pydantic
origin = get_origin(tp)

if is_enum(origin):
Expand Down
36 changes: 18 additions & 18 deletions tests/test_args_opts/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,16 @@

import pytest

from arger import Arger, Argument, Option
from arger import Arger
from arger.docstring import ParamDocTp
from arger.funcs import _EMPTY, FlagsGenerator, create_argument, create_option
from arger.funcs import FlagsGenerator, create_argument


@pytest.fixture
def param_doc(hlp=''):
return ParamDocTp.init('', hlp)


@pytest.fixture
def parameter(name, tp):
return inspect.Parameter(
name,
inspect.Parameter.POSITIONAL_OR_KEYWORD,
annotation=tp,
)


@pytest.fixture
def argument(parameter, param_doc) -> Argument:
return create_argument(parameter, param_doc)


@pytest.fixture
def add_arger():
def _add(argument):
Expand All @@ -44,5 +30,19 @@ def gen_options():


@pytest.fixture
def option(parameter, param_doc, gen_options) -> Option:
return create_option(parameter, param_doc, gen_options)
def parameter(name, tp):
return inspect.Parameter(
name,
inspect.Parameter.POSITIONAL_OR_KEYWORD,
annotation=tp,
)


@pytest.fixture
def argument(parameter, param_doc, gen_options):
return create_argument(parameter, param_doc, gen_options)


@pytest.fixture
def parser(add_arger, argument):
return add_arger(argument)
5 changes: 0 additions & 5 deletions tests/test_args_opts/test_arguments.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@
import pytest


@pytest.fixture
def parser(add_arger, argument):
return add_arger(argument)


class Num(Enum):
one = '1. one'
two = '2. two'
Expand Down
9 changes: 2 additions & 7 deletions tests/test_args_opts/test_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,6 @@ def parameter(name, default):
)


@pytest.fixture
def parser(add_arger, option):
return add_arger(option)


@pytest.mark.parametrize(
'name, default, input, expected',
[
Expand All @@ -34,11 +29,11 @@ def parser(add_arger, option):
('a_list', [], '1 2 3', ['1', '2', '3']),
],
)
def test_options(parser, option, name, default, input, expected):
def test_options(parser, argument, name, default, input, expected):
# parse defaults
ns = parser.parse_args([])
assert getattr(ns, name) == default

# parses input
ns = parser.parse_args([option.flags[0]] + input.split())
ns = parser.parse_args([argument.flags[0]] + input.split())
assert getattr(ns, name) == expected

0 comments on commit 023877d

Please sign in to comment.