labfreed 0.0.3__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.
- labfreed/DisplayNameExtension/DisplayNameExtension.py +37 -0
- labfreed/PAC_CAT/__init__.py +1 -0
- labfreed/PAC_CAT/data_model copy.py +232 -0
- labfreed/PAC_CAT/data_model.py +369 -0
- labfreed/PAC_ID/__init__.py +0 -0
- labfreed/PAC_ID/data_model.py +177 -0
- labfreed/PAC_ID/extensions.py +55 -0
- labfreed/TREX/UneceUnits.json +33730 -0
- labfreed/TREX/data_model.py +789 -0
- labfreed/TREX/parse.py +60 -0
- labfreed/TREX/unece_units.py +106 -0
- labfreed/__init__.py +5 -0
- labfreed/parse_pac.py +189 -0
- labfreed/utilities/base36.py +82 -0
- labfreed/utilities/extension_intertpreters.py +4 -0
- labfreed/utilities/utility_types.py +103 -0
- labfreed/utilities/well_known_keys.py +16 -0
- labfreed/validation.py +149 -0
- labfreed-0.0.3.dist-info/METADATA +7 -0
- labfreed-0.0.3.dist-info/RECORD +22 -0
- labfreed-0.0.3.dist-info/WHEEL +5 -0
- labfreed-0.0.3.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from typing import Optional
|
|
3
|
+
from typing_extensions import Self
|
|
4
|
+
from pydantic import Field, ValidationInfo, computed_field, conlist, model_validator, field_validator
|
|
5
|
+
|
|
6
|
+
from abc import ABC, abstractproperty, abstractstaticmethod
|
|
7
|
+
|
|
8
|
+
from ..utilities.well_known_keys import WellKnownKeys
|
|
9
|
+
from labfreed.validation import BaseModelWithValidationMessages, ValidationMessage, hsegment_pattern, domain_name_pattern
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class IDSegment(BaseModelWithValidationMessages):
|
|
13
|
+
key:str|None = None
|
|
14
|
+
value:str
|
|
15
|
+
|
|
16
|
+
@model_validator(mode="after")
|
|
17
|
+
def validate_segment(self):
|
|
18
|
+
key = self.key or ""
|
|
19
|
+
value = self.value
|
|
20
|
+
|
|
21
|
+
# MUST be a valid hsegment according to RFC 1738, but without * (see PAC-ID Extension)
|
|
22
|
+
# This means it must be true for both, key and value
|
|
23
|
+
if not_allowed_chars := set(re.sub(hsegment_pattern, '', key)):
|
|
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
|
+
)
|
|
32
|
+
|
|
33
|
+
if not_allowed_chars := set(re.sub(hsegment_pattern, '', value)):
|
|
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
|
+
)
|
|
42
|
+
|
|
43
|
+
# Segment key SHOULD be limited to A-Z, 0-9, and -+..
|
|
44
|
+
if not_recommended_chars := set(re.sub(r'[A-Z0-9-:+]', '', key)):
|
|
45
|
+
self.add_validation_message(
|
|
46
|
+
source=f"id segment key {key}",
|
|
47
|
+
type="Recommendation",
|
|
48
|
+
msg=f"{' '.join(not_recommended_chars)} should not be used.",
|
|
49
|
+
recommendation = "SHOULD be limited to A-Z, 0-9, and -+",
|
|
50
|
+
highlight_pattern = key,
|
|
51
|
+
highlight_sub = not_recommended_chars
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# Segment key should be in Well know keys
|
|
55
|
+
if key and key not in [k.value for k in WellKnownKeys]:
|
|
56
|
+
self.add_validation_message(
|
|
57
|
+
source=f"id segment key {key}",
|
|
58
|
+
type="Recommendation",
|
|
59
|
+
msg=f"{key} is not a well known segment key.",
|
|
60
|
+
recommendation = "RECOMMENDED to be a well-known id segment key.",
|
|
61
|
+
highlight_pattern = key
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# Segment value SHOULD be limited to A-Z, 0-9, and -+..
|
|
66
|
+
if not_recommended_chars := set(re.sub(r'[A-Z0-9-:+]', '', value)):
|
|
67
|
+
self.add_validation_message(
|
|
68
|
+
source=f"id segment value {value}",
|
|
69
|
+
type="Recommendation",
|
|
70
|
+
msg=f"Characters {' '.join(not_recommended_chars)} should not be used.",
|
|
71
|
+
recommendation = "SHOULD be limited to A-Z, 0-9, and -+",
|
|
72
|
+
highlight_pattern = value,
|
|
73
|
+
highlight_sub = not_recommended_chars
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# Segment value SHOULD be limited to A-Z, 0-9, and :-+ for new designs.
|
|
77
|
+
# this means that ":" in key or value is problematic
|
|
78
|
+
if ':' in key:
|
|
79
|
+
self.add_validation_message(
|
|
80
|
+
source=f"id segment key {key}",
|
|
81
|
+
type="Recommendation",
|
|
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.",
|
|
83
|
+
highlight_pattern = key
|
|
84
|
+
)
|
|
85
|
+
if ':' in value:
|
|
86
|
+
self.add_validation_message(
|
|
87
|
+
source=f"id segment value {value}",
|
|
88
|
+
type="Recommendation",
|
|
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.",
|
|
90
|
+
highlight_pattern = value
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
return self
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
|
|
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
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@model_validator(mode='after')
|
|
103
|
+
def check_length(self) -> Self:
|
|
104
|
+
l = 0
|
|
105
|
+
for s in self.identifier:
|
|
106
|
+
s:IDSegment = s
|
|
107
|
+
if s.key:
|
|
108
|
+
l += len(s.key)
|
|
109
|
+
l += 1 # for ":"
|
|
110
|
+
l += len(s.value)
|
|
111
|
+
l += len(self.identifier) - 1 # account for "/" separating the segments
|
|
112
|
+
|
|
113
|
+
if l > 256:
|
|
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
|
+
)
|
|
120
|
+
return self
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@model_validator(mode="after")
|
|
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
|
+
)
|
|
132
|
+
|
|
133
|
+
# recommendation that A-Z, 0-9, -, and . should be used
|
|
134
|
+
if not_recommended_chars := set(re.sub(r'[A-Z0-9\.-]', '', self.issuer)):
|
|
135
|
+
self.add_validation_message(
|
|
136
|
+
source="PAC-ID",
|
|
137
|
+
type="Recommendation",
|
|
138
|
+
highlight_pattern=self.issuer,
|
|
139
|
+
highlight_sub=not_recommended_chars,
|
|
140
|
+
msg=f"Characters {' '.join(not_recommended_chars)} should not be used. Issuer SHOULD contain only the characters A-Z, 0-9, -, and . "
|
|
141
|
+
)
|
|
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)
|
|
175
|
+
|
|
176
|
+
|
|
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)
|