Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
features:
- |
Riot will attempt to re-install the development package if changes to the
setup files are detected when using the skip option with the run command.
33 changes: 29 additions & 4 deletions riot/riot.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import itertools
import logging
import os
from pathlib import Path
import shutil
import subprocess
import sys
Expand All @@ -25,6 +26,7 @@

DEFAULT_RIOT_PATH = ".riot"
DEFAULT_RIOT_ENV_PREFIX = "venv_py"
SETUP_FILES = ["setup.py", "pyproject.toml"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setup.cfg?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. I think pip looks for just setup.py and/or pyproject.toml, but setup.cfg should probably be hashed as well, if present 🤔

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we want to add setup.cfg here? probably a good idea since it can contain install/build requirements

... would that mean we also care about requirements.txt? or any variation of those?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
SETUP_FILES = ["setup.py", "pyproject.toml"]
SETUP_FILES = ["setup.py", "pyproject.toml", "setup.cfg"]


SHELL = os.getenv("SHELL", "/bin/bash")
ENCODING = sys.getdefaultencoding()
Expand Down Expand Up @@ -886,8 +888,16 @@ def generate_base_venvs(
logger.error("Python version '%s' not found.", py)
else:
if not install_deps and skip_deps:
logger.info("Skipping global deps install.")
continue
hash_file = Path(py.venv_path) / ".riot-setup-hash"
if (
hash_file.exists()
and hash_file.read_text().strip() == setup_hash()
):
logger.info("Skipping global deps install.")
continue
logger.info(
"Re-installing development package because of setup hash mismatch."
)

# Install the dev package into the base venv.
install_dev_pkg(py.venv_path)
Expand Down Expand Up @@ -1082,9 +1092,21 @@ def pip_deps(pkgs: t.Dict[str, str]) -> str:
)


def setup_hash() -> t.Optional[str]:
"""Generate a hash from the current setup files."""
setup_hash = ""

for setup_file in SETUP_FILES:
path = Path(setup_file)
if path.exists():
setup_hash += sha256(path.read_bytes()).hexdigest()

return setup_hash or None


def install_dev_pkg(venv_path):
for setup_file in {"setup.py", "pyproject.toml"}:
if os.path.exists(setup_file):
for setup_file in SETUP_FILES:
if Path(setup_file).exists():
break
else:
logger.warning("No Python setup file found. Skipping dev package installation.")
Expand All @@ -1098,3 +1120,6 @@ def install_dev_pkg(venv_path):
except CmdFailure as e:
logger.error("Dev install failed, aborting!\n%s", e.proc.stdout)
sys.exit(1)

# Store setup hash in the interpreter venv
(Path(venv_path) / ".riot-setup-hash").write_text(setup_hash())
72 changes: 70 additions & 2 deletions tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,6 @@ def test_failure():
result.stdout,
re.MULTILINE,
)
assert result.stderr == ""
assert result.returncode == 1


Expand Down Expand Up @@ -545,7 +544,6 @@ def test_run_cmdargs(tmp_path: pathlib.Path, tmp_run: _T_TmpRun) -> None:
)
result = tmp_run("riot run -s test_cmdargs -- -k filter")
assert "cmdargs=-k filter" in result.stdout
assert result.stderr == ""
assert result.returncode == 0


Expand Down Expand Up @@ -871,3 +869,73 @@ def test_shell_created(tmp_path: pathlib.Path, tmp_run: _T_TmpRun) -> None:
r"Setting venv path to .+[.]riot/venv_py[0-9]+_requests", result.stderr
)
assert result.returncode == 0, result.stderr


def test_setup_hash_changed(tmp_path: pathlib.Path, tmp_run: _T_TmpRun) -> None:
rf_path = tmp_path / "riotfile.py"
rf_path.write_text(
"""
from riot import Venv, latest
venv = Venv(
name="test",
pys=["3"],
pkgs={"requests": latest},
command="python -c 'import requests'",
)
""",
)

setup_path = tmp_path / "setup.py"
setup_path.write_text(
"""
from setuptools import setup, find_packages

setup(
name='foo',
version='1.0.0',
url='https://github.com/bar/foo.git',
author='Me',
author_email='author@email.com',
description='Noop',
packages=find_packages(),
install_requires=[],
)
""",
)
result = tmp_run("riot -vd --pipe run -s test")
assert "Installing dev package (edit mode)" in result.stderr
assert result.returncode == 0, result.stderr

# Check that we skip re-installing the dev packege if no changes to setup.py
result = tmp_run("riot -vd --pipe run -s test")
assert "Skipping global deps install." in result.stderr
assert result.returncode == 0, result.stderr

# Modify setup.py and re-run to check that we are re-installing the dev package
setup_path.write_text(
"""
from setuptools import setup, find_packages

setup(
name='foo',
version='2.0.0',
url='https://github.com/bar/foo.git',
author='Me',
author_email='author@email.com',
description='Noop',
packages=find_packages(),
install_requires=[],
)
""",
)
result = tmp_run("riot -vd --pipe run -s test")
assert (
"Re-installing development package because of setup hash mismatch."
in result.stderr
)
assert result.returncode == 0, result.stderr

# Check that we skip re-installing the dev packege if no changes to setup.py
result = tmp_run("riot -vd --pipe run -s test")
assert "Skipping global deps install." in result.stderr
assert result.returncode == 0, result.stderr