Skip to content

Commit 8062d77

Browse files
FynnBectr26
authored andcommitted
WIP RDF v0_2 goes pydantic
1 parent 6f51244 commit 8062d77

File tree

21 files changed

+310
-205
lines changed

21 files changed

+310
-205
lines changed

bioimageio/spec/__init__.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
from . import collection, model, rdf, shared
2-
from .commands import update_format, update_rdf, validate
3-
from .io_ import (
4-
get_resource_package_content,
5-
load_raw_resource_description,
6-
serialize_raw_resource_description,
7-
serialize_raw_resource_description_to_dict,
8-
)
1+
# from . import collection, model, rdf, shared
2+
from . import rdf
3+
4+
# from .commands import update_format, update_rdf, validate
5+
# from .io_ import (
6+
# get_resource_package_content,
7+
# load_raw_resource_description,
8+
# serialize_raw_resource_description,
9+
# serialize_raw_resource_description_to_dict,
10+
# )
911
from .v import __version__
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
from typing import Any, Dict
2+
3+
from bioimageio.spec.model.v0_4.converters import maybe_convert as maybe_convert_v04
4+
5+
6+
def maybe_convert(data: Dict[str, Any]) -> Dict[str, Any]:
7+
"""auto converts model 'data' to newest format"""
8+
data = maybe_convert_v04(data)
9+
assert "format_version" in data
10+
major, minor, patch = map(int, data["format_version"].split("."))
11+
if (major, minor) > (0, 5):
12+
return data
13+
14+
if minor == 4:
15+
data = convert_model_from_v0_4_to_0_5_0(data)
16+
17+
return data
18+
19+
20+
def convert_model_from_v0_4_to_0_5_0(data: Dict[str, Any]) -> Dict[str, Any]:
21+
data = dict(data)
22+
23+
# convert axes string to axis descriptions
24+
inputs = data.get("inputs")
25+
outputs = data.get("outputs")
26+
test_inputs = data.get("test_inputs")
27+
test_outputs = data.get("test_outputs")
28+
sample_inputs = data.get("sample_inputs")
29+
sample_outputs = data.get("sample_outputs")
30+
31+
axis_letter_map = {"t": "time", "c": "channel", "i": "index"}
32+
33+
def convert_axes(tensor_spec):
34+
axes = tensor_spec.get("axes")
35+
if isinstance(axes, str):
36+
tensor_spec["axes"] = [dict(role=axis_letter_map.get(a, a)) for a in axes]
37+
38+
def update_tensor_specs(tensor_specs, test_tensors, sample_tensors):
39+
for i, param in enumerate(tensor_specs):
40+
if not isinstance(param, dict):
41+
continue
42+
43+
convert_axes(param)
44+
if isinstance(test_tensors, list) and len(test_tensors) == len(inputs):
45+
param["test_tensor"] = test_tensors[i]
46+
47+
if isinstance(sample_tensors, list) and len(sample_tensors) == len(inputs):
48+
param["sample_tensor"] = sample_tensors[i]
49+
50+
if isinstance(inputs, list):
51+
update_tensor_specs(inputs, test_inputs, sample_inputs)
52+
53+
if isinstance(outputs, list):
54+
update_tensor_specs(outputs, test_outputs, sample_outputs)
55+
56+
data["format_version"] = "0.5.0"
57+
return data

bioimageio/spec/rdf/__init__.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
from . import v0_2
22

33
# autogen: start
4-
from . import converters, raw_nodes, schema, utils
5-
from .raw_nodes import FormatVersion
4+
from . import nodes
65

76
try:
87
from typing import get_args
98
except ImportError:
109
from typing_extensions import get_args # type: ignore
1110

12-
format_version = get_args(FormatVersion)[-1]
11+
format_version = get_args(nodes.FormatVersion)[-1]
1312

1413
# autogen: stop

bioimageio/spec/rdf/converters.py

Lines changed: 0 additions & 3 deletions
This file was deleted.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# Auto-generated by generate_passthrough_modules.py - do not modify
22

