11
11
from collections import defaultdict
12
12
from inspect import isclass
13
13
from itertools import chain
14
+ from json import load as json_load
14
15
from pathlib import Path
15
- from typing import TYPE_CHECKING , Any , Optional , Union
16
+ from typing import TYPE_CHECKING , Any , Literal , Optional , Union
16
17
17
- import yaml
18
18
from pydantic import BaseModel , ConfigDict , RootModel , ValidationError , ValidationInfo , field_validator , model_serializer , model_validator
19
19
from pydantic .types import ImportString
20
20
from pydantic_core import PydanticCustomError
21
- from yaml import YAMLError , safe_load
21
+ from yaml import YAMLError , safe_dump , safe_load
22
22
23
23
from anta .logger import anta_log_exception
24
24
from anta .models import AntaTest
@@ -239,7 +239,16 @@ def yaml(self) -> str:
239
239
# This could be improved.
240
240
# https://github.com/pydantic/pydantic/issues/1043
241
241
# Explore if this worth using this: https://github.com/NowanIlfideme/pydantic-yaml
242
- return yaml .safe_dump (yaml .safe_load (self .model_dump_json (serialize_as_any = True , exclude_unset = True )), indent = 2 , width = math .inf )
242
+ return safe_dump (safe_load (self .model_dump_json (serialize_as_any = True , exclude_unset = True )), indent = 2 , width = math .inf )
243
+
244
+ def to_json (self ) -> str :
245
+ """Return a JSON representation string of this model.
246
+
247
+ Returns
248
+ -------
249
+ The JSON representation string of this model.
250
+ """
251
+ return self .model_dump_json (serialize_as_any = True , exclude_unset = True , indent = 2 )
243
252
244
253
245
254
class AntaCatalog :
@@ -255,8 +264,8 @@ def __init__(
255
264
) -> None :
256
265
"""Instantiate an AntaCatalog instance.
257
266
258
- Args:
259
- ----
267
+ Parameters
268
+ ----------
260
269
tests: A list of AntaTestDefinition instances.
261
270
filename: The path from which the catalog is loaded.
262
271
@@ -299,19 +308,24 @@ def tests(self, value: list[AntaTestDefinition]) -> None:
299
308
self ._tests = value
300
309
301
310
@staticmethod
302
- def parse (filename : str | Path ) -> AntaCatalog :
311
+ def parse (filename : str | Path , file_format : Literal [ "yaml" , "json" ] = "yaml" ) -> AntaCatalog :
303
312
"""Create an AntaCatalog instance from a test catalog file.
304
313
305
- Args:
306
- ----
307
- filename: Path to test catalog YAML file
314
+ Parameters
315
+ ----------
316
+ filename: Path to test catalog YAML or JSON fil
317
+ file_format: Format of the file, either 'yaml' or 'json'
308
318
309
319
"""
320
+ if file_format not in ["yaml" , "json" ]:
321
+ message = f"'{ file_format } ' is not a valid format for an AntaCatalog file. Only 'yaml' and 'json' are supported."
322
+ raise ValueError (message )
323
+
310
324
try :
311
325
file : Path = filename if isinstance (filename , Path ) else Path (filename )
312
326
with file .open (encoding = "UTF-8" ) as f :
313
- data = safe_load (f )
314
- except (TypeError , YAMLError , OSError ) as e :
327
+ data = safe_load (f ) if file_format == "yaml" else json_load ( f )
328
+ except (TypeError , YAMLError , OSError , ValueError ) as e :
315
329
message = f"Unable to parse ANTA Test Catalog file '{ filename } '"
316
330
anta_log_exception (e , message , logger )
317
331
raise
@@ -326,8 +340,8 @@ def from_dict(data: RawCatalogInput, filename: str | Path | None = None) -> Anta
326
340
It is the data structure returned by `yaml.load()` function of a valid
327
341
YAML Test Catalog file.
328
342
329
- Args:
330
- ----
343
+ Parameters
344
+ ----------
331
345
data: Python dictionary used to instantiate the AntaCatalog instance
332
346
filename: value to be set as AntaCatalog instance attribute
333
347
@@ -360,8 +374,8 @@ def from_list(data: ListAntaTestTuples) -> AntaCatalog:
360
374
361
375
See ListAntaTestTuples type alias for details.
362
376
363
- Args:
364
- ----
377
+ Parameters
378
+ ----------
365
379
data: Python list used to instantiate the AntaCatalog instance
366
380
367
381
"""
@@ -378,8 +392,8 @@ def from_list(data: ListAntaTestTuples) -> AntaCatalog:
378
392
def merge (catalogs : list [AntaCatalog ]) -> AntaCatalog :
379
393
"""Merge multiple AntaCatalog instances.
380
394
381
- Args:
382
- ----
395
+ Parameters
396
+ ----------
383
397
catalogs: List of AntaCatalog instances to merge.
384
398
385
399
Returns
@@ -431,8 +445,8 @@ def build_indexes(self, filtered_tests: set[str] | None = None) -> None:
431
445
def get_tests_by_tags (self , tags : set [str ], * , strict : bool = False ) -> set [AntaTestDefinition ]:
432
446
"""Return all tests that match a given set of tags, according to the specified strictness.
433
447
434
- Args:
435
- ----
448
+ Parameters
449
+ ----------
436
450
tags: The tags to filter tests by. If empty, return all tests without tags.
437
451
strict: If True, returns only tests that contain all specified tags (intersection).
438
452
If False, returns tests that contain any of the specified tags (union).
0 commit comments