We no longer support Python 2 so we can drop support for linting code
for Python 2 support.
This gets rid of the check for from __future__, enabling us to delete
those imports.
( )
| Alphare |
| hg-reviewers |
We no longer support Python 2 so we can drop support for linting code
for Python 2 support.
This gets rid of the check for from __future__, enabling us to delete
those imports.
| Automatic diff as part of commit; lint not applicable. |
| Automatic diff as part of commit; unit tests not applicable. |
| Path | Packages | |||
|---|---|---|---|---|
| M | contrib/check-py3-compat.py (49 lines) |
| import ast | import ast | ||||
| import importlib | import importlib | ||||
| import os | import os | ||||
| import sys | import sys | ||||
| import traceback | import traceback | ||||
| import warnings | import warnings | ||||
| def check_compat_py2(f): | |||||
| """Check Python 3 compatibility for a file with Python 2""" | |||||
| with open(f, 'rb') as fh: | |||||
| content = fh.read() | |||||
| root = ast.parse(content) | |||||
| # Ignore empty files. | |||||
| if not root.body: | |||||
| return | |||||
| futures = set() | |||||
| haveprint = False | |||||
| for node in ast.walk(root): | |||||
| if isinstance(node, ast.ImportFrom): | |||||
| if node.module == '__future__': | |||||
| futures |= {n.name for n in node.names} | |||||
| elif isinstance(node, ast.Print): | |||||
| haveprint = True | |||||
| if 'absolute_import' not in futures: | |||||
| print('%s not using absolute_import' % f) | |||||
| if haveprint and 'print_function' not in futures: | |||||
| print('%s requires print_function' % f) | |||||
| def check_compat_py3(f): | def check_compat_py3(f): | ||||
| """Check Python 3 compatibility of a file with Python 3.""" | """Check Python 3 compatibility of a file with Python 3.""" | ||||
| with open(f, 'rb') as fh: | with open(f, 'rb') as fh: | ||||
| content = fh.read() | content = fh.read() | ||||
| try: | try: | ||||
| ast.parse(content, filename=f) | ast.parse(content, filename=f) | ||||
| except SyntaxError as e: | except SyntaxError as e: | ||||
| else: | else: | ||||
| print( | print( | ||||
| '%s: error importing module: <%s> %s (line %d)' | '%s: error importing module: <%s> %s (line %d)' | ||||
| % (f, type(e).__name__, e, frame.lineno) | % (f, type(e).__name__, e, frame.lineno) | ||||
| ) | ) | ||||
| if __name__ == '__main__': | if __name__ == '__main__': | ||||
| if sys.version_info[0] == 2: | |||||
| fn = check_compat_py2 | |||||
| else: | |||||
| # check_compat_py3 will import every filename we specify as long as it | # check_compat_py3 will import every filename we specify as long as it | ||||
| # starts with one of a few prefixes. It does this by converting | # starts with one of a few prefixes. It does this by converting | ||||
| # specified filenames like 'mercurial/foo.py' to 'mercurial.foo' and | # specified filenames like 'mercurial/foo.py' to 'mercurial.foo' and | ||||
| # importing that. When running standalone (not as part of a test), this | # importing that. When running standalone (not as part of a test), this | ||||
| # means we actually import the installed versions, not the files we just | # means we actually import the installed versions, not the files we just | ||||
| # specified. When running as test-check-py3-compat.t, we technically | # specified. When running as test-check-py3-compat.t, we technically | ||||
| # would import the correct paths, but it's cleaner to have both cases | # would import the correct paths, but it's cleaner to have both cases | ||||
| # use the same import logic. | # use the same import logic. | ||||
| sys.path.insert(0, '.') | sys.path.insert(0, '.') | ||||
| fn = check_compat_py3 | |||||
| for f in sys.argv[1:]: | for f in sys.argv[1:]: | ||||
| with warnings.catch_warnings(record=True) as warns: | with warnings.catch_warnings(record=True) as warns: | ||||
| fn(f) | check_compat_py3(f) | ||||
| for w in warns: | for w in warns: | ||||
| print( | print( | ||||
| warnings.formatwarning( | warnings.formatwarning( | ||||
| w.message, w.category, w.filename, w.lineno | w.message, w.category, w.filename, w.lineno | ||||
| ).rstrip() | ).rstrip() | ||||
| ) | ) | ||||
| sys.exit(0) | sys.exit(0) | ||||