amati 0.1.1__py3-none-any.whl → 0.2__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.
- amati/__init__.py +0 -1
- amati/_error_handler.py +48 -0
- amati/amati.py +86 -20
- amati/exceptions.py +2 -4
- amati/fields/email.py +3 -7
- amati/fields/http_status_codes.py +4 -5
- amati/fields/iso9110.py +4 -5
- amati/fields/media.py +3 -7
- amati/fields/oas.py +6 -12
- amati/fields/spdx_licences.py +4 -7
- amati/fields/uri.py +3 -11
- amati/logging.py +8 -8
- amati/model_validators.py +42 -33
- amati/validators/generic.py +18 -13
- amati/validators/oas304.py +93 -130
- amati/validators/oas311.py +58 -156
- {amati-0.1.1.dist-info → amati-0.2.dist-info}/METADATA +71 -9
- amati-0.2.dist-info/RECORD +37 -0
- amati/references.py +0 -33
- amati-0.1.1.dist-info/RECORD +0 -37
- {amati-0.1.1.dist-info → amati-0.2.dist-info}/WHEEL +0 -0
- {amati-0.1.1.dist-info → amati-0.2.dist-info}/entry_points.txt +0 -0
- {amati-0.1.1.dist-info → amati-0.2.dist-info}/licenses/LICENSE +0 -0
amati/model_validators.py
CHANGED
@@ -10,7 +10,7 @@ from pydantic._internal._decorators import (
|
|
10
10
|
PydanticDescriptorProxy,
|
11
11
|
)
|
12
12
|
|
13
|
-
from amati.logging import
|
13
|
+
from amati.logging import LogMixin
|
14
14
|
from amati.validators.generic import GenericObject
|
15
15
|
|
16
16
|
|
@@ -107,14 +107,13 @@ def at_least_one_of(
|
|
107
107
|
The validator that ensures at least one public field is non-empty.
|
108
108
|
|
109
109
|
Example:
|
110
|
-
>>> from amati import Reference
|
111
110
|
>>> LogMixin.logs = []
|
112
111
|
>>>
|
113
112
|
>>> class User(GenericObject):
|
114
113
|
... name: str = ""
|
115
114
|
... email: str = None
|
116
115
|
... _at_least_one_of = at_least_one_of()
|
117
|
-
...
|
116
|
+
... _reference_uri = "https://example.com"
|
118
117
|
...
|
119
118
|
>>> user = User()
|
120
119
|
>>> assert len(LogMixin.logs) == 1
|
@@ -125,7 +124,7 @@ def at_least_one_of(
|
|
125
124
|
... email: str = None
|
126
125
|
... age: int = None
|
127
126
|
... _at_least_one_of = at_least_one_of(fields=["name", "email"])
|
128
|
-
...
|
127
|
+
... _reference_uri = "https://example.com"
|
129
128
|
...
|
130
129
|
>>>
|
131
130
|
>>> user = User(name="John") # Works fine
|
@@ -159,11 +158,13 @@ def at_least_one_of(
|
|
159
158
|
|
160
159
|
msg = f"{public_fields} do not have values, expected at least one."
|
161
160
|
LogMixin.log(
|
162
|
-
|
163
|
-
|
164
|
-
type
|
165
|
-
|
166
|
-
|
161
|
+
{
|
162
|
+
"msg": msg,
|
163
|
+
"type": "value_error",
|
164
|
+
"loc": (self.__class__.__name__,),
|
165
|
+
"input": candidates,
|
166
|
+
"url": self._reference_uri, # pylint: disable=protected-access # type: ignore
|
167
|
+
}
|
167
168
|
)
|
168
169
|
|
169
170
|
return self
|
@@ -173,6 +174,7 @@ def at_least_one_of(
|
|
173
174
|
|
174
175
|
def only_one_of(
|
175
176
|
fields: Optional[Sequence[str]] = None,
|
177
|
+
type_: Optional[str] = "value_error",
|
176
178
|
) -> PydanticDescriptorProxy[ModelValidatorDecoratorInfo]:
|
177
179
|
"""Factory that adds validation to ensure one public field is non-empty.
|
178
180
|
|
@@ -189,14 +191,13 @@ def only_one_of(
|
|
189
191
|
The validator that ensures at one public field is non-empty.
|
190
192
|
|
191
193
|
Example:
|
192
|
-
>>> from amati import Reference
|
193
194
|
>>> LogMixin.logs = []
|
194
195
|
>>>
|
195
196
|
>>> class User(GenericObject):
|
196
197
|
... email: str = ""
|
197
198
|
... name: str = ""
|
198
199
|
... _only_one_of = only_one_of()
|
199
|
-
...
|
200
|
+
... _reference_uri = "https://example.com"
|
200
201
|
...
|
201
202
|
>>> user = User(email="test@example.com") # Works fine
|
202
203
|
>>> user = User(name="123-456-7890") # Works fine
|
@@ -210,7 +211,7 @@ def only_one_of(
|
|
210
211
|
... email: str = ""
|
211
212
|
... age: int = None
|
212
213
|
... _only_one_of = only_one_of(["name", "email"])
|
213
|
-
...
|
214
|
+
... _reference_uri = "https://example.com"
|
214
215
|
...
|
215
216
|
>>> user = User(name="Bob") # Works fine
|
216
217
|
>>> user = User(email="test@example.com") # Works fine
|
@@ -242,14 +243,20 @@ def only_one_of(
|
|
242
243
|
truthy.append(name)
|
243
244
|
|
244
245
|
if len(truthy) != 1:
|
245
|
-
|
246
|
+
if truthy:
|
247
|
+
field_string = ", ".join(truthy)
|
248
|
+
else:
|
249
|
+
field_string = "none"
|
250
|
+
msg = f"Expected at most one field to have a value, {field_string} did"
|
246
251
|
|
247
252
|
LogMixin.log(
|
248
|
-
|
249
|
-
|
250
|
-
type
|
251
|
-
|
252
|
-
|
253
|
+
{
|
254
|
+
"msg": msg,
|
255
|
+
"type": type_ or "value_error",
|
256
|
+
"loc": (self.__class__.__name__,),
|
257
|
+
"input": candidates,
|
258
|
+
"url": self._reference_uri, # pylint: disable=protected-access # type: ignore
|
259
|
+
}
|
253
260
|
)
|
254
261
|
|
255
262
|
return self
|
@@ -275,14 +282,13 @@ def all_of(
|
|
275
282
|
The validator that ensures at most one public field is non-empty.
|
276
283
|
|
277
284
|
Example:
|
278
|
-
>>> from amati import Reference
|
279
285
|
>>> LogMixin.logs = []
|
280
286
|
>>>
|
281
287
|
>>> class User(GenericObject):
|
282
288
|
... email: str = ""
|
283
289
|
... name: str = ""
|
284
290
|
... _all_of = all_of()
|
285
|
-
...
|
291
|
+
... _reference_uri = "https://example.com"
|
286
292
|
...
|
287
293
|
>>> user = User(email="a@b.com", name="123") # Works fine
|
288
294
|
>>> assert not LogMixin.logs
|
@@ -296,7 +302,7 @@ def all_of(
|
|
296
302
|
... email: str = ""
|
297
303
|
... age: int = None
|
298
304
|
... _all_of = all_of(["name", "email"])
|
299
|
-
...
|
305
|
+
... _reference_uri = "https://example.com"
|
300
306
|
...
|
301
307
|
>>> LogMixin.logs = []
|
302
308
|
>>> user = User(name="Bob", email="a@b.com") # Works fine
|
@@ -334,11 +340,13 @@ def all_of(
|
|
334
340
|
msg = f"Expected at all fields to have a value, {", ".join(falsy)} did not"
|
335
341
|
|
336
342
|
LogMixin.log(
|
337
|
-
|
338
|
-
|
339
|
-
type
|
340
|
-
|
341
|
-
|
343
|
+
{
|
344
|
+
"msg": msg,
|
345
|
+
"type": "value_error",
|
346
|
+
"loc": (self.__class__.__name__,),
|
347
|
+
"input": candidates,
|
348
|
+
"url": self._reference_uri, # pylint: disable=protected-access # type: ignore
|
349
|
+
}
|
342
350
|
)
|
343
351
|
|
344
352
|
return self
|
@@ -370,7 +378,6 @@ def if_then(
|
|
370
378
|
ValueError: If a condition and consequence are not present
|
371
379
|
|
372
380
|
Example:
|
373
|
-
>>> from amati import Reference
|
374
381
|
>>> LogMixin.logs = []
|
375
382
|
>>>
|
376
383
|
>>> class User(GenericObject):
|
@@ -380,7 +387,7 @@ def if_then(
|
|
380
387
|
... conditions={"role": "admin"},
|
381
388
|
... consequences={"can_edit": True}
|
382
389
|
... )
|
383
|
-
...
|
390
|
+
... _reference_uri = "https://example.com"
|
384
391
|
...
|
385
392
|
>>> user = User(role="admin", can_edit=True) # Works fine
|
386
393
|
>>> assert not LogMixin.logs
|
@@ -425,12 +432,14 @@ def if_then(
|
|
425
432
|
continue
|
426
433
|
|
427
434
|
LogMixin.log(
|
428
|
-
|
429
|
-
|
435
|
+
{
|
436
|
+
"msg": f"Expected {field} to be {"in " if iterable else ""}"
|
430
437
|
f"{value} found {actual}",
|
431
|
-
type
|
432
|
-
|
433
|
-
|
438
|
+
"type": "value_error",
|
439
|
+
"loc": (self.__class__.__name__,),
|
440
|
+
"input": candidates,
|
441
|
+
"url": self._reference_uri, # pylint: disable=protected-access # type: ignore
|
442
|
+
}
|
434
443
|
)
|
435
444
|
|
436
445
|
return self
|
amati/validators/generic.py
CHANGED
@@ -20,17 +20,16 @@ from typing import (
|
|
20
20
|
from pydantic import BaseModel, ConfigDict, PrivateAttr
|
21
21
|
from pydantic_core._pydantic_core import PydanticUndefined
|
22
22
|
|
23
|
-
from amati import
|
24
|
-
from amati.logging import Log, LogMixin
|
23
|
+
from amati.logging import LogMixin
|
25
24
|
|
26
25
|
|
27
|
-
class GenericObject(
|
26
|
+
class GenericObject(BaseModel):
|
28
27
|
"""
|
29
28
|
A generic model to overwrite provide extra functionality
|
30
29
|
to pydantic.BaseModel.
|
31
30
|
"""
|
32
31
|
|
33
|
-
|
32
|
+
_reference_uri: ClassVar[str] = PrivateAttr()
|
34
33
|
_extra_field_pattern: Optional[Pattern[str]] = PrivateAttr()
|
35
34
|
|
36
35
|
def __init__(self, **data: Any) -> None:
|
@@ -48,11 +47,14 @@ class GenericObject(LogMixin, BaseModel):
|
|
48
47
|
and field not in self.get_field_aliases()
|
49
48
|
):
|
50
49
|
message = f"{field} is not a valid field for {self.__repr_name__()}."
|
51
|
-
|
52
|
-
|
53
|
-
message
|
54
|
-
type
|
55
|
-
|
50
|
+
LogMixin.log(
|
51
|
+
{
|
52
|
+
"msg": message,
|
53
|
+
"type": "value_error",
|
54
|
+
"loc": (self.__repr_name__(),),
|
55
|
+
"input": field,
|
56
|
+
"url": self._reference_uri,
|
57
|
+
}
|
56
58
|
)
|
57
59
|
|
58
60
|
def model_post_init(self, __context: Any) -> None:
|
@@ -78,10 +80,13 @@ class GenericObject(LogMixin, BaseModel):
|
|
78
80
|
for field in excess_fields:
|
79
81
|
message = f"{field} is not a valid field for {self.__repr_name__()}."
|
80
82
|
LogMixin.log(
|
81
|
-
|
82
|
-
message
|
83
|
-
type
|
84
|
-
|
83
|
+
{
|
84
|
+
"msg": message,
|
85
|
+
"type": "value_error",
|
86
|
+
"loc": (self.__repr_name__(),),
|
87
|
+
"input": field,
|
88
|
+
"url": self._reference_uri,
|
89
|
+
}
|
85
90
|
)
|
86
91
|
|
87
92
|
def get_field_aliases(self) -> list[str]:
|