Repository: pex-tool/pex

Issue:
PEX_INHERIT_PATH (and maybe other vars) ignored when venv used from cache
To reproduce:

```
python -m pex ansicolors --venv -o repro.pex 
./repro.pex

ctrl-D

PEX_INHERIT_PATH=invalid_val ./repro.pex

ctrl-D
```
expected behavior: `Invalid value for PEX_INHERIT_PATH` error.
actual behavior: pex runs from cached venv

Expected behavior can be triggered by:

```
rm -rf ~/.pex/venvs
PEX_INHERIT_PATH=invalid_val ./repro.pex
```

Obviously the `invalid_val` thing is just an easy way to show whether `PEX_INHERIT_PATH` is being inspected. My real goal is to use it with a valid value and have that value take effect...




Discussion:
Where would you expect the PEX to inherit from? It's running in a venv after all.
Good point from an implementation perspective. 

But from the user perspective: I've been handed a .pex file (I neither know nor care that it was created with `--venv`). I see in the documentation that PEX_INHERIT_PATH should do a thing, and I see that in practice it not only does not do the thing, but it is ignored - I can give it a bad value without getting an error. Then I nuke the venv cache and suddenly do get that error. 

I think that user would expect the PEX to inherit from the venv they think they're in (that is, the venv active in their current shell), and/or from their PYTHONPATH. 

But if we can't make the feature work sensibly, and you make a good argument for that, then we should probably error out on any attempt to use it?
Yeah, it's a tough feature generally. Venv aside it almost makes no sense since Pex will pick a compatible interpreter at runtime and maybe that one was not the one you were expecting / doesn't have the extra distributions you wanted. Afaict the feature is only for experimenting unless you either:
1. Have tightly controlled target hosts with just one compatible interpreter.
2. Like 1, except you use pexrc files to do the tightening.
3. You run PEX files via a script or other command line that is of the form `/this/compatible/interpreter my.pex`.

I'd prefer to fail fast for unsupported Pex env vars in venv mode until someone can come up with an easy to understand algorithm for picking which env to inherit the path from for a venv pex. I'd like even more to drop the feature!
Remind me why we have the feature? AFAICT we don't use it in Pants.
Pretty sure Twitter. You can imagine having a blessed interpreter on a host with puppet installed numerical computing distributions in its site-packages and avoiding shipping those dists over the network in N PEXes. I need to do some research though to confirm this.
Oh right, that makes sense. Well we should probably keep the feature, I can see how it might be handy.

Resolving patch:
diff --git a/pex/tools/commands/venv.py b/pex/tools/commands/venv.py
--- a/pex/tools/commands/venv.py
+++ b/pex/tools/commands/venv.py
@@ -175,6 +175,42 @@ def iter_valid_venv_pythons():
                 os.environ[current_interpreter_blessed_env_var] = "1"
                 os.execv(python, [python, "-sE"] + sys.argv)
 
+            ignored_pex_env_vars = [
+                "{{}}={{}}".format(name, value)
+                for name, value in os.environ.items()
+                if name.startswith(("PEX_", "_PEX_", "__PEX_")) and name not in (
+                    # These are used inside this script.
+                    "_PEX_SHOULD_EXIT_VENV_REEXEC",
+                    "PEX_EXTRA_SYS_PATH",
+                    "PEX_VENV_BIN_PATH",
+                    "PEX_INTERPRETER",
+                    "PEX_SCRIPT",
+                    "PEX_MODULE",
+                    # This is used when loading ENV (Variables()):
+                    "PEX_IGNORE_RCFILES",
+                    # And ENV is used to access these during PEX bootstrap when delegating here via
+                    # a --venv mode PEX file.
+                    "PEX_ROOT",
+                    "PEX_VENV",
+                    "PEX_PATH",
+                    "PEX_PYTHON",
+                    "PEX_PYTHON_PATH",
+                    "PEX_VERBOSE",
+                    "__PEX_EXE__",
+                    "__PEX_UNVENDORED__",
+                    # This is _not_ used (it is ignored), but it's present under CI and simplest to
+                    # add an exception for here and not warn about in CI runs.
+                    "_PEX_TEST_PYENV_ROOT",
+                )
+            ]
+            if ignored_pex_env_vars:
+                sys.stderr.write(
+                    "Ignoring the following environment variables in Pex venv mode:\\n"
+                    "{{}}\\n\\n".format(
+                        os.linesep.join(sorted(ignored_pex_env_vars))
+                    )
+                )
+
             os.environ["VIRTUAL_ENV"] = venv_dir
             sys.path.extend(os.environ.get("PEX_EXTRA_SYS_PATH", "").split(os.pathsep))
 


Test patch:
diff --git a/tests/tools/commands/test_venv.py b/tests/tools/commands/test_venv.py
--- a/tests/tools/commands/test_venv.py
+++ b/tests/tools/commands/test_venv.py
@@ -15,7 +15,7 @@
 
 import pytest
 
-from pex.common import safe_open, temporary_dir, touch
+from pex.common import safe_mkdtemp, safe_open, temporary_dir, touch
 from pex.compatibility import PY2
 from pex.executor import Executor
 from pex.interpreter import PythonInterpreter
@@ -631,3 +631,53 @@ def create_pex_venv(strip_pex_env):
         env=two_pex_env_vars,
     )
     assert 2 == process.wait()
+
+
+def test_warn_unused_pex_env_vars():
+    # type: () -> None
+    # N.B.: We don't use the pytest tmpdir fixture here since it creates fairly length paths under
+    # /tmp and under macOS, where TMPDIR is already fairly deeply nested, we trigger Pex warinings
+    # about script shebang length. Those warnings pollute stderr.
+    tmpdir = safe_mkdtemp()
+    venv_pex = os.path.join(tmpdir, "venv.pex")
+    run_pex_command(["--venv", "-o", venv_pex]).assert_success()
+
+    def assert_execute_venv_pex(expected_stderr, **env_vars):
+        env = os.environ.copy()
+        env.update(env_vars)
+        process = subprocess.Popen(
+            [venv_pex, "-c", ""], stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env
+        )
+        stdout, stderr = process.communicate()
+        assert 0 == process.returncode
+        assert not stdout
+        assert expected_stderr.strip() == stderr.decode("utf-8").strip()
+
+    assert_execute_venv_pex(expected_stderr="")
+    assert_execute_venv_pex(expected_stderr="", PEX_ROOT=os.path.join(tmpdir, "pex_root"))
+    assert_execute_venv_pex(expected_stderr="", PEX_UNZIP="1")
+    assert_execute_venv_pex(expected_stderr="", PEX_EXTRA_SYS_PATH="more")
+    assert_execute_venv_pex(expected_stderr="", PEX_VERBOSE="0")
+
+    assert_execute_venv_pex(
+        expected_stderr=dedent(
+            """\
+            Ignoring the following environment variables in Pex venv mode:
+            PEX_INHERIT_PATH=fallback
+            """
+        ),
+        PEX_INHERIT_PATH="fallback",
+    )
+
+    assert_execute_venv_pex(
+        expected_stderr=dedent(
+            """\
+            Ignoring the following environment variables in Pex venv mode:
+            PEX_EMIT_WARNINGS=0
+            PEX_INHERIT_PATH=fallback
+            """
+        ),
+        PEX_EMIT_WARNINGS="0",
+        PEX_INHERIT_PATH="fallback",
+        PEX_VERBOSE="0",
+    )