diff --git a/nox/manifest.py b/nox/manifest.py index 616de130..1334fa9d 100644 --- a/nox/manifest.py +++ b/nox/manifest.py @@ -16,7 +16,18 @@ import collections.abc import itertools from collections import OrderedDict -from typing import Any, Iterable, Iterator, List, Mapping, Sequence, Set, Tuple, Union +from typing import ( + Any, + Iterable, + Iterator, + List, + Mapping, + Optional, + Sequence, + Set, + Tuple, + Union, +) from nox._decorators import Call, Func from nox.sessions import Session, SessionRunner @@ -257,7 +268,9 @@ def make_session( def next(self) -> SessionRunner: return self.__next__() - def notify(self, session: Union[str, SessionRunner]) -> bool: + def notify( + self, session: Union[str, SessionRunner], posargs: Optional[List[str]] = None + ) -> bool: """Enqueue the specified session in the queue. If the session is already in the queue, or has been run already, @@ -266,6 +279,10 @@ def notify(self, session: Union[str, SessionRunner]) -> bool: Args: session (Union[str, ~nox.session.Session]): The session to be enqueued. + posargs (Optional[List[str]]): If given, sets the positional + arguments *only* for the queued session. Otherwise, the + standard globally available positional arguments will be + used instead. Returns: bool: Whether the session was added to the queue. @@ -282,6 +299,8 @@ def notify(self, session: Union[str, SessionRunner]) -> bool: # the end of the queue. for s in self._all_sessions: if s == session or s.name == session or session in s.signatures: + if posargs is not None: + s.posargs = posargs self._queue.append(s) return True diff --git a/nox/sessions.py b/nox/sessions.py index 78cdcbba..52f647a1 100644 --- a/nox/sessions.py +++ b/nox/sessions.py @@ -149,9 +149,8 @@ def env(self) -> dict: @property def posargs(self) -> List[str]: - """This is set to any extra arguments - passed to ``nox`` on the commandline.""" - return self._runner.global_config.posargs + """Any extra arguments from the ``nox`` commandline or :class:`Session.notify`.""" + return self._runner.posargs @property def virtualenv(self) -> ProcessEnv: @@ -432,7 +431,11 @@ def install(self, *args: str, **kwargs: Any) -> None: self._run("python", "-m", "pip", "install", *args, external="error", **kwargs) - def notify(self, target: "Union[str, SessionRunner]") -> None: + def notify( + self, + target: "Union[str, SessionRunner]", + posargs: Optional[Iterable[str]] = None, + ) -> None: """Place the given session at the end of the queue. This method is idempotent; multiple notifications to the same session @@ -442,8 +445,14 @@ def notify(self, target: "Union[str, SessionRunner]") -> None: target (Union[str, Callable]): The session to be notified. This may be specified as the appropriate string (same as used for ``nox -s``) or using the function object. + posargs (Optional[Iterable[str]]): If given, sets the positional + arguments *only* for the queued session. Otherwise, the + standard globally available positional arguments will be + used instead. """ - self._runner.manifest.notify(target) + if posargs is not None: + posargs = list(posargs) + self._runner.manifest.notify(target, posargs) def log(self, *args: Any, **kwargs: Any) -> None: """Outputs a log during the session.""" @@ -472,7 +481,8 @@ def __init__( self.func = func self.global_config = global_config self.manifest = manifest - self.venv = None # type: Optional[ProcessEnv] + self.venv: Optional[ProcessEnv] = None + self.posargs: List[str] = global_config.posargs @property def description(self) -> Optional[str]: diff --git a/tests/test_manifest.py b/tests/test_manifest.py index a3631511..35eb775d 100644 --- a/tests/test_manifest.py +++ b/tests/test_manifest.py @@ -34,10 +34,11 @@ def create_mock_sessions(): def create_mock_config(): - cfg = mock.sentinel.CONFIG + cfg = mock.sentinel.MOCKED_CONFIG cfg.force_venv_backend = None cfg.default_venv_backend = None cfg.extra_pythons = None + cfg.posargs = [] return cfg @@ -223,9 +224,7 @@ def session_func(): ], ) def test_extra_pythons(python, extra_pythons, expected): - cfg = mock.sentinel.CONFIG - cfg.force_venv_backend = None - cfg.default_venv_backend = None + cfg = create_mock_config() cfg.extra_pythons = extra_pythons manifest = Manifest({}, cfg) @@ -345,6 +344,21 @@ def my_session(session): assert len(manifest) == 1 +def test_notify_with_posargs(): + cfg = create_mock_config() + manifest = Manifest({}, cfg) + + session = manifest.make_session("my_session", Func(lambda session: None))[0] + manifest.add_session(session) + + # delete my_session from the queue + manifest.filter_by_name(()) + + assert session.posargs is cfg.posargs + assert manifest.notify("my_session", posargs=["--an-arg"]) + assert session.posargs == ["--an-arg"] + + def test_notify_error(): manifest = Manifest({}, create_mock_config()) with pytest.raises(ValueError): diff --git a/tests/test_sessions.py b/tests/test_sessions.py index 8bb9b787..150c15ec 100644 --- a/tests/test_sessions.py +++ b/tests/test_sessions.py @@ -505,7 +505,11 @@ def test_notify(self): session.notify("other") - runner.manifest.notify.assert_called_once_with("other") + runner.manifest.notify.assert_called_once_with("other", None) + + session.notify("other", posargs=["--an-arg"]) + + runner.manifest.notify.assert_called_with("other", ["--an-arg"]) def test_log(self, caplog): caplog.set_level(logging.INFO)