Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

optparse: Fix OptionParser types #13339

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 15 additions & 18 deletions stdlib/optparse.pyi
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import builtins
from _typeshed import Incomplete, MaybeNone
from _typeshed import Incomplete, MaybeNone, SupportsWrite
from abc import abstractmethod
from collections.abc import Callable, Iterable, Mapping, Sequence
from typing import IO, Any, AnyStr, ClassVar, Literal, NoReturn, overload
from typing import Any, ClassVar, Literal, NoReturn, overload
from typing_extensions import Self

__all__ = [
Expand Down Expand Up @@ -254,13 +254,13 @@ class OptionParser(OptionContainer):
def _add_version_option(self) -> None: ...
def _create_option_list(self) -> None: ...
def _get_all_options(self) -> list[Option]: ...
def _get_args(self, args: Iterable[Incomplete]) -> list[Incomplete]: ...
def _get_args(self, args: list[str] | None) -> list[str]: ...
def _init_parsing_state(self) -> None: ...
def _match_long_opt(self, opt: str) -> str: ...
def _populate_option_list(self, option_list: Iterable[Option], add_help: bool = True) -> None: ...
def _process_args(self, largs: list[Incomplete], rargs: list[Incomplete], values: Values) -> None: ...
def _process_long_opt(self, rargs: list[Incomplete], values) -> None: ...
def _process_short_opts(self, rargs: list[Incomplete], values) -> None: ...
def _populate_option_list(self, option_list: Iterable[Option] | None, add_help: bool = True) -> None: ...
def _process_args(self, largs: list[str], rargs: list[str], values: Values) -> None: ...
def _process_long_opt(self, rargs: list[str], values: Values) -> None: ...
def _process_short_opts(self, rargs: list[str], values: Values) -> None: ...
@overload
def add_option_group(self, opt_group: OptionGroup, /) -> OptionGroup: ...
@overload
Expand All @@ -279,14 +279,11 @@ class OptionParser(OptionContainer):
def get_prog_name(self) -> str: ...
def get_usage(self) -> str: ...
def get_version(self) -> str: ...
@overload
def parse_args(self, args: None = None, values: Values | None = None) -> tuple[Values, list[str]]: ...
@overload
def parse_args(self, args: Sequence[AnyStr], values: Values | None = None) -> tuple[Values, list[AnyStr]]: ...
Comment on lines -302 to -305
Copy link
Contributor Author

@hamdanal hamdanal Dec 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to explain the double change of parse_args's args annotation. Consider this snippet:

import optparse

parser = optparse.OptionParser()
parser.add_option("--target-dir", dest="target_dir", help="Target directory")
values, rest = parser.parse_args(["--target-dir", "/tmp"])
print(values, rest)

if you run it, it prints: {'target_dir': '/tmp'} []

  1. args is now annotated as a list instead of a sequence. If you make this change to the above snippet:
    - values, rest = parser.parse_args(["--target-dir", "/tmp"])
    + values, rest = parser.parse_args(("--target-dir", "/tmp"))
    you get: AttributeError: 'tuple' object has no attribute 'pop'
  2. args is now a list of strings, not AnyStr. If you make this change to the above snippet instead:
    - values, rest = parser.parse_args(["--target-dir", "/tmp"])
    + values, rest = parser.parse_args([b"--target-dir", b"/tmp"])
    you get: {'target_dir': None} [b'--target-dir', b'/tmp']
    This is not an error but it is clearly not what you intended as the program failed to parse the arguments in this case.

def print_usage(self, file: IO[str] | None = None) -> None: ...
def print_help(self, file: IO[str] | None = None) -> None: ...
def print_version(self, file: IO[str] | None = None) -> None: ...
def set_default(self, dest, value) -> None: ...
def set_defaults(self, **kwargs) -> None: ...
def set_process_default_values(self, process) -> None: ...
def set_usage(self, usage: str) -> None: ...
def parse_args(self, args: list[str] | None = None, values: Values | None = None) -> tuple[Values, list[str]]: ...
def print_usage(self, file: SupportsWrite[str] | None = None) -> None: ...
def print_help(self, file: SupportsWrite[str] | None = None) -> None: ...
def print_version(self, file: SupportsWrite[str] | None = None) -> None: ...
def set_default(self, dest: str, value: Any) -> None: ... # default value can be "any" type
def set_defaults(self, **kwargs: Any) -> None: ... # default values can be "any" type
def set_process_default_values(self, process: bool) -> None: ...
def set_usage(self, usage: str | None) -> None: ...
Loading