-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathsixify.py
executable file
·115 lines (100 loc) · 3.05 KB
/
sixify.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
#!/usr/bin/env python3
"""
Adds some py2&3 compatibility that modernize/futurize missed
"""
import argparse
from fissix.pygram import python_symbols as syms
from fissix.fixer_util import Name, Dot, Newline, touch_import, find_binding
from bowler import Query, TOKEN
from bowler.types import Leaf, Node
flags = {}
def replace_unicode_methods(node, capture, arguments):
# remove any existing __str__ method
b = find_binding("__str__", capture['suite'])
if b and b.type == syms.funcdef:
b.remove()
# rename __unicode__ to __str__
funcname = capture['funcname'].clone()
funcname.value = '__str__'
capture['funcname'].replace(funcname)
# Add a six import
touch_import(None, "six", node)
# Decorate the class with `@six.python_2_unicode_compatible`
classdef = node.clone()
classdef.prefix = ''
decorated = Node(
syms.decorated,
[
Node(
syms.decorator,
[
Leaf(TOKEN.AT, '@', prefix=node.prefix),
Node(
syms.dotted_name,
[Name('six'), Dot(), Name('python_2_unicode_compatible')],
),
Newline(),
],
prefix=node.prefix,
),
classdef,
],
prefix=node.prefix,
)
node.replace(decorated)
def main():
parser = argparse.ArgumentParser(
description="Adds some py2&3 compatibility that modernize/futurize missed"
)
parser.add_argument(
'--no-input',
dest='interactive',
default=True,
action='store_false',
help="Non-interactive mode",
)
parser.add_argument(
'--no-write',
dest='write',
default=True,
action='store_false',
help="Don't write the changes to the source file, just output a diff to stdout",
)
parser.add_argument(
'--debug',
dest='debug',
default=False,
action='store_true',
help="Spit out debugging information",
)
parser.add_argument(
'files', nargs='+', help="The python source file(s) to operate on."
)
args = parser.parse_args()
# No way to pass this to .modify() callables, so we just set it at module level
flags['debug'] = args.debug
(
# Look for files in the current working directory
Query(*args.files)
.select(
"""
classdef<
"class" classname=NAME any* ":"
suite=suite<
any*
func=funcdef< "def" funcname="__unicode__" parameters< "(" NAME ")" > any* >
any*
>
>
"""
)
.modify(callback=replace_unicode_methods)
# Actually run all of the above.
.execute(
# interactive diff implies write (for the bits the user says 'y' to)
interactive=(args.interactive and args.write),
write=args.write,
)
)
if __name__ == '__main__':
main()