1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
|
import itertools
from typing import Annotated, Sequence
from unittest.mock import Mock
import pytest
import cyclopts.group
from cyclopts import App, Group, Parameter, Token
from cyclopts.exceptions import ValidationError
from cyclopts.group import sort_groups
def upper(type_, tokens: Sequence[Token]):
return tokens[0].value.upper()
def test_group_show_property():
assert Group().show is False
assert Group("Foo").show is True
assert Group("Foo", show=False).show is False
def test_group_default_parameter_converter(app, assert_parse_args):
food_group = Group("Food", default_parameter=Parameter(converter=upper))
@app.default
def foo(ice_cream: Annotated[str, Parameter(group=food_group)]):
pass
assert_parse_args(foo, "chocolate", "CHOCOLATE")
def test_command_validator(app, assert_parse_args):
def bar_must_be_1(bar):
if bar == 1:
return
raise ValueError
@app.command(validator=bar_must_be_1)
def foo(bar: int):
pass
assert_parse_args(foo, "foo 1", bar=1)
with pytest.raises(ValidationError) as e:
app("foo 2", exit_on_error=False)
assert str(e.value) == "Invalid values for command 'foo'."
def test_command_validator_with_message(app, assert_parse_args):
def bar_must_be_1(bar):
if bar == 1:
return
raise ValueError("The value 'bar' must be 1.")
@app.command(validator=bar_must_be_1)
def foo(bar: int):
pass
assert_parse_args(foo, "foo 1", bar=1)
with pytest.raises(ValidationError) as e:
app("foo 2", exit_on_error=False)
assert str(e.value) == "Invalid values for command 'foo'. The value 'bar' must be 1."
def test_group_command_default_parameter_resolution(app):
app_validator = Mock()
sub_app_validator = Mock()
command_validator = Mock()
command_group_validator = Mock()
app.validator = app_validator
app.command(bar := App("bar", validator=sub_app_validator))
@bar.command(validator=command_validator, group=Group("Test", validator=command_group_validator))
def cmd(foo=5):
return 100
assert 100 == app("bar cmd")
app_validator.assert_not_called()
command_group_validator.assert_called_once()
sub_app_validator.assert_not_called()
command_validator.assert_called_once()
def test_group_default_parameter_validator(app):
validator = Mock()
food_group = Group("Food", default_parameter=Parameter(validator=validator))
@app.default
def foo(ice_cream: Annotated[str, Parameter(group=food_group)]):
pass
app.parse_args("chocolate")
validator.assert_called_once()
def test_group_validator(app):
validator = Mock()
group = Group("Spices", validator=validator)
@app.default
def foo(
salt: Annotated[bool, Parameter("--rock-salt", group=group)] = False,
pepper: Annotated[bool, Parameter("--peppercorn", group=group)] = False,
ketchup: bool = False,
):
pass
app.parse_args("--rock-salt --peppercorn --ketchup")
validator.assert_called_once()
provided_arguments = validator.call_args_list[0][0][0]
assert len(provided_arguments) == 2
assert provided_arguments[0].name == "--rock-salt"
assert provided_arguments[1].name == "--peppercorn"
def test_group_sort_key_property():
assert Group().sort_key is None
assert Group()._sort_key is cyclopts.group.UNSET
g = Group(sort_key=1)
assert g.sort_key == 1
@pytest.fixture
def mock_sort_key_counter(mocker):
mock = mocker.patch("cyclopts.group._sort_key_counter")
mock.__next__.side_effect = itertools.count()
return mock
def test_group_sorted_classmethod_basic(mock_sort_key_counter):
g4 = Group("unsorted group")
g1 = Group.create_ordered("foo")
g2 = Group.create_ordered("bar")
g3 = Group.create_ordered("baz", sort_key="non-int value")
assert g1.sort_key == (cyclopts.group.UNSET, 0)
assert g2.sort_key == (cyclopts.group.UNSET, 1)
assert g3.sort_key == ("non-int value", 2)
assert g4.sort_key is None
res = sort_groups([g1, g2, g3, g4], ["a", "b", "c", "d"])
assert ([g3, g1, g2, g4], ["c", "a", "b", "d"]) == res
def test_group_sorted_classmethod_tuple(mock_sort_key_counter):
g1 = Group.create_ordered("foo1", sort_key=("tuple", 7))
g2 = Group.create_ordered("foo2", sort_key=lambda x: ("tuple", 5))
def f_tuple_str(x):
return "tuple"
g3 = Group.create_ordered("foo3", sort_key=(f_tuple_str, 3))
g4 = Group.create_ordered("foo4", sort_key=("tuple", 3))
res = sort_groups([g1, g2, g3, g4], ["a", "b", "c", "d"])
assert ([g3, g4, g2, g1], ["c", "d", "b", "a"]) == res
|