amati 0.2.1__tar.gz → 0.2.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.
Files changed (86) hide show
  1. {amati-0.2.1 → amati-0.2.2}/.github/workflows/publish.yaml +1 -1
  2. {amati-0.2.1 → amati-0.2.2}/PKG-INFO +1 -1
  3. {amati-0.2.1 → amati-0.2.2}/amati/amati.py +37 -14
  4. {amati-0.2.1 → amati-0.2.2}/amati/logging.py +1 -1
  5. {amati-0.2.1 → amati-0.2.2}/amati/model_validators.py +32 -32
  6. {amati-0.2.1 → amati-0.2.2}/amati/validators/generic.py +3 -3
  7. {amati-0.2.1 → amati-0.2.2}/amati/validators/oas304.py +4 -4
  8. {amati-0.2.1 → amati-0.2.2}/amati/validators/oas311.py +5 -5
  9. {amati-0.2.1 → amati-0.2.2}/bin/checks.sh +1 -1
  10. {amati-0.2.1 → amati-0.2.2}/pyproject.toml +1 -1
  11. {amati-0.2.1 → amati-0.2.2}/tests/model_validators/test_all_of.py +63 -63
  12. {amati-0.2.1 → amati-0.2.2}/tests/model_validators/test_at_least_one.py +51 -51
  13. {amati-0.2.1 → amati-0.2.2}/tests/model_validators/test_if_then.py +57 -57
  14. {amati-0.2.1 → amati-0.2.2}/tests/model_validators/test_only_one.py +43 -43
  15. {amati-0.2.1 → amati-0.2.2}/tests/test_logging.py +5 -5
  16. {amati-0.2.1 → amati-0.2.2}/tests/validators/test_generic.py +11 -11
  17. {amati-0.2.1 → amati-0.2.2}/tests/validators/test_licence_object.py +24 -24
  18. {amati-0.2.1 → amati-0.2.2}/tests/validators/test_security_scheme_object.py +33 -33
  19. {amati-0.2.1 → amati-0.2.2}/tests/validators/test_server_variable_object.py +7 -7
  20. {amati-0.2.1 → amati-0.2.2}/uv.lock +1 -1
  21. {amati-0.2.1 → amati-0.2.2}/.dockerignore +0 -0
  22. {amati-0.2.1 → amati-0.2.2}/.github/dependabot.yml +0 -0
  23. {amati-0.2.1 → amati-0.2.2}/.github/workflows/checks.yaml +0 -0
  24. {amati-0.2.1 → amati-0.2.2}/.github/workflows/codeql.yml +0 -0
  25. {amati-0.2.1 → amati-0.2.2}/.github/workflows/coverage.yaml +0 -0
  26. {amati-0.2.1 → amati-0.2.2}/.gitignore +0 -0
  27. {amati-0.2.1 → amati-0.2.2}/.pre-commit-config.yaml +0 -0
  28. {amati-0.2.1 → amati-0.2.2}/.pylintrc +0 -0
  29. {amati-0.2.1 → amati-0.2.2}/.python-version +0 -0
  30. {amati-0.2.1 → amati-0.2.2}/Dockerfile +0 -0
  31. {amati-0.2.1 → amati-0.2.2}/LICENSE +0 -0
  32. {amati-0.2.1 → amati-0.2.2}/README.md +0 -0
  33. {amati-0.2.1 → amati-0.2.2}/TEMPLATE.html +0 -0
  34. {amati-0.2.1 → amati-0.2.2}/amati/__init__.py +0 -0
  35. {amati-0.2.1 → amati-0.2.2}/amati/_error_handler.py +0 -0
  36. {amati-0.2.1 → amati-0.2.2}/amati/_resolve_forward_references.py +0 -0
  37. {amati-0.2.1 → amati-0.2.2}/amati/data/http-status-codes.json +0 -0
  38. {amati-0.2.1 → amati-0.2.2}/amati/data/iso9110.json +0 -0
  39. {amati-0.2.1 → amati-0.2.2}/amati/data/media-types.json +0 -0
  40. {amati-0.2.1 → amati-0.2.2}/amati/data/schemes.json +0 -0
  41. {amati-0.2.1 → amati-0.2.2}/amati/data/spdx-licences.json +0 -0
  42. {amati-0.2.1 → amati-0.2.2}/amati/data/tlds.json +0 -0
  43. {amati-0.2.1 → amati-0.2.2}/amati/exceptions.py +0 -0
  44. {amati-0.2.1 → amati-0.2.2}/amati/fields/__init__.py +0 -0
  45. {amati-0.2.1 → amati-0.2.2}/amati/fields/_custom_types.py +0 -0
  46. {amati-0.2.1 → amati-0.2.2}/amati/fields/commonmark.py +0 -0
  47. {amati-0.2.1 → amati-0.2.2}/amati/fields/email.py +0 -0
  48. {amati-0.2.1 → amati-0.2.2}/amati/fields/http_status_codes.py +0 -0
  49. {amati-0.2.1 → amati-0.2.2}/amati/fields/iso9110.py +0 -0
  50. {amati-0.2.1 → amati-0.2.2}/amati/fields/json.py +0 -0
  51. {amati-0.2.1 → amati-0.2.2}/amati/fields/media.py +0 -0
  52. {amati-0.2.1 → amati-0.2.2}/amati/fields/oas.py +0 -0
  53. {amati-0.2.1 → amati-0.2.2}/amati/fields/spdx_licences.py +0 -0
  54. {amati-0.2.1 → amati-0.2.2}/amati/fields/uri.py +0 -0
  55. {amati-0.2.1 → amati-0.2.2}/amati/file_handler.py +0 -0
  56. {amati-0.2.1 → amati-0.2.2}/amati/grammars/oas.py +0 -0
  57. {amati-0.2.1 → amati-0.2.2}/amati/grammars/rfc6901.py +0 -0
  58. {amati-0.2.1 → amati-0.2.2}/amati/grammars/rfc7159.py +0 -0
  59. {amati-0.2.1 → amati-0.2.2}/amati/validators/__init__.py +0 -0
  60. {amati-0.2.1 → amati-0.2.2}/bin/startup.sh +0 -0
  61. {amati-0.2.1 → amati-0.2.2}/scripts/data/http_status_code.py +0 -0
  62. {amati-0.2.1 → amati-0.2.2}/scripts/data/iso9110.py +0 -0
  63. {amati-0.2.1 → amati-0.2.2}/scripts/data/media_types.py +0 -0
  64. {amati-0.2.1 → amati-0.2.2}/scripts/data/schemes.py +0 -0
  65. {amati-0.2.1 → amati-0.2.2}/scripts/data/spdx_licences.py +0 -0
  66. {amati-0.2.1 → amati-0.2.2}/scripts/data/tlds.py +0 -0
  67. {amati-0.2.1 → amati-0.2.2}/scripts/tests/setup_test_specs.py +0 -0
  68. {amati-0.2.1 → amati-0.2.2}/tests/__init__.py +0 -0
  69. {amati-0.2.1 → amati-0.2.2}/tests/data/.amati.tests.yaml +0 -0
  70. {amati-0.2.1 → amati-0.2.2}/tests/data/DigitalOcean-public.v2.errors.json +0 -0
  71. {amati-0.2.1 → amati-0.2.2}/tests/data/api.github.com.yaml.errors.json +0 -0
  72. {amati-0.2.1 → amati-0.2.2}/tests/data/next-api.github.com.yaml.errors.json +0 -0
  73. {amati-0.2.1 → amati-0.2.2}/tests/data/openapi.yaml +0 -0
  74. {amati-0.2.1 → amati-0.2.2}/tests/data/redocly.openapi.yaml.errors.json +0 -0
  75. {amati-0.2.1 → amati-0.2.2}/tests/fields/__init__.py +0 -0
  76. {amati-0.2.1 → amati-0.2.2}/tests/fields/test_email.py +0 -0
  77. {amati-0.2.1 → amati-0.2.2}/tests/fields/test_http_status_codes.py +0 -0
  78. {amati-0.2.1 → amati-0.2.2}/tests/fields/test_iso9110.py +0 -0
  79. {amati-0.2.1 → amati-0.2.2}/tests/fields/test_media.py +0 -0
  80. {amati-0.2.1 → amati-0.2.2}/tests/fields/test_oas.py +0 -0
  81. {amati-0.2.1 → amati-0.2.2}/tests/fields/test_spdx_licences.py +0 -0
  82. {amati-0.2.1 → amati-0.2.2}/tests/fields/test_uri.py +0 -0
  83. {amati-0.2.1 → amati-0.2.2}/tests/helpers.py +0 -0
  84. {amati-0.2.1 → amati-0.2.2}/tests/test_amati.py +0 -0
  85. {amati-0.2.1 → amati-0.2.2}/tests/test_external_specs.py +0 -0
  86. {amati-0.2.1 → amati-0.2.2}/tests/validators/__init__.py +0 -0
