Skip to content

Commit 1ad0ca1

Browse files
chore: more test
1 parent 5177ccb commit 1ad0ca1

1 file changed

Lines changed: 94 additions & 19 deletions

File tree

tests/test_annotated.py

Lines changed: 94 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from pathlib import Path
1212
from typing import (
1313
Annotated,
14+
ClassVar,
1415
Literal,
1516
)
1617

@@ -297,17 +298,6 @@ def do_broken(self, cmd2_handler, name: 'NonExistentType'): # noqa: F821
297298
with pytest.raises(TypeError, match="Failed to resolve type hints"):
298299
_validate_base_command_params(do_broken)
299300

300-
def test_choices_provider_overrides_enum_choices(self) -> None:
301-
action = _get_param_action(_func_choices_provider_on_enum)
302-
assert action.choices is None
303-
assert action.get_choices_provider() is not None # type: ignore[attr-defined]
304-
assert action.get_completer() is None # type: ignore[attr-defined]
305-
306-
def test_completer_overrides_path_choices(self) -> None:
307-
action = _get_param_action(_func_completer_on_path)
308-
assert action.get_choices_provider() is None # type: ignore[attr-defined]
309-
assert action.get_completer() is cmd2.Cmd.path_complete # type: ignore[attr-defined]
310-
311301
def test_dest_param_raises(self) -> None:
312302
with pytest.raises(ValueError, match="dest"):
313303
build_parser_from_function(_func_dest_param)
@@ -331,13 +321,6 @@ def test_annotated_ambiguous_union_raises(self) -> None:
331321
with pytest.raises(TypeError, match="ambiguous"):
332322
_resolve_annotation(Annotated[str | int, Option("--name")])
333323

334-
def test_enum_choices_match_converted_type(self) -> None:
335-
"""Enum choices must be convertible by the type converter."""
336-
action = _get_param_action(_func_enum)
337-
converter = action.type
338-
for choice in action.choices:
339-
assert isinstance(converter(str(choice)), _Color)
340-
341324
def test_multi_param_order_and_presence(self) -> None:
342325
"""Positional order preserved, options generated correctly."""
343326
parser = build_parser_from_function(_func_multi)
@@ -347,6 +330,28 @@ def test_multi_param_order_and_presence(self) -> None:
347330
assert 'c' in dests
348331

349332

333+
class TestTypeInferenceBuildParser:
334+
"""Type-inference behavior and override precedence when building parser actions."""
335+
336+
def test_choices_provider_overrides_inferred_enum_choices(self) -> None:
337+
action = _get_param_action(_func_choices_provider_on_enum)
338+
assert action.choices is None
339+
assert action.get_choices_provider() is not None # type: ignore[attr-defined]
340+
assert action.get_completer() is None # type: ignore[attr-defined]
341+
342+
def test_completer_overrides_inferred_path_completion(self) -> None:
343+
action = _get_param_action(_func_completer_on_path)
344+
assert action.get_choices_provider() is None # type: ignore[attr-defined]
345+
assert action.get_completer() is cmd2.Cmd.path_complete # type: ignore[attr-defined]
346+
347+
def test_inferred_enum_choices_match_type_converter(self) -> None:
348+
"""Enum choices must be convertible by the type converter."""
349+
action = _get_param_action(_func_enum)
350+
converter = action.type
351+
for choice in action.choices:
352+
assert isinstance(converter(str(choice)), _Color)
353+
354+
350355
# ---------------------------------------------------------------------------
351356
# Argument groups and mutually exclusive groups
352357
# ---------------------------------------------------------------------------
@@ -954,20 +959,64 @@ class _InferColor(str, enum.Enum):
954959

955960

956961
class _RuntimeTypeInferenceApp(cmd2.Cmd):
962+
enum_override_choices: ClassVar[list[str]] = ["amber", "violet"]
963+
path_override_values: ClassVar[list[str]] = ["override-a", "override-b"]
964+
957965
path_parser = Cmd2ArgumentParser()
958966
path_parser.add_argument("filepath", type=Path)
959967

960968
@cmd2.with_argparser(path_parser)
961969
def do_read(self, args: argparse.Namespace) -> None:
962970
self.poutput(str(args.filepath))
963971

972+
native_path_parser = Cmd2ArgumentParser()
973+
native_path_parser.add_argument("filepath", type=type(Path(".")))
974+
975+
@cmd2.with_argparser(native_path_parser)
976+
def do_read_native(self, args: argparse.Namespace) -> None:
977+
self.poutput(str(args.filepath))
978+
964979
enum_parser = Cmd2ArgumentParser()
965980
enum_parser.add_argument("color", type=_InferColor)
966981

