@@ -77,6 +77,27 @@ def build():
7777 yield " " * 8 + "^" * len (d ["line" ].rstrip ())
7878
7979 return "\n " .join (build ())
80+
81+ class HeaderFieldError (ValidationError ):
82+ def __init__ (self , field , found_len , expected_len ):
83+ self .field = field
84+ self .found_len = found_len
85+ self .expected_len = expected_len
86+
87+ def asdict (self , with_message = True ):
88+ return {
89+ "type" : "invalid_header_field" ,
90+ "field" : self .field ,
91+ "expected_field_count" : self .expected_len ,
92+ "actual_field_count" : self .found_len ,
93+ ** ({"message" : str (self )} if with_message else {}),
94+ }
95+
96+ def __str__ (self ):
97+ return (
98+ f"Invalid number of parameters for HEADER field '{ self .field } '. "
99+ f"Expected { self .expected_len } , found { self .found_len } ."
100+ )
80101
81102
82103grammar = r"""
@@ -91,9 +112,9 @@ def build():
91112simple_record_list:simple_record simple_record*
92113simple_record: keyword "("parameter_list?")"
93114header_entity_list: file_description file_name file_schema
94- file_description: "FILE_DESCRIPTION" "(" parameter "," parameter ")" ";"
95- file_name: "FILE_NAME" "(" parameter "," parameter "," parameter "," parameter "," parameter "," parameter "," parameter ")" ";"
96- file_schema: "FILE_SCHEMA" "(" parameter ")" ";"
115+ file_description: "FILE_DESCRIPTION" "(" parameter_list ")" ";"
116+ file_name: "FILE_NAME" "(" parameter_list ")" ";"
117+ file_schema: "FILE_SCHEMA" "(" parameter_list ")" ";"
97118id: /#[0-9]+/
98119keyword: /[A-Z][0-9A-Z_]*/
99120parameter: untyped_parameter|typed_parameter|omitted_parameter
@@ -184,6 +205,12 @@ def build():
184205%ignore /[ \t\f\r\n]/+
185206"""
186207
208+ HEADER_FIELDS = {
209+ "file_description" : namedtuple ('file_description' , ['description' , 'implementation_level' ]),
210+ "file_name" : namedtuple ('file_name' , ['name' , 'time_stamp' , 'author' , 'organization' , 'preprocessor_version' , 'originating_system' , 'authorization' ]),
211+ "file_schema" : namedtuple ('file_schema' , ['schema_identifiers' ]),
212+ }
213+
187214
188215class Ref :
189216 def __init__ (self , id ):
@@ -304,6 +331,11 @@ def process_tree(filecontent, file_tree, with_progress, with_header=False):
304331
305332 if with_header :
306333 header = dict (map (make_header_ent , header .children [0 ].children ))
334+ for field in HEADER_FIELDS .keys ():
335+ observed = header .get (field .upper (), [])
336+ expected = HEADER_FIELDS .get (field )._fields
337+ if len (header .get (field .upper (), [])) != len (expected ):
338+ raise HeaderFieldError (field .upper (), len (observed ), len (expected ))
307339
308340 n = len (data .children )
309341 if n :
@@ -373,6 +405,11 @@ def replace_fn(match):
373405 header_tree = ast .children [0 ] # HEADER section
374406
375407 header = dict (map (make_header_ent , header_tree .children [0 ].children ))
408+ for field in HEADER_FIELDS .keys ():
409+ observed = header .get (field .upper (), [])
410+ expected = HEADER_FIELDS .get (field )._fields
411+ if len (header .get (field .upper (), [])) != len (expected ):
412+ raise HeaderFieldError (field .upper (), len (observed ), len (expected ))
376413 return header
377414
378415
@@ -473,13 +510,7 @@ def schema_version(self) -> tuple[int, int, int, int]:
473510
474511 @property
475512 def header (self ):
476- HEADER_FIELDS = {
477- "file_description" : namedtuple ('file_description' , ['description' , 'implementation_level' ]),
478- "file_name" : namedtuple ('file_name' , ['name' , 'time_stamp' , 'author' , 'organization' , 'preprocessor_version' , 'originating_system' , 'authorization' ]),
479- "file_schema" : namedtuple ('file_schema' , ['schema_identifiers' ]),
480- }
481513 header = {}
482-
483514 for field_name , namedtuple_class in HEADER_FIELDS .items ():
484515 field_data = self .header_ .get (field_name .upper (), [])
485516 header [field_name .lower ()] = namedtuple_class (* field_data )
0 commit comments