Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

394 integrates no install flag #411

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions nox/_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,14 @@ def _session_completer(
hidden=True,
finalizer_func=_color_finalizer,
),
_option_set.Option(
"no_install",
"--no-install",
default=False,
group=options.groups["secondary"],
action="store_true",
help="Skip invocations of session methods for installing packages.",
),
)


Expand Down
13 changes: 12 additions & 1 deletion nox/sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,9 @@ def run_always(
do not have a virtualenv.
:type external: bool
"""
if self._runner.global_config.no_install:
return None

if not args:
raise ValueError("At least one argument required to run_always().")

Expand Down Expand Up @@ -368,6 +371,9 @@ def conda_install(
if not args:
raise ValueError("At least one argument required to install().")

if self._runner.global_config.no_install and venv._reused:
return None

# Escape args that should be (conda-specific; pip install does not need this)
args = _dblquote_pkg_install_args(args)

Expand Down Expand Up @@ -417,15 +423,20 @@ def install(self, *args: str, **kwargs: Any) -> None:

.. _pip: https://pip.readthedocs.org
"""
venv = self._runner.venv

if not isinstance(
self._runner.venv, (CondaEnv, VirtualEnv, PassthroughEnv)
venv, (CondaEnv, VirtualEnv, PassthroughEnv)
): # pragma: no cover
raise ValueError(
"A session without a virtualenv can not install dependencies."
)
if not args:
raise ValueError("At least one argument required to install().")

if self._runner.global_config.no_install and venv._reused:
return None

if "silent" not in kwargs:
kwargs["silent"] = True

Expand Down
8 changes: 8 additions & 0 deletions nox/virtualenv.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class ProcessEnv:
def __init__(self, bin_paths: None = None, env: Mapping[str, str] = None) -> None:
self._bin_paths = bin_paths
self.env = os.environ.copy()
self._reused = False

if env is not None:
self.env.update(env)
Expand Down Expand Up @@ -224,6 +225,9 @@ def create(self) -> bool:
logger.debug(
"Re-using existing conda env at {}.".format(self.location_name)
)

self._reused = True

return False

cmd = ["conda", "create", "--yes", "--prefix", self.location]
Expand Down Expand Up @@ -400,6 +404,7 @@ def create(self) -> bool:
[self._resolved_interpreter, "-c", "import sys; print(sys.prefix)"],
silent=True,
)

created = nox.command.run(
[
os.path.join(self.location, "bin", "python"),
Expand All @@ -414,6 +419,9 @@ def create(self) -> bool:
self.location_name
)
)

self._reused = True

return False

if self.venv_or_virtualenv == "virtualenv":
Expand Down
73 changes: 57 additions & 16 deletions tests/test_sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,17 @@ def test__normalize_path_give_up():
assert "any-path" in norm_path


class MockableSession(nox.sessions.Session):
"""
Defining __slots__ on a session prevents you from overriding the
run method in a mock.
Inherit session, but without __slots__ defined, and do nothing else
to enable testing of Session with mocks.
"""

pass


class TestSession:
def make_session_and_runner(self):
func = mock.Mock(spec=["python"], python="3.7")
Expand Down Expand Up @@ -310,6 +321,19 @@ def test_run_always_install_only(self, caplog):

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

def test_run_always_no_install(self, caplog):
caplog.clear()
caplog.set_level(logging.INFO)

session, runner = self.make_session_and_runner()
runner.global_config.no_install = True

with mock.patch.object(nox.command, "run") as run:
assert session.run_always(operator.add, 23, 19) is None
run.assert_not_called()

assert "Skipping run_always" in caplog.text

def test_conda_install_bad_args(self):
session, runner = self.make_session_and_runner()
runner.venv = mock.create_autospec(nox.virtualenv.CondaEnv)
Expand Down Expand Up @@ -359,10 +383,7 @@ def test_conda_install(self, auto_offline, offline):
runner.venv.env = {}
runner.venv.is_offline = lambda: offline

class SessionNoSlots(nox.sessions.Session):
pass

session = SessionNoSlots(runner=runner)
session = MockableSession(runner=runner)

with mock.patch.object(session, "_run", autospec=True) as run:
args = ("--offline",) if auto_offline and offline else ()
Expand All @@ -380,6 +401,23 @@ class SessionNoSlots(nox.sessions.Session):
external="error",
)

def test_conda_venv_reused_with_no_install(self, caplog):
caplog.set_level(logging.INFO)
session, runner = self.make_session_and_runner()

runner.venv = mock.create_autospec(nox.virtualenv.CondaEnv)
runner.venv.location = "/path/to/conda/env"
runner.venv.env = {}
runner.venv.is_offline = lambda: True

runner.global_config.no_install = True
runner.venv._reused = True

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

assert "Venv exists: skipping" in caplog.text

@pytest.mark.parametrize(
"version_constraint",
["no", "yes", "already_dbl_quoted"],
Expand All @@ -398,10 +436,7 @@ def test_conda_install_non_default_kwargs(self, version_constraint):
runner.venv.env = {}
runner.venv.is_offline = lambda: False

class SessionNoSlots(nox.sessions.Session):
pass

session = SessionNoSlots(runner=runner)
session = MockableSession(runner=runner)

if version_constraint == "no":
pkg_requirement = passed_arg = "urllib3"
Expand Down Expand Up @@ -453,10 +488,7 @@ def test_install(self):
runner.venv = mock.create_autospec(nox.virtualenv.VirtualEnv)
runner.venv.env = {}

class SessionNoSlots(nox.sessions.Session):
pass

session = SessionNoSlots(runner=runner)
session = MockableSession(runner=runner)

with mock.patch.object(session, "_run", autospec=True) as run:
session.install("requests", "urllib3")
Expand All @@ -482,10 +514,7 @@ def test_install_non_default_kwargs(self):
runner.venv = mock.create_autospec(nox.virtualenv.VirtualEnv)
runner.venv.env = {}

class SessionNoSlots(nox.sessions.Session):
pass

session = SessionNoSlots(runner=runner)
session = MockableSession(runner=runner)

with mock.patch.object(session, "_run", autospec=True) as run:
session.install("requests", "urllib3", silent=False)
Expand Down Expand Up @@ -538,6 +567,18 @@ def test_skip_no_log(self):
with pytest.raises(nox.sessions._SessionSkip):
session.skip()

def test_session_venv_reused_with_no_install(self, caplog):
caplog.clear()
caplog.set_level(logging.INFO)
session, runner = self.make_session_and_runner()
runner.global_config.no_install = True
runner.venv._reused = True

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

assert "Venv exists: skipping" in caplog.text

def test___slots__(self):
session, _ = self.make_session_and_runner()
with pytest.raises(AttributeError):
Expand Down
4 changes: 4 additions & 0 deletions tests/test_virtualenv.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ def test_condaenv_create(make_conda):
venv.reuse_existing = True
venv.create()
assert dir_.join("test.txt").check()
assert venv._reused


@pytest.mark.skipif(not HAS_CONDA, reason="Missing conda command.")
Expand Down Expand Up @@ -328,7 +329,10 @@ def test_create(monkeypatch, make_one):
assert dir_.join("test.txt").check()
venv.reuse_existing = True
monkeypatch.setattr(nox.virtualenv.nox.command, "run", mock.MagicMock())

venv.create()

assert venv._reused
assert dir_.join("test.txt").check()


Expand Down