Skip to content

Commit

Permalink
Cleaner handling for unsupported type annotation
Browse files Browse the repository at this point in the history
    * In preparation for #8
  • Loading branch information
inno committed May 20, 2024
1 parent d803dc8 commit 1eb7b57
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 11 deletions.
38 changes: 29 additions & 9 deletions simplecli/simplecli.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ class DefaultIfBool:
pass


class UnsupportedType(TypeError):
pass


_wrapped = False
ValueType = Union[type[DefaultIfBool], type[Empty], bool, float, int, str]
ArgDict = dict[str, ValueType]
Expand All @@ -65,14 +69,6 @@ def __init__(self, *argv: Any, **kwargs: Any) -> None:
raise TypeError("needs 'name' argument")
kwargs["name"] = argv[0]
argv = ()
if kwargs["annotation"] not in get_args(ValueType):
if get_origin(kwargs["annotation"]) not in (Union, UnionType):
if kwargs["annotation"] is not Empty:
raise ValueError(
"annotation type "
f"'{type(kwargs['annotation']).__name__}' "
"is not currently supported!"
)
param_description = str(kwargs.pop("description", ""))
param_line = str(kwargs.pop("line", ""))
param_value = kwargs.pop("value", Empty)
Expand All @@ -90,6 +86,16 @@ def __init__(self, *argv: Any, **kwargs: Any) -> None:
# Overrides required as these values are generally unused
if not self.description:
self.parse_or_prepend(param_line)
annotation = kwargs["annotation"]
if annotation not in get_args(ValueType):
if get_origin(annotation) not in (Union, UnionType):
if annotation is not Empty:
pretty_annotation = (
annotation
if type(annotation) is type
else annotation.__class__.__name__
)
raise UnsupportedType(kwargs["name"], pretty_annotation)

def __eq__(self, other: object) -> bool:
if not isinstance(other, Param):
Expand Down Expand Up @@ -376,7 +382,21 @@ def wrap(func: Callable[..., Any]) -> Callable[..., Any]:
_wrapped = True
filename = sys.argv[0]
argv = sys.argv[1:]
params = extract_code_params(code=func)
try:
params = extract_code_params(code=func)
except UnsupportedType as e:
source = inspect.findsource(func)
offset = source[1] + 1
offset += [
index
for index, line in enumerate(source[0][source[1] :])
if re.search(rf"[\(\s]{e.args[0]}:", line)
][0]
exit(
f"File {'"' + filename + '"'}, line {offset}\n"
f"{source[0][offset - 1].rstrip()}\n"
f"UnsupportedType: {e.args[1]}"
)
pos_args, kw_args = clean_args(argv)
params.append(
Param("help", description="Show this message", internal_only=True)
Expand Down
4 changes: 2 additions & 2 deletions tests/test_param.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import annotations
import pytest
import re
from simplecli.simplecli import DefaultIfBool, Empty, Param
from simplecli.simplecli import DefaultIfBool, Empty, Param, UnsupportedType
from tests.utils import skip_if_uniontype_unsupported
from typing import Optional, Union

Expand Down Expand Up @@ -92,7 +92,7 @@ def test_set_value():
p2 = Param(name="testparam2", annotation=bool, default=True)
assert p2.value is True

with pytest.raises(ValueError, match="list"):
with pytest.raises(UnsupportedType, match="list"):
Param(name="testparam3", annotation=[str, bool])

p3 = Param(name="testparam3", annotation=str)
Expand Down
11 changes: 11 additions & 0 deletions tests/test_wrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,3 +266,14 @@ def code1(foo):
with pytest.raises(SystemExit, match="function parameters need type"):
code1.__globals__["__name__"] = "__main__"
simplecli.wrap(code1)


def test_wrap_unsupported_type(monkeypatch):
monkeypatch.setattr(sys, "argv", ["filename", "--foo"])

def code1(foo: [str, int]):
pass

with pytest.raises(SystemExit, match="UnsupportedType: list"):
code1.__globals__["__name__"] = "__main__"
simplecli.wrap(code1)

0 comments on commit 1eb7b57

Please sign in to comment.