amati 0.2.29__tar.gz → 0.3.2__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.
- {amati-0.2.29 → amati-0.3.2}/.github/workflows/data-refresh.yaml +2 -2
- {amati-0.2.29 → amati-0.3.2}/PKG-INFO +21 -27
- {amati-0.2.29 → amati-0.3.2}/README.md +18 -24
- {amati-0.2.29 → amati-0.3.2}/amati/_data/files/spdx-licences.json +33 -0
- {amati-0.2.29 → amati-0.3.2}/amati/_logging.py +4 -5
- {amati-0.2.29 → amati-0.3.2}/amati/amati.py +54 -113
- {amati-0.2.29 → amati-0.3.2}/bin/checks.sh +1 -1
- {amati-0.2.29 → amati-0.3.2}/pyproject.toml +3 -3
- amati-0.3.2/tests/data/invalid-openapi.yaml +26 -0
- amati-0.3.2/tests/test_amati.py +87 -0
- {amati-0.2.29 → amati-0.3.2}/uv.lock +26 -26
- amati-0.2.29/tests/test_amati.py +0 -51
- {amati-0.2.29 → amati-0.3.2}/.dockerignore +0 -0
- {amati-0.2.29 → amati-0.3.2}/.github/actions/setup/action.yaml +0 -0
- {amati-0.2.29 → amati-0.3.2}/.github/dependabot.yml +0 -0
- {amati-0.2.29 → amati-0.3.2}/.github/workflows/checks.yaml +0 -0
- {amati-0.2.29 → amati-0.3.2}/.github/workflows/codeql.yml +0 -0
- {amati-0.2.29 → amati-0.3.2}/.github/workflows/coverage.yaml +0 -0
- {amati-0.2.29 → amati-0.3.2}/.github/workflows/dependency-review.yml +0 -0
- {amati-0.2.29 → amati-0.3.2}/.github/workflows/publish.yaml +0 -0
- {amati-0.2.29 → amati-0.3.2}/.github/workflows/scorecards.yml +0 -0
- {amati-0.2.29 → amati-0.3.2}/.github/workflows/tag-and-create-release.yaml +0 -0
- {amati-0.2.29 → amati-0.3.2}/.gitignore +0 -0
- {amati-0.2.29 → amati-0.3.2}/.pre-commit-config.yaml +0 -0
- {amati-0.2.29 → amati-0.3.2}/.python-version +0 -0
- {amati-0.2.29 → amati-0.3.2}/Dockerfile +0 -0
- {amati-0.2.29 → amati-0.3.2}/LICENSE +0 -0
- {amati-0.2.29 → amati-0.3.2}/SECURITY.md +0 -0
- {amati-0.2.29 → amati-0.3.2}/TEMPLATE.html +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/__init__.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/_data/files/http-status-codes.json +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/_data/files/iso9110.json +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/_data/files/media-types.json +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/_data/files/schemes.json +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/_data/files/tlds.json +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/_data/http_status_code.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/_data/iso9110.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/_data/media_types.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/_data/refresh.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/_data/schemes.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/_data/spdx_licences.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/_data/tlds.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/_error_handler.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/_resolve_forward_references.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/exceptions.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/fields/__init__.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/fields/_custom_types.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/fields/commonmark.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/fields/email.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/fields/http_status_codes.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/fields/iso9110.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/fields/json.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/fields/media.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/fields/oas.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/fields/spdx_licences.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/fields/uri.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/file_handler.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/grammars/oas.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/grammars/rfc6901.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/grammars/rfc7159.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/model_validators.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/py.typed +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/validators/__init__.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/validators/generic.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/validators/oas304.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/amati/validators/oas311.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/bin/startup.sh +0 -0
- {amati-0.2.29 → amati-0.3.2}/bin/upgrade-python.sh +0 -0
- {amati-0.2.29 → amati-0.3.2}/bin/uv-upgrade-from-main.sh +0 -0
- {amati-0.2.29 → amati-0.3.2}/scripts/setup_test_specs.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/tests/__init__.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/tests/data/.amati.tests.yaml +0 -0
- {amati-0.2.29 → amati-0.3.2}/tests/data/DigitalOcean-public.v2.errors.json +0 -0
- {amati-0.2.29 → amati-0.3.2}/tests/data/api.github.com.yaml.errors.json +0 -0
- {amati-0.2.29 → amati-0.3.2}/tests/data/discourse.yml.errors.json +0 -0
- {amati-0.2.29 → amati-0.3.2}/tests/data/next-api.github.com.yaml.errors.json +0 -0
- {amati-0.2.29 → amati-0.3.2}/tests/data/openapi.yaml +0 -0
- {amati-0.2.29 → amati-0.3.2}/tests/data/openapi.yaml.gz +0 -0
- {amati-0.2.29 → amati-0.3.2}/tests/data/redocly.openapi.yaml.errors.json +0 -0
- {amati-0.2.29 → amati-0.3.2}/tests/fields/__init__.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/tests/fields/test_email.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/tests/fields/test_http_status_codes.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/tests/fields/test_iso9110.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/tests/fields/test_media.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/tests/fields/test_oas.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/tests/fields/test_spdx_licences.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/tests/fields/test_uri.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/tests/helpers.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/tests/model_validators/test_all_of.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/tests/model_validators/test_at_least_one.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/tests/model_validators/test_if_then.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/tests/model_validators/test_only_one.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/tests/test_external_specs.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/tests/test_logging.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/tests/validators/__init__.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/tests/validators/test_generic.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/tests/validators/test_licence_object.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/tests/validators/test_security_scheme_object.py +0 -0
- {amati-0.2.29 → amati-0.3.2}/tests/validators/test_server_variable_object.py +0 -0
|
@@ -51,7 +51,7 @@ jobs:
|
|
|
51
51
|
run: |
|
|
52
52
|
# Capture both stdout and exit code
|
|
53
53
|
set +e
|
|
54
|
-
OUTPUT=$(uv run python amati/amati.py
|
|
54
|
+
OUTPUT=$(uv run python amati/amati.py refresh 2>&1)
|
|
55
55
|
EXIT_CODE=$?
|
|
56
56
|
set -e
|
|
57
57
|
|
|
@@ -119,7 +119,7 @@ jobs:
|
|
|
119
119
|
This PR contains automated data refresh changes generated by the weekly data refresh workflow.
|
|
120
120
|
|
|
121
121
|
### Changes
|
|
122
|
-
- Data refreshed using \`python amati/amati.py
|
|
122
|
+
- Data refreshed using \`python amati/amati.py refresh\`
|
|
123
123
|
- Files updated in \`amati/_data/files/\` directory
|
|
124
124
|
|
|
125
125
|
### Details
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: amati
|
|
3
|
-
Version: 0.2
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Validates that a .yaml or .json file conforms to the OpenAPI Specifications 3.x.
|
|
5
|
-
Project-URL: Homepage, https://github.com/
|
|
6
|
-
Project-URL: Issues, https://github.com/
|
|
5
|
+
Project-URL: Homepage, https://github.com/gwyli/amati
|
|
6
|
+
Project-URL: Issues, https://github.com/gwyli/amati/issues
|
|
7
7
|
Author-email: Ben <2551337+ben-alexander@users.noreply.github.com>
|
|
8
8
|
License-File: LICENSE
|
|
9
9
|
Classifier: Development Status :: 3 - Alpha
|
|
@@ -42,42 +42,33 @@ amati is designed to validate that a file conforms to the [OpenAPI Specification
|
|
|
42
42
|
## Usage
|
|
43
43
|
|
|
44
44
|
```sh
|
|
45
|
-
python amati/amati.py --help
|
|
46
|
-
usage: amati [-h]
|
|
47
|
-
|
|
48
|
-
Tests whether a OpenAPI specification is valid. Will look an openapi.json or openapi.yaml file in the directory that
|
|
49
|
-
amati is called from. If --discover is set will search the directory tree. If the specification does not follow the
|
|
50
|
-
naming recommendation the --spec switch should be used. Creates a file <filename>.errors.json alongside the original
|
|
51
|
-
specification containing a JSON representation of all the errors.
|
|
45
|
+
python amati/amati.py validate --help
|
|
46
|
+
usage: amati validate [-h] -s SPEC [--consistency-check] [--local] [--html-report]
|
|
52
47
|
|
|
53
48
|
options:
|
|
54
49
|
-h, --help show this help message and exit
|
|
55
|
-
-s, --spec SPEC The specification to be
|
|
50
|
+
-s, --spec SPEC The specification to be validated
|
|
56
51
|
--consistency-check Runs a consistency check between the input specification and the parsed specification
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
--local Store errors local to the caller in a file called <file-name>.errors.json; a .amati/ directory
|
|
60
|
-
will be created.
|
|
61
|
-
--html-report Creates an HTML report of the errors, called <file-name>.errors.html, alongside the original
|
|
62
|
-
file or in a .amati/ directory if the --local switch is used
|
|
52
|
+
--local Store errors local to the caller in .amati/<file-name>.errors.json
|
|
53
|
+
--html-report Creates an HTML report of the errors, called <file-name>.errors.html, alongside <filename>.errors.json
|
|
63
54
|
```
|
|
64
55
|
|
|
65
56
|
### Docker
|
|
66
57
|
|
|
67
58
|
A Dockerfile is available on [DockerHub](https://hub.docker.com/r/benale/amati/tags) or `docker pull benale/amati:alpha`.
|
|
68
59
|
|
|
69
|
-
Whilst an alpha build only the image tagged `alpha` will be maintained. If there are breaking API changes these will be detailed in releases
|
|
60
|
+
Whilst an alpha build, only the image tagged `alpha` will be maintained. If there are breaking API changes these will be detailed in releases. Releases can be separately watched using the custom option when watching this repository.
|
|
70
61
|
|
|
71
62
|
To run against a specific specification the location of the specification needs to be mounted in the container.
|
|
72
63
|
|
|
73
64
|
```sh
|
|
74
|
-
docker run -v "<path-to-specification>:/<mount-name> benale/amati:alpha <options>
|
|
65
|
+
docker run -v "<path-to-specification>:/<mount-name> benale/amati:alpha validate --spec <path-to-spec> <options>
|
|
75
66
|
```
|
|
76
67
|
|
|
77
68
|
e.g. where you have a specification located in `/Users/myuser/myrepo/myspec.yaml` and create a mount `/data`:
|
|
78
69
|
|
|
79
70
|
```sh
|
|
80
|
-
docker run -v /Users/myuser/myrepo:/data benale/amati:alpha --spec /data/myspec.yaml --html-report
|
|
71
|
+
docker run -v /Users/myuser/myrepo:/data benale/amati:alpha validate --spec /data/myspec.yaml --html-report
|
|
81
72
|
```
|
|
82
73
|
|
|
83
74
|
### PyPI
|
|
@@ -86,14 +77,13 @@ amati is [available on PyPI](https://pypi.org/project/amati/), to run everything
|
|
|
86
77
|
|
|
87
78
|
```py
|
|
88
79
|
>>> from amati import amati
|
|
89
|
-
>>>
|
|
90
|
-
>>> check
|
|
80
|
+
>>> amati.run('tests/data/openapi.yaml', consistency_check=True, local=True, html_report=True)
|
|
91
81
|
True
|
|
92
82
|
```
|
|
93
83
|
|
|
94
84
|
## Architecture
|
|
95
85
|
|
|
96
|
-
amati uses Pydantic, especially the validation, and
|
|
86
|
+
amati uses [Pydantic](https://docs.pydantic.dev/latest/), especially the validation, and [typing](https://docs.python.org/3/library/typing.html) 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. To the extent that Pydantic is functional, amati has a [functional core and an imperative shell](https://www.destroyallsoftware.com/screencasts/catalog/functional-core-imperative-shell).
|
|
97
87
|
|
|
98
88
|
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.
|
|
99
89
|
|
|
@@ -130,7 +120,7 @@ It's expected that there are no errors and 100% of the code is reached and execu
|
|
|
130
120
|
amati runs tests on the external specifications, detailed in `tests/data/.amati.tests.yaml`. To be able to run these tests the GitHub repos containing the specifications need to be available locally. Specific revisions of the repos can be downloaded by running the following, which will clone the repos into `.amati/amati-tests-specs/<repo-name>`.
|
|
131
121
|
|
|
132
122
|
```sh
|
|
133
|
-
python scripts/
|
|
123
|
+
python scripts/setup_test_specs.py
|
|
134
124
|
```
|
|
135
125
|
|
|
136
126
|
If there are some issues with the specification a JSON file detailing those should be placed into `tests/data/` and the name of that file noted in `tests/data/.amati.tests.yaml` for the test suite to pick it up and check that the errors are expected. Any specifications that close the coverage gap are gratefully received.
|
|
@@ -152,18 +142,22 @@ docker build -t amati -f Dockerfile .
|
|
|
152
142
|
to run against a specific specification the location of the specification needs to be mounted in the container.
|
|
153
143
|
|
|
154
144
|
```sh
|
|
155
|
-
docker run -v "<path-to-specification>:/<mount-name> amati <options>
|
|
145
|
+
docker run -v "<path-to-specification>:/<mount-name> amati validate -s <path-to-spec> <options>
|
|
156
146
|
```
|
|
157
147
|
|
|
158
148
|
This can be tested against a provided specification, from the root directory
|
|
159
149
|
|
|
160
150
|
```sh
|
|
161
|
-
docker run --detach -v "$(pwd):/data" amati <options>
|
|
151
|
+
docker run --detach -v "$(pwd):/data" amati validate -s <path-to-spec> <options>
|
|
162
152
|
```
|
|
163
153
|
|
|
164
154
|
|
|
165
155
|
### Data
|
|
166
156
|
|
|
167
|
-
There are some scripts to create the data needed by the project, for example, all the registered TLDs. To refresh the data, run
|
|
157
|
+
There are some scripts to create the data needed by the project, for example, all the registered TLDs. To refresh the data, run:
|
|
158
|
+
|
|
159
|
+
```py
|
|
160
|
+
python amati/amati.py refresh
|
|
161
|
+
```
|
|
168
162
|
|
|
169
163
|
[](https://scorecard.dev/viewer/?uri=github.com/gwyli/amati)
|
|
@@ -9,42 +9,33 @@ amati is designed to validate that a file conforms to the [OpenAPI Specification
|
|
|
9
9
|
## Usage
|
|
10
10
|
|
|
11
11
|
```sh
|
|
12
|
-
python amati/amati.py --help
|
|
13
|
-
usage: amati [-h]
|
|
14
|
-
|
|
15
|
-
Tests whether a OpenAPI specification is valid. Will look an openapi.json or openapi.yaml file in the directory that
|
|
16
|
-
amati is called from. If --discover is set will search the directory tree. If the specification does not follow the
|
|
17
|
-
naming recommendation the --spec switch should be used. Creates a file <filename>.errors.json alongside the original
|
|
18
|
-
specification containing a JSON representation of all the errors.
|
|
12
|
+
python amati/amati.py validate --help
|
|
13
|
+
usage: amati validate [-h] -s SPEC [--consistency-check] [--local] [--html-report]
|
|
19
14
|
|
|
20
15
|
options:
|
|
21
16
|
-h, --help show this help message and exit
|
|
22
|
-
-s, --spec SPEC The specification to be
|
|
17
|
+
-s, --spec SPEC The specification to be validated
|
|
23
18
|
--consistency-check Runs a consistency check between the input specification and the parsed specification
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
--local Store errors local to the caller in a file called <file-name>.errors.json; a .amati/ directory
|
|
27
|
-
will be created.
|
|
28
|
-
--html-report Creates an HTML report of the errors, called <file-name>.errors.html, alongside the original
|
|
29
|
-
file or in a .amati/ directory if the --local switch is used
|
|
19
|
+
--local Store errors local to the caller in .amati/<file-name>.errors.json
|
|
20
|
+
--html-report Creates an HTML report of the errors, called <file-name>.errors.html, alongside <filename>.errors.json
|
|
30
21
|
```
|
|
31
22
|
|
|
32
23
|
### Docker
|
|
33
24
|
|
|
34
25
|
A Dockerfile is available on [DockerHub](https://hub.docker.com/r/benale/amati/tags) or `docker pull benale/amati:alpha`.
|
|
35
26
|
|
|
36
|
-
Whilst an alpha build only the image tagged `alpha` will be maintained. If there are breaking API changes these will be detailed in releases
|
|
27
|
+
Whilst an alpha build, only the image tagged `alpha` will be maintained. If there are breaking API changes these will be detailed in releases. Releases can be separately watched using the custom option when watching this repository.
|
|
37
28
|
|
|
38
29
|
To run against a specific specification the location of the specification needs to be mounted in the container.
|
|
39
30
|
|
|
40
31
|
```sh
|
|
41
|
-
docker run -v "<path-to-specification>:/<mount-name> benale/amati:alpha <options>
|
|
32
|
+
docker run -v "<path-to-specification>:/<mount-name> benale/amati:alpha validate --spec <path-to-spec> <options>
|
|
42
33
|
```
|
|
43
34
|
|
|
44
35
|
e.g. where you have a specification located in `/Users/myuser/myrepo/myspec.yaml` and create a mount `/data`:
|
|
45
36
|
|
|
46
37
|
```sh
|
|
47
|
-
docker run -v /Users/myuser/myrepo:/data benale/amati:alpha --spec /data/myspec.yaml --html-report
|
|
38
|
+
docker run -v /Users/myuser/myrepo:/data benale/amati:alpha validate --spec /data/myspec.yaml --html-report
|
|
48
39
|
```
|
|
49
40
|
|
|
50
41
|
### PyPI
|
|
@@ -53,14 +44,13 @@ amati is [available on PyPI](https://pypi.org/project/amati/), to run everything
|
|
|
53
44
|
|
|
54
45
|
```py
|
|
55
46
|
>>> from amati import amati
|
|
56
|
-
>>>
|
|
57
|
-
>>> check
|
|
47
|
+
>>> amati.run('tests/data/openapi.yaml', consistency_check=True, local=True, html_report=True)
|
|
58
48
|
True
|
|
59
49
|
```
|
|
60
50
|
|
|
61
51
|
## Architecture
|
|
62
52
|
|
|
63
|
-
amati uses Pydantic, especially the validation, and
|
|
53
|
+
amati uses [Pydantic](https://docs.pydantic.dev/latest/), especially the validation, and [typing](https://docs.python.org/3/library/typing.html) 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. To the extent that Pydantic is functional, amati has a [functional core and an imperative shell](https://www.destroyallsoftware.com/screencasts/catalog/functional-core-imperative-shell).
|
|
64
54
|
|
|
65
55
|
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.
|
|
66
56
|
|
|
@@ -97,7 +87,7 @@ It's expected that there are no errors and 100% of the code is reached and execu
|
|
|
97
87
|
amati runs tests on the external specifications, detailed in `tests/data/.amati.tests.yaml`. To be able to run these tests the GitHub repos containing the specifications need to be available locally. Specific revisions of the repos can be downloaded by running the following, which will clone the repos into `.amati/amati-tests-specs/<repo-name>`.
|
|
98
88
|
|
|
99
89
|
```sh
|
|
100
|
-
python scripts/
|
|
90
|
+
python scripts/setup_test_specs.py
|
|
101
91
|
```
|
|
102
92
|
|
|
103
93
|
If there are some issues with the specification a JSON file detailing those should be placed into `tests/data/` and the name of that file noted in `tests/data/.amati.tests.yaml` for the test suite to pick it up and check that the errors are expected. Any specifications that close the coverage gap are gratefully received.
|
|
@@ -119,18 +109,22 @@ docker build -t amati -f Dockerfile .
|
|
|
119
109
|
to run against a specific specification the location of the specification needs to be mounted in the container.
|
|
120
110
|
|
|
121
111
|
```sh
|
|
122
|
-
docker run -v "<path-to-specification>:/<mount-name> amati <options>
|
|
112
|
+
docker run -v "<path-to-specification>:/<mount-name> amati validate -s <path-to-spec> <options>
|
|
123
113
|
```
|
|
124
114
|
|
|
125
115
|
This can be tested against a provided specification, from the root directory
|
|
126
116
|
|
|
127
117
|
```sh
|
|
128
|
-
docker run --detach -v "$(pwd):/data" amati <options>
|
|
118
|
+
docker run --detach -v "$(pwd):/data" amati validate -s <path-to-spec> <options>
|
|
129
119
|
```
|
|
130
120
|
|
|
131
121
|
|
|
132
122
|
### Data
|
|
133
123
|
|
|
134
|
-
There are some scripts to create the data needed by the project, for example, all the registered TLDs. To refresh the data, run
|
|
124
|
+
There are some scripts to create the data needed by the project, for example, all the registered TLDs. To refresh the data, run:
|
|
125
|
+
|
|
126
|
+
```py
|
|
127
|
+
python amati/amati.py refresh
|
|
128
|
+
```
|
|
135
129
|
|
|
136
130
|
[](https://scorecard.dev/viewer/?uri=github.com/gwyli/amati)
|
|
@@ -2817,6 +2817,17 @@
|
|
|
2817
2817
|
],
|
|
2818
2818
|
"isOsiApproved": false
|
|
2819
2819
|
},
|
|
2820
|
+
{
|
|
2821
|
+
"reference": "https://spdx.org/licenses/ESA-PL-strong-copyleft-2.4.html",
|
|
2822
|
+
"isDeprecatedLicenseId": false,
|
|
2823
|
+
"detailsUrl": "https://spdx.org/licenses/ESA-PL-strong-copyleft-2.4.json",
|
|
2824
|
+
"name": "European Space Agency Public License (ESA-PL) - V2.4 - Strong Copyleft (Type 1)",
|
|
2825
|
+
"licenseId": "ESA-PL-strong-copyleft-2.4",
|
|
2826
|
+
"seeAlso": [
|
|
2827
|
+
"https://essr.esa.int/license/european-space-agency-public-license-v2-4-strong-copyleft-type-1"
|
|
2828
|
+
],
|
|
2829
|
+
"isOsiApproved": false
|
|
2830
|
+
},
|
|
2820
2831
|
{
|
|
2821
2832
|
"reference": "https://spdx.org/licenses/ESA-PL-weak-copyleft-2.4.html",
|
|
2822
2833
|
"isDeprecatedLicenseId": false,
|
|
@@ -6416,6 +6427,17 @@
|
|
|
6416
6427
|
"isOsiApproved": true,
|
|
6417
6428
|
"isFsfLibre": true
|
|
6418
6429
|
},
|
|
6430
|
+
{
|
|
6431
|
+
"reference": "https://spdx.org/licenses/OSSP.html",
|
|
6432
|
+
"isDeprecatedLicenseId": false,
|
|
6433
|
+
"detailsUrl": "https://spdx.org/licenses/OSSP.json",
|
|
6434
|
+
"name": "OSSP License",
|
|
6435
|
+
"licenseId": "OSSP",
|
|
6436
|
+
"seeAlso": [
|
|
6437
|
+
"https://git.sr.ht/~nabijaczleweli/ossp-var"
|
|
6438
|
+
],
|
|
6439
|
+
"isOsiApproved": false
|
|
6440
|
+
},
|
|
6419
6441
|
{
|
|
6420
6442
|
"reference": "https://spdx.org/licenses/PADL.html",
|
|
6421
6443
|
"isDeprecatedLicenseId": false,
|
|
@@ -6934,6 +6956,17 @@
|
|
|
6934
6956
|
],
|
|
6935
6957
|
"isOsiApproved": false
|
|
6936
6958
|
},
|
|
6959
|
+
{
|
|
6960
|
+
"reference": "https://spdx.org/licenses/SGMLUG-PM.html",
|
|
6961
|
+
"isDeprecatedLicenseId": false,
|
|
6962
|
+
"detailsUrl": "https://spdx.org/licenses/SGMLUG-PM.json",
|
|
6963
|
+
"name": "SGMLUG Parser Materials License",
|
|
6964
|
+
"licenseId": "SGMLUG-PM",
|
|
6965
|
+
"seeAlso": [
|
|
6966
|
+
"https://gitweb.gentoo.org/repo/gentoo.git/tree/licenses/SGMLUG?id=7d999af4a47bf55e53e54713d98d145f935935c1"
|
|
6967
|
+
],
|
|
6968
|
+
"isOsiApproved": false
|
|
6969
|
+
},
|
|
6937
6970
|
{
|
|
6938
6971
|
"reference": "https://spdx.org/licenses/SGP4.html",
|
|
6939
6972
|
"isDeprecatedLicenseId": false,
|
|
@@ -10,7 +10,7 @@ from typing import Any, ClassVar, NotRequired, TypedDict
|
|
|
10
10
|
type LogType = Exception | Warning
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
@dataclass
|
|
13
|
+
@dataclass(frozen=True)
|
|
14
14
|
class Log(TypedDict):
|
|
15
15
|
type: str
|
|
16
16
|
loc: NotRequired[tuple[int | str, ...]]
|
|
@@ -20,11 +20,10 @@ class Log(TypedDict):
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
class Logger:
|
|
23
|
-
"""
|
|
24
|
-
A mixin class that provides logging functionality.
|
|
23
|
+
"""A simple class-level logger for collecting Log objects.
|
|
25
24
|
|
|
26
|
-
This class
|
|
27
|
-
|
|
25
|
+
This class provides methods for appending logs and managing
|
|
26
|
+
a logging context that automatically clears the logs.
|
|
28
27
|
"""
|
|
29
28
|
|
|
30
29
|
logs: ClassVar[list[Log]] = []
|
|
@@ -163,86 +163,39 @@ def run(
|
|
|
163
163
|
return True
|
|
164
164
|
|
|
165
165
|
|
|
166
|
-
def discover(spec: str, discover_dir: str = ".") -> list[Path]:
|
|
167
|
-
"""
|
|
168
|
-
Finds OpenAPI Specification files to validate
|
|
169
|
-
|
|
170
|
-
Args:
|
|
171
|
-
spec: The path to a specific OpenAPI specification file.
|
|
172
|
-
discover_dir: The directory to search through.
|
|
173
|
-
Returns:
|
|
174
|
-
A list of specifications to validate.
|
|
175
|
-
"""
|
|
176
|
-
|
|
177
|
-
specs: list[Path] = []
|
|
178
|
-
|
|
179
|
-
# If a spec is provided, check if it exists and erorr if not
|
|
180
|
-
if spec:
|
|
181
|
-
spec_path = Path(spec)
|
|
182
|
-
|
|
183
|
-
if not spec_path.exists():
|
|
184
|
-
raise FileNotFoundError(f"File {spec} does not exist.")
|
|
185
|
-
|
|
186
|
-
if not spec_path.is_file():
|
|
187
|
-
raise IsADirectoryError(f"{spec} is a directory, not a file.")
|
|
188
|
-
|
|
189
|
-
specs.append(spec_path)
|
|
190
|
-
|
|
191
|
-
# End early if we're not also trying to find files
|
|
192
|
-
if not discover_dir:
|
|
193
|
-
return specs
|
|
194
|
-
|
|
195
|
-
if Path("openapi.json").exists():
|
|
196
|
-
specs.append(Path("openapi.json"))
|
|
197
|
-
|
|
198
|
-
if Path("openapi.yaml").exists():
|
|
199
|
-
specs.append(Path("openapi.yaml"))
|
|
200
|
-
|
|
201
|
-
if specs:
|
|
202
|
-
return specs
|
|
203
|
-
|
|
204
|
-
if discover_dir == ".":
|
|
205
|
-
raise FileNotFoundError(
|
|
206
|
-
"openapi.json or openapi.yaml can't be found, use --discover or --spec."
|
|
207
|
-
)
|
|
208
|
-
|
|
209
|
-
specs = specs + list(Path(discover_dir).glob("**/openapi.json"))
|
|
210
|
-
specs = specs + list(Path(discover_dir).glob("**/openapi.yaml"))
|
|
211
|
-
|
|
212
|
-
if not specs:
|
|
213
|
-
raise FileNotFoundError(
|
|
214
|
-
"openapi.json or openapi.yaml can't be found, use --spec."
|
|
215
|
-
)
|
|
216
|
-
|
|
217
|
-
return specs
|
|
218
|
-
|
|
219
|
-
|
|
220
166
|
if __name__ == "__main__":
|
|
167
|
+
logger.remove() # Remove the default logger
|
|
168
|
+
# Add a new logger that outputs to stderr with a specific format
|
|
169
|
+
logger.add(sys.stderr, format="{time} | {level} | {message}")
|
|
170
|
+
|
|
221
171
|
import argparse
|
|
222
172
|
|
|
223
173
|
parser: argparse.ArgumentParser = argparse.ArgumentParser(
|
|
224
174
|
prog="amati",
|
|
225
175
|
description="""
|
|
226
|
-
Tests whether a OpenAPI specification is valid.
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
containing a JSON representation of all the errors.
|
|
176
|
+
Tests whether a OpenAPI specification is valid. Creates a file
|
|
177
|
+
<filename>.errors.json alongside the original specification containing
|
|
178
|
+
a JSON representation of all the errors.
|
|
179
|
+
|
|
180
|
+
Optionally creates an HTML report of the errors, and performs an internal
|
|
181
|
+
consistency check to verify that the output of the validation is identical
|
|
182
|
+
to the input.
|
|
234
183
|
""",
|
|
235
184
|
suggest_on_error=True,
|
|
236
185
|
)
|
|
237
186
|
|
|
238
|
-
parser.
|
|
187
|
+
subparsers: argparse.Action = parser.add_subparsers(required=True, dest="command")
|
|
188
|
+
|
|
189
|
+
validation: argparse.ArgumentParser = subparsers.add_parser("validate")
|
|
190
|
+
|
|
191
|
+
validation.add_argument(
|
|
239
192
|
"-s",
|
|
240
193
|
"--spec",
|
|
241
|
-
required=
|
|
242
|
-
help="The specification to be
|
|
194
|
+
required=True,
|
|
195
|
+
help="The specification to be validated",
|
|
243
196
|
)
|
|
244
197
|
|
|
245
|
-
|
|
198
|
+
validation.add_argument(
|
|
246
199
|
"--consistency-check",
|
|
247
200
|
required=False,
|
|
248
201
|
action="store_true",
|
|
@@ -250,48 +203,44 @@ if __name__ == "__main__":
|
|
|
250
203
|
" parsed specification",
|
|
251
204
|
)
|
|
252
205
|
|
|
253
|
-
|
|
254
|
-
"-d",
|
|
255
|
-
"--discover",
|
|
256
|
-
required=False,
|
|
257
|
-
default=".",
|
|
258
|
-
help="Searches the specified directory tree for openapi.yaml or openapi.json.",
|
|
259
|
-
)
|
|
260
|
-
|
|
261
|
-
parser.add_argument(
|
|
206
|
+
validation.add_argument(
|
|
262
207
|
"--local",
|
|
263
208
|
required=False,
|
|
264
209
|
action="store_true",
|
|
265
|
-
help="Store errors local to the caller in
|
|
266
|
-
"; a .amati/ directory will be created.",
|
|
210
|
+
help="Store errors local to the caller in .amati/<file-name>.errors.json",
|
|
267
211
|
)
|
|
268
212
|
|
|
269
|
-
|
|
213
|
+
validation.add_argument(
|
|
270
214
|
"--html-report",
|
|
271
215
|
required=False,
|
|
272
216
|
action="store_true",
|
|
273
217
|
help="Creates an HTML report of the errors, called <file-name>.errors.html,"
|
|
274
|
-
" alongside
|
|
275
|
-
" is used",
|
|
218
|
+
" alongside <filename>.errors.json",
|
|
276
219
|
)
|
|
277
220
|
|
|
278
|
-
|
|
279
|
-
|
|
221
|
+
refreshment: argparse.ArgumentParser = subparsers.add_parser("refresh")
|
|
222
|
+
|
|
223
|
+
refreshment.add_argument(
|
|
224
|
+
"--type",
|
|
280
225
|
required=False,
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
226
|
+
default="all",
|
|
227
|
+
choices=[
|
|
228
|
+
"all",
|
|
229
|
+
"http_status_code",
|
|
230
|
+
"iso9110",
|
|
231
|
+
"media_types",
|
|
232
|
+
"schemes",
|
|
233
|
+
"spdx_licences",
|
|
234
|
+
"tlds",
|
|
235
|
+
],
|
|
236
|
+
help="The type of data to refresh. Defaults to all.",
|
|
284
237
|
)
|
|
285
238
|
|
|
286
239
|
args: argparse.Namespace = parser.parse_args()
|
|
287
240
|
|
|
288
|
-
logger.remove() # Remove the default logger
|
|
289
|
-
# Add a new logger that outputs to stderr with a specific format
|
|
290
|
-
logger.add(sys.stderr, format="{time} | {level} | {message}")
|
|
291
|
-
|
|
292
241
|
logger.info("Starting amati")
|
|
293
242
|
|
|
294
|
-
if args.
|
|
243
|
+
if args.command == "refresh":
|
|
295
244
|
logger.info("Refreshing data.")
|
|
296
245
|
try:
|
|
297
246
|
refresh("all")
|
|
@@ -301,32 +250,24 @@ if __name__ == "__main__":
|
|
|
301
250
|
logger.error(f"Error refreshing data: {str(e)}")
|
|
302
251
|
sys.exit(1)
|
|
303
252
|
|
|
253
|
+
specification: Path = Path(args.spec)
|
|
254
|
+
logger.info(f"Processing specification {specification}")
|
|
255
|
+
|
|
256
|
+
# Top-level try/except to ensure one failed spec doesn't stop the rest
|
|
257
|
+
# from being processed.
|
|
258
|
+
e: Exception
|
|
304
259
|
try:
|
|
305
|
-
|
|
260
|
+
successful_check: bool = run(
|
|
261
|
+
specification, args.consistency_check, args.local, args.html_report
|
|
262
|
+
)
|
|
263
|
+
logger.info(f"Specification {specification} processed successfully.")
|
|
306
264
|
except Exception as e:
|
|
307
|
-
logger.error(str(e))
|
|
265
|
+
logger.error(f"Error processing {specification}, {str(e)}")
|
|
308
266
|
sys.exit(1)
|
|
309
267
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
logger.info(f"
|
|
314
|
-
|
|
315
|
-
# Top-level try/except to ensure one failed spec doesn't stop the rest
|
|
316
|
-
# from being processed.
|
|
317
|
-
e: Exception
|
|
318
|
-
try:
|
|
319
|
-
successful_check = run(
|
|
320
|
-
specification, args.consistency_check, args.local, args.html_report
|
|
321
|
-
)
|
|
322
|
-
logger.info(f"Specification {specification} processed successfully.")
|
|
323
|
-
except Exception as e:
|
|
324
|
-
logger.error(f"Error processing {specification}, {str(e)}")
|
|
325
|
-
sys.exit(1)
|
|
326
|
-
|
|
327
|
-
if args.consistency_check and successful_check:
|
|
328
|
-
logger.info(f"Consistency check successful for {specification}")
|
|
329
|
-
elif args.consistency_check:
|
|
330
|
-
logger.info(f"Consistency check failed for {specification}")
|
|
268
|
+
if args.consistency_check and successful_check:
|
|
269
|
+
logger.info(f"Consistency check successful for {specification}")
|
|
270
|
+
elif args.consistency_check:
|
|
271
|
+
logger.info(f"Consistency check failed for {specification}")
|
|
331
272
|
|
|
332
273
|
logger.info("Stopping amati.")
|
|
@@ -7,5 +7,5 @@ pytest --doctest-modules amati/
|
|
|
7
7
|
pyright --verifytypes amati --ignoreexternal
|
|
8
8
|
docker build -t amati -f Dockerfile .
|
|
9
9
|
cd .amati/test-specs/swagger/src/main/resources/ || exit
|
|
10
|
-
docker run -v "$(pwd):/data" amati -
|
|
10
|
+
docker run -v "$(pwd):/data" amati validate -s /data/openapi.yaml --consistency-check
|
|
11
11
|
cd - || exit
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "amati"
|
|
3
|
-
version = "0.2
|
|
3
|
+
version = "0.3.2"
|
|
4
4
|
description = "Validates that a .yaml or .json file conforms to the OpenAPI Specifications 3.x."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [
|
|
@@ -37,8 +37,8 @@ dependencies = [
|
|
|
37
37
|
]
|
|
38
38
|
|
|
39
39
|
[project.urls]
|
|
40
|
-
Homepage = "https://github.com/
|
|
41
|
-
Issues = "https://github.com/
|
|
40
|
+
Homepage = "https://github.com/gwyli/amati"
|
|
41
|
+
Issues = "https://github.com/gwyli/amati/issues"
|
|
42
42
|
|
|
43
43
|
[project.scripts]
|
|
44
44
|
amati = "amati:main"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Minimal example of an OpenAPI spec for use in tests/test_amati.py
|
|
2
|
+
openapi: 3.1.1
|
|
3
|
+
info:
|
|
4
|
+
title: Example API
|
|
5
|
+
termsOfService: https://example.com/terms
|
|
6
|
+
contact:
|
|
7
|
+
email: support@example.com
|
|
8
|
+
license:
|
|
9
|
+
name: MIT
|
|
10
|
+
identifier: MIT2
|
|
11
|
+
version: 1.0.0
|
|
12
|
+
servers:
|
|
13
|
+
- url: https://api.example.com/v1
|
|
14
|
+
description: Production server
|
|
15
|
+
- url: https://api.{environment}.example.com/v1/users/{userId}
|
|
16
|
+
description: Test servers
|
|
17
|
+
variables:
|
|
18
|
+
environment:
|
|
19
|
+
default: staging
|
|
20
|
+
enum:
|
|
21
|
+
- staging
|
|
22
|
+
- development
|
|
23
|
+
description: The environment to use
|
|
24
|
+
userId:
|
|
25
|
+
default: "1"
|
|
26
|
+
description: The user ID
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests amati/amati.py, especially the args.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import subprocess
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def test_specifc_spec():
|
|
10
|
+
subprocess.run(
|
|
11
|
+
[
|
|
12
|
+
"python",
|
|
13
|
+
"amati/amati.py",
|
|
14
|
+
"validate",
|
|
15
|
+
"-s",
|
|
16
|
+
"tests/data/openapi.yaml",
|
|
17
|
+
"--consistency-check",
|
|
18
|
+
],
|
|
19
|
+
check=True,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_gzip():
|
|
24
|
+
subprocess.run(
|
|
25
|
+
[
|
|
26
|
+
"python",
|
|
27
|
+
"amati/amati.py",
|
|
28
|
+
"validate",
|
|
29
|
+
"-s",
|
|
30
|
+
"tests/data/openapi.yaml.gz",
|
|
31
|
+
"--consistency-check",
|
|
32
|
+
],
|
|
33
|
+
check=True,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_errors_created_local():
|
|
38
|
+
error_file: Path = Path(".amati/invalid-openapi.yaml.errors.json")
|
|
39
|
+
html_file: Path = Path(".amati/invalid-openapi.yaml.errors.html")
|
|
40
|
+
|
|
41
|
+
if error_file.exists():
|
|
42
|
+
error_file.unlink()
|
|
43
|
+
if html_file.exists():
|
|
44
|
+
html_file.unlink()
|
|
45
|
+
|
|
46
|
+
subprocess.run(
|
|
47
|
+
[
|
|
48
|
+
"python",
|
|
49
|
+
"amati/amati.py",
|
|
50
|
+
"validate",
|
|
51
|
+
"-s",
|
|
52
|
+
"tests/data/invalid-openapi.yaml",
|
|
53
|
+
"--local",
|
|
54
|
+
"--html-report",
|
|
55
|
+
],
|
|
56
|
+
check=True,
|
|
57
|
+
)
|
|
58
|
+
assert error_file.exists()
|
|
59
|
+
assert html_file.exists()
|
|
60
|
+
error_file.unlink()
|
|
61
|
+
html_file.unlink()
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_errors_created():
|
|
65
|
+
error_file: Path = Path("tests/data/invalid-openapi.yaml.errors.json")
|
|
66
|
+
html_file: Path = Path("tests/data/invalid-openapi.yaml.errors.html")
|
|
67
|
+
|
|
68
|
+
if error_file.exists():
|
|
69
|
+
error_file.unlink()
|
|
70
|
+
if html_file.exists():
|
|
71
|
+
html_file.unlink()
|
|
72
|
+
|
|
73
|
+
subprocess.run(
|
|
74
|
+
[
|
|
75
|
+
"python",
|
|
76
|
+
"amati/amati.py",
|
|
77
|
+
"validate",
|
|
78
|
+
"-s",
|
|
79
|
+
"tests/data/invalid-openapi.yaml",
|
|
80
|
+
"--html-report",
|
|
81
|
+
],
|
|
82
|
+
check=True,
|
|
83
|
+
)
|
|
84
|
+
assert error_file.exists()
|
|
85
|
+
assert html_file.exists()
|
|
86
|
+
error_file.unlink()
|
|
87
|
+
html_file.unlink()
|
|
@@ -13,7 +13,7 @@ wheels = [
|
|
|
13
13
|
|
|
14
14
|
[[package]]
|
|
15
15
|
name = "amati"
|
|
16
|
-
version = "0.2
|
|
16
|
+
version = "0.3.2"
|
|
17
17
|
source = { editable = "." }
|
|
18
18
|
dependencies = [
|
|
19
19
|
{ name = "abnf" },
|
|
@@ -362,7 +362,7 @@ wheels = [
|
|
|
362
362
|
|
|
363
363
|
[[package]]
|
|
364
364
|
name = "pydantic"
|
|
365
|
-
version = "2.12.
|
|
365
|
+
version = "2.12.2"
|
|
366
366
|
source = { registry = "https://pypi.org/simple/" }
|
|
367
367
|
dependencies = [
|
|
368
368
|
{ name = "annotated-types" },
|
|
@@ -370,39 +370,39 @@ dependencies = [
|
|
|
370
370
|
{ name = "typing-extensions" },
|
|
371
371
|
{ name = "typing-inspection" },
|
|
372
372
|
]
|
|
373
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
373
|
+
sdist = { url = "https://files.pythonhosted.org/packages/8d/35/d319ed522433215526689bad428a94058b6dd12190ce7ddd78618ac14b28/pydantic-2.12.2.tar.gz", hash = "sha256:7b8fa15b831a4bbde9d5b84028641ac3080a4ca2cbd4a621a661687e741624fd", size = 816358, upload-time = "2025-10-14T15:02:21.842Z" }
|
|
374
374
|
wheels = [
|
|
375
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
375
|
+
{ url = "https://files.pythonhosted.org/packages/6c/98/468cb649f208a6f1279448e6e5247b37ae79cf5e4041186f1e2ef3d16345/pydantic-2.12.2-py3-none-any.whl", hash = "sha256:25ff718ee909acd82f1ff9b1a4acfd781bb23ab3739adaa7144f19a6a4e231ae", size = 460628, upload-time = "2025-10-14T15:02:19.623Z" },
|
|
376
376
|
]
|
|
377
377
|
|
|
378
378
|
[[package]]
|
|
379
379
|
name = "pydantic-core"
|
|
380
|
-
version = "2.41.
|
|
380
|
+
version = "2.41.4"
|
|
381
381
|
source = { registry = "https://pypi.org/simple/" }
|
|
382
382
|
dependencies = [
|
|
383
383
|
{ name = "typing-extensions" },
|
|
384
384
|
]
|
|
385
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
386
|
-
wheels = [
|
|
387
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
388
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
389
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
390
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
391
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
392
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
393
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
394
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
395
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
396
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
397
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
398
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
399
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
400
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
401
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
402
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
403
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
404
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
405
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
385
|
+
sdist = { url = "https://files.pythonhosted.org/packages/df/18/d0944e8eaaa3efd0a91b0f1fc537d3be55ad35091b6a87638211ba691964/pydantic_core-2.41.4.tar.gz", hash = "sha256:70e47929a9d4a1905a67e4b687d5946026390568a8e952b92824118063cee4d5", size = 457557, upload-time = "2025-10-14T10:23:47.909Z" }
|
|
386
|
+
wheels = [
|
|
387
|
+
{ url = "https://files.pythonhosted.org/packages/54/28/d3325da57d413b9819365546eb9a6e8b7cbd9373d9380efd5f74326143e6/pydantic_core-2.41.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:e9205d97ed08a82ebb9a307e92914bb30e18cdf6f6b12ca4bedadb1588a0bfe1", size = 2102022, upload-time = "2025-10-14T10:21:32.809Z" },
|
|
388
|
+
{ url = "https://files.pythonhosted.org/packages/9e/24/b58a1bc0d834bf1acc4361e61233ee217169a42efbdc15a60296e13ce438/pydantic_core-2.41.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:82df1f432b37d832709fbcc0e24394bba04a01b6ecf1ee87578145c19cde12ac", size = 1905495, upload-time = "2025-10-14T10:21:34.812Z" },
|
|
389
|
+
{ url = "https://files.pythonhosted.org/packages/fb/a4/71f759cc41b7043e8ecdaab81b985a9b6cad7cec077e0b92cff8b71ecf6b/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc3b4cc4539e055cfa39a3763c939f9d409eb40e85813257dcd761985a108554", size = 1956131, upload-time = "2025-10-14T10:21:36.924Z" },
|
|
390
|
+
{ url = "https://files.pythonhosted.org/packages/b0/64/1e79ac7aa51f1eec7c4cda8cbe456d5d09f05fdd68b32776d72168d54275/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b1eb1754fce47c63d2ff57fdb88c351a6c0150995890088b33767a10218eaa4e", size = 2052236, upload-time = "2025-10-14T10:21:38.927Z" },
|
|
391
|
+
{ url = "https://files.pythonhosted.org/packages/e9/e3/a3ffc363bd4287b80f1d43dc1c28ba64831f8dfc237d6fec8f2661138d48/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6ab5ab30ef325b443f379ddb575a34969c333004fca5a1daa0133a6ffaad616", size = 2223573, upload-time = "2025-10-14T10:21:41.574Z" },
|
|
392
|
+
{ url = "https://files.pythonhosted.org/packages/28/27/78814089b4d2e684a9088ede3790763c64693c3d1408ddc0a248bc789126/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:31a41030b1d9ca497634092b46481b937ff9397a86f9f51bd41c4767b6fc04af", size = 2342467, upload-time = "2025-10-14T10:21:44.018Z" },
|
|
393
|
+
{ url = "https://files.pythonhosted.org/packages/92/97/4de0e2a1159cb85ad737e03306717637842c88c7fd6d97973172fb183149/pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a44ac1738591472c3d020f61c6df1e4015180d6262ebd39bf2aeb52571b60f12", size = 2063754, upload-time = "2025-10-14T10:21:46.466Z" },
|
|
394
|
+
{ url = "https://files.pythonhosted.org/packages/0f/50/8cb90ce4b9efcf7ae78130afeb99fd1c86125ccdf9906ef64b9d42f37c25/pydantic_core-2.41.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d72f2b5e6e82ab8f94ea7d0d42f83c487dc159c5240d8f83beae684472864e2d", size = 2196754, upload-time = "2025-10-14T10:21:48.486Z" },
|
|
395
|
+
{ url = "https://files.pythonhosted.org/packages/34/3b/ccdc77af9cd5082723574a1cc1bcae7a6acacc829d7c0a06201f7886a109/pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:c4d1e854aaf044487d31143f541f7aafe7b482ae72a022c664b2de2e466ed0ad", size = 2137115, upload-time = "2025-10-14T10:21:50.63Z" },
|
|
396
|
+
{ url = "https://files.pythonhosted.org/packages/ca/ba/e7c7a02651a8f7c52dc2cff2b64a30c313e3b57c7d93703cecea76c09b71/pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b568af94267729d76e6ee5ececda4e283d07bbb28e8148bb17adad93d025d25a", size = 2317400, upload-time = "2025-10-14T10:21:52.959Z" },
|
|
397
|
+
{ url = "https://files.pythonhosted.org/packages/2c/ba/6c533a4ee8aec6b812c643c49bb3bd88d3f01e3cebe451bb85512d37f00f/pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:6d55fb8b1e8929b341cc313a81a26e0d48aa3b519c1dbaadec3a6a2b4fcad025", size = 2312070, upload-time = "2025-10-14T10:21:55.419Z" },
|
|
398
|
+
{ url = "https://files.pythonhosted.org/packages/22/ae/f10524fcc0ab8d7f96cf9a74c880243576fd3e72bd8ce4f81e43d22bcab7/pydantic_core-2.41.4-cp314-cp314-win32.whl", hash = "sha256:5b66584e549e2e32a1398df11da2e0a7eff45d5c2d9db9d5667c5e6ac764d77e", size = 1982277, upload-time = "2025-10-14T10:21:57.474Z" },
|
|
399
|
+
{ url = "https://files.pythonhosted.org/packages/b4/dc/e5aa27aea1ad4638f0c3fb41132f7eb583bd7420ee63204e2d4333a3bbf9/pydantic_core-2.41.4-cp314-cp314-win_amd64.whl", hash = "sha256:557a0aab88664cc552285316809cab897716a372afaf8efdbef756f8b890e894", size = 2024608, upload-time = "2025-10-14T10:21:59.557Z" },
|
|
400
|
+
{ url = "https://files.pythonhosted.org/packages/3e/61/51d89cc2612bd147198e120a13f150afbf0bcb4615cddb049ab10b81b79e/pydantic_core-2.41.4-cp314-cp314-win_arm64.whl", hash = "sha256:3f1ea6f48a045745d0d9f325989d8abd3f1eaf47dd00485912d1a3a63c623a8d", size = 1967614, upload-time = "2025-10-14T10:22:01.847Z" },
|
|
401
|
+
{ url = "https://files.pythonhosted.org/packages/0d/c2/472f2e31b95eff099961fa050c376ab7156a81da194f9edb9f710f68787b/pydantic_core-2.41.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6c1fe4c5404c448b13188dd8bd2ebc2bdd7e6727fa61ff481bcc2cca894018da", size = 1876904, upload-time = "2025-10-14T10:22:04.062Z" },
|
|
402
|
+
{ url = "https://files.pythonhosted.org/packages/4a/07/ea8eeb91173807ecdae4f4a5f4b150a520085b35454350fc219ba79e66a3/pydantic_core-2.41.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:523e7da4d43b113bf8e7b49fa4ec0c35bf4fe66b2230bfc5c13cc498f12c6c3e", size = 1882538, upload-time = "2025-10-14T10:22:06.39Z" },
|
|
403
|
+
{ url = "https://files.pythonhosted.org/packages/1e/29/b53a9ca6cd366bfc928823679c6a76c7a4c69f8201c0ba7903ad18ebae2f/pydantic_core-2.41.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5729225de81fb65b70fdb1907fcf08c75d498f4a6f15af005aabb1fdadc19dfa", size = 2041183, upload-time = "2025-10-14T10:22:08.812Z" },
|
|
404
|
+
{ url = "https://files.pythonhosted.org/packages/c7/3d/f8c1a371ceebcaf94d6dd2d77c6cf4b1c078e13a5837aee83f760b4f7cfd/pydantic_core-2.41.4-cp314-cp314t-win_amd64.whl", hash = "sha256:de2cfbb09e88f0f795fd90cf955858fc2c691df65b1f21f0aa00b99f3fbc661d", size = 1993542, upload-time = "2025-10-14T10:22:11.332Z" },
|
|
405
|
+
{ url = "https://files.pythonhosted.org/packages/8a/ac/9fc61b4f9d079482a290afe8d206b8f490e9fd32d4fc03ed4fc698214e01/pydantic_core-2.41.4-cp314-cp314t-win_arm64.whl", hash = "sha256:d34f950ae05a83e0ede899c595f312ca976023ea1db100cd5aa188f7005e3ab0", size = 1973897, upload-time = "2025-10-14T10:22:13.444Z" },
|
|
406
406
|
]
|
|
407
407
|
|
|
408
408
|
[[package]]
|
amati-0.2.29/tests/test_amati.py
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Tests amati/amati.py, especially the args.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
import os
|
|
6
|
-
import subprocess
|
|
7
|
-
|
|
8
|
-
import pytest
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def test_specifc_spec():
|
|
12
|
-
subprocess.run(
|
|
13
|
-
[
|
|
14
|
-
"python",
|
|
15
|
-
"amati/amati.py",
|
|
16
|
-
"-s",
|
|
17
|
-
"tests/data/openapi.yaml",
|
|
18
|
-
"--consistency-check",
|
|
19
|
-
],
|
|
20
|
-
check=True,
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def test_gzip():
|
|
25
|
-
subprocess.run(
|
|
26
|
-
[
|
|
27
|
-
"python",
|
|
28
|
-
"amati/amati.py",
|
|
29
|
-
"-s",
|
|
30
|
-
"tests/data/openapi.yaml.gz",
|
|
31
|
-
"--consistency-check",
|
|
32
|
-
],
|
|
33
|
-
check=True,
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def test_discover_without_directory_failure():
|
|
38
|
-
with pytest.raises(subprocess.CalledProcessError):
|
|
39
|
-
subprocess.run(["python", "amati/amati.py", "--consistency-check"], check=True)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def test_discover_without_directory_success():
|
|
43
|
-
os.chdir("tests/data")
|
|
44
|
-
subprocess.run(
|
|
45
|
-
["python", "../../amati/amati.py", "--consistency-check"], check=True
|
|
46
|
-
)
|
|
47
|
-
os.chdir("../../")
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def test_discover_with_directory():
|
|
51
|
-
subprocess.run(["python", "amati/amati.py", "-d", "tests/data/"], check=True)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|