3-
from .v0_2.utils import *
3+
from .v0_2.nodes import *

bioimageio/spec/rdf/raw_nodes.py

Lines changed: 0 additions & 3 deletions
This file was deleted.

bioimageio/spec/rdf/schema.py

Lines changed: 0 additions & 3 deletions
This file was deleted.

bioimageio/spec/rdf/v0_2/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
from . import converters, raw_nodes, schema, utils
2-
from .raw_nodes import FormatVersion
1+
from . import nodes
2+
from .nodes import FormatVersion
33

44
try:
55
from typing import get_args

bioimageio/spec/rdf/v0_2/nodes.py

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
"""nodes for the general RDF spec
2+
3+
nodes represent the content of any RDF.
4+
"""
5+
import dataclasses
6+
import pathlib
7+
8+
import packaging.version
9+
import warnings
10+
from dataclasses import dataclass
11+
from pathlib import Path, PurePosixPath
12+
from typing import Any, Dict, List, Optional, Sequence, Tuple, Union, Annotated
13+
14+
from pydantic import EmailStr, Extra, Field, FilePath, HttpUrl, ValidationError, constr, validator
15+
from bioimageio.spec.shared.nodes import Node
16+
17+
from typing import Literal
18+
19+
from bioimageio.spec.shared.types_ import RawMapping
20+
from bioimageio.spec.shared.utils import is_valid_orcid_id
21+
22+
FormatVersion = Literal[
23+
"0.2.0", "0.2.1", "0.2.2", "0.2.3"
24+
] # newest format needs to be last (used to determine latest format version)
25+
26+
27+
class Attachments(Node, extra=Extra.allow):
28+
files: Union[Tuple[Union[HttpUrl, PurePosixPath], ...], None] = Field(
29+
description="File attachments; included when packaging the resource.", in_package=True
30+
)
31+
32+
33+
class _Person(Node):
34+
name: Optional[str] = Field(None, description="Full name")
35+
affiliation: Optional[str] = Field(None, description="Affiliation")
36+
email: Optional[EmailStr] = Field(None, description="Email")
37+
github_user: Optional[str] = Field(None, description="GitHub user name")
38+
orcid: Optional[str] = Field(
39+
None,
40+
description=(
41+
"An [ORCID iD](https://support.orcid.org/hc/en-us/sections/360001495313-What-is-ORCID)"
42+
"in hyphenated groups of 4 digits, e.g. '0000-0001-2345-6789' (and [valid]("
43+
"https://support.orcid.org/hc/en-us/articles/360006897674-Structure-of-the-ORCID-Identifier"
44+
") as per ISO 7064 11,2.)"
45+
),
46+
)
47+
48+
@validator("orcid")
49+
def check_orcid(cls, orcid: str):
50+
if (
51+
len(orcid) != 19
52+
or any(orcid[idx] != "-" for idx in [4, 9, 14])
53+
or not is_valid_orcid_id(orcid.replace("-", ""))
54+
):
55+
raise ValueError(f"'{orcid} is not a valid ORCID iD in hyphenated groups of 4 digits")
56+
57+
58+
class Author(_Person):
59+
name: str = Field(..., description="Full name")
60+
61+
62+
class Maintainer(_Person):
63+
github_user: str = Field(..., description="GitHub user name")
64+
65+
66+
class CiteEntry(Node):
67+
text: str
68+
doi: Optional[str] = None
69+
url: Optional[str] = None
70+
71+
@validator("url", always=True)
72+
def check_doi_or_url(cls, url: Optional[str], values: RawMapping):
73+
if not values.get("doi") and not url:
74+
raise ValueError("Either 'doi' or 'url' is required")
75+
return url
76+
77+
78+
class Badge(Node):
79+
label: str
80+
icon: Union[HttpUrl, Annotated[str, constr(min_length=1, max_length=2), PurePosixPath], None] = None
81+
url: HttpUrl
82+
83+
84+
# @dataclass
85+
# class RDF_Base(ResourceDescription):
86+
# attachments: Union[_Missing, Attachments] = missing
87+
# authors: Union[_Missing, List[Author]] = missing
88+
# badges: Union[_Missing, List[Badge]] = missing
89+
# cite: Union[_Missing, List[CiteEntry]] = missing
90+
# config: Union[_Missing, dict] = missing
91+
# covers: Union[_Missing, List[Union[URI, Path]]] = missing
92+
# description: str = missing
93+
# documentation: Union[_Missing, Path, URI] = missing
94+
# download_url: Union[_Missing, Path, URI] = missing
95+
# format_version: str = missing
96+
# git_repo: Union[_Missing, str] = missing
97+
# id: Union[_Missing, str] = missing
98+
# icon: Union[_Missing, str] = missing
99+
# license: Union[_Missing, str] = missing
100+
# links: Union[_Missing, List[str]] = missing
101+
# maintainers: Union[_Missing, List[Maintainer]] = missing
102+
# rdf_source: Union[_Missing, URI] = missing
103+
# source: Union[_Missing, URI, Path] = missing
104+
# tags: Union[_Missing, List[str]] = missing
105+
106+
# # manual __init__ to allow for unknown kwargs
107+
# def __init__(
108+
# self,
109+
# *,
110+
# # ResourceDescription
111+
# format_version: str,
112+
# name: str,
113+
# type: str = missing,
114+
# version: Union[_Missing, packaging.version.Version] = missing,
115+
# root_path: pathlib.Path = pathlib.Path(),
116+
# # RDF
117+
# attachments: Union[_Missing, Dict[str, Any]] = missing,
118+
# authors: Union[_Missing, List[Author]] = missing,
119+
# badges: Union[_Missing, List[Badge]] = missing,
120+
# cite: Union[_Missing, List[CiteEntry]] = missing,
121+
# config: Union[_Missing, dict] = missing,
122+
# covers: Union[_Missing, List[Union[URI, Path]]] = missing,
123+
# description: str,
124+
# documentation: Union[_Missing, Path, URI] = missing,
125+
# download_url: Union[_Missing, Path, URI] = missing,
126+
# git_repo: Union[_Missing, str] = missing,
127+
# id: Union[_Missing, str] = missing,
128+
# icon: Union[_Missing, str] = missing,
129+
# license: Union[_Missing, str] = missing,
130+
# links: Union[_Missing, List[str]] = missing,
131+
# maintainers: Union[_Missing, List[Maintainer]] = missing,
132+
# rdf_source: Union[_Missing, URI] = missing,
133+
# source: Union[_Missing, URI, Path] = missing,
134+
# tags: Union[_Missing, List[str]] = missing,
135+
# **unknown_kwargs,
136+
# ):
137+
# self.attachments = attachments
138+
# self.authors = authors
139+
# self.badges = badges
140+
# self.cite = cite
141+
# self.config = config
142+
# self.covers = covers
143+
# self.description = description
144+
# self.documentation = documentation
145+
# self.download_url = download_url
146+
# self.git_repo = git_repo
147+
# self.id = id
148+
# self.icon = icon
149+
# self.license = license
150+
# self.links = links
151+
# self.maintainers = maintainers
152+
# self.rdf_source = rdf_source
153+
# self.source = source
154+
# self.tags = tags
155+
# super().__init__(format_version=format_version, name=name, type=type, version=version, root_path=root_path)
156+
157+
# if unknown_kwargs:
158+
# # make sure we didn't forget a defined field
159+
# field_names = set(f.name for f in dataclasses.fields(self))
160+
# for uk in unknown_kwargs:
161+
# assert uk not in field_names, uk
162+
163+
# warnings.warn(f"discarding unknown RDF fields: {unknown_kwargs}")
164+
165+
# def __post_init__(self):
166+
# if self.type is missing:
167+
# self.type = self.__class__.__name__.lower()
168+
169+
# super().__post_init__()
170+
171+
172+
# @dataclass(init=False)
173+
# class RDF(RDF_Base):
174+
# format_version: FormatVersion = missing

0 commit comments

Comments
 (0)