forked from google/transitfeed
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathadhere_to_PEP8.py
139 lines (122 loc) · 5.99 KB
/
adhere_to_PEP8.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
127
128
129
130
131
132
133
134
135
136
137
138
139
#!/usr/bin/python
import os
import re
class AdhereToPEP8:
def __init__(self):
self._current_path = os.path.dirname(__file__)
self.py_files_list = []
self.file_data = ''
self.alternative_import_strings = {
'import cStringIO as StringIO':
'try:\n import io as StringIO\nexcept ImportError:\n import cStringIO as StringIO',
'import StringIO':
'try:\n import io as StringIO\nexcept ImportError:\n import StringIO',
'import dircache':
'try:\n import os as dircache\nexcept ImportError:\n import dircache',
'import urlparse':
'try:\n from urllib import parse as urlparse # Python 3\n'
'except ImportError:\n import urlparse # Python 2.7',
'import urllib2':
'try:\n from urllib import request as urllib2\n'
'except ImportError:\n import urllib2',
'import cStringIO':
'try:\n from io import StringIO as cStringIO\n'
'except ImportError:\n import cStringIO as StringIO',
'from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer\n':
'try:\n from http.server import BaseHTTPRequestHandler, HTTPServer\n'
'except ImportError:\n from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer',
'from urllib2 import HTTPError, URLError':
'try:\n from urllib.error import HTTPError, URLError\n'
'except ImportError:\n from urllib2 import HTTPError, URLError',
'basestring': 'str',
'raw_input(': 'input(',
'str(': 'str('
}
# TODO: should leave raw_input above for python2.7 and do something like this insead
'''
try:
raw_input('Press enter to continue...')
except EOFError:
input('Press enter to continue...')
'''
def find_py_files(self):
for py_file in self.follow_rabbit(self._current_path):
if py_file.endswith('.py') and py_file not in __file__:
self.py_files_list.append(py_file)
# scan current path and return all files recursively
def follow_rabbit(self, trinity):
for neo in os.scandir(trinity):
# make sure to exclude virtualenv (venv in this case) and hidden folders
if neo.is_dir(follow_symlinks=False) and not neo.name == 'venv' and not neo.name.startswith('.'):
yield from self.follow_rabbit(neo.path)
else:
yield neo.path
'''
ref https://www.python.org/dev/peps/pep-0008/
Use the function naming rules: lowercase with words separated by underscores as necessary to improve readability.
Use one leading underscore only for non-public methods and instance variables.
'''
def bring_code_to_future(self, file):
with open(file, 'r') as f:
self.file_data = f.read()
# print(f'search through {os.path.basename(file)}')
for line in self.file_data.split('\n'):
# check for python 2 imports and replace with try for python3 imports
self.alternative_imports(line)
# check for camel case method names and replace with kebab case
self.convert_to_pep8(line)
# check for class names with underscores and replace with camel case
self.remove_underscore_from_class_name(line)
# write changed data back into the same file.
with open(filename, 'w') as f:
f.write(self.file_data)
def convert_to_pep8(self, line):
match = re.search(r'def\s[A-Z].*\(', line)
if match and not '__init__' in match.group():
wrong_method = self.format_regex_object(match)
# change first capital letter in method name to lower case
correct_method = re.sub(r'^[A-Z]', self.to_lower, wrong_method)
# handle 2 or more letters like CSV or GET first
while re.search('[A-Z?*].[^a-z]', correct_method):
correct_method = re.sub('[A-Z?*].[^a-z]', self.dash_and_lower, correct_method)
# replace the rest of capital letters with _<lowercase>
while not correct_method.islower():
correct_method = re.sub('[A-Z]', self.dash_and_lower, correct_method)
self.replace_in_file(wrong_method, correct_method)
self.replace_in_project(wrong_method, correct_method)
def remove_underscore_from_class_name(self, line):
match = re.search(r'class\s[A-Z].*_[a-z].*\(', line)
if match:
wrong_class = self.format_regex_object(match)
correct_class = re.sub(r'_[a-z]', self.without_dash_and_upper, wrong_class)
self.replace_in_file(wrong_class, correct_class)
self.replace_in_project(wrong_class, correct_class)
def alternative_imports(self, line):
if line in self.alternative_import_strings.keys():
print(f' we got match on {line}')
self.replace_in_file(line, self.alternative_import_strings[line])
def replace_in_file(self, wrong, correct):
self.file_data = self.file_data.replace(wrong, correct)
@staticmethod
def replace_in_project(wrong, correct):
# replace all occurrences in the project
cmd = f"""for f in $(find ./ -type f |grep -Ev 'venv|.git');do sed -i 's/{wrong}/{correct}/g' $f;done"""
os.system(cmd)
@staticmethod
def without_dash_and_upper(match):
return match.group().replace('_', '').upper()
@staticmethod
def to_lower(match):
return match.group().lower()
@staticmethod
def dash_and_lower(match):
return f'_{match.group().lower()}'
@staticmethod
def format_regex_object(match):
return match.group().split(' ')[1].rstrip('(')
if __name__ == '__main__':
fix_names = AdhereToPEP8()
fix_names.find_py_files()
for filename in fix_names.py_files_list:
print(filename)
fix_names.bring_code_to_future(filename)