summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartijn Pieters <mj@zopatista.com>2022-11-08 19:44:29 +0000
committerDavid Lord <davidism@gmail.com>2023-01-19 16:33:27 -0800
commita1093bbe0dae00eea8342247a0c2739b07a6acd8 (patch)
tree25c1680557687164f30ff08c5fa0e1e1693fac66
parenta6c7ee060b02eaa62fd15264a669220914cfad4c (diff)
downloadclick-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.cfg1
-rw-r--r--src/click/_compat.py36
-rw-r--r--src/click/_termui_impl.py2
-rw-r--r--src/click/core.py10
-rw-r--r--src/click/decorators.py2
-rw-r--r--src/click/exceptions.py4
-rw-r--r--src/click/testing.py12
-rw-r--r--src/click/types.py10
-rw-r--r--src/click/utils.py18
9 files changed, 49 insertions, 46 deletions
diff --git a/setup.cfg b/setup.cfg
index ea0a52c..fa4ff5d 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -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: