bioimageio.spec.generic.v0_2

  1import string
  2from typing import (
  3    Any,
  4    ClassVar,
  5    Dict,
  6    List,
  7    Literal,
  8    Mapping,
  9    Optional,
 10    Sequence,
 11    Type,
 12    TypeVar,
 13    Union,
 14)
 15
 16import annotated_types
 17from annotated_types import Len, LowerCase, MaxLen
 18from pydantic import (
 19    EmailStr,
 20    Field,
 21    RootModel,
 22    ValidationInfo,
 23    field_validator,
 24    model_validator,
 25)
 26from typing_extensions import Annotated, Self, assert_never
 27
 28from .._internal.common_nodes import Node, ResourceDescrBase
 29from .._internal.constants import TAG_CATEGORIES
 30from .._internal.field_warning import as_warning, issue_warning, warn
 31from .._internal.io import (
 32    BioimageioYamlContent,
 33    InPackageIfLocalFileSource,
 34    WithSuffix,
 35    YamlValue,
 36    include_in_package_serializer,
 37)
 38from .._internal.io_basics import AbsoluteFilePath as AbsoluteFilePath
 39from .._internal.type_guards import is_sequence
 40from .._internal.types import (
 41    DeprecatedLicenseId,
 42    FileSource,
 43    ImportantFileSource,
 44    LicenseId,
 45    NotEmpty,
 46)
 47from .._internal.types import Doi as Doi
 48from .._internal.types import OrcidId as OrcidId
 49from .._internal.types import RelativeFilePath as RelativeFilePath
 50from .._internal.url import HttpUrl as HttpUrl
 51from .._internal.validated_string import ValidatedString
 52from .._internal.validator_annotations import AfterValidator, RestrictCharacters
 53from .._internal.version_type import Version as Version
 54from ._v0_2_converter import convert_from_older_format as _convert_from_older_format
 55
 56
 57class ResourceId(ValidatedString):
 58    root_model: ClassVar[Type[RootModel[Any]]] = RootModel[
 59        Annotated[
 60            NotEmpty[str],
 61            AfterValidator(str.lower),  # convert upper case on the fly
 62            RestrictCharacters(string.ascii_lowercase + string.digits + "_-/."),
 63            annotated_types.Predicate(
 64                lambda s: not (s.startswith("/") or s.endswith("/"))
 65            ),
 66        ]
 67    ]
 68
 69
 70KNOWN_SPECIFIC_RESOURCE_TYPES = (
 71    "application",
 72    "collection",
 73    "dataset",
 74    "model",
 75    "notebook",
 76)
 77
 78VALID_COVER_IMAGE_EXTENSIONS = (
 79    ".gif",
 80    ".jpeg",
 81    ".jpg",
 82    ".png",
 83    ".svg",
 84    ".tif",
 85    ".tiff",
 86)
 87
 88_WithImageSuffix = WithSuffix(VALID_COVER_IMAGE_EXTENSIONS, case_sensitive=False)
 89CoverImageSource = Annotated[
 90    Union[AbsoluteFilePath, RelativeFilePath, HttpUrl],
 91    Field(union_mode="left_to_right"),
 92    _WithImageSuffix,
 93    include_in_package_serializer,
 94]
 95
 96
 97class AttachmentsDescr(Node):
 98    model_config = {**Node.model_config, "extra": "allow"}
 99    """update pydantic model config to allow additional unknown keys"""
