Repository: pallets/click

Issue:
Unable to properly inherit and hint Group due to use of Dict for parameters
I am working on updating to Click 8 in https://github.com/Chia-Network/chia-blockchain/pull/11361 and wanted to make `windows_expand_args=True` default for all my uses and have a single place to apply any other adjustments I need at that level.  I wrote a custom subclass of `Group` as below to achieve that, as well as a similar subclass of `Command`.  The issue is that when I hint that only my `_Command` subclass should be passed mypy complains due to the variance of `Dict`.  Modifying `click.Group.__init__()` to accept `Mapping[str, Command]` instead of `Dict[str, Command]` resolves the issue.  I will submit a PR immediately following this.

```
chia/util/click.py:22: error: Argument "commands" to "__init__" of "Group" has incompatible type "Union[Dict[str, _Command], Sequence[_Command], None]"; expected "Union[Dict[str, Command], Sequence[Command], None]"  [arg-type]
```

```python
class _Group(click.Group):
    def __init__(
        self,
        name: Optional[str] = None,
        commands: Optional[Union[Dict[str, _Command], Sequence[_Command]]] = None,
        **attrs: Any,
    ) -> None:
        # _commands = cast(Optional[Union[Dict[str, click.Command], Sequence[click.Command]]], commands)
        super().__init__(name=name, commands=commands, **attrs)
        self.command_class = _Command

    def main(
        self,
        args: Optional[Sequence[str]] = None,
        prog_name: Optional[str] = None,
        complete_var: Optional[str] = None,
        standalone_mode: bool = True,
        **extra: Any,
    ) -> Any:
        return super().main(
            args=args,
            prog_name=prog_name,
            complete_var=complete_var,
            standalone_mode=standalone_mode,
            windows_expand_args=True,
            **extra,
        )
```

Environment:

- Python version: 3.10.1
- Click version: 8.1.3



Discussion:

Resolving patch:
diff --git a/src/click/core.py b/src/click/core.py
--- a/src/click/core.py
+++ b/src/click/core.py
@@ -264,7 +264,7 @@ def __init__(
         info_name: t.Optional[str] = None,
         obj: t.Optional[t.Any] = None,
         auto_envvar_prefix: t.Optional[str] = None,
-        default_map: t.Optional[t.Dict[str, t.Any]] = None,
+        default_map: t.Optional[t.MutableMapping[str, t.Any]] = None,
         terminal_width: t.Optional[int] = None,
         max_content_width: t.Optional[int] = None,
         resilient_parsing: bool = False,
@@ -311,7 +311,7 @@ def __init__(
         ):
             default_map = parent.default_map.get(info_name)
 
-        self.default_map: t.Optional[t.Dict[str, t.Any]] = default_map
+        self.default_map: t.Optional[t.MutableMapping[str, t.Any]] = default_map
 
         #: This flag indicates if a subcommand is going to be executed. A
         #: group callback can use this information to figure out if it's
@@ -862,7 +862,7 @@ class BaseCommand:
     def __init__(
         self,
         name: t.Optional[str],
-        context_settings: t.Optional[t.Dict[str, t.Any]] = None,
+        context_settings: t.Optional[t.MutableMapping[str, t.Any]] = None,
     ) -> None:
         #: the name the command thinks it has.  Upon registering a command
         #: on a :class:`Group` the group will default the command name
@@ -874,7 +874,7 @@ def __init__(
             context_settings = {}
 
         #: an optional dictionary with defaults passed to the context.
-        self.context_settings: t.Dict[str, t.Any] = context_settings
+        self.context_settings: t.MutableMapping[str, t.Any] = context_settings
 
     def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]:
         """Gather information that could be useful for a tool generating
@@ -1117,7 +1117,7 @@ def main(
 
     def _main_shell_completion(
         self,
-        ctx_args: t.Dict[str, t.Any],
+        ctx_args: t.MutableMapping[str, t.Any],
         prog_name: str,
         complete_var: t.Optional[str] = None,
     ) -> None:
@@ -1193,7 +1193,7 @@ class Command(BaseCommand):
     def __init__(
         self,
         name: t.Optional[str],
-        context_settings: t.Optional[t.Dict[str, t.Any]] = None,
+        context_settings: t.Optional[t.MutableMapping[str, t.Any]] = None,
         callback: t.Optional[t.Callable[..., t.Any]] = None,
         params: t.Optional[t.List["Parameter"]] = None,
         help: t.Optional[str] = None,
@@ -1804,7 +1804,9 @@ class Group(MultiCommand):
     def __init__(
         self,
         name: t.Optional[str] = None,
-        commands: t.Optional[t.Union[t.Dict[str, Command], t.Sequence[Command]]] = None,
+        commands: t.Optional[
+            t.Union[t.MutableMapping[str, Command], t.Sequence[Command]]
+        ] = None,
         **attrs: t.Any,
     ) -> None:
         super().__init__(name, **attrs)
@@ -1815,7 +1817,7 @@ def __init__(
             commands = {c.name: c for c in commands if c.name is not None}
 
         #: The registered subcommands by their exported names.
-        self.commands: t.Dict[str, Command] = commands
+        self.commands: t.MutableMapping[str, Command] = commands
 
     def add_command(self, cmd: Command, name: t.Optional[str] = None) -> None:
         """Registers another :class:`Command` with this group.  If the name
diff --git a/src/click/shell_completion.py b/src/click/shell_completion.py
--- a/src/click/shell_completion.py
+++ b/src/click/shell_completion.py
@@ -16,7 +16,7 @@
 
 def shell_complete(
     cli: BaseCommand,
-    ctx_args: t.Dict[str, t.Any],
+    ctx_args: t.MutableMapping[str, t.Any],
     prog_name: str,
     complete_var: str,
     instruction: str,
@@ -214,7 +214,7 @@ class ShellComplete:
     def __init__(
         self,
         cli: BaseCommand,
-        ctx_args: t.Dict[str, t.Any],
+        ctx_args: t.MutableMapping[str, t.Any],
         prog_name: str,
         complete_var: str,
     ) -> None:
@@ -482,7 +482,10 @@ def _is_incomplete_option(ctx: Context, args: t.List[str], param: Parameter) ->
 
 
 def _resolve_context(
-    cli: BaseCommand, ctx_args: t.Dict[str, t.Any], prog_name: str, args: t.List[str]
+    cli: BaseCommand,
+    ctx_args: t.MutableMapping[str, t.Any],
+    prog_name: str,
+    args: t.List[str],
 ) -> Context:
     """Produce the context hierarchy starting with the command and
     traversing the complete arguments. This only follows the commands,


Test patch: