def remove_unused_import_by_node(context: CodemodContext, node: cst.CSTNode) -> None: """ Schedule any imports referenced by ``node`` or one of its children to be removed in a future invocation of this class by updating the ``context`` to include the ``module``, ``obj`` and ``alias`` for each import in question. When subclassing from :class:`~libcst.codemod.CodemodCommand`, this will be performed for you after your transform finishes executing. If you are subclassing from a :class:`~libcst.codemod.Codemod` instead, you will need to call the :meth:`~libcst.codemod.Codemod.transform_module` method on the module under modification with an instance of this class after performing your transform. Note that all imports that are referenced by this ``node`` or its children will only be removed if they are not in use at the time of exeucting :meth:`~libcst.codemod.Codemod.transform_module` on an instance of :class:`~libcst.codemod.visitors.AddImportsVisitor` in order to avoid removing an in-use import. """ # Special case both Import and ImportFrom so they can be # directly removed here. if isinstance(node, cst.Import): for import_alias in node.names: RemoveImportsVisitor.remove_unused_import( context, import_alias.evaluated_name, asname=import_alias.evaluated_alias, ) elif isinstance(node, cst.ImportFrom): names = node.names if isinstance(names, cst.ImportStar): # We don't handle removing this, so ignore it. return module_name = get_absolute_module_for_import( context.full_module_name, node) if module_name is None: raise Exception( "Cannot look up absolute module from relative import!") for import_alias in names: RemoveImportsVisitor.remove_unused_import( context, module_name, obj=import_alias.evaluated_name, asname=import_alias.evaluated_alias, ) else: # Look up all children that could have been imported. Any that # we find will be scheduled for removal. node.visit(RemovedNodeVisitor(context))
def rewrite(self, tree: cst.CSTNode, env: SymbolTable, metadata: tp.MutableMapping) -> PASS_ARGS_T: if self.replace_and: visitor = AndTransformer() tree = tree.visit(visitor) if self.replace_or: visitor = OrTransformer() tree = tree.visit(visitor) if self.replace_not: visitor = NotTransformer() tree = tree.visit(visitor) return tree, env, metadata
def __assert_visit_returns_identity(self, node: cst.CSTNode) -> None: """ When visit is called with a visitor that acts as a no-op, the visit method should return the same node it started with. """ # TODO: We're only checking equality right now, because visit currently clones # the node, since that was easier to implement. We should fix that behavior in a # later version and tighten this check. self.assertEqual(node, node.visit(_NOOPVisitor()))
def _replace_names( node: libcst.CSTNode, wrapper: libcst.metadata.MetadataWrapper, replacements: Dict[str, libcst.CSTNode], ) -> libcst.CSTNode: replacer = _ReplaceTransformer(replacements) with replacer.resolve(wrapper): # The result of node.visit can never be a RemovalSentinel. return cast(libcst.CSTNode, node.visit(replacer))
def rewrite(self, tree: cst.CSTNode, env: SymbolTable, metadata: tp.MutableMapping) -> PASS_ARGS_T: if not isinstance(self.phi, str): phi_name = gen_free_name(tree, env, self.phi_name_prefix) env.locals[phi_name] = self.phi else: phi_name = self.phi visitor = IfExpTransformer(phi_name) tree = tree.visit(visitor) return tree, env, metadata
def assert_(py_ast: cst.CSTNode, transform: Transform) -> cst.CSTNode: """ TODO: in programming `assert` has a context of being passive, not fixing if it finds that it's incorrect, perhaps a more active word should be chosen. Maybe *ensure*? """ matches = transform.matches first_ref_index = None find_attempt = tryFirst(transform.capture_reference_indices) if find_attempt is not notFound: _, (first_ref_index, _) = first(transform.capture_reference_indices) @ unified_visit class Transformer(cst.CSTTransformer): def _leave(self, original: cst.CSTNode, updated: cst.CSTNode) -> cst.CSTNode: # TODO: if global scope query create a module tree from scratch? # NOTE: in the future can cache lib cst node comparisons for speed match = notFound if first_ref_index is not None: match = tryFind(lambda m: original.deep_equals( m.path[first_ref_index].node), matches) if match is not notFound: from_assert = first(astNodeFromAssertion(transform, match)) return from_assert elif original in transform.references: # TODO: replace references to anything destroyed by the transform pass elif find_attempt is notFound and isinstance(updated, cst.Module): module_match = Match( [CaptureExpr().contextualize(node=updated)]) return updated.with_changes(body=(*updated.body, *astNodeFromAssertion(transform, module_match))) else: return updated transformed_tree = py_ast.visit(Transformer()) return transformed_tree
def unmangle_nodes( tree: cst.CSTNode, template_replacements: Mapping[str, ValidReplacementType], ) -> cst.CSTNode: unmangler = TemplateTransformer(template_replacements) return ensure_type(tree.visit(unmangler), cst.CSTNode)
def unroll_for_loops(tree: cst.CSTNode, env: tp.Mapping[str, tp.Any]) -> cst.CSTNode: return tree.visit(Unroller(env))
def rewrite(self, tree: cst.CSTNode, env: SymbolTable, metadata: tp.MutableMapping) -> PASS_ARGS_T: visitor = AssertRemover() tree = tree.visit(visitor) return tree, env, metadata
def _normalize(node: cst.CSTNode): node = node.visit(StripParens()) node = node.visit(WhiteSpaceNormalizer()) node.validate_types_deep() return node