Skip to content

Commit

Permalink
Add Session.run_always(). (#331)
Browse files Browse the repository at this point in the history
Fixes #330.
  • Loading branch information
dhermes committed Jun 8, 2020
1 parent 323ab59 commit e3d4696
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 3 deletions.
2 changes: 1 addition & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ Projects that use Nox

Nox is lucky to have several wonderful projects that use it and provide feedback and contributions.

- `Bezier <https://github.com/dhermes/bezier>`__
- `Bézier <https://github.com/dhermes/bezier>`__
- `gapic-generator-python <https://github.com/googleapis/gapic-generator-python>`__
- `gdbgui <https://github.com/cs01/gdbgui>`__
- `Google Assistant SDK <https://github.com/googlesamples/assistant-sdk-python>`__
Expand Down
24 changes: 24 additions & 0 deletions docs/tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,30 @@ If your project is a Python package and you want to install it:
session.install(".")
...
In some cases such as Python binary extensions, your package may depend on
code compiled outside of the Python ecosystem. To make sure a low-level
dependency (e.g. ``libfoo``) is available during installation

.. code-block:: python
@nox.session
def tests(session):
...
session.run_always(
"cmake", "-DCMAKE_BUILD_TYPE=Debug",
"-S", libfoo_src_dir,
"-B", build_dir,
external=True,
)
session.run_always(
"cmake",
"--build", build_dir,
"--config", "Debug",
"--target", "install",
external=True,
)
session.install(".")
...
Running commands
----------------
Expand Down
28 changes: 28 additions & 0 deletions nox/sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,34 @@ def run(

return self._run(*args, env=env, **kwargs)

def run_always(
self, *args: str, env: Mapping[str, str] = None, **kwargs: Any
) -> Optional[Any]:
"""Run a command **always**.
This is a variant of :meth:`run` that runs in all cases, including in
the presence of ``--install-only``.
:param env: A dictionary of environment variables to expose to the
command. By default, all environment variables are passed.
:type env: dict or None
:param bool silent: Silence command output, unless the command fails.
``False`` by default.
:param success_codes: A list of return codes that are considered
successful. By default, only ``0`` is considered success.
:type success_codes: list, tuple, or None
:param external: If False (the default) then programs not in the
virtualenv path will cause a warning. If True, no warning will be
emitted. These warnings can be turned into errors using
``--error-on-external-run``. This has no effect for sessions that
do not have a virtualenv.
:type external: bool
"""
if not args:
raise ValueError("At least one argument required to run_always().")

return self._run(*args, env=env, **kwargs)

def _run(self, *args: str, env: Mapping[str, str] = None, **kwargs: Any) -> Any:
"""Like run(), except that it runs even if --install-only is provided."""
# Legacy support - run a function given.
Expand Down
25 changes: 23 additions & 2 deletions tests/test_sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import argparse
import logging
import operator
import os
import sys
import tempfile
Expand Down Expand Up @@ -151,7 +152,7 @@ def test_run_bad_args(self):
def test_run_with_func(self):
session, _ = self.make_session_and_runner()

assert session.run(lambda a, b: a + b, 1, 2) == 3
assert session.run(operator.add, 1, 2) == 3

def test_run_with_func_error(self):
session, _ = self.make_session_and_runner()
Expand All @@ -168,7 +169,7 @@ def test_run_install_only(self, caplog):
runner.global_config.install_only = True

with mock.patch.object(nox.command, "run") as run:
session.run("spam", "eggs")
assert session.run("spam", "eggs") is None

run.assert_not_called()

Expand Down Expand Up @@ -262,6 +263,26 @@ def test_run_external_with_error_on_external_run_condaenv(self):
with pytest.raises(nox.command.CommandFailed, match="External"):
session.run(sys.executable, "--version")

def test_run_always_bad_args(self):
session, _ = self.make_session_and_runner()

with pytest.raises(ValueError) as exc_info:
session.run_always()

exc_args = exc_info.value.args
assert exc_args == ("At least one argument required to run_always().",)

def test_run_always_success(self):
session, _ = self.make_session_and_runner()

assert session.run_always(operator.add, 1300, 37) == 1337

def test_run_always_install_only(self, caplog):
session, runner = self.make_session_and_runner()
runner.global_config.install_only = True

assert session.run_always(operator.add, 23, 19) == 42

def test_conda_install_bad_args(self):
session, runner = self.make_session_and_runner()
runner.venv = mock.create_autospec(nox.virtualenv.CondaEnv)
Expand Down

0 comments on commit e3d4696

Please sign in to comment.