def leave_FunctionDef(self, original_node: cst.FunctionDef, updated_node: cst.FunctionDef) -> cst.FunctionDef: docstring = None docstring_node = get_docstring_node(updated_node.body) if docstring_node: if isinstance(docstring_node.value, (cst.SimpleString, cst.ConcatenatedString)): docstring = docstring_node.value.evaluated_value if not docstring: return updated_node new_docstring, types = gather_types(docstring) if types.get(RETURN): updated_node = updated_node.with_changes(returns=cst.Annotation( cst.Name(types.pop(RETURN))), ) if types: def get_annotation(p: cst.Param) -> Optional[cst.Annotation]: pname = p.name.value if types.get(pname): return cst.Annotation(cst.parse_expression(types[pname])) return None updated_node = updated_node.with_changes(params=update_parameters( updated_node.params, get_annotation, False)) new_docstring_node = cst.SimpleString('"""%s"""' % new_docstring) return updated_node.deep_replace(docstring_node, cst.Expr(new_docstring_node))
def leave_FunctionDef(self, node: cst.FunctionDef, updated_node: cst.FunctionDef) -> cst.CSTNode: key = self._qualifier_name() self.qualifier.pop() if key in self.function_annotations: annotations = self.function_annotations[key] # Only add new annotation if one doesn't already exist if not updated_node.returns: updated_node = updated_node.with_changes( returns=annotations.returns) return updated_node.with_changes(params=annotations.parameters) return updated_node
def leave_FunctionDef(self, node: cst.FunctionDef, updated_node: cst.FunctionDef) -> cst.FunctionDef: returns = self.stack.pop() if returns is None: return updated_node if not returns: return updated_node.with_changes(returns=cst.Annotation( annotation=cst.Name(value="None"))) last_line = node.body.body[-1] if not isinstance(last_line, cst.SimpleStatementLine): if returns and all(r.value is None or isinstance( r.value, cst.Name) and r.value.value == 'None' for r in returns): return updated_node.with_changes(returns=cst.Annotation( annotation=cst.Name(value="None"))) return updated_node elif not isinstance(last_line.body[-1], cst.Return): if returns and all(r.value is None or isinstance( r.value, cst.Name) and r.value.value == 'None' for r in returns): return updated_node.with_changes(returns=cst.Annotation( annotation=cst.Name(value="None"))) return updated_node if len(returns) == 1: rvalue = returns[0].value if isinstance(rvalue, cst.BaseString): if isinstance( rvalue, cst.SimpleString) and rvalue.value.startswith("b"): return updated_node.with_changes(returns=cst.Annotation( annotation=cst.Name(value="bytes"))) return updated_node.with_changes(returns=cst.Annotation( annotation=cst.Name(value="str"))) if isinstance(rvalue, cst.Name): if rvalue.value in ("False", "True"): return updated_node.with_changes(returns=cst.Annotation( annotation=cst.Name(value="bool"))) if rvalue.value == "None": return updated_node.with_changes(returns=cst.Annotation( annotation=cst.Name(value="None"))) if isinstance(rvalue, cst.Integer): return updated_node.with_changes(returns=cst.Annotation( annotation=cst.Name(value="int"))) if isinstance(rvalue, cst.Float): return updated_node.with_changes(returns=cst.Annotation( annotation=cst.Name(value="float"))) elif returns and all(r.value is None or isinstance(r.value, cst.Name) and r.value.value == 'None' for r in returns): return updated_node.with_changes(returns=cst.Annotation( annotation=cst.Name(value="None"))) return updated_node
def leave_FunctionDef(self, original_node: cst.FunctionDef, updated_node: cst.FunctionDef) -> cst.FunctionDef: key = self._qualifier_name() self.qualifier.pop() if key in self.function_annotations: annotations = self.function_annotations[key] # Only add new annotation if one doesn't already exist if not updated_node.returns: updated_node = updated_node.with_changes( returns=annotations.returns) # Don't override default values when annotating functions new_parameters = self._update_parameters(annotations, updated_node) return updated_node.with_changes(params=new_parameters) return updated_node
def leave_FunctionDef(self, original_node: cst.FunctionDef, updated_node: cst.FunctionDef) -> cst.FunctionDef: key = self._qualifier_name() self.qualifier.pop() if key in self.annotations.function_annotations: function_annotation = self.annotations.function_annotations[key] # Only add new annotation if explicitly told to overwrite existing # annotations or if one doesn't already exist. if self.overwrite_existing_annotations or not updated_node.returns: updated_node = updated_node.with_changes( returns=function_annotation.returns) # Don't override default values when annotating functions new_parameters = self._update_parameters(function_annotation, updated_node) return updated_node.with_changes(params=new_parameters) return updated_node
def visit_FunctionDef(self, node: cst.FunctionDef) -> None: if not any( QualifiedNameProvider.has_name( self, decorator.decorator, QualifiedName(name="builtins.classmethod", source=QualifiedNameSource.BUILTIN), ) for decorator in node.decorators ): return # If it's not a @classmethod, we are not interested. if not node.params.params: # No params, but there must be the 'cls' param. # Note that pyre[47] already catches this, but we also generate # an autofix, so it still makes sense for us to report it here. new_params = node.params.with_changes(params=(cst.Param(name=cst.Name(value=CLS)),)) repl = node.with_changes(params=new_params) self.report(node, replacement=repl) return p0_name = node.params.params[0].name if p0_name.value == CLS: return # All good. # Rename all assignments and references of the first param within the # function scope, as long as they are done via a Name node. # We rely on the parser to correctly derive all # assigments and references within the FunctionScope. # The Param node's scope is our classmethod's FunctionScope. scope = self.get_metadata(ScopeProvider, p0_name, None) if not scope: # Cannot autofix without scope metadata. Only report in this case. # Not sure how to repro+cover this in a unit test... # If metadata creation fails, then the whole lint fails, and if it succeeds, # then there is valid metadata. But many other lint rule implementations contain # a defensive scope None check like this one, so I assume it is necessary. self.report(node) return if scope[CLS]: # The scope already has another assignment to "cls". # Trying to rename the first param to "cls" as well may produce broken code. # We should therefore refrain from suggesting an autofix in this case. self.report(node) return refs: List[Union[cst.Name, cst.Attribute]] = [] assignments = scope[p0_name.value] for a in assignments: if isinstance(a, Assignment): assign_node = a.node if isinstance(assign_node, cst.Name): refs.append(assign_node) elif isinstance(assign_node, cst.Param): refs.append(assign_node.name) # There are other types of possible assignment nodes: ClassDef, # FunctionDef, Import, etc. We deliberately do not handle those here. refs += [r.node for r in a.references] repl = node.visit(_RenameTransformer(refs, CLS)) self.report(node, replacement=repl)
def leave_FunctionDef( self, original_node: cst.FunctionDef, updated_node: cst.FunctionDef, ) -> cst.FunctionDef: key = FunctionKey.make(self._qualifier_name(), updated_node.params) self.qualifier.pop() if key in self.annotations.functions: function_annotation = self.annotations.functions[key] # Only add new annotation if: # * we have matching function signatures and # * we are explicitly told to overwrite existing annotations or # * there is no existing annotation if not self._match_signatures(updated_node, function_annotation): return updated_node set_return_annotation = (self.overwrite_existing_annotations or updated_node.returns is None) if set_return_annotation and function_annotation.returns is not None: updated_node = self._apply_annotation_to_return( function_def=updated_node, annotation=function_annotation.returns, ) # Don't override default values when annotating functions new_parameters = self._update_parameters(function_annotation, updated_node) return updated_node.with_changes(params=new_parameters) return updated_node
def leave_FunctionDef( self, original_node: cst.FunctionDef, updated_node: cst.FunctionDef ) -> cst.FunctionDef: if matchers.matches(updated_node, self.matcher): return updated_node.with_changes(returns=cst.Annotation(cst.Name(value="None"))) return updated_node
def _apply_annotation_to_return( self, function_def: cst.FunctionDef, annotation: cst.Annotation, ) -> cst.FunctionDef: self.annotation_counts.return_annotations += 1 return function_def.with_changes(returns=annotation)
def leave_FunctionDef( self, original_node: cst.FunctionDef, updated_node: cst.FunctionDef ) -> cst.CSTNode: key = tuple(self.stack) self.stack.pop() if key in self.annotations: annotations = self.annotations[key] params, returns = annotations return updated_node.with_changes(params=params, returns=returns) return updated_node
def leave_FunctionDef( self, node: cst.FunctionDef, updated_node: cst.FunctionDef ) -> cst.CSTNode: key = self._qualifier_name() self.qualifier.pop() if key in self.function_annotations: annotations = self.function_annotations[key] return updated_node.with_changes( params=annotations.parameters, returns=annotations.returns ) return updated_node
def leave_FunctionDef(self, original_node: cst.FunctionDef, updated_node: cst.FunctionDef): should_async = self.stack == self.fn_should_async self.fn_should_async = None self.stack.pop() if not should_async: return updated_node # mark fn as async return updated_node.with_changes(asynchronous=cst.Asynchronous())
def leave_FunctionDef(self, original_node: cst.FunctionDef, updated_node: cst.FunctionDef): fn_ret_type = self.fn_visited[-1][0]['ret_type'] self.fn_visited.pop() if fn_ret_type != "": fn_ret_type_resolved = self.resolve_type_alias(fn_ret_type) fn_ret_type = self.__name2annotation(fn_ret_type_resolved) if fn_ret_type is not None: self.all_applied_types.add((fn_ret_type_resolved, fn_ret_type)) return updated_node.with_changes(returns=fn_ret_type) return updated_node
def leave_FunctionDef(self, original_node: cst.FunctionDef, updated_node: cst.FunctionDef): ret_type = self.module_type_annot[(self.cls_stack[-1] if len(self.cls_stack) > 0 else None, self.fn_stack[-1], None)][0] self.fn_stack.pop() if ret_type != '': return updated_node.with_changes(name=cst.Name( value=f"${ret_type}$")) else: return updated_node
def leave_FunctionDef(self, original_node: cst.FunctionDef, updated_node: cst.FunctionDef) -> cst.CSTNode: key = tuple(self.stack) self.stack.pop() if key in self.annotations: annotations = self.annotations[key] # Todo: add/update Docstring return updated_node.with_changes(params=annotations[0], returns=annotations[1]) return updated_node
def leave_FunctionDef( self, original_node: FunctionDef, updated_node: FunctionDef ) -> Union[BaseStatement, FlattenSentinel[BaseStatement], RemovalSentinel]: if (self.is_visiting_subclass and m.matches( updated_node, m.FunctionDef(name=m.Name("has_add_permission"))) and len(updated_node.params.params) == 2): old_params = updated_node.params updated_params = old_params.with_changes(params=( *old_params.params, parse_param("obj=None"), )) return updated_node.with_changes(params=updated_params) return super().leave_FunctionDef(original_node, updated_node)
def leave_FunctionDef(self, node: cst.FunctionDef, updated_node: cst.FunctionDef) -> cst.FunctionDef: leaving_coroutine = self.coroutine_stack.pop() if not leaving_coroutine: return updated_node return updated_node.with_changes( decorators=[ decorator for decorator in updated_node.decorators if not m.matches(decorator, gen_coroutine_decorator_matcher) ], asynchronous=cst.Asynchronous(), )
def leave_FunctionDef( self, original_node: libcst.FunctionDef, updated_node: libcst.FunctionDef) -> libcst.FunctionDef: self.count += 1 function_position = self.get_metadata(libcst.metadata.PositionProvider, original_node).start body_position = self.get_metadata(libcst.metadata.PositionProvider, original_node.body).start function_defition_length = body_position.line - function_position.line # print(original_node.name.value, '\t', function_defition_length, '\t', function_position.line) new_fstr = self.empty_function_body( self.module.code_for_node(original_node), function_defition_length) new_fdef = self.lparser.parse_module(new_fstr).body[0] return updated_node.with_changes(body=new_fdef.body)
def leave_FunctionDef( self, original_node: FunctionDef, updated_node: FunctionDef ) -> Union[BaseStatement, RemovalSentinel]: if (m.matches(updated_node, m.FunctionDef(name=m.Name("has_add_permission"))) and self._is_context_right): if len(updated_node.params.params) == 2: old_params = updated_node.params updated_params = old_params.with_changes(params=( *old_params.params, Param(name=Name("obj"), default=Name("None")), )) return updated_node.with_changes(params=updated_params) return super().leave_FunctionDef(original_node, updated_node)
def leave_FunctionDef( self, original_node: cst.FunctionDef, updated_node: cst.FunctionDef, ) -> cst.FunctionDef: self.function_body_stack.pop() function_type_info = self.function_type_info_stack.pop() if updated_node.returns is None and function_type_info.returns is not None: return updated_node.with_changes( returns=_convert_annotation( raw=function_type_info.returns, quote_annotations=self.quote_annotations, ) ) else: return updated_node
def leave_FunctionDef( self, original_node: FunctionDef, updated_node: FunctionDef ) -> Union[BaseStatement, FlattenSentinel[BaseStatement], RemovalSentinel]: if self.visiting_permalink_method: for decorator in updated_node.decorators: if m.matches(decorator, self.decorator_matcher): AddImportsVisitor.add_needed_import( context=self.context, module="django.urls", obj="reverse", ) updated_decorators = list(updated_node.decorators) updated_decorators.remove(decorator) self.context.scratch.pop(self.ctx_key_inside_method, None) return updated_node.with_changes( decorators=tuple(updated_decorators)) return super().leave_FunctionDef(original_node, updated_node)
def leave_FunctionDef(self, original_node: cst.FunctionDef, updated_node: cst.FunctionDef) -> cst.FunctionDef: final_node = updated_node if original_node is self.scope: # Don't want to ssa params but do want them in the name table for param in updated_node.params.params: name = param.name.value self.name_table[name] = name self.name_assignments[name] = param # Need to visit params to get them to be rebuilt and therfore # tracked to build the symbol table self._skip += 1 update_params = updated_node.params.visit(self) self._skip -= 1 assert not self._skip assert not self._assigned_names, self._assigned_names new_body = updated_node.body.visit(self) final_node = updated_node.with_changes(body=new_body, params=update_params) assert not self._skip assert not self._assigned_names, self._assigned_names return final_node
def leave_FunctionDef( self, original_node: cst.FunctionDef, updated_node: cst.FunctionDef ) -> Union[cst.BaseStatement, cst.RemovalSentinel]: return updated_node.with_changes( returns=None ) if original_node.returns is not None else updated_node
def leave_FunctionDef(self, original_node: cst.FunctionDef, updated_node: cst.FunctionDef): return updated_node.with_changes(returns=None)
def leave_FunctionDef( self, original_node: cst.FunctionDef, updated_node: cst.FunctionDef ) -> Union[cst.BaseStatement, cst.RemovalSentinel]: modified_defaults: List = [] mutable_args: List[Tuple[cst.Name, Union[cst.List, cst.Dict]]] = [] for param in updated_node.params.params: if not m.matches(param, m.Param(default=m.OneOf(m.List(), m.Dict()))): modified_defaults.append(param) continue # This line here is just for type checkers peace of mind, # since it cannot reason about variables from matchers result. if not isinstance(param.default, (cst.List, cst.Dict)): continue mutable_args.append((param.name, param.default)) modified_defaults.append( param.with_changes(default=cst.Name("None"), )) if not mutable_args: return original_node modified_params: cst.Parameters = updated_node.params.with_changes( params=modified_defaults) initializations: List[Union[ cst.SimpleStatementLine, cst.BaseCompoundStatement]] = [ # We use generation by template here since construction of the # resulting 'if' can be burdensome due to many nested objects # involved. Additional line is attached so that we may control # exact spacing between generated statements. parse_template_statement( DEFAULT_INIT_TEMPLATE, config=self.module_config, arg=arg, init=init).with_changes(leading_lines=[EMPTY_LINE]) for arg, init in mutable_args ] # Docstring should always go right after the function definition, # so we take special care to insert our initializations after the # last docstring found. docstrings = takewhile(is_docstring, updated_node.body.body) function_code = dropwhile(is_docstring, updated_node.body.body) # It is not possible to insert empty line after the statement line, # because whitespace is owned by the next statement after it. stmt_with_empty_line = next(function_code).with_changes( leading_lines=[EMPTY_LINE]) modified_body = ( *docstrings, *initializations, stmt_with_empty_line, *function_code, ) return updated_node.with_changes( params=modified_params, body=updated_node.body.with_changes(body=modified_body), )
def leave_FunctionDef(self, original_node: cst.FunctionDef, updated_node: cst.FunctionDef): # Удаление аннотаций if self.delete_annotations: old_params = original_node.params.params old_default_params = original_node.params.default_params small_params = [ i.with_changes(annotation=None) for i in old_params ] small_default_params = [ i.with_changes(annotation=None) for i in old_default_params ] new_params = original_node.params.with_changes( params=small_params, default_params=small_default_params) updated_node = updated_node.with_changes(params=new_params, returns=None) # Удаление многострочных комментариев if self.delete_docstrings: pass # Замена аргументов if (self.change_method_arguments and self.class_stack) or\ (self.change_arguments and not self.class_stack): all_params = updated_node.params new_params = [] new_default_params = [] for argument in all_params.params: # Обфускация имени аргумента if self.can_rename(argument.name.value, 'a', 'ca'): argument = argument.with_changes( name=self.get_new_cst_name(argument.name)) new_params.append(argument) for argument in all_params.default_params: # Обфкскация значения по умолчанию argument = argument.with_changes( default=self.obf_universal(argument.default)) # Обфускация имени аргумента if self.can_rename(argument.name.value, 'a', 'ca'): argument = argument.with_changes( name=self.get_new_cst_name(argument.name)) new_default_params.append(argument) arg, kwarg = all_params.star_arg, all_params.star_kwarg new_all_params = all_params.with_changes( params=new_params, default_params=new_default_params) if arg is not None and not isinstance( arg.name, str) and self.can_rename(arg.name.value, 'a', 'ca'): new_star_arg = arg.with_changes( name=self.get_new_cst_name(arg.name.value)) new_all_params = new_all_params.with_changes( star_arg=new_star_arg) if kwarg is not None and not isinstance( kwarg.name, str) and self.can_rename( kwarg.name.value, 'a', 'ca'): new_star_kwarg = kwarg.with_changes( name=self.get_new_cst_name(kwarg.name.value)) new_all_params = new_all_params.with_changes( star_kwarg=new_star_kwarg) updated_node = updated_node.with_changes(params=new_all_params) is_property = 'property' in get_function_decorators(updated_node) # Замена названия if (self.can_rename(updated_node.name.value, 'f', 'cf', 'cv') and ((self.class_stack and ((self.change_methods and self.change_fields) or (self.change_fields and not self.change_methods and is_property) or (not self.change_fields and self.change_methods and not is_property))) or (not self.class_stack and self.change_functions))): updated_node = self.renamed(updated_node) self.function_stack.pop() return updated_node