labfreed 0.0.8__py2.py3-none-any.whl → 0.0.10__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.
@@ -2,30 +2,47 @@ 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
- from .well_known_segment_keys import WellKnownSegmentKeys
7
+
8
+ from ..utilities.well_known_keys import WellKnownKeys
9
+ from labfreed.validation import BaseModelWithValidationMessages, ValidationMessage, hsegment_pattern, domain_name_pattern
8
10
 
9
11
 
10
- class IDSegment(BaseModelWithWarnings):
12
+ class IDSegment(BaseModelWithValidationMessages):
11
13
  key:str|None = None
12
14
  value:str
15
+
13
16
  @model_validator(mode="after")
14
- def validate_segment(cls, model):
15
- key = model.key or ""
16
- value = model.value
17
+ def validate_segment(self):
18
+ key = self.key or ""
19
+ value = self.value
17
20
 
18
21
  # MUST be a valid hsegment according to RFC 1738, but without * (see PAC-ID Extension)
19
22
  # This means it must be true for both, key and value
20
23
  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)}.")
24
+ self.add_validation_message(
25
+ source=f"id segment key {key}",
26
+ type="Error",
27
+ msg=f"{' '.join(not_allowed_chars)} must not be used.",
28
+ recommendation = "The segment key must be a valid hsegment",
29
+ highlight_pattern = key,
30
+ highlight_sub = not_allowed_chars
31
+ )
22
32
 
23
33
  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)}.")
34
+ self.add_validation_message(
35
+ source=f"id segment key {value}",
36
+ type="Error",
37
+ msg=f"{' '.join(not_allowed_chars)} must not be used.",
38
+ recommendation = "The segment key must be a valid hsegment",
39
+ highlight_pattern = value,
40
+ highlight_sub = not_allowed_chars
41
+ )
25
42
 
26
43
  # Segment key SHOULD be limited to A-Z, 0-9, and -+..
27
44
  if not_recommended_chars := set(re.sub(r'[A-Z0-9-:+]', '', key)):
