Repository: theacodes/nox
Pull request #427 (2021-05-24 11:37:46)

Add --force-python option as shorthand for --python and --extra-python
This PR adds an option `--force-python` / `--force-pythons` which functions as a shorthand for when the same interpreter spec should be passed to both `--python` and `--extra-python`. This has the effect of running sessions on the specified interpreter *instead* of those listed for each session.

In other words, these two are equivalent:

- `nox --python=X.Y --extra-python=X.Y`
- `nox --force-python=X.Y`

By comparison, `--extra-python` on its own adds an interpreter to those listed for a session. The sessions will be run on the original Python versions *as well as* the extra version.

This came up a few times in the past:

- https://github.com/theacodes/nox/pull/361#issuecomment-748300867
- https://github.com/theacodes/nox/issues/412#issuecomment-810164777
- https://github.com/theacodes/nox/issues/233#issuecomment-846614741



Patch:
diff --git a/docs/usage.rst b/docs/usage.rst
index bca73f3..5567551 100644
--- a/docs/usage.rst
+++ b/docs/usage.rst
@@ -163,22 +163,27 @@ If the Noxfile sets ``nox.options.reuse_existing_virtualenvs``, you can override
 
 Running additional Python versions
 ----------------------------------
-In addition to Nox supporting executing single sessions, it also supports runnings python versions that aren't specified using ``--extra-pythons``.
+
+In addition to Nox supporting executing single sessions, it also supports running Python versions that aren't specified using ``--extra-pythons``.
 
 .. code-block:: console
 
     nox --extra-pythons 3.8 3.9
 
-This will, in addition to specified python versions in the Noxfile, also create sessions for the specified versions.
+This will, in addition to specified Python versions in the Noxfile, also create sessions for the specified versions.
 
 This option can be combined with ``--python`` to replace, instead of appending, the Python interpreter for a given session::
 
     nox --python 3.10 --extra-python 3.10 -s lint
 
+Instead of passing both options, you can use the ``--force-python`` shorthand::
+
+    nox --force-python 3.10 -s lint
+
 Also, you can specify ``python`` in place of a specific version. This will run the session
 using the ``python`` specified for the current ``PATH``::
 
-    nox --python python --extra-python python -s lint
+    nox --force-python python -s lint
 
 
 .. _opt-stop-on-first-error:
diff --git a/nox/_options.py b/nox/_options.py
index 8f66b1a..4fbd8f2 100644
--- a/nox/_options.py
+++ b/nox/_options.py
@@ -148,6 +148,15 @@ def _color_finalizer(value: bool, args: argparse.Namespace) -> bool:
     return sys.stdout.isatty()
 
 
+def _force_pythons_finalizer(
+    value: Sequence[str], args: argparse.Namespace
+) -> Sequence[str]:
+    """Propagate ``--force-python`` to ``--python`` and ``--extra-python``."""
+    if value:
+        args.pythons = args.extra_pythons = value
+    return value
+
+
 def _posargs_finalizer(
     value: Sequence[Any], args: argparse.Namespace
 ) -> Union[Sequence[Any], List[Any]]:
@@ -335,6 +344,18 @@ options.add_options(
         nargs="*",
         help="Additionally, run sessions using the given python interpreter versions.",
     ),
+    _option_set.Option(
+        "force_pythons",
+        "--force-pythons",
+        "--force-python",
+        group=options.groups["secondary"],
+        nargs="*",
+        help=(
+            "Run sessions with the given interpreters instead of those listed in the Noxfile."
+            " This is a shorthand for ``--python=X.Y --extra-python=X.Y``."
+        ),
+        finalizer_func=_force_pythons_finalizer,
+    ),
     *_option_set.make_flag_pair(
         "stop_on_first_error",
         ("-x", "--stop-on-first-error"),


Test patch:
diff --git a/tests/test_main.py b/tests/test_main.py
index be7c841..90120c3 100644
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -578,3 +578,12 @@ def test_main_color_conflict(capsys, monkeypatch):
     _, err = capsys.readouterr()
 
     assert "color" in err
+
+
+def test_main_force_python(monkeypatch):
+    monkeypatch.setattr(sys, "argv", ["nox", "--force-python=3.10"])
+    with mock.patch("nox.workflow.execute", return_value=0) as execute:
+        with mock.patch.object(sys, "exit"):
+            nox.__main__.main()
+        config = execute.call_args[1]["global_config"]
+    assert config.pythons == config.extra_pythons == ["3.10"]