967982
@cmd2.with_argparser(enum_parser)
968983
def do_pick_color(self, args: argparse.Namespace) -> None:
969984
self.poutput(args.color.value)
970985

986+
def enum_choices_override(self) -> list[cmd2.CompletionItem]:
987+
return [cmd2.CompletionItem(value) for value in self.enum_override_choices]
988+
989+
enum_override_parser = Cmd2ArgumentParser()
990+
enum_override_parser.add_argument("color", type=_InferColor, choices_provider=enum_choices_override)
991+
992+
@cmd2.with_argparser(enum_override_parser)
993+
def do_pick_color_override(self, args: argparse.Namespace) -> None:
994+
self.poutput(str(args.color))
995+
996+
enum_converter_parser = Cmd2ArgumentParser()
997+
enum_converter_parser.add_argument("color", type=_make_enum_type(_InferColor))
998+
999+
@cmd2.with_argparser(enum_converter_parser)
1000+
def do_pick_color_converter(self, args: argparse.Namespace) -> None:
1001+
self.poutput(args.color.value)
1002+
1003+
bool_parser = Cmd2ArgumentParser()
1004+
bool_parser.add_argument("enabled", type=_parse_bool)
1005+
1006+
@cmd2.with_argparser(bool_parser)
1007+
def do_set_flag(self, args: argparse.Namespace) -> None:
1008+
self.poutput(str(args.enabled))
1009+
1010+
def path_completer_override(self, text: str, line: str, begidx: int, endidx: int) -> cmd2.Completions:
1011+
return self.basic_complete(text, line, begidx, endidx, self.path_override_values)
1012+
1013+
path_override_parser = Cmd2ArgumentParser()
1014+
path_override_parser.add_argument("filepath", type=Path, completer=path_completer_override)
1015+
1016+
@cmd2.with_argparser(path_override_parser)
1017+
def do_read_override(self, args: argparse.Namespace) -> None:
1018+
self.poutput(str(args.filepath))
1019+
9711020

9721021
@pytest.fixture
9731022
def infer_app() -> _RuntimeTypeInferenceApp:
@@ -976,10 +1025,15 @@ def infer_app() -> _RuntimeTypeInferenceApp:
9761025
return app
9771026

9781027

979-
class TestTypeInference:
1028+
class TestTypeInferenceCompletion:
1029+
"""Runtime completion tests for type-inferred argparse argument types."""
1030+
9801031
def test_enum_type_inference(self, infer_app) -> None:
9811032
assert sorted(_complete_cmd(infer_app, "pick_color ", "")) == ["green", "red"]
9821033

1034+
def test_enum_converter_type_inference(self, infer_app) -> None:
1035+
assert sorted(_complete_cmd(infer_app, "pick_color_converter ", "")) == ["green", "red"]
1036+
9831037
def test_path_type_inference(self, infer_app, tmp_path) -> None:
9841038
test_file = tmp_path / "testfile.txt"
9851039
test_file.touch()
@@ -988,6 +1042,27 @@ def test_path_type_inference(self, infer_app, tmp_path) -> None:
9881042
assert len(result_strings) > 0
9891043
assert any("testfile.txt" in item for item in result_strings)
9901044

1045+
def test_native_path_subclass_type_inference(self, infer_app, tmp_path) -> None:
1046+
test_file = tmp_path / "native-test.txt"
1047+
test_file.touch()
1048+
text = str(tmp_path) + "/"
1049+
result_strings = _complete_cmd(infer_app, f"read_native {text}", text)
1050+
assert len(result_strings) > 0
1051+
assert any("native-test.txt" in item for item in result_strings)
1052+
1053+
def test_bool_parser_type_inference(self, infer_app) -> None:
1054+
assert sorted(_complete_cmd(infer_app, "set_flag ", "")) == sorted(
1055+
["true", "false", "yes", "no", "on", "off", "1", "0"]
1056+
)
1057+
1058+
def test_choices_provider_takes_precedence_over_enum_inference(self, infer_app) -> None:
1059+
assert sorted(_complete_cmd(infer_app, "pick_color_override ", "")) == sorted(
1060+
_RuntimeTypeInferenceApp.enum_override_choices
1061+
)
1062+
1063+
def test_completer_takes_precedence_over_path_inference(self, infer_app) -> None:
1064+
assert sorted(_complete_cmd(infer_app, "read_override ", "")) == sorted(_RuntimeTypeInferenceApp.path_override_values)
1065+
9911066

9921067
class _AnnotatedCommandSet(cmd2.CommandSet):
9931068
def __init__(self) -> None:

0 commit comments

Comments
 (0)