/external-py3/rope/refactor/importutils/actions.py
Python | 359 lines | 290 code | 58 blank | 11 comment | 97 complexity | d3a5495955ff8ce42382cbbadbf8c591 MD5 | raw file
1import os 2import sys 3 4from rope.base import pyobjects, exceptions, stdmods 5from rope.refactor import occurrences 6from rope.refactor.importutils import importinfo 7 8 9class ImportInfoVisitor(object): 10 11 def dispatch(self, import_): 12 try: 13 method_name = 'visit' + import_.import_info.__class__.__name__ 14 method = getattr(self, method_name) 15 return method(import_, import_.import_info) 16 except exceptions.ModuleNotFoundError: 17 pass 18 19 def visitEmptyImport(self, import_stmt, import_info): 20 pass 21 22 def visitNormalImport(self, import_stmt, import_info): 23 pass 24 25 def visitFromImport(self, import_stmt, import_info): 26 pass 27 28 29class RelativeToAbsoluteVisitor(ImportInfoVisitor): 30 31 def __init__(self, pycore, current_folder): 32 self.to_be_absolute = [] 33 self.pycore = pycore 34 self.folder = current_folder 35 self.context = importinfo.ImportContext(pycore, current_folder) 36 37 def visitNormalImport(self, import_stmt, import_info): 38 self.to_be_absolute.extend(self._get_relative_to_absolute_list(import_info)) 39 new_pairs = [] 40 for name, alias in import_info.names_and_aliases: 41 resource = self.pycore.find_module(name, folder=self.folder) 42 if resource is None: 43 new_pairs.append((name, alias)) 44 continue 45 absolute_name = self.pycore.modname(resource) 46 new_pairs.append((absolute_name, alias)) 47 if not import_info._are_name_and_alias_lists_equal( 48 new_pairs, import_info.names_and_aliases): 49 import_stmt.import_info = importinfo.NormalImport(new_pairs) 50 51 def _get_relative_to_absolute_list(self, import_info): 52 result = [] 53 for name, alias in import_info.names_and_aliases: 54 if alias is not None: 55 continue 56 resource = self.pycore.find_module(name, folder=self.folder) 57 if resource is None: 58 continue 59 absolute_name = self.pycore.modname(resource) 60 if absolute_name != name: 61 result.append((name, absolute_name)) 62 return result 63 64 def visitFromImport(self, import_stmt, import_info): 65 resource = import_info.get_imported_resource(self.context) 66 if resource is None: 67 return None 68 absolute_name = self.pycore.modname(resource) 69 if import_info.module_name != absolute_name: 70 import_stmt.import_info = importinfo.FromImport( 71 absolute_name, 0, import_info.names_and_aliases) 72 73 74class FilteringVisitor(ImportInfoVisitor): 75 76 def __init__(self, pycore, folder, can_select): 77 self.to_be_absolute = [] 78 self.pycore = pycore 79 self.can_select = self._transform_can_select(can_select) 80 self.context = importinfo.ImportContext(pycore, folder) 81 82 def _transform_can_select(self, can_select): 83 def can_select_name_and_alias(name, alias): 84 imported = name 85 if alias is not None: 86 imported = alias 87 return can_select(imported) 88 return can_select_name_and_alias 89 90 def visitNormalImport(self, import_stmt, import_info): 91 new_pairs = [] 92 for name, alias in import_info.names_and_aliases: 93 if self.can_select(name, alias): 94 new_pairs.append((name, alias)) 95 return importinfo.NormalImport(new_pairs) 96 97 def visitFromImport(self, import_stmt, import_info): 98 if _is_future(import_info): 99 return import_info 100 new_pairs = [] 101 if import_info.is_star_import(): 102 for name in import_info.get_imported_names(self.context): 103 if self.can_select(name, None): 104 new_pairs.append(import_info.names_and_aliases[0]) 105 break 106 else: 107 for name, alias in import_info.names_and_aliases: 108 if self.can_select(name, alias): 109 new_pairs.append((name, alias)) 110 return importinfo.FromImport( 111 import_info.module_name, import_info.level, new_pairs) 112 113 114class RemovingVisitor(ImportInfoVisitor): 115 116 def __init__(self, pycore, folder, can_select): 117 self.to_be_absolute = [] 118 self.pycore = pycore 119 self.filtering = FilteringVisitor(pycore, folder, can_select) 120 121 def dispatch(self, import_): 122 result = self.filtering.dispatch(import_) 123 if result is not None: 124 import_.import_info = result 125 126 127class AddingVisitor(ImportInfoVisitor): 128 """A class for adding imports 129 130 Given a list of `ImportInfo`\s, it tries to add each import to the 131 module and returns `True` and gives up when an import can be added 132 to older ones. 133 134 """ 135 136 def __init__(self, pycore, import_list): 137 self.pycore = pycore 138 self.import_list = import_list 139 self.import_info = None 140 141 def dispatch(self, import_): 142 for import_info in self.import_list: 143 self.import_info = import_info 144 if ImportInfoVisitor.dispatch(self, import_): 145 return True 146 147 # TODO: Handle adding relative and absolute imports 148 def visitNormalImport(self, import_stmt, import_info): 149 if not isinstance(self.import_info, import_info.__class__): 150 return False 151 # Adding ``import x`` and ``import x.y`` that results ``import x.y`` 152 if len(import_info.names_and_aliases) == \ 153 len(self.import_info.names_and_aliases) == 1: 154 imported1 = import_info.names_and_aliases[0] 155 imported2 = self.import_info.names_and_aliases[0] 156 if imported1[1] == imported2[1] is None: 157 if imported1[0].startswith(imported2[0] + '.'): 158 return True 159 if imported2[0].startswith(imported1[0] + '.'): 160 import_stmt.import_info = self.import_info 161 return True 162 # Multiple imports using a single import statement is discouraged 163 # so we won't bother adding them. 164 if self.import_info._are_name_and_alias_lists_equal( 165 import_info.names_and_aliases, self.import_info.names_and_aliases): 166 return True 167 168 def visitFromImport(self, import_stmt, import_info): 169 if isinstance(self.import_info, import_info.__class__) and \ 170 import_info.module_name == self.import_info.module_name and \ 171 import_info.level == self.import_info.level: 172 if import_info.is_star_import(): 173 return True 174 if self.import_info.is_star_import(): 175 import_stmt.import_info = self.import_info 176 return True 177 new_pairs = list(import_info.names_and_aliases) 178 for pair in self.import_info.names_and_aliases: 179 if pair not in new_pairs: 180 new_pairs.append(pair) 181 import_stmt.import_info = importinfo.FromImport( 182 import_info.module_name, import_info.level, new_pairs) 183 return True 184 185 186class ExpandStarsVisitor(ImportInfoVisitor): 187 188 def __init__(self, pycore, folder, can_select): 189 self.pycore = pycore 190 self.filtering = FilteringVisitor(pycore, folder, can_select) 191 self.context = importinfo.ImportContext(pycore, folder) 192 193 def visitNormalImport(self, import_stmt, import_info): 194 self.filtering.dispatch(import_stmt) 195 196 def visitFromImport(self, import_stmt, import_info): 197 if import_info.is_star_import(): 198 new_pairs = [] 199 for name in import_info.get_imported_names(self.context): 200 new_pairs.append((name, None)) 201 new_import = importinfo.FromImport( 202 import_info.module_name, import_info.level, new_pairs) 203 import_stmt.import_info = \ 204 self.filtering.visitFromImport(None, new_import) 205 else: 206 self.filtering.dispatch(import_stmt) 207 208 209class SelfImportVisitor(ImportInfoVisitor): 210 211 def __init__(self, pycore, current_folder, resource): 212 self.pycore = pycore 213 self.folder = current_folder 214 self.resource = resource 215 self.to_be_fixed = set() 216 self.to_be_renamed = set() 217 self.context = importinfo.ImportContext(pycore, current_folder) 218 219 def visitNormalImport(self, import_stmt, import_info): 220 new_pairs = [] 221 for name, alias in import_info.names_and_aliases: 222 resource = self.pycore.find_module(name, folder=self.folder) 223 if resource is not None and resource == self.resource: 224 imported = name 225 if alias is not None: 226 imported = alias 227 self.to_be_fixed.add(imported) 228 else: 229 new_pairs.append((name, alias)) 230 if not import_info._are_name_and_alias_lists_equal( 231 new_pairs, import_info.names_and_aliases): 232 import_stmt.import_info = importinfo.NormalImport(new_pairs) 233 234 def visitFromImport(self, import_stmt, import_info): 235 resource = import_info.get_imported_resource(self.context) 236 if resource is None: 237 return 238 if resource == self.resource: 239 self._importing_names_from_self(import_info, import_stmt) 240 return 241 pymodule = self.pycore.resource_to_pyobject(resource) 242 new_pairs = [] 243 for name, alias in import_info.names_and_aliases: 244 try: 245 result = pymodule[name].get_object() 246 if isinstance(result, pyobjects.PyModule) and \ 247 result.get_resource() == self.resource: 248 imported = name 249 if alias is not None: 250 imported = alias 251 self.to_be_fixed.add(imported) 252 else: 253 new_pairs.append((name, alias)) 254 except exceptions.AttributeNotFoundError: 255 new_pairs.append((name, alias)) 256 if not import_info._are_name_and_alias_lists_equal( 257 new_pairs, import_info.names_and_aliases): 258 import_stmt.import_info = importinfo.FromImport( 259 import_info.module_name, import_info.level, new_pairs) 260 261 def _importing_names_from_self(self, import_info, import_stmt): 262 if not import_info.is_star_import(): 263 for name, alias in import_info.names_and_aliases: 264 if alias is not None: 265 self.to_be_renamed.add((alias, name)) 266 import_stmt.empty_import() 267 268 269class SortingVisitor(ImportInfoVisitor): 270 271 def __init__(self, pycore, current_folder): 272 self.pycore = pycore 273 self.folder = current_folder 274 self.standard = set() 275 self.third_party = set() 276 self.in_project = set() 277 self.future = set() 278 self.context = importinfo.ImportContext(pycore, current_folder) 279 280 def visitNormalImport(self, import_stmt, import_info): 281 if import_info.names_and_aliases: 282 name, alias = import_info.names_and_aliases[0] 283 resource = self.pycore.find_module( 284 name, folder=self.folder) 285 self._check_imported_resource(import_stmt, resource, name) 286 287 def visitFromImport(self, import_stmt, import_info): 288 resource = import_info.get_imported_resource(self.context) 289 self._check_imported_resource(import_stmt, resource, 290 import_info.module_name) 291 292 def _check_imported_resource(self, import_stmt, resource, imported_name): 293 info = import_stmt.import_info 294 if resource is not None and resource.project == self.pycore.project: 295 self.in_project.add(import_stmt) 296 elif _is_future(info): 297 self.future.add(import_stmt) 298 elif imported_name.split('.')[0] in stdmods.standard_modules(): 299 self.standard.add(import_stmt) 300 else: 301 self.third_party.add(import_stmt) 302 303 304class LongImportVisitor(ImportInfoVisitor): 305 306 def __init__(self, current_folder, pycore, maxdots, maxlength): 307 self.maxdots = maxdots 308 self.maxlength = maxlength 309 self.to_be_renamed = set() 310 self.current_folder = current_folder 311 self.pycore = pycore 312 self.new_imports = [] 313 314 def visitNormalImport(self, import_stmt, import_info): 315 new_pairs = [] 316 for name, alias in import_info.names_and_aliases: 317 if alias is None and self._is_long(name): 318 self.to_be_renamed.add(name) 319 last_dot = name.rindex('.') 320 from_ = name[:last_dot] 321 imported = name[last_dot + 1:] 322 self.new_imports.append( 323 importinfo.FromImport(from_, 0, ((imported, None), ))) 324 325 def _is_long(self, name): 326 return name.count('.') > self.maxdots or \ 327 ('.' in name and len(name) > self.maxlength) 328 329 330class RemovePyNameVisitor(ImportInfoVisitor): 331 332 def __init__(self, pycore, pymodule, pyname, folder): 333 self.pymodule = pymodule 334 self.pyname = pyname 335 self.context = importinfo.ImportContext(pycore, folder) 336 337 def visitFromImport(self, import_stmt, import_info): 338 new_pairs = [] 339 if not import_info.is_star_import(): 340 for name, alias in import_info.names_and_aliases: 341 try: 342 pyname = self.pymodule[alias or name] 343 if occurrences.same_pyname(self.pyname, pyname): 344 continue 345 except exceptions.AttributeNotFoundError: 346 pass 347 new_pairs.append((name, alias)) 348 return importinfo.FromImport( 349 import_info.module_name, import_info.level, new_pairs) 350 351 def dispatch(self, import_): 352 result = ImportInfoVisitor.dispatch(self, import_) 353 if result is not None: 354 import_.import_info = result 355 356 357def _is_future(info): 358 return isinstance(info, importinfo.FromImport) and \ 359 info.module_name == '__future__'