| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128 | #!/usr/bin/env pythonfrom __future__ import absolute_importfrom __future__ import with_statementimport osimport pprintimport reimport sysfrom collections import defaultdictfrom itertools import starmapfrom unipath import PathRE_COMMENT = r'^\s*\#'RE_NOQA = r'.+?\#\s+noqa+'RE_MULTILINE_COMMENT_O = r'^\s*(?:\'\'\'|""").+?(?:\'\'\'|""")'RE_MULTILINE_COMMENT_S = r'^\s*(?:\'\'\'|""")'RE_MULTILINE_COMMENT_E = r'(?:^|.+?)(?:\'\'\'|""")'RE_WITH = r'(?:^|\s+)with\s+'RE_WITH_IMPORT = r'''from\s+ __future__\s+ import\s+ with_statement'''RE_PRINT = r'''(?:^|\s+)print\((?:"|')(?:\W+?)?[A-Z0-9:]{2,}'''RE_ABS_IMPORT = r'''from\s+ __future__\s+ import\s+ absolute_import'''acc = defaultdict(lambda: {"abs": False, "print": False})def compile(regex):    return re.compile(regex, re.VERBOSE)class FlakePP(object):    re_comment = compile(RE_COMMENT)    re_ml_comment_o = compile(RE_MULTILINE_COMMENT_O)    re_ml_comment_s = compile(RE_MULTILINE_COMMENT_S)    re_ml_comment_e = compile(RE_MULTILINE_COMMENT_E)    re_abs_import = compile(RE_ABS_IMPORT)    re_print = compile(RE_PRINT)    re_with_import = compile(RE_WITH_IMPORT)    re_with = compile(RE_WITH)    re_noqa = compile(RE_NOQA)    map = {"abs": False, "print": False,            "with": False, "with-used": False}    def __init__(self, verbose=False):        self.verbose = verbose        self.steps = (("abs", self.re_abs_import),                      ("with", self.re_with_import),                      ("with-used", self.re_with),                      ("print", self.re_print))    def analyze_fh(self, fh):        steps = self.steps        filename = fh.name        acc = dict(self.map)        index = 0        errors = [0]        def error(fmt, **kwargs):            errors[0] += 1            self.announce(fmt, **dict(kwargs, filename=filename))        for index, line in enumerate(self.strip_comments(fh)):            for key, pattern in self.steps:                if pattern.match(line):                    acc[key] = True        if index:            if not acc["abs"]:                error("%(filename)s: missing abs import")            if acc["with-used"] and not acc["with"]:                error("%(filename)s: missing with import")            if acc["print"]:                error("%(filename)s: left over print statement")        return filename, errors[0], acc    def analyze_file(self, filename):        with open(filename) as fh:            return self.analyze_fh(fh)    def analyze_tree(self, dir):        for dirpath, _, filenames in os.walk(dir):            for path in (Path(dirpath, f) for f in filenames):                if path.endswith(".py"):                    yield self.analyze_file(path)    def analyze(self, *paths):        for path in map(Path, paths):            if path.isdir():                for res in self.analyze_tree(path):                    yield res            else:                yield self.analyze_file(path)    def strip_comments(self, fh):        re_comment = self.re_comment        re_ml_comment_o = self.re_ml_comment_o        re_ml_comment_s = self.re_ml_comment_s        re_ml_comment_e = self.re_ml_comment_e        re_noqa = self.re_noqa        in_ml = False        for line in fh.readlines():            if in_ml:                if re_ml_comment_e.match(line):                    in_ml = False            else:                if re_noqa.match(line) or re_ml_comment_o.match(line):                    pass                elif re_ml_comment_s.match(line):                    in_ml = True                elif re_comment.match(line):                    pass                else:                    yield line    def announce(self, fmt, **kwargs):        sys.stderr.write((fmt + "\n") % kwargs)def main(argv=sys.argv, exitcode=0):    for _, errors, _ in FlakePP(verbose=True).analyze(*argv[1:]):        if errors:            exitcode = 1    return exitcodeif __name__ == "__main__":    sys.exit(main())
 |