Coverage for src/bioimageio/spec/_description_impl.py: 92%
60 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-18 09:17 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-18 09:17 +0000
1"""implementation details for building a bioimage.io resource description"""
3from __future__ import annotations
5import collections.abc
6from typing import Any, Callable, Literal, Mapping, TypeVar
8from ._internal.common_nodes import InvalidDescr, ResourceDescrBase
9from ._internal.field_validation import issue_warning
10from ._internal.io import BioimageioYamlContentView
11from ._internal.types import FormatVersionPlaceholder
12from ._internal.validation_context import ValidationContext, get_validation_context
13from .summary import (
14 ErrorEntry,
15 ValidationDetail,
16)
18ResourceDescrT = TypeVar("ResourceDescrT", bound=ResourceDescrBase)
21DISCOVER: Literal["discover"] = "discover"
22"""placeholder for whatever format version an RDF specifies"""
25def get_rd_class_impl(
26 typ: Any,
27 format_version: Any,
28 descriptions_map: Mapping[str | None, Mapping[str, type[ResourceDescrT]]],
29 fallback_to_latest: bool,
30) -> type[ResourceDescrT]:
31 """get the resource description class for the given type and format version"""
32 assert None in descriptions_map
33 assert all("latest" in version_map for version_map in descriptions_map.values())
34 assert all(
35 fv == "latest" or fv.count(".") == 1
36 for version_map in descriptions_map.values()
37 for fv in version_map
38 )
39 if not isinstance(typ, str) or typ not in descriptions_map:
40 typ = None
42 format_version = str(format_version)
43 if format_version == "latest" or (ndots := format_version.count(".")) == 1:
44 use_format_version = format_version
45 elif ndots == 0:
46 use_format_version = format_version + ".0"
47 else:
48 assert ndots > 1
49 use_format_version = format_version[: format_version.rfind(".")]
51 descr_versions = descriptions_map[typ]
52 if use_format_version not in descr_versions:
53 if fallback_to_latest:
54 issue_warning(
55 "Unsupported format version '{value}' for type '{typ}'.",
56 value=format_version,
57 field="format_version",
58 log_depth=3,
59 msg_context={"typ": typ},
60 )
61 use_format_version = "latest"
62 else:
63 raise ValueError(
64 f"Unsupported format version '{format_version}' for type '{typ}'."
65 + " Supported format versions are: {', '.join(sorted(fv for fv in descr_versions))}"
66 )
68 return descr_versions[use_format_version]
71def build_description_impl(
72 content: BioimageioYamlContentView,
73 /,
74 *,
75 context: ValidationContext | None = None,
76 format_version: FormatVersionPlaceholder | str = DISCOVER,
77 get_rd_class: Callable[[Any, Any, bool], type[ResourceDescrT]],
78) -> ResourceDescrT | InvalidDescr:
79 context = context or get_validation_context()
80 errors: list[ErrorEntry] = []
81 if isinstance(content, collections.abc.Mapping):
82 for minimum in ("type", "format_version"):
83 if minimum not in content:
84 errors.append(
85 ErrorEntry(
86 loc=(minimum,), msg=f"Missing field '{minimum}'", type="error"
87 )
88 )
89 elif not isinstance(content[minimum], str):
90 errors.append(
91 ErrorEntry(
92 loc=(minimum,),
93 msg=f"Invalid type '{type(content[minimum])}'",
94 type="error",
95 )
96 )
97 else:
98 errors.append(
99 ErrorEntry(
100 loc=(), msg=f"Invalid content of type '{type(content)}'", type="error"
101 )
102 )
103 content = {}
105 if errors:
106 ret = InvalidDescr(**content) # pyright: ignore[reportArgumentType]
107 ret.validation_summary.add_detail(
108 ValidationDetail(
109 name="extract fields to chose description class",
110 status="failed",
111 errors=errors,
112 context=context.summary,
113 )
114 )
115 assert ret.validation_summary.status == "failed", (
116 "expected invalid description to have a failed validation summary status"
117 )
118 return ret
120 typ = content["type"]
121 # check format_version argument before loading as 'discover'
122 # to throw an exception for an invalid format_version early
123 if str(format_version).lower() != DISCOVER:
124 as_rd_class = get_rd_class(typ, format_version, False)
125 else:
126 as_rd_class = None
127 # always load with discovered format_version first
128 rd_class = get_rd_class(typ, content["format_version"], True)
129 rd = rd_class.load(content, context=context)
131 if as_rd_class is not None and as_rd_class is not rd_class:
132 # load with requested format_version
133 discover_details = rd.validation_summary.details
134 rd = as_rd_class.load(content, context=context)
135 assert rd.validation_summary is not None
136 rd.validation_summary.details[:0] = discover_details
138 return rd