Skip to content

Commit

Permalink
Types: don't leave generic types without a parameter
Browse files Browse the repository at this point in the history
Enable `disallow_any_generics` and provide type information for missing parameters for type hints.
  • Loading branch information
mjpieters committed Nov 8, 2022
1 parent c65c6ad commit b8f7c68
Show file tree
Hide file tree
Showing 9 changed files with 51 additions and 48 deletions.
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,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
Expand Down
36 changes: 18 additions & 18 deletions src/click/_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -153,7 +153,7 @@ def seekable(self) -> bool:
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:
Expand All @@ -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:
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -494,15 +494,15 @@ 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

return stream.__class__.__module__.startswith("ipykernel.")


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:
Expand Down Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion src/click/_termui_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def __init__(
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
Expand Down
10 changes: 5 additions & 5 deletions src/click/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1841,7 +1841,7 @@ def command(
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 (
Expand Down Expand Up @@ -1889,7 +1889,7 @@ def group(
"""
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 (
Expand Down Expand Up @@ -2260,7 +2260,7 @@ def type_cast_value(self, ctx: Context, value: t.Any) -> t.Any:
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:
Expand All @@ -2277,12 +2277,12 @@ def check_iter(value: t.Any) -> t.Iterator:
)
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:
Expand Down
2 changes: 1 addition & 1 deletion src/click/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def new_func(*args, **kwargs): # type: ignore


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
Expand Down
4 changes: 2 additions & 2 deletions src/click/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def format_message(self) -> str:
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()

Expand All @@ -59,7 +59,7 @@ def __init__(self, message: str, ctx: t.Optional["Context"] = None) -> None:
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
Expand Down
16 changes: 8 additions & 8 deletions src/click/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,11 @@ def mode(self) -> str:


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
Expand Down Expand Up @@ -206,7 +206,7 @@ def make_env(
@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]]]:
Expand Down Expand Up @@ -301,7 +301,7 @@ def _getchar(echo: bool) -> str:
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
Expand Down Expand Up @@ -350,7 +350,7 @@ def invoke(
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,
Expand Down Expand Up @@ -449,7 +449,7 @@ def invoke(

@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
Expand All @@ -464,11 +464,11 @@ def isolated_filesystem(
Added the ``temp_dir`` parameter.
"""
cwd = os.getcwd()
dt = tempfile.mkdtemp(dir=temp_dir) # type: ignore[type-var]
dt = tempfile.mkdtemp(dir=temp_dir)
os.chdir(dt)

try:
yield t.cast(str, dt)
yield dt
finally:
os.chdir(cwd)

Expand Down
10 changes: 5 additions & 5 deletions src/click/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ def __repr__(self) -> str:


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"]
Expand Down Expand Up @@ -702,8 +702,8 @@ def convert(
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
),
Expand Down Expand Up @@ -794,7 +794,7 @@ def __init__(
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
Expand Down Expand Up @@ -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]:
Expand Down
18 changes: 10 additions & 8 deletions src/click/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ def __init__(
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)
Expand All @@ -141,7 +141,7 @@ def __repr__(self) -> str:
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.
Expand Down Expand Up @@ -183,7 +183,7 @@ def __iter__(self) -> t.Iterator[t.AnyStr]:


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:
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down

0 comments on commit b8f7c68

Please sign in to comment.