Repository: pallets/click

Issue:
Allow disabling pattern expansion on Windows
## TL;DR

Essentially, make this optional: https://github.com/pallets/click/pull/1830

## Use case

We have a command that passes arbitrary arguments to be executed in other contexts such as Docker containers, servers over SSH, etc.

On Windows since Click 8.0 this breaks (depending on files in cwd, etc.) and unlike on Linux we can't just escape special characters

## Implementation

Setting could be called `disable_pattern_expansion`, e.g.:

```python
import click


@click.command(
    context_settings={'help_option_names': [], 'ignore_unknown_options': True, 'disable_pattern_expansion': True},
)
@click.argument('args', required=True, nargs=-1)
@click.pass_obj
def run(app, args):
    environment = app.get_environment()
    environment.send_command(args)
```


Discussion:
I looked into the code and it appears nested commands will have already received expanded arguments so `disable_pattern_expansion` will need to be of type `t.Optional[t.Union[bool, t.List[str]]]` and if nesting then defined on the group as a list of sub-command names to ignore.
Can you provide an example of what goes wrong, so we can explain better in the changelog? Also, there was some discussion of escaping in the original issue, maybe it can help?
Basic example:

```cli run external-tool *.log```

now would run `external-tool` on the other system with expanded args if there are any log files in the current directory.

On Linux we can do:

```cli run external-tool \*.log```

but on Windows the Click args become `('external-tool', '\\*.log')`
Not ideal, I understand, but the original feature request discussed escaping https://github.com/pallets/click/issues/1096#issuecomment-415892327

> Python's glob module lets you use `[]` for quoting. (E.g., the pattern `[*].txt` matches the literal filename `*.txt`.) Not very intuitive, but it is possible, and I guess Windows users are used to weird edge cases when trying to use the cli to refer to files with `*` in the name?
> Windows users are used to weird edge cases when trying to use the cli to refer to files with `*` in the name

Actually, us Windows users are accustomed to CLIs not supporting expansion by default 😉 

> Python's glob module lets you use `[]` for quoting

What about `~` and env vars?

I guess the point I'm trying to stress is that certain inputs can no longer be achieved in a cross-platform way
Pinging @njsmith and @asweigart for their thoughts since they participated in the original discussion.

Due to the way the processing pipeline works, the expansion has to be done at the beginning, before any parsing of the command line is done. This would be an all or nothing toggle, either no expansion happens for any command, or it happens for every command.

Resolving patch:
diff --git a/src/click/core.py b/src/click/core.py
--- a/src/click/core.py
+++ b/src/click/core.py
@@ -993,6 +993,7 @@ def main(
         prog_name: t.Optional[str] = None,
         complete_var: t.Optional[str] = None,
         standalone_mode: bool = True,
+        windows_expand_args: bool = True,
         **extra: t.Any,
     ) -> t.Any:
         """This is the way to invoke a script with all the bells and
@@ -1021,9 +1022,15 @@ def main(
                                 propagated to the caller and the return
                                 value of this function is the return value
                                 of :meth:`invoke`.
+        :param windows_expand_args: Expand glob patterns, user dir, and
+            env vars in command line args on Windows.
         :param extra: extra keyword arguments are forwarded to the context
                       constructor.  See :class:`Context` for more information.
 
+        .. versionchanged:: 8.0.1
+            Added the ``windows_expand_args`` parameter to allow
+            disabling command line arg expansion on Windows.
+
         .. versionchanged:: 8.0
             When taking arguments from ``sys.argv`` on Windows, glob
             patterns, user dir, and env vars are expanded.
@@ -1038,7 +1045,7 @@ def main(
         if args is None:
             args = sys.argv[1:]
 
-            if os.name == "nt":
+            if os.name == "nt" and windows_expand_args:
                 args = _expand_args(args)
         else:
             args = list(args)


Test patch: