Skip to content

Commit

Permalink
feat: support users specifying an undeclared parametrization of python (
Browse files Browse the repository at this point in the history
#361)

* feat: support users specifying an undeclared parametrization of python

Co-authored-by: Danny Hermes <daniel.j.hermes@gmail.com>
Co-authored-by: Claudio Jolowicz <cjolowicz@gmail.com>
  • Loading branch information
3 people committed Dec 29, 2020
1 parent cdb391f commit e2db5ed
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 0 deletions.
22 changes: 22 additions & 0 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,28 @@ By default, Nox deletes and recreates virtualenvs every time it is run. This is
If the Noxfile sets ``nox.options.reuse_existing_virtualenvs``, you can override the Noxfile setting from the command line by using ``--no-reuse-existing-virtualenvs``.

.. _opt-running-extra-pythons:

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``.

.. 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 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

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


.. _opt-stop-on-first-error:

Stopping if any session fails
Expand Down
8 changes: 8 additions & 0 deletions nox/_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,14 @@ def _session_completer(
group=options.groups["secondary"],
help="Directory where nox will store virtualenvs, this is ``.nox`` by default.",
),
_option_set.Option(
"extra_pythons",
"--extra-pythons",
"--extra-python",
group=options.groups["secondary"],
nargs="*",
help="Additionally, run sessions using the given python interpreter versions.",
),
*_option_set.make_flag_pair(
"stop_on_first_error",
("-x", "--stop-on-first-error"),
Expand Down
21 changes: 21 additions & 0 deletions nox/manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import argparse
import collections.abc
import itertools
from collections import OrderedDict
from typing import Any, Iterable, Iterator, List, Mapping, Sequence, Set, Tuple, Union

from nox._decorators import Call, Func
Expand All @@ -23,6 +24,11 @@
WARN_PYTHONS_IGNORED = "python_ignored"


def _unique_list(*args: str) -> List[str]:
"""Return a list without duplicates, while preserving order."""
return list(OrderedDict.fromkeys(args))


class Manifest:
"""Session manifest.
Expand Down Expand Up @@ -184,6 +190,21 @@ def make_session(
func.should_warn[WARN_PYTHONS_IGNORED] = func.python
func.python = False

if self._config.extra_pythons:
# If extra python is provided, expand the func.python list to
# include additional python interpreters
extra_pythons = self._config.extra_pythons # type: List[str]
if isinstance(func.python, (list, tuple, set)):
func.python = _unique_list(*func.python, *extra_pythons)
elif not multi and func.python:
# If this is multi, but there is only a single interpreter, it
# is the reentrant case. The extra_python interpreter shouldn't
# be added in that case. If func.python is False, the session
# has no backend; if None, it uses the same interpreter as Nox.
# Otherwise, add the extra specified python.
assert isinstance(func.python, str)
func.python = _unique_list(func.python, *extra_pythons)

# If the func has the python attribute set to a list, we'll need
# to expand them.
if isinstance(func.python, (list, tuple, set)):
Expand Down
37 changes: 37 additions & 0 deletions tests/test_manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ def create_mock_config():
cfg = mock.sentinel.CONFIG
cfg.force_venv_backend = None
cfg.default_venv_backend = None
cfg.extra_pythons = None
return cfg


Expand Down Expand Up @@ -203,6 +204,42 @@ def session_func():
assert len(manifest) == 2


@pytest.mark.parametrize(
"python,extra_pythons,expected",
[
(None, [], [None]),
(None, ["3.8"], [None]),
(None, ["3.8", "3.9"], [None]),
(False, [], [False]),
(False, ["3.8"], [False]),
(False, ["3.8", "3.9"], [False]),
("3.5", [], ["3.5"]),
("3.5", ["3.8"], ["3.5", "3.8"]),
("3.5", ["3.8", "3.9"], ["3.5", "3.8", "3.9"]),
(["3.5", "3.9"], [], ["3.5", "3.9"]),
(["3.5", "3.9"], ["3.8"], ["3.5", "3.9", "3.8"]),
(["3.5", "3.9"], ["3.8", "3.4"], ["3.5", "3.9", "3.8", "3.4"]),
(["3.5", "3.9"], ["3.5", "3.9"], ["3.5", "3.9"]),
],
)
def test_extra_pythons(python, extra_pythons, expected):
cfg = mock.sentinel.CONFIG
cfg.force_venv_backend = None
cfg.default_venv_backend = None
cfg.extra_pythons = extra_pythons

manifest = Manifest({}, cfg)

def session_func():
pass

func = Func(session_func, python=python)
for session in manifest.make_session("my_session", func):
manifest.add_session(session)

assert expected == [session.func.python for session in manifest._all_sessions]


def test_add_session_parametrized():
manifest = Manifest({}, create_mock_config())

Expand Down

0 comments on commit e2db5ed

Please sign in to comment.