11import yaml
2+
23import sys
4+ from pathlib import Path
5+
6+ # Add parent directory to sys.path
7+ parent = Path (__file__ ).resolve ().parent .parent
8+ sys .path .insert (0 , str (parent ))
9+
10+ # Now you can import normally
11+ from yaml_to_html import default_columns as REQUIRED_FIELDS
312
4- # Define the required fields your YAML must have
5- REQUIRED_FIELDS = [
6- "name" ,
7- "suite/generator/single" ,
8- "objectives" ,
9- "dimensionality" ,
10- "variable type" ,
11- "constraints" ,
12- "dynamic" ,
13- "noise" ,
14- "multimodal" ,
15- "multi-fidelity" ,
16- "reference" ,
17- "implementation" ,
18- "source (real-world/artificial)" ,
19- "textual description" ,
20- ]
21-
22- UNIQUE_FIELDS = ["name" , "reference" , "implementation" ]
13+ OPTIONAL_FIELDS = ["multimodal" ]
14+ UNIQUE_FIELDS = ["name" ]
15+ UNIQUE_WARNING_FIELDS = ["reference" , "implementation" ]
2316PROBLEMS_FILE = "problems.yaml"
2417
2518
@@ -29,34 +22,48 @@ def read_data(filepath):
2922 data = yaml .safe_load (f )
3023 return 0 , data
3124 except FileNotFoundError :
32- print (f"File not found: { filepath } " )
25+ print (f"::error:: File not found: { filepath } " )
3326 return 1 , None
3427 except yaml .YAMLError as e :
35- print (f"YAML syntax error: { e } " )
28+ print (f"::error:: YAML syntax error: { e } " )
3629 return 1 , None
3730
3831
3932def check_format (data ):
40- if len (data ) != 1 :
41- print ("YAML file should contain exactly one top-level document." )
42- return False
43- if not isinstance (data [0 ], dict ):
44- print ("Top-level document should be a dictionary." )
33+ num_problems = len (data )
34+ if len (data ) < 1 :
35+ print ("::error::YAML file should contain at least one top level entry." )
4536 return False
37+ print (f"::notice::YAML file contains { num_problems } top-level entries." )
38+ unique_fields = []
39+ for i , entry in enumerate (data ):
40+ if not isinstance (entry , dict ):
41+ print (f"::error::Entry { i } is not a dictionary." )
42+ return False
43+ unique_fields .append ({k : v for k , v in entry .items () if k in UNIQUE_FIELDS })
44+ for k in UNIQUE_FIELDS :
45+ values = [entry [k ] for entry in unique_fields ]
46+ if len (values ) != len (set (values )):
47+ print (f"::error::Field '{ k } ' must be unique across all entries." )
48+ return False
4649 return True
4750
4851
4952def check_fields (data ):
50- if len (data ) != len (REQUIRED_FIELDS ):
51- print (f"YAML file should contain exactly { len (REQUIRED_FIELDS )} fields." )
52- return False
5353 missing = [field for field in REQUIRED_FIELDS if field not in data ]
5454 if missing :
55- print (f"Missing required fields: { ', ' .join (missing )} " )
55+ print (f"::error:: Missing required fields: { ', ' .join (missing )} " )
5656 return False
57+ new_fields = [
58+ field for field in data if field not in REQUIRED_FIELDS + OPTIONAL_FIELDS
59+ ]
60+ if new_fields :
61+ print (f"::warning::New field added: { ', ' .join (new_fields )} " )
5762 # Check that the name is not still template
5863 if data .get ("name" ) == "template" :
59- print ("Please change the 'name' field from 'template' to a unique name." )
64+ print (
65+ "::error::Please change the 'name' field from 'template' to a unique name."
66+ )
6067 return False
6168 return True
6269
@@ -65,18 +72,24 @@ def check_novelty(data):
6572 # Load existing problems
6673 read_status , existing_data = read_data (PROBLEMS_FILE )
6774 if read_status != 0 :
68- print ("Could not read existing problems for novelty check." )
75+ print ("::eror:: Could not read existing problems for novelty check." )
6976 return False
7077 assert existing_data is not None
71- for field in UNIQUE_FIELDS :
78+ for field in UNIQUE_FIELDS or UNIQUE_WARNING_FIELDS :
7279 existing_values = {
7380 entry .get (field ) for entry in existing_data if isinstance (entry , dict )
7481 }
7582 if data .get (field ) in existing_values :
76- print (
77- f"Field '{ field } ' with value '{ data .get (field )} ' already exists. Please choose a unique value."
78- )
79- return False
83+ if field in UNIQUE_WARNING_FIELDS :
84+ print (
85+ f"::warning::Field '{ field } ' with value '{ data .get (field )} ' already exists. Consider choosing a unique value."
86+ )
87+ continue
88+ elif field in UNIQUE_FIELDS :
89+ print (
90+ f"::error::Field '{ field } ' with value '{ data .get (field )} ' already exists. Please choose a unique value."
91+ )
92+ return False
8093 return True
8194
8295
@@ -86,12 +99,13 @@ def validate_yaml(filepath):
8699 sys .exit (1 )
87100 if not check_format (data ):
88101 sys .exit (1 )
89- assert data is not None and len (data ) == 1
90- new_data = data [0 ] # Extract the single top-level entry
102+ assert data is not None
91103
92- # Check required and unique fields
93- if not check_fields (new_data ) or not check_novelty (new_data ):
94- sys .exit (1 )
104+ for i , new_data in enumerate (data ): # Iterate through each top-level entry
105+ # Check required and unique fields
106+ if not check_fields (new_data ) or not check_novelty (new_data ):
107+ print (f"::error::Validation failed for entry { i + 1 } ." )
108+ sys .exit (1 )
95109
96110 # YAML is valid if we reach this point
97111 print ("YAML syntax is valid." )
@@ -100,7 +114,7 @@ def validate_yaml(filepath):
100114
101115if __name__ == "__main__" :
102116 if len (sys .argv ) < 2 :
103- print ("Usage: python validate_yaml.py <yourfile.yaml>" )
117+ print ("::error:: Usage: python validate_yaml.py <yourfile.yaml>" )
104118 sys .exit (1 )
105119
106120 filepath = sys .argv [1 ]
0 commit comments