100    files: List[ImportantFileSource] = Field(default_factory=list)
101    """∈📦 File attachments"""
102
103
104def _remove_slashes(s: str):
105    return s.replace("/", "").replace("\\", "")
106
107
108class Uploader(Node):
109    email: EmailStr
110    """Email"""
111    name: Optional[Annotated[str, AfterValidator(_remove_slashes)]] = None
112    """name"""
113
114
115class _Person(Node):
116    affiliation: Optional[str] = None
117    """Affiliation"""
118
119    email: Optional[EmailStr] = None
120    """Email"""
121
122    orcid: Annotated[Optional[OrcidId], Field(examples=["0000-0001-2345-6789"])] = None
123    """An [ORCID iD](https://support.orcid.org/hc/en-us/sections/360001495313-What-is-ORCID
124    ) in hyphenated groups of 4 digits, (and [valid](
125    https://support.orcid.org/hc/en-us/articles/360006897674-Structure-of-the-ORCID-Identifier
126    ) as per ISO 7064 11,2.)
127    """
128
129
130class Author(_Person):
131    name: Annotated[str, AfterValidator(_remove_slashes)]
132    github_user: Optional[str] = None  # TODO: validate github_user
133
134
135class Maintainer(_Person):
136    name: Optional[Annotated[str, AfterValidator(_remove_slashes)]] = None
137    github_user: str
138
139
140class BadgeDescr(Node):
141    """A custom badge"""
142
143    label: Annotated[str, Field(examples=["Open in Colab"])]
144    """badge label to display on hover"""
145
146    icon: Annotated[
147        Optional[InPackageIfLocalFileSource],
148        Field(examples=["https://colab.research.google.com/assets/colab-badge.svg"]),
149    ] = None
150    """badge icon"""
151
152    url: Annotated[
153        HttpUrl,
154        Field(
155            examples=[
156                "https://colab.research.google.com/github/HenriquesLab/ZeroCostDL4Mic/blob/master/Colab_notebooks/U-net_2D_ZeroCostDL4Mic.ipynb"
157            ]
158        ),
159    ]
160    """target URL"""
161
162
163class CiteEntry(Node):
164    text: str
165    """free text description"""
166
167    doi: Optional[Doi] = None
168    """A digital object identifier (DOI) is the prefered citation reference.
169    See https://www.doi.org/ for details. (alternatively specify `url`)"""
170
171    @field_validator("doi", mode="before")
172    @classmethod
173    def accept_prefixed_doi(cls, doi: Any) -> Any:
174        if isinstance(doi, str):
175            for doi_prefix in ("https://doi.org/", "http://dx.doi.org/"):
176                if doi.startswith(doi_prefix):
177                    doi = doi[len(doi_prefix) :]
178                    break
179
180        return doi
181
182    url: Optional[str] = None
183    """URL to cite (preferably specify a `doi` instead)"""
184
185    @model_validator(mode="after")
186    def _check_doi_or_url(self) -> Self:
187        if not self.doi and not self.url:
188            raise ValueError("Either 'doi' or 'url' is required")
189
190        return self
191
192
193class LinkedResource(Node):
194    """Reference to a bioimage.io resource"""
195
196    id: ResourceId
197    """A valid resource `id` from the bioimage.io collection."""
198
199    version_number: Optional[int] = None
200    """version number (n-th published version, not the semantic version) of linked resource"""
201
202
203class GenericModelDescrBase(ResourceDescrBase):
204    """Base for all resource descriptions including of model descriptions"""
205
206    name: Annotated[NotEmpty[str], warn(MaxLen(128), "Longer than 128 characters.")]
207    """A human-friendly name of the resource description"""
208
209    description: str
210
211    covers: Annotated[
212        List[CoverImageSource],
213        Field(
214            examples=["cover.png"],
215            description=(
216                "Cover images. Please use an image smaller than 500KB and an aspect"
217                " ratio width to height of 2:1.\nThe supported image formats are:"
218                f" {VALID_COVER_IMAGE_EXTENSIONS}"
219            ),
220        ),
221    ] = Field(
222        default_factory=list,
223    )
224    """∈📦 Cover images. Please use an image smaller than 500KB and an aspect ratio width to height of 2:1."""
225
226    id_emoji: Optional[
227        Annotated[str, Len(min_length=1, max_length=1), Field(examples=["🦈", "🦥"])]
228    ] = None
229    """UTF-8 emoji for display alongside the `id`."""
230
231    authors: List[Author] = Field(default_factory=list)
232    """The authors are the creators of the RDF and the primary points of contact."""
233
234    @field_validator("authors", mode="before")
235    @classmethod
236    def accept_author_strings(cls, authors: Union[Any, Sequence[Any]]) -> Any:
237        """we unofficially accept strings as author entries"""
238        if is_sequence(authors):
239            authors = [{"name": a} if isinstance(a, str) else a for a in authors]
240
241        if not authors:
242            issue_warning("missing", value=authors, field="authors")
243
244        return authors
245
246    attachments: Optional[AttachmentsDescr] = None
247    """file and other attachments"""
248
249    cite: List[CiteEntry] = Field(default_factory=list)
250    """citations"""
251
252    @field_validator("cite", mode="after")
253    @classmethod
254    def _warn_empty_cite(cls, value: Any):
255        if not value:
256            issue_warning("missing", value=value, field="cite")
257
258        return value
259
260    config: Annotated[
261        Dict[str, YamlValue],
262        Field(
263            examples=[
264                dict(
265                    bioimageio={
266                        "my_custom_key": 3837283,
267                        "another_key": {"nested": "value"},
268                    },
269                    imagej={"macro_dir": "path/to/macro/file"},
270                )
271            ],
272        ),
273    ] = Field(default_factory=dict)
274    """A field for custom configuration that can contain any keys not present in the RDF spec.
275    This means you should not store, for example, a github repo URL in `config` since we already have the
276    `git_repo` field defined in the spec.
277    Keys in `config` may be very specific to a tool or consumer software. To avoid conflicting definitions,
278    it is recommended to wrap added configuration into a sub-field named with the specific domain or tool name,
279    for example:
280    ```yaml
281    config:
282        bioimageio:  # here is the domain name
283            my_custom_key: 3837283
284            another_key:
285                nested: value
286        imagej:       # config specific to ImageJ
287            macro_dir: path/to/macro/file
288    ```
289    If possible, please use [`snake_case`](https://en.wikipedia.org/wiki/Snake_case) for keys in `config`.
290    You may want to list linked files additionally under `attachments` to include them when packaging a resource
291    (packaging a resource means downloading/copying important linked files and creating a ZIP archive that contains
292    an altered rdf.yaml file with local references to the downloaded files)"""
293
294    download_url: Optional[HttpUrl] = None
295    """URL to download the resource from (deprecated)"""
296
297    git_repo: Annotated[
298        Optional[str],
299        Field(
300            examples=[
301                "https://github.com/bioimage-io/spec-bioimage-io/tree/main/example_descriptions/models/unet2d_nuclei_broad"
302            ],
303        ),
304    ] = None
305    """A URL to the Git repository where the resource is being developed."""
306
307    icon: Union[
308        Annotated[str, Len(min_length=1, max_length=2)], ImportantFileSource, None
309    ] = None
310    """An icon for illustration"""
311
312    links: Annotated[
313        List[str],
314        Field(
315            examples=[
316                (
317                    "ilastik/ilastik",
318                    "deepimagej/deepimagej",
319                    "zero/notebook_u-net_3d_zerocostdl4mic",
320                )
321            ],
322        ),
323    ] = Field(default_factory=list)
324    """IDs of other bioimage.io resources"""
325
326    uploader: Optional[Uploader] = None
327    """The person who uploaded the model (e.g. to bioimage.io)"""
328
329    maintainers: List[Maintainer] = Field(default_factory=list)
330    """Maintainers of this resource.
331    If not specified `authors` are maintainers and at least some of them should specify their `github_user` name"""
332
333    rdf_source: Optional[FileSource] = None
334    """Resource description file (RDF) source; used to keep track of where an rdf.yaml was loaded from.
335    Do not set this field in a YAML file."""
336
337    tags: Annotated[
338        List[str],
339        Field(examples=[("unet2d", "pytorch", "nucleus", "segmentation", "dsb2018")]),
340    ] = Field(default_factory=list)
341    """Associated tags"""
342
343    @as_warning
344    @field_validator("tags")
345    @classmethod
346    def warn_about_tag_categories(
347        cls, value: List[str], info: ValidationInfo
348    ) -> List[str]:
349        categories = TAG_CATEGORIES.get(info.data["type"], {})
350        missing_categories: List[Mapping[str, Sequence[str]]] = []
351        for cat, entries in categories.items():
352            if not any(e in value for e in entries):
353                missing_categories.append({cat: entries})
354
355        if missing_categories:
356            raise ValueError(
357                "Missing tags from bioimage.io categories: {missing_categories}"
358            )
359
360        return value
361
362    version: Optional[Version] = None
363    """The version of the resource following SemVer 2.0."""
364
365    version_number: Optional[int] = None
366    """version number (n-th published version, not the semantic version)"""
367
368
369class GenericDescrBase(GenericModelDescrBase):
370    """Base for all resource descriptions except for the model descriptions"""
371
372    format_version: Literal["0.2.4"] = "0.2.4"
373    """The format version of this resource specification
374    (not the `version` of the resource description)
375    When creating a new resource always use the latest micro/patch version described here.
376    The `format_version` is important for any consumer software to understand how to parse the fields.
377    """
378
379    @model_validator(mode="before")
380    @classmethod
381    def _convert_from_older_format(
382        cls, data: BioimageioYamlContent, /
383    ) -> BioimageioYamlContent:
384        _convert_from_older_format(data)
385        return data
386
387    badges: List[BadgeDescr] = Field(default_factory=list)
388    """badges associated with this resource"""
389
390    documentation: Annotated[
391        Optional[ImportantFileSource],
392        Field(
393            examples=[
394                "https://raw.githubusercontent.com/bioimage-io/spec-bioimage-io/main/example_descriptions/models/unet2d_nuclei_broad/README.md",
395                "README.md",
396            ],
397        ),
398    ] = None
399    """∈📦 URL or relative path to a markdown file with additional documentation.
400    The recommended documentation file name is `README.md`. An `.md` suffix is mandatory."""
401
402    license: Annotated[
403        Union[LicenseId, DeprecatedLicenseId, str, None],
404        Field(union_mode="left_to_right", examples=["CC0-1.0", "MIT", "BSD-2-Clause"]),
405    ] = None
406    """A [SPDX license identifier](https://spdx.org/licenses/).
407    We do not support custom license beyond the SPDX license list, if you need that please
408    [open a GitHub issue](https://github.com/bioimage-io/spec-bioimage-io/issues/new/choose
409    ) to discuss your intentions with the community."""
410
411    @field_validator("license", mode="after")
412    @classmethod
413    def deprecated_spdx_license(
414        cls, value: Optional[Union[LicenseId, DeprecatedLicenseId, str]]
415    ):
416        if isinstance(value, LicenseId):
417            pass
418        elif value is None:
419            issue_warning("missing", value=value, field="license")
420        elif isinstance(value, DeprecatedLicenseId):
421            issue_warning(
422                "'{value}' is a deprecated license identifier.",
423                value=value,
424                field="license",
425            )
426        elif isinstance(value, str):
427            issue_warning(
428                "'{value}' is an unknown license identifier.",
429                value=value,
430                field="license",
431            )
432        else:
433            assert_never(value)
434
435        return value
436
437
438ResourceDescrType = TypeVar("ResourceDescrType", bound=GenericDescrBase)
439
440
441class GenericDescr(GenericDescrBase, extra="ignore"):
442    """Specification of the fields used in a generic bioimage.io-compliant resource description file (RDF).
443
444    An RDF is a YAML file that describes a resource such as a model, a dataset, or a notebook.
445    Note that those resources are described with a type-specific RDF.
446    Use this generic resource description, if none of the known specific types matches your resource.
447    """
448
449    type: Annotated[str, LowerCase, Field(frozen=True)] = "generic"
450    """The resource type assigns a broad category to the resource."""
451
452    id: Optional[
453        Annotated[ResourceId, Field(examples=["affable-shark", "ambitious-sloth"])]
454    ] = None
455    """bioimage.io-wide unique resource identifier
456    assigned by bioimage.io; version **un**specific."""
457
458    source: Optional[HttpUrl] = None
459    """The primary source of the resource"""
460
461    @field_validator("type", mode="after")
462    @classmethod
463    def check_specific_types(cls, value: str) -> str:
464        if value in KNOWN_SPECIFIC_RESOURCE_TYPES:
465            raise ValueError(
466                f"Use the {value} description instead of this generic description for"
467                + f" your '{value}' resource."
468            )
469
470        return value
58class ResourceId(ValidatedString):
59    root_model: ClassVar[Type[RootModel[Any]]] = RootModel[
60        Annotated[
61            NotEmpty[str],
62            AfterValidator(str.lower),  # convert upper case on the fly
63            RestrictCharacters(string.ascii_lowercase + string.digits + "_-/."),
64            annotated_types.Predicate(
65                lambda s: not (s.startswith("/") or s.endswith("/"))
66            ),
67        ]
68    ]

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'.

