diff options
author | Martijn Pieters <mj@zopatista.com> | 2022-11-08 19:44:29 +0000 |
---|---|---|
committer | David Lord <davidism@gmail.com> | 2023-01-19 16:33:27 -0800 |
commit | a1093bbe0dae00eea8342247a0c2739b07a6acd8 (patch) | |
tree | 25c1680557687164f30ff08c5fa0e1e1693fac66 | |
parent | a6c7ee060b02eaa62fd15264a669220914cfad4c (diff) | |
download | click-a1093bbe0dae00eea8342247a0c2739b07a6acd8.tar.gz |
Types: don't leave generic types without a parameter
Enable `disallow_any_generics` and provide type information for missing parameters for type hints.
-rw-r--r-- | setup.cfg | 1 | ||||
-rw-r--r-- | src/click/_compat.py | 36 | ||||
-rw-r--r-- | src/click/_termui_impl.py | 2 | ||||
-rw-r--r-- | src/click/core.py | 10 | ||||
-rw-r--r-- | src/click/decorators.py | 2 | ||||
-rw-r--r-- | src/click/exceptions.py | 4 | ||||
-rw-r--r-- | src/click/testing.py | 12 | ||||
-rw-r--r-- | src/click/types.py | 10 | ||||
-rw-r--r-- | src/click/utils.py | 18 |
9 files changed, 49 insertions, 46 deletions
@@ -85,6 +85,7 @@ disallow_subclassing_any = True disallow_untyped_calls = True disallow_untyped_defs = True disallow_incomplete_defs = True +disallow_any_generics = True check_untyped_defs = True no_implicit_optional = True local_partial_types = True diff --git a/src/click/_compat.py b/src/click/_compat.py index 766d286..57faa91 100644 --- a/src/click/_compat.py +++ b/src/click/_compat.py @@ -50,7 +50,7 @@ def is_ascii_encoding(encoding: str) -> bool: return False -def get_best_encoding(stream: t.IO) -> str: +def get_best_encoding(stream: t.IO[t.Any]) -> str: """Returns the default stream encoding if not found.""" rv = getattr(stream, "encoding", None) or sys.getdefaultencoding() if is_ascii_encoding(rv): @@ -153,7 +153,7 @@ class _FixupStream: return True -def _is_binary_reader(stream: t.IO, default: bool = False) -> bool: +def _is_binary_reader(stream: t.IO[t.Any], default: bool = False) -> bool: try: return isinstance(stream.read(0), bytes) except Exception: @@ -162,7 +162,7 @@ def _is_binary_reader(stream: t.IO, default: bool = False) -> bool: # closed. In this case, we assume the default. -def _is_binary_writer(stream: t.IO, default: bool = False) -> bool: +def _is_binary_writer(stream: t.IO[t.Any], default: bool = False) -> bool: try: stream.write(b"") except Exception: @@ -175,7 +175,7 @@ def _is_binary_writer(stream: t.IO, default: bool = False) -> bool: return True -def _find_binary_reader(stream: t.IO) -> t.Optional[t.BinaryIO]: +def _find_binary_reader(stream: t.IO[t.Any]) -> t.Optional[t.BinaryIO]: # We need to figure out if the given stream is already binary. # This can happen because the official docs recommend detaching # the streams to get binary streams. Some code might do this, so @@ -193,7 +193,7 @@ def _find_binary_reader(stream: t.IO) -> t.Optional[t.BinaryIO]: return None -def _find_binary_writer(stream: t.IO) -> t.Optional[t.BinaryIO]: +def _find_binary_writer(stream: t.IO[t.Any]) -> t.Optional[t.BinaryIO]: # We need to figure out if the given stream is already binary. # This can happen because the official docs recommend detaching # the streams to get binary streams. Some code might do this, so @@ -241,11 +241,11 @@ def _is_compatible_text_stream( def _force_correct_text_stream( - text_stream: t.IO, + text_stream: t.IO[t.Any], encoding: t.Optional[str], errors: t.Optional[str], - is_binary: t.Callable[[t.IO, bool], bool], - find_binary: t.Callable[[t.IO], t.Optional[t.BinaryIO]], + is_binary: t.Callable[[t.IO[t.Any], bool], bool], + find_binary: t.Callable[[t.IO[t.Any]], t.Optional[t.BinaryIO]], force_readable: bool = False, force_writable: bool = False, ) -> t.TextIO: @@ -287,7 +287,7 @@ def _force_correct_text_stream( def _force_correct_text_reader( - text_reader: t.IO, + text_reader: t.IO[t.Any], encoding: t.Optional[str], errors: t.Optional[str], force_readable: bool = False, @@ -303,7 +303,7 @@ def _force_correct_text_reader( def _force_correct_text_writer( - text_writer: t.IO, + text_writer: t.IO[t.Any], encoding: t.Optional[str], errors: t.Optional[str], force_writable: bool = False, @@ -367,11 +367,11 @@ def get_text_stderr( def _wrap_io_open( - file: t.Union[str, os.PathLike, int], + file: t.Union[str, "os.PathLike[t.AnyStr]", int], mode: str, encoding: t.Optional[str], errors: t.Optional[str], -) -> t.IO: +) -> t.IO[t.Any]: """Handles not passing ``encoding`` and ``errors`` in binary mode.""" if "b" in mode: return open(file, mode) @@ -385,7 +385,7 @@ def open_stream( encoding: t.Optional[str] = None, errors: t.Optional[str] = "strict", atomic: bool = False, -) -> t.Tuple[t.IO, bool]: +) -> t.Tuple[t.IO[t.Any], bool]: binary = "b" in mode # Standard streams first. These are simple because they ignore the @@ -456,11 +456,11 @@ def open_stream( f = _wrap_io_open(fd, mode, encoding, errors) af = _AtomicFile(f, tmp_filename, os.path.realpath(filename)) - return t.cast(t.IO, af), True + return t.cast(t.IO[t.Any], af), True class _AtomicFile: - def __init__(self, f: t.IO, tmp_filename: str, real_filename: str) -> None: + def __init__(self, f: t.IO[t.Any], tmp_filename: str, real_filename: str) -> None: self._f = f self._tmp_filename = tmp_filename self._real_filename = real_filename @@ -494,7 +494,7 @@ def strip_ansi(value: str) -> str: return _ansi_re.sub("", value) -def _is_jupyter_kernel_output(stream: t.IO) -> bool: +def _is_jupyter_kernel_output(stream: t.IO[t.Any]) -> bool: while isinstance(stream, (_FixupStream, _NonClosingTextIOWrapper)): stream = stream._stream @@ -502,7 +502,7 @@ def _is_jupyter_kernel_output(stream: t.IO) -> bool: def should_strip_ansi( - stream: t.Optional[t.IO] = None, color: t.Optional[bool] = None + stream: t.Optional[t.IO[t.Any]] = None, color: t.Optional[bool] = None ) -> bool: if color is None: if stream is None: @@ -576,7 +576,7 @@ def term_len(x: str) -> int: return len(strip_ansi(x)) -def isatty(stream: t.IO) -> bool: +def isatty(stream: t.IO[t.Any]) -> bool: try: return stream.isatty() except Exception: diff --git a/src/click/_termui_impl.py b/src/click/_termui_impl.py index 4b979bc..1caaad8 100644 --- a/src/click/_termui_impl.py +++ b/src/click/_termui_impl.py @@ -93,7 +93,7 @@ class ProgressBar(t.Generic[V]): self.is_hidden = not isatty(self.file) self._last_line: t.Optional[str] = None - def __enter__(self) -> "ProgressBar": + def __enter__(self) -> "ProgressBar[V]": self.entered = True self.render_progress() return self diff --git a/src/click/core.py b/src/click/core.py index 5abfb0f..9aef380 100644 --- a/src/click/core.py +++ b/src/click/core.py @@ -1841,7 +1841,7 @@ class Group(MultiCommand): if self.command_class and kwargs.get("cls") is None: kwargs["cls"] = self.command_class - func: t.Optional[t.Callable] = None + func: t.Optional[t.Callable[..., t.Any]] = None if args and callable(args[0]): assert ( @@ -1889,7 +1889,7 @@ class Group(MultiCommand): """ from .decorators import group - func: t.Optional[t.Callable] = None + func: t.Optional[t.Callable[..., t.Any]] = None if args and callable(args[0]): assert ( @@ -2260,7 +2260,7 @@ class Parameter: if value is None: return () if self.multiple or self.nargs == -1 else None - def check_iter(value: t.Any) -> t.Iterator: + def check_iter(value: t.Any) -> t.Iterator[t.Any]: try: return _check_iter(value) except TypeError: @@ -2277,12 +2277,12 @@ class Parameter: ) elif self.nargs == -1: - def convert(value: t.Any) -> t.Tuple: + def convert(value: t.Any) -> t.Tuple[t.Any, ...]: return tuple(self.type(x, self, ctx) for x in check_iter(value)) else: # nargs > 1 - def convert(value: t.Any) -> t.Tuple: + def convert(value: t.Any) -> t.Tuple[t.Any, ...]: value = tuple(check_iter(value)) if len(value) != self.nargs: diff --git a/src/click/decorators.py b/src/click/decorators.py index 28618dc..4f7ecbb 100644 --- a/src/click/decorators.py +++ b/src/click/decorators.py @@ -41,7 +41,7 @@ def pass_obj(f: F) -> F: def make_pass_decorator( - object_type: t.Type, ensure: bool = False + object_type: t.Type[t.Any], ensure: bool = False ) -> "t.Callable[[F], F]": """Given an object type this creates a decorator that will work similar to :func:`pass_obj` but instead of passing the object of the diff --git a/src/click/exceptions.py b/src/click/exceptions.py index 9e20b3e..59b18c6 100644 --- a/src/click/exceptions.py +++ b/src/click/exceptions.py @@ -36,7 +36,7 @@ class ClickException(Exception): def __str__(self) -> str: return self.message - def show(self, file: t.Optional[t.IO] = None) -> None: + def show(self, file: t.Optional[t.IO[t.Any]] = None) -> None: if file is None: file = get_text_stderr() @@ -59,7 +59,7 @@ class UsageError(ClickException): self.ctx = ctx self.cmd = self.ctx.command if self.ctx else None - def show(self, file: t.Optional[t.IO] = None) -> None: + def show(self, file: t.Optional[t.IO[t.Any]] = None) -> None: if file is None: file = get_text_stderr() color = None diff --git a/src/click/testing.py b/src/click/testing.py index 244d326..7b6dd7f 100644 --- a/src/click/testing.py +++ b/src/click/testing.py @@ -79,11 +79,11 @@ class _NamedTextIOWrapper(io.TextIOWrapper): def make_input_stream( - input: t.Optional[t.Union[str, bytes, t.IO]], charset: str + input: t.Optional[t.Union[str, bytes, t.IO[t.Any]]], charset: str ) -> t.BinaryIO: # Is already an input stream. if hasattr(input, "read"): - rv = _find_binary_reader(t.cast(t.IO, input)) + rv = _find_binary_reader(t.cast(t.IO[t.Any], input)) if rv is not None: return rv @@ -206,7 +206,7 @@ class CliRunner: @contextlib.contextmanager def isolation( self, - input: t.Optional[t.Union[str, bytes, t.IO]] = None, + input: t.Optional[t.Union[str, bytes, t.IO[t.Any]]] = None, env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, color: bool = False, ) -> t.Iterator[t.Tuple[io.BytesIO, t.Optional[io.BytesIO]]]: @@ -301,7 +301,7 @@ class CliRunner: default_color = color def should_strip_ansi( - stream: t.Optional[t.IO] = None, color: t.Optional[bool] = None + stream: t.Optional[t.IO[t.Any]] = None, color: t.Optional[bool] = None ) -> bool: if color is None: return not default_color @@ -350,7 +350,7 @@ class CliRunner: self, cli: "BaseCommand", args: t.Optional[t.Union[str, t.Sequence[str]]] = None, - input: t.Optional[t.Union[str, bytes, t.IO]] = None, + input: t.Optional[t.Union[str, bytes, t.IO[t.Any]]] = None, env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, catch_exceptions: bool = True, color: bool = False, @@ -449,7 +449,7 @@ class CliRunner: @contextlib.contextmanager def isolated_filesystem( - self, temp_dir: t.Optional[t.Union[str, os.PathLike]] = None + self, temp_dir: t.Optional[t.Union[str, "os.PathLike[str]"]] = None ) -> t.Iterator[str]: """A context manager that creates a temporary directory and changes the current working directory to it. This isolates tests diff --git a/src/click/types.py b/src/click/types.py index d948c70..1b04e37 100644 --- a/src/click/types.py +++ b/src/click/types.py @@ -397,7 +397,7 @@ class DateTime(ParamType): class _NumberParamTypeBase(ParamType): - _number_class: t.ClassVar[t.Type] + _number_class: t.ClassVar[t.Type[t.Any]] def convert( self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] @@ -702,8 +702,8 @@ class File(ParamType): lazy = self.resolve_lazy_flag(value) if lazy: - f: t.IO = t.cast( - t.IO, + f: t.IO[t.Any] = t.cast( + t.IO[t.Any], LazyFile( value, self.mode, self.encoding, self.errors, atomic=self.atomic ), @@ -794,7 +794,7 @@ class Path(ParamType): readable: bool = True, resolve_path: bool = False, allow_dash: bool = False, - path_type: t.Optional[t.Type] = None, + path_type: t.Optional[t.Type[t.Any]] = None, executable: bool = False, ): self.exists = exists @@ -944,7 +944,7 @@ class Tuple(CompositeParamType): :param types: a list of types that should be used for the tuple items. """ - def __init__(self, types: t.Sequence[t.Union[t.Type, ParamType]]) -> None: + def __init__(self, types: t.Sequence[t.Union[t.Type[t.Any], ParamType]]) -> None: self.types = [convert_type(ty) for ty in types] def to_info_dict(self) -> t.Dict[str, t.Any]: diff --git a/src/click/utils.py b/src/click/utils.py index 8283788..fca3eba 100644 --- a/src/click/utils.py +++ b/src/click/utils.py @@ -120,7 +120,7 @@ class LazyFile: self.encoding = encoding self.errors = errors self.atomic = atomic - self._f: t.Optional[t.IO] + self._f: t.Optional[t.IO[t.Any]] if filename == "-": self._f, self.should_close = open_stream(filename, mode, encoding, errors) @@ -141,7 +141,7 @@ class LazyFile: return repr(self._f) return f"<unopened file '{self.name}' {self.mode}>" - def open(self) -> t.IO: + def open(self) -> t.IO[t.Any]: """Opens the file if it's not yet open. This call might fail with a :exc:`FileError`. Not handling this error will produce an error that Click shows. @@ -183,7 +183,7 @@ class LazyFile: class KeepOpenFile: - def __init__(self, file: t.IO) -> None: + def __init__(self, file: t.IO[t.Any]) -> None: self._file = file def __getattr__(self, name: str) -> t.Any: @@ -340,7 +340,7 @@ def open_file( errors: t.Optional[str] = "strict", lazy: bool = False, atomic: bool = False, -) -> t.IO: +) -> t.IO[t.Any]: """Open a file, with extra behavior to handle ``'-'`` to indicate a standard stream, lazy open on write, and atomic write. Similar to the behavior of the :class:`~click.File` param type. @@ -370,18 +370,20 @@ def open_file( .. versionadded:: 3.0 """ if lazy: - return t.cast(t.IO, LazyFile(filename, mode, encoding, errors, atomic=atomic)) + return t.cast( + t.IO[t.Any], LazyFile(filename, mode, encoding, errors, atomic=atomic) + ) f, should_close = open_stream(filename, mode, encoding, errors, atomic=atomic) if not should_close: - f = t.cast(t.IO, KeepOpenFile(f)) + f = t.cast(t.IO[t.Any], KeepOpenFile(f)) return f def format_filename( - filename: t.Union[str, bytes, os.PathLike], shorten: bool = False + filename: t.Union[str, bytes, "os.PathLike[t.AnyStr]"], shorten: bool = False ) -> str: """Formats a filename for user display. The main purpose of this function is to ensure that the filename can be displayed at all. This @@ -458,7 +460,7 @@ class PacifyFlushWrapper: pipe, all calls and attributes are proxied. """ - def __init__(self, wrapped: t.IO) -> None: + def __init__(self, wrapped: t.IO[t.Any]) -> None: self.wrapped = wrapped def flush(self) -> None: |