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

1"""implementation details for building a bioimage.io resource description""" 

2 

3from __future__ import annotations 

4 

5import collections.abc 

6from typing import Any, Callable, Literal, Mapping, TypeVar 

7 

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) 

17 

18ResourceDescrT = TypeVar("ResourceDescrT", bound=ResourceDescrBase) 

19 

20 

21DISCOVER: Literal["discover"] = "discover" 

22"""placeholder for whatever format version an RDF specifies""" 

23 

24 

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 

41 

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(".")] 

50 

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 ) 

67 

68 return descr_versions[use_format_version] 

69 

70 

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 = {} 

104 

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 

119 

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) 

130 

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 

137 

138 return rd