Repository: pallets/click (python)

Issue:
Trailing blanks in envvars of boolean options cause otherwise valid values to be rejected
### Actual Behavior

When defining a boolean option that also can be set via envvar, trailing blanks in the envvar value cause otherwise valid values to be rejected with this error message:

```
Error: Invalid value for '--something': true  is not a valid boolean
```
Because the value is shown in the error message unquoted, the trailing blank cannot easily be seen, but it is there.

Why is this an issue?

Invoking the command on Linux/macOS as follows does not trigger the issue:
```
$ SOMETHING=true command 
```

However, invoking it like this on Windows does trigger the issue:
```
> set SOMETHING=true & command
```

Apparently, Windows sets the envvar with a trailing blank in this case.

The issue can be circumvented like this:
```
> set SOMETHING=true& command
```

Here is the code that causes this behavior:

click/types.py at line 406:
```
class BoolParamType(ParamType):
    name = "boolean"

    def convert(self, value, param, ctx):
        if isinstance(value, bool):
            return bool(value)
        value = value.lower()
        if value in ("true", "t", "1", "yes", "y"):
            return True
        elif value in ("false", "f", "0", "no", "n"):
            return False
        self.fail("{!r} is not a valid boolean".format(value), param, ctx)

    def __repr__(self):
        return "BOOL"
```
### Expected Behavior

I would like to see click strip any leading or trailing blanks from the boolean envvar before it is evaluated.

### Environment

* Python version: any
* Click version: 7.1.2


Pull request description:
boolean type strips space before converting
fixes #1605 

Patch:
diff --git a/CHANGES.rst b/CHANGES.rst
index 548ac13..c27b103 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -111,6 +111,8 @@ Unreleased
     completion system. :issue:`942`
 -   Include ``--help`` option in completion. :pr:`1504`
 -   ``ParameterSource`` is an ``enum.Enum`` subclass. :issue:`1530`
+-   Boolean type strips surrounding space before converting.
+    :issue:`1605`
 
 
 Version 7.1.2
diff --git a/src/click/types.py b/src/click/types.py
index b7161d9..721c556 100644
--- a/src/click/types.py
+++ b/src/click/types.py
@@ -497,11 +497,15 @@ class BoolParamType(ParamType):
     def convert(self, value, param, ctx):
         if isinstance(value, bool):
             return bool(value)
-        value = value.lower()
-        if value in {"1", "true", "t", "yes", "y", "on"}:
+
+        norm = value.strip().lower()
+
+        if norm in {"1", "true", "t", "yes", "y", "on"}:
             return True
-        elif value in {"0", "false", "f", "no", "n", "off"}:
+
+        if norm in {"0", "false", "f", "no", "n", "off"}:
             return False
+
         self.fail(f"{value!r} is not a valid boolean value.", param, ctx)
 
     def __repr__(self):


Test patch:
diff --git a/tests/test_options.py b/tests/test_options.py
index c57525c..e86a82a 100644
--- a/tests/test_options.py
+++ b/tests/test_options.py
@@ -164,6 +164,17 @@ def test_multiple_envvar(runner):
     assert result.output == "foo|bar\n"
 
 
+def test_trailing_blanks_boolean_envvar(runner):
+    @click.command()
+    @click.option("--shout/--no-shout", envvar="SHOUT")
+    def cli(shout):
+        click.echo(f"shout: {shout!r}")
+
+    result = runner.invoke(cli, [], env={"SHOUT": " true "})
+    assert result.exit_code == 0
+    assert result.output == "shout: True\n"
+
+
 def test_multiple_default_help(runner):
     @click.command()
     @click.option("--arg1", multiple=True, default=("foo", "bar"), show_default=True)