labfreed 0.0.7__py2.py3-none-any.whl → 0.0.9__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  import logging
2
2
  from pydantic import BaseModel
3
3
  from ..PAC_ID.data_model import Extension
4
- from .base36 import from_base36, to_base36
4
+ from labfreed.utilities.base36 import from_base36, to_base36
5
5
 
6
6
 
7
7
  class DisplayNames(Extension, BaseModel):
@@ -2,30 +2,46 @@ import re
2
2
  from typing import Optional
3
3
  from typing_extensions import Self
4
4
  from pydantic import Field, ValidationInfo, computed_field, conlist, model_validator, field_validator
5
- from ..validation import BaseModelWithWarnings, ValidationWarning, hsegment_pattern, domain_name_pattern
5
+
6
6
  from abc import ABC, abstractproperty, abstractstaticmethod
7
7
  from .well_known_segment_keys import WellKnownSegmentKeys
8
+ from labfreed.validation import BaseModelWithValidationMessages, ValidationMessage, hsegment_pattern, domain_name_pattern
8
9
 
9
10
 
10
- class IDSegment(BaseModelWithWarnings):
11
+ class IDSegment(BaseModelWithValidationMessages):
11
12
  key:str|None = None
12
13
  value:str
14
+
13
15
  @model_validator(mode="after")
14
- def validate_segment(cls, model):
15
- key = model.key or ""
16
- value = model.value
16
+ def validate_segment(self):
17
+ key = self.key or ""
18
+ value = self.value
17
19
 
18
20
  # MUST be a valid hsegment according to RFC 1738, but without * (see PAC-ID Extension)
19
21
  # This means it must be true for both, key and value
20
22
  if not_allowed_chars := set(re.sub(hsegment_pattern, '', key)):
21
- raise ValueError(f"id segment key {key} contains invalid characters {' '.join(not_allowed_chars)}.")
23
+ self.add_validation_message(
24
+ source=f"id segment key {key}",
25
+ type="Error",
26
+ msg=f"{' '.join(not_allowed_chars)} must not be used.",
27
+ recommendation = "The segment key must be a valid hsegment",
28
+ highlight_pattern = key,
29
+ highlight_sub = not_allowed_chars
30
+ )
22
31
 
23
32
  if not_allowed_chars := set(re.sub(hsegment_pattern, '', value)):
24
- raise ValueError(f"id segment key {value} contains invalid characters {' '.join(not_allowed_chars)}.")
33
+ self.add_validation_message(
34
+ source=f"id segment key {value}",
35
+ type="Error",
36
+ msg=f"{' '.join(not_allowed_chars)} must not be used.",
37
+ recommendation = "The segment key must be a valid hsegment",
38
+ highlight_pattern = value,
39
+ highlight_sub = not_allowed_chars
40
+ )
25
41
 
26
42
  # Segment key SHOULD be limited to A-Z, 0-9, and -+..
27
43
  if not_recommended_chars := set(re.sub(r'[A-Z0-9-:+]', '', key)):
