bioimageio.spec.generic.v0_3
1from __future__ import annotations 2 3import string 4from functools import partial 5from typing import ( 6 TYPE_CHECKING, 7 Any, 8 ClassVar, 9 Dict, 10 List, 11 Literal, 12 Optional, 13 Sequence, 14 Type, 15 TypeVar, 16 Union, 17) 18 19import annotated_types 20from annotated_types import Len, LowerCase, MaxLen, MinLen 21from pydantic import Field, RootModel, ValidationInfo, field_validator, model_validator 22from typing_extensions import Annotated 23 24from .._internal.common_nodes import Node, ResourceDescrBase 25from .._internal.constants import TAG_CATEGORIES 26from .._internal.field_validation import validate_gh_user 27from .._internal.field_warning import as_warning, warn 28from .._internal.io import ( 29 BioimageioYamlContent, 30 FileDescr, 31 V_suffix, 32 include_in_package_serializer, 33 is_yaml_value, 34 validate_suffix, 35) 36from .._internal.io_basics import AbsoluteFilePath, Sha256 37from .._internal.license_id import DeprecatedLicenseId, LicenseId 38from .._internal.node_converter import Converter 39from .._internal.types import ImportantFileSource, NotEmpty, RelativeFilePath 40from .._internal.url import HttpUrl 41from .._internal.validated_string import ValidatedString 42from .._internal.validator_annotations import ( 43 AfterValidator, 44 Predicate, 45 RestrictCharacters, 46) 47from .._internal.version_type import Version 48from .._internal.warning_levels import ALERT, INFO 49from ._v0_3_converter import convert_from_older_format 50from .v0_2 import Author as _Author_v0_2 51from .v0_2 import BadgeDescr, CoverImageSource, Doi, OrcidId, Uploader 52from .v0_2 import Maintainer as _Maintainer_v0_2 53 54__all__ = [ 55 "Author", 56 "BadgeDescr", 57 "CiteEntry", 58 "DeprecatedLicenseId", 59 "Doi", 60 "FileDescr", 61 "GenericDescr", 62 "HttpUrl", 63 "KNOWN_SPECIFIC_RESOURCE_TYPES", 64 "LicenseId", 65 "LinkedResource", 66 "Maintainer", 67 "OrcidId", 68 "RelativeFilePath", 69 "ResourceId", 70 "Sha256", 71 "Uploader", 72 "VALID_COVER_IMAGE_EXTENSIONS", 73 "Version", 74] 75 76KNOWN_SPECIFIC_RESOURCE_TYPES = ( 77 "application", 78 "collection", 79 "dataset", 80 "model", 81 "notebook", 82) 83VALID_COVER_IMAGE_EXTENSIONS = ( 84 ".gif", 85 ".jpeg", 86 ".jpg", 87 ".png", 88 ".svg", 89) 90 91 92class ResourceId(ValidatedString): 93 root_model: ClassVar[Type[RootModel[Any]]] = RootModel[ 94 Annotated[ 95 NotEmpty[str], 96 RestrictCharacters(string.ascii_lowercase + string.digits + "_-/."), 97 annotated_types.Predicate( 98 lambda s: not (s.startswith("/") or s.endswith("/")) 99 ), 100 ] 101 ] 102 103 104def _validate_md_suffix(value: V_suffix) -> V_suffix: 105 return validate_suffix(value, suffix=".md", case_sensitive=True) 106 107 108DocumentationSource = Annotated[ 109 Union[AbsoluteFilePath, RelativeFilePath, HttpUrl], 110 Field(union_mode="left_to_right"), 111 AfterValidator(_validate_md_suffix), 112 include_in_package_serializer, 113] 114 115 116def _has_no_slash(s: str) -> bool: 117 return "/" not in s and "\\" not in s 118 119 120class Author(_Author_v0_2): 121 name: Annotated[str, Predicate(_has_no_slash)] 122 github_user: Optional[str] = None 123 124 @field_validator("github_user", mode="after") 125 def _validate_gh_user(cls, value: Optional[str]): 126 if value is None: 127 return None 128 else: 129 return validate_gh_user(value) 130 131 132class _AuthorConv(Converter[_Author_v0_2, Author]): 133 def _convert( 134 self, src: _Author_v0_2, tgt: "type[Author] | type[dict[str, Any]]" 135 ) -> "Author | dict[str, Any]": 136 return tgt( 137 name=src.name, 138 github_user=src.github_user, 139 affiliation=src.affiliation, 140 email=src.email, 141 orcid=src.orcid, 142 ) 143 144 145_author_conv = _AuthorConv(_Author_v0_2, Author) 146 147 148class Maintainer(_Maintainer_v0_2): 149 name: Optional[Annotated[str, Predicate(_has_no_slash)]] = None 150 github_user: str 151 152 @field_validator("github_user", mode="after") 153 def validate_gh_user(cls, value: str): 154 return validate_gh_user(value) 155 156 157class _MaintainerConv(Converter[_Maintainer_v0_2, Maintainer]): 158 def _convert( 159 self, src: _Maintainer_v0_2, tgt: "type[Maintainer | dict[str, Any]]" 160 ) -> "Maintainer | dict[str, Any]": 161 return tgt( 162 name=src.name, 163 github_user=src.github_user, 164 affiliation=src.affiliation, 165 email=src.email, 166 orcid=src.orcid, 167 ) 168 169 170_maintainer_conv = _MaintainerConv(_Maintainer_v0_2, Maintainer) 171 172 173class CiteEntry(Node): 174 """A citation that should be referenced in work using this resource.""" 175 176 text: str 177 """free text description""" 178 179 doi: Optional[Doi] = None 180 """A digital object identifier (DOI) is the prefered citation reference. 181 See https://www.doi.org/ for details. 182 Note: 183 Either **doi** or **url** have to be specified. 184 """ 185 186 url: Optional[HttpUrl] = None 187 """URL to cite (preferably specify a **doi** instead/also). 188 Note: 189 Either **doi** or **url** have to be specified. 190 """ 191 192 @model_validator(mode="after") 193 def _check_doi_or_url(self): 194 if not self.doi and not self.url: 195 raise ValueError("Either 'doi' or 'url' is required") 196 197 return self 198 199 200class LinkedResourceBase(Node): 201 202 @model_validator(mode="before") 203 def _remove_version_number( # pyright: ignore[reportUnknownParameterType] 204 cls, value: Union[Any, Dict[Any, Any]] 205 ): 206 if isinstance(value, dict): 207 vn: Any = value.pop("version_number", None) 208 if vn is not None and value.get("version") is None: 209 value["version"] = vn 210 211 return value # pyright: ignore[reportUnknownVariableType] 212 213 version: Optional[Version] = None 214 """The version of the linked resource following SemVer 2.0.""" 215 216 217class LinkedResource(LinkedResourceBase): 218 """Reference to a bioimage.io resource""" 219 220 id: ResourceId 221 """A valid resource `id` from the official bioimage.io collection.""" 222 223 224class BioimageioConfig(Node, extra="allow"): 225 """bioimage.io internal metadata.""" 226 227 228class Config(Node, extra="allow"): 229 """A place to store additional metadata (often tool specific). 230 231 Such additional metadata is typically set programmatically by the respective tool 232 or by people with specific insights into the tool. 233 If you want to store additional metadata that does not match any of the other 234 fields, think of a key unlikely to collide with anyone elses use-case/tool and save 235 it here. 236 237 Please consider creating [an issue in the bioimageio.spec repository](https://github.com/bioimage-io/spec-bioimage-io/issues/new?template=Blank+issue) 238 if you are not sure if an existing field could cover your use case 239 or if you think such a field should exist. 240 """ 241 242 bioimageio: BioimageioConfig = Field(default_factory=BioimageioConfig) 243 """bioimage.io internal metadata.""" 244 245 @model_validator(mode="after") 246 def _validate_extra_fields(self): 247 if self.model_extra: 248 for k, v in self.model_extra.items(): 249 if not isinstance(v, Node) and not is_yaml_value(v): 250 raise ValueError( 251 f"config.{k} is not a valid YAML value or `Node` instance" 252 ) 253 254 return self 255 256 257class GenericModelDescrBase(ResourceDescrBase): 258 """Base for all resource descriptions including of model descriptions""" 259 260 name: Annotated[ 261 Annotated[ 262 str, RestrictCharacters(string.ascii_letters + string.digits + "_+- ()") 263 ], 264 MinLen(5), 265 MaxLen(128), 266 warn(MaxLen(64), "Name longer than 64 characters.", INFO), 267 ] 268 name: Annotated[NotEmpty[str], MaxLen(128)] 269 """A human-friendly name of the resource description. 270 May only contains letters, digits, underscore, minus, parentheses and spaces.""" 271 272 description: Annotated[ 273 str, MaxLen(1024), warn(MaxLen(512), "Description longer than 512 characters.") 274 ] 275 """A string containing a brief description.""" 276 277 covers: Annotated[ 278 List[CoverImageSource], 279 Field( 280 examples=[], 281 description=( 282 "Cover images. Please use an image smaller than 500KB and an aspect" 283 " ratio width to height of 2:1 or 1:1.\nThe supported image formats" 284 f" are: {VALID_COVER_IMAGE_EXTENSIONS}" 285 ), 286 ), 287 ] = Field(default_factory=list) 288 """∈📦 Cover images.""" 289 290 id_emoji: Optional[ 291 Annotated[str, Len(min_length=1, max_length=2), Field(examples=["🦈", "🦥"])] 292 ] = None 293 """UTF-8 emoji for display alongside the `id`.""" 294 295 authors: NotEmpty[List[Author]] 296 """The authors are the creators of this resource description and the primary points of contact.""" 297 298 attachments: List[FileDescr] = Field(default_factory=list) 299 """file attachments""" 300 301 cite: NotEmpty[List[CiteEntry]] 302 """citations""" 303 304 license: Annotated[ 305 Annotated[ 306 Union[LicenseId, DeprecatedLicenseId], Field(union_mode="left_to_right") 307 ], 308 warn( 309 LicenseId, 310 "{value} is deprecated, see https://spdx.org/licenses/{value}.html", 311 ), 312 Field(examples=["CC0-1.0", "MIT", "BSD-2-Clause"]), 313 ] 314 """A [SPDX license identifier](https://spdx.org/licenses/). 315 We do not support custom license beyond the SPDX license list, if you need that please 316 [open a GitHub issue](https://github.com/bioimage-io/spec-bioimage-io/issues/new/choose) 317 to discuss your intentions with the community.""" 318 319 git_repo: Annotated[ 320 Optional[HttpUrl], 321 Field( 322 examples=[ 323 "https://github.com/bioimage-io/spec-bioimage-io/tree/main/example_descriptions/models/unet2d_nuclei_broad" 324 ], 325 ), 326 ] = None 327 """A URL to the Git repository where the resource is being developed.""" 328 329 icon: Union[ 330 Annotated[str, Len(min_length=1, max_length=2)], ImportantFileSource, None 331 ] = None 332 """An icon for illustration, e.g. on bioimage.io""" 333 334 links: Annotated[ 335 List[str], 336 Field( 337 examples=[ 338 ( 339 "ilastik/ilastik", 340 "deepimagej/deepimagej", 341 "zero/notebook_u-net_3d_zerocostdl4mic", 342 ) 343 ], 344 ), 345 ] = Field(default_factory=list) 346 """IDs of other bioimage.io resources""" 347 348 uploader: Optional[Uploader] = None 349 """The person who uploaded the model (e.g. to bioimage.io)""" 350 351 maintainers: List[Maintainer] = Field(default_factory=list) 352 """Maintainers of this resource. 353 If not specified, `authors` are maintainers and at least some of them has to specify their `github_user` name""" 354 355 @partial(as_warning, severity=ALERT) 356 @field_validator("maintainers", mode="after") 357 @classmethod 358 def check_maintainers_exist( 359 cls, maintainers: List[Maintainer], info: ValidationInfo 360 ) -> List[Maintainer]: 361 if not maintainers and "authors" in info.data: 362 authors: List[Author] = info.data["authors"] 363 if all(a.github_user is None for a in authors): 364 raise ValueError( 365 "Missing `maintainers` or any author in `authors` with a specified" 366 + " `github_user` name." 367 ) 368 369 return maintainers 370 371 tags: Annotated[ 372 List[str], 373 Field(examples=[("unet2d", "pytorch", "nucleus", "segmentation", "dsb2018")]), 374 ] = Field(default_factory=list) 375 """Associated tags""" 376 377 @as_warning 378 @field_validator("tags") 379 @classmethod 380 def warn_about_tag_categories( 381 cls, value: List[str], info: ValidationInfo 382 ) -> List[str]: 383 categories = TAG_CATEGORIES.get(info.data["type"], {}) 384 missing_categories: List[Dict[str, Sequence[str]]] = [] 385 for cat, entries in categories.items(): 386 if not any(e in value for e in entries): 387 missing_categories.append({cat: entries}) 388 389 if missing_categories: 390 raise ValueError( 391 f"Missing tags from bioimage.io categories: {missing_categories}" 392 ) 393 394 return value 395 396 version: Optional[Version] = None 397 """The version of the resource following SemVer 2.0.""" 398 399 @model_validator(mode="before") 400 def _remove_version_number( # pyright: ignore[reportUnknownParameterType] 401 cls, value: Union[Any, Dict[Any, Any]] 402 ): 403 if isinstance(value, dict): 404 vn: Any = value.pop("version_number", None) 405 if vn is not None and value.get("version") is None: 406 value["version"] = vn 407 408 return value # pyright: ignore[reportUnknownVariableType] 409 410 411class GenericDescrBase(GenericModelDescrBase): 412 """Base for all resource descriptions except for the model descriptions""" 413 414 implemented_format_version: ClassVar[Literal["0.3.0"]] = "0.3.0" 415 if TYPE_CHECKING: 416 format_version: Literal["0.3.0"] = "0.3.0" 417 else: 418 format_version: Literal["0.3.0"] 419 """The **format** version of this resource specification""" 420 421 @model_validator(mode="before") 422 @classmethod 423 def _convert_from_older_format( 424 cls, data: BioimageioYamlContent, / 425 ) -> BioimageioYamlContent: 426 cls.convert_from_old_format_wo_validation(data) 427 return data 428 429 @classmethod 430 def convert_from_old_format_wo_validation(cls, data: BioimageioYamlContent) -> None: 431 """Convert metadata following an older format version to this classes' format 432 without validating the result. 433 """ 434 convert_from_older_format(data) 435 436 documentation: Annotated[ 437 Optional[DocumentationSource], 438 Field( 439 examples=[ 440 "https://raw.githubusercontent.com/bioimage-io/spec-bioimage-io/main/example_descriptions/models/unet2d_nuclei_broad/README.md", 441 "README.md", 442 ], 443 ), 444 ] = None 445 """∈📦 URL or relative path to a markdown file encoded in UTF-8 with additional documentation. 446 The recommended documentation file name is `README.md`. An `.md` suffix is mandatory.""" 447 448 badges: List[BadgeDescr] = Field(default_factory=list) 449 """badges associated with this resource""" 450 451 config: Config = Field(default_factory=Config) 452 """A field for custom configuration that can contain any keys not present in the RDF spec. 453 This means you should not store, for example, a GitHub repo URL in `config` since there is a `git_repo` field. 454 Keys in `config` may be very specific to a tool or consumer software. To avoid conflicting definitions, 455 it is recommended to wrap added configuration into a sub-field named with the specific domain or tool name, 456 for example: 457 ```yaml 458 config: 459 giraffe_neckometer: # here is the domain name 460 length: 3837283 461 address: 462 home: zoo 463 imagej: # config specific to ImageJ 464 macro_dir: path/to/macro/file 465 ``` 466 If possible, please use [`snake_case`](https://en.wikipedia.org/wiki/Snake_case) for keys in `config`. 467 You may want to list linked files additionally under `attachments` to include them when packaging a resource. 468 (Packaging a resource means downloading/copying important linked files and creating a ZIP archive that contains 469 an altered rdf.yaml file with local references to the downloaded files.)""" 470 471 472ResourceDescrType = TypeVar("ResourceDescrType", bound=GenericDescrBase) 473 474 475class GenericDescr(GenericDescrBase, extra="ignore"): 476 """Specification of the fields used in a generic bioimage.io-compliant resource description file (RDF). 477 478 An RDF is a YAML file that describes a resource such as a model, a dataset, or a notebook. 479 Note that those resources are described with a type-specific RDF. 480 Use this generic resource description, if none of the known specific types matches your resource. 481 """ 482 483 type: Annotated[str, LowerCase] = Field("generic", frozen=True) 484 """The resource type assigns a broad category to the resource.""" 485 486 id: Optional[ 487 Annotated[ResourceId, Field(examples=["affable-shark", "ambitious-sloth"])] 488 ] = None 489 """bioimage.io-wide unique resource identifier 490 assigned by bioimage.io; version **un**specific.""" 491 492 parent: Optional[ResourceId] = None 493 """The description from which this one is derived""" 494 495 source: Optional[HttpUrl] = None 496 """The primary source of the resource""" 497 498 @field_validator("type", mode="after") 499 @classmethod 500 def check_specific_types(cls, value: str) -> str: 501 if value in KNOWN_SPECIFIC_RESOURCE_TYPES: 502 raise ValueError( 503 f"Use the {value} description instead of this generic description for" 504 + f" your '{value}' resource." 505 ) 506 507 return value
121class Author(_Author_v0_2): 122 name: Annotated[str, Predicate(_has_no_slash)] 123 github_user: Optional[str] = None 124 125 @field_validator("github_user", mode="after") 126 def _validate_gh_user(cls, value: Optional[str]): 127 if value is None: 128 return None 129 else: 130 return validate_gh_user(value)
Inherited Members
142class BadgeDescr(Node): 143 """A custom badge""" 144 145 label: Annotated[str, Field(examples=["Open in Colab"])] 146 """badge label to display on hover""" 147 148 icon: Annotated[ 149 Optional[InPackageIfLocalFileSource], 150 Field(examples=["https://colab.research.google.com/assets/colab-badge.svg"]), 151 ] = None 152 """badge icon""" 153 154 url: Annotated[ 155 HttpUrl, 156 Field( 157 examples=[ 158 "https://colab.research.google.com/github/HenriquesLab/ZeroCostDL4Mic/blob/master/Colab_notebooks/U-net_2D_ZeroCostDL4Mic.ipynb" 159 ] 160 ), 161 ] 162 """target URL"""
A custom badge
badge label to display on hover
badge icon
target URL
174class CiteEntry(Node): 175 """A citation that should be referenced in work using this resource.""" 176 177 text: str 178 """free text description""" 179 180 doi: Optional[Doi] = None 181 """A digital object identifier (DOI) is the prefered citation reference. 182 See https://www.doi.org/ for details. 183 Note: 184 Either **doi** or **url** have to be specified. 185 """ 186 187 url: Optional[HttpUrl] = None 188 """URL to cite (preferably specify a **doi** instead/also). 189 Note: 190 Either **doi** or **url** have to be specified. 191 """ 192 193 @model_validator(mode="after") 194 def _check_doi_or_url(self): 195 if not self.doi and not self.url: 196 raise ValueError("Either 'doi' or 'url' is required") 197 198 return self
A citation that should be referenced in work using this resource.
A digital object identifier (DOI) is the prefered citation reference. See https://www.doi.org/ for details.
Note:
Either doi or url have to be specified.
URL to cite (preferably specify a doi instead/also).
Note:
Either doi or url have to be specified.
24class DeprecatedLicenseId(ValidatedString): 25 root_model: ClassVar[Type[RootModel[Any]]] = RootModel[DeprecatedLicenseIdLiteral]
str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.
the pydantic root model to validate the string
115class Doi(ValidatedString): 116 """A digital object identifier, see https://www.doi.org/""" 117 118 root_model: ClassVar[Type[RootModel[Any]]] = RootModel[ 119 Annotated[str, StringConstraints(pattern=DOI_REGEX)] 120 ]
A digital object identifier, see https://www.doi.org/
787class FileDescr(Node): 788 source: ImportantFileSource 789 """∈📦 file source""" 790 791 sha256: Optional[Sha256] = None 792 """SHA256 checksum of the source file""" 793 794 @model_validator(mode="after") 795 def _validate_sha256(self) -> Self: 796 if get_validation_context().perform_io_checks: 797 self.validate_sha256() 798 799 return self 800 801 def validate_sha256(self): 802 context = get_validation_context() 803 if (src_str := str(self.source)) in context.known_files: 804 actual_sha = context.known_files[src_str] 805 else: 806 local_source = download(self.source, sha256=self.sha256).path 807 actual_sha = get_sha256(local_source) 808 context.known_files[str(self.source)] = actual_sha 809 810 if self.sha256 == actual_sha: 811 pass 812 elif self.sha256 is None or context.update_hashes: 813 self.sha256 = actual_sha 814 elif self.sha256 != actual_sha: 815 raise ValueError( 816 f"Sha256 mismatch for {self.source}. Expected {self.sha256}, got " 817 + f"{actual_sha}. Update expected `sha256` or point to the matching " 818 + "file." 819 ) 820 821 return 822 823 def download(self): 824 825 return download(self.source, sha256=self.sha256)
∈📦 file source
801 def validate_sha256(self): 802 context = get_validation_context() 803 if (src_str := str(self.source)) in context.known_files: 804 actual_sha = context.known_files[src_str] 805 else: 806 local_source = download(self.source, sha256=self.sha256).path 807 actual_sha = get_sha256(local_source) 808 context.known_files[str(self.source)] = actual_sha 809 810 if self.sha256 == actual_sha: 811 pass 812 elif self.sha256 is None or context.update_hashes: 813 self.sha256 = actual_sha 814 elif self.sha256 != actual_sha: 815 raise ValueError( 816 f"Sha256 mismatch for {self.source}. Expected {self.sha256}, got " 817 + f"{actual_sha}. Update expected `sha256` or point to the matching " 818 + "file." 819 ) 820 821 return
476class GenericDescr(GenericDescrBase, extra="ignore"): 477 """Specification of the fields used in a generic bioimage.io-compliant resource description file (RDF). 478 479 An RDF is a YAML file that describes a resource such as a model, a dataset, or a notebook. 480 Note that those resources are described with a type-specific RDF. 481 Use this generic resource description, if none of the known specific types matches your resource. 482 """ 483 484 type: Annotated[str, LowerCase] = Field("generic", frozen=True) 485 """The resource type assigns a broad category to the resource.""" 486 487 id: Optional[ 488 Annotated[ResourceId, Field(examples=["affable-shark", "ambitious-sloth"])] 489 ] = None 490 """bioimage.io-wide unique resource identifier 491 assigned by bioimage.io; version **un**specific.""" 492 493 parent: Optional[ResourceId] = None 494 """The description from which this one is derived""" 495 496 source: Optional[HttpUrl] = None 497 """The primary source of the resource""" 498 499 @field_validator("type", mode="after") 500 @classmethod 501 def check_specific_types(cls, value: str) -> str: 502 if value in KNOWN_SPECIFIC_RESOURCE_TYPES: 503 raise ValueError( 504 f"Use the {value} description instead of this generic description for" 505 + f" your '{value}' resource." 506 ) 507 508 return value
Specification of the fields used in a generic bioimage.io-compliant resource description file (RDF).
An RDF is a YAML file that describes a resource such as a model, a dataset, or a notebook. Note that those resources are described with a type-specific RDF. Use this generic resource description, if none of the known specific types matches your resource.
The resource type assigns a broad category to the resource.
bioimage.io-wide unique resource identifier assigned by bioimage.io; version unspecific.
499 @field_validator("type", mode="after") 500 @classmethod 501 def check_specific_types(cls, value: str) -> str: 502 if value in KNOWN_SPECIFIC_RESOURCE_TYPES: 503 raise ValueError( 504 f"Use the {value} description instead of this generic description for" 505 + f" your '{value}' resource." 506 ) 507 508 return value
124 def wrapped_model_post_init(self: BaseModel, context: Any, /) -> None: 125 """We need to both initialize private attributes and call the user-defined model_post_init 126 method. 127 """ 128 init_private_attributes(self, context) 129 original_model_post_init(self, context)
We need to both initialize private attributes and call the user-defined model_post_init method.
Inherited Members
123class HttpUrl(RootHttpUrl): 124 """A URL with the HTTP or HTTPS scheme.""" 125 126 root_model: ClassVar[Type[RootModel[Any]]] = RootModel[pydantic.HttpUrl] 127 _exists: Optional[bool] = None 128 129 def _after_validator(self): 130 self = super()._after_validator() 131 context = get_validation_context() 132 if ( 133 context.perform_io_checks 134 and str(self._validated) not in context.known_files 135 ): 136 self._validated = _validate_url(self._validated) 137 self._exists = True 138 139 return self 140 141 def exists(self): 142 """True if URL is available""" 143 if self._exists is None: 144 try: 145 self._validated = _validate_url(self._validated) 146 except Exception as e: 147 logger.info(e) 148 self._exists = False 149 else: 150 self._exists = True 151 152 return self._exists
A URL with the HTTP or HTTPS scheme.
the pydantic root model to validate the string
141 def exists(self): 142 """True if URL is available""" 143 if self._exists is None: 144 try: 145 self._validated = _validate_url(self._validated) 146 except Exception as e: 147 logger.info(e) 148 self._exists = False 149 else: 150 self._exists = True 151 152 return self._exists
True if URL is available
20class LicenseId(ValidatedString): 21 root_model: ClassVar[Type[RootModel[Any]]] = RootModel[LicenseIdLiteral]
str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.
the pydantic root model to validate the string
218class LinkedResource(LinkedResourceBase): 219 """Reference to a bioimage.io resource""" 220 221 id: ResourceId 222 """A valid resource `id` from the official bioimage.io collection."""
Reference to a bioimage.io resource
Inherited Members
149class Maintainer(_Maintainer_v0_2): 150 name: Optional[Annotated[str, Predicate(_has_no_slash)]] = None 151 github_user: str 152 153 @field_validator("github_user", mode="after") 154 def validate_gh_user(cls, value: str): 155 return validate_gh_user(value)
Inherited Members
142class OrcidId(ValidatedString): 143 """An ORCID identifier, see https://orcid.org/""" 144 145 root_model: ClassVar[Type[RootModel[Any]]] = RootModel[ 146 Annotated[str, AfterValidator(_validate_orcid_id)] 147 ]
An ORCID identifier, see https://orcid.org/
184class RelativeFilePath( 185 RelativePathBase[Union[AbsoluteFilePath, HttpUrl, ZipPath]], frozen=True 186): 187 """A path relative to the `rdf.yaml` file (also if the RDF source is a URL).""" 188 189 def model_post_init(self, __context: Any) -> None: 190 """add validation @private""" 191 if not self.root.parts: # an empty path can only be a directory 192 raise ValueError(f"{self.root} is not a valid file path.") 193 194 super().model_post_init(__context) 195 196 def get_absolute( 197 self, root: "RootHttpUrl | Path | AnyUrl | ZipFile" 198 ) -> "AbsoluteFilePath | HttpUrl | ZipPath": 199 absolute = self._get_absolute_impl(root) 200 if ( 201 isinstance(absolute, Path) 202 and (context := get_validation_context()).perform_io_checks 203 and str(self.root) not in context.known_files 204 and not absolute.is_file() 205 ): 206 raise ValueError(f"{absolute} does not point to an existing file") 207 208 return absolute
A path relative to the rdf.yaml
file (also if the RDF source is a URL).
124 def wrapped_model_post_init(self: BaseModel, context: Any, /) -> None: 125 """We need to both initialize private attributes and call the user-defined model_post_init 126 method. 127 """ 128 init_private_attributes(self, context) 129 original_model_post_init(self, context)
We need to both initialize private attributes and call the user-defined model_post_init method.
196 def get_absolute( 197 self, root: "RootHttpUrl | Path | AnyUrl | ZipFile" 198 ) -> "AbsoluteFilePath | HttpUrl | ZipPath": 199 absolute = self._get_absolute_impl(root) 200 if ( 201 isinstance(absolute, Path) 202 and (context := get_validation_context()).perform_io_checks 203 and str(self.root) not in context.known_files 204 and not absolute.is_file() 205 ): 206 raise ValueError(f"{absolute} does not point to an existing file") 207 208 return absolute
93class ResourceId(ValidatedString): 94 root_model: ClassVar[Type[RootModel[Any]]] = RootModel[ 95 Annotated[ 96 NotEmpty[str], 97 RestrictCharacters(string.ascii_lowercase + string.digits + "_-/."), 98 annotated_types.Predicate( 99 lambda s: not (s.startswith("/") or s.endswith("/")) 100 ), 101 ] 102 ]
str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.
33class Sha256(ValidatedString): 34 """A SHA-256 hash value""" 35 36 root_model: ClassVar[Type[RootModel[Any]]] = RootModel[ 37 Annotated[ 38 str, 39 StringConstraints( 40 strip_whitespace=True, to_lower=True, min_length=64, max_length=64 41 ), 42 ] 43 ]
A SHA-256 hash value
10class Version(RootModel[Union[str, int, float]]): 11 """wraps a packaging.version.Version instance for validation in pydantic models""" 12 13 _version: packaging.version.Version = PrivateAttr() 14 15 def __str__(self): 16 return str(self._version) 17 18 def model_post_init(self, __context: Any) -> None: 19 """set `_version` attribute @private""" 20 self._version = packaging.version.Version(str(self.root)) 21 return super().model_post_init(__context) 22 23 def __lt__(self, other: Version): 24 return self._version < other._version 25 26 def __eq__(self, other: Version): 27 return self._version == other._version 28 29 # the properties below are adopted from and mirror properties of packaging.version.Version 30 @property 31 def epoch(self) -> int: 32 """The epoch of the version. 33 34 >>> Version("2.0.0").epoch 35 0 36 >>> Version("1!2.0.0").epoch 37 1 38 """ 39 return self._version.epoch 40 41 @property 42 def release(self) -> Tuple[int, ...]: 43 """The components of the "release" segment of the version. 44 45 >>> Version("1.2.3").release 46 (1, 2, 3) 47 >>> Version("2.0.0").release 48 (2, 0, 0) 49 >>> Version("1!2.0.0.post0").release 50 (2, 0, 0) 51 52 Includes trailing zeroes but not the epoch or any pre-release / development / 53 post-release suffixes. 54 """ 55 return self._version.release 56 57 @property 58 def pre(self) -> Optional[Tuple[str, int]]: 59 """The pre-release segment of the version. 60 61 >>> print(Version("1.2.3").pre) 62 None 63 >>> Version("1.2.3a1").pre 64 ('a', 1) 65 >>> Version("1.2.3b1").pre 66 ('b', 1) 67 >>> Version("1.2.3rc1").pre 68 ('rc', 1) 69 """ 70 return self._version.pre 71 72 @property 73 def post(self) -> Optional[int]: 74 """The post-release number of the version. 75 76 >>> print(Version("1.2.3").post) 77 None 78 >>> Version("1.2.3.post1").post 79 1 80 """ 81 return self._version.post 82 83 @property 84 def dev(self) -> Optional[int]: 85 """The development number of the version. 86 87 >>> print(Version("1.2.3").dev) 88 None 89 >>> Version("1.2.3.dev1").dev 90 1 91 """ 92 return self._version.dev 93 94 @property 95 def local(self) -> Optional[str]: 96 """The local version segment of the version. 97 98 >>> print(Version("1.2.3").local) 99 None 100 >>> Version("1.2.3+abc").local 101 'abc' 102 """ 103 return self._version.local 104 105 @property 106 def public(self) -> str: 107 """The public portion of the version. 108 109 >>> Version("1.2.3").public 110 '1.2.3' 111 >>> Version("1.2.3+abc").public 112 '1.2.3' 113 >>> Version("1.2.3+abc.dev1").public 114 '1.2.3' 115 """ 116 return self._version.public 117 118 @property 119 def base_version(self) -> str: 120 """The "base version" of the version. 121 122 >>> Version("1.2.3").base_version 123 '1.2.3' 124 >>> Version("1.2.3+abc").base_version 125 '1.2.3' 126 >>> Version("1!1.2.3+abc.dev1").base_version 127 '1!1.2.3' 128 129 The "base version" is the public version of the project without any pre or post 130 release markers. 131 """ 132 return self._version.base_version 133 134 @property 135 def is_prerelease(self) -> bool: 136 """Whether this version is a pre-release. 137 138 >>> Version("1.2.3").is_prerelease 139 False 140 >>> Version("1.2.3a1").is_prerelease 141 True 142 >>> Version("1.2.3b1").is_prerelease 143 True 144 >>> Version("1.2.3rc1").is_prerelease 145 True 146 >>> Version("1.2.3dev1").is_prerelease 147 True 148 """ 149 return self._version.is_prerelease 150 151 @property 152 def is_postrelease(self) -> bool: 153 """Whether this version is a post-release. 154 155 >>> Version("1.2.3").is_postrelease 156 False 157 >>> Version("1.2.3.post1").is_postrelease 158 True 159 """ 160 return self._version.is_postrelease 161 162 @property 163 def is_devrelease(self) -> bool: 164 """Whether this version is a development release. 165 166 >>> Version("1.2.3").is_devrelease 167 False 168 >>> Version("1.2.3.dev1").is_devrelease 169 True 170 """ 171 return self._version.is_devrelease 172 173 @property 174 def major(self) -> int: 175 """The first item of :attr:`release` or ``0`` if unavailable. 176 177 >>> Version("1.2.3").major 178 1 179 """ 180 return self._version.major 181 182 @property 183 def minor(self) -> int: 184 """The second item of :attr:`release` or ``0`` if unavailable. 185 186 >>> Version("1.2.3").minor 187 2 188 >>> Version("1").minor 189 0 190 """ 191 return self._version.minor 192 193 @property 194 def micro(self) -> int: 195 """The third item of :attr:`release` or ``0`` if unavailable. 196 197 >>> Version("1.2.3").micro 198 3 199 >>> Version("1").micro 200 0 201 """ 202 return self._version.micro
wraps a packaging.version.Version instance for validation in pydantic models
30 @property 31 def epoch(self) -> int: 32 """The epoch of the version. 33 34 >>> Version("2.0.0").epoch 35 0 36 >>> Version("1!2.0.0").epoch 37 1 38 """ 39 return self._version.epoch
The epoch of the version.
>>> Version("2.0.0").epoch
0
>>> Version("1!2.0.0").epoch
1
41 @property 42 def release(self) -> Tuple[int, ...]: 43 """The components of the "release" segment of the version. 44 45 >>> Version("1.2.3").release 46 (1, 2, 3) 47 >>> Version("2.0.0").release 48 (2, 0, 0) 49 >>> Version("1!2.0.0.post0").release 50 (2, 0, 0) 51 52 Includes trailing zeroes but not the epoch or any pre-release / development / 53 post-release suffixes. 54 """ 55 return self._version.release
The components of the "release" segment of the version.
>>> Version("1.2.3").release
(1, 2, 3)
>>> Version("2.0.0").release
(2, 0, 0)
>>> Version("1!2.0.0.post0").release
(2, 0, 0)
Includes trailing zeroes but not the epoch or any pre-release / development / post-release suffixes.
57 @property 58 def pre(self) -> Optional[Tuple[str, int]]: 59 """The pre-release segment of the version. 60 61 >>> print(Version("1.2.3").pre) 62 None 63 >>> Version("1.2.3a1").pre 64 ('a', 1) 65 >>> Version("1.2.3b1").pre 66 ('b', 1) 67 >>> Version("1.2.3rc1").pre 68 ('rc', 1) 69 """ 70 return self._version.pre
The pre-release segment of the version.
>>> print(Version("1.2.3").pre)
None
>>> Version("1.2.3a1").pre
('a', 1)
>>> Version("1.2.3b1").pre
('b', 1)
>>> Version("1.2.3rc1").pre
('rc', 1)
72 @property 73 def post(self) -> Optional[int]: 74 """The post-release number of the version. 75 76 >>> print(Version("1.2.3").post) 77 None 78 >>> Version("1.2.3.post1").post 79 1 80 """ 81 return self._version.post
The post-release number of the version.
>>> print(Version("1.2.3").post)
None
>>> Version("1.2.3.post1").post
1
83 @property 84 def dev(self) -> Optional[int]: 85 """The development number of the version. 86 87 >>> print(Version("1.2.3").dev) 88 None 89 >>> Version("1.2.3.dev1").dev 90 1 91 """ 92 return self._version.dev
The development number of the version.
>>> print(Version("1.2.3").dev)
None
>>> Version("1.2.3.dev1").dev
1
94 @property 95 def local(self) -> Optional[str]: 96 """The local version segment of the version. 97 98 >>> print(Version("1.2.3").local) 99 None 100 >>> Version("1.2.3+abc").local 101 'abc' 102 """ 103 return self._version.local
The local version segment of the version.
>>> print(Version("1.2.3").local)
None
>>> Version("1.2.3+abc").local
'abc'
105 @property 106 def public(self) -> str: 107 """The public portion of the version. 108 109 >>> Version("1.2.3").public 110 '1.2.3' 111 >>> Version("1.2.3+abc").public 112 '1.2.3' 113 >>> Version("1.2.3+abc.dev1").public 114 '1.2.3' 115 """ 116 return self._version.public
The public portion of the version.
>>> Version("1.2.3").public
'1.2.3'
>>> Version("1.2.3+abc").public
'1.2.3'
>>> Version("1.2.3+abc.dev1").public
'1.2.3'
118 @property 119 def base_version(self) -> str: 120 """The "base version" of the version. 121 122 >>> Version("1.2.3").base_version 123 '1.2.3' 124 >>> Version("1.2.3+abc").base_version 125 '1.2.3' 126 >>> Version("1!1.2.3+abc.dev1").base_version 127 '1!1.2.3' 128 129 The "base version" is the public version of the project without any pre or post 130 release markers. 131 """ 132 return self._version.base_version
The "base version" of the version.
>>> Version("1.2.3").base_version
'1.2.3'
>>> Version("1.2.3+abc").base_version
'1.2.3'
>>> Version("1!1.2.3+abc.dev1").base_version
'1!1.2.3'
The "base version" is the public version of the project without any pre or post release markers.
134 @property 135 def is_prerelease(self) -> bool: 136 """Whether this version is a pre-release. 137 138 >>> Version("1.2.3").is_prerelease 139 False 140 >>> Version("1.2.3a1").is_prerelease 141 True 142 >>> Version("1.2.3b1").is_prerelease 143 True 144 >>> Version("1.2.3rc1").is_prerelease 145 True 146 >>> Version("1.2.3dev1").is_prerelease 147 True 148 """ 149 return self._version.is_prerelease
Whether this version is a pre-release.
>>> Version("1.2.3").is_prerelease
False
>>> Version("1.2.3a1").is_prerelease
True
>>> Version("1.2.3b1").is_prerelease
True
>>> Version("1.2.3rc1").is_prerelease
True
>>> Version("1.2.3dev1").is_prerelease
True
151 @property 152 def is_postrelease(self) -> bool: 153 """Whether this version is a post-release. 154 155 >>> Version("1.2.3").is_postrelease 156 False 157 >>> Version("1.2.3.post1").is_postrelease 158 True 159 """ 160 return self._version.is_postrelease
Whether this version is a post-release.
>>> Version("1.2.3").is_postrelease
False
>>> Version("1.2.3.post1").is_postrelease
True
162 @property 163 def is_devrelease(self) -> bool: 164 """Whether this version is a development release. 165 166 >>> Version("1.2.3").is_devrelease 167 False 168 >>> Version("1.2.3.dev1").is_devrelease 169 True 170 """ 171 return self._version.is_devrelease
Whether this version is a development release.
>>> Version("1.2.3").is_devrelease
False
>>> Version("1.2.3.dev1").is_devrelease
True
173 @property 174 def major(self) -> int: 175 """The first item of :attr:`release` or ``0`` if unavailable. 176 177 >>> Version("1.2.3").major 178 1 179 """ 180 return self._version.major
The first item of release
or 0
if unavailable.
>>> Version("1.2.3").major
1
182 @property 183 def minor(self) -> int: 184 """The second item of :attr:`release` or ``0`` if unavailable. 185 186 >>> Version("1.2.3").minor 187 2 188 >>> Version("1").minor 189 0 190 """ 191 return self._version.minor
The second item of release
or 0
if unavailable.
>>> Version("1.2.3").minor
2
>>> Version("1").minor
0
193 @property 194 def micro(self) -> int: 195 """The third item of :attr:`release` or ``0`` if unavailable. 196 197 >>> Version("1.2.3").micro 198 3 199 >>> Version("1").micro 200 0 201 """ 202 return self._version.micro
The third item of release
or 0
if unavailable.
>>> Version("1.2.3").micro
3
>>> Version("1").micro
0