-
Notifications
You must be signed in to change notification settings - Fork 287
/
auto2to3.py
126 lines (110 loc) · 3.9 KB
/
auto2to3.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#!/usr/bin/env python3
"""Wrapper to run 2to3 automatically at import time.
Usage:
auto2to3 -m mypackage.main_module
auto2to3 mypackage/script.py
By default, all modules imported from a subdirectory of the current
directory will be run through `2to3`. To change this behavior, use the
`--package` or `--dir` flags to `auto2to3` to specify which packages or
directories contain Python 2 code that should be converted.
2to3 output is cached on disk between runs for speed.
Based on auto2to3.py by Georg Brandl:
http://dev.pocoo.org/hg/sandbox/file/tip/auto2to3.py
"""
import argparse
import os
import sys
# imp was deprecated in python 3.6
if sys.version_info >= (3, 6):
import importlib as imp
else:
import imp
import runpy
from io import StringIO
from pkgutil import ImpImporter, ImpLoader
import runpy
import sys
import tempfile
import lib2to3
from lib2to3.refactor import RefactoringTool, get_fixers_from_package
fixes = get_fixers_from_package('lib2to3.fixes')
rt = RefactoringTool(fixes)
PACKAGES = []
DIRS = []
def maybe_2to3(filename, modname=None):
"""Returns a python3 version of filename."""
need_2to3 = False
filename = os.path.abspath(filename)
if any(filename.startswith(d) for d in DIRS):
need_2to3 = True
elif modname is not None and any(modname.startswith(p) for p in PACKAGES):
need_2to3 = True
if not need_2to3:
return filename
outfilename = '/_auto2to3_'.join(os.path.split(filename))
if (not os.path.exists(outfilename) or
os.stat(filename).st_mtime > os.stat(outfilename).st_mtime):
try:
with open(filename) as file:
contents = file.read()
contents = rt.refactor_docstring(contents, filename)
tree = rt.refactor_string(contents, filename)
except Exception as err:
raise ImportError("2to3 couldn't convert %r" % filename)
outfile = open(outfilename, 'wb')
outfile.write(str(tree).encode('utf8'))
outfile.close()
return outfilename
class ToThreeImporter(ImpImporter):
def find_module(self, fullname, path=None):
# this duplicates most of ImpImporter.find_module
subname = fullname.split(".")[-1]
if subname != fullname and self.path is None:
return None
if self.path is None:
path = None
else:
path = [os.path.realpath(self.path)]
try:
file, filename, etc = imp.find_module(subname, path)
except ImportError:
return None
if file and etc[2] == imp.PY_SOURCE:
outfilename = maybe_2to3(filename, modname=fullname)
if outfilename != filename:
file.close()
filename = outfilename
file = open(filename, 'rb')
return ImpLoader(fullname, file, filename, etc)
# setup the hook
sys.path_hooks.append(ToThreeImporter)
for key in sys.path_importer_cache:
if sys.path_importer_cache[key] is None:
sys.path_importer_cache[key] = ToThreeImporter(key)
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--package', action='append')
parser.add_argument('--dir', action='append')
parser.add_argument('-m', action='store', metavar='MODULE')
args, rest = parser.parse_known_args()
if args.package:
PACKAGES.extend(args.package)
if args.dir:
DIRS.extend(os.path.abspath(d) for d in args.dir)
if not PACKAGES and not DIRS:
DIRS.append(os.getcwd())
if args.m:
sys.argv[1:] = rest
runpy.run_module(args.m, run_name='__main__', alter_sys=True)
elif rest:
sys.argv = rest
converted = maybe_2to3(rest[0])
with open(converted) as f:
new_globals = dict(__name__='__main__',
__file__=rest[0])
exec(f.read(), new_globals)
else:
import code
code.interact()
if __name__ == '__main__':
main()