Skip to content

Commit

Permalink
Merge pull request #662 from bioimage-io/prettier_json_schema
Browse files Browse the repository at this point in the history
Improve docs and examples
  • Loading branch information
FynnBe authored Nov 19, 2024
2 parents 896e289 + 0db0b57 commit f255c13
Show file tree
Hide file tree
Showing 27 changed files with 143 additions and 55 deletions.
30 changes: 16 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,25 +29,27 @@ bioimageio validate path/to/your/rdf.yaml

All bioimage.io description formats are defined as [Pydantic models](https://docs.pydantic.dev/latest/).

| type | format version | documentation |
| --- | --- | --- |
| model | 0.5 </br> 0.4 | [model_descr_v0-5.md](https://github.com/bioimage-io/spec-bioimage-io/blob/gh-pages/user_docs/model_descr_v0-5.md) </br> [model_descr_v0-4.md](https://github.com/bioimage-io/spec-bioimage-io/blob/gh-pages/user_docs/model_descr_v0-4.md) |
| dataset | 0.3 </br> 0.2 | [dataset_descr_v0-3.md](https://github.com/bioimage-io/spec-bioimage-io/blob/gh-pages/user_docs/dataset_descr_v0-3.md) </br> [dataset_descr_v0-2.md](https://github.com/bioimage-io/spec-bioimage-io/blob/gh-pages/user_docs/dataset_descr_v0-2.md) |
| notebook | 0.3 </br> 0.2 | [notebook_descr_v0-3.md](https://github.com/bioimage-io/spec-bioimage-io/blob/gh-pages/user_docs/notebook_descr_v0-3.md) </br> [notebook_descr_v0-2.md](https://github.com/bioimage-io/spec-bioimage-io/blob/gh-pages/user_docs/notebook_descr_v0-2.md) |
| application | 0.3 </br> 0.2 | [application_descr_v0-3.md](https://github.com/bioimage-io/spec-bioimage-io/blob/gh-pages/user_docs/application_descr_v0-3.md) </br> [application_descr_v0-2.md](https://github.com/bioimage-io/spec-bioimage-io/blob/gh-pages/user_docs/application_descr_v0-2.md) |
| collection | 0.3 </br> 0.2 | [collection_descr_v0-3.md](https://github.com/bioimage-io/spec-bioimage-io/blob/gh-pages/user_docs/collection_descr_v0-3.md) </br> [collection_descr_v0-2.md](https://github.com/bioimage-io/spec-bioimage-io/blob/gh-pages/user_docs/collection_descr_v0-2.md) |
| generic | 0.3 </br> 0.2 | [generic_descr_v0-3.md](https://github.com/bioimage-io/spec-bioimage-io/blob/gh-pages/user_docs/generic_descr_v0-3.md) </br> [generic_descr_v0-2.md](https://github.com/bioimage-io/spec-bioimage-io/blob/gh-pages/user_docs/generic_descr_v0-2.md) |
| Type | Format Version | Documentation[^1] | Developer Documentation[^2] |
| --- | --- | --- | --- |
| model | 0.5 </br> 0.4 | [model 0.5](https://bioimage-io.github.io/spec-bioimage-io/bioimageio_schema_latest/index.html#oneOf_i2_oneOf_i1) | [ModelDescr_v0_5](https://bioimage-io.github.io/spec-bioimage-io/bioimageio/spec/model/v0_5.html#ModelDescr) </br> [model 0.4](https://bioimage-io.github.io/spec-bioimage-io/bioimageio_schema_latest/index.html#oneOf_i2_oneOf_i0) | [ModelDescr_v0_4](https://bioimage-io.github.io/spec-bioimage-io/bioimageio/spec/model/v0_4.html#ModelDescr) |
| dataset | 0.3 </br> 0.2 | [dataset 0.3](https://bioimage-io.github.io/spec-bioimage-io/bioimageio_schema_latest/index.html#oneOf_i1_oneOf_i1) | [DatasetDescr_v0_3](https://bioimage-io.github.io/spec-bioimage-io/bioimageio/spec/dataset/v0_3.html#DatasetDescr) </br> [dataset 0.2](https://bioimage-io.github.io/spec-bioimage-io/bioimageio_schema_latest/index.html#oneOf_i1_oneOf_i0) | [DatasetDescr_v0_2](https://bioimage-io.github.io/spec-bioimage-io/bioimageio/spec/dataset/v0_2.html#DatasetDescr) |
| notebook | 0.3 </br> 0.2 | [notebook 0.3](https://bioimage-io.github.io/spec-bioimage-io/bioimageio_schema_latest/index.html#oneOf_i3_oneOf_i1) | [NotebookDescr_v0_3](https://bioimage-io.github.io/spec-bioimage-io/bioimageio/spec/notebook/v0_3.html#NotebookDescr) </br> [notebook 0.2](https://bioimage-io.github.io/spec-bioimage-io/bioimageio_schema_latest/index.html#oneOf_i3_oneOf_i0) | [NotebookDescr_v0_2](https://bioimage-io.github.io/spec-bioimage-io/bioimageio/spec/notebook/v0_2.html#NotebookDescr) |
| application | 0.3 </br> 0.2 | [application 0.3](https://bioimage-io.github.io/spec-bioimage-io/bioimageio_schema_latest/index.html#oneOf_i0_oneOf_i1) | [ApplicationDescr_v0_3](https://bioimage-io.github.io/spec-bioimage-io/bioimageio/spec/application/v0_3.html#ApplicationDescr) </br> [application 0.2](https://bioimage-io.github.io/spec-bioimage-io/bioimageio_schema_latest/index.html#oneOf_i0_oneOf_i0) | [ApplicationDescr_v0_2](https://bioimage-io.github.io/spec-bioimage-io/bioimageio/spec/application/v0_2.html#ApplicationDescr) |
| generic | 0.3 </br> 0.2 | - | [GenericDescr_v0_3](https://bioimage-io.github.io/spec-bioimage-io/bioimageio/spec/generic/v0_3.html#GenericDescr) </br> - | [GenericDescr_v0_2](https://bioimage-io.github.io/spec-bioimage-io/bioimageio/spec/generic/v0_2.html#GenericDescr) |

[^1]: JSON Schema based documentation generated with [json-schema-for-humans](https://coveooss.github.io/json-schema-for-humans/).
[^2]: Part of the bioimageio.spec package documentation generated with [pdoc](https://pdoc.dev/).

## JSON schema

Simplified descriptions are available as [JSON schema](https://json-schema.org/):
Simplified descriptions are available as [JSON schema](https://json-schema.org/) (generated with Pydantic):

| bioimageio.spec version | JSON schema |
| --- | --- |
| latest | [bioimageio_schema_latest.json](https://github.com/bioimage-io/spec-bioimage-io/blob/gh-pages/bioimageio_schema_latest.json) ([documentation (WIP)](https://bioimage-io.github.io/spec-bioimage-io/bioimageio_schema_latest/index.html)) |
| 0.5 | [bioimageio_schema_v0-5.json](https://github.com/bioimage-io/spec-bioimage-io/blob/gh-pages/bioimageio_schema_v0-5.json) ([documentation (WIP)](https://bioimage-io.github.io/spec-bioimage-io/bioimageio_schema_v0-5/index.html)) |
| bioimageio.spec version | JSON schema | documentation[^1] |
| --- | --- | --- |
| latest | [bioimageio_schema_latest.json](https://github.com/bioimage-io/spec-bioimage-io/blob/gh-pages/bioimageio_schema_latest.json) | [latest documentation](https://bioimage-io.github.io/spec-bioimage-io/bioimageio_schema_latest/index.html) |
| 0.5 | [bioimageio_schema_v0-5.json](https://github.com/bioimage-io/spec-bioimage-io/blob/gh-pages/bioimageio_schema_v0-5.json) | [0.5 documentation](https://bioimage-io.github.io/spec-bioimage-io/bioimageio_schema_v0-5/index.html) |

These are primarily intended for syntax highlighting.
Note: [bioimageio_schema_v0-5.json](https://github.com/bioimage-io/spec-bioimage-io/blob/gh-pages/bioimageio_schema_v0-5.json) and [bioimageio_schema_latest.json](https://github.com/bioimage-io/spec-bioimage-io/blob/gh-pages/bioimageio_schema_latest.json) are identical, but [bioimageio_schema_latest.json](https://github.com/bioimage-io/spec-bioimage-io/blob/gh-pages/bioimageio_schema_latest.json) will eventually refer to the future `bioimageio_schema_v0-6.json`.

## Flattened, interactive docs

Expand Down
7 changes: 7 additions & 0 deletions bioimageio/spec/_internal/common_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ class ResourceDescrBase(
fields_to_set_explicitly: ClassVar[FrozenSet[LiteralString]] = frozenset(
{"type", "format_version"}
)
implemented_type: ClassVar[str]
implemented_format_version: ClassVar[str]
implemented_format_version_tuple: ClassVar[Tuple[int, int, int]]

Expand Down Expand Up @@ -353,6 +354,12 @@ def root(self) -> Union[HttpUrl, DirectoryPath]:
@classmethod
def __pydantic_init_subclass__(cls, **kwargs: Any):
super().__pydantic_init_subclass__(**kwargs)
if (
"type" in cls.model_fields
and cls.model_fields["type"].default is not PydanticUndefined
):
cls.implemented_type = cls.model_fields["type"].default

if (
"format_version" in cls.model_fields
and cls.model_fields["format_version"].default is not PydanticUndefined
Expand Down
2 changes: 1 addition & 1 deletion bioimageio/spec/_internal/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
AnyUrl,
DirectoryPath,
Field,
FilePath,
GetCoreSchemaHandler,
PlainSerializer,
PrivateAttr,
Expand Down Expand Up @@ -66,6 +65,7 @@
AbsoluteDirectory,
AbsoluteFilePath,
FileName,
FilePath,
Sha256,
ZipPath,
)
Expand Down
18 changes: 14 additions & 4 deletions bioimageio/spec/_internal/io_basics.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
from pathlib import Path
from typing import Any, ClassVar, Type

import pydantic
import zipp
from annotated_types import Predicate
from pydantic import DirectoryPath, FilePath, RootModel, StringConstraints
from pydantic import RootModel, StringConstraints
from typing_extensions import Annotated

from .validated_string import ValidatedString

FileName = str
AbsoluteDirectory = Annotated[DirectoryPath, Predicate(Path.is_absolute)]
AbsoluteFilePath = Annotated[FilePath, Predicate(Path.is_absolute)]
FilePath = Annotated[pydantic.FilePath, pydantic.Field(title="FilePath")]
AbsoluteDirectory = Annotated[
pydantic.DirectoryPath,
Predicate(Path.is_absolute),
pydantic.Field(title="AbsoluteDirectory"),
]
AbsoluteFilePath = Annotated[
pydantic.FilePath,
Predicate(Path.is_absolute),
pydantic.Field(title="AbsoluteFilePath"),
]

BIOIMAGEIO_YAML = "rdf.yaml"
ALTERNATIVE_BIOIMAGEIO_YAML_NAMES = ("bioimageio.yaml", "model.yaml")
Expand All @@ -20,7 +30,7 @@


class Sha256(ValidatedString):
"""SHA-256 hash value"""
"""A SHA-256 hash value"""

root_model: ClassVar[Type[RootModel[Any]]] = RootModel[
Annotated[
Expand Down
11 changes: 11 additions & 0 deletions bioimageio/spec/_internal/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
Any,
Dict,
Optional,
Type,
Union,
)

Expand All @@ -16,6 +17,15 @@
from .validation_context import ValidationContext, validation_context_var


def _node_title_generator(model: Type[Node]) -> str:
return (
f"{model.implemented_type} {model.implemented_format_version}" # pyright: ignore[reportAttributeAccessIssue]
if hasattr(model, "implemented_type")
and hasattr(model, "implemented_format_version")
else model.__name__
)


class Node(
pydantic.BaseModel,
extra="forbid",
Expand All @@ -26,6 +36,7 @@ class Node(
validate_default=False,
validate_return=True, # TODO: check if False here would bring a speedup and can still be safe
use_attribute_docstrings=True,
model_title_generator=_node_title_generator,
):
"""Subpart of a resource description"""

Expand Down
2 changes: 1 addition & 1 deletion bioimageio/spec/_internal/root_url.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@


class RootHttpUrl(ValidatedString):
"""A 'URL folder', possibly an invalid http URL"""
"""A 'URL folder', possibly an invalid HTTP URL"""

root_model: ClassVar[Type[RootModel[Any]]] = RootModel[pydantic.HttpUrl]
_validated: pydantic.HttpUrl
Expand Down
3 changes: 2 additions & 1 deletion bioimageio/spec/_internal/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

from .constants import DOI_REGEX, SI_UNIT_REGEX
from .io import FileSource, ImportantFileSource, PermissiveFileSource, RelativeFilePath
from .io_basics import AbsoluteDirectory, AbsoluteFilePath, FileName, Sha256
from .io_basics import AbsoluteDirectory, AbsoluteFilePath, FileName, FilePath, Sha256
from .license_id import DeprecatedLicenseId, LicenseId
from .url import HttpUrl
from .validated_string import ValidatedString
Expand All @@ -25,6 +25,7 @@
"DeprecatedLicenseId",
"Doi",
"FileName",
"FilePath",
"FileSource",
"HttpUrl",
"Identifier",
Expand Down
2 changes: 2 additions & 0 deletions bioimageio/spec/_internal/url.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ def _validate_url_impl(


class HttpUrl(RootHttpUrl):
"""A URL with the HTTP or HTTPS scheme."""

root_model: ClassVar[Type[RootModel[Any]]] = RootModel[pydantic.HttpUrl]
_exists: Optional[bool] = None

Expand Down
14 changes: 13 additions & 1 deletion bioimageio/spec/_internal/validated_string.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import Any, ClassVar, Type

from pydantic import GetCoreSchemaHandler, RootModel
from pydantic import GetCoreSchemaHandler, GetJsonSchemaHandler, RootModel
from pydantic.json_schema import JsonSchemaValue
from pydantic_core.core_schema import (
CoreSchema,
no_info_after_validator_function,
Expand All @@ -24,3 +25,14 @@ def __get_pydantic_core_schema__(
cls, source_type: Any, handler: GetCoreSchemaHandler
) -> CoreSchema:
return no_info_after_validator_function(cls, handler(str))

@classmethod
def __get_pydantic_json_schema__(
cls, core_schema: CoreSchema, handler: GetJsonSchemaHandler
) -> JsonSchemaValue:
json_schema = cls.root_model.model_json_schema(mode=handler.mode)
json_schema["title"] = cls.__name__.strip("_")
if cls.__doc__:
json_schema["description"] = cls.__doc__

return json_schema
9 changes: 7 additions & 2 deletions bioimageio/spec/application/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from typing import Union

from pydantic import Discriminator
from pydantic import Discriminator, Field
from typing_extensions import Annotated

from . import v0_2, v0_3
Expand All @@ -17,7 +17,12 @@
ApplicationDescr_v0_3 = v0_3.ApplicationDescr

AnyApplicationDescr = Annotated[
Union[ApplicationDescr_v0_2, ApplicationDescr_v0_3], Discriminator("format_version")
Union[
Annotated[ApplicationDescr_v0_2, Field(title="application 0.2")],
Annotated[ApplicationDescr_v0_3, Field(title="application 0.3")],
],
Discriminator("format_version"),
Field(title="application"),
]
"""Union of any released application desription"""
# autogen: stop
2 changes: 1 addition & 1 deletion bioimageio/spec/application/v0_2.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class ApplicationId(ResourceId):
pass


class ApplicationDescr(GenericDescrBase, title="bioimage.io application specification"):
class ApplicationDescr(GenericDescrBase):
"""Bioimage.io description of an application."""

type: Literal["application"] = "application"
Expand Down
2 changes: 1 addition & 1 deletion bioimageio/spec/application/v0_3.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class ApplicationId(ResourceId):
pass


class ApplicationDescr(GenericDescrBase, title="bioimage.io application specification"):
class ApplicationDescr(GenericDescrBase):
"""Bioimage.io description of an application."""

type: Literal["application"] = "application"
Expand Down
8 changes: 7 additions & 1 deletion bioimageio/spec/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@
)
from ._internal.io_basics import AbsoluteDirectory, AbsoluteFilePath, FileName, Sha256
from ._internal.root_url import RootHttpUrl
from ._internal.types import FileSource, PermissiveFileSource, RelativeFilePath
from ._internal.types import (
FilePath,
FileSource,
PermissiveFileSource,
RelativeFilePath,
)
from ._internal.url import HttpUrl

__all__ = [
Expand All @@ -19,6 +24,7 @@
"BioimageioYamlSource",
"FileDescr",
"FileName",
"FilePath",
"FileSource",
"HttpUrl",
"InvalidDescr",
Expand Down
9 changes: 7 additions & 2 deletions bioimageio/spec/dataset/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from typing import Union

from pydantic import Discriminator
from pydantic import Discriminator, Field
from typing_extensions import Annotated

from . import v0_2, v0_3
Expand All @@ -17,7 +17,12 @@
DatasetDescr_v0_3 = v0_3.DatasetDescr

AnyDatasetDescr = Annotated[
Union[DatasetDescr_v0_2, DatasetDescr_v0_3], Discriminator("format_version")
Union[
Annotated[DatasetDescr_v0_2, Field(title="dataset 0.2")],
Annotated[DatasetDescr_v0_3, Field(title="dataset 0.3")],
],
Discriminator("format_version"),
Field(title="dataset"),
]
"""Union of any released dataset desription"""
# autogen: stop
2 changes: 1 addition & 1 deletion bioimageio/spec/dataset/v0_2.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class DatasetId(ResourceId):
pass


class DatasetDescr(GenericDescrBase, title="bioimage.io dataset specification"):
class DatasetDescr(GenericDescrBase):
"""A bioimage.io dataset resource description file (dataset RDF) describes a dataset relevant to bioimage
processing.
"""
Expand Down
2 changes: 1 addition & 1 deletion bioimageio/spec/dataset/v0_3.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class DatasetId(ResourceId):
pass


class DatasetDescr(GenericDescrBase, title="bioimage.io dataset specification"):
class DatasetDescr(GenericDescrBase):
"""A bioimage.io dataset resource description file (dataset RDF) describes a dataset relevant to bioimage
processing.
"""
Expand Down
9 changes: 7 additions & 2 deletions bioimageio/spec/generic/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from typing import Union

from pydantic import Discriminator
from pydantic import Discriminator, Field
from typing_extensions import Annotated

from . import v0_2, v0_3
Expand All @@ -17,7 +17,12 @@
GenericDescr_v0_3 = v0_3.GenericDescr

AnyGenericDescr = Annotated[
Union[GenericDescr_v0_2, GenericDescr_v0_3], Discriminator("format_version")
Union[
Annotated[GenericDescr_v0_2, Field(title="generic 0.2")],
Annotated[GenericDescr_v0_3, Field(title="generic 0.3")],
],
Discriminator("format_version"),
Field(title="generic"),
]
"""Union of any released generic desription"""
# autogen: stop
14 changes: 8 additions & 6 deletions bioimageio/spec/generic/v0_2.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ class Maintainer(_Person):
github_user: str


class BadgeDescr(Node, title="Custom badge"):
class BadgeDescr(Node):
"""A custom badge"""

label: Annotated[str, Field(examples=["Open in Colab"])]
Expand Down Expand Up @@ -223,7 +223,9 @@ class GenericModelDescrBase(ResourceDescrBase):
)
"""∈📦 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)]] = None
id_emoji: Optional[
Annotated[str, Len(min_length=1, max_length=1), Field(examples=["🦈", "🦥"])]
] = None
"""UTF-8 emoji for display alongside the `id`."""

authors: List[Author] = Field(default_factory=list)
Expand Down Expand Up @@ -436,9 +438,7 @@ def deprecated_spdx_license(
ResourceDescrType = TypeVar("ResourceDescrType", bound=GenericDescrBase)


class GenericDescr(
GenericDescrBase, extra="ignore", title="bioimage.io generic specification"
):
class GenericDescr(GenericDescrBase, extra="ignore"):
"""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.
Expand All @@ -449,7 +449,9 @@ class GenericDescr(
type: Annotated[str, LowerCase, Field(frozen=True)] = "generic"
"""The resource type assigns a broad category to the resource."""

id: Optional[ResourceId] = None
id: Optional[
Annotated[ResourceId, Field(examples=["affable-shark", "ambitious-sloth"])]
] = None
"""bioimage.io-wide unique resource identifier
assigned by bioimage.io; version **un**specific."""

Expand Down
Loading

0 comments on commit f255c13

Please sign in to comment.