Skip to content

Commit

Permalink
Merge pull request #753 from jezdez/platform-option
Browse files Browse the repository at this point in the history
Add `--force-platform` to `conda-lock install` command.
  • Loading branch information
maresb authored Nov 25, 2024
2 parents d1741c7 + 662bc6a commit b7e87a1
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 21 deletions.
19 changes: 15 additions & 4 deletions conda_lock/conda_lock.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,6 @@ def extract_input_hash(lockfile_contents: str) -> Optional[str]:


def _do_validate_platform(platform: str) -> Tuple[bool, str]:
from ensureconda.resolve import platform_subdir

determined_subdir = platform_subdir()
return platform == determined_subdir, determined_subdir

Expand Down Expand Up @@ -985,6 +983,7 @@ def _render_lockfile_for_install(
filename: pathlib.Path,
include_dev_dependencies: bool = True,
extras: Optional[AbstractSet[str]] = None,
force_platform: Optional[str] = None,
) -> Iterator[pathlib.Path]:
"""
Render lock content into a temporary, explicit lockfile for the current platform
Expand All @@ -1006,7 +1005,8 @@ def _render_lockfile_for_install(

lock_content = parse_conda_lock_file(pathlib.Path(filename))

platform = platform_subdir()
platform = force_platform or platform_subdir()

if platform not in lock_content.metadata.platforms:
suggested_platforms_section = "platforms:\n- "
suggested_platforms_section += "\n- ".join(
Expand Down Expand Up @@ -1506,6 +1506,11 @@ def lock(
default=[],
help="include extra dependencies from the lockfile (where applicable)",
)
@click.option(
"--force-platform",
help="Force using the given platform when installing from the lockfile, instead of the native platform.",
default=platform_subdir,
)
@click.argument("lock-file", default=DEFAULT_INSTALL_OPT_LOCK_FILE, type=click.Path())
@click.pass_context
def click_install(
Expand All @@ -1523,6 +1528,7 @@ def click_install(
log_level: TLogLevel,
dev: bool,
extras: List[str],
force_platform: str,
) -> None:
# bail out if we do not encounter the lockfile
lock_file = pathlib.Path(lock_file)
Expand All @@ -1545,6 +1551,7 @@ def click_install(
validate_platform=validate_platform,
dev=dev,
extras=extras,
force_platform=force_platform,
)


Expand All @@ -1561,6 +1568,7 @@ def install(
validate_platform: bool = DEFAULT_INSTALL_OPT_VALIDATE_PLATFORM,
dev: bool = DEFAULT_INSTALL_OPT_DEV,
extras: Optional[List[str]] = None,
force_platform: Optional[str] = None,
) -> None:
if extras is None:
extras = []
Expand All @@ -1580,7 +1588,10 @@ def install(
error.args[0] + " Disable validation with `--no-validate-platform`."
)
with _render_lockfile_for_install(
lock_file, include_dev_dependencies=dev, extras=set(extras)
lock_file,
include_dev_dependencies=dev,
extras=set(extras),
force_platform=force_platform,
) as lockfile:
if _auth is not None:
with _add_auth(read_file(lockfile), _auth) as lockfile_with_auth:
Expand Down
74 changes: 57 additions & 17 deletions tests/test_conda_lock.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

from click.testing import CliRunner
from click.testing import Result as CliResult
from ensureconda.resolve import platform_subdir
from flaky import flaky
from freezegun import freeze_time

Expand Down Expand Up @@ -2060,16 +2061,16 @@ def test_solve_arch_multiple_categories():
assert numpy_deps[0].categories == {"test", "dev"}


def _check_package_installed(package: str, prefix: str):
import glob

files = list(glob.glob(f"{prefix}/conda-meta/{package}-*.json"))
def _check_package_installed(package: str, prefix: str, subdir: Optional[str] = None):
files = list(glob(f"{prefix}/conda-meta/{package}-*.json"))
assert len(files) >= 1
# TODO: validate that all the files are in there
for fn in files:
data = json.load(open(fn))
for expected_file in data["files"]:
assert (Path(prefix) / Path(expected_file)).exists()
if subdir is not None:
assert data["subdir"] == subdir
return True


Expand Down Expand Up @@ -2503,8 +2504,6 @@ def test_virtual_packages(

platform = "linux-64"

from click.testing import CliRunner

for lockfile in glob(f"conda-{platform}.*"):
os.unlink(lockfile)

Expand Down Expand Up @@ -2651,6 +2650,54 @@ def test_fake_conda_env(conda_exe: str, conda_lock_yaml: Path):
assert platform == path.parent.name


def test_forced_platform(
conda_exe: str,
tmp_path: Path,
conda_lock_yaml: Path,
capsys: "pytest.CaptureFixture[str]",
):
if is_micromamba(conda_exe):
pytest.skip(reason="micromamba tests are failing")
if platform_subdir() != "osx-arm64":
pytest.skip("Test is only relevant for macOS on ARM64")

root_prefix = tmp_path / "root_prefix"
root_prefix.mkdir(exist_ok=True)
prefix = root_prefix / "test_env"

package = "bzip2"
platform = "osx-64"
assert platform != platform_subdir()

install_args = [
"install",
"--conda",
conda_exe,
"--prefix",
str(prefix),
"--force-platform",
platform,
str(conda_lock_yaml),
]
with capsys.disabled():
runner = CliRunner(mix_stderr=False)
result = runner.invoke(main, install_args, catch_exceptions=False)

print(result.stdout, file=sys.stdout)
print(result.stderr, file=sys.stderr)
if Path(conda_lock_yaml).exists():
logging.debug(
"lockfile contents: \n\n=======\n%s\n\n==========",
Path(conda_lock_yaml).read_text(),
)

assert _check_package_installed(
package=package,
prefix=str(prefix),
subdir=platform,
), f"Package {package} does not exist in {prefix} environment"


@pytest.mark.parametrize("placeholder", ["$QUETZ_API_KEY", "${QUETZ_API_KEY}"])
def test_private_lock(
quetz_server: "QuetzServerInfo",
Expand All @@ -2666,7 +2713,6 @@ def test_private_lock(
[conda_exe, "--version"], stdout=subprocess.PIPE, encoding="utf8"
)
logging.info("using micromamba version %s", res.stdout)
from ensureconda.resolve import platform_subdir

monkeypatch.chdir(tmp_path)

Expand All @@ -2680,10 +2726,8 @@ def test_private_lock(
(tmp_path / "environment.yml").write_text(content)

with capsys.disabled():
from click.testing import CliRunner, Result

runner = CliRunner(mix_stderr=False)
result: Result = runner.invoke(
result: CliResult = runner.invoke(
main,
[
"lock",
Expand All @@ -2695,7 +2739,7 @@ def test_private_lock(
)
assert result.exit_code == 0

def run_install(with_env: bool) -> Result:
def run_install(with_env: bool) -> CliResult:
with capsys.disabled():
runner = CliRunner(mix_stderr=False)
env_name = uuid.uuid4().hex
Expand All @@ -2706,7 +2750,7 @@ def run_install(with_env: bool) -> Result:
else:
env = dict(os.environ)

result: Result = runner.invoke(
result: CliResult = runner.invoke(
main,
[
"install",
Expand Down Expand Up @@ -2782,10 +2826,8 @@ def test_lookup(
monkeypatch.chdir(cwd)
lookup_filename = str((cwd / lookup_source).absolute())
with capsys.disabled():
from click.testing import CliRunner, Result

runner = CliRunner(mix_stderr=False)
result: Result = runner.invoke(
result: CliResult = runner.invoke(
main,
["lock", "--pypi_to_conda_lookup_file", lookup_filename],
catch_exceptions=False,
Expand Down Expand Up @@ -2863,8 +2905,6 @@ def test_get_pkgs_dirs_mocked_output(info_file: str, expected: Optional[List[Pat
def test_cli_version(capsys: "pytest.CaptureFixture[str]"):
"""It should correctly report its version."""

from click.testing import CliRunner

with capsys.disabled():
runner = CliRunner(mix_stderr=False)
result = runner.invoke(
Expand Down

0 comments on commit b7e87a1

Please sign in to comment.