def convert_classdef(config: ParserConfig, children: Sequence[Any]) -> Any: classdef, name, *arglist, colon, suite = children # First, parse out the comments and empty lines before the statement. leading_lines = parse_empty_lines(config, classdef.whitespace_before) # Compute common whitespace and nodes whitespace_after_class = parse_simple_whitespace(config, classdef.whitespace_after) namenode = Name(name.string) whitespace_after_name = parse_simple_whitespace(config, name.whitespace_after) # Now, construct the classdef node itself if not arglist: # No arglist, so no arguments to this class return ClassDef( leading_lines=leading_lines, lines_after_decorators=(), whitespace_after_class=whitespace_after_class, name=namenode, whitespace_after_name=whitespace_after_name, body=suite, ) else: # Unwrap arglist partial, because its valid to not have any lpar, *args, rpar = arglist args = args[0].args if args else [] bases: List[Arg] = [] keywords: List[Arg] = [] current_arg = bases for arg in args: if arg.star == "**" or arg.keyword is not None: current_arg = keywords # Some quick validation if current_arg is keywords and (arg.star == "*" or (arg.star == "" and arg.keyword is None)): raise PartialParserSyntaxError( "Positional argument follows keyword argument.") current_arg.append(arg) return ClassDef( leading_lines=leading_lines, lines_after_decorators=(), whitespace_after_class=whitespace_after_class, name=namenode, whitespace_after_name=whitespace_after_name, lpar=LeftParen(whitespace_after=parse_parenthesizable_whitespace( config, lpar.whitespace_after)), bases=bases, keywords=keywords, rpar=RightParen(whitespace_before=parse_parenthesizable_whitespace( config, rpar.whitespace_before)), whitespace_before_colon=parse_simple_whitespace( config, colon.whitespace_before), body=suite, )
def convert_except_clause(config: ParserConfig, children: Sequence[Any]) -> Any: if len(children) == 1: (except_token,) = children whitespace_after_except = SimpleWhitespace("") test = None name = None elif len(children) == 2: (except_token, test_node) = children whitespace_after_except = parse_simple_whitespace( config, except_token.whitespace_after ) test = test_node.value name = None else: (except_token, test_node, as_token, name_token) = children whitespace_after_except = parse_simple_whitespace( config, except_token.whitespace_after ) test = test_node.value name = AsName( whitespace_before_as=parse_simple_whitespace( config, as_token.whitespace_before ), whitespace_after_as=parse_simple_whitespace( config, as_token.whitespace_after ), name=Name(name_token.string), ) return ExceptClausePartial( leading_lines=parse_empty_lines(config, except_token.whitespace_before), whitespace_after_except=whitespace_after_except, type=test, name=name, )
def convert_dotted_name(config: ParserConfig, children: Sequence[Any]) -> Any: left, *rest = children node = Name(left.string) for dot, right in grouper(rest, 2): node = Attribute( value=node, dot=Dot( whitespace_before=parse_parenthesizable_whitespace( config, dot.whitespace_before), whitespace_after=parse_parenthesizable_whitespace( config, dot.whitespace_after), ), attr=Name(right.string), ) return node
def convert_import_as_name(config: ParserConfig, children: Sequence[Any]) -> Any: if len(children) == 1: (dotted_name, ) = children return ImportAlias(name=Name(dotted_name.string), asname=None) else: dotted_name, astoken, name = children return ImportAlias( name=Name(dotted_name.string), asname=AsName( whitespace_before_as=parse_simple_whitespace( config, astoken.whitespace_before), whitespace_after_as=parse_simple_whitespace( config, astoken.whitespace_after), name=Name(name.string), ), )
def _construct_nameitems(config: ParserConfig, names: Sequence[Any]) -> List[NameItem]: nameitems: List[NameItem] = [] for name, maybe_comma in grouper(names, 2): if maybe_comma is None: nameitems.append(NameItem(Name(name.string))) else: nameitems.append( NameItem( Name(name.string), comma=Comma( whitespace_before=parse_simple_whitespace( config, maybe_comma.whitespace_before), whitespace_after=parse_simple_whitespace( config, maybe_comma.whitespace_after), ), )) return nameitems
def convert_fpdef(config: ParserConfig, children: Sequence[Any]) -> Any: if len(children) == 1: # This is just a parameter (child, ) = children namenode = Name(child.string) annotation = None else: # This is a parameter with a type hint name, colon, typehint = children namenode = Name(name.string) annotation = Annotation( whitespace_before_indicator=parse_parenthesizable_whitespace( config, colon.whitespace_before), whitespace_after_indicator=parse_parenthesizable_whitespace( config, colon.whitespace_after), annotation=typehint.value, ) return Param(star="", name=namenode, annotation=annotation, default=None)
def convert_funcdef(config: ParserConfig, children: Sequence[Any]) -> Any: defnode, namenode, param_partial, *annotation, colon, suite = children # If the trailing paremeter doesn't have a comma, then it owns the trailing # whitespace before the rpar. Otherwise, the comma owns it (and will have # already parsed it). We don't check/update ParamStar because if it exists # then we are guaranteed have at least one kwonly_param. parameters = param_partial.params if parameters.star_kwarg is not None: if parameters.star_kwarg.comma == MaybeSentinel.DEFAULT: parameters = parameters.with_changes( star_kwarg=parameters.star_kwarg.with_changes( whitespace_after_param=param_partial.rpar.whitespace_before )) elif parameters.kwonly_params: if parameters.kwonly_params[-1].comma == MaybeSentinel.DEFAULT: parameters = parameters.with_changes(kwonly_params=( *parameters.kwonly_params[:-1], parameters.kwonly_params[-1].with_changes( whitespace_after_param=param_partial.rpar.whitespace_before ), )) elif isinstance(parameters.star_arg, Param): if parameters.star_arg.comma == MaybeSentinel.DEFAULT: parameters = parameters.with_changes( star_arg=parameters.star_arg.with_changes( whitespace_after_param=param_partial.rpar.whitespace_before )) elif parameters.params: if parameters.params[-1].comma == MaybeSentinel.DEFAULT: parameters = parameters.with_changes(params=( *parameters.params[:-1], parameters.params[-1].with_changes( whitespace_after_param=param_partial.rpar.whitespace_before ), )) return WithLeadingWhitespace( FunctionDef( whitespace_after_def=parse_simple_whitespace( config, defnode.whitespace_after), name=Name(namenode.string), whitespace_after_name=parse_simple_whitespace( config, namenode.whitespace_after), whitespace_before_params=param_partial.lpar.whitespace_after, params=parameters, returns=None if not annotation else annotation[0], whitespace_before_colon=parse_simple_whitespace( config, colon.whitespace_before), body=suite, ), defnode.whitespace_before, )
def convert_trailer_attribute( config: ParserConfig, children: typing.Sequence[typing.Any]) -> typing.Any: dot, name = children return AttributePartial( dot=Dot( whitespace_before=parse_parenthesizable_whitespace( config, dot.whitespace_before), whitespace_after=parse_parenthesizable_whitespace( config, dot.whitespace_after), ), attr=Name(name.string), )
def convert_atom_basic( config: ParserConfig, children: typing.Sequence[typing.Any] ) -> typing.Any: (child,) = children if child.type.name == "NAME": # This also handles 'None', 'True', and 'False' directly, but we # keep it in the grammar to be more correct. return WithLeadingWhitespace(Name(child.string), child.whitespace_before) elif child.type.name == "NUMBER": # We must determine what type of number it is since we split node # types up this way. if re.fullmatch(INTNUMBER_RE, child.string): return WithLeadingWhitespace(Integer(child.string), child.whitespace_before) elif re.fullmatch(FLOATNUMBER_RE, child.string): return WithLeadingWhitespace(Float(child.string), child.whitespace_before) elif re.fullmatch(IMAGNUMBER_RE, child.string): return WithLeadingWhitespace( Imaginary(child.string), child.whitespace_before ) else: raise Exception("Unparseable number {child.string}") else: raise Exception(f"Logic error, unexpected token {child.type.name}")