plint

French poetry validator (local mirror of https://gitlab.com/a3nm/plint)
git clone https://a3nm.net/git/plint/
Log | Files | Refs | README

error.py (8327B)


      1 from plint import common
      2 
      3 
      4 class ReportableError:
      5 
      6     def report(self, pattern):
      7         raise NotImplementedError
      8 
      9 
     10 class ErrorCollection(ReportableError):
     11     keys = {'hiatus': 'H', 'ambiguous': 'A', 'illegal': 'I'}
     12 
     13     @property
     14     def prefix(self):
     15         return "stdin:%d: " % self.line_no
     16 
     17     def __init__(self, line_no, line, pattern, verse, errors=None):
     18         self.line_no = line_no
     19         self.line = line
     20         self.errors = errors or []
     21         self.pattern = pattern
     22         self.verse = verse
     23 
     24     def isEmpty(self):
     25         return len(self.errors) == 0
     26 
     27     def say(self, l, short):
     28         return l if short else self.prefix + l
     29 
     30     def align(self, fmt="text"):
     31         return self.verse.align(fmt=fmt)
     32 
     33     def lines(self, short=False, fmt="text"):
     34         result = []
     35         if self.verse.possible is not None:
     36             result.append([self.say(x, short) for x in self.align(fmt=fmt)])
     37         for e in self.errors:
     38             result.append([self.say(e.report(self.pattern, fmt=fmt), short)])
     39         return result
     40 
     41     def report(self, short=False, fmt="text"):
     42         if fmt == "text":
     43             return '\n'.join(sum(self.lines(short, fmt=fmt), []))
     44         elif fmt == "json":
     45             return {
     46                     'line': self.line,
     47                     'line_no': self.line_no,
     48                     'possible_parsings': self.align(fmt=fmt),
     49                     'errors': [
     50                         e.report(self.pattern, fmt=fmt)
     51                         for e in self.errors]
     52                     }
     53         else:
     54             raise ValueError("bad format")
     55 
     56 
     57 class ErrorBadElement(ReportableError):
     58 
     59     def __init__(self):
     60         self.message = None
     61         self.key = None
     62         self.report_key = None
     63 
     64     def report(self, pattern, fmt="text"):
     65         if fmt == "text":
     66             return (self.message
     67                 + _(" (see '%s' above)")) % ErrorCollection.keys[self.key]
     68         elif fmt == "json":
     69             return {'error': self.report_key,
     70                     'error_kind': "local_error_collection"}
     71         else:
     72             raise ValueError("bad format")
     73 
     74 
     75 class ErrorBadCharacters(ErrorBadElement):
     76 
     77     def __init__(self):
     78         super().__init__()
     79         self.message = _("Illegal characters")
     80         self.key = "illegal"
     81         self.report_key = "illegal_characters"
     82 
     83 
     84 class ErrorForbiddenPattern(ErrorBadElement):
     85 
     86     def __init__(self):
     87         super().__init__()
     88         self.message = _("Illegal ambiguous pattern")
     89         self.key = "ambiguous"
     90         self.report_key = "ambiguous_patterns"
     91 
     92 
     93 class ErrorHiatus(ErrorBadElement):
     94 
     95     def __init__(self):
     96         super().__init__()
     97         self.message = _("Illegal hiatus")
     98         self.key = "hiatus"
     99         self.report_key = "hiatus"
    100 
    101 
    102 class ErrorBadRhyme(ReportableError):
    103 
    104     def __init__(self, expected, inferred, old_phon=None):
    105         self.expected = expected
    106         self.inferred = inferred
    107         self.old_phon = old_phon
    108         self.kind_human = None
    109         self.kind = None
    110 
    111     def get_id(self, pattern):
    112         raise NotImplementedError
    113 
    114     def fmt(self, l, fmt="text"):
    115         raise NotImplementedError
    116 
    117     def report(self, pattern, fmt="text"):
    118         if fmt == "text":
    119             return (_("%s for type %s (expected %s, inferred %s)")
    120                 % (self.kind_human, self.get_id(pattern),
    121                     self.fmt(self.expected, fmt=fmt),
    122                     self.fmt(self.inferred, fmt=fmt)))
    123         elif fmt == "json":
    124             return {
    125                 'error': self.kind, 'error_kind': "rhyme_error",
    126                 'pattern_rhyme_type': self.get_id(pattern),
    127                 'expected': self.fmt(self.expected, fmt=fmt),
    128                 'inferred': self.fmt(self.inferred, fmt=fmt)}
    129         else:
    130             raise ValueError("bad format")
    131 
    132 
    133 class ErrorBadRhymeGenre(ErrorBadRhyme):
    134 
    135     def __init__(self, expected, inferred, old_phon=None):
    136         super().__init__(expected, inferred, old_phon)
    137         self.kind_human = _("Bad rhyme genre")
    138         self.kind = "rhyme_genre"
    139 
    140     def fmt(self, l, fmt="text"):
    141         if fmt == "text":
    142             result = _(' or ').join(sorted(list(l)))
    143             if result == '':
    144                 result = "?"
    145             return "\"" + result + "\""
    146         elif fmt == "json":
    147             return sorted(list(l))
    148         else:
    149             raise ValueError("bad format")
    150 
    151     def get_id(self, pattern):
    152         return pattern.feminine_id
    153 
    154 
    155 class ErrorBadRhymeObject(ErrorBadRhyme):
    156 
    157     def fmt(self, l):
    158         raise NotImplementedError
    159 
    160     def get_id(self, pattern):
    161         return pattern.my_id
    162 
    163 
    164 class ErrorBadRhymeSound(ErrorBadRhymeObject):
    165 
    166     def __init__(self, expected, inferred, old_phon=None):
    167         super().__init__(expected, inferred, old_phon)
    168         self.kind_human = _("Bad rhyme sound")
    169         self.kind = "rhyme_sound"
    170 
    171     def fmt(self, l, fmt="text"):
    172         if fmt == "text":
    173             return ('/'.join("\"" + common.to_xsampa(x) + "\"" 
    174                     for x in sorted(list(l.sufficient_phon()))))
    175         elif fmt == "json":
    176             return (sorted(common.to_xsampa(x)
    177                     for x in list(l.sufficient_phon())))
    178         else:
    179             raise ValueError("bad format")
    180 
    181 
    182 class ErrorBadRhymeEye(ErrorBadRhymeObject):
    183 
    184     def __init__(self, expected, inferred, old_phon=None):
    185         super().__init__(expected, inferred, old_phon)
    186         self.kind_human = _("Bad rhyme ending")
    187         self.kind = "rhyme_ending"
    188 
    189     def fmt(self, l, fmt="text"):
    190         if fmt == "text":
    191             return "\"-" + l.sufficient_eye(self.old_phon) + "\""
    192         elif fmt == "json":
    193             return (l.sufficient_eye(self.old_phon))
    194         else:
    195             raise ValueError("bad format")
    196 
    197 
    198 class ErrorBadMetric(ReportableError):
    199 
    200     def report(self, pattern, fmt="text"):
    201         if fmt == "text":
    202             plural_hemistiche = '' if len(pattern.hemistiches) == 1 else 's'
    203             plural_syllable = '' if pattern.length == 1 else 's'
    204             if len(pattern.hemistiches) == 0:
    205                 hemistiche_string = ""
    206             else:
    207                 hemistiche_positions = (','.join(str(a)
    208                             for a in pattern.hemistiches))
    209                 hemistiche_string = ((_(" with hemistiche%s at ")
    210                             % plural_hemistiche) + hemistiche_positions)
    211             return (_("Illegal metric: expected %d syllable%s%s") %
    212                     (pattern.length, plural_syllable, hemistiche_string))
    213         elif fmt == "json":
    214             return {
    215                     'error': "metric", 'error_kind': "metric_error",
    216                     'expected_syllables': pattern.length,
    217                     'expected_hemistiches': pattern.hemistiches
    218                     }
    219         else:
    220             raise ValueError("bad format")
    221 
    222 
    223 class ErrorMultipleWordOccurrence(ReportableError):
    224 
    225     def __init__(self, word, occurrences):
    226         self.word = word
    227         self.occurrences = occurrences
    228 
    229     def report(self, pattern, fmt="text"):
    230         if fmt == "text":
    231             return (_("Too many occurrences of word \"%s\" for rhyme %s")
    232                     % (self.word, pattern.my_id))
    233         elif fmt == "json":
    234             return {
    235                 'error': "rhyme_occurrences", 'error_kind': "rhyme_error",
    236                 'pattern_rhyme_type': pattern.my_id,
    237                 'word': self.word
    238                 }
    239         else:
    240             raise ValueError("bad format")
    241 
    242 
    243 class ErrorIncompleteTemplate(ReportableError):
    244 
    245     def report(self, pattern, fmt="text"):
    246         if fmt == "text":
    247             return _("Poem is not complete")
    248         elif fmt == "json":
    249             return {
    250                 'error': "incomplete_poem",
    251                 'error_kind': "global_error"
    252                 }
    253         else:
    254             raise ValueError("bad format")
    255 
    256 
    257 
    258 class ErrorOverflowedTemplate(ReportableError):
    259 
    260     def report(self, pattern, fmt="text"):
    261         if fmt == "text":
    262             return _("Verse is beyond end of poem")
    263         elif fmt == "json":
    264             return {
    265                 'error': "verse_beyond_end_of_poem",
    266                 'error_kind': "global_error"
    267                 }
    268         else:
    269             raise ValueError("bad format")
    270 
    271 
    272 class TemplateLoadError(BaseException):
    273 
    274     def __init__(self, msg):
    275         self.msg = msg