From 878302430ff6240d705df7c3937d0ce2d3e4b67a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josef=20Kemetm=C3=BCller?= Date: Fri, 10 Nov 2023 10:43:27 +0100 Subject: [PATCH] Attempt supporting multi-file version constraints --- conda_lock/models/lock_spec.py | 109 +++++++++++++++++++++++++++ conda_lock/src_parser/aggregation.py | 2 +- 2 files changed, 110 insertions(+), 1 deletion(-) diff --git a/conda_lock/models/lock_spec.py b/conda_lock/models/lock_spec.py index 6448800b0..df404471c 100644 --- a/conda_lock/models/lock_spec.py +++ b/conda_lock/models/lock_spec.py @@ -24,23 +24,132 @@ class _BaseDependency(StrictModel): def sorted_extras(cls, v: List[str]) -> List[str]: return sorted(v) + def merge(self, other): + if other is None: + return self + if ( + self.name != other.name + or self.manager != other.manager + or self.category != other.category + ): + raise ValueError( + "Cannot merge incompatible dependencies: {self} != {other}" + ) + return _BaseDependency( + name=self.name, + manager=self.manager, + category=self.category, + extras=list(set(self.extras + other.extras)), + ) + class VersionedDependency(_BaseDependency): version: str build: Optional[str] = None conda_channel: Optional[str] = None + @staticmethod + def _merge_versions(version1, version2): + if version1 is None or version1 == "": + return version2 + if version2 is None or version2 == "": + return version1 + return f"{version1},{version2}" + + def merge(self, other): + if other is None: + return self + if ( + self.build is not None + and other.build is not None + and self.build != other.build + ): + raise NotImplementedError( + f"VersionedDependency has two different builds:\n{self}\n{other}" + ) + + if ( + self.conda_channel is not None + and other.conda_channel is not None + and self.conda_channel != other.conda_channel + ): + raise NotImplementedError( + f"VersionedDependency has two different conda_channels:\n{self}\n{other}" + ) + merged_base = super().merge(other) + return VersionedDependency( + name=merged_base.name, + manager=merged_base.manager, + category=merged_base.category, + extras=merged_base.extras, + version=self._merge_versions(self.version, other.version), + build=self.build or other.build, + conda_channel=self.conda_channel or other.conda_channel, + ) + class URLDependency(_BaseDependency): url: str hashes: List[str] + def merge(self, other): + if other is None: + return self + if self.url != other.url: + raise NotImplementedError( + f"URLDependency has two different urls:\n{self}\n{other}" + ) + + if self.hashes != other.hashes: + raise NotImplementedError( + f"URLDependency has two different hashess:\n{self}\n{other}" + ) + merged_base = super().merge(other) + + return URLDependency( + name=merged_base.name, + manager=merged_base.manager, + category=merged_base.category, + extras=merged_base.extras, + url=self.url, + hashes=self.hashes, + ) + class VCSDependency(_BaseDependency): source: str vcs: str rev: Optional[str] = None + def merge(self, other): + if other is None: + return self + if self.source != other.source: + raise NotImplementedError( + f"VCSDependency has two different sources:\n{self}\n{other}" + ) + + if self.vcs != other.vcs: + raise NotImplementedError( + f"VCSDependency has two different vcss:\n{self}\n{other}" + ) + + if self.rev is not None and other.rev is not None and self.rev != other.rev: + raise NotImplementedError( + f"VCSDependency has two different revs:\n{self}\n{other}" + ) + merged_base = super().merge(other) + + return VCSDependency( + name=merged_base.name, + manager=merged_base.manager, + category=merged_base.category, + extras=merged_base.extras, + source=self.source, + vcs=self.vcs, + rev=self.rev or other.rev, + ) + Dependency = Union[VersionedDependency, URLDependency, VCSDependency] diff --git a/conda_lock/src_parser/aggregation.py b/conda_lock/src_parser/aggregation.py index d2b5349b3..401ac3004 100644 --- a/conda_lock/src_parser/aggregation.py +++ b/conda_lock/src_parser/aggregation.py @@ -34,7 +34,7 @@ def aggregate_lock_specs( lock_spec.dependencies.get(platform, []) for lock_spec in lock_specs ): key = (dep.manager, dep.name) - unique_deps[key] = dep + unique_deps[key] = dep.merge(unique_deps.get(key)) dependencies[platform] = list(unique_deps.values())