Repository: pallets/click

Issue:
`<nothing> not callable` error in Mypy with Click 8.1.0
<!--
This issue tracker is a tool to address bugs in Click itself. Please use
Pallets Discord or Stack Overflow for questions about your own code.

Replace this comment with a clear outline of what the bug is.
-->

```py
# a.py
import click
@click.command()
def test():
  pass
test()
```

Run

```
$ mypy a.py
a.py:9: error: <nothing> not callable
Found 1 error in 1 file (checked 1 source file)
```

This used to pass with Click 8.0.4

Environment:

- Python version: 3.10.2
- Click version: 8.1.0



Discussion:
Happy to consider a PR that adjusts the type annotations.
Looking at the code (https://github.com/pallets/click/blob/main/src/click/core.py#L1814), it appears to have been changed to a Union while adding support for not calling the decorator before the function.

As per the mypy documentation, this should be represented with overloads, rather than a union:
https://mypy.readthedocs.io/en/stable/generics.html?highlight=decorator#decorator-factories
I think this is the same problem:


This MWE:

```python
import click

@click.group()
def main() -> None:
    pass

@main.command()
def tree() -> None:
    pass
```

Run with this noxfile:
```python
import nox

@nox.session
@nox.parametrize("click", ["8.0.4", "8.1.0"])
def mypy(session: nox.session, click: str) -> None:
    session.install("mypy==0.942", f"click=={click}")
    session.run("mypy", "--strict", "example.py")
```


Produces:

```console
$ nox
nox > Running session mypy(click='8.0.4')
nox > Creating virtual environment (virtualenv) using python3.10 in .nox/mypy-click-8-0-4
nox > python -m pip install mypy==0.942 click==8.0.4
nox > mypy --strict example.py
Success: no issues found in 1 source file
nox > Session mypy(click='8.0.4') was successful.
nox > Running session mypy(click='8.1.0')
nox > Creating virtual environment (virtualenv) using python3.10 in .nox/mypy-click-8-1-0
nox > python -m pip install mypy==0.942 click==8.1.0
nox > mypy --strict example.py
example.py:7: error: Untyped decorator makes function "tree" untyped
Found 1 error in 1 file (checked 1 source file)
nox > Command mypy --strict example.py failed with exit code 1
nox > Session mypy(click='8.1.0') failed.
nox > Ran multiple sessions:
nox > * mypy(click='8.0.4'): success
nox > * mypy(click='8.1.0'): failed
```

I'm guessing this was broken by #2212?

Resolving patch:
diff --git a/src/click/core.py b/src/click/core.py
--- a/src/click/core.py
+++ b/src/click/core.py
@@ -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]:
@@ -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)
@@ -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"]:
@@ -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:
diff --git a/src/click/decorators.py b/src/click/decorators.py
--- a/src/click/decorators.py
+++ b/src/click/decorators.py
@@ -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:
     ...
 
 
@@ -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]:
     ...
 
 
@@ -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):
@@ -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]:
     ...
 
 


Test patch: