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