#!/usr/bin/env python # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. import ast import json import sys def parse_file(f): with open(f, "rb") as fh: content = fh.read() try: return ast.parse(content) except SyntaxError as e: err = { "path": f, "message": e.msg, "lineno": e.lineno, "column": e.offset, "source": e.text, "rule": "is-parseable", } print(json.dumps(err)) def check_compat_py2(f): """Check Python 2 and Python 3 compatibility for a file with Python 2""" root = parse_file(f) # Ignore empty or un-parseable files. if not root or not root.body: return futures = set() haveprint = False future_lineno = 1 may_have_relative_imports = False for node in ast.walk(root): if isinstance(node, ast.ImportFrom): if node.module == "__future__": future_lineno = node.lineno futures |= set(n.name for n in node.names) else: may_have_relative_imports = True elif isinstance(node, ast.Import): may_have_relative_imports = True elif isinstance(node, ast.Print): haveprint = True err = { "path": f, "lineno": future_lineno, "column": 1, } if "absolute_import" not in futures and may_have_relative_imports: err["rule"] = "require absolute_import" err["message"] = "Missing from __future__ import absolute_import" print(json.dumps(err)) if haveprint and "print_function" not in futures: err["rule"] = "require print_function" err["message"] = "Missing from __future__ import print_function" print(json.dumps(err)) def check_compat_py3(f): """Check Python 3 compatibility of a file with Python 3.""" parse_file(f) if __name__ == "__main__": if sys.version_info[0] == 2: fn = check_compat_py2 else: fn = check_compat_py3 manifest = sys.argv[1] with open(manifest, "r") as fh: files = fh.read().splitlines() for f in files: fn(f) sys.exit(0)