root_model: ClassVar[Type[pydantic.root_model.RootModel[Any]]] = <class 'pydantic.root_model.RootModel[Annotated[str, MinLen, AfterValidator, RestrictCharacters, Predicate]]'>

the pydantic root model to validate the string

KNOWN_SPECIFIC_RESOURCE_TYPES = ('application', 'collection', 'dataset', 'model', 'notebook')
VALID_COVER_IMAGE_EXTENSIONS = ('.gif', '.jpeg', '.jpg', '.png', '.svg', '.tif', '.tiff')
CoverImageSource = typing.Annotated[typing.Union[typing.Annotated[pathlib.Path, PathType(path_type='file'), Predicate(is_absolute), FieldInfo(annotation=NoneType, required=True, title='AbsoluteFilePath')], bioimageio.spec._internal.io.RelativeFilePath, bioimageio.spec._internal.url.HttpUrl], FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')]), WithSuffix(suffix=('.gif', '.jpeg', '.jpg', '.png', '.svg', '.tif', '.tiff'), case_sensitive=False), PlainSerializer(func=<function _package>, return_type=PydanticUndefined, when_used='unless-none')]
class AttachmentsDescr(bioimageio.spec._internal.node.Node):
 98class AttachmentsDescr(Node):
 99    model_config = {**Node.model_config, "extra": "allow"}
100    """update pydantic model config to allow additional unknown keys"""
101    files: List[ImportantFileSource] = Field(default_factory=list)
102    """∈📦 File attachments"""
model_config = {'extra': 'allow', 'frozen': False, 'populate_by_name': True, 'revalidate_instances': 'never', 'validate_assignment': True, 'validate_default': False, 'validate_return': True, 'use_attribute_docstrings': True, 'model_title_generator': <function _node_title_generator>}

update pydantic model config to allow additional unknown keys

files: List[Annotated[Union[bioimageio.spec._internal.url.HttpUrl, bioimageio.spec._internal.io.RelativeFilePath, Annotated[pathlib.Path, PathType(path_type='file'), FieldInfo(annotation=NoneType, required=True, title='FilePath')]], FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')]), AfterValidator(func=<function wo_special_file_name at 0x7fec152e3740>), PlainSerializer(func=<function _package at 0x7fec152e2a20>, return_type=PydanticUndefined, when_used='unless-none')]]

∈📦 File attachments

class Uploader(bioimageio.spec._internal.node.Node):
109class Uploader(Node):
110    email: EmailStr
111    """Email"""
112    name: Optional[Annotated[str, AfterValidator(_remove_slashes)]] = None
113    """name"""
email: pydantic.networks.EmailStr

Email

name: Optional[Annotated[str, AfterValidator(func=<function _remove_slashes at 0x7fec151bb240>)]]

name

class Author(_Person):
131class Author(_Person):
132    name: Annotated[str, AfterValidator(_remove_slashes)]
133    github_user: Optional[str] = None  # TODO: validate github_user
name: Annotated[str, AfterValidator(func=<function _remove_slashes at 0x7fec151bb240>)]
github_user: Optional[str]
class Maintainer(_Person):
136class Maintainer(_Person):
137    name: Optional[Annotated[str, AfterValidator(_remove_slashes)]] = None
138    github_user: str
name: Optional[Annotated[str, AfterValidator(func=<function _remove_slashes at 0x7fec151bb240>)]]
github_user: str
class BadgeDescr(bioimageio.spec._internal.node.Node):
141class BadgeDescr(Node):
142    """A custom badge"""
143
144    label: Annotated[str, Field(examples=["Open in Colab"])]
145    """badge label to display on hover"""
146
147    icon: Annotated[
148        Optional[InPackageIfLocalFileSource],
149        Field(examples=["https://colab.research.google.com/assets/colab-badge.svg"]),
150    ] = None
151    """badge icon"""
152
153    url: Annotated[
154        HttpUrl,
155        Field(
156            examples=[
157                "https://colab.research.google.com/github/HenriquesLab/ZeroCostDL4Mic/blob/master/Colab_notebooks/U-net_2D_ZeroCostDL4Mic.ipynb"
158            ]
159        ),
160    ]
161    """target URL"""

A custom badge

label: Annotated[str, FieldInfo(annotation=NoneType, required=True, examples=['Open in Colab'])]

badge label to display on hover

icon: Annotated[Union[Annotated[Union[Annotated[pathlib.Path, PathType(path_type='file'), FieldInfo(annotation=NoneType, required=True, title='FilePath')], bioimageio.spec._internal.io.RelativeFilePath], AfterValidator(func=<function wo_special_file_name at 0x7fec152e3740>), PlainSerializer(func=<function _package at 0x7fec152e2a20>, return_type=PydanticUndefined, when_used='unless-none')], bioimageio.spec._internal.url.HttpUrl, Annotated[pydantic_core._pydantic_core.Url, UrlConstraints(max_length=2083, allowed_schemes=['http', 'https'], host_required=None, default_host=None, default_port=None, default_path=None)], NoneType], FieldInfo(annotation=NoneType, required=True, examples=['https://colab.research.google.com/assets/colab-badge.svg'])]

badge icon

url: Annotated[bioimageio.spec._internal.url.HttpUrl, FieldInfo(annotation=NoneType, required=True, examples=['https://colab.research.google.com/github/HenriquesLab/ZeroCostDL4Mic/blob/master/Colab_notebooks/U-net_2D_ZeroCostDL4Mic.ipynb'])]

target URL

class CiteEntry(bioimageio.spec._internal.node.Node):
164class CiteEntry(Node):
165    text: str
166    """free text description"""
167
168    doi: Optional[Doi] = None
169    """A digital object identifier (DOI) is the prefered citation reference.
170    See https://www.doi.org/ for details. (alternatively specify `url`)"""
171
172    @field_validator("doi", mode="before")
173    @classmethod
174    def accept_prefixed_doi(cls, doi: Any) -> Any:
175        if isinstance(doi, str):
176            for doi_prefix in ("https://doi.org/", "http://dx.doi.org/"):
177                if doi.startswith(doi_prefix):
178                    doi = doi[len(doi_prefix) :]
179                    break
180
181        return doi
182
183    url: Optional[str] = None
184    """URL to cite (preferably specify a `doi` instead)"""
185
186    @model_validator(mode="after")
187    def _check_doi_or_url(self) -> Self:
188        if not self.doi and not self.url:
189            raise ValueError("Either 'doi' or 'url' is required")
190
191        return self
text: str

free text description

A digital object identifier (DOI) is the prefered citation reference. See https://www.doi.org/ for details. (alternatively specify url)

@field_validator('doi', mode='before')
@classmethod
def accept_prefixed_doi(cls, doi: Any) -> Any:
172    @field_validator("doi", mode="before")
173    @classmethod
174    def accept_prefixed_doi(cls, doi: Any) -> Any:
175        if isinstance(doi, str):
176            for doi_prefix in ("https://doi.org/", "http://dx.doi.org/"):
177                if doi.startswith(doi_prefix):
178                    doi = doi[len(doi_prefix) :]
179                    break
180
181        return doi
url: Optional[str]

URL to cite (preferably specify a doi instead)

class LinkedResource(bioimageio.spec._internal.node.Node):
194class LinkedResource(Node):
195    """Reference to a bioimage.io resource"""
196
197    id: ResourceId
198    """A valid resource `id` from the bioimage.io collection."""
199
200    version_number: Optional[int] = None
201    """version number (n-th published version, not the semantic version) of linked resource"""

Reference to a bioimage.io resource

A valid resource id from the bioimage.io collection.

version_number: Optional[int]

version number (n-th published version, not the semantic version) of linked resource

class GenericModelDescrBase(bioimageio.spec._internal.common_nodes.ResourceDescrBase):
204class GenericModelDescrBase(ResourceDescrBase):
205    """Base for all resource descriptions including of model descriptions"""
206
207    name: Annotated[NotEmpty[str], warn(MaxLen(128), "Longer than 128 characters.")]
208    """A human-friendly name of the resource description"""
209
210    description: str
211
212    covers: Annotated[
213        List[CoverImageSource],
214        Field(
215            examples=["cover.png"],
216            description=(
217                "Cover images. Please use an image smaller than 500KB and an aspect"
218                " ratio width to height of 2:1.\nThe supported image formats are:"
219                f" {VALID_COVER_IMAGE_EXTENSIONS}"
220            ),
221        ),
222    ] = Field(
223        default_factory=list,
224    )
225    """∈📦 Cover images. Please use an image smaller than 500KB and an aspect ratio width to height of 2:1."""
226
227    id_emoji: Optional[
228        Annotated[str, Len(min_length=1, max_length=1), Field(examples=["🦈", "🦥"])]
229    ] = None
230    """UTF-8 emoji for display alongside the `id`."""
231
232    authors: List[Author] = Field(default_factory=list)
233    """The authors are the creators of the RDF and the primary points of contact."""
234
235    @field_validator("authors", mode="before")
236    @classmethod
237    def accept_author_strings(cls, authors: Union[Any, Sequence[Any]]) -> Any:
238        """we unofficially accept strings as author entries"""
239        if is_sequence(authors):
240            authors = [{"name": a} if isinstance(a, str) else a for a in authors]
241
242        if not authors:
243            issue_warning("missing", value=authors, field="authors")
244
245        return authors
246
247    attachments: Optional[AttachmentsDescr] = None
248    """file and other attachments"""
249
250    cite: List[CiteEntry] = Field(default_factory=list)
251    """citations"""
252
253    @field_validator("cite", mode="after")
254    @classmethod
255    def _warn_empty_cite(cls, value: Any):
256        if not value:
257            issue_warning("missing", value=value, field="cite")
258
259        return value
260
261    config: Annotated[
262        Dict[str, YamlValue],
263        Field(
264            examples=[
265                dict(
266                    bioimageio={
267                        "my_custom_key": 3837283,
268                        "another_key": {"nested": "value"},
269                    },
270                    imagej={"macro_dir": "path/to/macro/file"},
271                )
272            ],
273        ),
274    ] = Field(default_factory=dict)
275    """A field for custom configuration that can contain any keys not present in the RDF spec.
276    This means you should not store, for example, a github repo URL in `config` since we already have the
277    `git_repo` field defined in the spec.
278    Keys in `config` may be very specific to a tool or consumer software. To avoid conflicting definitions,
279    it is recommended to wrap added configuration into a sub-field named with the specific domain or tool name,
280    for example:
281    ```yaml
282    config:
283        bioimageio:  # here is the domain name
284            my_custom_key: 3837283
285            another_key:
286                nested: value
287        imagej:       # config specific to ImageJ
288            macro_dir: path/to/macro/file
289    ```
290    If possible, please use [`snake_case`](https://en.wikipedia.org/wiki/Snake_case) for keys in `config`.
291    You may want to list linked files additionally under `attachments` to include them when packaging a resource
292    (packaging a resource means downloading/copying important linked files and creating a ZIP archive that contains
293    an altered rdf.yaml file with local references to the downloaded files)"""
294
295    download_url: Optional[HttpUrl] = None
296    """URL to download the resource from (deprecated)"""
297
298    git_repo: Annotated[
299        Optional[str],
300        Field(
301            examples=[
302                "https://github.com/bioimage-io/spec-bioimage-io/tree/main/example_descriptions/models/unet2d_nuclei_broad"
303            ],
304        ),
305    ] = None
306    """A URL to the Git repository where the resource is being developed."""
307
308    icon: Union[
309        Annotated[str, Len(min_length=1, max_length=2)], ImportantFileSource, None
310    ] = None
311    """An icon for illustration"""
312
313    links: Annotated[
314        List[str],
315        Field(
316            examples=[
317                (
318                    "ilastik/ilastik",
319                    "deepimagej/deepimagej",
320                    "zero/notebook_u-net_3d_zerocostdl4mic",
321                )
322            ],
323        ),
324    ] = Field(default_factory=list)
325    """IDs of other bioimage.io resources"""
326
327    uploader: Optional[Uploader] = None
328    """The person who uploaded the model (e.g. to bioimage.io)"""
329
330    maintainers: List[Maintainer] = Field(default_factory=list)
331    """Maintainers of this resource.
332    If not specified `authors` are maintainers and at least some of them should specify their `github_user` name"""
333
334    rdf_source: Optional[FileSource] = None
335    """Resource description file (RDF) source; used to keep track of where an rdf.yaml was loaded from.
336    Do not set this field in a YAML file."""
337
338    tags: Annotated[
339        List[str],
340        Field(examples=[("unet2d", "pytorch", "nucleus", "segmentation", "dsb2018")]),
341    ] = Field(default_factory=list)
342    """Associated tags"""
343
344    @as_warning
345    @field_validator("tags")
346    @classmethod
347    def warn_about_tag_categories(
348        cls, value: List[str], info: ValidationInfo
349    ) -> List[str]:
350        categories = TAG_CATEGORIES.get(info.data["type"], {})
351        missing_categories: List[Mapping[str, Sequence[str]]] = []
352        for cat, entries in categories.items():
353            if not any(e in value for e in entries):
354                missing_categories.append({cat: entries})
355
356        if missing_categories:
357            raise ValueError(
358                "Missing tags from bioimage.io categories: {missing_categories}"
359            )
360
361        return value
362
363    version: Optional[Version] = None
364    """The version of the resource following SemVer 2.0."""
365
366    version_number: Optional[int] = None
367    """version number (n-th published version, not the semantic version)"""

Base for all resource descriptions including of model descriptions

name: Annotated[str, MinLen(min_length=1), AfterWarner(func=<function as_warning.<locals>.wrapper at 0x7fec14b439c0>, severity=30, msg='Longer than 128 characters.', context={'typ': Annotated[Any, MaxLen(max_length=128)]})]

A human-friendly name of the resource description

description: str
covers: Annotated[List[Annotated[Union[Annotated[pathlib.Path, PathType(path_type='file'), Predicate(is_absolute), FieldInfo(annotation=NoneType, required=True, title='AbsoluteFilePath')], bioimageio.spec._internal.io.RelativeFilePath, bioimageio.spec._internal.url.HttpUrl], FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')]), WithSuffix(suffix=('.gif', '.jpeg', '.jpg', '.png', '.svg', '.tif', '.tiff'), case_sensitive=False), PlainSerializer(func=<function _package at 0x7fec152e2a20>, return_type=PydanticUndefined, when_used='unless-none')]], FieldInfo(annotation=NoneType, required=True, description="Cover images. Please use an image smaller than 500KB and an aspect ratio width to height of 2:1.\nThe supported image formats are: ('.gif', '.jpeg', '.jpg', '.png', '.svg', '.tif', '.tiff')", examples=['cover.png'])]

∈📦 Cover images. Please use an image smaller than 500KB and an aspect ratio width to height of 2:1.

id_emoji: Optional[Annotated[str, Len(min_length=1, max_length=1), FieldInfo(annotation=NoneType, required=True, examples=['🦈', '🦥'])]]

UTF-8 emoji for display alongside the id.

authors: List[Author]

The authors are the creators of the RDF and the primary points of contact.

@field_validator('authors', mode='before')
@classmethod
def accept_author_strings(cls, authors: Union[Any, Sequence[Any]]) -> Any:
235    @field_validator("authors", mode="before")
236    @classmethod
237    def accept_author_strings(cls, authors: Union[Any, Sequence[Any]]) -> Any:
238        """we unofficially accept strings as author entries"""
239        if is_sequence(authors):
240            authors = [{"name": a} if isinstance(a, str) else a for a in authors]
241
242        if not authors:
243            issue_warning("missing", value=authors, field="authors")
244
245        return authors

we unofficially accept strings as author entries

attachments: Optional[AttachmentsDescr]

file and other attachments

cite: List[CiteEntry]

citations

config: Annotated[Dict[str, YamlValue], FieldInfo(annotation=NoneType, required=True, examples=[{'bioimageio': {'my_custom_key': 3837283, 'another_key': {'nested': 'value'}}, 'imagej': {'macro_dir': 'path/to/macro/file'}}])]

A field for custom configuration that can contain any keys not present in the RDF spec. This means you should not store, for example, a github repo URL in config since we already have the git_repo field defined in the spec. Keys in config may be very specific to a tool or consumer software. To avoid conflicting definitions, it is recommended to wrap added configuration into a sub-field named with the specific domain or tool name, for example:

config:
    bioimageio:  # here is the domain name
        my_custom_key: 3837283
        another_key:
            nested: value
    imagej:       # config specific to ImageJ
        macro_dir: path/to/macro/file

If possible, please use snake_case for keys in config. You may want to list linked files additionally under attachments to include them when packaging a resource (packaging a resource means downloading/copying important linked files and creating a ZIP archive that contains an altered rdf.yaml file with local references to the downloaded files)

download_url: Optional[bioimageio.spec._internal.url.HttpUrl]

URL to download the resource from (deprecated)

git_repo: Annotated[Optional[str], FieldInfo(annotation=NoneType, required=True, examples=['https://github.com/bioimage-io/spec-bioimage-io/tree/main/example_descriptions/models/unet2d_nuclei_broad'])]

A URL to the Git repository where the resource is being developed.

icon: Union[Annotated[str, Len(min_length=1, max_length=2)], Annotated[Union[bioimageio.spec._internal.url.HttpUrl, bioimageio.spec._internal.io.RelativeFilePath, Annotated[pathlib.Path, PathType(path_type='file'), FieldInfo(annotation=NoneType, required=True, title='FilePath')]], FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')]), AfterValidator(func=<function wo_special_file_name at 0x7fec152e3740>), PlainSerializer(func=<function _package at 0x7fec152e2a20>, return_type=PydanticUndefined, when_used='unless-none')], NoneType]

An icon for illustration

uploader: Optional[Uploader]

The person who uploaded the model (e.g. to bioimage.io)

maintainers: List[Maintainer]

Maintainers of this resource. If not specified authors are maintainers and at least some of them should specify their github_user name

rdf_source: Optional[Annotated[Union[bioimageio.spec._internal.url.HttpUrl, bioimageio.spec._internal.io.RelativeFilePath, Annotated[pathlib.Path, PathType(path_type='file'), FieldInfo(annotation=NoneType, required=True, title='FilePath')]], FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')])]]

Resource description file (RDF) source; used to keep track of where an rdf.yaml was loaded from. Do not set this field in a YAML file.

tags: Annotated[List[str], FieldInfo(annotation=NoneType, required=True, examples=[('unet2d', 'pytorch', 'nucleus', 'segmentation', 'dsb2018')])]

Associated tags

def warn_about_tag_categories(value: Any, info: pydantic_core.core_schema.ValidationInfo) -> Any:
75    def wrapper(value: Any, info: ValidationInfo) -> Any:
76        try:
77            call_validator_func(func, mode, value, info)
78        except (AssertionError, ValueError) as e:
79            issue_warning(
80                msg or ",".join(e.args),
81                value=value,
82                severity=severity,
83                msg_context=msg_context,
84            )
85
86        return value

The version of the resource following SemVer 2.0.

version_number: Optional[int]

version number (n-th published version, not the semantic version)

def model_post_init(self: pydantic.main.BaseModel, context: Any, /) -> None:
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.

class GenericDescrBase(GenericModelDescrBase):
370class GenericDescrBase(GenericModelDescrBase):
371    """Base for all resource descriptions except for the model descriptions"""
372
373    format_version: Literal["0.2.4"] = "0.2.4"
374    """The format version of this resource specification
375    (not the `version` of the resource description)
376    When creating a new resource always use the latest micro/patch version described here.
377    The `format_version` is important for any consumer software to understand how to parse the fields.
378    """
379
380    @model_validator(mode="before")
381    @classmethod
382    def _convert_from_older_format(
383        cls, data: BioimageioYamlContent, /
384    ) -> BioimageioYamlContent:
385        _convert_from_older_format(data)
386        return data
387
388    badges: List[BadgeDescr] = Field(default_factory=list)
389    """badges associated with this resource"""
390
391    documentation: Annotated[
392        Optional[ImportantFileSource],
393        Field(
394            examples=[
395                "https://raw.githubusercontent.com/bioimage-io/spec-bioimage-io/main/example_descriptions/models/unet2d_nuclei_broad/README.md",
396                "README.md",
397            ],
398        ),
399    ] = None
400    """∈📦 URL or relative path to a markdown file with additional documentation.
401    The recommended documentation file name is `README.md`. An `.md` suffix is mandatory."""
402
403    license: Annotated[
404        Union[LicenseId, DeprecatedLicenseId, str, None],
405        Field(union_mode="left_to_right", examples=["CC0-1.0", "MIT", "BSD-2-Clause"]),
406    ] = None
407    """A [SPDX license identifier](https://spdx.org/licenses/).
408    We do not support custom license beyond the SPDX license list, if you need that please
409    [open a GitHub issue](https://github.com/bioimage-io/spec-bioimage-io/issues/new/choose
410    ) to discuss your intentions with the community."""
411
412    @field_validator("license", mode="after")
413    @classmethod
414    def deprecated_spdx_license(
415        cls, value: Optional[Union[LicenseId, DeprecatedLicenseId, str]]
416    ):
417        if isinstance(value, LicenseId):
418            pass
419        elif value is None:
420            issue_warning("missing", value=value, field="license")
421        elif isinstance(value, DeprecatedLicenseId):
422            issue_warning(
423                "'{value}' is a deprecated license identifier.",
424                value=value,
425                field="license",
426            )
427        elif isinstance(value, str):
428            issue_warning(
429                "'{value}' is an unknown license identifier.",
430                value=value,
431                field="license",
432            )
433        else:
434            assert_never(value)
435
436        return value

Base for all resource descriptions except for the model descriptions

format_version: Literal['0.2.4']

The format version of this resource specification (not the version of the resource description) When creating a new resource always use the latest micro/patch version described here. The format_version is important for any consumer software to understand how to parse the fields.

badges: List[BadgeDescr]

badges associated with this resource

documentation: Annotated[Optional[Annotated[Union[bioimageio.spec._internal.url.HttpUrl, bioimageio.spec._internal.io.RelativeFilePath, Annotated[pathlib.Path, PathType(path_type='file'), FieldInfo(annotation=NoneType, required=True, title='FilePath')]], FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')]), AfterValidator(func=<function wo_special_file_name at 0x7fec152e3740>), PlainSerializer(func=<function _package at 0x7fec152e2a20>, return_type=PydanticUndefined, when_used='unless-none')]], FieldInfo(annotation=NoneType, required=True, examples=['https://raw.githubusercontent.com/bioimage-io/spec-bioimage-io/main/example_descriptions/models/unet2d_nuclei_broad/README.md', 'README.md'])]

∈📦 URL or relative path to a markdown file with additional documentation. The recommended documentation file name is README.md. An .md suffix is mandatory.

license: Annotated[Union[bioimageio.spec._internal.license_id.LicenseId, bioimageio.spec._internal.license_id.DeprecatedLicenseId, str, NoneType], FieldInfo(annotation=NoneType, required=True, examples=['CC0-1.0', 'MIT', 'BSD-2-Clause'], metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')])]

A SPDX license identifier. We do not support custom license beyond the SPDX license list, if you need that please open a GitHub issue to discuss your intentions with the community.

@field_validator('license', mode='after')
@classmethod
def deprecated_spdx_license( cls, value: Union[bioimageio.spec._internal.license_id.LicenseId, bioimageio.spec._internal.license_id.DeprecatedLicenseId, str, NoneType]):
412    @field_validator("license", mode="after")
413    @classmethod
414    def deprecated_spdx_license(
415        cls, value: Optional[Union[LicenseId, DeprecatedLicenseId, str]]
416    ):
417        if isinstance(value, LicenseId):
418            pass
419        elif value is None:
420            issue_warning("missing", value=value, field="license")
421        elif isinstance(value, DeprecatedLicenseId):
422            issue_warning(
423                "'{value}' is a deprecated license identifier.",
424                value=value,
425                field="license",
426            )
427        elif isinstance(value, str):
428            issue_warning(
429                "'{value}' is an unknown license identifier.",
430                value=value,
431                field="license",
432            )
433        else:
434            assert_never(value)
435
436        return value
implemented_format_version: ClassVar[str] = '0.2.4'
implemented_format_version_tuple: ClassVar[Tuple[int, int, int]] = (0, 2, 4)
def model_post_init(self: pydantic.main.BaseModel, context: Any, /) -> None:
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.

class GenericDescr(GenericDescrBase):
442class GenericDescr(GenericDescrBase, extra="ignore"):
443    """Specification of the fields used in a generic bioimage.io-compliant resource description file (RDF).
444
445    An RDF is a YAML file that describes a resource such as a model, a dataset, or a notebook.
446    Note that those resources are described with a type-specific RDF.
447    Use this generic resource description, if none of the known specific types matches your resource.
448    """
449
450    type: Annotated[str, LowerCase, Field(frozen=True)] = "generic"
451    """The resource type assigns a broad category to the resource."""
452
453    id: Optional[
454        Annotated[ResourceId, Field(examples=["affable-shark", "ambitious-sloth"])]
455    ] = None
456    """bioimage.io-wide unique resource identifier
457    assigned by bioimage.io; version **un**specific."""
458
459    source: Optional[HttpUrl] = None
460    """The primary source of the resource"""
461
462    @field_validator("type", mode="after")
463    @classmethod
464    def check_specific_types(cls, value: str) -> str:
465        if value in KNOWN_SPECIFIC_RESOURCE_TYPES:
466            raise ValueError(
467                f"Use the {value} description instead of this generic description for"
468                + f" your '{value}' resource."
469            )
470
471        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.

type: Annotated[str, Annotated[~_StrType, Predicate(str.islower)], FieldInfo(annotation=NoneType, required=True, frozen=True)]

The resource type assigns a broad category to the resource.

id: Optional[Annotated[ResourceId, FieldInfo(annotation=NoneType, required=True, examples=['affable-shark', 'ambitious-sloth'])]]

bioimage.io-wide unique resource identifier assigned by bioimage.io; version unspecific.

The primary source of the resource

@field_validator('type', mode='after')
@classmethod
def check_specific_types(cls, value: str) -> str:
462    @field_validator("type", mode="after")
463    @classmethod
464    def check_specific_types(cls, value: str) -> str:
465        if value in KNOWN_SPECIFIC_RESOURCE_TYPES:
466            raise ValueError(
467                f"Use the {value} description instead of this generic description for"
468                + f" your '{value}' resource."
469            )
470
471        return value
implemented_type: ClassVar[str] = 'generic'
implemented_format_version: ClassVar[str] = '0.2.4'
implemented_format_version_tuple: ClassVar[Tuple[int, int, int]] = (0, 2, 4)
def model_post_init(self: pydantic.main.BaseModel, context: Any, /) -> None:
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.