Repository: pypa/pipenv

Issue:
pipenv run no longer sets $VIRTUAL_ENV
### Issue description

After updating to the latest version of pipenv, commands run with `pipenv run` no longer have `VIRTUAL_ENV` set in the environment

### Expected result

`VIRTUAL_ENV` is set in the environment of the subprocess to point to the virtual environment path

### Actual result

```
$ pipenv --version
pipenv, version 2021.5.29
$ pipenv run bash -c 'echo $VIRTUAL_ENV'
/home/phlip/.local/share/virtualenvs/test-hxkKlP5o
$ pip install --user --upgrade pipenv
[etc etc installing progress]
$ pipenv --version
pipenv, version 2021.11.5.post0
$ pipenv run bash -c 'echo $VIRTUAL_ENV'

```

I believe this is due to [this change](https://github.com/pypa/pipenv/commit/93251ba7fc4073aca857ccdc46976f39f50e6050#diff-ef852c4ac364f946819f765a6bc26f04f1b0968f31fc512949a60fa2ab0685e8L2477) replacing the call to `inline_activate_virtual_environment`, which is where `VIRTUAL_ENV` is set.

<details><summary>$ pipenv --support</summary>

Pipenv version: `'2021.11.5.post0'`

Pipenv location: `'/home/phlip/.local/lib/python3.8/site-packages/pipenv'`

Python location: `'/usr/bin/python3'`

Python installations found:

  - `3.9.7`: `/usr/bin/python3.9`
  - `3.8.10`: `/usr/bin/python3.8`
  - `3.8.10`: `/usr/bin/python3`
  - `3.7.12`: `/usr/bin/python3.7m`
  - `3.7.12`: `/usr/bin/python3.7`
  - `3.6.15`: `/usr/bin/python3.6m`
  - `3.6.15`: `/usr/bin/python3.6`
  - `2.7.18`: `/usr/bin/python2`
  - `2.7.18`: `/usr/bin/python`
  - `2.7.18`: `/usr/bin/python2.7`

PEP 508 Information:

```
{'implementation_name': 'cpython',
 'implementation_version': '3.8.10',
 'os_name': 'posix',
 'platform_machine': 'x86_64',
 'platform_python_implementation': 'CPython',
 'platform_release': '5.4.0-89-generic',
 'platform_system': 'Linux',
 'platform_version': '#100-Ubuntu SMP Fri Sep 24 14:50:10 UTC 2021',
 'python_full_version': '3.8.10',
 'python_version': '3.8',
 'sys_platform': 'linux'}
```

System environment variables:

  - `SHELL`
  - `DERBY_HOME`
  - `LANGUAGE`
  - `DEVKITPRO`
  - `TERMCAP`
  - `JAVA_HOME`
  - `WINDOW`
  - `EDITOR`
  - `PWD`
  - `LOGNAME`
  - `XDG_SESSION_TYPE`
  - `J2REDIR`
  - `MOTD_SHOWN`
  - `HOME`
  - `LANG`
  - `LS_COLORS`
  - `PERL5LIB`
  - `SSH_CONNECTION`
  - `DEVKITARM`
  - `LESSCLOSE`
  - `XDG_SESSION_CLASS`
  - `TERM`
  - `PERL_MB_OPT`
  - `J2SDKDIR`
  - `LESSOPEN`
  - `USER`
  - `PERL_MM_OPT`
  - `DISPLAY`
  - `SHLVL`
  - `XDG_SESSION_ID`
  - `XDG_RUNTIME_DIR`
  - `SSH_CLIENT`
  - `XDG_DATA_DIRS`
  - `PERL_LOCAL_LIB_ROOT`
  - `PATH`
  - `STY`
  - `DBUS_SESSION_BUS_ADDRESS`
  - `SSH_TTY`
  - `OLDPWD`
  - `_`
  - `PIP_SHIMS_BASE_MODULE`
  - `PIP_DISABLE_PIP_VERSION_CHECK`
  - `PYTHONDONTWRITEBYTECODE`
  - `PIP_PYTHON_PATH`
  - `PYTHONFINDER_IGNORE_UNSUPPORTED`

Pipenv–specific environment variables:


Debug–specific environment variables:

  - `PATH`: `/home/phlip/perl5/bin:/home/phlip/bin:/home/phlip/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/lib/jvm/java-8-oracle/bin:/usr/lib/jvm/java-8-oracle/db/bin:/usr/lib/jvm/java-8-oracle/jre/bin:/usr/lib/jvm/java-8-oracle/bin:/usr/lib/jvm/java-8-oracle/db/bin:/usr/lib/jvm/java-8-oracle/jre/bin:/sbin:/usr/sbin:/usr/lib/xscreensaver:/usr/local/devkitPro/devkitARM/bin`
  - `SHELL`: `/bin/bash`
  - `EDITOR`: `vim`
  - `LANG`: `en_AU.UTF-8`
  - `PWD`: `/tmp/test`


---------------------------

Contents of `Pipfile` ('/tmp/test/Pipfile'):

```toml
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]

[dev-packages]

[requires]
python_version = "3.9"

```

</details>


Discussion:

Resolving patch:
diff --git a/pipenv/core.py b/pipenv/core.py
--- a/pipenv/core.py
+++ b/pipenv/core.py
@@ -96,7 +96,7 @@ def do_clear(project):
         raise
 
 
-def load_dot_env(project):
+def load_dot_env(project, as_dict=False):
     """Loads .env file into sys.environ."""
     if not project.s.PIPENV_DONT_LOAD_ENV:
         # If the project doesn't exist yet, check current directory for a .env file
@@ -121,8 +121,11 @@ def load_dot_env(project):
                     ),
                     err=True,
                 )
