From 55c7eaf2eb03feb4a4b79e74966c542b75d61401 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Mon, 8 Apr 2024 15:36:00 -0400 Subject: [PATCH] feat: support default=False (#810) Signed-off-by: Henry Schreiner --- docs/config.rst | 6 +++++- nox/_decorators.py | 5 +++++ nox/manifest.py | 5 +++++ nox/registry.py | 14 +++++++++++++- nox/tasks.py | 4 +++- tests/test_tasks.py | 36 ++++++++++++++++++++++++++++++++++++ 6 files changed, 67 insertions(+), 3 deletions(-) diff --git a/docs/config.rst b/docs/config.rst index 9464b68a..7d2a4bb7 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -26,6 +26,10 @@ with ``@nox.session``. For example: You can also configure sessions to run against multiple Python versions as described in :ref:`virtualenv config` and parametrize sessions as described in :ref:`parametrized sessions `. +By default, all sessions will be run if no sessions are specified. You can +remove sessions from this default list by passing ``default=False`` in the +``@nox.session(...)`` decorator. You can also specify a list of sessions to run by +default using the ``nox.options.sessions = [...]`` configuration option. Session description ------------------- @@ -421,7 +425,7 @@ Nox has various :doc:`command line arguments ` that can be used to modify def tests(session): ... -Or, if you wanted to provide a set of sessions that are run by default: +Or, if you wanted to provide a set of sessions that are run by default (this overrides the ``default=`` argument to sessions): .. code-block:: python diff --git a/nox/_decorators.py b/nox/_decorators.py index aaef96a9..25f13de6 100644 --- a/nox/_decorators.py +++ b/nox/_decorators.py @@ -69,6 +69,8 @@ def __init__( venv_params: Any = None, should_warn: Mapping[str, Any] | None = None, tags: Sequence[str] | None = None, + *, + default: bool = True, ) -> None: self.func = func self.python = python @@ -78,6 +80,7 @@ def __init__( self.venv_params = venv_params self.should_warn = dict(should_warn or {}) self.tags = list(tags or []) + self.default = default def __call__(self, *args: Any, **kwargs: Any) -> Any: return self.func(*args, **kwargs) @@ -94,6 +97,7 @@ def copy(self, name: str | None = None) -> Func: self.venv_params, self.should_warn, self.tags, + default=self.default, ) @@ -125,6 +129,7 @@ def __init__(self, func: Func, param_spec: Param) -> None: func.venv_params, func.should_warn, func.tags, + default=func.default, ) self.call_spec = call_spec self.session_signature = session_signature diff --git a/nox/manifest.py b/nox/manifest.py index fa9f7978..e813f02b 100644 --- a/nox/manifest.py +++ b/nox/manifest.py @@ -155,6 +155,11 @@ def filter_by_name(self, specified_sessions: Iterable[str]) -> None: if missing_sessions: raise KeyError(f"Sessions not found: {', '.join(missing_sessions)}") + def filter_by_default(self) -> None: + """Filter sessions in the queue based on the default flag.""" + + self._queue = [x for x in self._queue if x.func.default] + def filter_by_python_interpreter(self, specified_pythons: Sequence[str]) -> None: """Filter sessions in the queue based on the user-specified python interpreter versions. diff --git a/nox/registry.py b/nox/registry.py index 60765ae3..4e5c72aa 100644 --- a/nox/registry.py +++ b/nox/registry.py @@ -43,6 +43,8 @@ def session_decorator( venv_backend: Any | None = ..., venv_params: Any | None = ..., tags: Sequence[str] | None = ..., + *, + default: bool = ..., ) -> Callable[[F], F]: ... @@ -56,6 +58,8 @@ def session_decorator( venv_backend: Any | None = None, venv_params: Any | None = None, tags: Sequence[str] | None = None, + *, + default: bool = True, ) -> F | Callable[[F], F]: """Designate the decorated function as a session.""" # If `func` is provided, then this is the decorator call with the function @@ -75,6 +79,7 @@ def session_decorator( venv_backend=venv_backend, venv_params=venv_params, tags=tags, + default=default, ) if py is not None and python is not None: @@ -88,7 +93,14 @@ def session_decorator( final_name = name or func.__name__ fn = Func( - func, python, reuse_venv, final_name, venv_backend, venv_params, tags=tags + func, + python, + reuse_venv, + final_name, + venv_backend, + venv_params, + tags=tags, + default=default, ) _REGISTRY[final_name] = fn return fn diff --git a/nox/tasks.py b/nox/tasks.py index 595a4056..818dc935 100644 --- a/nox/tasks.py +++ b/nox/tasks.py @@ -173,7 +173,9 @@ def filter_manifest(manifest: Manifest, global_config: Namespace) -> Manifest | # This can raise KeyError if a specified session does not exist; # log this if it happens. The sessions does not come from the Noxfile # if keywords is not empty. - if global_config.sessions is not None: + if global_config.sessions is None: + manifest.filter_by_default() + else: try: manifest.filter_by_name(global_config.sessions) except KeyError as exc: diff --git a/tests/test_tasks.py b/tests/test_tasks.py index 947239ed..122cc0e8 100644 --- a/tests/test_tasks.py +++ b/tests/test_tasks.py @@ -40,6 +40,7 @@ def session_func(): session_func.venv_backend = None session_func.should_warn = {} session_func.tags = [] +session_func.default = True def session_func_with_python(): @@ -48,6 +49,7 @@ def session_func_with_python(): session_func_with_python.python = "3.8" session_func_with_python.venv_backend = None +session_func_with_python.default = True def session_func_venv_pythons_warning(): @@ -346,6 +348,40 @@ def bar(): assert len(manifest) == 2 +@pytest.mark.parametrize("selection", [None, ["qux"], ["quuz"], ["qux", "quuz"]]) +def test_default_false(selection): + @nox.session() + def qux(): + pass + + @nox.session() + def quux(): + pass + + @nox.session(default=False) + def quuz(): + pass + + @nox.session(default=False) + def corge(): + pass + + config = _options.options.namespace(sessions=selection, pythons=(), posargs=[]) + manifest = Manifest( + { + "qux": qux, + "quux": quux, + "quuz": quuz, + "corge": corge, + }, + config, + ) + return_value = tasks.filter_manifest(manifest, config) + assert return_value is manifest + expected = 2 if selection is None else len(selection) + assert len(manifest) == expected + + def test_honor_list_request_noop(): config = _options.options.namespace(list_sessions=False) manifest = {"thing": mock.sentinel.THING}