28
- model.add_warning(
45
+ self.add_validation_message(
29
46
  source=f"id segment key {key}",
30
47
  type="Recommendation",
31
48
  msg=f"{' '.join(not_recommended_chars)} should not be used.",
@@ -35,8 +52,8 @@ class IDSegment(BaseModelWithWarnings):
35
52
  )
36
53
 
37
54
  # Segment key should be in Well know keys
38
- if key and key not in [k.value for k in WellKnownSegmentKeys]:
39
- model.add_warning(
55
+ if key and key not in [k.value for k in WellKnownKeys]:
56
+ self.add_validation_message(
40
57
  source=f"id segment key {key}",
41
58
  type="Recommendation",
42
59
  msg=f"{key} is not a well known segment key.",
@@ -47,7 +64,7 @@ class IDSegment(BaseModelWithWarnings):
47
64
 
48
65
  # Segment value SHOULD be limited to A-Z, 0-9, and -+..
49
66
  if not_recommended_chars := set(re.sub(r'[A-Z0-9-:+]', '', value)):
50
- model.add_warning(
67
+ self.add_validation_message(
51
68
  source=f"id segment value {value}",
52
69
  type="Recommendation",
53
70
  msg=f"Characters {' '.join(not_recommended_chars)} should not be used.",
@@ -59,157 +76,102 @@ class IDSegment(BaseModelWithWarnings):
59
76
  # Segment value SHOULD be limited to A-Z, 0-9, and :-+ for new designs.
60
77
  # this means that ":" in key or value is problematic
61
78
  if ':' in key:
62
- model.add_warning(
79
+ self.add_validation_message(
63
80
  source=f"id segment key {key}",
64
81
  type="Recommendation",
65
82
  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
83
  highlight_pattern = key
67
84
  )
68
85
  if ':' in value:
69
- model.add_warning(
86
+ self.add_validation_message(
70
87
  source=f"id segment value {value}",
71
88
  type="Recommendation",
72
89
  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
90
  highlight_pattern = value
74
91
  )
75
92
 
76
- return model
77
-
93
+ return self
94
+
78
95
 
79
-
80
-
81
- class Category(BaseModelWithWarnings):
82
- key:str|None = None
83
- segments: list[IDSegment]
84
96
 
85
-
86
- class Identifier(BaseModelWithWarnings):
87
- segments: conlist(IDSegment, min_length=1) = Field(..., exclude=True) # type: ignore # exclude=True prevents this from being serialized by Pydantic
97
+ class PACID(BaseModelWithValidationMessages):
98
+ issuer:str
99
+ identifier: conlist(IDSegment, min_length=1) = Field(..., default_factory=list()) # type: ignore # exclude=True prevents this from being serialized by Pydantic
88
100
 
89
- @computed_field
90
- @property
91
- def categories(self) -> list[Category]:
92
- categories = list()
93
- c = Category(segments=[])
94
- categories.append(c)
95
- for s in self.segments:
96
- # new category starts with "-"
97
- if s.value[0] == '-':
98
- cat_key = s.value
99
- c = Category(key=cat_key, segments=[])
100
- categories.append(c)
101
- else:
102
- c.segments.append(s)
103
-
104
- # the first category might have no segments. remove categories without segments
105
- if not categories[0].segments:
106
- categories = categories[1:]
107
-
108
- return categories
109
-
110
- @model_validator(mode='after')
111
- def check_keys_are_unique_in_each_category(self) -> Self:
112
- for c in self.categories:
113
- keys = [s.key for s in c.segments if s.key]
114
- duplicate_keys = [k for k in set(keys) if keys.count(k) > 1]
115
- if duplicate_keys:
116
- raise ValueError(f'Duplicate keys {",".join(duplicate_keys)} in category {c.key}')
117
- return self
118
101
 
119
102
  @model_validator(mode='after')
120
103
  def check_length(self) -> Self:
121
104
  l = 0
122
- for s in self.segments:
105
+ for s in self.identifier:
106
+ s:IDSegment = s
123
107
  if s.key:
124
108
  l += len(s.key)
125
109
  l += 1 # for ":"
126
110
  l += len(s.value)
127
- l += len(self.segments) - 1 # account for "/" separating the segments
111
+ l += len(self.identifier) - 1 # account for "/" separating the segments
128
112
 
129
113
  if l > 256:
130
- raise ValueError(f'Identifier is {l} characters long, Identifier must not exceed 256 characters.')
114
+ self.add_validation_message(
115
+ source=f"identifier",
116
+ type="Error",
117
+ msg=f'Identifier is {l} characters long, Identifier must not exceed 256 characters.',
118
+ highlight_pattern = ""
119
+ )
131
120
  return self
132
121
 
133
- @staticmethod
134
- def from_categories(categories:list[Category]) :
135
- segments = list()
136
- for c in categories:
137
- if c.key:
138
- segments.append(IDSegment(value=c.key))
139
- segments.extend(c.segments)
140
- return Identifier(segments=segments)
141
-
142
-
143
-
144
- class Extension(ABC, BaseModelWithWarnings):
145
-
146
- @abstractproperty
147
- def name(self)->str:
148
- pass
149
-
150
- @abstractproperty
151
- def type(self)->str:
152
- pass
153
-
154
- @abstractproperty
155
- def data(self)->str:
156
- pass
157
-
158
- @abstractstaticmethod
159
- def from_spec_fields(name, type, data):
160
- pass
161
-
162
-
163
- class UnknownExtension(Extension):
164
- name_:str
165
- type_:str
166
- data_:str
167
-
168
- @property
169
- def name(self)->str:
170
- return self.name_
171
-
172
- @property
173
- def type(self)->str:
174
- return self.type_
175
-
176
- @property
177
- def data(self)->str:
178
- return self.data_
179
-
180
- @staticmethod
181
- def from_spec_fields(name, type, data):
182
- return UnknownExtension(name_=name, type_=type, data_=data)
183
-
184
-
185
-
186
- class PACID(BaseModelWithWarnings):
187
- issuer:str
188
- identifier: Identifier
189
122
 
190
123
  @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
-
124
+ def validate_issuer(self):
125
+ if not re.fullmatch(domain_name_pattern, self.issuer):
126
+ self.add_validation_message(
127
+ source="PAC-ID",
128
+ type="Error",
129
+ highlight_pattern=self.issuer,
130
+ msg=f"Issuer must be a valid domain name. "
131
+ )
195
132
 
196
133
  # 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(
134
+ if not_recommended_chars := set(re.sub(r'[A-Z0-9\.-]', '', self.issuer)):
135
+ self.add_validation_message(
199
136
  source="PAC-ID",
200
137
  type="Recommendation",
201
- highlight_pattern=model.issuer,
138
+ highlight_pattern=self.issuer,
202
139
  highlight_sub=not_recommended_chars,
203
140
  msg=f"Characters {' '.join(not_recommended_chars)} should not be used. Issuer SHOULD contain only the characters A-Z, 0-9, -, and . "
204
141
  )
205
- return model
142
+ return self
143
+
144
+
145
+ @model_validator(mode='after')
146
+ def check_identifier_segment_keys_are_unique(self) -> Self:
147
+ keys = [s.key for s in self.identifier if s.key]
148
+ duplicate_keys = [k for k in set(keys) if keys.count(k) > 1]
149
+ if duplicate_keys:
150
+ for k in duplicate_keys:
151
+ self.add_validation_message(
152
+ source=f"identifier {k}",
153
+ type="Recommendation",
154
+ msg=f"Duplicate segment key {k}. This will probably lead to undefined behaviour",
155
+ highlight_pattern = k
156
+ )
157
+ return self
158
+
159
+
160
+ def __str__(self):
161
+ id_segments = ''
162
+ for s in self.identifier:
163
+ s:IDSegment = s
164
+ if s.key:
165
+ id_segments += f'/{s.key}:{s.value}'
166
+ else:
167
+ id_segments += f'/{s.value}'
168
+
169
+ out = f"HTTPS://PAC.{self.issuer}{id_segments}"
170
+ return out
171
+
172
+
173
+ def serialize(self):
174
+ return str(self)
206
175
 
207
176
 
208
- class PACID_With_Extensions(BaseModelWithWarnings):
209
- pac_id: PACID
210
- extensions: list[Extension] = Field(default_factory=list)
211
-
212
-
213
-
214
-
215
177
 
@@ -0,0 +1,55 @@
1
+
2
+ from abc import ABC, abstractproperty, abstractstaticmethod
3
+ from types import MappingProxyType
4
+
5
+ from pydantic import Field
6
+
7
+ from labfreed.validation import BaseModelWithValidationMessages
8
+ from labfreed.PAC_ID.data_model import PACID
9
+
10
+
11
+
12
+
13
+ class Extension(ABC, BaseModelWithValidationMessages):
14
+
15
+ @abstractproperty
16
+ def name(self)->str:
17
+ pass
18
+
19
+ @abstractproperty
20
+ def type(self)->str:
21
+ pass
22
+
23
+ @abstractproperty
24
+ def data(self)->str:
25
+ pass
26
+
27
+ @abstractstaticmethod
28
+ def from_spec_fields(*, name, type, data):
29
+ pass
30
+
31
+ def __str__(self):
32
+ return f'{self.name}${self.type}/{self.data}'
33
+
34
+
35
+
36
+ class UnknownExtension(Extension):
37
+ name_:str
38
+ type_:str
39
+ data_:str
40
+
41
+ @property
42
+ def name(self)->str:
43
+ return self.name_
44
+
45
+ @property
46
+ def type(self)->str:
47
+ return self.type_
48
+
49
+ @property
50
+ def data(self)->str:
51
+ return self.data_
52
+
53
+ @staticmethod
54
+ def from_spec_fields(*, name, type, data):
55
+ return UnknownExtension(name_=name, type_=type, data_=data)