-        dotenv.load_dotenv(dotenv_file, override=True)
-        project.s.initialize()
+        if as_dict:
+            return dotenv.dotenv_values(dotenv_file)
+        else:
+            dotenv.load_dotenv(dotenv_file, override=True)
+            project.s.initialize()
 
 
 def cleanup_virtualenv(project, bare=True):
@@ -2384,11 +2387,13 @@ def inline_activate_virtual_environment(project):
         os.environ["VIRTUAL_ENV"] = vistir.misc.fs_str(root)
 
 
-def _launch_windows_subprocess(script, path):
+def _launch_windows_subprocess(script, env):
     import subprocess
+
+    path = env.get("PATH", "")
     command = system_which(script.command, path=path)
 
-    options = {"universal_newlines": True}
+    options = {"universal_newlines": True, "env": env}
     script.cmd_args[1:] = [expandvars(arg) for arg in script.args]
 
     # Command not found, maybe this is a shell built-in?
@@ -2408,13 +2413,14 @@ def _launch_windows_subprocess(script, path):
     return subprocess.Popen(script.cmdify(), shell=True, **options)
 
 
-def do_run_nt(project, script, path):
-    p = _launch_windows_subprocess(script, path)
+def do_run_nt(project, script, env):
+    p = _launch_windows_subprocess(script, env)
     p.communicate()
     sys.exit(p.returncode)
 
 
-def do_run_posix(project, script, command, path):
+def do_run_posix(project, script, command, env):
+    path = env.get("PATH")
     command_path = system_which(script.command, path=path)
     if not command_path:
         if project.has_script(command):
@@ -2440,8 +2446,10 @@ def do_run_posix(project, script, command, path):
                 err=True,
             )
         sys.exit(1)
-    os.execl(
-        command_path, command_path, *(os.path.expandvars(arg) for arg in script.args)
+    os.execve(
+        command_path,
+        [command_path, *(os.path.expandvars(arg) for arg in script.args)],
+        env
     )
 
 
@@ -2457,25 +2465,25 @@ def do_run(project, command, args, three=None, python=False, pypi_mirror=None):
         project, three=three, python=python, validate=False, pypi_mirror=pypi_mirror,
     )
 
-    load_dot_env(project)
+    env = os.environ.copy()
+    env.update(load_dot_env(project, as_dict=True))
+    env.pop("PIP_SHIMS_BASE_MODULE", None)
 
-    previous_pip_shims_module = os.environ.pop("PIP_SHIMS_BASE_MODULE", None)
-
-    path = os.getenv('PATH', '')
+    path = env.get('PATH', '')
     if project.virtualenv_location:
         new_path = os.path.join(project.virtualenv_location, 'Scripts' if os.name == 'nt' else 'bin')
         paths = path.split(os.pathsep)
         paths.insert(0, new_path)
         path = os.pathsep.join(paths)
+        env["VIRTUAL_ENV"] = project.virtualenv_location
+    env["PATH"] = path
 
     # Set an environment variable, so we know we're in the environment.
     # Only set PIPENV_ACTIVE after finishing reading virtualenv_location
     # such as in inline_activate_virtual_environment
     # otherwise its value will be changed
-    previous_pipenv_active_value = os.environ.get("PIPENV_ACTIVE")
-    os.environ["PIPENV_ACTIVE"] = vistir.misc.fs_str("1")
-
-    os.environ.pop("PIP_SHIMS_BASE_MODULE", None)
+    env["PIPENV_ACTIVE"] = vistir.misc.fs_str("1")
+    env.pop("PIP_SHIMS_BASE_MODULE", None)
 
     try:
         script = project.build_script(command, args)
