Skip to content

Commit

Permalink
Merge pull request #2233 from henryiii/henryiii/fix/commandtype
Browse files Browse the repository at this point in the history
fix(types): decorator typing fails
  • Loading branch information
davidism committed Mar 30, 2022
2 parents fb7c6db + e234cf9 commit d5741a2
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 25 deletions.
3 changes: 3 additions & 0 deletions CHANGES.rst
Expand Up @@ -5,6 +5,9 @@ Version 8.1.1

Unreleased

- Fix an issue with decorator typing that caused type checking to
report that a command was not callable. :issue:`2227`


Version 8.1.0
-------------
Expand Down
34 changes: 30 additions & 4 deletions src/click/core.py
Expand Up @@ -1809,6 +1809,16 @@ def add_command(self, cmd: Command, name: t.Optional[str] = None) -> None:
_check_multicommand(self, name, cmd, register=True)
self.commands[name] = cmd

@t.overload
def command(self, __func: t.Callable[..., t.Any]) -> Command:
...

@t.overload
def command(
self, *args: t.Any, **kwargs: t.Any
) -> t.Callable[[t.Callable[..., t.Any]], Command]:
...

def command(
self, *args: t.Any, **kwargs: t.Any
) -> t.Union[t.Callable[[t.Callable[..., t.Any]], Command], Command]:
Expand All @@ -1834,8 +1844,11 @@ def command(
func: t.Optional[t.Callable] = None

if args and callable(args[0]):
func = args[0]
args = args[1:]
assert (
len(args) == 1 and not kwargs
), "Use 'command(**kwargs)(callable)' to provide arguments."
(func,) = args
args = ()

def decorator(f: t.Callable[..., t.Any]) -> Command:
cmd: Command = command(*args, **kwargs)(f)
Expand All @@ -1847,6 +1860,16 @@ def decorator(f: t.Callable[..., t.Any]) -> Command:

return decorator

@t.overload
def group(self, __func: t.Callable[..., t.Any]) -> "Group":
...

@t.overload
def group(
self, *args: t.Any, **kwargs: t.Any
) -> t.Callable[[t.Callable[..., t.Any]], "Group"]:
...

def group(
self, *args: t.Any, **kwargs: t.Any
) -> t.Union[t.Callable[[t.Callable[..., t.Any]], "Group"], "Group"]:
Expand All @@ -1869,8 +1892,11 @@ def group(
func: t.Optional[t.Callable] = None

if args and callable(args[0]):
func = args[0]
args = args[1:]
assert (
len(args) == 1 and not kwargs
), "Use 'group(**kwargs)(callable)' to provide arguments."
(func,) = args
args = ()

if self.group_class is not None and kwargs.get("cls") is None:
if self.group_class is type:
Expand Down
34 changes: 13 additions & 21 deletions src/click/decorators.py
Expand Up @@ -126,10 +126,8 @@ def new_func(*args, **kwargs): # type: ignore

@t.overload
def command(
name: t.Optional[str] = None,
cls: t.Type[CmdType] = ...,
**attrs: t.Any,
) -> t.Callable[..., CmdType]:
__func: t.Callable[..., t.Any],
) -> Command:
...


Expand All @@ -143,18 +141,10 @@ def command(

@t.overload
def command(
name: t.Callable,
name: t.Optional[str] = None,
cls: t.Type[CmdType] = ...,
**attrs: t.Any,
) -> CmdType:
...


@t.overload
def command(
name: t.Callable,
**attrs: t.Any,
) -> Command:
) -> t.Callable[..., CmdType]:
...


Expand Down Expand Up @@ -191,14 +181,17 @@ def command(
The ``params`` argument can be used. Decorated params are
appended to the end of the list.
"""
if cls is None:
cls = Command

func: t.Optional[t.Callable] = None

if callable(name):
func = name
name = None
assert cls is None, "Use 'command(cls=cls)(callable)' to specify a class."
assert not attrs, "Use 'command(**kwargs)(callable)' to provide arguments."

if cls is None:
cls = Command

def decorator(f: t.Callable[..., t.Any]) -> Command:
if isinstance(f, Command):
Expand Down Expand Up @@ -235,17 +228,16 @@ def decorator(f: t.Callable[..., t.Any]) -> Command:

@t.overload
def group(
name: t.Optional[str] = None,
**attrs: t.Any,
) -> t.Callable[[F], Group]:
__func: t.Callable,
) -> Group:
...


@t.overload
def group(
name: t.Callable,
name: t.Optional[str] = None,
**attrs: t.Any,
) -> Group:
) -> t.Callable[[F], Group]:
...


Expand Down

0 comments on commit d5741a2

Please sign in to comment.