Mercurial > hg > freeDiameter
comparison contrib/tools/csv_to_fd @ 1487:5c2d061a8c8e
csv_to_fd: improve validation
When an AVP Code or Name is duplicate, differentiate
between a duplicate entry (AVPs are equivalent)
versus an actual mismatch.
Consistent AVP formatting in errors.
Collect all errors and display at end of each file parse,
instead of exiting on first error.
author | Luke Mewburn <luke@mewburn.net> |
---|---|
date | Fri, 27 Mar 2020 15:32:27 +1100 |
parents | c0aa1e66c12e |
children | ae76ea63ee12 |
comparison
equal
deleted
inserted
replaced
1486:eeb5706333c3 | 1487:5c2d061a8c8e |
---|---|
119 self.name, self.code)) | 119 self.name, self.code)) |
120 if (self.datatype not in ( | 120 if (self.datatype not in ( |
121 'OctetString', 'Integer32', 'Integer64', 'Unsigned32', | 121 'OctetString', 'Integer32', 'Integer64', 'Unsigned32', |
122 'Unsigned64', 'Float32', 'Float64', 'Grouped') | 122 'Unsigned64', 'Float32', 'Float64', 'Grouped') |
123 and self.datatype not in DERIVED_TO_BASE): | 123 and self.datatype not in DERIVED_TO_BASE): |
124 raise ValueError('AVP "{}" invalid data type "{}"'.format( | 124 raise ValueError('{} invalid data type "{}"'.format( |
125 self.name, self.datatype)) | 125 self.description(), self.datatype)) |
126 # Validate flags | 126 # Validate flags |
127 flags = collections.Counter() | 127 flags = collections.Counter() |
128 for val, desc in [ | 128 for val, desc in [ |
129 (self.must, 'Must'), | 129 (self.must, 'Must'), |
130 (self.may, 'May'), | 130 (self.may, 'May'), |
131 (self.shouldnot, 'Should Not'), | 131 (self.shouldnot, 'Should Not'), |
132 (self.mustnot, 'Must Not'), | 132 (self.mustnot, 'Must Not'), |
133 ]: | 133 ]: |
134 if not self._flags_re.match(val): | 134 if not self._flags_re.match(val): |
135 raise ValueError('AVP "{}" invalid {} Flags "{}"'.format( | 135 raise ValueError('{} invalid {} Flags "{}"'.format( |
136 self.name, desc, val)) | 136 self.description(), desc, val)) |
137 flags.update(val) | 137 flags.update(val) |
138 # Check occurrence of M,V in Must,May,ShouldNot,MustNot | 138 # Check occurrence of M,V in Must,May,ShouldNot,MustNot |
139 for flag in 'MV': | 139 for flag in 'MV': |
140 # TODO: can AVP flags not appear at all? | 140 # TODO: can AVP flags not appear at all? |
141 # if flags[flag] == 0: | 141 # if flags[flag] == 0: |
142 # raise ValueError('AVP "{}" Flag "{}" not set'.format( | 142 # raise ValueError('{} Flag "{}" not set'.format( |
143 # self.name, flag)) | 143 # self.description(), flag)) |
144 if flags[flag] > 1: | 144 if flags[flag] > 1: |
145 raise ValueError('AVP "{}" Flag "{}" set {} times'.format( | 145 raise ValueError('{} Flag "{}" set {} times'.format( |
146 self.name, flag, flags[flag])) | 146 self.description(), flag, flags[flag])) |
147 # Compare V presence against vendor | 147 # Compare V presence against vendor |
148 if 'V' in self.must: | 148 if 'V' in self.must: |
149 if self.vendor == 0: | 149 if self.vendor == 0: |
150 raise ValueError('AVP "{}" Flag "V" set for vendor 0'.format( | 150 raise ValueError('{} Flag "V" set for vendor 0'.format( |
151 self.name)) | 151 self.description())) |
152 else: | 152 else: |
153 if self.vendor != 0: | 153 if self.vendor != 0: |
154 raise ValueError( | 154 raise ValueError('{} Flag "V" not set for vendor {}'.format( |
155 'AVP "{}" Flag "V" not set for vendor {}'.format( | 155 self.description(), self.vendor)) |
156 self.name, self.vendor)) | |
157 | 156 |
158 @property | 157 @property |
159 def __dict__(self): | 158 def __dict__(self): |
160 return {s: getattr(self, s) for s in self.__slots__} | 159 return {s: getattr(self, s) for s in self.__slots__} |
160 | |
161 def __eq__(self, other): | |
162 """Equality comparison of Avp instances. | |
163 Considered equal if name, vendor, code, datatype, and flags are equal. | |
164 """ | |
165 if other is self: | |
166 return True | |
167 if type(other) is not type(self): | |
168 return NotImplemented | |
169 return ( | |
170 other.name, other.vendor, other.code, other.datatype, | |
171 other.must, other.may, other.shouldnot, other.mustnot, | |
172 ) == ( | |
173 self.name, self.vendor, self.code, self.datatype, | |
174 self.must, self.may, self.shouldnot, self.mustnot, | |
175 ) | |
176 | |
177 def __ne__(self, other): | |
178 return not self == other | |
179 | |
180 def description(self): | |
181 return 'AVP "{}" ({})'.format(self.name, self.code) | |
161 | 182 |
162 | 183 |
163 class Processor(object): | 184 class Processor(object): |
164 """Interface for processor of Avp""" | 185 """Interface for processor of Avp""" |
165 | 186 |
390 if 'M' in flags: | 411 if 'M' in flags: |
391 result.append('M') | 412 result.append('M') |
392 return ''.join(result) | 413 return ''.join(result) |
393 | 414 |
394 | 415 |
416 def avp_conflict(description, avp, conflict): | |
417 """Raise error for duplicate or conflicting AVPs. | |
418 """ | |
419 if avp == conflict: | |
420 raise ValueError( | |
421 '{} {} duplicated in' | |
422 ' file "{}" line {}'.format( | |
423 avp.description(), description, | |
424 conflict.filename, conflict.line_num)) | |
425 else: | |
426 raise ValueError( | |
427 '{} {} conflicts with {}' | |
428 ' in file "{}" line {}'.format( | |
429 avp.description(), description, | |
430 conflict.description(), | |
431 conflict.filename, conflict.line_num)) | |
432 | |
433 | |
395 def main(): | 434 def main(): |
435 """Main application entry. | |
436 """ | |
396 | 437 |
397 # Build dict of name: NameProcessor | 438 # Build dict of name: NameProcessor |
398 processors = { | 439 processors = { |
399 cls.cls_name(): cls | 440 cls.cls_name(): cls |
400 for cls in Processor.__subclasses__() | 441 for cls in Processor.__subclasses__() |
451 with open(filename, 'r') as csvfile: | 492 with open(filename, 'r') as csvfile: |
452 csvdata = csv.DictReader(csvfile, CSV_COLUMN_NAMES, | 493 csvdata = csv.DictReader(csvfile, CSV_COLUMN_NAMES, |
453 restkey='extra_cells') | 494 restkey='extra_cells') |
454 standard = '' | 495 standard = '' |
455 vendor = 0 | 496 vendor = 0 |
497 errors = [] | |
456 for row in csvdata: | 498 for row in csvdata: |
457 try: | 499 try: |
458 if row['name'] in (None, '', 'Attribute Name'): | 500 if row['name'] in (None, '', 'Attribute Name'): |
459 continue | 501 continue |
460 elif row['name'].startswith('#'): | 502 elif row['name'].startswith('#'): |
477 standard=standard, vendor=vendor, | 519 standard=standard, vendor=vendor, |
478 **row) | 520 **row) |
479 # Ensure AVP vendor/code not already defined | 521 # Ensure AVP vendor/code not already defined |
480 if avp.code in avp_codes[avp.vendor]: | 522 if avp.code in avp_codes[avp.vendor]: |
481 conflict = avp_codes[avp.vendor][avp.code] | 523 conflict = avp_codes[avp.vendor][avp.code] |
482 raise ValueError( | 524 avp_conflict('Code', avp, conflict) |
483 'AVP vendor {} code {} already present' | |
484 ' in file "{}" line {}'.format( | |
485 avp.vendor, avp.code, | |
486 conflict.filename, conflict.line_num)) | |
487 avp_codes[avp.vendor][avp.code] = avp | 525 avp_codes[avp.vendor][avp.code] = avp |
488 # Ensure AVP vendor/name not already defined | 526 # Ensure AVP vendor/name not already defined |
489 if avp.name in avp_names[avp.vendor]: | 527 if avp.name in avp_names[avp.vendor]: |
490 conflict = avp_names[avp.vendor][avp.name] | 528 conflict = avp_names[avp.vendor][avp.name] |
491 raise ValueError( | 529 avp_conflict('Name', avp, conflict) |
492 'AVP vendor {} name "{}" already present' | |
493 ' in file "{}" line {}'.format( | |
494 avp.vendor, avp.name, | |
495 conflict.filename, conflict.line_num)) | |
496 avp_names[avp.vendor][avp.name] = avp | 530 avp_names[avp.vendor][avp.name] = avp |
497 # Process AVP | 531 # Process AVP |
498 avpproc.avp(avp) | 532 avpproc.avp(avp) |
499 except ValueError as e: | 533 except ValueError as e: |
500 sys.stderr.write('CSV file "{}" line {}: {}\n'.format( | 534 errors.append('CSV file "{}" line {}: {}\n'.format( |
501 filename, csvdata.line_num, e)) | 535 filename, csvdata.line_num, e)) |
502 sys.exit(1) | 536 if errors: |
537 sys.stderr.write(''.join(errors)) | |
538 sys.exit(1) | |
503 | 539 |
504 # Generate result | 540 # Generate result |
505 avpproc.generate() | 541 avpproc.generate() |
506 | 542 |
507 | 543 |