示例#1
0
    def leave_ImportFrom(self, original_node: cst.ImportFrom,
                         updated_node: cst.ImportFrom) -> cst.ImportFrom:
        module = updated_node.module
        if module is None:
            return updated_node
        imported_module_name = get_full_name_for_node(module)
        names = original_node.names

        if imported_module_name is None or not isinstance(names, Sequence):
            return updated_node

        else:
            new_names = []
            for import_alias in names:
                alias_name = get_full_name_for_node(import_alias.name)
                if alias_name is not None:
                    qual_name = f"{imported_module_name}.{alias_name}"
                    if self.old_name == qual_name:

                        replacement_module = self.gen_replacement_module(
                            imported_module_name)
                        replacement_obj = self.gen_replacement(alias_name)
                        if not replacement_obj:
                            # The user has requested an `import` statement rather than an `from ... import`.
                            # This will be taken care of in `leave_Module`, in the meantime, schedule for potential removal.
                            new_names.append(import_alias)
                            self.scheduled_removals.add(original_node)
                            continue

                        new_import_alias_name: Union[
                            cst.Attribute,
                            cst.Name] = self.gen_name_or_attr_node(
                                replacement_obj)
                        # Rename on the spot only if this is the only imported name under the module.
                        if len(names) == 1:
                            self.bypass_import = True
                            return updated_node.with_changes(
                                module=cst.parse_expression(
                                    replacement_module),
                                names=(cst.ImportAlias(
                                    name=new_import_alias_name), ),
                            )
                        # Or if the module name is to stay the same.
                        elif replacement_module == imported_module_name:
                            self.bypass_import = True
                            new_names.append(
                                cst.ImportAlias(name=new_import_alias_name))
                    else:
                        if self.old_name.startswith(qual_name + "."):
                            # This import might be in use elsewhere in the code, so schedule a potential removal.
                            self.scheduled_removals.add(original_node)
                        new_names.append(import_alias)

            return updated_node.with_changes(names=new_names)
        return updated_node
示例#2
0
    def leave_ImportFrom(self, original_node: libcst.ImportFrom,
                         updated_node: libcst.ImportFrom) -> libcst.ImportFrom:
        if isinstance(updated_node.names, libcst.ImportStar):
            # There's nothing to do here!
            return updated_node

        # Get the module we're importing as a string, see if we have work to do.
        module = get_absolute_module_for_import(self.context.full_module_name,
                                                updated_node)
        if (module is None or module not in self.module_mapping
                and module not in self.alias_mapping):
            return updated_node

        # We have work to do, mark that we won't modify this again.
        imports_to_add = self.module_mapping.get(module, [])
        if module in self.module_mapping:
            del self.module_mapping[module]
        aliases_to_add = self.alias_mapping.get(module, [])
        if module in self.alias_mapping:
            del self.alias_mapping[module]

        # Now, do the actual update.
        return updated_node.with_changes(names=[
            *(libcst.ImportAlias(name=libcst.Name(imp))
              for imp in sorted(imports_to_add)),
            *(libcst.ImportAlias(
                name=libcst.Name(imp),
                asname=libcst.AsName(name=libcst.Name(alias)),
            ) for (imp, alias) in sorted(aliases_to_add)),
            *updated_node.names,
        ])
示例#3
0
    def leave_ImportFrom(
        self, original_node: cst.ImportFrom, updated_node: cst.ImportFrom
    ) -> Union[cst.RemovalSentinel, cst.ImportFrom]:
        if isinstance(updated_node.names, cst.ImportStar):

            def get_star_imp() -> Optional[ImportFrom]:
                if isinstance(updated_node.module, cst.Attribute):
                    import_name = self.get_import_name_from_attr(
                        attr_node=updated_node.module)
                else:
                    import_name = updated_node.module.value
                location = self.get_location(original_node)
                for imp in self.unused_imports:
                    if (isinstance(imp, ImportFrom) and imp.name == import_name
                            and imp.lineno == location.start.line):
                        return imp
                else:
                    return None

            imp = get_star_imp()
            if imp:
                return self.leave_StarImport(updated_node, imp)
            else:
                return original_node
        rpar = self.get_rpar(updated_node.rpar,
                             self.get_location(original_node))
        return self.leave_import_alike(original_node,
                                       updated_node.with_changes(rpar=rpar))
