Coverage for src / bioimageio / spec / _internal / version_type.py: 86%

69 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-03-27 14:45 +0000

1from __future__ import annotations 

2 

3from typing import Any, Optional, Tuple, Union 

4 

5import packaging.version 

6from pydantic import PrivateAttr, RootModel 

7 

8 

9class Version(RootModel[Union[str, int, float]]): 

10 """wraps a packaging.version.Version instance for validation in pydantic models""" 

11 

12 _version: packaging.version.Version = PrivateAttr() 

13 

14 def __str__(self): 

15 return str(self._version) 

16 

17 def model_post_init(self, __context: Any) -> None: 

18 """set `_version` attribute @private""" 

19 self._version = packaging.version.Version(str(self.root)) 

20 return super().model_post_init(__context) 

21 

22 def __lt__(self, other: Any): 

23 if not isinstance(other, Version): 

24 return NotImplemented 

25 

26 return self._version < other._version 

27 

28 def __eq__(self, other: Any): 

29 if not isinstance(other, Version): 

30 return NotImplemented 

31 return self._version == other._version 

32 

33 def __ge__(self, other: Any): 

34 if not isinstance(other, Version): 

35 return NotImplemented 

36 return self._version >= other._version 

37 

38 def __le__(self, other: Any): 

39 if not isinstance(other, Version): 

40 return NotImplemented 

41 return self._version <= other._version 

42 

43 # the properties below are adopted from and mirror properties of packaging.version.Version 

44 @property 

45 def epoch(self) -> int: 

46 """The epoch of the version. 

47 

48 >>> Version("2.0.0").epoch 

49 0 

50 >>> Version("1!2.0.0").epoch 

51 1 

52 """ 

53 return self._version.epoch 

54 

55 @property 

56 def release(self) -> Tuple[int, ...]: 

57 """The components of the "release" segment of the version. 

58 

59 >>> Version("1.2.3").release 

60 (1, 2, 3) 

61 >>> Version("2.0.0").release 

62 (2, 0, 0) 

63 >>> Version("1!2.0.0.post0").release 

64 (2, 0, 0) 

65 

66 Includes trailing zeroes but not the epoch or any pre-release / development / 

67 post-release suffixes. 

68 """ 

69 return self._version.release 

70 

71 @property 

72 def pre(self) -> Optional[Tuple[str, int]]: 

73 """The pre-release segment of the version. 

74 

75 >>> print(Version("1.2.3").pre) 

76 None 

77 >>> Version("1.2.3a1").pre 

78 ('a', 1) 

79 >>> Version("1.2.3b1").pre 

80 ('b', 1) 

81 >>> Version("1.2.3rc1").pre 

82 ('rc', 1) 

83 """ 

84 return self._version.pre 

85 

86 @property 

87 def post(self) -> Optional[int]: 

88 """The post-release number of the version. 

89 

90 >>> print(Version("1.2.3").post) 

91 None 

92 >>> Version("1.2.3.post1").post 

93 1 

94 """ 

95 return self._version.post 

96 

97 @property 

98 def dev(self) -> Optional[int]: 

99 """The development number of the version. 

100 

101 >>> print(Version("1.2.3").dev) 

102 None 

103 >>> Version("1.2.3.dev1").dev 

104 1 

105 """ 

106 return self._version.dev 

107 

108 @property 

109 def local(self) -> Optional[str]: 

110 """The local version segment of the version. 

111 

112 >>> print(Version("1.2.3").local) 

113 None 

114 >>> Version("1.2.3+abc").local 

115 'abc' 

116 """ 

117 return self._version.local 

118 

119 @property 

120 def public(self) -> str: 

121 """The public portion of the version. 

122 

123 >>> Version("1.2.3").public 

124 '1.2.3' 

125 >>> Version("1.2.3+abc").public 

126 '1.2.3' 

127 >>> Version("1.2.3+abc.dev1").public 

128 '1.2.3' 

129 """ 

130 return self._version.public 

131 

132 @property 

133 def base_version(self) -> str: 

134 """The "base version" of the version. 

135 

136 >>> Version("1.2.3").base_version 

137 '1.2.3' 

138 >>> Version("1.2.3+abc").base_version 

139 '1.2.3' 

140 >>> Version("1!1.2.3+abc.dev1").base_version 

141 '1!1.2.3' 

142 

143 The "base version" is the public version of the project without any pre or post 

144 release markers. 

145 """ 

146 return self._version.base_version 

147 

148 @property 

149 def is_prerelease(self) -> bool: 

150 """Whether this version is a pre-release. 

151 

152 >>> Version("1.2.3").is_prerelease 

153 False 

154 >>> Version("1.2.3a1").is_prerelease 

155 True 

156 >>> Version("1.2.3b1").is_prerelease 

157 True 

158 >>> Version("1.2.3rc1").is_prerelease 

159 True 

160 >>> Version("1.2.3dev1").is_prerelease 

161 True 

162 """ 

163 return self._version.is_prerelease 

164 

165 @property 

166 def is_postrelease(self) -> bool: 

167 """Whether this version is a post-release. 

168 

169 >>> Version("1.2.3").is_postrelease 

170 False 

171 >>> Version("1.2.3.post1").is_postrelease 

172 True 

173 """ 

174 return self._version.is_postrelease 

175 

176 @property 

177 def is_devrelease(self) -> bool: 

178 """Whether this version is a development release. 

179 

180 >>> Version("1.2.3").is_devrelease 

181 False 

182 >>> Version("1.2.3.dev1").is_devrelease 

183 True 

184 """ 

185 return self._version.is_devrelease 

186 

187 @property 

188 def major(self) -> int: 

189 """The first item of :attr:`release` or ``0`` if unavailable. 

190 

191 >>> Version("1.2.3").major 

192 1 

193 """ 

194 return self._version.major 

195 

196 @property 

197 def minor(self) -> int: 

198 """The second item of :attr:`release` or ``0`` if unavailable. 

199 

200 >>> Version("1.2.3").minor 

201 2 

202 >>> Version("1").minor 

203 0 

204 """ 

205 return self._version.minor 

206 

207 @property 

208 def micro(self) -> int: 

209 """The third item of :attr:`release` or ``0`` if unavailable. 

210 

211 >>> Version("1.2.3").micro 

212 3 

213 >>> Version("1").micro 

214 0 

215 """ 

216 return self._version.micro