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
« 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
5import base64
6import re
7from datetime import datetime
8from urllib.parse import urlparse
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
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)
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
46def validate_integer(value: str) -> bool:
47 try:
48 int(value)
49 except (ValueError, TypeError):
50 return False
51 else:
52 return True
55def validate_positive_integer(value: str) -> bool:
56 try:
57 return int(value) > 0
58 except (ValueError, TypeError):
59 return False
62def validate_negative_integer(value: str) -> bool:
63 try:
64 return int(value) < 0
65 except (ValueError, TypeError):
66 return False
69def validate_non_negative_integer(value: str) -> bool:
70 try:
71 return int(value) >= 0
72 except (ValueError, TypeError):
73 return False
76def validate_non_positive_integer(value: str) -> bool:
77 try:
78 return int(value) <= 0
79 except (ValueError, TypeError):
80 return False
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
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
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
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
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
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
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
146def validate_float(value: str) -> bool:
147 try:
148 float(value)
149 except (ValueError, TypeError):
150 return False
151 else:
152 return True
155def validate_double(value: str) -> bool:
156 try:
157 float(value)
158 except (ValueError, TypeError):
159 return False
160 else:
161 return True
164def validate_decimal(value: str) -> bool:
165 try:
166 float(value)
167 except (ValueError, TypeError):
168 return False
169 else:
170 return True
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
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
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
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
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
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
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
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
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
261def validate_hour(value: str) -> bool:
262 try:
263 return 0 <= int(value) <= _MAX_HOUR
264 except (ValueError, TypeError):
265 return False
268def validate_minute(value: str) -> bool:
269 try:
270 return 0 <= int(value) <= _MAX_MINUTE
271 except (ValueError, TypeError):
272 return False
275def validate_second(value: str) -> bool:
276 try:
277 return 0 <= float(value) < _MAX_SECOND
278 except (ValueError, TypeError):
279 return False
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
290def validate_boolean(value: str) -> bool:
291 try:
292 return value.lower() in ["true", "false"]
293 except AttributeError:
294 return False
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
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
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
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
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
339validate_entity = validate_entities
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
349validate_idref = validate_id
350validate_idrefs = validate_entities
351validate_ncname = validate_id
354def validate_nmtoken(value: str) -> bool:
355 try:
356 return re.match(r"^[\w.-]+$", value) is not None
357 except TypeError:
358 return False
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
369validate_notation = validate_qname
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