示例#4
0
    def leave_ImportFrom(
        self, original_node: cst.ImportFrom, updated_node: cst.ImportFrom
    ) -> Union[cst.ImportFrom, 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

        # Make sure we have anything to do with this node.
        names = original_node.names
        if isinstance(names, cst.ImportStar):
            # This is a star import, so we won't remove it.
            return updated_node

        # Make sure we actually know the absolute module.
        module_name = get_absolute_module_for_import(
            self.context.full_module_name, updated_node
        )
        if module_name is None or module_name not in self.unused_obj_imports:
            # This node isn't on our list of todos, so let's bail.
            return updated_node
        objects_to_remove = self.unused_obj_imports[module_name]

        names_to_keep = []
        for import_alias in names:
            # Figure out if it is in our list of things to kill
            for name, alias in objects_to_remove:
                if (
                    name == import_alias.evaluated_name
                    and alias == import_alias.evaluated_alias
                ):
                    break
            else:
                # This is a keeper, we don't have it on our list.
                names_to_keep.append(import_alias)
                continue

            # Now that we know we want to remove this object, 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 == 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] != 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)
示例#5
0
 def leave_StarImport(
     updated_node: cst.ImportFrom,
     imp: ImportFrom,
 ) -> Union[cst.ImportFrom, cst.RemovalSentinel]:
     if imp.suggestions:
         names_to_suggestions = [
             cst.ImportAlias(cst.Name(module)) for module in imp.suggestions
         ]
         return updated_node.with_changes(names=names_to_suggestions)
     else:
         return cst.RemoveFromParent()
示例#6
0
 def leave_ImportFrom(self, original_node: cst.ImportFrom,
                      updated_node: cst.ImportFrom) -> cst.ImportFrom:
     self.import_statements.append(original_node)
     # pyre-fixme[6]: Expected `Union[Attribute, Name]` for 1st param but got
     #  `Optional[Union[Attribute, Name]]`.
     key = _get_attribute_as_string(original_node.module)
     if (original_node.module is not None
             # pyre-fixme[16]: `Optional` has no attribute `value`.
             and original_node.module.value in self.imports):
         names = list(updated_node.names) + self.imports[key].names
         updated_node = updated_node.with_changes(names=tuple(names))
         del self.imports[key]
     return updated_node
示例#7
0
 def leave_StarImport(original_node: cst.ImportFrom,
                      updated_node: cst.ImportFrom,
                      **kwargs) -> Union[cst.ImportFrom, RemovalSentinel]:
     imp = kwargs["imp"]
     if imp["modules"]:
         modules = ",".join(imp["modules"])
         names_to_suggestion = []
         for module in modules.split(","):
             names_to_suggestion.append(cst.ImportAlias(cst.Name(module)))
         return updated_node.with_changes(names=names_to_suggestion)
     else:
         if imp["module"]:
             return cst.RemoveFromParent()
     return original_node
示例#8
0
    def _check_import_from_parent(
        self, original_node: ImportFrom, updated_node: ImportFrom
    ) -> Optional[Union[BaseSmallStatement, RemovalSentinel]]:
        """
        Check for when the parent module of thing to replace is imported.

        When `parent.module.the_thing` is transformed, detect such import:

            from parent import module
        """
        # First, exit early if 'import *' is used
        if isinstance(updated_node.names, ImportStar):
            return None
        # Check whether parent module is imported
        if not import_from_matches(updated_node, self.old_parent_module_parts):
            return None
        # Match, update the node an return it
        new_import_aliases = []
        for import_alias in updated_node.names:
            if import_alias.evaluated_name == self.old_parent_name:
                self.save_import_scope(original_node)
                module_name_str = (import_alias.evaluated_alias
                                   or import_alias.evaluated_name)
                self.context.scratch[self.ctx_key_name_matcher] = m.Attribute(
                    value=m.Name(module_name_str),
                    attr=m.Name(self.old_name),
                )
                self.context.scratch[self.ctx_key_new_func] = Attribute(
                    attr=Name(self.new_name),
                    value=Name(import_alias.evaluated_alias
                               or self.new_parent_name),
                )
                if self.old_parent_module_parts != self.new_parent_module_parts:
                    # import statement needs updating
                    AddImportsVisitor.add_needed_import(
                        context=self.context,
                        module=".".join(self.new_parent_module_parts),
                        obj=self.new_parent_name,
                        asname=import_alias.evaluated_alias,
                    )
                    continue
            new_import_aliases.append(import_alias)
        if not new_import_aliases:
            # Nothing left in the import statement: remove it
            return RemoveFromParent()
        # Some imports are left, update the statement
        new_import_aliases = clean_new_import_aliases(new_import_aliases)
        return updated_node.with_changes(names=new_import_aliases)
示例#9
0
 def leave_ImportFrom(
     self, original_node: ImportFrom, updated_node: ImportFrom
 ) -> Union[BaseSmallStatement, RemovalSentinel]:
     """Update import statements for matching old module name."""
     if not import_from_matches(updated_node,
                                self.old_module_parts) or isinstance(
                                    updated_node.names, ImportStar):
         return updated_node
     # This is a match
     new_names = list(self.gen_new_imported_names(updated_node.names))
     self.save_import_scope(original_node)
     if not new_names:
         # Nothing left in the import statement: remove it
         return RemoveFromParent()
     # Some imports are left, update the statement
     cleaned_names = self.tidy_new_imported_names(new_names)
     return updated_node.with_changes(names=cleaned_names)
示例#10
0
 def leave_ImportFrom(
     self, original_node: ImportFrom, updated_node: ImportFrom
 ) -> Union[BaseSmallStatement, RemovalSentinel]:
     if isinstance(updated_node.names, ImportStar):
         return super().leave_ImportFrom(original_node, updated_node)
     if m.matches(
         updated_node,
         m.ImportFrom(module=module_matcher(["django", "db"])),
     ):
         for imported_name in updated_node.names:
             if m.matches(imported_name, m.ImportAlias(name=m.Name("models"))):
                 self.add_decorator_matcher(
                     m.Decorator(
                         decorator=m.Attribute(
                             value=m.Name("models"), attr=m.Name("permalink")
                         )
                     )
                 )
     if m.matches(
         updated_node,
         m.ImportFrom(module=module_matcher(["django", "db", "models"])),
     ):
         updated_names = []
         for imported_name in updated_node.names:
             if m.matches(imported_name, m.ImportAlias(name=m.Name("permalink"))):
                 decorator_name_str = (
                     imported_name.evaluated_alias or imported_name.evaluated_name
                 )
                 self.add_decorator_matcher(
                     m.Decorator(decorator=m.Name(decorator_name_str))
                 )
             else:
                 updated_names.append(imported_name)
         if not updated_names:
             return RemoveFromParent()
         # sort imports
         new_names = sorted(updated_names, key=lambda n: n.evaluated_name)
         # remove any trailing commas
         last_name = new_names[-1]
         if last_name.comma != MaybeSentinel.DEFAULT:
             new_names[-1] = last_name.with_changes(comma=MaybeSentinel.DEFAULT)
         return updated_node.with_changes(names=new_names)
     return super().leave_ImportFrom(original_node, updated_node)
示例#11
0
    def leave_ImportFrom(self, original_node: cst.ImportFrom,
                         updated_node: cst.ImportFrom):

        new_names = []

        if m.matches(updated_node.names, m.ImportStar()):
            new_names = updated_node.names
        else:
            for el in updated_node.names:
                el_name = el.name.value
                if self.can_rename(
                        el_name) and self.name_generator.in_dictionary(
                            el_name):
                    el = el.with_changes(name=self.get_new_cst_name(el_name))
                new_names.append(el)

        updated_node = updated_node.with_changes(names=new_names)

        return updated_node
示例#12
0
 def leave_ImportFrom(
     self, original_node: cst.ImportFrom, updated_node: cst.ImportFrom
 ) -> cst.ImportFrom:
     self.import_statements.append(original_node)
     # pyre-fixme[6]: Expected `Union[Attribute, Name]` for 1st param but got
     #  `Optional[Union[Attribute, Name]]`.
     key = _get_attribute_as_string(original_node.module)
     import_names = updated_node.names
     module = original_node.module
     if (
         module is not None
         and module.value in self.imports
         and not isinstance(import_names, cst.ImportStar)
     ):
         names_as_string = [_get_name_as_string(name.name) for name in import_names]
         updated_names = self.imports[key].names.union(set(names_as_string))
         names = [cst.ImportAlias(cst.Name(name)) for name in sorted(updated_names)]
         updated_node = updated_node.with_changes(names=tuple(names))
         del self.imports[key]
     return updated_node
示例#13
0
 def leave_ImportFrom(self, original_node: cst.ImportFrom,
                      updated_node: cst.ImportFrom) -> cst.ImportFrom:
     code = self.get_metadata(cst.metadata.PositionProvider, original_node)
     line_no = code.start.line
     new_import_alias = []
     if isinstance(updated_node.names, cst.ImportStar):
         # we do not handle ImportStar
         return updated_node
     for import_alias in updated_node.names:
         if self.is_reimport_from(line_no, updated_node.module,
                                  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
示例#14
0
    def leave_ImportFrom(
        self, original_node: cst.ImportFrom, updated_node: cst.ImportFrom
    ) -> Union[cst.ImportFrom, cst.RemovalSentinel]:
        names = original_node.names
        if isinstance(names, cst.ImportStar):
            # This is a star import, so we won't remove it.
            return updated_node

        # Make sure we actually know the absolute module.
        module_name = get_absolute_module_for_import(
            self.context.full_module_name, updated_node)
        if module_name is None or module_name not in self.unused_obj_imports:
            # This node isn't on our list of todos, so let's bail.
            return updated_node

        updates = self._process_importfrom_aliases(updated_node, names,
                                                   module_name)
        names_to_keep = updates["names"]

        # no changes
        if names_to_keep == 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] != 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),
            ]
        updates["names"] = names_to_keep
        return updated_node.with_changes(**updates)
