labfreed 0.0.8__tar.gz → 0.0.9__tar.gz
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.
Potentially problematic release.
This version of labfreed might be problematic. Click here for more details.
- {labfreed-0.0.8 → labfreed-0.0.9}/.vscode/launch.json +10 -0
- {labfreed-0.0.8 → labfreed-0.0.9}/.vscode/settings.json +2 -1
- {labfreed-0.0.8 → labfreed-0.0.9}/PKG-INFO +1 -1
- {labfreed-0.0.8 → labfreed-0.0.9}/labfreed/DisplayNameExtension/DisplayNameExtension.py +1 -1
- {labfreed-0.0.8 → labfreed-0.0.9}/labfreed/PAC_ID/data_model.py +60 -28
- {labfreed-0.0.8 → labfreed-0.0.9}/labfreed/PAC_ID/parse.py +6 -4
- labfreed-0.0.9/labfreed/TREX/UneceUnits.json +33730 -0
- labfreed-0.0.9/labfreed/TREX/data_model.py +869 -0
- labfreed-0.0.9/labfreed/TREX/parse.py +128 -0
- labfreed-0.0.9/labfreed/TREX/serialize.py +3 -0
- labfreed-0.0.9/labfreed/TREX/unece_units.py +90 -0
- {labfreed-0.0.8 → labfreed-0.0.9}/labfreed/__init__.py +1 -1
- {labfreed-0.0.8/labfreed/TREXExtension → labfreed-0.0.9/labfreed/conversion_tools}/unit_utilities.py +8 -42
- labfreed-0.0.9/labfreed/validation.py +147 -0
- labfreed-0.0.9/main.py +142 -0
- {labfreed-0.0.8 → labfreed-0.0.9}/pytest.ini +1 -0
- {labfreed-0.0.8 → labfreed-0.0.9}/tests/test_PAC_ID/test_pac_id_parse.py +25 -25
- labfreed-0.0.9/tests/test_TREXExtension/test_TREX_parse.py +271 -0
- labfreed-0.0.9/tests/test_TREXExtension/test_TREX_serialize.py +29 -0
- labfreed-0.0.8/labfreed/TREXExtension/data_model.py +0 -239
- labfreed-0.0.8/labfreed/TREXExtension/parse.py +0 -53
- labfreed-0.0.8/labfreed/validation.py +0 -71
- labfreed-0.0.8/main.py +0 -22
- labfreed-0.0.8/tests/test_TREXExtension/test_TREX.py +0 -31
- {labfreed-0.0.8 → labfreed-0.0.9}/LICENSE +0 -0
- {labfreed-0.0.8 → labfreed-0.0.9}/README.md +0 -0
- {labfreed-0.0.8 → labfreed-0.0.9}/labfreed/PAC_CAT/__init__.py +0 -0
- {labfreed-0.0.8 → labfreed-0.0.9}/labfreed/PAC_CAT/data_model.py +0 -0
- {labfreed-0.0.8 → labfreed-0.0.9}/labfreed/PAC_ID/__init__.py +0 -0
- {labfreed-0.0.8 → labfreed-0.0.9}/labfreed/PAC_ID/serialize.py +0 -0
- {labfreed-0.0.8 → labfreed-0.0.9}/labfreed/PAC_ID/well_known_segment_keys.py +0 -0
- {labfreed-0.0.8/labfreed/TREXExtension → labfreed-0.0.9/labfreed/conversion_tools}/uncertainty.py +0 -0
- {labfreed-0.0.8/labfreed/DisplayNameExtension → labfreed-0.0.9/labfreed/utilities}/base36.py +0 -0
- {labfreed-0.0.8 → labfreed-0.0.9}/pyproject.toml +0 -0
- {labfreed-0.0.8 → labfreed-0.0.9}/tests/test_PAC_CAT/test_PAC_CAT.py +0 -0
- {labfreed-0.0.8 → labfreed-0.0.9}/tests/test_PAC_ID/test_pac_id_serialization.py +0 -0
|
@@ -13,6 +13,16 @@
|
|
|
13
13
|
"env": {
|
|
14
14
|
"PYTHONPATH": "$(pwd)"
|
|
15
15
|
}
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"name": "Python Debugger: Main",
|
|
19
|
+
"type": "debugpy",
|
|
20
|
+
"request": "launch",
|
|
21
|
+
"program": "main.py",
|
|
22
|
+
"console": "integratedTerminal",
|
|
23
|
+
"env": {
|
|
24
|
+
"PYTHONPATH": "$(pwd)"
|
|
25
|
+
}
|
|
16
26
|
}
|
|
17
27
|
]
|
|
18
28
|
}
|
|
@@ -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
|
-
|
|
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(
|
|
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(
|
|
15
|
-
key =
|
|
16
|
-
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
92
|
+
return self
|
|
77
93
|
|
|
78
94
|
|
|
79
95
|
|
|
80
96
|
|
|
81
|
-
class Category(
|
|
97
|
+
class Category(BaseModelWithValidationMessages):
|
|
82
98
|
key:str|None = None
|
|
83
99
|
segments: list[IDSegment]
|
|
84
100
|
|
|
85
101
|
|
|
86
|
-
class Identifier(
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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(
|
|
213
|
+
class PACID(BaseModelWithValidationMessages):
|
|
187
214
|
issuer:str
|
|
188
215
|
identifier: Identifier
|
|
189
216
|
|
|
190
217
|
@model_validator(mode="after")
|
|
191
|
-
def validate_issuer(
|
|
192
|
-
if not re.fullmatch(domain_name_pattern,
|
|
193
|
-
|
|
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\.-]', '',
|
|
198
|
-
|
|
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=
|
|
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
|
|
237
|
+
return self
|
|
206
238
|
|
|
207
239
|
|
|
208
|
-
class PACID_With_Extensions(
|
|
240
|
+
class PACID_With_Extensions(BaseModelWithValidationMessages):
|
|
209
241
|
pac_id: PACID
|
|
210
242
|
extensions: list[Extension] = Field(default_factory=list)
|
|
211
243
|
|
|
@@ -4,7 +4,7 @@ import re
|
|
|
4
4
|
from types import MappingProxyType
|
|
5
5
|
from .data_model import *
|
|
6
6
|
|
|
7
|
-
from ..validation import
|
|
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[
|
|
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
|
-
|
|
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
|
|
49
|
+
return pac_with_extension
|
|
48
50
|
|
|
49
51
|
|
|
50
52
|
def parse_id_segments(self, identifier:str):
|