diff --git a/stdlib/@tests/test_cases/check_argparse.py b/stdlib/@tests/test_cases/check_argparse.py new file mode 100644 index 000000000000..9b31abaf1e8b --- /dev/null +++ b/stdlib/@tests/test_cases/check_argparse.py @@ -0,0 +1,18 @@ +from argparse import ArgumentParser, FileType + +parser = ArgumentParser() + +parser.add_argument("--foo") +parser.add_argument("--foo", choices=[""]) +parser.add_argument("--foo", choices=[1, 2, 3]) # type: ignore + +parser.add_argument("--foo", type=int) +parser.add_argument("--foo", type=int, choices=[1, 2, 3]) +parser.add_argument("--foo", type=int, choices=[""]) # type: ignore + +parser.add_argument("--foo", type="") +parser.add_argument("--foo", type="", choices=[1, 2, 3]) +parser.add_argument("--foo", type="", choices=[""]) + +parser.add_argument("--foo", type=FileType()) +parser.add_argument("--foo", type=FileType(), choices=[""]) # type: ignore diff --git a/stdlib/argparse.pyi b/stdlib/argparse.pyi index 016920923af2..70afcf71b6d1 100644 --- a/stdlib/argparse.pyi +++ b/stdlib/argparse.pyi @@ -3,7 +3,7 @@ from _typeshed import sentinel from collections.abc import Callable, Generator, Iterable, Sequence from re import Pattern from typing import IO, Any, Generic, Literal, NewType, NoReturn, Protocol, TypeVar, overload -from typing_extensions import Self, TypeAlias, deprecated +from typing_extensions import Never, Self, TypeAlias, deprecated __all__ = [ "ArgumentParser", @@ -83,6 +83,11 @@ class _ActionsContainer: def _registry_get(self, registry_name: str, value: Any, default: Any = None) -> Any: ... def set_defaults(self, **kwargs: Any) -> None: ... def get_default(self, dest: str) -> Any: ... + # If `type` is `FileType`, supplying `choices` makes no sense. + # (This overload is provided for documentation's sake, as the next overload + # will match if `type` is `FileType` and `choices` is provided, although + # the possible choices are `IO[Any]`.) + @overload def add_argument( self, *name_or_flags: str, @@ -90,7 +95,25 @@ class _ActionsContainer: nargs: int | _NArgsStr | _SUPPRESS_T | None = None, const: Any = ..., default: Any = ..., - type: _ActionType = ..., + type: FileType, + choices: None = ..., + required: bool = ..., + help: str | None = ..., + metavar: str | tuple[str, ...] | None = ..., + dest: str | None = ..., + version: str = ..., + **kwargs: Any, + ) -> Action: ... + # If `type` is a callable, its return type is supplied to `choices`. + @overload + def add_argument( + self, + *name_or_flags: str, + action: _ActionStr | type[Action] = ..., + nargs: int | _NArgsStr | _SUPPRESS_T | None = None, + const: Any = ..., + default: Any = ..., + type: Callable[[str], _T], choices: Iterable[_T] | None = ..., required: bool = ..., help: str | None = ..., @@ -99,6 +122,42 @@ class _ActionsContainer: version: str = ..., **kwargs: Any, ) -> Action: ... + # If `type` is a string, the possible `choices` depend on `type`. + @overload + def add_argument( + self, + *name_or_flags: str, + action: _ActionStr | type[Action] = ..., + nargs: int | _NArgsStr | _SUPPRESS_T | None = None, + const: Any = ..., + default: Any = ..., + type: str, + choices: Iterable[Any] | None = ..., + required: bool = ..., + help: str | None = ..., + metavar: str | tuple[str, ...] | None = ..., + dest: str | None = ..., + version: str = ..., + **kwargs: Any, + ) -> Action: ... + # If `type` is not supplied, `choices` must be strings. + @overload + def add_argument( + self, + *name_or_flags: str, + action: _ActionStr | type[Action] = ..., + nargs: int | _NArgsStr | _SUPPRESS_T | None = None, + const: Any = ..., + default: Any = ..., + type: Never = ..., + choices: Iterable[str] | None = ..., + required: bool = ..., + help: str | None = ..., + metavar: str | tuple[str, ...] | None = ..., + dest: str | None = ..., + version: str = ..., + **kwargs: Any, + ) -> Action: ... def add_argument_group( self, title: str | None = None,