Repository: theacodes/nox (python)

Issue:
Running with `--no-venv` causes `session.install` to use unexpected interpreter
**Describe the bug**
Running with `--no-venv` causes `session.install` to be invoked with the `python` executable found on `PATH`, not the one running `nox`.

**How to reproduce**
Running in an environment where the executables `python` and `python3.8` are distinct:
```console
$ which python python3.8
/usr/bin/python
/usr/bin/python3.8
```

My `noxfile.py` just installs `pytest`.  Running we obtain:

```
$ python3.8 -m nox --no-venv
nox > Running session validate
nox > python -m pip install pytest
nox > Command python -m pip install failed with exit code 1:
/usr/bin/python: No module named pip
nox > Session validate failed.
```

Ignore the "No module named pip", the point being it's invoking `/usr/bin/python`, not `/usr/bin/python3.8`.

**Expected behavior**
Documentation on [forcing-sessions-backend](https://nox.thea.codes/en/stable/usage.html?highlight=no-venv#forcing-the-sessions-backend) suggests this should run "on the current python interpreter (the one running nox)".

**Possible fix**
I suspect a fix could be applied in `Session.install` [here](https://github.com/theacodes/nox/blob/main/nox/sessions.py#L476) to use `sys.executable` instead of the string `"python".  I'm not certain what conditions we'd need to check beforhand (how to verify we aren't running in a venv).

Pull request:
Deprecate use of session.install() without a virtual env
Close #503.

I've scanned the docs and cannot see anything that should be changed based on this change. Please correct me if I'm wrong.

Also feel free to chime in on the wording of the warning message.

Patch:
diff --git a/docs/config.rst b/docs/config.rst
index f3bb1b9..7773da1 100644
--- a/docs/config.rst
+++ b/docs/config.rst
@@ -149,6 +149,14 @@ If you want to disable virtualenv creation altogether, you can set ``python`` to
     def tests(session):
         pass
 
+Use of :func:`session.install()` is deprecated without a virtualenv since it modifies the global Python environment. If this is what you really want, use :func:`session.run()` and pip instead.
+
+.. code-block:: python
+
+    @nox.session(python=False)
+    def tests(session):
+        session.run("pip", "install", "nox")
+
 You can also specify that the virtualenv should *always* be reused instead of recreated every time:
 
 .. code-block:: python
diff --git a/nox/sessions.py b/nox/sessions.py
index f476bb2..043a9e2 100644
--- a/nox/sessions.py
+++ b/nox/sessions.py
@@ -20,6 +20,7 @@ import pathlib
 import re
 import sys
 import unicodedata
+import warnings
 from typing import (
     Any,
     Callable,
@@ -488,6 +489,15 @@ class Session:
             raise ValueError(
                 "A session without a virtualenv can not install dependencies."
             )
+        if isinstance(venv, PassthroughEnv):
+            warnings.warn(
+                f"Session {self.name} does not have a virtual environment, "
+                "so use of session.install() is deprecated since it would modify "
+                "the global Python environment. If you're really sure that is "
+                'what you want to do, use session.run("pip", "install", ...) instead.',
+                category=FutureWarning,
+                stacklevel=2,
+            )
         if not args:
             raise ValueError("At least one argument required to install().")
 


Test patch:
diff --git a/tests/test_sessions.py b/tests/test_sessions.py
index d5a895d..4f31a82 100644
--- a/tests/test_sessions.py
+++ b/tests/test_sessions.py
@@ -578,6 +578,39 @@ class TestSession:
                 external="error",
             )
 
+    def test_install_no_venv_deprecated(self):
+        runner = nox.sessions.SessionRunner(
+            name="test",
+            signatures=["test"],
+            func=mock.sentinel.func,
+            global_config=_options.options.namespace(posargs=[]),
+            manifest=mock.create_autospec(nox.manifest.Manifest),
+        )
+        runner.venv = mock.create_autospec(nox.virtualenv.PassthroughEnv)
+        runner.venv.env = {}
+
+        class SessionNoSlots(nox.sessions.Session):
+            pass
+
+        session = SessionNoSlots(runner=runner)
+
+        with mock.patch.object(session, "_run", autospec=True) as run:
+            with pytest.warns(
+                FutureWarning,
+                match=r"use of session\.install\(\) is deprecated since it would modify the global Python environment",
+            ):
+                session.install("requests", "urllib3")
+            run.assert_called_once_with(
+                "python",
+                "-m",
+                "pip",
+                "install",
+                "requests",
+                "urllib3",
+                silent=True,
+                external="error",
+            )
+
     def test_notify(self):
         session, runner = self.make_session_and_runner()