示例#15
0
    def leave_ImportFrom(self, original_node: libcst.ImportFrom,
                         updated_node: libcst.ImportFrom) -> libcst.ImportFrom:
        if len(updated_node.relative) > 0 or updated_node.module is None:
            # Don't support relative-only imports at the moment.
            return updated_node
        if updated_node.names == "*":
            # There's nothing to do here!
            return updated_node

        # Get the module we're importing as a string, see if we have work to do
        module = self._get_string_name(updated_node.module)
        if module not in self.module_mapping and module not in self.alias_mapping:
            return updated_node

        # We have work to do, mark that we won't modify this again.
        imports_to_add = self.module_mapping.get(module, [])
        if module in self.module_mapping:
            del self.module_mapping[module]
        aliases_to_add = self.alias_mapping.get(module, [])
        if module in self.alias_mapping:
            del self.alias_mapping[module]

        # Now, do the actual update.
        return updated_node.with_changes(names=(
            *[
                libcst.ImportAlias(name=libcst.Name(imp))
                for imp in imports_to_add
            ],
            *[
                libcst.ImportAlias(
                    name=libcst.Name(imp),
                    asname=libcst.AsName(name=libcst.Name(alias)),
                ) for (imp, alias) in aliases_to_add
            ],
            *updated_node.names,
        ))