def leave_Import( self, original_node: cst.Import, updated_node: cst.Import ) -> Union[cst.Import, cst.RemovalSentinel]: name_idxs_to_remove = [] for n, alias in enumerate(original_node.names): if alias.asname: # If an `import ...` has an alias, then it simply needs to be # replaced, because that alias will necessarily serve as a type # of indirect import. indirect_ref = cst.helpers.parse_template_expression( self.pkg_fullname + ".{name}", name=alias.asname.name) direct_ref = alias.name indirect_ref_name = cst.helpers.get_full_name_for_node( indirect_ref) self.rewrites[indirect_ref_name] = direct_ref name_idxs_to_remove.append(n) else: module_fullname = cst.helpers.get_full_name_for_node( alias.name) module_package = self.pkg_info.fullnames_to_packages[ module_fullname] if module_package == self.pkg_fullname: pass elif module_package >= self.pkg_fullname: # The imported object is in a sub-package of this package # We could remove it, but that would require new `import` # statements in the modules that use this direct reference. # TODO: This seems like a good optional functionality to # offer. pass else: # This import is for an object above this package level, # but it's not an aliased import, so it can't be an # indirect reference, but it could definitely be # introducing some unwanted (sub-)package dependencies. pass if name_idxs_to_remove: new_names = tuple(name for n, name in enumerate(updated_node.names) if n not in name_idxs_to_remove) if not new_names: return cst.RemoveFromParent() updated_node = updated_node.with_changes(names=new_names) return updated_node
def leave_Import( self, original_node: cst.Import, updated_node: cst.Import ) -> Union[cst.Import, cst.RemovalSentinel]: # Grab the scope for this import. If we don't have scope, we can't determine # whether this import is unused so it is unsafe to remove. scope = self.get_metadata(ScopeProvider, original_node, None) if scope is None: return updated_node names_to_keep = [] for import_alias in original_node.names: if import_alias.evaluated_name not in self.unused_module_imports: # This is a keeper since we aren't removing it names_to_keep.append(import_alias) continue if ( import_alias.evaluated_alias != self.unused_module_imports[import_alias.evaluated_name] ): # This is a keeper since the alias does not match # what we are looking for. names_to_keep.append(import_alias) continue # Now that we know we want to remove this module, figure out if # there are any live references to it. if self._is_in_use(scope, import_alias): names_to_keep.append(import_alias) continue # no changes if names_to_keep == original_node.names: return updated_node # Now, either remove this statement or remove the imports we are # deleting from this statement. if len(names_to_keep) == 0: return cst.RemoveFromParent() if names_to_keep[-1] != original_node.names[-1]: # Remove trailing comma in order to not mess up import statements. names_to_keep = [ *names_to_keep[:-1], names_to_keep[-1].with_changes(comma=cst.MaybeSentinel.DEFAULT), ] return updated_node.with_changes(names=names_to_keep)
def leave_Import(self, original_node: cst.Import, updated_node: cst.Import) -> cst.Import: code = self.get_metadata(cst.metadata.PositionProvider, original_node) new_import_alias = [] line_no = code.start.line for import_alias in updated_node.names: if self.is_reimport(line_no, import_alias): continue new_import_alias.append(import_alias) if new_import_alias: new_import_alias[-1] = new_import_alias[-1].with_changes( comma=cst.MaybeSentinel.DEFAULT) return updated_node.with_changes(names=new_import_alias) if len(new_import_alias) == 0: return cst.RemoveFromParent() return updated_node
def leave_Import( self, original_node: cst.Import, updated_node: cst.Import ) -> Union[cst.Import, cst.RemovalSentinel]: names_to_keep = [] for import_alias in original_node.names: if import_alias.evaluated_name not in self.unused_module_imports: # This is a keeper since we aren't removing it names_to_keep.append(import_alias) continue if (import_alias.evaluated_alias != self.unused_module_imports[import_alias.evaluated_name]): # This is a keeper since the alias does not match # what we are looking for. names_to_keep.append(import_alias) continue # Now that we know we want to remove this module, figure out if # there are any live references to it. if import_alias not in self._unused_imports: names_to_keep.append(import_alias) continue # no changes if names_to_keep == original_node.names: return updated_node # Now, either remove this statement or remove the imports we are # deleting from this statement. if len(names_to_keep) == 0: return cst.RemoveFromParent() if names_to_keep[-1] != original_node.names[-1]: # Remove trailing comma in order to not mess up import statements. names_to_keep = [ *names_to_keep[:-1], names_to_keep[-1].with_changes( comma=cst.MaybeSentinel.DEFAULT), ] return updated_node.with_changes(names=names_to_keep)
def leave_Import(self, original_node: cst.Import, updated_node: cst.Import) -> cst.Import: new_names = [] for import_alias in updated_node.names: import_alias_name = import_alias.name import_alias_full_name = get_full_name_for_node(import_alias_name) if import_alias_full_name is None: raise Exception( "Could not parse full name for ImportAlias.name node.") if isinstance(import_alias_name, cst.Name) and self.old_name.startswith( import_alias_full_name + "."): # Might, be in use elsewhere in the code, so schedule a potential removal, and add another alias. new_names.append(import_alias) self.scheduled_removals.add(original_node) new_names.append( cst.ImportAlias(name=cst.Name( value=self.gen_replacement_module( import_alias_full_name)))) self.bypass_import = True elif isinstance(import_alias_name, cst.Attribute) and self.old_name.startswith( import_alias_full_name + "."): # Same idea as above. new_names.append(import_alias) self.scheduled_removals.add(original_node) new_name_node: Union[ cst.Attribute, cst.Name] = self.gen_name_or_attr_node( self.gen_replacement_module(import_alias_full_name)) new_names.append(cst.ImportAlias(name=new_name_node)) self.bypass_import = True else: new_names.append(import_alias) return updated_node.with_changes(names=new_names)