Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(488)

Side by Side Diff: tools/telemetry/refactor

Issue 1033053002: [telemetry] Refactoring script. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 #! /usr/bin/env python
2 # Copyright 2015 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 import cStringIO
7 import imp
8 import inspect
9 import os
10 import re
11 import sys
12
13 from telemetry.core import command_line
14 from telemetry.util import path
15
16
17 # All folders dependent on Telemetry, found using a code search.
18 BASE_DIRS = (
19 path.GetTelemetryDir(),
20 os.path.join(path.GetChromiumSrcDir(), 'chrome', 'test', 'telemetry'),
21 os.path.join(path.GetChromiumSrcDir(), 'content', 'test', 'gpu'),
22 os.path.join(path.GetChromiumSrcDir(), 'tools', 'bisect-manual-test.py'),
23 os.path.join(path.GetChromiumSrcDir(), 'tools', 'chrome_proxy'),
24 os.path.join(path.GetChromiumSrcDir(), 'tools', 'perf'),
25 os.path.join(path.GetChromiumSrcDir(),
26 'tools', 'profile_chrome', 'perf_controller.py'),
27 os.path.join(path.GetChromiumSrcDir(), 'tools', 'run-bisect-manual-test.py'),
28 os.path.join(path.GetChromiumSrcDir(),
29 'third_party', 'skia', 'tools', 'skp', 'page_sets'),
30 os.path.join(path.GetChromiumSrcDir(), 'third_party', 'trace-viewer',
eakuefner 2015/03/27 17:55:05 I'm a little worried about potentially accidentall
dtu 2015/03/27 19:17:15 Since we plan on using Telemetry in the future, it
31 'third_party', 'tvcm', 'tvcm', 'browser_controller.py'),
32 )
33
34
35 def SortImportGroups(module_path):
36 """Sort each group of imports in the given Python module.
37
38 A group is a collection of adjacent import statements, with no non-import
39 lines in between. Groups are sorted according to the Google Python Style
40 Guide: "lexicographically, ignoring case, according to each module's full
41 package path."
42 """
43 _TransformImportGroups(module_path, _SortImportGroup)
44
45
46 def _SortImportGroup(import_group):
47 def _ImportComparator(import1, import2):
48 _, root1, module1, _, _ = import1
49 _, root2, module2, _, _ = import2
50 full_module1 = (root1 + '.' + module1 if root1 else module1).lower()
51 full_module2 = (root2 + '.' + module2 if root2 else module2).lower()
52 return cmp(full_module1, full_module2)
53 return sorted(import_group, cmp=_ImportComparator)
54
55
56 def _TransformImportGroups(module_path, transformation):
57 """Apply a transformation to each group of imports in the given module."""
eakuefner 2015/03/27 17:55:06 Please write the expected type of `transformation`
dtu 2015/03/27 19:17:15 I did it anyway, since the function is non-trivial
58 def _WriteImports(output_stream, import_group):
59 for indent, root, module, alias, suffix in transformation(import_group):
60 output_stream.write(indent)
61 if root:
62 output_stream.write('from ')
63 output_stream.write(root)
64 output_stream.write(' ')
65 output_stream.write('import ')
66 output_stream.write(module)
67 if alias:
68 output_stream.write(' as ')
69 output_stream.write(alias)
70 output_stream.write(suffix)
71 output_stream.write('\n')
72
73 # Read the file so we can diff it later to determine if we made any changes.
74 with open(module_path, 'r') as module_file:
75 original_file = module_file.read()
76
77 # Locate imports using regex, group them, and transform each one.
78 # This regex produces a tuple of (indent, root, module, alias, suffix).
79 regex = (r'(\s*)(?:from ((?:[a-z0-9_]+\.)*[a-z0-9_]+) )?'
80 r'import ((?:[a-z0-9_]+\.)*[A-Za-z0-9_]+)(?: as ([A-Za-z0-9_]+))?(.*)')
81 pattern = re.compile(regex)
82
83 updated_file = cStringIO.StringIO()
84 with open(module_path, 'r') as module_file:
85 import_group = []
86 for line in module_file:
87 import_match = pattern.match(line)
88 if import_match:
89 import_group.append(list(import_match.groups()))
90 continue
91
92 if not import_group:
93 updated_file.write(line)
94 continue
95
96 _WriteImports(updated_file, import_group)
97 import_group = []
98
99 Updated_file.write(line)
100
101 if import_group:
102 _WriteImports(updated_file, import_group)
103 import_group = []
104
105 if original_file == updated_file.getvalue():
106 return False
107
108 with open(module_path, 'w') as module_file:
109 module_file.write(updated_file.getvalue())
110 return True
111
112
113 def _ListFiles(base_directory, should_include_dir, should_include_file):
114 matching_files = []
115 for root, dirs, files in os.walk(base_directory):
116 dirs[:] = [dir_name for dir_name in dirs if should_include_dir(dir_name)]
117 matching_files += [os.path.join(root, file_name)
118 for file_name in files if should_include_file(file_name)]
119 return sorted(matching_files)
120
121
122 class Count(command_line.Command):
123 """Print the number of public modules."""
124
125 def Run(self, args):
126 modules = _ListFiles(path.GetTelemetryDir(),
127 self._IsPublicApiDir, self._IsPublicApiFile)
128 print len(modules)
129 return 0
130
131 @staticmethod
132 def _IsPublicApiDir(dir_name):
133 return (dir_name[0] != '.' and dir_name[0] != '_' and
134 not dir_name.startswith('internal') and not dir_name == 'third_party')
135
136 @staticmethod
137 def _IsPublicApiFile(file_name):
138 root, ext = os.path.splitext(file_name)
139 return (file_name[0] != '.' and
140 not root.endswith('_unittest') and ext == '.py')
141
142
143 class Mv(command_line.Command):
144 """Move modules or packages."""
145
146 @classmethod
147 def AddCommandLineArgs(cls, parser):
148 parser.add_argument('source', nargs='+')
149 parser.add_argument('destination')
150
151 @classmethod
152 def ProcessCommandLineArgs(cls, parser, args):
153 for source in args.source:
154 # Ensure source path exists.
155 if not os.path.exists(source):
156 parser.error('"%s" not found.' % source)
157
158 # Ensure source path is in one of the BASE_DIRS.
159 for base_dir in BASE_DIRS:
160 if path.InDirectory(source, base_dir):
161 break
162 else:
163 parser.error('Source path "%s" is not in any of the base dirs.')
164
165 # Ensure destination path exists.
166 if not os.path.exists(args.destination):
167 parser.error('"%s" not found.' % args.destination)
168
169 # Ensure destination path is in one of the BASE_DIRS.
170 for base_dir in BASE_DIRS:
171 if path.InDirectory(args.destination, base_dir):
172 break
173 else:
174 parser.error('Destination path "%s" is not in any of the base dirs.')
175
176 # If there are multiple source paths, ensure destination is a directory.
177 if len(args.source) > 1 and not os.path.isdir(args.destination):
178 parser.error('Target "%s" is not a directory.' % args.destination)
179
180 # Ensure destination is not in any of the source paths.
181 for source in args.source:
182 if path.InDirectory(args.destination, source):
183 parser.error('Cannot move "%s" to a subdirectory of itself, "%s".' %
184 (source, args.destination))
185
186 def Run(self, args):
187 for dest_base_dir in BASE_DIRS:
188 if path.InDirectory(args.destination, dest_base_dir):
189 break
190
191 # Get a list of old and new module names for renaming imports.
192 moved_modules = {}
193 for source in args.source:
194 for source_base_dir in BASE_DIRS:
195 if path.InDirectory(source, source_base_dir):
196 break
197
198 source_dir = os.path.dirname(os.path.normpath(source))
199
200 if os.path.isdir(source):
201 source_files = _ListFiles(source,
202 self._IsSourceDir, self._IsPythonModule)
203 else:
204 source_files = (source,)
205
206 for source_file_path in source_files:
207 source_rel_path = os.path.relpath(source_file_path, source_base_dir)
208 source_module_name = os.path.splitext(
209 source_rel_path)[0].replace(os.sep, '.')
210
211 source_tree = os.path.relpath(source_file_path, source_dir)
212 dest_path = os.path.join(args.destination, source_tree)
213 dest_rel_path = os.path.relpath(dest_path, dest_base_dir)
214 dest_module_name = os.path.splitext(
215 dest_rel_path)[0].replace(os.sep, '.')
216
217 moved_modules[source_module_name] = dest_module_name
218
219 # Move things!
220 if os.path.isdir(args.destination):
221 for source in args.source:
222 destination_path = os.path.join(
223 args.destination, os.path.split(os.path.normpath(source))[1])
224 os.rename(source, destination_path)
225 else:
226 assert len(args.source) == 1
227 os.rename(args.source.pop(), args.destination)
228
229 # Update imports!
230 def _UpdateImportGroup(import_group):
231 modified = False
232 for import_line in import_group:
233 _, root, module, _, _ = import_line
234 full_module = root + '.' + module if root else module
235
236 if full_module not in moved_modules:
237 continue
238
239 modified = True
240
241 # Update import line.
242 new_root, _, new_module = moved_modules[full_module].rpartition('.')
243 import_line[1] = new_root
244 import_line[2] = new_module
245
246 if modified:
247 return _SortImportGroup(import_group)
248 else:
249 return import_group
250
251 for base_dir in BASE_DIRS:
252 for module_path in _ListFiles(base_dir,
253 self._IsSourceDir, self._IsPythonModule):
254 if not _TransformImportGroups(module_path, _UpdateImportGroup):
255 continue
256
257 # TODO(dtu): Update occurrences.
258
259 print moved_modules
260
261 return 0
262
263 @staticmethod
264 def _IsSourceDir(dir_name):
265 return dir_name[0] != '.' and dir_name != 'third_party'
266
267 @staticmethod
268 def _IsPythonModule(file_name):
269 _, ext = os.path.splitext(file_name)
270 return ext == '.py'
271
272
273 class RefactorCommand(command_line.SubcommandCommand):
eakuefner 2015/03/27 17:55:05 Do we get usage text for free with this?
dtu 2015/03/27 19:17:15 Yes, but its default formatting is like so-so.
274 commands = (Count, Mv,)
275
276
277 if __name__ == '__main__':
278 sys.exit(RefactorCommand.main())
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698