def convert_indented_suite(config: ParserConfig, children: Sequence[Any]) -> Any: newline, indent, *stmts, dedent = children return IndentedBlock( header=newline, indent=( None if indent.relative_indent == config.default_indent else indent.relative_indent ), body=stmts, # We want to be able to only keep comments in the footer that are actually for # this IndentedBlock. We do so by assuming that lines which are indented to the # same level as the block itself are comments that go at the footer of the # block. Comments that are indented to less than this indent are assumed to # belong to the next line of code. We override the indent here because the # dedent node's absolute indent is the resulting indentation after the dedent # is performed. Its this way because the whitespace state for both the dedent's # whitespace_after and the next BaseCompoundStatement's whitespace_before is # shared. This allows us to partially parse here and parse the rest of the # whitespace and comments on the next line, effectively making sure that # comments are attached to the correct node. footer=parse_empty_lines( config, dedent.whitespace_after, override_absolute_indent=indent.whitespace_before.absolute_indent, ), )
def convert_if_stmt_else(config: ParserConfig, children: Sequence[Any]) -> Any: else_tok, colon_tok, suite = children return Else( leading_lines=parse_empty_lines(config, else_tok.whitespace_before), whitespace_before_colon=parse_simple_whitespace( config, colon_tok.whitespace_before), body=suite, )
def convert_ENDMARKER(config: ParserConfig, token: Token) -> Any: # Parse any and all empty lines with an indent similar to the header. That is, # indent of nothing and including all indents. In some cases, like when the # footer parser follows an indented suite, the state's indent can be wrong # due to the fact that it is shared with the _DEDENT node. We know that if # we're parsing the end of a file, we will have no indent. return parse_empty_lines(config, token.whitespace_before, override_absolute_indent="")
def convert_simple_stmt_line(config: ParserConfig, children: Sequence[Any]) -> Any: """ This function is similar to convert_simple_stmt_suite, but yields a different type """ (partial,) = children return SimpleStatementLine( partial.body, leading_lines=parse_empty_lines(config, partial.whitespace_before), trailing_whitespace=partial.trailing_whitespace, )
def _extract_async( config: ParserConfig, children: Sequence[Any] ) -> Tuple[List[EmptyLine], Optional[Asynchronous], Any]: if len(children) == 1: (stmt, ) = children whitespace_before = stmt.whitespace_before asyncnode = None else: asynctoken, stmt = children whitespace_before = asynctoken.whitespace_before asyncnode = Asynchronous(whitespace_after=parse_simple_whitespace( config, asynctoken.whitespace_after)) return (parse_empty_lines(config, whitespace_before), asyncnode, stmt.value)
def convert_if_stmt(config: ParserConfig, children: Sequence[Any]) -> Any: if_tok, test, colon_tok, suite, *tail = children if len(tail) > 0: (orelse, ) = tail else: orelse = None return If( leading_lines=parse_empty_lines(config, if_tok.whitespace_before), whitespace_before_test=parse_simple_whitespace( config, if_tok.whitespace_after), test=test.value, whitespace_after_test=parse_simple_whitespace( config, colon_tok.whitespace_before), body=suite, orelse=orelse, )
def convert_for_stmt(config: ParserConfig, children: Sequence[Any]) -> Any: ( for_token, expr, in_token, test, for_colon_token, for_suite, *else_block, ) = children if len(else_block) > 0: (else_token, else_colon_token, else_suite) = else_block orelse = Else( leading_lines=parse_empty_lines(config, else_token.whitespace_before), whitespace_before_colon=parse_simple_whitespace( config, else_colon_token.whitespace_before ), body=else_suite, ) else: orelse = None return WithLeadingWhitespace( For( whitespace_after_for=parse_simple_whitespace( config, for_token.whitespace_after ), target=expr.value, whitespace_before_in=parse_simple_whitespace( config, in_token.whitespace_before ), whitespace_after_in=parse_simple_whitespace( config, in_token.whitespace_after ), iter=test.value, whitespace_before_colon=parse_simple_whitespace( config, for_colon_token.whitespace_before ), body=for_suite, orelse=orelse, ), for_token.whitespace_before, )
def convert_decorator(config: ParserConfig, children: Sequence[Any]) -> Any: atsign, name, *arglist, newline = children if not arglist: # This is either a name or an attribute node, so just extract it. decoratornode = name else: # This needs to be converted into a call node, and we have the # arglist partial. lpar, *args, rpar = arglist args = args[0].args if args else [] # If the trailing argument doesn't have a comma, then it owns the # trailing whitespace before the rpar. Otherwise, the comma owns # it. if len(args) > 0 and args[-1].comma == MaybeSentinel.DEFAULT: args[-1] = args[-1].with_changes( whitespace_after_arg=parse_parenthesizable_whitespace( config, rpar.whitespace_before ) ) decoratornode = Call( func=name, whitespace_after_func=parse_simple_whitespace( config, lpar.whitespace_before ), whitespace_before_args=parse_parenthesizable_whitespace( config, lpar.whitespace_after ), args=tuple(args), ) return Decorator( leading_lines=parse_empty_lines(config, atsign.whitespace_before), whitespace_after_at=parse_simple_whitespace(config, atsign.whitespace_after), decorator=decoratornode, trailing_whitespace=newline, )
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, )