amati 0.1.0__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 +3 -2
- amati/_error_handler.py +48 -0
- amati/amati.py +156 -26
- 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.0.dist-info → amati-0.2.dist-info}/METADATA +72 -10
- amati-0.2.dist-info/RECORD +37 -0
- amati/references.py +0 -33
- amati-0.1.0.dist-info/RECORD +0 -37
- {amati-0.1.0.dist-info → amati-0.2.dist-info}/WHEEL +0 -0
- {amati-0.1.0.dist-info → amati-0.2.dist-info}/entry_points.txt +0 -0
- {amati-0.1.0.dist-info → amati-0.2.dist-info}/licenses/LICENSE +0 -0
amati/validators/oas311.py
CHANGED
@@ -23,23 +23,21 @@ from pydantic import (
|
|
23
23
|
ConfigDict,
|
24
24
|
Field,
|
25
25
|
RootModel,
|
26
|
-
ValidationError,
|
27
26
|
model_validator,
|
28
27
|
)
|
29
28
|
|
30
|
-
from amati import AmatiValueError
|
29
|
+
from amati import AmatiValueError
|
31
30
|
from amati import model_validators as mv
|
32
31
|
from amati.fields import (
|
33
32
|
SPDXURL,
|
34
33
|
URI,
|
35
|
-
HTTPStatusCode,
|
36
34
|
SPDXIdentifier,
|
37
35
|
)
|
38
36
|
from amati.fields.commonmark import CommonMark
|
39
37
|
from amati.fields.json import JSON
|
40
38
|
from amati.fields.oas import OpenAPI
|
41
39
|
from amati.fields.spdx_licences import VALID_LICENCES
|
42
|
-
from amati.logging import
|
40
|
+
from amati.logging import LogMixin
|
43
41
|
from amati.validators.generic import GenericObject, allow_extra_fields
|
44
42
|
from amati.validators.oas304 import (
|
45
43
|
CallbackObject,
|
@@ -53,6 +51,7 @@ from amati.validators.oas304 import (
|
|
53
51
|
PathsObject,
|
54
52
|
RequestBodyObject,
|
55
53
|
ResponseObject,
|
54
|
+
ResponsesObject,
|
56
55
|
)
|
57
56
|
from amati.validators.oas304 import SecuritySchemeObject as OAS30SecuritySchemeObject
|
58
57
|
from amati.validators.oas304 import (
|
@@ -84,10 +83,8 @@ class LicenceObject(GenericObject):
|
|
84
83
|
# What difference does Optional make here?
|
85
84
|
identifier: Optional[SPDXIdentifier] = None
|
86
85
|
url: Optional[URI] = None
|
87
|
-
|
88
|
-
|
89
|
-
url="https://spec.openapis.org/oas/v3.1.1.html#license-object",
|
90
|
-
section="License Object",
|
86
|
+
_reference_uri: ClassVar[str] = URI(
|
87
|
+
"https://spec.openapis.org/oas/v3.1.1.html#license-object"
|
91
88
|
)
|
92
89
|
|
93
90
|
_not_url_and_identifier = mv.only_one_of(["url", "identifier"])
|
@@ -109,11 +106,13 @@ class LicenceObject(GenericObject):
|
|
109
106
|
SPDXURL(self.url)
|
110
107
|
except AmatiValueError:
|
111
108
|
LogMixin.log(
|
112
|
-
|
113
|
-
|
114
|
-
type
|
115
|
-
|
116
|
-
|
109
|
+
{
|
110
|
+
"msg": f"{str(self.url)} is not a valid SPDX URL",
|
111
|
+
"type": "warning",
|
112
|
+
"loc": (self.__class__.__name__,),
|
113
|
+
"input": self.url,
|
114
|
+
"url": self._reference_uri,
|
115
|
+
}
|
117
116
|
)
|
118
117
|
|
119
118
|
# Both Identifier and URI, technically invalid, but should check if
|
@@ -124,11 +123,13 @@ class LicenceObject(GenericObject):
|
|
124
123
|
and str(self.url) not in VALID_LICENCES[self.identifier]
|
125
124
|
):
|
126
125
|
LogMixin.log(
|
127
|
-
|
128
|
-
|
129
|
-
type
|
130
|
-
|
131
|
-
|
126
|
+
{
|
127
|
+
"msg": f"{self.url} is not associated with the identifier {self.identifier}", # pylint: disable=line-too-long
|
128
|
+
"type": "warning",
|
129
|
+
"loc": (self.__class__.__name__,),
|
130
|
+
"input": self.model_dump_json(),
|
131
|
+
"url": self._reference_uri,
|
132
|
+
}
|
132
133
|
)
|
133
134
|
|
134
135
|
return self
|
@@ -149,10 +150,8 @@ class ReferenceObject(GenericObject):
|
|
149
150
|
ref: URI = Field(alias="$ref")
|
150
151
|
summary: Optional[str]
|
151
152
|
description: Optional[CommonMark]
|
152
|
-
|
153
|
-
|
154
|
-
url="https://spec.openapis.org/oas/v3.1.1.html#reference-object",
|
155
|
-
section="Reference Object",
|
153
|
+
_reference_uri: ClassVar[str] = (
|
154
|
+
"https://spec.openapis.org/oas/v3.1.1.html#reference-object"
|
156
155
|
)
|
157
156
|
|
158
157
|
|
@@ -169,10 +168,8 @@ class InfoObject(GenericObject):
|
|
169
168
|
contact: Optional[ContactObject] = None
|
170
169
|
license: Optional[LicenceObject] = None
|
171
170
|
version: str
|
172
|
-
|
173
|
-
|
174
|
-
url="https://spec.openapis.org/oas/3.1.1.html#info-object",
|
175
|
-
section="Info Object",
|
171
|
+
_reference_uri: ClassVar[str] = (
|
172
|
+
"https://spec.openapis.org/oas/3.1.1.html#info-object"
|
176
173
|
)
|
177
174
|
|
178
175
|
|
@@ -187,10 +184,8 @@ class DiscriminatorObject(GenericObject):
|
|
187
184
|
# properly.
|
188
185
|
propertyName: str
|
189
186
|
mapping: Optional[dict[str, str | URI]] = None
|
190
|
-
|
191
|
-
|
192
|
-
url="https://spec.openapis.org/oas/v3.1.1.html#discriminator-object",
|
193
|
-
section="Discriminator Object",
|
187
|
+
_reference_uri: ClassVar[str] = (
|
188
|
+
"https://spec.openapis.org/oas/v3.1.1.html#discriminator-object"
|
194
189
|
)
|
195
190
|
|
196
191
|
|
@@ -203,10 +198,8 @@ class ServerVariableObject(GenericObject):
|
|
203
198
|
enum: Optional[list[str]] = Field(None, min_length=1)
|
204
199
|
default: str = Field(min_length=1)
|
205
200
|
description: Optional[str | CommonMark] = None
|
206
|
-
|
207
|
-
|
208
|
-
url="https://spec.openapis.org/oas/v3.1.1.html#server-variable-object",
|
209
|
-
section="Server Variable Object",
|
201
|
+
_reference_uri: ClassVar[str] = (
|
202
|
+
"https://spec.openapis.org/oas/v3.1.1.html#server-variable-object"
|
210
203
|
)
|
211
204
|
|
212
205
|
@model_validator(mode="after")
|
@@ -222,11 +215,13 @@ class ServerVariableObject(GenericObject):
|
|
222
215
|
|
223
216
|
if self.default not in self.enum:
|
224
217
|
LogMixin.log(
|
225
|
-
|
226
|
-
|
227
|
-
type
|
228
|
-
|
229
|
-
|
218
|
+
{
|
219
|
+
"msg": f"The default value {self.default} is not in the enum list {self.enum}", # pylint: disable=line-too-long
|
220
|
+
"type": "value_error",
|
221
|
+
"loc": (self.__class__.__name__,),
|
222
|
+
"input": {"default": self.default, "enum": self.enum},
|
223
|
+
"url": self._reference_uri,
|
224
|
+
}
|
230
225
|
)
|
231
226
|
|
232
227
|
return self
|
@@ -249,10 +244,8 @@ class OperationObject(GenericObject):
|
|
249
244
|
security: Optional[list["SecurityRequirementObject"]] = None
|
250
245
|
servers: Optional[list[ServerObject]] = None
|
251
246
|
|
252
|
-
|
253
|
-
|
254
|
-
url="https://spec.openapis.org/oas/v3.1.1.html#operation-object",
|
255
|
-
section="Operation Object",
|
247
|
+
_reference_uri: ClassVar[str] = (
|
248
|
+
"https://spec.openapis.org/oas/v3.1.1.html#operation-object"
|
256
249
|
)
|
257
250
|
|
258
251
|
|
@@ -267,6 +260,7 @@ PARAMETER_STYLES: set[str] = {
|
|
267
260
|
}
|
268
261
|
|
269
262
|
|
263
|
+
@specification_extensions("x-")
|
270
264
|
class ParameterObject(GenericObject):
|
271
265
|
"""Validates the OpenAPI Specification parameter object - §4.8.11"""
|
272
266
|
|
@@ -283,6 +277,9 @@ class ParameterObject(GenericObject):
|
|
283
277
|
example: Optional[Any] = None
|
284
278
|
examples: Optional[dict[str, "ExampleObject | ReferenceObject"]] = None
|
285
279
|
content: Optional[dict[str, "MediaTypeObject"]] = None
|
280
|
+
_reference_uri: ClassVar[str] = (
|
281
|
+
"https://spec.openapis.org/oas/v3.1.1.html#parameter-object"
|
282
|
+
)
|
286
283
|
|
287
284
|
_in_valid = mv.if_then(
|
288
285
|
conditions={"in_": mv.UNKNOWN},
|
@@ -328,99 +325,10 @@ class MediaTypeObject(GenericObject):
|
|
328
325
|
example: Optional[Any] = None
|
329
326
|
examples: Optional[dict[str, ExampleObject | ReferenceObject]] = None
|
330
327
|
encoding: Optional["EncodingObject"] = None
|
331
|
-
|
332
|
-
|
333
|
-
url="https://spec.openapis.org/oas/v3.1.1.html#media-type-object",
|
334
|
-
section="Tag Object",
|
335
|
-
)
|
336
|
-
|
337
|
-
|
338
|
-
type _ResponsesObjectReturnType = dict[str, "ReferenceObject | ResponseObject"]
|
339
|
-
|
340
|
-
|
341
|
-
@specification_extensions("x-")
|
342
|
-
class ResponsesObject(GenericObject):
|
343
|
-
"""
|
344
|
-
Validates the OpenAPI Specification responses object - §4.8.16
|
345
|
-
"""
|
346
|
-
|
347
|
-
model_config = ConfigDict(
|
348
|
-
extra="allow",
|
349
|
-
)
|
350
|
-
|
351
|
-
default: Optional["ResponseObject | ReferenceObject"] = None
|
352
|
-
_reference: ClassVar[Reference] = Reference(
|
353
|
-
title=TITLE,
|
354
|
-
url="https://spec.openapis.org/oas/v3.1.1.html#responses-object",
|
355
|
-
section="Responses Object",
|
328
|
+
_reference_uri: ClassVar[str] = (
|
329
|
+
"https://spec.openapis.org/oas/v3.1.1.html#media-type-object"
|
356
330
|
)
|
357
331
|
|
358
|
-
@classmethod
|
359
|
-
def _choose_model(
|
360
|
-
cls, value: Any, field_name: str
|
361
|
-
) -> "ReferenceObject | ResponseObject":
|
362
|
-
"""
|
363
|
-
Choose the model to use for validation based on the type of value.
|
364
|
-
|
365
|
-
Args:
|
366
|
-
value: The value to validate.
|
367
|
-
|
368
|
-
Returns:
|
369
|
-
The model class to use for validation.
|
370
|
-
"""
|
371
|
-
|
372
|
-
message = f"{field_name} must be a ResponseObject or ReferenceObject, got {type(value)}" # pylint: disable=line-too-long
|
373
|
-
|
374
|
-
try:
|
375
|
-
return ResponseObject.model_validate(value)
|
376
|
-
except ValidationError:
|
377
|
-
try:
|
378
|
-
return ReferenceObject.model_validate(value)
|
379
|
-
except ValidationError as e:
|
380
|
-
raise ValueError(message, ResponsesObject._reference) from e
|
381
|
-
|
382
|
-
@model_validator(mode="before")
|
383
|
-
@classmethod
|
384
|
-
def validate_all_fields(cls, data: dict[str, Any]) -> _ResponsesObjectReturnType:
|
385
|
-
"""
|
386
|
-
Validates the responses object.
|
387
|
-
"""
|
388
|
-
|
389
|
-
validated_data: _ResponsesObjectReturnType = {}
|
390
|
-
|
391
|
-
for field_name, value in data.items():
|
392
|
-
|
393
|
-
# If the value is a specification extension, allow it
|
394
|
-
if field_name.startswith("x-"):
|
395
|
-
validated_data[field_name] = value
|
396
|
-
continue
|
397
|
-
|
398
|
-
# If the value is the fixed field, "default", allow it
|
399
|
-
if field_name == "default":
|
400
|
-
if isinstance(value, dict):
|
401
|
-
validated_data[field_name] = ResponsesObject._choose_model(
|
402
|
-
value, field_name
|
403
|
-
)
|
404
|
-
continue
|
405
|
-
|
406
|
-
# Otherwise, if the field appears like a valid HTTP status code or a range
|
407
|
-
if re.match(r"^[1-5]([0-9]{2}|XX)+$", str(field_name)):
|
408
|
-
|
409
|
-
# Double check and raise a value error if not
|
410
|
-
HTTPStatusCode(field_name)
|
411
|
-
|
412
|
-
# and validate as a ResponseObject or ReferenceObject
|
413
|
-
validated_data[field_name] = ResponsesObject._choose_model(
|
414
|
-
value, field_name
|
415
|
-
)
|
416
|
-
|
417
|
-
continue
|
418
|
-
|
419
|
-
# If the field is not a valid HTTP status code or "default"
|
420
|
-
raise ValueError(f"Invalid type for numeric field '{field_name}'")
|
421
|
-
|
422
|
-
return validated_data
|
423
|
-
|
424
332
|
|
425
333
|
class SchemaObject(GenericObject):
|
426
334
|
"""
|
@@ -452,10 +360,8 @@ class SchemaObject(GenericObject):
|
|
452
360
|
default=None, alias="$ref"
|
453
361
|
) # Reference to another schema
|
454
362
|
|
455
|
-
|
456
|
-
|
457
|
-
url="https://spec.openapis.org/oas/v3.1.1.html#schema-object",
|
458
|
-
section="Link Object",
|
363
|
+
_reference_uri: ClassVar[str] = (
|
364
|
+
"https://spec.openapis.org/oas/v3.1.1.html#schema-object"
|
459
365
|
)
|
460
366
|
|
461
367
|
@model_validator(mode="after")
|
@@ -491,11 +397,13 @@ class SchemaObject(GenericObject):
|
|
491
397
|
validator_cls(meta_schema).validate(schema_dict) # type: ignore
|
492
398
|
except JSONVSchemeValidationError as e:
|
493
399
|
LogMixin.log(
|
494
|
-
|
495
|
-
|
496
|
-
type
|
497
|
-
|
498
|
-
|
400
|
+
{
|
401
|
+
"msg": f"Invalid JSON Schema: {e.message}",
|
402
|
+
"type": "value_error",
|
403
|
+
"loc": (self.__class__.__name__,),
|
404
|
+
"input": schema_dict,
|
405
|
+
"url": self._reference_uri,
|
406
|
+
}
|
499
407
|
)
|
500
408
|
|
501
409
|
return self
|
@@ -527,10 +435,8 @@ class SecurityRequirementObject(RootModel[list[_Requirement] | _Requirement]):
|
|
527
435
|
# FIXME: The name must be a valid Security Scheme - need to use post-processing
|
528
436
|
# FIXME If the security scheme is of type "oauth2" or "openIdConnect", then the
|
529
437
|
# value must be a list
|
530
|
-
|
531
|
-
|
532
|
-
url="https://spec.openapis.org/oas/3.1.1.html#security-requirement-object",
|
533
|
-
section="Security Requirement Object",
|
438
|
+
_reference_uri: ClassVar[str] = (
|
439
|
+
"https://spec.openapis.org/oas/3.1.1.html#security-requirement-object"
|
534
440
|
)
|
535
441
|
|
536
442
|
|
@@ -542,7 +448,7 @@ class ComponentsObject(GenericObject):
|
|
542
448
|
|
543
449
|
schemas: Optional[dict[str, SchemaObject | ReferenceObject]] = None
|
544
450
|
responses: Optional[dict[str, ResponseObject | ReferenceObject]] = None
|
545
|
-
|
451
|
+
parameters: Optional[dict[str, ParameterObject | ReferenceObject]] = None
|
546
452
|
examples: Optional[dict[str, ExampleObject | ReferenceObject]] = None
|
547
453
|
requestBodies: Optional[dict[str, RequestBodyObject | ReferenceObject]] = None
|
548
454
|
headers: Optional[dict[str, HeaderObject | ReferenceObject]] = None
|
@@ -550,10 +456,8 @@ class ComponentsObject(GenericObject):
|
|
550
456
|
links: Optional[dict[str, LinkObject | ReferenceObject]] = None
|
551
457
|
callbacks: Optional[dict[str, CallbackObject | ReferenceObject]] = None
|
552
458
|
pathItems: Optional[dict[str, PathItemObject]] = None
|
553
|
-
|
554
|
-
|
555
|
-
url="https://spec.openapis.org/oas/v3.1.1.html#components-object",
|
556
|
-
section="Components Object",
|
459
|
+
_reference_uri: ClassVar[str] = (
|
460
|
+
"https://spec.openapis.org/oas/v3.1.1.html#components-object"
|
557
461
|
)
|
558
462
|
|
559
463
|
@model_validator(mode="before")
|
@@ -608,8 +512,6 @@ class OpenAPIObject(GenericObject):
|
|
608
512
|
security: Optional[list[SecurityRequirementObject]] = None
|
609
513
|
tags: Optional[list[TagObject]] = None
|
610
514
|
externalDocs: Optional[ExternalDocumentationObject] = None
|
611
|
-
|
612
|
-
|
613
|
-
url="https://spec.openapis.org/oas/3.1.1.html#openapi-object",
|
614
|
-
section="OpenAPI Object",
|
515
|
+
_reference_uri: ClassVar[str] = (
|
516
|
+
"https://spec.openapis.org/oas/3.1.1.html#openapi-object"
|
615
517
|
)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: amati
|
3
|
-
Version: 0.
|
4
|
-
Summary: Validates that a .yaml or .json file conforms to the OpenAPI Specifications 3.x.
|
3
|
+
Version: 0.2
|
4
|
+
Summary: Validates that a .yaml or .json file conforms to the OpenAPI Specifications 3.x.
|
5
5
|
Project-URL: Homepage, https://github.com/ben-alexander/amati
|
6
6
|
Project-URL: Issues, https://github.com/ben-alexander/amati/issues
|
7
7
|
Author-email: Ben <2551337+ben-alexander@users.noreply.github.com>
|
@@ -15,6 +15,7 @@ Classifier: Topic :: Software Development :: Testing :: Acceptance
|
|
15
15
|
Requires-Python: >=3.13
|
16
16
|
Requires-Dist: abnf>=2.3.1
|
17
17
|
Requires-Dist: idna>=3.10
|
18
|
+
Requires-Dist: jinja2>=3.1.6
|
18
19
|
Requires-Dist: jsonpickle>=4.1.1
|
19
20
|
Requires-Dist: jsonschema>=4.24.0
|
20
21
|
Requires-Dist: pydantic>=2.11.5
|
@@ -23,26 +24,64 @@ Description-Content-Type: text/markdown
|
|
23
24
|
|
24
25
|
# amati
|
25
26
|
|
26
|
-
|
27
|
-
|
28
|
-
Currently a proof of concept.
|
27
|
+
amati is designed to validate that a file conforms to the [OpenAPI Specification v3.x](https://spec.openapis.org/) (OAS).
|
29
28
|
|
30
29
|
## Name
|
31
30
|
|
32
31
|
amati means to observe in Malay, especially with attention to detail. It's also one of the plurals of beloved or favourite in Italian.
|
33
32
|
|
33
|
+
## Usage
|
34
|
+
|
35
|
+
```sh
|
36
|
+
python amati/amati.py --help
|
37
|
+
usage: amati [-h] [-s SPEC] [-cc] [-d DISCOVER] [-l] [-hr]
|
38
|
+
|
39
|
+
Tests whether a OpenAPI specification is valid. Will look an openapi.json or openapi.yaml file in the directory that
|
40
|
+
amati is called from. If --discover is set will search the directory tree. If the specification does not follow the
|
41
|
+
naming recommendation the --spec switch should be used. Creates a file <filename>.errors.json alongside the original
|
42
|
+
specification containing a JSON representation of all the errors.
|
43
|
+
|
44
|
+
options:
|
45
|
+
-h, --help show this help message and exit
|
46
|
+
-s, --spec SPEC The specification to be parsed
|
47
|
+
-cc, --consistency-check
|
48
|
+
Runs a consistency check between the input specification and the parsed specification
|
49
|
+
-d, --discover DISCOVER
|
50
|
+
Searches the specified directory tree for openapi.yaml or openapi.json.
|
51
|
+
-l, --local Store errors local to the caller in a file called <file-name>.errors.json; a .amati/ directory
|
52
|
+
will be created.
|
53
|
+
-hr, --html-report Creates an HTML report of the errors, called <file-name>.errors.html, alongside the original
|
54
|
+
file or in a .amati/ directory if the --local switch is used
|
55
|
+
```
|
56
|
+
|
57
|
+
A Dockerfile is available on [DockerHub](https://hub.docker.com/r/benale/amati/tags)
|
58
|
+
|
59
|
+
To run against a specific specification the location of the specification needs to be mounted in the container.
|
60
|
+
|
61
|
+
```sh
|
62
|
+
docker run -v "<path-to-mount>:/<mount-name> amati <options>
|
63
|
+
```
|
64
|
+
|
65
|
+
e.g.
|
66
|
+
|
67
|
+
```sh
|
68
|
+
docker run -v /Users/myuser/myrepo:/data amati --spec data/myspec.yaml --hr
|
69
|
+
```
|
70
|
+
|
34
71
|
## Architecture
|
35
72
|
|
36
73
|
This uses Pydantic, especially the validation, and Typing to construct the entire OAS as a single data type. Passing a dictionary to the top-level data type runs all the validation in the Pydantic models constructing a single set of inherited classes and datatypes that validate that the API specification is accurate.
|
37
74
|
|
38
75
|
Where the specification conforms, but relies on implementation-defined behavior (e.g. [data type formats](https://spec.openapis.org/oas/v3.1.1.html#data-type-format)), a warning will be raised.
|
39
76
|
|
40
|
-
##
|
77
|
+
## Contributing
|
78
|
+
|
79
|
+
### Requirements
|
41
80
|
|
42
81
|
* The latest version of [uv](https://docs.astral.sh/uv/)
|
43
82
|
* [git 2.49+](https://git-scm.com/downloads/linux)
|
44
83
|
|
45
|
-
|
84
|
+
### Testing and formatting
|
46
85
|
|
47
86
|
This project uses:
|
48
87
|
|
@@ -54,7 +93,7 @@ This project uses:
|
|
54
93
|
* [Black](https://black.readthedocs.io/en/stable/index.html) for automated formatting
|
55
94
|
* [isort](https://pycqa.github.io/isort/) for import sorting
|
56
95
|
|
57
|
-
It's expected that there are no errors
|
96
|
+
It's expected that there are no errors and 100% of the code is reached and executed. The strategy for test coverage is based on parsing test specifications and not unit tests.
|
58
97
|
|
59
98
|
amati runs tests on external specifications, detailed in `tests/data/.amati.tests.yaml`. To be able to run these tests the appropriate GitHub repos need to be local. Specific revisions of the repos can be downloaded by running
|
60
99
|
|
@@ -62,13 +101,15 @@ amati runs tests on external specifications, detailed in `tests/data/.amati.test
|
|
62
101
|
python scripts/tests/setup_test_specs.py
|
63
102
|
```
|
64
103
|
|
65
|
-
To run everything, from linting, type checking to downloading test specs run:
|
104
|
+
To run everything, from linting, type checking to downloading test specs and building and testing the Docker image run:
|
66
105
|
|
67
106
|
```sh
|
68
107
|
sh bin/checks.sh
|
69
108
|
```
|
70
109
|
|
71
|
-
|
110
|
+
You will need to have Docker installed.
|
111
|
+
|
112
|
+
### Building
|
72
113
|
|
73
114
|
The project uses a [`pyproject.toml` file](https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#writing-pyproject-toml) to determine what to build.
|
74
115
|
|
@@ -80,6 +121,27 @@ uv venv
|
|
80
121
|
uv sync
|
81
122
|
```
|
82
123
|
|
124
|
+
### Docker
|
125
|
+
|
126
|
+
A development Docker image is provided, `Dockerfile.dev`, to build:
|
127
|
+
|
128
|
+
```sh
|
129
|
+
docker build -t amati -f Dockerfile .
|
130
|
+
```
|
131
|
+
|
132
|
+
and to run against a specific specification the location of the specification needs to be mounted in the container.
|
133
|
+
|
134
|
+
```sh
|
135
|
+
docker run -v "<path-to-mount>:/<mount-name> amati <options>
|
136
|
+
```
|
137
|
+
|
138
|
+
This can be tested against a provided specification, from the root directory
|
139
|
+
|
140
|
+
```sh
|
141
|
+
docker run --detach -v "$(pwd):/data" amati
|
142
|
+
```
|
143
|
+
|
144
|
+
|
83
145
|
### Data
|
84
146
|
|
85
147
|
There are some scripts to create the data needed by the project, for example, all the possible licences. If the data needs to be refreshed this can be done by running the contents of `/scripts/data`.
|
@@ -0,0 +1,37 @@
|
|
1
|
+
amati/__init__.py,sha256=bsPoaYcFmejsqQN6eDxcuGKXg8ZXgk1NoD6Hujwhkkw,428
|
2
|
+
amati/_error_handler.py,sha256=s_Nhnq8hz7xQP_yeCCJ8Hwzs5xyrxlisd1XFYYECtSY,1244
|
3
|
+
amati/_resolve_forward_references.py,sha256=iOibCv_XuIUpe7qbRBtPzd7H5rvL-aldUzlXV_9FJaw,6511
|
4
|
+
amati/amati.py,sha256=sgWQrlvvusK8EgdcsYkCZWUNiv0TAHV8jRivUCBByQA,7698
|
5
|
+
amati/exceptions.py,sha256=X_RDBVjcFn_gXPFkhKuREmm5nDCdGs4rBy6sZsXBJDo,609
|
6
|
+
amati/file_handler.py,sha256=h95t7TDxDA_qr4rIe2bddCjh2Xw5Ht27yLK7zCQzT68,5085
|
7
|
+
amati/logging.py,sha256=X37gLui5Y94X1mlJIzkt3fQDwPcHlZiP3QBnM5Ipric,1339
|
8
|
+
amati/model_validators.py,sha256=zKK9G-xaERc-e9AmWBLzCIGKkmpi0nMMx94N_nd8fs8,14979
|
9
|
+
amati/data/http-status-codes.json,sha256=xEGBlE7eCXaQFG2gNSeK1aCs7A8XxOe6JcXwAKxwWBU,10903
|
10
|
+
amati/data/iso9110.json,sha256=YLv6V8dPrgagjAnHUWKMmade5xboV_eJB9smnUfwgDY,1692
|
11
|
+
amati/data/media-types.json,sha256=tSRZTUqIQeiW5cmw5aDAWNqXLGcvX4gwwEkDL5H6lDg,47108
|
12
|
+
amati/data/schemes.json,sha256=47a16eEatxahMMAgSQXRiFF7FotiETKTkQcleqF5m6w,10252
|
13
|
+
amati/data/spdx-licences.json,sha256=s9DHaYn4PSLVAEKE6wHEu_Rs-VI73WmsvcEgXSU6TTk,255373
|
14
|
+
amati/data/tlds.json,sha256=wDio6l8toi54-cano3EUuH3zGEoBvivagKjgbpsCdYo,19235
|
15
|
+
amati/fields/__init__.py,sha256=YPFOs0RZu7JlVuMEAkp6w9mNa1zgIZ6-0Mh-2v2tm6g,565
|
16
|
+
amati/fields/_custom_types.py,sha256=zHQdzyntVEGbLSblOOdKNzpZTCkmpUZ4Ji6WS6TkcPc,2513
|
17
|
+
amati/fields/commonmark.py,sha256=ssRc_338xd76y0MXR9U5OMkFE1ARCy-CWVOMY7AB8SU,220
|
18
|
+
amati/fields/email.py,sha256=1jkRVT_uUrDS9Y_AilvWMm776rMYDPiqXM3nq8VZqGY,559
|
19
|
+
amati/fields/http_status_codes.py,sha256=PaoebUSul9ZPwmyM0XIrrQ_4dxV4U0NURzXtbQlQVMg,3370
|
20
|
+
amati/fields/iso9110.py,sha256=MCfPOC0vmRI0VhM2WmzQyFurHA9kpKR4_fBL2rzYqvw,1855
|
21
|
+
amati/fields/json.py,sha256=TsNMW5gcCc3tpjDd0Sew_8okzDQu6S_FLKjGGvoRVos,304
|
22
|
+
amati/fields/media.py,sha256=ZFgNHDr-kGzGToOH0RkV4xhuMW-KrVdYlU6_8-G1EHk,3132
|
23
|
+
amati/fields/oas.py,sha256=oRvruhRmY9VQtlqiPaRvGKeeOgjYE-JG5aUZYZay6Kg,1733
|
24
|
+
amati/fields/spdx_licences.py,sha256=0iXZYzdzElvYmnTrtJM07IODENMP7qd21knZ5eH-7ws,2794
|
25
|
+
amati/fields/uri.py,sha256=iLAStugKYt1md4vP9oYrrsmME2d7XF9vSnLb6q_D5EA,11967
|
26
|
+
amati/grammars/oas.py,sha256=vTMnJIxeiTC3gJATOo0RRZDlRsQOWkY6jG1VRzazLjM,1666
|
27
|
+
amati/grammars/rfc6901.py,sha256=ChpKnoPDTsaNMvJT_UCpY8xwbl9zWZcD36MjRsD8IpQ,656
|
28
|
+
amati/grammars/rfc7159.py,sha256=eksX1m7WRzQ9iGeANG1BgmsXbAhV6gQVRkPpLqPjq6U,2218
|
29
|
+
amati/validators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
30
|
+
amati/validators/generic.py,sha256=AYIHIuEHpf1p-lCynBp-YGaayIZOylzNgGi533ykVbc,3983
|
31
|
+
amati/validators/oas304.py,sha256=84sAc37azkgrOt29scjRtYtJGypnFZd5UkV9VGLsX8c,32472
|
32
|
+
amati/validators/oas311.py,sha256=FVMOjNXWlZhq6fgah0NGGPlgdcSZFgVxVbEwWnk4Sn4,17712
|
33
|
+
amati-0.2.dist-info/METADATA,sha256=pDJg9ueeR6-_otHva88kNPxiEy3e-AAMIRvxsc4RFhg,5916
|
34
|
+
amati-0.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
35
|
+
amati-0.2.dist-info/entry_points.txt,sha256=sacBb6g0f0ZJtNjNYx93_Xe4y5xzawvklCFVXup9ru0,37
|
36
|
+
amati-0.2.dist-info/licenses/LICENSE,sha256=WAA01ZXeNs1bwpNWKR6aVucjtYjYm_iQIUYkCAENjqM,1070
|
37
|
+
amati-0.2.dist-info/RECORD,,
|
amati/references.py
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Represents a reference, declared here to not put in __init__.
|
3
|
-
"""
|
4
|
-
|
5
|
-
from dataclasses import dataclass
|
6
|
-
from typing import Optional, Sequence
|
7
|
-
|
8
|
-
|
9
|
-
class AmatiReferenceException(Exception):
|
10
|
-
message: str = "Cannot construct empty references"
|
11
|
-
|
12
|
-
|
13
|
-
@dataclass
|
14
|
-
class Reference:
|
15
|
-
"""
|
16
|
-
Attributes:
|
17
|
-
title : Title of the referenced content
|
18
|
-
section : Section of the referenced content
|
19
|
-
url : URL where the referenced content can be found
|
20
|
-
"""
|
21
|
-
|
22
|
-
title: Optional[str] = None
|
23
|
-
section: Optional[str] = None
|
24
|
-
url: Optional[str] = None
|
25
|
-
|
26
|
-
def __post_init__(self):
|
27
|
-
|
28
|
-
if not self.title and not self.section and not self.url:
|
29
|
-
raise AmatiReferenceException
|
30
|
-
|
31
|
-
|
32
|
-
type ReferenceArray = Sequence[Reference]
|
33
|
-
type References = Reference | ReferenceArray
|
amati-0.1.0.dist-info/RECORD
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
amati/__init__.py,sha256=C-4SGpCtZFCxS_PEPaGNFiKJu-gW5YxMSauYA-LxmzQ,449
|
2
|
-
amati/_resolve_forward_references.py,sha256=iOibCv_XuIUpe7qbRBtPzd7H5rvL-aldUzlXV_9FJaw,6511
|
3
|
-
amati/amati.py,sha256=fl6RL5_RWtoSpxTkEcDsgeejSh8HynxPuKvU4F5EwXY,3775
|
4
|
-
amati/exceptions.py,sha256=Rm0njRFjyYTa6KcrbfwDXOz8IqZraCoXo82sDKseQMY,645
|
5
|
-
amati/file_handler.py,sha256=h95t7TDxDA_qr4rIe2bddCjh2Xw5Ht27yLK7zCQzT68,5085
|
6
|
-
amati/logging.py,sha256=AZXQv4jXKy6NAxths7DbpUlOWAhkh3XZ6imBtYjkHxc,1323
|
7
|
-
amati/model_validators.py,sha256=omKagnWKQkhMc7AF5JWYZaLGNwYe38jVlCGgTO71zsY,14645
|
8
|
-
amati/references.py,sha256=nGMUKIMGqZMpZcdQ7r_gxkcGARh9RULNhVBMej-ZiJE,788
|
9
|
-
amati/data/http-status-codes.json,sha256=xEGBlE7eCXaQFG2gNSeK1aCs7A8XxOe6JcXwAKxwWBU,10903
|
10
|
-
amati/data/iso9110.json,sha256=YLv6V8dPrgagjAnHUWKMmade5xboV_eJB9smnUfwgDY,1692
|
11
|
-
amati/data/media-types.json,sha256=tSRZTUqIQeiW5cmw5aDAWNqXLGcvX4gwwEkDL5H6lDg,47108
|
12
|
-
amati/data/schemes.json,sha256=47a16eEatxahMMAgSQXRiFF7FotiETKTkQcleqF5m6w,10252
|
13
|
-
amati/data/spdx-licences.json,sha256=s9DHaYn4PSLVAEKE6wHEu_Rs-VI73WmsvcEgXSU6TTk,255373
|
14
|
-
amati/data/tlds.json,sha256=wDio6l8toi54-cano3EUuH3zGEoBvivagKjgbpsCdYo,19235
|
15
|
-
amati/fields/__init__.py,sha256=YPFOs0RZu7JlVuMEAkp6w9mNa1zgIZ6-0Mh-2v2tm6g,565
|
16
|
-
amati/fields/_custom_types.py,sha256=zHQdzyntVEGbLSblOOdKNzpZTCkmpUZ4Ji6WS6TkcPc,2513
|
17
|
-
amati/fields/commonmark.py,sha256=ssRc_338xd76y0MXR9U5OMkFE1ARCy-CWVOMY7AB8SU,220
|
18
|
-
amati/fields/email.py,sha256=335vaJJmpASmScYCliCyxGElPvLt3bflNuAL6p-KGCg,661
|
19
|
-
amati/fields/http_status_codes.py,sha256=bxhZIAlHyf7gYHIhxub-u2xb8VbXsqk96Oe3SnZ8RPM,3466
|
20
|
-
amati/fields/iso9110.py,sha256=0nCJqJ3KXRnyUvJGyiMZfLd5ngixZlZpcYqsORAPhjc,1971
|
21
|
-
amati/fields/json.py,sha256=TsNMW5gcCc3tpjDd0Sew_8okzDQu6S_FLKjGGvoRVos,304
|
22
|
-
amati/fields/media.py,sha256=rst8okknJCQiQlDYYR_DIckd65VYHAK4A98Fl8C26z0,3268
|
23
|
-
amati/fields/oas.py,sha256=J8QegOIiGcqGziJEaYxalsGZAMjWOt85R30k4oQ7h6w,1980
|
24
|
-
amati/fields/spdx_licences.py,sha256=vPBkQsxC2z4uyeS2iGrgMTXJKHSn3VLBo9LR1uIyyh0,2866
|
25
|
-
amati/fields/uri.py,sha256=FryHcn3D74-wA0GK9qATMju1KtWmfFb6K-Cy6jfXDL4,12390
|
26
|
-
amati/grammars/oas.py,sha256=vTMnJIxeiTC3gJATOo0RRZDlRsQOWkY6jG1VRzazLjM,1666
|
27
|
-
amati/grammars/rfc6901.py,sha256=ChpKnoPDTsaNMvJT_UCpY8xwbl9zWZcD36MjRsD8IpQ,656
|
28
|
-
amati/grammars/rfc7159.py,sha256=eksX1m7WRzQ9iGeANG1BgmsXbAhV6gQVRkPpLqPjq6U,2218
|
29
|
-
amati/validators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
30
|
-
amati/validators/generic.py,sha256=NehHehVimlsVHDgStRWYgn0MZyR-i78yIPP_lw6L1bU,3736
|
31
|
-
amati/validators/oas304.py,sha256=5QrfvBRKNZnqGZZgG7VbdIkbvklW_1M9qHeBh55D-d4,33885
|
32
|
-
amati/validators/oas311.py,sha256=F6ejYYtSrSPFQm2MneuzbtTtlw7hv7AkfrYHDOlpWDs,20776
|
33
|
-
amati-0.1.0.dist-info/METADATA,sha256=5Up6rsMw3Vev44sHuGovORpjYrpKKZ0-fn3Fx0WTRbE,3695
|
34
|
-
amati-0.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
35
|
-
amati-0.1.0.dist-info/entry_points.txt,sha256=sacBb6g0f0ZJtNjNYx93_Xe4y5xzawvklCFVXup9ru0,37
|
36
|
-
amati-0.1.0.dist-info/licenses/LICENSE,sha256=WAA01ZXeNs1bwpNWKR6aVucjtYjYm_iQIUYkCAENjqM,1070
|
37
|
-
amati-0.1.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|