From 2762c3ddfcf6caa056163f7b5b465a7946f65878 Mon Sep 17 00:00:00 2001 From: Rudolfs Praulins <92643844+rudispr@users.noreply.github.com> Date: Fri, 13 Mar 2026 15:56:00 +0100 Subject: [PATCH 01/12] initial proposal. still needs to be tested. --- scripts/zwik_client.py | 183 ++++++++++++++++++++++++----------------- 1 file changed, 108 insertions(+), 75 deletions(-) diff --git a/scripts/zwik_client.py b/scripts/zwik_client.py index 27cb5b7..d84b904 100644 --- a/scripts/zwik_client.py +++ b/scripts/zwik_client.py @@ -28,7 +28,7 @@ from logging.handlers import RotatingFileHandler from typing import Optional -__version__ = "5.16" +__version__ = "5.17" min_supported_conda_version = "4.5.4" max_supported_conda_version = "24.3.0" min_supported_bootstrap_version = 7 @@ -727,7 +727,9 @@ def from_yaml(cls, zwik_settings, yaml_file, working_dir=None): yaml = import_yaml() with open(obj.yaml_path) as fp: - obj.env_data = yaml.load(fp) + value = yaml.load(fp) + # When debugging in PyCharm obj.env_data = yaml.load(fp) will set env_data to None + obj.env_data = value if obj.env_data: if "channels" in obj.env_data: channels = obj.env_data["channels"] @@ -822,12 +824,26 @@ def conda_envs_dir(self): def yaml_hash(self): if not self._yaml_hash: import hashlib + import re + + use_legacy = True + script_ver = "NONE FOUND" + if self.lock_data and self.lock_data.get("script_version"): + script_ver = str(self.lock_data["script_version"]) + from conda.exports import VersionOrder + if VersionOrder(script_ver) > VersionOrder("5.16"): + use_legacy = False hash_md5 = hashlib.md5() with open(self.yaml_path, "r") as f: for line in f.readlines(): + if use_legacy: + line = re.sub(r'\s*#\s*CAUTION:\s*(UNSAFE|OBSOLETE)\s*PACKAGE.*', '', line) + else: + line = line.split('#')[0].rstrip() + '\n' hash_md5.update(line.encode("utf-8")) self._yaml_hash = hash_md5.hexdigest() + return self._yaml_hash @property @@ -893,8 +909,13 @@ def read_version_lock(self): raise LockfileError("lock file seems corrupt") with open(path, "r") as fp: data = yaml.load(fp) + + # Temporarily set lock_data so yaml_hash can read the script_version + self.lock_data = data + if "yaml_hash" not in data: raise LockfileError("lock file seems incomplete") + if data["yaml_hash"] == self.yaml_hash: channel_alias = ( data.get("channel_alias"), @@ -904,8 +925,6 @@ def read_version_lock(self): raise LockfileError("lock file conda alias mismatch") lock_file_channels = ";".join(data.get("channels", [])) env_channels = ";".join(self.channels) - # also compare with list of default channels - # for backwards compatibility def_channels = ";".join(self.settings.default_channels) if lock_file_channels not in (env_channels, def_channels): raise LockfileError("lock file conda channel mismatch") @@ -917,7 +936,7 @@ def read_version_lock(self): log.info("No version lock file found") return None - def write_version_lock(self, lock_dep, obsolete_pkgs=(), unsafe_pkgs=()): + def write_version_lock(self, lock_dep): import getpass import hashlib import io @@ -939,14 +958,6 @@ def write_version_lock(self, lock_dep, obsolete_pkgs=(), unsafe_pkgs=()): "dependencies": sorted(lock_dep), } - labels = {} - for p in obsolete_pkgs: - labels[p] = "obsolete" - for p in unsafe_pkgs: - labels[p] = "unsafe" - if labels: - data["labels"] = labels - stream = io.StringIO() yaml.dump(data, stream) output = stream.getvalue() @@ -1013,15 +1024,17 @@ def create_lockfile(self, additional_dependencies=()): PackagesNotFoundError, ResolvePackageNotFound, UnsatisfiableError, + UnavailableInvalidChannel, ) from conda.exports import subdir obsolete_pkgs = set() unsafe_pkgs = set() last_exception = None - # First check only the original urls, then also the obsolete labels - # and finally also unsafe labels + + # Keep the loop for performance: only query obsolete/unsafe if standard fails for labels in ((), ("obsolete",), ("obsolete", "unsafe")): + print(f"Checking labels {labels}") solver = self.get_solver(dependencies, labels) try: link_precs = solver.solve_final_state() @@ -1030,35 +1043,41 @@ def create_lockfile(self, additional_dependencies=()): for prec in link_precs: split_channel = prec.channel.name.split("/") if len(split_channel) > 1: - # Format is /labels/