@@ -77,6 +77,27 @@ def build():
77
77
yield " " * 8 + "^" * len (d ["line" ].rstrip ())
78
78
79
79
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
+ )
80
101
81
102
82
103
grammar = r"""
@@ -91,9 +112,9 @@ def build():
91
112
simple_record_list:simple_record simple_record*
92
113
simple_record: keyword "("parameter_list?")"
93
114
header_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 ")" ";"
97
118
id: /#[0-9]+/
98
119
keyword: /[A-Z][0-9A-Z_]*/
99
120
parameter: untyped_parameter|typed_parameter|omitted_parameter
@@ -184,6 +205,12 @@ def build():
184
205
%ignore /[ \t\f\r\n]/+
185
206
"""
186
207
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
+
187
214
188
215
class Ref :
189
216
def __init__ (self , id ):
@@ -304,6 +331,11 @@ def process_tree(filecontent, file_tree, with_progress, with_header=False):
304
331
305
332
if with_header :
306
333
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 ))
307
339
308
340
n = len (data .children )
309
341
if n :
@@ -373,6 +405,11 @@ def replace_fn(match):
373
405
header_tree = ast .children [0 ] # HEADER section
374
406
375
407
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 ))
376
413
return header
377
414
378
415
@@ -473,13 +510,7 @@ def schema_version(self) -> tuple[int, int, int, int]:
473
510
474
511
@property
475
512
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
- }
481
513
header = {}
482
-
483
514
for field_name , namedtuple_class in HEADER_FIELDS .items ():
484
515
field_data = self .header_ .get (field_name .upper (), [])
485
516
header [field_name .lower ()] = namedtuple_class (* field_data )
0 commit comments