class RemoveBarTransformer(VisitorBasedCodemodCommand): METADATA_DEPENDENCIES = (QualifiedNameProvider, ScopeProvider) @m.leave( m.SimpleStatementLine(body=[ m.Expr( m.Call(metadata=m.MatchMetadata( QualifiedNameProvider, { QualifiedName( source=QualifiedNameSource.IMPORT, name="foo.bar", ) }, ))) ])) def _leave_foo_bar( self, original_node: cst.SimpleStatementLine, updated_node: cst.SimpleStatementLine, ) -> cst.RemovalSentinel: RemoveImportsVisitor.remove_unused_import_by_node( self.context, original_node) return cst.RemoveFromParent()
def leave_Expr(self, original_node, updated_node): final_node = super().leave_Expr(original_node, updated_node) if is_pure(final_node.value): if m.matches(final_node, m.Expr(m.SimpleString())): s = final_node.value.value if s.startswith('"""'): return final_node return cst.RemoveFromParent() return final_node
def leave_Expr( self, original_node: cst.Expr, updated_node: cst.Expr ) -> Union[cst.BaseSmallStatement, cst.RemovalSentinel]: if match.matches(original_node, match.Expr(value=match.SimpleString())): return updated_node.with_changes(value=cst.SimpleString( value='"""[docstring]"""')) else: return updated_node
def on_leave(self, original_node, updated_node): final_node = super().on_leave(original_node, updated_node) if (isinstance(final_node, cst.BaseStatement) and not m.matches( final_node, m.SimpleStatementLine(body=[m.Expr(m.SimpleString())])) and self.exec_counts[original_node] == 0): return cst.RemoveFromParent() return final_node
def leave_Expr(self, original_node, updated_node): if m.matches(updated_node, m.Expr(m.SimpleString())): s = updated_node.value.value if s.startswith('"""'): lines = s[3:-3].splitlines() final = '' for line in lines: if line.strip() != '': final = line break return updated_node.with_changes( value=cst.SimpleString(f'"""{final}"""')) return updated_node
def _split_module( self, orig_module: libcst.Module, updated_module: libcst.Module ) -> Tuple[List[Union[libcst.SimpleStatementLine, libcst.BaseCompoundStatement]], List[Union[libcst.SimpleStatementLine, libcst.BaseCompoundStatement]], List[Union[ libcst.SimpleStatementLine, libcst.BaseCompoundStatement]], ]: statement_before_import_location = 0 import_add_location = 0 # never insert an import before initial __strict__ flag if m.matches( orig_module, m.Module(body=[ m.SimpleStatementLine(body=[ m.Assign(targets=[ m.AssignTarget(target=m.Name("__strict__")) ]) ]), m.ZeroOrMore(), ]), ): statement_before_import_location = import_add_location = 1 # This works under the principle that while we might modify node contents, # we have yet to modify the number of statements. So we can match on the # original tree but break up the statements of the modified tree. If we # change this assumption in this visitor, we will have to change this code. for i, statement in enumerate(orig_module.body): if m.matches( statement, m.SimpleStatementLine( body=[m.Expr(value=m.SimpleString())])): statement_before_import_location = import_add_location = 1 elif isinstance(statement, libcst.SimpleStatementLine): for possible_import in statement.body: for last_import in self.all_imports: if possible_import is last_import: import_add_location = i + 1 break return ( list(updated_module.body[:statement_before_import_location]), list(updated_module. body[statement_before_import_location:import_add_location]), list(updated_module.body[import_add_location:]), )
def leave_Expr( self, original_node: "Expr", updated_node: "Expr" ) -> Union["BaseSmallStatement", RemovalSentinel]: # FIXME: For some strange reason if we put that matcher combination # into m.call_if_inside() (or others), then it will get triggered on # _every_ function call. Not sure if bug or a feature :/ if m.matches( original_node, m.Expr(value=m.Call(func=m.OneOf( m.Attribute( value=m.OneOf(m.Name(value="pdb"), m.Name( value="ipdb")), attr=m.Name(value="set_trace"), ), m.Name("breakpoint"), ))), ): return cst.RemoveFromParent() return original_node
def _is_awaitable_callable(annotation: str) -> bool: if not (annotation.startswith("typing.Callable") or annotation.startswith("typing.ClassMethod") or annotation.startswith("StaticMethod")): # Exit early if this is not even a `typing.Callable` annotation. return False try: # Wrap this in a try-except since the type annotation may not be parse-able as a module. # If it is not parse-able, we know it's not what we are looking for anyway, so return `False`. parsed_ann = cst.parse_module(annotation) except Exception: return False # If passed annotation does not match the expected annotation structure for a `typing.Callable` with # typing.Coroutine as the return type, matched_callable_ann will simply be `None`. # The expected structure of an awaitable callable annotation from Pyre is: typing.Callable()[[...], typing.Coroutine[...]] matched_callable_ann: Optional[Dict[str, Union[ Sequence[cst.CSTNode], cst.CSTNode]]] = m.extract( parsed_ann, m.Module(body=[ m.SimpleStatementLine(body=[ m.Expr(value=m.Subscript(slice=[ m.SubscriptElement(), m.SubscriptElement(slice=m.Index(value=m.Subscript( value=m.SaveMatchedNode( m.Attribute(), "base_return_type", )))), ], )) ]), ]), ) if (matched_callable_ann is not None and "base_return_type" in matched_callable_ann): base_return_type = get_full_name_for_node( cst.ensure_type(matched_callable_ann["base_return_type"], cst.CSTNode)) return (base_return_type is not None and base_return_type == "typing.Coroutine") return False
def is_docstring(node): return m.matches( node, m.SimpleStatementLine(body=[m.Expr(value=m.SimpleString())]))
def leave_Expr(self, old_node, new_node): if m.matches(old_node, m.Expr(m.SimpleString())): return cst.RemovalSentinel.REMOVE return new_node