Coverage for heritrace / utils / datatypes_validation.py: 99%

262 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-07-02 10:16 +0000

1# SPDX-FileCopyrightText: 2025 Arcangelo Massari <arcangelo.massari@unibo.it> 

2# 

3# SPDX-License-Identifier: ISC 

4 

5import base64 

6import re 

7from datetime import datetime 

8from urllib.parse import urlparse 

9 

10_BYTE_MIN = -128 

11_BYTE_MAX = 127 

12_SHORT_MIN = -32_768 

13_SHORT_MAX = 32_767 

14_INT_MIN = -2_147_483_648 

15_INT_MAX = 2_147_483_647 

16_UNSIGNED_BYTE_MAX = 255 

17_UNSIGNED_SHORT_MAX = 65_535 

18_UNSIGNED_INT_MAX = 4_294_967_295 

19_UNSIGNED_LONG_MAX = 18_446_744_073_709_551_615 

20_LONG_MIN = -9_223_372_036_854_775_808 

21_LONG_MAX = 9_223_372_036_854_775_807 

22_MAX_YEAR = 9999 

23_MIN_GREGORIAN_YEAR = 1582 

24_MAX_MONTH = 12 

25_MAX_HOUR = 23 

26_MAX_MINUTE = 59 

27_MAX_SECOND = 60 

28 

29 

30def validate_string(value: str) -> bool: 

31 try: 

32 value = str(value) 

33 except ValueError: 

34 return False 

35 else: 

36 return isinstance(value, str) 

37 

38 

39def validate_normalized_string(value: str) -> bool: 

40 try: 

41 return "\n" not in value and "\r" not in value and "\t" not in value 

42 except TypeError: 

43 return False 

44 

45 

46def validate_integer(value: str) -> bool: 

47 try: 

48 int(value) 

49 except (ValueError, TypeError): 

50 return False 

51 else: 

52 return True 

53 

54 

55def validate_positive_integer(value: str) -> bool: 

56 try: 

57 return int(value) > 0 

58 except (ValueError, TypeError): 

59 return False 

60 

61 

62def validate_negative_integer(value: str) -> bool: 

63 try: 

64 return int(value) < 0 

65 except (ValueError, TypeError): 

66 return False 

67 

68 

69def validate_non_negative_integer(value: str) -> bool: 

70 try: 

71 return int(value) >= 0 

72 except (ValueError, TypeError): 

73 return False 

74 

75 

76def validate_non_positive_integer(value: str) -> bool: 

77 try: 

78 return int(value) <= 0 

79 except (ValueError, TypeError): 

80 return False 

81 

82 

83def validate_byte(value: str) -> bool: 

84 try: 

85 val = int(value) 

86 except (ValueError, TypeError): 

87 return False 

88 else: 

89 return _BYTE_MIN <= val <= _BYTE_MAX 

90 

91 

92def validate_short(value: str) -> bool: 

93 try: 

94 val = int(value) 

95 except (ValueError, TypeError): 

96 return False 

97 else: 

98 return _SHORT_MIN <= val <= _SHORT_MAX 

99 

100 

101def validate_long(value: str) -> bool: 

102 try: 

103 val = int(value) 

104 except (ValueError, TypeError): 

105 return False 

106 else: 

107 return _INT_MIN <= val <= _INT_MAX 

108 

109 

110def validate_unsigned_byte(value: str) -> bool: 

111 try: 

112 val = int(value) 

113 except (ValueError, TypeError): 

114 return False 

115 else: 

116 return 0 <= val <= _UNSIGNED_BYTE_MAX 

117 

118 

119def validate_unsigned_short(value: str) -> bool: 

120 try: 

121 val = int(value) 

122 except (ValueError, TypeError): 

123 return False 

124 else: 

125 return 0 <= val <= _UNSIGNED_SHORT_MAX 

126 

127 

128def validate_unsigned_long(value: str) -> bool: 

129 try: 

130 val = int(value) 

131 except (ValueError, TypeError): 

132 return False 

133 else: 

134 return 0 <= val <= _UNSIGNED_INT_MAX 

135 

136 

137def validate_unsigned_int(value: str) -> bool: 

138 try: 

139 val = int(value) 

140 except (ValueError, TypeError): 

141 return False 

142 else: 

143 return 0 <= val <= _UNSIGNED_INT_MAX 

144 

145 

146def validate_float(value: str) -> bool: 

147 try: 

148 float(value) 

149 except (ValueError, TypeError): 

150 return False 

151 else: 

152 return True 

153 

154 

155def validate_double(value: str) -> bool: 

156 try: 

157 float(value) 

158 except (ValueError, TypeError): 

159 return False 

160 else: 

161 return True 

162 

163 

164def validate_decimal(value: str) -> bool: 

165 try: 

166 float(value) 

167 except (ValueError, TypeError): 

168 return False 

169 else: 

170 return True 

171 

172 

173def validate_duration(value: str) -> bool: 

174 try: 

175 duration_pattern = re.compile( 

176 r"^P(?=\d|T\d)(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+(?:\.\d+?)?)S)?)?$" 

177 ) 

178 return bool(duration_pattern.match(value)) 

179 except TypeError: 

180 return False 

181 

182 

183def validate_day_time_duration(value: str) -> bool: 

184 try: 

185 pattern = re.compile(r"^P(?:\d+D)?(?:T(?:\d+H)?(?:\d+M)?(?:\d+(?:\.\d+)?S)?)?$") 

