Skip to content

Commit

Permalink
feat: install_and_run_script (PEP 721) (#847)
Browse files Browse the repository at this point in the history
Signed-off-by: Henry Schreiner <[email protected]>
  • Loading branch information
henryiii authored Oct 7, 2024
1 parent 9058e72 commit 40d6b49
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 0 deletions.
9 changes: 9 additions & 0 deletions docs/tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,15 @@ You can make a session for it like this:
session.install(*requirements)
session.run("peps.py")
This is a common structure for scripts following this PEP, so a helper for it
is provided:

.. code-block:: python
@nox.session
def peps(session):
session.install_and_run_script("peps.py")
Running commands
----------------
Expand Down
35 changes: 35 additions & 0 deletions nox/sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,41 @@ def interactive(self) -> bool:
"""Returns True if Nox is being run in an interactive session or False otherwise."""
return not self._runner.global_config.non_interactive and sys.stdin.isatty()

def install_and_run_script(
self,
script: str | os.PathLike[str],
*args: str | os.PathLike[str],
env: Mapping[str, str | None] | None = None,
include_outer_env: bool = True,
silent: bool = False,
success_codes: Iterable[int] | None = None,
log: bool = True,
stdout: int | IO[str] | None = None,
stderr: int | IO[str] | None = subprocess.STDOUT,
interrupt_timeout: float | None = DEFAULT_INTERRUPT_TIMEOUT,
terminate_timeout: float | None = DEFAULT_TERMINATE_TIMEOUT,
) -> Any | None:
"""
Install dependencies and run a Python script.
"""
deps = (nox.project.load_toml(script) or {}).get("dependencies", [])
self.install(*deps)

return self.run(
"python",
script,
*args,
env=env,
include_outer_env=include_outer_env,
silent=silent,
success_codes=success_codes,
external=None,
stdout=stdout,
stderr=stderr,
interrupt_timeout=interrupt_timeout,
terminate_timeout=terminate_timeout,
)

@property
def invoked_from(self) -> str:
"""The directory that Nox was originally invoked from.
Expand Down
7 changes: 7 additions & 0 deletions tests/resources/pep721example1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# /// script
# dependencies = ["rich"]
# ///

import rich

rich.print("[blue]This worked!")
12 changes: 12 additions & 0 deletions tests/test_sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
HAS_CONDA = shutil.which("conda") is not None
has_conda = pytest.mark.skipif(not HAS_CONDA, reason="Missing conda command.")

DIR = Path(__file__).parent.resolve()


def run_with_defaults(**kwargs):
return {
Expand Down Expand Up @@ -298,6 +300,16 @@ def test_run_error(self):
with pytest.raises(nox.command.CommandFailed):
session.run(sys.executable, "-c", "import sys; sys.exit(1)")

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

with mock.patch.object(nox.command, "run") as run:
session.install_and_run_script(DIR / "resources/pep721example1.py")

assert len(run.call_args_list) == 2
assert "rich" in run.call_args_list[0][0][0]
assert DIR / "resources/pep721example1.py" in run.call_args_list[1][0][0]

def test_run_overly_env(self):
session, runner = self.make_session_and_runner()
runner.venv.env["A"] = "1"
Expand Down

0 comments on commit 40d6b49

Please sign in to comment.