@@ -43,5 +43,5 @@ jobs:
43
43
  context: .
44
44
  push: true
45
45
  tags: |
46
- ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKERHUB_REPO }}:${{ github.event.release.tag_name }}-alpha.1
46
+ ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKERHUB_REPO }}:${{ github.event.release.tag_name }}.1
47
47
  ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKERHUB_REPO }}:alpha
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: amati
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
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
@@ -16,7 +16,7 @@ sys.path.insert(0, str(Path(__file__).parent.parent))
16
16
  from amati._error_handler import handle_errors
17
17
  from amati._resolve_forward_references import resolve_forward_references
18
18
  from amati.file_handler import load_file
19
- from amati.logging import Log, LogMixin
19
+ from amati.logging import Log, Logger
20
20
 
21
21
  type JSONPrimitive = str | int | float | bool | None
22
22
  type JSONArray = list["JSONValue"]
@@ -113,9 +113,9 @@ def run(
113
113
 
114
114
  logs: list[Log] = []
115
115
 
116
- with LogMixin.context():
116
+ with Logger.context():
117
117
  result, errors = dispatch(data)
118
- logs.extend(LogMixin.logs)
118
+ logs.extend(Logger.logs)
119
119
 
120
120
  if errors or logs:
121
121
 
@@ -158,19 +158,38 @@ def run(
158
158
  if result and consistency_check:
159
159
  return check(data, result)
160
160
 
161
+ return True
161
162
 
162
- def discover(discover_dir: str = ".") -> list[Path]:
163
+
164
+ def discover(spec: str, discover_dir: str = ".") -> list[Path]:
163
165
  """
164
166
  Finds OpenAPI Specification files to validate
165
167
 
166
168
  Args:
169
+ spec: The path to a specific OpenAPI specification file.
167
170
  discover_dir: The directory to search through.
168
171
  Returns:
169
- A list of paths to validate.
172
+ A list of specifications to validate.
170
173
  """
171
174
 
172
175
  specs: list[Path] = []
173
176
 
177
+ # If a spec is provided, check if it exists and erorr if not
178
+ if spec:
179
+ spec_path = Path(spec)
180
+
181
+ if not spec_path.exists():
182
+ raise FileNotFoundError(f"File {spec} does not exist.")
183
+
184
+ if not spec_path.is_file():
185
+ raise IsADirectoryError(f"{spec} is a directory, not a file.")
186
+
187
+ specs.append(spec_path)
188
+
189
+ # End early if we're not also trying to find files
190
+ if not discover_dir:
191
+ return specs
192
+
174
193
  if Path("openapi.json").exists():
175
194
  specs.append(Path("openapi.json"))
176
195
 
@@ -258,16 +277,20 @@ if __name__ == "__main__":
258
277
  )
259
278
 
260
279
  args = parser.parse_args()
280
+
281
+ print('Starting amati...')
261
282
 
262
- if args.spec:
263
- specifications: list[Path] = [Path(args.spec)]
264
- else:
265
- specifications = discover(args.discover)
283
+ specifications = discover(args.spec, args.discover)
284
+ print(specifications)
266
285
 
267
286
  for specification in specifications:
268
- if successful_check := run(
287
+ successful_check = run(
269
288
  specification, args.consistency_check, args.local, args.html_report
270
- ):
271
- print("Consistency check successful for {specification}")
272
- else:
273
- print("Consistency check failed for {specification}")
289
+ )
290
+
291
+ if args.consistency_check and successful_check:
292
+ print(f"Consistency check successful for {specification}")
293
+ elif args.consistency_check:
294
+ print(f"Consistency check failed for {specification}")
295
+
296
+ print('completed.')
@@ -18,7 +18,7 @@ class Log(TypedDict):
18
18
  url: NotRequired[str]
19
19
 
20
20
 
21
- class LogMixin:
21
+ class Logger:
22
22
  """
23
23
  A mixin class that provides logging functionality.
24
24
 
@@ -10,7 +10,7 @@ from pydantic._internal._decorators import (
10
10
  PydanticDescriptorProxy,
11
11
  )
12
12
 
13
- from amati.logging import LogMixin
13
+ from amati.logging import Logger
14
14
  from amati.validators.generic import GenericObject
15
15
 
16
16
 
@@ -107,7 +107,7 @@ 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
- >>> LogMixin.logs = []
110
+ >>> Logger.logs = []
111
111
  >>>
112
112
  >>> class User(GenericObject):
113
113
  ... name: str = ""
@@ -116,8 +116,8 @@ def at_least_one_of(
116
116
  ... _reference_uri = "https://example.com"
117
117
  ...
118
118
  >>> user = User()
119
- >>> assert len(LogMixin.logs) == 1
120
- >>> LogMixin.logs = []
119
+ >>> assert len(Logger.logs) == 1
120
+ >>> Logger.logs = []
121
121
 
122
122
  >>> class User(GenericObject):
123
123
  ... name: str = ""
@@ -128,11 +128,11 @@ def at_least_one_of(
128
128
  ...
129
129
  >>>
130
130
  >>> user = User(name="John") # Works fine
131
- >>> assert not LogMixin.logs
131
+ >>> assert not Logger.logs
132
132
  >>> user = User()
133
- >>> assert len(LogMixin.logs) == 1
133
+ >>> assert len(Logger.logs) == 1
134
134
  >>> user = User(age=30)
135
- >>> assert len(LogMixin.logs) == 2
135
+ >>> assert len(Logger.logs) == 2
136
136
 
137
137
 
138
138
  Note:
@@ -157,7 +157,7 @@ def at_least_one_of(
157
157
  public_fields = ", ".join(f"{name}" for name in candidates.keys())
158
158
 
159
159
  msg = f"{public_fields} do not have values, expected at least one."
160
- LogMixin.log(
160
+ Logger.log(
161
161
  {
162
162
  "msg": msg,
163
163
  "type": "value_error",
@@ -191,7 +191,7 @@ def only_one_of(
191
191
  The validator that ensures at one public field is non-empty.
192
192
 
193
193
  Example:
194
- >>> LogMixin.logs = []
194
+ >>> Logger.logs = []
195
195
  >>>
196
196
  >>> class User(GenericObject):
197
197
  ... email: str = ""
@@ -201,10 +201,10 @@ def only_one_of(
201
201
  ...
202
202
  >>> user = User(email="test@example.com") # Works fine
203
203
  >>> user = User(name="123-456-7890") # Works fine
204
- >>> assert not LogMixin.logs
204
+ >>> assert not Logger.logs
205
205
  >>> user = User(email="a@b.com", name="123")
206
- >>> assert LogMixin.logs
207
- >>> LogMixin.logs = []
206
+ >>> assert Logger.logs
207
+ >>> Logger.logs = []
208
208
 
209
209
  >>> class User(GenericObject):
210
210
  ... name: str = ""
@@ -216,11 +216,11 @@ def only_one_of(
216
216
  >>> user = User(name="Bob") # Works fine
217
217
  >>> user = User(email="test@example.com") # Works fine
218
218
  >>> user = User(name="Bob", age=30) # Works fine
219
- >>> assert not LogMixin.logs
219
+ >>> assert not Logger.logs
220
220
  >>> user = User(name="Bob", email="a@b.com")
221
- >>> assert len(LogMixin.logs) == 1
221
+ >>> assert len(Logger.logs) == 1
222
222
  >>> user = User(age=30)
223
- >>> assert len(LogMixin.logs) == 2
223
+ >>> assert len(Logger.logs) == 2
224
224
 
225
225
  Note:
226
226
  Only public fields (not starting with '_') are checked. Private fields
@@ -249,7 +249,7 @@ def only_one_of(
249
249
  field_string = "none"
250
250
  msg = f"Expected at most one field to have a value, {field_string} did"
251
251
 
252
- LogMixin.log(
252
+ Logger.log(
253
253
  {
254
254
  "msg": msg,
255
255
  "type": type_ or "value_error",
@@ -282,7 +282,7 @@ def all_of(
282
282
  The validator that ensures at most one public field is non-empty.
283
283
 
284
284
  Example:
285
- >>> LogMixin.logs = []
285
+ >>> Logger.logs = []
286
286
  >>>
287
287
  >>> class User(GenericObject):
288
288
  ... email: str = ""
@@ -291,11 +291,11 @@ def all_of(
291
291
  ... _reference_uri = "https://example.com"
292
292
  ...
293
293
  >>> user = User(email="a@b.com", name="123") # Works fine
294
- >>> assert not LogMixin.logs
294
+ >>> assert not Logger.logs
295
295
  >>> user = User(email="test@example.com")
296
- >>> assert len(LogMixin.logs) == 1
296
+ >>> assert len(Logger.logs) == 1
297
297
  >>> user = User(name="123-456-7890")
298
- >>> assert len(LogMixin.logs) == 2
298
+ >>> assert len(Logger.logs) == 2
299
299
 
300
300
  >>> class User(GenericObject):
301
301
  ... name: str = ""
@@ -304,17 +304,17 @@ def all_of(
304
304
  ... _all_of = all_of(["name", "email"])
305
305
  ... _reference_uri = "https://example.com"
306
306
  ...
307
- >>> LogMixin.logs = []
307
+ >>> Logger.logs = []
308
308
  >>> user = User(name="Bob", email="a@b.com") # Works fine
309
- >>> assert not LogMixin.logs
309
+ >>> assert not Logger.logs
310
310
  >>> user = User(name="Bob")
311
- >>> assert len(LogMixin.logs) == 1
311
+ >>> assert len(Logger.logs) == 1
312
312
  >>> user = User(email="test@example.com")
313
- >>> assert len(LogMixin.logs) == 2
313
+ >>> assert len(Logger.logs) == 2
314
314
  >>> user = User(age=30)
315
- >>> assert len(LogMixin.logs) == 3
315
+ >>> assert len(Logger.logs) == 3
316
316
  >>> user = User(name="Bob", age=30)
317
- >>> assert len(LogMixin.logs) == 4
317
+ >>> assert len(Logger.logs) == 4
318
318
 
319
319
  Note:
320
320
  Only public fields (not starting with '_') are checked. Private fields
@@ -339,7 +339,7 @@ def all_of(
339
339
  if falsy:
340
340
  msg = f"Expected at all fields to have a value, {", ".join(falsy)} did not"
341
341
 
342
- LogMixin.log(
342
+ Logger.log(
343
343
  {
344
344
  "msg": msg,
345
345
  "type": "value_error",
@@ -378,7 +378,7 @@ def if_then(
378
378
  ValueError: If a condition and consequence are not present
379
379
 
380
380
  Example:
381
- >>> LogMixin.logs = []
381
+ >>> Logger.logs = []
382
382
  >>>
383
383
  >>> class User(GenericObject):
384
384
  ... role: str = ""
@@ -390,11 +390,11 @@ def if_then(
390
390
  ... _reference_uri = "https://example.com"
391
391
  ...
392
392
  >>> user = User(role="admin", can_edit=True) # Works fine
393
- >>> assert not LogMixin.logs
393
+ >>> assert not Logger.logs
394
394
  >>> user = User(role="admin", can_edit=False) # Fails validation
395
- >>> assert len(LogMixin.logs) == 1
395
+ >>> assert len(Logger.logs) == 1
396
396
  >>> user = User(role="user", can_edit=False) # Works fine
397
- >>> assert len(LogMixin.logs) == 1
397
+ >>> assert len(Logger.logs) == 1
398
398
  """
399
399
 
400
400
  @model_validator(mode="after")
@@ -431,7 +431,7 @@ def if_then(
431
431
  if value == actual:
432
432
  continue
433
433
 
434
- LogMixin.log(
434
+ Logger.log(
435
435
  {
436
436
  "msg": f"Expected {field} to be {"in " if iterable else ""}"
437
437
  f"{value} found {actual}",
@@ -20,7 +20,7 @@ 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.logging import LogMixin
23
+ from amati.logging import Logger
24
24
 
25
25
 
26
26
  class GenericObject(BaseModel):
@@ -47,7 +47,7 @@ class GenericObject(BaseModel):
47
47
  and field not in self.get_field_aliases()
48
48
  ):
49
49
  message = f"{field} is not a valid field for {self.__repr_name__()}."
50
- LogMixin.log(
50
+ Logger.log(
51
51
  {
52
52
  "msg": message,
53
53
  "type": "value_error",
@@ -79,7 +79,7 @@ class GenericObject(BaseModel):
79
79
 
80
80
  for field in excess_fields:
81
81
  message = f"{field} is not a valid field for {self.__repr_name__()}."
82
- LogMixin.log(
82
+ Logger.log(
83
83
  {
84
84
  "msg": message,
85
85
  "type": "value_error",
@@ -42,7 +42,7 @@ from amati.fields import (
42
42
  from amati.fields.commonmark import CommonMark
43
43
  from amati.fields.json import JSON
44
44
  from amati.fields.oas import OpenAPI, RuntimeExpression
45
- from amati.logging import LogMixin
45
+ from amati.logging import Logger
46
46
  from amati.validators.generic import GenericObject, allow_extra_fields
47
47
 
48
48
  type JSONPrimitive = str | int | float | bool | None
@@ -179,7 +179,7 @@ class ServerVariableObject(GenericObject):
179
179
  return self
180
180
 
181
181
  if self.default not in self.enum:
182
- LogMixin.log(
182
+ Logger.log(
183
183
  {
184
184
  "msg": f"The default value {self.default} is not in the enum list {self.enum}", # pylint: disable=line-too-long
185
185
  "type": "warning",
@@ -647,7 +647,7 @@ class XMLObject(GenericObject):
647
647
  """
648
648
  if value.type == URIType.RELATIVE:
649
649
  message = "XML namespace {value} cannot be a relative URI"
650
- LogMixin.log(
650
+ Logger.log(
651
651
  {
652
652
  "msg": message,
653
653
  "type": "value_error",
@@ -726,7 +726,7 @@ class SchemaObject(GenericObject):
726
726
  # This will validate the structure conforms to JSON Schema
727
727
  validator_cls(meta_schema).validate(schema_dict) # type: ignore
728
728
  except JSONVSchemeValidationError as e:
729
- LogMixin.log(
729
+ Logger.log(
730
730
  {
731
731
  "msg": f"Invalid JSON Schema: {e.message}",
732
732
  "type": "value_error",
@@ -37,7 +37,7 @@ from amati.fields.commonmark import CommonMark
37
37
  from amati.fields.json import JSON
38
38
  from amati.fields.oas import OpenAPI
39
39
  from amati.fields.spdx_licences import VALID_LICENCES
40
- from amati.logging import LogMixin
40
+ from amati.logging import Logger
41
41
  from amati.validators.generic import GenericObject, allow_extra_fields
42
42
  from amati.validators.oas304 import (
43
43
  CallbackObject,
@@ -105,7 +105,7 @@ class LicenceObject(GenericObject):
105
105
  try:
106
106
  SPDXURL(self.url)
107
107
  except AmatiValueError:
108
- LogMixin.log(
108
+ Logger.log(
109
109
  {
110
110
  "msg": f"{str(self.url)} is not a valid SPDX URL",
111
111
  "type": "warning",
@@ -122,7 +122,7 @@ class LicenceObject(GenericObject):
122
122
  and self.identifier
123
123
  and str(self.url) not in VALID_LICENCES[self.identifier]
124
124
  ):
125
- LogMixin.log(
125
+ Logger.log(
126
126
  {
127
127
  "msg": f"{self.url} is not associated with the identifier {self.identifier}", # pylint: disable=line-too-long
128
128
  "type": "warning",
@@ -214,7 +214,7 @@ class ServerVariableObject(GenericObject):
214
214
  return self
215
215
 
216
216
  if self.default not in self.enum:
217
- LogMixin.log(
217
+ Logger.log(
218
218
  {
219
219
  "msg": f"The default value {self.default} is not in the enum list {self.enum}", # pylint: disable=line-too-long
220
220
  "type": "value_error",
@@ -396,7 +396,7 @@ class SchemaObject(GenericObject):
396
396
  # This will validate the structure conforms to JSON Schema
397
397
  validator_cls(meta_schema).validate(schema_dict) # type: ignore
398
398
  except JSONVSchemeValidationError as e:
399
- LogMixin.log(
399
+ Logger.log(
400
400
  {
401
401
  "msg": f"Invalid JSON Schema: {e.message}",
402
402
  "type": "value_error",
@@ -6,5 +6,5 @@ pytest --cov-report term-missing --cov=amati tests
6
6
  pytest --doctest-modules amati/
7
7
  docker build -t amati -f Dockerfile .
8
8
  cd tests/
9
- docker run --detach -v "$(pwd):/data" amati -d /data
9
+ docker run --detach -v "$(pwd):/data" amati -d /data -cc
10
10
  cd ../
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "amati"
3
- version = "0.2.1"
3
+ version = "0.2.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 = [
@@ -10,7 +10,7 @@ from hypothesis import strategies as st
10
10
  from pydantic import BaseModel
11
11
 
12
12
  from amati import model_validators as mv
13
- from amati.logging import LogMixin
13
+ from amati.logging import Logger
14
14
  from tests.helpers import text_excluding_empty_string
15
15
 
16
16
  MIN = int(float_info.min)
@@ -38,9 +38,9 @@ class AllWithRestrictions(BaseModel):
38
38
 
39
39
 
40
40
  def test_empty_object():
41
- with LogMixin.context():
41
+ with Logger.context():
42
42
  EmptyObject()
43
- assert not LogMixin.logs
43
+ assert not Logger.logs
44
44
 
45
45
 
46
46
  # Using a min_value forces integers to be not-None
@@ -56,71 +56,71 @@ def test_all_of_no_restrictions(name: str, age: int, music: list[int]):
56
56
  model = AllNoRestrictions(name=name, age=age, music=music)
57
57
  assert model.name and model.age == age and model.music
58
58
 
59
- with LogMixin.context():
59
+ with Logger.context():
60
60
  AllNoRestrictions(name=None, age=age, music=music)
61
- assert LogMixin.logs
61
+ assert Logger.logs
62
62
 
63
- with LogMixin.context():
63
+ with Logger.context():
64
64
  AllNoRestrictions(name=name, age=None, music=music)
65
- assert LogMixin.logs
65
+ assert Logger.logs
66
66
 
67
- with LogMixin.context():
67
+ with Logger.context():
68
68
  AllNoRestrictions(name=name, age=age, music=None)
69
- assert LogMixin.logs
69
+ assert Logger.logs
70
70
 
71
- with LogMixin.context():
71
+ with Logger.context():
72
72
  AllNoRestrictions(name=None, age=None, music=music)
73
- assert LogMixin.logs
73
+ assert Logger.logs
74
74
 
75
- with LogMixin.context():
75
+ with Logger.context():
76
76
  AllNoRestrictions(name=name, age=None, music=None)
77
- assert LogMixin.logs
77
+ assert Logger.logs
78
78
 
79
- with LogMixin.context():
79
+ with Logger.context():
80
80
  AllNoRestrictions(name=None, age=age, music=None)
81
- assert LogMixin.logs
81
+ assert Logger.logs
82
82
 
83
83
  # Tests with falsy values
84
- with LogMixin.context():
84
+ with Logger.context():
85
85
  AllNoRestrictions(name="", age=age, music=music)
86
- assert LogMixin.logs
86
+ assert Logger.logs
87
87
 
88
- with LogMixin.context():
88
+ with Logger.context():
89
89
  AllNoRestrictions(name=name, age=None, music=music)
90
- assert LogMixin.logs
90
+ assert Logger.logs
91
91
 
92
- with LogMixin.context():
92
+ with Logger.context():
93
93
  AllNoRestrictions(name=name, age=age, music=[])
94
- assert LogMixin.logs
94
+ assert Logger.logs
95
95
 
96
- with LogMixin.context():
96
+ with Logger.context():
97
97
  AllNoRestrictions(name="", age=None, music=music)
98
- assert LogMixin.logs
98
+ assert Logger.logs
99
99
 
100
- with LogMixin.context():
100
+ with Logger.context():
101
101
  AllNoRestrictions(name=name, age=None, music=[])
102
- assert LogMixin.logs
102
+ assert Logger.logs
103
103
 
104
- with LogMixin.context():
104
+ with Logger.context():
105
105
  AllNoRestrictions(name="", age=age, music=[])
106
- assert LogMixin.logs
106
+ assert Logger.logs
107
107
 
108
108
  # Test when no fields are provided
109
- with LogMixin.context():
109
+ with Logger.context():
110
110
  AllNoRestrictions(name=None, age=None, music=None)
111
- assert LogMixin.logs
111
+ assert Logger.logs
112
112
 
113
- with LogMixin.context():
113
+ with Logger.context():
114
114
  AllNoRestrictions(name="", age=None, music=None)
115
- assert LogMixin.logs
115
+ assert Logger.logs
116
116
 
117
- with LogMixin.context():
117
+ with Logger.context():
118
118
  AllNoRestrictions(name=None, age=None, music=[])
119
- assert LogMixin.logs
119
+ assert Logger.logs
120
120
 
121
- with LogMixin.context():
121
+ with Logger.context():
122
122
  AllNoRestrictions(name="", age=None, music=[])
123
- assert LogMixin.logs
123
+ assert Logger.logs
124
124
 
125
125
 
126
126
  # Using a min_value forces integers to be not-None
@@ -137,66 +137,66 @@ def test_all_of_with_restrictions(name: str, age: int, music: list[int]):
137
137
  model = AllWithRestrictions(name=name, age=age, music=music)
138
138
  assert model.name and model.age == age and model.music
139
139
 
140
- with LogMixin.context():
140
+ with Logger.context():
141
141
  AllWithRestrictions(name=None, age=age, music=music)
142
- assert LogMixin.logs
142
+ assert Logger.logs
143
143
 
144
- with LogMixin.context():
144
+ with Logger.context():
145
145
  AllWithRestrictions(name=name, age=None, music=music)
146
- assert LogMixin.logs
146
+ assert Logger.logs
147
147
 
148
148
  model = AllWithRestrictions(name=name, age=age, music=None)
149
149
  assert model.name and model.age == age
150
150
 
151
- with LogMixin.context():
151
+ with Logger.context():
152
152
  model = AllWithRestrictions(name=None, age=None, music=music)
153
- assert LogMixin.logs
153
+ assert Logger.logs
154
154
 
155
- with LogMixin.context():
155
+ with Logger.context():
156
156
  AllWithRestrictions(name=name, age=None, music=None)
157
- assert LogMixin.logs
157
+ assert Logger.logs
158
158
 
159
- with LogMixin.context():
159
+ with Logger.context():
160
160
  AllWithRestrictions(name=None, age=age, music=None)
161
- assert LogMixin.logs
161
+ assert Logger.logs
162
162
 
163
163
  # Tests with falsy values
164
- with LogMixin.context():
164
+ with Logger.context():
165
165
  AllWithRestrictions(name="", age=age, music=music)
166
- assert LogMixin.logs
166
+ assert Logger.logs
167
167
 
168
- with LogMixin.context():
168
+ with Logger.context():
169
169
  AllWithRestrictions(name=name, age=None, music=music)
170
- assert LogMixin.logs
170
+ assert Logger.logs
171
171
 
172
172
  model = AllWithRestrictions(name=name, age=age, music=[])
173
173
  assert model.name and model.age == age
174
174
 
175
- with LogMixin.context():
175
+ with Logger.context():
176
176
  model = AllWithRestrictions(name="", age=None, music=music)
177
- assert LogMixin.logs
177
+ assert Logger.logs
178
178
 
179
- with LogMixin.context():
179
+ with Logger.context():
180
180
  AllWithRestrictions(name=name, age=None, music=[])
181
- assert LogMixin.logs
181
+ assert Logger.logs
182
182
 
183
- with LogMixin.context():
183
+ with Logger.context():
184
184
  AllWithRestrictions(name="", age=age, music=[])
185
- assert LogMixin.logs
185
+ assert Logger.logs
186
186
 
187
187
  # Test when no fields are provided
188
- with LogMixin.context():
188
+ with Logger.context():
189
189
  AllNoRestrictions(name=None, age=None, music=None)
190
- assert LogMixin.logs
190
+ assert Logger.logs
191
191
 
192
- with LogMixin.context():
192
+ with Logger.context():
193
193
  AllNoRestrictions(name="", age=None, music=None)
194
- assert LogMixin.logs
194
+ assert Logger.logs
195
195
 
196
- with LogMixin.context():
196
+ with Logger.context():
197
197
  AllNoRestrictions(name=None, age=None, music=[])
198
- assert LogMixin.logs
198
+ assert Logger.logs
199
199
 
200
- with LogMixin.context():
200
+ with Logger.context():
201
201
  AllNoRestrictions(name="", age=None, music=[])
202
- assert LogMixin.logs
202
+ assert Logger.logs