bioimageio.spec.conda_env
1import warnings 2from typing import Any, Callable, List, Optional, Union, cast 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( 36 default_factory=cast(Callable[[], List[Union[str, PipDeps]]], list) 37 ) 38 39 @field_validator("name", mode="after") 40 def _ensure_valid_conda_env_name(cls, value: Optional[str]) -> Optional[str]: 41 if value is None: 42 return None 43 44 for illegal in ("/", " ", ":", "#"): 45 value = value.replace(illegal, "") 46 47 return value or "empty" 48 49 @property 50 def wo_name(self): 51 return self.model_construct(**{k: v for k, v in self if k != "name"}) 52 53 def _get_version_pin(self, package: str): 54 """Helper to return any version pin for **package** 55 56 TODO: improve: interprete version pin and return structured information. 57 """ 58 for d in self.dependencies: 59 if isinstance(d, PipDeps): 60 for p in d.pip: 61 if p.startswith(package): 62 return p[len(package) :] 63 elif d.startswith(package): 64 return d[len(package) :] 65 elif "::" in d and (d_wo_channel := d.split("::", 1)[-1]).startswith( 66 package 67 ): 68 return d_wo_channel[len(package) :] 69 70 def get_pip_deps(self) -> List[str]: 71 """Get the pip dependencies of this conda env.""" 72 for dep in self.dependencies: 73 if isinstance(dep, PipDeps): 74 return dep.pip 75 76 return [] 77 78 79class BioimageioCondaEnv(CondaEnv): 80 """A special `CondaEnv` that 81 - automatically adds bioimageio specific dependencies 82 - sorts dependencies 83 """ 84 85 @model_validator(mode="after") 86 def _normalize_bioimageio_conda_env(self): 87 """update a conda env such that we have bioimageio.core and sorted dependencies""" 88 for req_channel in ("conda-forge", "nodefaults"): 89 if req_channel not in self.channels: 90 self.channels.append(req_channel) 91 92 if "defaults" in self.channels: 93 warnings.warn("removing 'defaults' from conda-channels") 94 self.channels.remove("defaults") 95 96 if "pip" not in self.dependencies: 97 self.dependencies.append("pip") 98 99 for dep in self.dependencies: 100 if isinstance(dep, PipDeps): 101 pip_section = dep 102 pip_section.pip.sort() 103 break 104 else: 105 pip_section = None 106 107 if ( 108 pip_section is None 109 or not any(pd.startswith("bioimageio.core") for pd in pip_section.pip) 110 ) and not any( 111 d.startswith("bioimageio.core") 112 or d.startswith("conda-forge::bioimageio.core") 113 for d in self.dependencies 114 if not isinstance(d, PipDeps) 115 ): 116 self.dependencies.append("conda-forge::bioimageio.core") 117 118 self.dependencies.sort() 119 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
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( 37 default_factory=cast(Callable[[], List[Union[str, PipDeps]]], list) 38 ) 39 40 @field_validator("name", mode="after") 41 def _ensure_valid_conda_env_name(cls, value: Optional[str]) -> Optional[str]: 42 if value is None: 43 return None 44 45 for illegal in ("/", " ", ":", "#"): 46 value = value.replace(illegal, "") 47 48 return value or "empty" 49 50 @property 51 def wo_name(self): 52 return self.model_construct(**{k: v for k, v in self if k != "name"}) 53 54 def _get_version_pin(self, package: str): 55 """Helper to return any version pin for **package** 56 57 TODO: improve: interprete version pin and return structured information. 58 """ 59 for d in self.dependencies: 60 if isinstance(d, PipDeps): 61 for p in d.pip: 62 if p.startswith(package): 63 return p[len(package) :] 64 elif d.startswith(package): 65 return d[len(package) :] 66 elif "::" in d and (d_wo_channel := d.split("::", 1)[-1]).startswith( 67 package 68 ): 69 return d_wo_channel[len(package) :] 70 71 def get_pip_deps(self) -> List[str]: 72 """Get the pip dependencies of this conda env.""" 73 for dep in self.dependencies: 74 if isinstance(dep, PipDeps): 75 return dep.pip 76 77 return []
Represenation of the content of a conda environment.yaml file
dependencies: List[Union[str, PipDeps]]
80class BioimageioCondaEnv(CondaEnv): 81 """A special `CondaEnv` that 82 - automatically adds bioimageio specific dependencies 83 - sorts dependencies 84 """ 85 86 @model_validator(mode="after") 87 def _normalize_bioimageio_conda_env(self): 88 """update a conda env such that we have bioimageio.core and sorted dependencies""" 89 for req_channel in ("conda-forge", "nodefaults"): 90 if req_channel not in self.channels: 91 self.channels.append(req_channel) 92 93 if "defaults" in self.channels: 94 warnings.warn("removing 'defaults' from conda-channels") 95 self.channels.remove("defaults") 96 97 if "pip" not in self.dependencies: 98 self.dependencies.append("pip") 99 100 for dep in self.dependencies: 101 if isinstance(dep, PipDeps): 102 pip_section = dep 103 pip_section.pip.sort() 104 break 105 else: 106 pip_section = None 107 108 if ( 109 pip_section is None 110 or not any(pd.startswith("bioimageio.core") for pd in pip_section.pip) 111 ) and not any( 112 d.startswith("bioimageio.core") 113 or d.startswith("conda-forge::bioimageio.core") 114 for d in self.dependencies 115 if not isinstance(d, PipDeps) 116 ): 117 self.dependencies.append("conda-forge::bioimageio.core") 118 119 self.dependencies.sort() 120 return self
A special CondaEnv
that
- automatically adds bioimageio specific dependencies
- sorts dependencies
model_config: ClassVar[pydantic.config.ConfigDict] =
{}
Configuration for the model, should be a dictionary conforming to [ConfigDict
][pydantic.config.ConfigDict].