Skip to content

Commit

Permalink
Merge pull request #94 from janjagusch/add-platform-validation
Browse files Browse the repository at this point in the history
Add platform validation
  • Loading branch information
mariusvniekerk authored Jun 9, 2021
2 parents aea73a7 + a54774a commit 272d94c
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 25 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@
.idea/
build/
dist/

venv/
8 changes: 4 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.4.0
rev: v4.0.1
hooks:
- id: trailing-whitespace
- id: check-ast

- repo: https://gitlab.com/pycqa/flake8
rev: 3.8.4
rev: 3.9.2
hooks:
- id: flake8

- repo: https://github.com/pycqa/isort
rev: 5.7.0
rev: 5.8.0
hooks:
- id: isort
args: ["--profile", "black", "--filter-files"]

- repo: https://github.com/psf/black
rev: 20.8b1
rev: 21.5b1
hooks:
- id: black
language_version: python3
Expand Down
63 changes: 62 additions & 1 deletion conda_lock/conda_lock.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
from click_default_group import DefaultGroup

from conda_lock.common import read_file, read_json, write_file
from conda_lock.errors import PlatformValidationError
from conda_lock.src_parser import LockSpecification
from conda_lock.src_parser.environment_yaml import parse_environment_file
from conda_lock.src_parser.meta_yaml import parse_meta_yaml_file
Expand All @@ -51,6 +52,9 @@
# Captures the domain in the second group.
DOMAIN_PATTERN = re.compile(r"^(https?:\/\/)?([^\/]+)(.*)")

# Captures the platform in the first group.
PLATFORM_PATTERN = re.compile(r"^# platform: (.*)$")

if not (sys.version_info.major >= 3 and sys.version_info.minor >= 6):
print("conda_lock needs to run under python >=3.6")
sys.exit(1)
Expand All @@ -60,6 +64,40 @@
DEFAULT_PLATFORMS = ["osx-64", "linux-64", "win-64"]


def _extract_platform(line: str) -> Optional[str]:
search = PLATFORM_PATTERN.search(line)
if search:
return search.group(1)
return None


def extract_platform(lockfile: str) -> str:
for line in lockfile.strip().split("\n"):
platform = _extract_platform(line)
if platform:
return platform
raise RuntimeError("Cannot find platform in lockfile.")


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

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


def do_validate_platform(lockfile: str):
platform_lockfile = extract_platform(lockfile)
try:
success, platform_sys = _do_validate_platform(platform_lockfile)
except KeyError:
raise RuntimeError(f"Unknown platform type in lockfile '{platform_lockfile}'.")
if not success:
raise PlatformValidationError(
f"Platform in lockfile '{platform_lockfile}' is not compatible with system platform '{platform_sys}'."
)


def conda_pkgs_dir():
global CONDA_PKGS_DIRS
if CONDA_PKGS_DIRS is None:
Expand Down Expand Up @@ -749,6 +787,12 @@ def lock(
default="",
)
@click.option("--auth-file", help="Path to the authentication file.", default="")
@click.option(
"--validate-platform",
is_flag=True,
default=True,
help="Whether the platform compatibility between your lockfile and the host system should be validated.",
)
@click.option(
"--log-level",
help="Log level.",
Expand All @@ -757,13 +801,30 @@ def lock(
)
@click.argument("lock-file")
def install(
conda, mamba, micromamba, prefix, name, lock_file, auth, auth_file, log_level
conda,
mamba,
micromamba,
prefix,
name,
lock_file,
auth,
auth_file,
validate_platform,
log_level,
):
"""Perform a conda install"""
logging.basicConfig(level=log_level)
auth = json.loads(auth) if auth else read_json(auth_file) if auth_file else None
_conda_exe = determine_conda_executable(conda, mamba=mamba, micromamba=micromamba)
install_func = partial(do_conda_install, conda=_conda_exe, prefix=prefix, name=name)
if validate_platform:
lockfile = read_file(lock_file)
try:
do_validate_platform(lockfile)
except PlatformValidationError as error:
raise PlatformValidationError(
error.args[0] + " Disable validation with `--validate-platform=False`."
)
if auth:
lockfile = read_file(lock_file)
with _add_auth(lockfile, auth) as lockfile_with_auth:
Expand Down
11 changes: 11 additions & 0 deletions conda_lock/errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class CondaLockError(Exception):
"""
Generic conda-lock error.
"""


class PlatformValidationError(CondaLockError):
"""
Error that is thrown when trying to install a lockfile that was built
for a different platform.
"""
2 changes: 1 addition & 1 deletion conda_lock/src_parser/selectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@


def filter_platform_selectors(content: str, platform) -> Iterator[str]:
""""""
""" """
# we support a very limited set of selectors that adhere to platform only
platform_sel = {
"linux-64": {"linux64", "unix", "linux"},
Expand Down
44 changes: 28 additions & 16 deletions tests/test_conda_lock.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,31 +266,43 @@ def test_install(tmp_path, conda_exe, zlib_environment, monkeypatch):
lock_filename_template,
],
)
if result.exit_code != 0:
print(result.stdout, file=sys.stdout)
print(result.stderr, file=sys.stderr)
assert result.exit_code == 0

env_name = "test_env"
result = runner.invoke(
main,
[
"install",
"--conda",
conda_exe,
"--prefix",
tmp_path / env_name,
lock_filename,
],
)

def invoke_install(*extra_args):
return runner.invoke(
main,
[
"install",
"--conda",
conda_exe,
"--prefix",
tmp_path / env_name,
*extra_args,
lock_filename,
],
)

result = invoke_install()
print(result.stdout, file=sys.stdout)
print(result.stderr, file=sys.stderr)
logging.debug(
"lockfile contents: \n\n=======\n%s\n\n==========",
pathlib.Path(lock_filename).read_text(),
)
assert result.exit_code == 0
assert _check_package_installed(
package=package,
prefix=str(tmp_path / env_name),
), f"Package {package} does not exist in {tmp_path} environment"
if sys.platform.lower().startswith("linux"):
assert result.exit_code == 0
assert _check_package_installed(
package=package,
prefix=str(tmp_path / env_name),
), f"Package {package} does not exist in {tmp_path} environment"
else:
# since by default we do platform validation we would expect this to fail
assert result.exit_code != 0


@pytest.mark.parametrize(
Expand Down
4 changes: 2 additions & 2 deletions tests/zlib/conda-linux-64.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# env_hash: 3785fe554e946f6fcbd195fa2ce80493621fe6798c631d2e00b8df3f7c3c2282
@EXPLICIT
https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81
https://conda.anaconda.org/conda-forge/linux-64/libgomp-9.3.0-h2828fa1_18.tar.bz2#fc7a2a7e6a741c8afdd764715ac7039d
https://conda.anaconda.org/conda-forge/linux-64/libgomp-9.3.0-h2828fa1_19.tar.bz2#ab0a307912033126da02507b59e79ec9
https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-1_gnu.tar.bz2#561e277319a41d4f24f5c05a9ef63c04
https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-9.3.0-h2828fa1_18.tar.bz2#5a9490c49a3505a6d19bda012cde6ad3
https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-9.3.0-h2828fa1_19.tar.bz2#9d5cdfc51476ee4dcdd96ed2dca3f943
https://conda.anaconda.org/conda-forge/linux-64/zlib-1.2.11-h516909a_1010.tar.bz2#339cc5584e6d26bc73a875ba900028c3

0 comments on commit 272d94c

Please sign in to comment.