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

Pip dependencies to include in conda dependecies

pip: List[str]
class CondaEnv(pydantic.main.BaseModel):
31class CondaEnv(BaseModel):
32    """Represenation of the content of a conda environment.yaml file"""
33
34    name: Optional[str] = None
35    channels: List[str] = Field(default_factory=list)
36    dependencies: List[Union[str, PipDeps]] = Field(default_factory=list)
37
38    @field_validator("name", mode="after")
39    def _ensure_valid_conda_env_name(cls, value: Optional[str]) -> Optional[str]:
40        if value is None:
41            return None
42
43        for illegal in ("/", " ", ":", "#"):
44            value = value.replace(illegal, "")
45
46        return value or "empty"
47
48    @property
49    def wo_name(self):
50        return self.model_construct(**{k: v for k, v in self if k != "name"})
51
52    def _get_version(self, package: str):
53        """Helper to return any verison pin for **package**
54
55        TODO: improve: interprete version pin and return structured information.
56        """
57        for d in self.dependencies:
58            if isinstance(d, PipDeps):
59                for p in d.pip:
60                    if p.startswith(package):
61                        return p[len(package) :]
62            elif d.startswith(package):
63                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
48    @property
49    def wo_name(self):
50        return self.model_construct(**{k: v for k, v in self if k != "name"})
class BioimageioCondaEnv(CondaEnv):
 66class BioimageioCondaEnv(CondaEnv):
 67    """A special `CondaEnv` that
 68    - automatically adds bioimageio specific dependencies
 69    - sorts dependencies
 70    """
 71
 72    @model_validator(mode="after")
 73    def _normalize_bioimageio_conda_env(self):
 74        """update a conda env such that we have bioimageio.core and sorted dependencies"""
 75        for req_channel in ("conda-forge", "nodefaults"):
 76            if req_channel not in self.channels:
 77                self.channels.append(req_channel)
 78
 79        if "defaults" in self.channels:
 80            warnings.warn("removing 'defaults' from conda-channels")
 81            self.channels.remove("defaults")
 82
 83        if "pip" not in self.dependencies:
 84            self.dependencies.append("pip")
 85
 86        for dep in self.dependencies:
 87            if isinstance(dep, PipDeps):
 88                pip_section = dep
 89                pip_section.pip.sort()
 90                break
 91        else:
 92            pip_section = None
 93
 94        if (
 95            pip_section is None
 96            or not any(pd.startswith("bioimageio.core") for pd in pip_section.pip)
 97        ) and not any(
 98            d.startswith("bioimageio.core")
 99            or d.startswith("conda-forge::bioimageio.core")
100            for d in self.dependencies
101            if not isinstance(d, PipDeps)
102        ):
103            self.dependencies.append("conda-forge::bioimageio.core")
104
105        self.dependencies.sort()
106        return self

A special CondaEnv that

  • automatically adds bioimageio specific dependencies
  • sorts dependencies