OLD | NEW |
| (Empty) |
1 # Copyright 2015 The Chromium Authors. All rights reserved. | |
2 # Use of this source code is governed by a BSD-style license that can be | |
3 # found in the LICENSE file. | |
4 | |
5 import functools | |
6 import os | |
7 import sys | |
8 | |
9 from catapult_base import refactor | |
10 | |
11 | |
12 def Run(sources, target, files_to_update): | |
13 """Move modules and update imports. | |
14 | |
15 Args: | |
16 sources: List of source module or package paths. | |
17 target: Destination module or package path. | |
18 files_to_update: Modules whose imports we should check for changes. | |
19 """ | |
20 # TODO(dtu): Support moving classes and functions. | |
21 moves = tuple(_Move(source, target) for source in sources) | |
22 | |
23 # Update imports and references. | |
24 refactor.Transform(functools.partial(_Update, moves), files_to_update) | |
25 | |
26 # Move files. | |
27 for move in moves: | |
28 os.rename(move.source_path, move.target_path) | |
29 | |
30 | |
31 def _Update(moves, module): | |
32 for import_statement in module.FindAll(refactor.Import): | |
33 for move in moves: | |
34 try: | |
35 if move.UpdateImportAndReferences(module, import_statement): | |
36 break | |
37 except NotImplementedError as e: | |
38 print >> sys.stderr, 'Error updating %s: %s' % (module.file_path, e) | |
39 | |
40 | |
41 class _Move(object): | |
42 def __init__(self, source, target): | |
43 self._source_path = os.path.realpath(source) | |
44 self._target_path = os.path.realpath(target) | |
45 | |
46 if os.path.isdir(self._target_path): | |
47 self._target_path = os.path.join( | |
48 self._target_path, os.path.basename(self._source_path)) | |
49 | |
50 @property | |
51 def source_path(self): | |
52 return self._source_path | |
53 | |
54 @property | |
55 def target_path(self): | |
56 return self._target_path | |
57 | |
58 @property | |
59 def source_module_path(self): | |
60 return _ModulePath(self._source_path) | |
61 | |
62 @property | |
63 def target_module_path(self): | |
64 return _ModulePath(self._target_path) | |
65 | |
66 def UpdateImportAndReferences(self, module, import_statement): | |
67 """Update an import statement in a module and all its references.. | |
68 | |
69 Args: | |
70 module: The refactor.Module to update. | |
71 import_statement: The refactor.Import to update. | |
72 | |
73 Returns: | |
74 True if the import statement was updated, or False if the import statement | |
75 needed no updating. | |
76 """ | |
77 statement_path_parts = import_statement.path.split('.') | |
78 source_path_parts = self.source_module_path.split('.') | |
79 if source_path_parts != statement_path_parts[:len(source_path_parts)]: | |
80 return False | |
81 | |
82 # Update import statement. | |
83 old_name_parts = import_statement.name.split('.') | |
84 new_name_parts = ([self.target_module_path] + | |
85 statement_path_parts[len(source_path_parts):]) | |
86 import_statement.path = '.'.join(new_name_parts) | |
87 new_name = import_statement.name | |
88 | |
89 # Update references. | |
90 for reference in module.FindAll(refactor.Reference): | |
91 reference_parts = reference.value.split('.') | |
92 if old_name_parts != reference_parts[:len(old_name_parts)]: | |
93 continue | |
94 | |
95 new_reference_parts = [new_name] + reference_parts[len(old_name_parts):] | |
96 reference.value = '.'.join(new_reference_parts) | |
97 | |
98 return True | |
99 | |
100 | |
101 def _BaseDir(module_path): | |
102 if not os.path.isdir(module_path): | |
103 module_path = os.path.dirname(module_path) | |
104 | |
105 while '__init__.py' in os.listdir(module_path): | |
106 module_path = os.path.dirname(module_path) | |
107 | |
108 return module_path | |
109 | |
110 | |
111 def _ModulePath(module_path): | |
112 if os.path.split(module_path)[1] == '__init__.py': | |
113 module_path = os.path.dirname(module_path) | |
114 rel_path = os.path.relpath(module_path, _BaseDir(module_path)) | |
115 return os.path.splitext(rel_path)[0].replace(os.sep, '.') | |
OLD | NEW |