28
- model.add_warning(
44
+ self.add_validation_message(
29
45
  source=f"id segment key {key}",
30
46
  type="Recommendation",
31
47
  msg=f"{' '.join(not_recommended_chars)} should not be used.",
@@ -36,7 +52,7 @@ class IDSegment(BaseModelWithWarnings):
36
52
 
37
53
  # Segment key should be in Well know keys
38
54
  if key and key not in [k.value for k in WellKnownSegmentKeys]:
39
- model.add_warning(
55
+ self.add_validation_message(
40
56
  source=f"id segment key {key}",
41
57
  type="Recommendation",
42
58
  msg=f"{key} is not a well known segment key.",
@@ -47,7 +63,7 @@ class IDSegment(BaseModelWithWarnings):
47
63
 
48
64
  # Segment value SHOULD be limited to A-Z, 0-9, and -+..
49
65
  if not_recommended_chars := set(re.sub(r'[A-Z0-9-:+]', '', value)):
50
- model.add_warning(
66
+ self.add_validation_message(
51
67
  source=f"id segment value {value}",
52
68
  type="Recommendation",
53
69
  msg=f"Characters {' '.join(not_recommended_chars)} should not be used.",
@@ -59,31 +75,31 @@ class IDSegment(BaseModelWithWarnings):
59
75
  # Segment value SHOULD be limited to A-Z, 0-9, and :-+ for new designs.
60
76
  # this means that ":" in key or value is problematic
61
77
  if ':' in key:
62
- model.add_warning(
78
+ self.add_validation_message(
63
79
  source=f"id segment key {key}",
64
80
  type="Recommendation",
65
81
  msg=f"Character ':' should not be used in segment key, since this character is used to separate key and value this can lead to undefined behaviour.",
66
82
  highlight_pattern = key
67
83
  )
68
84
  if ':' in value:
69
- model.add_warning(
85
+ self.add_validation_message(
70
86
  source=f"id segment value {value}",
71
87
  type="Recommendation",
72
88
  msg=f"Character ':' should not be used in segment value, since this character is used to separate key and value this can lead to undefined behaviour.",
73
89
  highlight_pattern = value
74
90
  )
75
91
 
76
- return model
92
+ return self
77
93
 
78
94
 
79
95
 
80
96
 
81
- class Category(BaseModelWithWarnings):
97
+ class Category(BaseModelWithValidationMessages):
82
98
  key:str|None = None
83
99
  segments: list[IDSegment]
84
100
 
85
101
 
86
- class Identifier(BaseModelWithWarnings):
102
+ class Identifier(BaseModelWithValidationMessages):
87
103
  segments: conlist(IDSegment, min_length=1) = Field(..., exclude=True) # type: ignore # exclude=True prevents this from being serialized by Pydantic
88
104
 
89
105
  @computed_field
@@ -113,7 +129,13 @@ class Identifier(BaseModelWithWarnings):
113
129
  keys = [s.key for s in c.segments if s.key]
114
130
  duplicate_keys = [k for k in set(keys) if keys.count(k) > 1]
115
131
  if duplicate_keys:
116
- raise ValueError(f'Duplicate keys {",".join(duplicate_keys)} in category {c.key}')
132
+ for k in duplicate_keys:
133
+ self.add_validation_message(
134
+ source=f"identifier {k}",
135
+ type="Error",
136
+ msg=f"Duplicate key {k} in category {c.key}",
137
+ highlight_pattern = k
138
+ )
117
139
  return self
118
140
 
119
141
  @model_validator(mode='after')
@@ -127,7 +149,12 @@ class Identifier(BaseModelWithWarnings):
127
149
  l += len(self.segments) - 1 # account for "/" separating the segments
128
150
 
129
151
  if l > 256:
130
- raise ValueError(f'Identifier is {l} characters long, Identifier must not exceed 256 characters.')
152
+ self.add_validation_message(
153
+ source=f"identifier",
154
+ type="Error",
155
+ msg=f'Identifier is {l} characters long, Identifier must not exceed 256 characters.',
156
+ highlight_pattern = ""
157
+ )
131
158
  return self
132
159
 
133
160
  @staticmethod
@@ -141,7 +168,7 @@ class Identifier(BaseModelWithWarnings):
141
168
 
142
169
 
143
170
 
144
- class Extension(ABC, BaseModelWithWarnings):
171
+ class Extension(ABC, BaseModelWithValidationMessages):
145
172
 
146
173
  @abstractproperty
147
174
  def name(self)->str:
@@ -183,29 +210,34 @@ class UnknownExtension(Extension):
183
210
 
184
211
 
185
212
 
186
- class PACID(BaseModelWithWarnings):
213
+ class PACID(BaseModelWithValidationMessages):
187
214
  issuer:str
188
215
  identifier: Identifier
189
216
 
190
217
  @model_validator(mode="after")
191
- def validate_issuer(cls, model):
192
- if not re.fullmatch(domain_name_pattern, model.issuer):
193
- raise ValueError("Issuer must be a valid domain name.")
194
-
218
+ def validate_issuer(self):
219
+ if not re.fullmatch(domain_name_pattern, self.issuer):
220
+ self.add_validation_message(
221
+ source="PAC-ID",
222
+ type="Error",
223
+ highlight_pattern=self.issuer,
224
+ highlight_sub=not_recommended_chars,
225
+ msg=f"Issuer must be a valid domain name. "
226
+ )
195
227
 
196
228
  # recommendation that A-Z, 0-9, -, and . should be used
197
- if not_recommended_chars := set(re.sub(r'[A-Z0-9\.-]', '', model.issuer)):
198
- model.add_warning(
229
+ if not_recommended_chars := set(re.sub(r'[A-Z0-9\.-]', '', self.issuer)):
230
+ self.add_validation_message(
199
231
  source="PAC-ID",
200
232
  type="Recommendation",
201
- highlight_pattern=model.issuer,
233
+ highlight_pattern=self.issuer,
202
234
  highlight_sub=not_recommended_chars,
203
235
  msg=f"Characters {' '.join(not_recommended_chars)} should not be used. Issuer SHOULD contain only the characters A-Z, 0-9, -, and . "
204
236
  )
205
- return model
237
+ return self
206
238
 
207
239
 
208
- class PACID_With_Extensions(BaseModelWithWarnings):
240
+ class PACID_With_Extensions(BaseModelWithValidationMessages):
209
241
  pac_id: PACID
210
242
  extensions: list[Extension] = Field(default_factory=list)
211
243
 
labfreed/PAC_ID/parse.py CHANGED
@@ -4,7 +4,7 @@ import re
4
4
  from types import MappingProxyType
5
5
  from .data_model import *
6
6
 
7
- from ..validation import extract_warnings, ValidationWarning
7
+ from ..validation import ValidationMessage, LabFREEDValidationError
8
8
 
9
9
 
10
10
  category_conventions = MappingProxyType(
@@ -31,7 +31,7 @@ class PAC_Parser():
31
31
  def __init__(self, extension_interpreters:dict[str, Extension]=None):
32
32
  self.extension_interpreters = extension_interpreters or {}
33
33
 
34
- def parse_pac_url(self, pac_url:str) -> tuple[PACID_With_Extensions, list[ValidationWarning] ]:
34
+ def parse_pac_url(self, pac_url:str) -> tuple[PACID_With_Extensions, list[ValidationMessage] ]:
35
35
  if '*' in pac_url:
36
36
  id_str, ext_str = pac_url.split('*', 1)
37
37
  else:
@@ -42,9 +42,11 @@ class PAC_Parser():
42
42
  extensions = self.parse_extensions(ext_str)
43
43
 
44
44
  pac_with_extension = PACID_With_Extensions(pac_id=pac_id, extensions=extensions)
45
- warnings = extract_warnings(pac_with_extension)
45
+ pac_with_extension.print_validation_messages(pac_url)
46
+ if not pac_with_extension.is_valid():
47
+ raise LabFREEDValidationError(validation_msgs = pac_with_extension.get_nested_validation_messages())
46
48
 
47
- return pac_with_extension, warnings
49
+ return pac_with_extension
48
50
 
49
51
 
50
52
  def parse_id_segments(self, identifier:str):