186 return bool(pattern.match(value)) 

187 except TypeError: 

188 return False 

189 

190 

191def validate_year_month_duration(value: str) -> bool: 

192 try: 

193 pattern = re.compile(r"^P(?:\d+Y)?(?:\d+M)?$") 

194 return bool(pattern.match(value)) 

195 except TypeError: 

196 return False 

197 

198 

199def validate_g_year_month(value: str) -> bool: 

200 try: 

201 pattern = re.compile(r"^(\d{4})-(\d{2})$") 

202 match = pattern.match(value) 

203 except TypeError: 

204 return False 

205 else: 

206 if match: 

207 year, month = map(int, match.groups()) 

208 return year <= _MAX_YEAR and 1 <= month <= _MAX_MONTH 

209 return False 

210 

211 

212def validate_g_year(value: str) -> bool: 

213 try: 

214 pattern = re.compile(r"^\d{4}$") 

215 match = pattern.match(value) 

216 except TypeError: 

217 return False 

218 else: 

219 if match: 

220 year = int(value) 

221 return _MIN_GREGORIAN_YEAR <= year <= _MAX_YEAR 

222 return False 

223 

224 

225def validate_date_time(value: str) -> bool: 

226 try: 

227 pattern = re.compile( 

228 r"^-?\d{4,}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?$" 

229 ) 

230 return bool(pattern.match(value)) 

231 except TypeError: 

232 return False 

233 

234 

235def validate_date_time_stamp(value: str) -> bool: 

236 try: 

237 pattern = re.compile( 

238 r"^-?\d{4,}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(Z|[+-]\d{2}:\d{2})$" 

239 ) 

240 return bool(pattern.match(value)) 

241 except TypeError: 

242 return False 

243 

244 

245def validate_date(value: str) -> bool: 

246 try: 

247 datetime.strptime(value, "%Y-%m-%d").date() 

248 except (ValueError, TypeError): 

249 return False 

250 else: 

251 return True 

252 

253 

254def validate_time(value: str) -> bool: 

255 try: 

256 return bool(re.match(r"^([01]\d|2[0-3]):?([0-5]\d):?([0-5]\d)$", value)) 

257 except TypeError: 

258 return False 

259 

260 

261def validate_hour(value: str) -> bool: 

262 try: 

263 return 0 <= int(value) <= _MAX_HOUR 

264 except (ValueError, TypeError): 

265 return False 

266 

267 

268def validate_minute(value: str) -> bool: 

269 try: 

270 return 0 <= int(value) <= _MAX_MINUTE 

271 except (ValueError, TypeError): 

272 return False 

273 

274 

275def validate_second(value: str) -> bool: 

276 try: 

277 return 0 <= float(value) < _MAX_SECOND 

278 except (ValueError, TypeError): 

279 return False 

280 

281 

282def validate_timezone_offset(value: str) -> bool: 

283 try: 

284 pattern = re.compile(r"^[+-]\d{2}:\d{2}$") 

285 return bool(pattern.match(value)) 

286 except TypeError: 

287 return False 

288 

289 

290def validate_boolean(value: str) -> bool: 

291 try: 

292 return value.lower() in ["true", "false"] 

293 except AttributeError: 

294 return False 

295 

296 

297def validate_hex_binary(value: str) -> bool: 

298 try: 

299 bytes.fromhex(value) 

300 except (ValueError, TypeError): 

301 return False 

302 else: 

303 return True 

304 

305 

306def validate_base64_binary(value: str) -> bool: 

307 try: 

308 base64.b64decode(value) 

309 except (ValueError, TypeError): 

310 return False 

311 else: 

312 return True 

313 

314 

315def validate_url(value: str) -> bool: 

316 try: 

317 result = urlparse(value) 

318 return all([result.scheme, result.netloc]) 

319 except (ValueError, TypeError): 

320 return False 

321 

322 

323def validate_qname(value: str) -> bool: 

324 try: 

325 pattern = re.compile(r"^(?:[a-zA-Z_][\w.-]*:)?[a-zA-Z_][\w.-]*$") 

326 return bool(pattern.match(value)) 

327 except TypeError: 

328 return False 

329 

330 

331def validate_entities(value: str) -> bool: 

332 try: 

333 entities = value.split() 

334 return all(re.match(r"^[a-zA-Z_][\w.-]*$", entity) for entity in entities) 

335 except (TypeError, AttributeError): 

336 return False 

337 

338 

339validate_entity = validate_entities 

340 

341 

342def validate_id(value: str) -> bool: 

343 try: 

344 return re.match(r"^[a-zA-Z_][\w.-]*$", value) is not None 

345 except TypeError: 

346 return False 

347 

348 

349validate_idref = validate_id 

350validate_idrefs = validate_entities 

351validate_ncname = validate_id 

352 

353 

354def validate_nmtoken(value: str) -> bool: 

355 try: 

356 return re.match(r"^[\w.-]+$", value) is not None 

357 except TypeError: 

358 return False 

359 

360 

361def validate_nmtokens(value: str) -> bool: 

362 try: 

363 tokens = value.split() 

364 return all(re.match(r"^[\w.-]+$", token) for token in tokens) 

365 except (TypeError, AttributeError): 

366 return False 

367 

368 

369validate_notation = validate_qname 

370 

371 

372def validate_name(value: str) -> bool: 

373 try: 

374 return re.match(r"^[a-zA-Z_:][\w.-]*$", value) is not None 

375 except TypeError: 

376 return False