bioimageio.spec.conda_env

  1import warnings
  2from typing import Any, List, Optional, Union
  3
  4from pydantic import BaseModel, Field, field_validator, model_validator
  5
  6
  7class PipDeps(BaseModel):
  8    """Pip dependencies to include in conda dependecies"""
  9
 10    pip: List[str] = Field(default_factory=list)
 11
 12    def __lt__(self, other: Any):
 13        if isinstance(other, PipDeps):
 14            return len(self.pip) < len(other.pip)
 15        else:
 16            return False
 17
 18    def __gt__(self, other: Any):
 19        if isinstance(other, PipDeps):
 20            return len(self.pip) > len(other.pip)
 21        else:
 22            return False
 23
 24
 25class CondaEnv(BaseModel):
 26    """Represenation of the content of a conda environment.yaml file"""
 27
 28    name: Optional[str] = None
 29    channels: List[str] = Field(default_factory=list)
 30    dependencies: List[Union[str, PipDeps]] = Field(default_factory=list)
 31
 32    @field_validator("name", mode="after")
 33    def _ensure_valid_conda_env_name(cls, value: Optional[str]) -> Optional[str]:
 34        if value is None:
 35            return None
 36
 37        for illegal in ("/", " ", ":", "#"):
 38            value = value.replace(illegal, "")
 39
 40        return value or "empty"
 41
 42    @property
 43    def wo_name(self):
 44        return self.model_construct(**{k: v for k, v in self if k != "name"})
 45
 46    def _get_version(self, package: str):
 47        """Helper to return any verison pin for **package**
 48
 49        TODO: improve: interprete version pin and return structured information.
 50        """
 51        for d in self.dependencies:
 52            if isinstance(d, PipDeps):
 53                for p in d.pip:
 54                    if p.startswith(package):
 55                        return p[len(package) :]
 56            elif d.startswith(package):
 57                return d[len(package) :]
 58
 59
 60class BioimageioCondaEnv(CondaEnv):
 61    """A special `CondaEnv` that
 62    - automatically adds bioimageio specific dependencies
 63    - sorts dependencies
 64    """
 65
 66    @model_validator(mode="after")
 67    def _normalize_bioimageio_conda_env(self):
 68        """update a conda env such that we have bioimageio.core and sorted dependencies"""
 69        for req_channel in ("conda-forge", "nodefaults"):
 70            if req_channel not in self.channels:
 71                self.channels.append(req_channel)
 72
 73        if "defaults" in self.channels:
 74            warnings.warn("removing 'defaults' from conda-channels")
 75            self.channels.remove("defaults")
 76
 77        if "pip" not in self.dependencies:
 78            self.dependencies.append("pip")
 79
 80        for dep in self.dependencies:
 81            if isinstance(dep, PipDeps):
 82                pip_section = dep
 83                pip_section.pip.sort()
 84                break
 85        else:
 86            pip_section = None
 87
 88        if (
 89            pip_section is not None
 90            and any(pd.startswith("bioimageio.core") for pd in pip_section.pip)
 91        ) and not any(
 92            d.startswith("bioimageio.core")
 93            or d.startswith("conda-forge::bioimageio.core")
 94            for d in self.dependencies
 95            if not isinstance(d, PipDeps)
 96        ):
 97            self.dependencies.append("conda-forge::bioimageio.core")
 98
 99        self.dependencies.sort()
100        return self
class PipDeps(pydantic.main.BaseModel):
 8class PipDeps(BaseModel):
 9    """Pip dependencies to include in conda dependecies"""
10
11    pip: List[str] = Field(default_factory=list)
12
13    def __lt__(self, other: Any):
14        if isinstance(other, PipDeps):
15            return len(self.pip) < len(other.pip)
16        else:
17            return False
18
19    def __gt__(self, other: Any):
20        if isinstance(other, PipDeps):
21            return len(self.pip) > len(other.pip)
22        else:
23            return False

Pip dependencies to include in conda dependecies

pip: List[str]
class CondaEnv(pydantic.main.BaseModel):
26class CondaEnv(BaseModel):
27    """Represenation of the content of a conda environment.yaml file"""
28
29    name: Optional[str] = None
30    channels: List[str] = Field(default_factory=list)
31    dependencies: List[Union[str, PipDeps]] = Field(default_factory=list)
32
33    @field_validator("name", mode="after")
34    def _ensure_valid_conda_env_name(cls, value: Optional[str]) -> Optional[str]:
35        if value is None:
36            return None
37
38        for illegal in ("/", " ", ":", "#"):
39            value = value.replace(illegal, "")
40
41        return value or "empty"
42
43    @property
44    def wo_name(self):
45        return self.model_construct(**{k: v for k, v in self if k != "name"})
46
47    def _get_version(self, package: str):
48        """Helper to return any verison pin for **package**
49
50        TODO: improve: interprete version pin and return structured information.
51        """
52        for d in self.dependencies:
53            if isinstance(d, PipDeps):
54                for p in d.pip:
55                    if p.startswith(package):
56                        return p[len(package) :]
57            elif d.startswith(package):
58                return d[len(package) :]

Represenation of the content of a conda environment.yaml file

name: Optional[str]
channels: List[str]
dependencies: List[Union[str, PipDeps]]
wo_name
43    @property
44    def wo_name(self):
45        return self.model_construct(**{k: v for k, v in self if k != "name"})
class BioimageioCondaEnv(CondaEnv):
 61class BioimageioCondaEnv(CondaEnv):
 62    """A special `CondaEnv` that
 63    - automatically adds bioimageio specific dependencies
 64    - sorts dependencies
 65    """
 66
 67    @model_validator(mode="after")
 68    def _normalize_bioimageio_conda_env(self):
 69        """update a conda env such that we have bioimageio.core and sorted dependencies"""
 70        for req_channel in ("conda-forge", "nodefaults"):
 71            if req_channel not in self.channels:
 72                self.channels.append(req_channel)
 73
 74        if "defaults" in self.channels:
 75            warnings.warn("removing 'defaults' from conda-channels")
 76            self.channels.remove("defaults")
 77
 78        if "pip" not in self.dependencies:
 79            self.dependencies.append("pip")
 80
 81        for dep in self.dependencies:
 82            if isinstance(dep, PipDeps):
 83                pip_section = dep
 84                pip_section.pip.sort()
 85                break
 86        else:
 87            pip_section = None
 88
 89        if (
 90            pip_section is not None
 91            and any(pd.startswith("bioimageio.core") for pd in pip_section.pip)
 92        ) and not any(
 93            d.startswith("bioimageio.core")
 94            or d.startswith("conda-forge::bioimageio.core")
 95            for d in self.dependencies
 96            if not isinstance(d, PipDeps)
 97        ):
 98            self.dependencies.append("conda-forge::bioimageio.core")
 99
100        self.dependencies.sort()
101        return self

A special CondaEnv that

  • automatically adds bioimageio specific dependencies
  • sorts dependencies