comparison contrib/check-py3-compat.py @ 48872:968b29a5a7fc

check-py3-compat: drop support for Python 2 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. Differential Revision: https://phab.mercurial-scm.org/D12251
author Gregory Szorc <gregory.szorc@gmail.com>
date Sun, 20 Feb 2022 14:52:40 -0700
parents c102b704edb5
children 6000f5b25c9b
comparison
equal deleted inserted replaced
48871:79009cca491e 48872:968b29a5a7fc
13 import importlib 13 import importlib
14 import os 14 import os
15 import sys 15 import sys
16 import traceback 16 import traceback
17 import warnings 17 import warnings
18
19
20 def check_compat_py2(f):
21 """Check Python 3 compatibility for a file with Python 2"""
22 with open(f, 'rb') as fh:
23 content = fh.read()
24 root = ast.parse(content)
25
26 # Ignore empty files.
27 if not root.body:
28 return
29
30 futures = set()
31 haveprint = False
32 for node in ast.walk(root):
33 if isinstance(node, ast.ImportFrom):
34 if node.module == '__future__':
35 futures |= {n.name for n in node.names}
36 elif isinstance(node, ast.Print):
37 haveprint = True
38
39 if 'absolute_import' not in futures:
40 print('%s not using absolute_import' % f)
41 if haveprint and 'print_function' not in futures:
42 print('%s requires print_function' % f)
43 18
44 19
45 def check_compat_py3(f): 20 def check_compat_py3(f):
46 """Check Python 3 compatibility of a file with Python 3.""" 21 """Check Python 3 compatibility of a file with Python 3."""
47 with open(f, 'rb') as fh: 22 with open(f, 'rb') as fh:
92 % (f, type(e).__name__, e, frame.lineno) 67 % (f, type(e).__name__, e, frame.lineno)
93 ) 68 )
94 69
95 70
96 if __name__ == '__main__': 71 if __name__ == '__main__':
97 if sys.version_info[0] == 2: 72 # check_compat_py3 will import every filename we specify as long as it
98 fn = check_compat_py2 73 # starts with one of a few prefixes. It does this by converting
99 else: 74 # specified filenames like 'mercurial/foo.py' to 'mercurial.foo' and
100 # check_compat_py3 will import every filename we specify as long as it 75 # importing that. When running standalone (not as part of a test), this
101 # starts with one of a few prefixes. It does this by converting 76 # means we actually import the installed versions, not the files we just
102 # specified filenames like 'mercurial/foo.py' to 'mercurial.foo' and 77 # specified. When running as test-check-py3-compat.t, we technically
103 # importing that. When running standalone (not as part of a test), this 78 # would import the correct paths, but it's cleaner to have both cases
104 # means we actually import the installed versions, not the files we just 79 # use the same import logic.
105 # specified. When running as test-check-py3-compat.t, we technically 80 sys.path.insert(0, '.')
106 # would import the correct paths, but it's cleaner to have both cases
107 # use the same import logic.
108 sys.path.insert(0, '.')
109 fn = check_compat_py3
110 81
111 for f in sys.argv[1:]: 82 for f in sys.argv[1:]:
112 with warnings.catch_warnings(record=True) as warns: 83 with warnings.catch_warnings(record=True) as warns:
113 fn(f) 84 check_compat_py3(f)
114 85
115 for w in warns: 86 for w in warns:
116 print( 87 print(
117 warnings.formatwarning( 88 warnings.formatwarning(
118 w.message, w.category, w.filename, w.lineno 89 w.message, w.category, w.filename, w.lineno