def __name2annotation(self, type_name: str): """ Converts Name nodes to valid annotation nodes """ try: return match.extract( cst.parse_module("x: %s=None" % type_name).body[0].body[0], match.AnnAssign(target=match.Name(value=match.DoNotCare()), annotation=match.SaveMatchedNode( match.DoNotCare(), "type")))['type'] except cst._exceptions.ParserSyntaxError: # To handle a bug in LibCST's scope provider where a local name shadows a type annotation with the same name if (self.last_visited_name.value, cst.metadata.QualifiedNameSource.IMPORT ) in self.q_names_cache: return match.extract( cst.parse_module( "x: %s=None" % self.q_names_cache[(self.last_visited_name.value, cst.metadata.QualifiedNameSource. IMPORT)]).body[0].body[0], match.AnnAssign(target=match.Name(value=match.DoNotCare()), annotation=match.SaveMatchedNode( match.DoNotCare(), "type")))['type'] else: return match.extract( cst.parse_module( "x: %s=None" % self.last_visited_name.value).body[0].body[0], match.AnnAssign(target=match.Name(value=match.DoNotCare()), annotation=match.SaveMatchedNode( match.DoNotCare(), "type")))['type']
def leave_AnnAssign( self, original_node: cst.AnnAssign, updated_node: cst.AnnAssign ) -> Union[cst.BaseSmallStatement, cst.RemovalSentinel]: # It handles a special case where a type-annotated variable has not initialized, e.g. foo: str # This case will be converted to foo = ... so that nodes traversal won't encounter exceptions later on if match.matches( original_node, match.AnnAssign( target=match.Name(value=match.DoNotCare()), annotation=match.Annotation(annotation=match.DoNotCare()), value=None)): updated_node = cst.Assign( targets=[cst.AssignTarget(target=original_node.target)], value=cst.Ellipsis()) # Handles type-annotated class attributes that has not been initialized, e.g. self.foo: str elif match.matches( original_node, match.AnnAssign( target=match.Attribute(value=match.DoNotCare()), annotation=match.Annotation(annotation=match.DoNotCare()), value=None)): updated_node = cst.Assign( targets=[cst.AssignTarget(target=original_node.target)], value=cst.Ellipsis()) else: updated_node = cst.Assign( targets=[cst.AssignTarget(target=original_node.target)], value=original_node.value) return updated_node
def __extract_variable_name_type(self, node: cst.AnnAssign): """ Extracts a variable's identifier name and its type annotation """ return match.extract( node, match.AnnAssign( # Annotated Assignment target=match.OneOf( match.Name( # Variable name of assignment (only one) value=match.SaveMatchedNode( # Save result match.MatchRegex( r'(.)+'), # Match any string literal "name")), # This extracts variables inside __init__ which typically starts with self (e.g. self.x:int=2) match.Attribute( value=match.Name(value=match.SaveMatchedNode( match.MatchRegex(r'(.)+'), "obj_name" # Object name )), attr=match.Name( match.SaveMatchedNode(match.MatchRegex(r'(.)+'), "name")), )), annotation=match.SaveMatchedNode( # Save result match.DoNotCare(), # Match any string literal "type")))
def __name2annotation(self, type_name: str): ext_annot = lambda t: match.extract( cst.parse_module("x: %s=None" % t).body[0].body[0], match.AnnAssign(target=match.Name(value=match.DoNotCare()), annotation=match.SaveMatchedNode( match.DoNotCare(), "type")))['type'] try: return ext_annot(type_name) except cst._exceptions.ParserSyntaxError: return None
def visit_AnnAssign(self, node: cst.AnnAssign) -> None: # The assignment value is optional, as it is possible to annotate an # expression without assigning to it: ``var: int`` if m.matches( node, m.AnnAssign( target=m.Name(), value=m.MatchIfTrue(lambda value: value is not None)), ): nodename = cst.ensure_type(node.target, cst.Name).value self._validate_nodename(node, nodename, NamingConvention.SNAKE_CASE)
def __get_var_names_counter(self, node, scope): vars_name = match.extractall( node, match.OneOf( match.AssignTarget(target=match.SaveMatchedNode( match.Name(value=match.DoNotCare()), "name")), match.AnnAssign(target=match.SaveMatchedNode( match.Name(value=match.DoNotCare()), "name")))) return Counter([ n['name'].value for n in vars_name if isinstance( self.get_metadata(cst.metadata.ScopeProvider, n['name']), scope) ])
def leave_SimpleStatementLine(self, original_node: cst.SimpleStatementLine, updated_node: cst.SimpleStatementLine): if match.matches( original_node, match.SimpleStatementLine(body=[ match.Assign(targets=[ match.AssignTarget(target=match.Name( value=match.DoNotCare())) ]) ])): t = self.__get_var_type_assign_t( original_node.body[0].targets[0].target.value) if t is not None: t_annot_node_resolved = self.resolve_type_alias(t) t_annot_node = self.__name2annotation(t_annot_node_resolved) if t_annot_node is not None: self.all_applied_types.add( (t_annot_node_resolved, t_annot_node)) return updated_node.with_changes(body=[ cst.AnnAssign( target=original_node.body[0].targets[0].target, value=original_node.body[0].value, annotation=t_annot_node, equal=cst.AssignEqual( whitespace_after=original_node.body[0]. targets[0].whitespace_after_equal, whitespace_before=original_node.body[0]. targets[0].whitespace_before_equal)) ]) elif match.matches( original_node, match.SimpleStatementLine(body=[ match.AnnAssign(target=match.Name(value=match.DoNotCare())) ])): t = self.__get_var_type_an_assign( original_node.body[0].target.value) if t is not None: t_annot_node_resolved = self.resolve_type_alias(t) t_annot_node = self.__name2annotation(t_annot_node_resolved) if t_annot_node is not None: self.all_applied_types.add( (t_annot_node_resolved, t_annot_node)) return updated_node.with_changes(body=[ cst.AnnAssign(target=original_node.body[0].target, value=original_node.body[0].value, annotation=t_annot_node, equal=original_node.body[0].equal) ]) return original_node
_django_model_field_name_value = m.Call(func=m.Attribute( attr=m.Name(m.MatchIfTrue(is_model_field_type)))) | m.Call( func=m.Name(m.MatchIfTrue(is_model_field_type))) _django_model_field_name_with_leading_comment_value = m.Call( func=m.Attribute(attr=m.Name(m.MatchIfTrue(is_model_field_type))), whitespace_before_args=m.ParenthesizedWhitespace(_any_comment), ) | m.Call( func=m.Name(m.MatchIfTrue(is_model_field_type)), whitespace_before_args=m.ParenthesizedWhitespace(_any_comment), ) _django_model_field_with_leading_comment = m.SimpleStatementLine(body=[ m.Assign(value=_django_model_field_name_with_leading_comment_value) | m.AnnAssign(value=_django_model_field_name_with_leading_comment_value) ]) _django_model_field_with_trailing_comment = m.SimpleStatementLine( body=[ m.Assign(value=_django_model_field_name_value) | m.AnnAssign(value=_django_model_field_name_value) ], trailing_whitespace=_any_comment, ) django_model_field_with_comments = (_django_model_field_with_leading_comment | _django_model_field_with_trailing_comment) def get_leading_comment(node: cst.SimpleStatementLine) -> typing.Optional[str]: