| 1 | #!/usr/bin/env python2
 | 
| 2 | from __future__ import print_function
 | 
| 3 | """
 | 
| 4 | Our wrapper around pyflakes 2.4.0.
 | 
| 5 | 
 | 
| 6 | Newer versions dropped support for Python 2.
 | 
| 7 | 
 | 
| 8 | All versions: https://pypi.org/simple/pyflakes/
 | 
| 9 | 
 | 
| 10 | Change log: https://github.com/PyCQA/pyflakes/blob/main/NEWS.rst
 | 
| 11 | """
 | 
| 12 | 
 | 
| 13 | import argparse
 | 
| 14 | import sys
 | 
| 15 | 
 | 
| 16 | from pyflakes import api
 | 
| 17 | from pyflakes import reporter
 | 
| 18 | 
 | 
| 19 | from core import ansi
 | 
| 20 | 
 | 
| 21 | # Our config for flake8
 | 
| 22 | # local fatal_errors='E901,E999,F821,F822,F823,F401'
 | 
| 23 | 
 | 
| 24 | # From flake8/src/flake8/plugins/pyflakes.py
 | 
| 25 | # "RaiseNotImplemented": "F901",
 | 
| 26 | # "UndefinedName": "F821",
 | 
| 27 | # "UndefinedLocal": "F823",
 | 
| 28 | # "UnusedImport": "F401",
 | 
| 29 | 
 | 
| 30 | FATAL_CLASS_NAMES = [
 | 
| 31 |     "RaiseNotImplemented",
 | 
| 32 |     "UndefinedName",
 | 
| 33 |     "UndefinedLocal",
 | 
| 34 |     "UnusedImport",
 | 
| 35 | ]
 | 
| 36 | 
 | 
| 37 | # Other useful ones
 | 
| 38 | # "RedefinedWhileUnused": "F811",
 | 
| 39 | 
 | 
| 40 | 
 | 
| 41 | class OilsReporter(reporter.Reporter):
 | 
| 42 | 
 | 
| 43 |     def __init__(self):
 | 
| 44 |         # Warnings and errors both go to stdout
 | 
| 45 |         reporter.Reporter.__init__(self, sys.stdout, sys.stdout)
 | 
| 46 |         self.num_fatal_errors = 0
 | 
| 47 | 
 | 
| 48 |     def flake(self, message):
 | 
| 49 |         """
 | 
| 50 |         pyflakes found something wrong with the code.
 | 
| 51 | 
 | 
| 52 |         @param: A L{pyflakes.messages.Message}.
 | 
| 53 |         """
 | 
| 54 |         type_name = type(message).__name__
 | 
| 55 | 
 | 
| 56 |         # Suppress some errors for now to reducenoise
 | 
| 57 |         if type_name == 'UnusedVariable':
 | 
| 58 |             if message.filename.endswith('_test.py'):
 | 
| 59 |                 return
 | 
| 60 | 
 | 
| 61 |             var_name = message.message_args[0]
 | 
| 62 |             if var_name == 'e':
 | 
| 63 |                 return
 | 
| 64 |             if var_name.startswith('unused'):
 | 
| 65 |                 return
 | 
| 66 | 
 | 
| 67 |         if type_name in FATAL_CLASS_NAMES:
 | 
| 68 |             self.num_fatal_errors += 1
 | 
| 69 |             color = self._stdout.isatty()
 | 
| 70 |         else:
 | 
| 71 |             color = False
 | 
| 72 | 
 | 
| 73 |         if color:
 | 
| 74 |             self._stdout.write(ansi.RED + ansi.BOLD)
 | 
| 75 |             self._stdout.write(str(message))
 | 
| 76 |             self._stdout.write(ansi.RESET)
 | 
| 77 |         else:
 | 
| 78 |             self._stdout.write(str(message))
 | 
| 79 | 
 | 
| 80 |         self._stdout.write('\n')
 | 
| 81 | 
 | 
| 82 | 
 | 
| 83 | def main(args):
 | 
| 84 |     parser = argparse.ArgumentParser(
 | 
| 85 |         prog=None, description='Check Python source files for errors')
 | 
| 86 |     #parser.add_argument('-V', '--version', action='version', version=_get_version())
 | 
| 87 |     parser.add_argument(
 | 
| 88 |         'path',
 | 
| 89 |         nargs='*',
 | 
| 90 |         help='Path(s) of Python file(s) to check. STDIN if not given.')
 | 
| 91 |     paths = parser.parse_args(args).path
 | 
| 92 | 
 | 
| 93 |     rep = OilsReporter()
 | 
| 94 | 
 | 
| 95 |     api.checkRecursive(paths, rep)
 | 
| 96 |     return 0 if rep.num_fatal_errors == 0 else 1
 | 
| 97 | 
 | 
| 98 | 
 | 
| 99 | if __name__ == '__main__':
 | 
| 100 |     try:
 | 
| 101 |         sys.exit(main(sys.argv[1:]))
 | 
| 102 |     except KeyboardInterrupt as e:
 | 
| 103 |         print('%s: interrupted with Ctrl-C' % sys.argv[0], file=sys.stderr)
 | 
| 104 |         sys.exit(1)
 | 
| 105 |     except RuntimeError as e:
 | 
| 106 |         print('FATAL: %s' % e, file=sys.stderr)
 | 
| 107 |         sys.exit(1)
 |