1# -*- coding: utf-8 -*-
2
3# Copyright (c) 2014 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
4#
5
6"""
7Module implementing message translations for the code style plugin messages.
8"""
9
10import re
11import contextlib
12
13from PyQt5.QtCore import QCoreApplication
14
15from .Annotations.translations import (
16    _annotationsMessages, _annotationsMessagesSampleArgs
17)
18from .Complexity.translations import (
19    _complexityMessages, _complexityMessagesSampleArgs
20)
21from .DocStyle.translations import (
22    _docStyleMessages, _docStyleMessagesSampleArgs
23)
24from .Miscellaneous.translations import (
25    _miscellaneousMessages, _miscellaneousMessagesSampleArgs
26)
27from .Naming.translations import _namingStyleMessages
28from .PathLib.translations import _pathlibMessages
29from .Security.translations import (
30    _securityMessages, _securityMessagesSampleArgs
31)
32from .Simplify.translations import (
33    _simplifyMessages, _simplifyMessagesSampleArgs
34)
35
36##################################################################
37## pycodestyle error messages
38##################################################################
39
40_pycodestyleErrorMessages = {
41    "E101": QCoreApplication.translate(
42        "pycodestyle",
43        "indentation contains mixed spaces and tabs"),
44    "E111": QCoreApplication.translate(
45        "pycodestyle",
46        "indentation is not a multiple of four"),
47    "E112": QCoreApplication.translate(
48        "pycodestyle",
49        "expected an indented block"),
50    "E113": QCoreApplication.translate(
51        "pycodestyle",
52        "unexpected indentation"),
53    "E114": QCoreApplication.translate(
54        "pycodestyle",
55        "indentation is not a multiple of four (comment)"),
56    "E115": QCoreApplication.translate(
57        "pycodestyle",
58        "expected an indented block (comment)"),
59    "E116": QCoreApplication.translate(
60        "pycodestyle",
61        "unexpected indentation (comment)"),
62    "E117": QCoreApplication.translate(
63        "pycodestyle",
64        "over-indented"),
65    "E121": QCoreApplication.translate(
66        "pycodestyle",
67        "continuation line indentation is not a multiple of four"),
68    "E122": QCoreApplication.translate(
69        "pycodestyle",
70        "continuation line missing indentation or outdented"),
71    "E123": QCoreApplication.translate(
72        "pycodestyle",
73        "closing bracket does not match indentation of opening"
74        " bracket's line"),
75    "E124": QCoreApplication.translate(
76        "pycodestyle",
77        "closing bracket does not match visual indentation"),
78    "E125": QCoreApplication.translate(
79        "pycodestyle",
80        "continuation line with same indent as next logical line"),
81    "E126": QCoreApplication.translate(
82        "pycodestyle",
83        "continuation line over-indented for hanging indent"),
84    "E127": QCoreApplication.translate(
85        "pycodestyle",
86        "continuation line over-indented for visual indent"),
87    "E128": QCoreApplication.translate(
88        "pycodestyle",
89        "continuation line under-indented for visual indent"),
90    "E129": QCoreApplication.translate(
91        "pycodestyle",
92        "visually indented line with same indent as next logical line"),
93    "E131": QCoreApplication.translate(
94        "pycodestyle",
95        "continuation line unaligned for hanging indent"),
96    "E133": QCoreApplication.translate(
97        "pycodestyle",
98        "closing bracket is missing indentation"),
99    "E201": QCoreApplication.translate(
100        "pycodestyle",
101        "whitespace after '{0}'"),
102    "E202": QCoreApplication.translate(
103        "pycodestyle",
104        "whitespace before '{0}'"),
105    "E203": QCoreApplication.translate(
106        "pycodestyle",
107        "whitespace before '{0}'"),
108    "E211": QCoreApplication.translate(
109        "pycodestyle",
110        "whitespace before '{0}'"),
111    "E221": QCoreApplication.translate(
112        "pycodestyle",
113        "multiple spaces before operator"),
114    "E222": QCoreApplication.translate(
115        "pycodestyle",
116        "multiple spaces after operator"),
117    "E223": QCoreApplication.translate(
118        "pycodestyle",
119        "tab before operator"),
120    "E224": QCoreApplication.translate(
121        "pycodestyle",
122        "tab after operator"),
123    "E225": QCoreApplication.translate(
124        "pycodestyle",
125        "missing whitespace around operator"),
126    "E226": QCoreApplication.translate(
127        "pycodestyle",
128        "missing whitespace around arithmetic operator"),
129    "E227": QCoreApplication.translate(
130        "pycodestyle",
131        "missing whitespace around bitwise or shift operator"),
132    "E228": QCoreApplication.translate(
133        "pycodestyle",
134        "missing whitespace around modulo operator"),
135    "E231": QCoreApplication.translate(
136        "pycodestyle",
137        "missing whitespace after '{0}'"),
138    "E241": QCoreApplication.translate(
139        "pycodestyle",
140        "multiple spaces after '{0}'"),
141    "E242": QCoreApplication.translate(
142        "pycodestyle",
143        "tab after '{0}'"),
144    "E251": QCoreApplication.translate(
145        "pycodestyle",
146        "unexpected spaces around keyword / parameter equals"),
147    "E252": QCoreApplication.translate(
148        "pycodestyle",
149        "missing whitespace around parameter equals"),
150    "E261": QCoreApplication.translate(
151        "pycodestyle",
152        "at least two spaces before inline comment"),
153    "E262": QCoreApplication.translate(
154        "pycodestyle",
155        "inline comment should start with '# '"),
156    "E265": QCoreApplication.translate(
157        "pycodestyle",
158        "block comment should start with '# '"),
159    "E266": QCoreApplication.translate(
160        "pycodestyle",
161        "too many leading '#' for block comment"),
162    "E271": QCoreApplication.translate(
163        "pycodestyle",
164        "multiple spaces after keyword"),
165    "E272": QCoreApplication.translate(
166        "pycodestyle",
167        "multiple spaces before keyword"),
168    "E273": QCoreApplication.translate(
169        "pycodestyle",
170        "tab after keyword"),
171    "E274": QCoreApplication.translate(
172        "pycodestyle",
173        "tab before keyword"),
174    "E275": QCoreApplication.translate(
175        "pycodestyle",
176        "missing whitespace after keyword"),
177    "E301": QCoreApplication.translate(
178        "pycodestyle",
179        "expected {0} blank lines, found {1}"),
180    "E302": QCoreApplication.translate(
181        "pycodestyle",
182        "expected {0} blank lines, found {1}"),
183    "E303": QCoreApplication.translate(
184        "pycodestyle",
185        "too many blank lines ({0}), expected {1}"),
186    "E304": QCoreApplication.translate(
187        "pycodestyle",
188        "blank lines found after function decorator"),
189    "E305": QCoreApplication.translate(
190        "pycodestyle",
191        "expected {0} blank lines after class or function definition,"
192        " found {1}"),
193    "E306": QCoreApplication.translate(
194        "pycodestyle",
195        "expected {0} blank lines before a nested definition, found {1}"),
196    "E307": QCoreApplication.translate(
197        "pycodestyle",
198        "too many blank lines ({0}) before a nested definition, expected {1}"),
199    "E308": QCoreApplication.translate(
200        "pycodestyle",
201        "too many blank lines ({0})"),
202    "E401": QCoreApplication.translate(
203        "pycodestyle",
204        "multiple imports on one line"),
205    "E402": QCoreApplication.translate(
206        "pycodestyle",
207        "module level import not at top of file"),
208    "E501": QCoreApplication.translate(
209        "pycodestyle",
210        "line too long ({0} > {1} characters)"),
211    "E502": QCoreApplication.translate(
212        "pycodestyle",
213        "the backslash is redundant between brackets"),
214    "E701": QCoreApplication.translate(
215        "pycodestyle",
216        "multiple statements on one line (colon)"),
217    "E702": QCoreApplication.translate(
218        "pycodestyle",
219        "multiple statements on one line (semicolon)"),
220    "E703": QCoreApplication.translate(
221        "pycodestyle",
222        "statement ends with a semicolon"),
223    "E704": QCoreApplication.translate(
224        "pycodestyle",
225        "multiple statements on one line (def)"),
226    "E711": QCoreApplication.translate(
227        "pycodestyle",
228        "comparison to {0} should be {1}"),
229    "E712": QCoreApplication.translate(
230        "pycodestyle",
231        "comparison to {0} should be {1}"),
232    "E713": QCoreApplication.translate(
233        "pycodestyle",
234        "test for membership should be 'not in'"),
235    "E714": QCoreApplication.translate(
236        "pycodestyle",
237        "test for object identity should be 'is not'"),
238    "E721": QCoreApplication.translate(
239        "pycodestyle",
240        "do not compare types, use 'isinstance()'"),
241    "E722": QCoreApplication.translate(
242        "pycodestyle",
243        "do not use bare except"),
244    "E731": QCoreApplication.translate(
245        "pycodestyle",
246        "do not assign a lambda expression, use a def"),
247    "E741": QCoreApplication.translate(
248        "pycodestyle",
249        "ambiguous variable name '{0}'"),
250    "E742": QCoreApplication.translate(
251        "pycodestyle",
252        "ambiguous class definition '{0}'"),
253    "E743": QCoreApplication.translate(
254        "pycodestyle",
255        "ambiguous function definition '{0}'"),
256    "E901": QCoreApplication.translate(
257        "pycodestyle",
258        "{0}: {1}"),
259    "E902": QCoreApplication.translate(
260        "pycodestyle",
261        "{0}"),
262}
263
264##################################################################
265## pycodestyle warning messages
266##################################################################
267
268_pycodestyleWarningMessages = {
269    "W191": QCoreApplication.translate(
270        "pycodestyle",
271        "indentation contains tabs"),
272    "W291": QCoreApplication.translate(
273        "pycodestyle",
274        "trailing whitespace"),
275    "W292": QCoreApplication.translate(
276        "pycodestyle",
277        "no newline at end of file"),
278    "W293": QCoreApplication.translate(
279        "pycodestyle",
280        "blank line contains whitespace"),
281    "W391": QCoreApplication.translate(
282        "pycodestyle",
283        "blank line at end of file"),
284    "W503": QCoreApplication.translate(
285        "pycodestyle",
286        "line break before binary operator"),
287    "W504": QCoreApplication.translate(
288        "pycodestyle",
289        "line break after binary operator"),
290    "W505": QCoreApplication.translate(
291        "pycodestyle",
292        "doc line too long ({0} > {1} characters)"),
293    "W601": QCoreApplication.translate(
294        "pycodestyle",
295        ".has_key() is deprecated, use 'in'"),
296    "W602": QCoreApplication.translate(
297        "pycodestyle",
298        "deprecated form of raising exception"),
299    "W603": QCoreApplication.translate(
300        "pycodestyle",
301        "'<>' is deprecated, use '!='"),
302    "W604": QCoreApplication.translate(
303        "pycodestyle",
304        "backticks are deprecated, use 'repr()'"),
305    "W605": QCoreApplication.translate(
306        "pycodestyle",
307        "invalid escape sequence '\\{0}'"),
308    "W606": QCoreApplication.translate(
309        "pycodestyle",
310        "'async' and 'await' are reserved keywords starting with Python 3.7"),
311}
312
313##################################################################
314## CodeStyleFixer messages
315##################################################################
316
317_fixMessages = {
318    "FIXD111": QCoreApplication.translate(
319        'CodeStyleFixer',
320        "Triple single quotes converted to triple double quotes."),
321    'FIXD112': QCoreApplication.translate(
322        'CodeStyleFixer',
323        'Introductory quotes corrected to be {0}"""'),
324    "FIXD121": QCoreApplication.translate(
325        'CodeStyleFixer',
326        "Single line docstring put on one line."),
327    "FIXD131": QCoreApplication.translate(
328        'CodeStyleFixer',
329        "Period added to summary line."),
330    "FIXD141": QCoreApplication.translate(
331        'CodeStyleFixer',
332        "Blank line before function/method docstring removed."),
333    "FIXD142": QCoreApplication.translate(
334        'CodeStyleFixer',
335        "Blank line inserted before class docstring."),
336    "FIXD143": QCoreApplication.translate(
337        'CodeStyleFixer',
338        "Blank line inserted after class docstring."),
339    "FIXD144": QCoreApplication.translate(
340        'CodeStyleFixer',
341        "Blank line inserted after docstring summary."),
342    "FIXD145": QCoreApplication.translate(
343        'CodeStyleFixer',
344        "Blank line inserted after last paragraph of docstring."),
345    "FIXD221": QCoreApplication.translate(
346        'CodeStyleFixer',
347        "Leading quotes put on separate line."),
348    "FIXD222": QCoreApplication.translate(
349        'CodeStyleFixer',
350        "Trailing quotes put on separate line."),
351    "FIXD242": QCoreApplication.translate(
352        'CodeStyleFixer',
353        "Blank line before class docstring removed."),
354    "FIXD244": QCoreApplication.translate(
355        'CodeStyleFixer',
356        "Blank line before function/method docstring removed."),
357    "FIXD243": QCoreApplication.translate(
358        'CodeStyleFixer',
359        "Blank line after class docstring removed."),
360    "FIXD245": QCoreApplication.translate(
361        'CodeStyleFixer',
362        "Blank line after function/method docstring removed."),
363    "FIXD247": QCoreApplication.translate(
364        'CodeStyleFixer',
365        "Blank line after last paragraph removed."),
366    "FIXE101": QCoreApplication.translate(
367        'CodeStyleFixer',
368        "Tab converted to 4 spaces."),
369    "FIXE111": QCoreApplication.translate(
370        'CodeStyleFixer',
371        "Indentation adjusted to be a multiple of four."),
372    "FIXE121": QCoreApplication.translate(
373        'CodeStyleFixer',
374        "Indentation of continuation line corrected."),
375    "FIXE124": QCoreApplication.translate(
376        'CodeStyleFixer',
377        "Indentation of closing bracket corrected."),
378    "FIXE122": QCoreApplication.translate(
379        'CodeStyleFixer',
380        "Missing indentation of continuation line corrected."),
381    "FIXE123": QCoreApplication.translate(
382        'CodeStyleFixer',
383        "Closing bracket aligned to opening bracket."),
384    "FIXE125": QCoreApplication.translate(
385        'CodeStyleFixer',
386        "Indentation level changed."),
387    "FIXE126": QCoreApplication.translate(
388        'CodeStyleFixer',
389        "Indentation level of hanging indentation changed."),
390    "FIXE127": QCoreApplication.translate(
391        'CodeStyleFixer',
392        "Visual indentation corrected."),
393    "FIXE201": QCoreApplication.translate(
394        'CodeStyleFixer',
395        "Extraneous whitespace removed."),
396    "FIXE225": QCoreApplication.translate(
397        'CodeStyleFixer',
398        "Missing whitespace added."),
399    "FIXE221": QCoreApplication.translate(
400        'CodeStyleFixer',
401        "Extraneous whitespace removed."),
402    "FIXE231": QCoreApplication.translate(
403        'CodeStyleFixer',
404        "Missing whitespace added."),
405    "FIXE251": QCoreApplication.translate(
406        'CodeStyleFixer',
407        "Extraneous whitespace removed."),
408    "FIXE261": QCoreApplication.translate(
409        'CodeStyleFixer',
410        "Whitespace around comment sign corrected."),
411
412    "FIXE302+": lambda n=1: QCoreApplication.translate(
413        'CodeStyleFixer',
414        "%n blank line(s) inserted.", '', n),
415    "FIXE302-": lambda n=1: QCoreApplication.translate(
416        'CodeStyleFixer',
417        "%n superfluous lines removed", '', n),
418
419    "FIXE303": QCoreApplication.translate(
420        'CodeStyleFixer',
421        "Superfluous blank lines removed."),
422    "FIXE304": QCoreApplication.translate(
423        'CodeStyleFixer',
424        "Superfluous blank lines after function decorator removed."),
425    "FIXE401": QCoreApplication.translate(
426        'CodeStyleFixer',
427        "Imports were put on separate lines."),
428    "FIXE501": QCoreApplication.translate(
429        'CodeStyleFixer',
430        "Long lines have been shortened."),
431    "FIXE502": QCoreApplication.translate(
432        'CodeStyleFixer',
433        "Redundant backslash in brackets removed."),
434    "FIXE701": QCoreApplication.translate(
435        'CodeStyleFixer',
436        "Compound statement corrected."),
437    "FIXE702": QCoreApplication.translate(
438        'CodeStyleFixer',
439        "Compound statement corrected."),
440    "FIXE711": QCoreApplication.translate(
441        'CodeStyleFixer',
442        "Comparison to None/True/False corrected."),
443    "FIXN804": QCoreApplication.translate(
444        'CodeStyleFixer',
445        "'{0}' argument added."),
446    "FIXN806": QCoreApplication.translate(
447        'CodeStyleFixer',
448        "'{0}' argument removed."),
449    "FIXW291": QCoreApplication.translate(
450        'CodeStyleFixer',
451        "Whitespace stripped from end of line."),
452    "FIXW292": QCoreApplication.translate(
453        'CodeStyleFixer',
454        "newline added to end of file."),
455    "FIXW391": QCoreApplication.translate(
456        'CodeStyleFixer',
457        "Superfluous trailing blank lines removed from end of file."),
458    "FIXW603": QCoreApplication.translate(
459        'CodeStyleFixer',
460        "'<>' replaced by '!='."),
461
462    "FIXWRITE_ERROR": QCoreApplication.translate(
463        'CodeStyleFixer',
464        "Could not save the file! Skipping it. Reason: {0}"),
465}
466
467_pycodestyleErrorMessagesSampleArgs = {
468    "E201": ["([{"],
469    "E202": ["}])"],
470    "E203": [",;:"],
471    "E211": ["(["],
472    "E231": [",;:"],
473    "E241": [",;:"],
474    "E242": [",;:"],
475    "E301": [1, 0],
476    "E302": [2, 1],
477    "E303": [3, 2],
478    "E305": [2, 1],
479    "E306": [1, 0],
480    "E307": [3, 1],
481    "E308": [3],
482    "E501": [85, 79],
483    "E711": ["None", "'if cond is None:'"],
484    "E712": ["True", "'if cond is True:' or 'if cond:'"],
485    "E741": ["l"],
486    "E742": ["l"],
487    "E743": ["l"],
488    "E901": ["SyntaxError", "Invalid Syntax"],
489    "E902": ["OSError"],
490}
491
492_pycodestyleWarningMessagesSampleArgs = {
493    "W505": [80, 72],
494    "W605": ["A"],
495}
496
497_fixMessagesSampleArgs = {
498    "FIXWRITE_ERROR": ["OSError"],
499}
500
501messageCatalogs = {
502    "A": _annotationsMessages,
503    "C": _complexityMessages,
504    "D": _docStyleMessages,
505    "E": _pycodestyleErrorMessages,
506    "M": _miscellaneousMessages,
507    "N": _namingStyleMessages,
508    "P": _pathlibMessages,
509    "S": _securityMessages,
510    "W": _pycodestyleWarningMessages,
511    "Y": _simplifyMessages,
512
513    "FIX": _fixMessages,
514}
515
516messageSampleArgsCatalog = {
517    "A": _annotationsMessagesSampleArgs,
518    "C": _complexityMessagesSampleArgs,
519    "D": _docStyleMessagesSampleArgs,
520    "E": _pycodestyleErrorMessagesSampleArgs,
521    "M": _miscellaneousMessagesSampleArgs,
522    "S": _securityMessagesSampleArgs,
523    "W": _pycodestyleWarningMessagesSampleArgs,
524    "Y": _simplifyMessagesSampleArgs,
525
526    "FIX": _fixMessagesSampleArgs,
527}
528
529messageCategoryRe = re.compile(r"([A-Z]{1,3}).+")
530
531
532def getTranslatedMessage(messageCode, messageArgs, example=False):
533    """
534    Module function to get a translated and formatted message for a
535    given message ID.
536
537    @param messageCode the message code
538    @type str
539    @param messageArgs list of arguments or a single integer value to format
540        the message
541    @type list or int
542    @param example flag indicating a translated message filled with example
543        data is requested (messageArgs is ignored if given)
544    @type bool
545    @return translated and formatted message
546    @rtype str
547    """
548    match = messageCategoryRe.match(messageCode)
549    if match:
550        # the message code is OK
551        messageCategory = match.group(1)
552
553        if example:
554            try:
555                argsCatalog = messageSampleArgsCatalog[messageCategory]
556                try:
557                    args = argsCatalog[messageCode]
558                except KeyError:
559                    args = None
560            except KeyError:
561                args = None
562        else:
563            args = messageArgs
564
565        with contextlib.suppress(KeyError):
566            catalog = messageCatalogs[messageCategory]
567            with contextlib.suppress(KeyError):
568                message = catalog[messageCode]
569                if args is None:
570                    return message
571                elif isinstance(args, int):
572                    # Retranslate with correct plural form
573                    return message(args)
574                else:
575                    return message.format(*args)
576
577    if example:
578        return None
579    else:
580        return QCoreApplication.translate(
581            "CodeStyleChecker",
582            "No message defined for code '{0}'."
583        ).format(messageCode)
584
585
586def getMessageCodes():
587    """
588    Module function to get a list of known message codes.
589
590    @return list of known message codes
591    @rtype set of str
592    """
593    knownCodes = []
594    for catalog in messageCatalogs.values():
595        knownCodes += list(catalog.keys())
596    return {c.split(".", 1)[0] for c in knownCodes}
597
598#
599# eflag: noqa = M201
600