Repository: pallets/click

Issue:
Path(exists=True, envvar='FOO') tries to interpret FOO="" as an existing path
Example:
```python
@click.command()
@click.option('--mypath', type=click.Path(exists=True), envvar='MYPATH')
def cli(mypath):
    click.echo(f'mypath: {mypath}')
```

Test case:
```
➜ env MYPATH= ./mycli.py
```

Observed output:
```
Usage: mycli.py [OPTIONS]
Try "mycli.py --help" for help.

Error: Invalid value for "--mypath": Path "" does not exist.
```

Expected output:
```
➜ ./mycli.py
mypath: None
```

i.e. Shouldn't Click special-case `envvar == ""` and treat it as though the option wasn't passed? This is more useful than failing with "`Path "" does not exist`", as it gives users who are inheriting an unwanted env var, and who can only set it to empty string rather than unset, a way to clear it.


Discussion:

Resolving patch:
diff --git a/click/core.py b/click/core.py
--- a/click/core.py
+++ b/click/core.py
@@ -1323,12 +1323,6 @@ class Parameter(object):
 
     Some settings are supported by both options and arguments.
 
-    .. versionchanged:: 2.0
-       Changed signature for parameter callback to also be passed the
-       parameter.  In Click 2.0, the old callback format will still work,
-       but it will raise a warning to give you change to migrate the
-       code easier.
-
     :param param_decls: the parameter declarations for this option or
                         argument.  This is a list of flags or argument
                         names.
@@ -1356,6 +1350,16 @@ class Parameter(object):
                      order of processing.
     :param envvar: a string or list of strings that are environment variables
                    that should be checked.
+
+    .. versionchanged:: 7.1
+        Empty environment variables are ignored rather than taking the
+        empty string value. This makes it possible for scripts to clear
+        variables if they can't unset them.
+
+    .. versionchanged:: 2.0
+        Changed signature for parameter callback to also be passed the
+        parameter. The old callback format will still work, but it will
+        raise a warning to give you a chance to migrate the code easier.
     """
     param_type_name = 'parameter'
 
@@ -1483,7 +1487,10 @@ def resolve_envvar_value(self, ctx):
                 if rv is not None:
                     return rv
         else:
-            return os.environ.get(self.envvar)
+            rv = os.environ.get(self.envvar)
+
+            if rv != "":
+                return rv
 
     def value_from_envvar(self, ctx):
         rv = self.resolve_envvar_value(ctx)


Test patch:
diff --git a/tests/test_options.py b/tests/test_options.py
--- a/tests/test_options.py
+++ b/tests/test_options.py
@@ -113,6 +113,17 @@ def cli(message):
     assert 'Error: Missing option "-m" / "--message".' in result.output
 
 
+def test_empty_envvar(runner):
+    @click.command()
+    @click.option('--mypath', type=click.Path(exists=True), envvar='MYPATH')
+    def cli(mypath):
+        click.echo('mypath: %s' % mypath)
+
+    result = runner.invoke(cli, [], env={'MYPATH': ''})
+    assert result.exit_code == 0
+    assert result.output == 'mypath: None\n'
+
+
 def test_multiple_envvar(runner):
     @click.command()
     @click.option('--arg', multiple=True)