@@ -2485,20 +2493,13 @@ def do_run(project, command, args, three=None, python=False, pypi_mirror=None):
     except ScriptEmptyError:
         click.echo("Can't run script {0!r}-it's empty?", err=True)
     run_args = [project, script]
-    run_kwargs = {'path': path}
+    run_kwargs = {'env': env}
     if os.name == "nt" or environments.PIPENV_IS_CI:
         run_fn = do_run_nt
     else:
         run_fn = do_run_posix
         run_kwargs.update({"command": command})
-    try:
-        run_fn(*run_args, **run_kwargs)
-    finally:
-        os.environ.pop("PIPENV_ACTIVE", None)
-        if previous_pipenv_active_value is not None:
-            os.environ["PIPENV_ACTIVE"] = previous_pipenv_active_value
-        if previous_pip_shims_module is not None:
-            os.environ["PIP_SHIMS_BASE_MODULE"] = previous_pip_shims_module
+    run_fn(*run_args, **run_kwargs)
 
 
 def do_check(


Test patch:
diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py
--- a/tests/integration/conftest.py
+++ b/tests/integration/conftest.py
@@ -113,18 +113,10 @@ def pytest_runtest_setup(item):
         pytest.skip('requires github ssh')
     if item.get_closest_marker('needs_hg') is not None and not WE_HAVE_HG:
         pytest.skip('requires mercurial')
-    if item.get_closest_marker('skip_py27_win') is not None and (
-        sys.version_info[:2] <= (2, 7) and os.name == "nt"
-    ):
-        pytest.skip('must use python > 2.7 on windows')
     if item.get_closest_marker('skip_py38') is not None and (
         sys.version_info[:2] == (3, 8)
     ):
         pytest.skip('test not applicable on python 3.8')
-    if item.get_closest_marker('py3_only') is not None and (
-        sys.version_info < (3, 0)
-    ):
-        pytest.skip('test only runs on python 3')
     if item.get_closest_marker('skip_osx') is not None and sys.platform == 'darwin':
         pytest.skip('test does not apply on OSX')
     if item.get_closest_marker('lte_py36') is not None and (
diff --git a/tests/integration/test_install_twists.py b/tests/integration/test_install_twists.py
--- a/tests/integration/test_install_twists.py
+++ b/tests/integration/test_install_twists.py
@@ -372,7 +372,6 @@ def test_multiple_editable_packages_should_not_race(PipenvInstance, testsroot):
 
 
 @pytest.mark.outdated
-@pytest.mark.py3_only
 def test_outdated_should_compare_postreleases_without_failing(PipenvInstance):
     with PipenvInstance(chdir=True) as p:
         c = p.pipenv("install ibm-db-sa-py3==0.3.0")
diff --git a/tests/integration/test_install_uri.py b/tests/integration/test_install_uri.py
--- a/tests/integration/test_install_uri.py
+++ b/tests/integration/test_install_uri.py
@@ -257,7 +257,6 @@ def test_get_vcs_refs(PipenvInstance_NoPyPI):
 @pytest.mark.urls
 @pytest.mark.install
 @pytest.mark.needs_internet
-@pytest.mark.py3_only
 def test_vcs_entry_supersedes_non_vcs(PipenvInstance):
     """See issue #2181 -- non-editable VCS dep was specified, but not showing up
     in the lockfile -- due to not running pip install before locking and not locking
diff --git a/tests/integration/test_run.py b/tests/integration/test_run.py
--- a/tests/integration/test_run.py
+++ b/tests/integration/test_run.py
@@ -61,3 +61,24 @@ def test_scripts(PipenvInstance):
             assert c.returncode == 0
             if os.name != "nt":  # This doesn't work on CI windows.
                 assert c.stdout.strip() == "WORLD"
+
+
+@pytest.mark.run
+@pytest.mark.skip_windows
+def test_run_with_usr_env_shebang(PipenvInstance):
+    with PipenvInstance(chdir=True) as p:
+        p.pipenv('install')
+        script_path = os.path.join(p.path, "test_script")
+        with open(script_path, "w") as f:
+            f.write(
+                "#!/usr/bin/env python\n"
+                "import sys, os\n\n"
+                "print(sys.prefix)\n"
+                "print(os.getenv('VIRTUAL_ENV'))\n"
+            )
+        os.chmod(script_path, 0o700)
+        c = p.pipenv("run ./test_script")
+        assert c.returncode == 0
+        project = Project()
+        lines = [line.strip() for line in c.stdout.splitlines()]
+        assert all(line == project.virtualenv_location for line in lines)