src/black/parsing.py
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | """
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | Parse Python code and perform AST validation.
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | """
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | import ast
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | import sys
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | from typing import Iterable, Iterator, List, Set, Tuple
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | from black.mode import VERSION_TO_FEATURES, Feature, TargetVersion, supports_feature
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | from black.nodes import syms
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | from blib2to3 import pygram
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | from blib2to3.pgen2 import driver
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | from blib2to3.pgen2.grammar import Grammar
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | from blib2to3.pgen2.parse import ParseError
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | from blib2to3.pgen2.tokenize import TokenError
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- | from blib2to3.pytree import Leaf, Node
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
---- ---- ---- ---- ---- ---- ---- 01 -- --- --- --- --- --- --- ------- ------- ------- | class InvalidInput(ValueError):
---- ---- ---- ---- ---- ---- ---- 01 -- --- --- --- --- --- --- ------- ------- ------- | """Raised when input source code fails all parse attempts."""
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
0029 0011 0020 0010 0000 0002 0007 -- 07 003 008 006 008 011 014 0048.43 0072.65 0001.50 | def get_grammars(target_versions: Set[TargetVersion]) -> List[Grammar]:
0029 0011 0020 0010 0000 0002 0007 -- 07 003 008 006 008 011 014 0048.43 0072.65 0001.50 | if not target_versions:
0029 0011 0020 0010 0000 0002 0007 -- 07 003 008 006 008 011 014 0048.43 0072.65 0001.50 | # No target_version specified, so try all grammars.
0029 0011 0020 0010 0000 0002 0007 -- 07 003 008 006 008 011 014 0048.43 0072.65 0001.50 | return [
0029 0011 0020 0010 0000 0002 0007 -- 07 003 008 006 008 011 014 0048.43 0072.65 0001.50 | # Python 3.7-3.9
0029 0011 0020 0010 0000 0002 0007 -- 07 003 008 006 008 011 014 0048.43 0072.65 0001.50 | pygram.python_grammar_async_keywords,
0029 0011 0020 0010 0000 0002 0007 -- 07 003 008 006 008 011 014 0048.43 0072.65 0001.50 | # Python 3.0-3.6
0029 0011 0020 0010 0000 0002 0007 -- 07 003 008 006 008 011 014 0048.43 0072.65 0001.50 | pygram.python_grammar,
0029 0011 0020 0010 0000 0002 0007 -- 07 003 008 006 008 011 014 0048.43 0072.65 0001.50 | # Python 3.10+
0029 0011 0020 0010 0000 0002 0007 -- 07 003 008 006 008 011 014 0048.43 0072.65 0001.50 | pygram.python_grammar_soft_keywords,
0029 0011 0020 0010 0000 0002 0007 -- 07 003 008 006 008 011 014 0048.43 0072.65 0001.50 | ]
0029 0011 0020 0010 0000 0002 0007 -- 07 003 008 006 008 011 014 0048.43 0072.65 0001.50 |
0029 0011 0020 0010 0000 0002 0007 -- 07 003 008 006 008 011 014 0048.43 0072.65 0001.50 | grammars = []
0029 0011 0020 0010 0000 0002 0007 -- 07 003 008 006 008 011 014 0048.43 0072.65 0001.50 | # If we have to parse both, try to parse async as a keyword first
0029 0011 0020 0010 0000 0002 0007 -- 07 003 008 006 008 011 014 0048.43 0072.65 0001.50 | if not supports_feature(
0029 0011 0020 0010 0000 0002 0007 -- 07 003 008 006 008 011 014 0048.43 0072.65 0001.50 | target_versions, Feature.ASYNC_IDENTIFIERS
0029 0011 0020 0010 0000 0002 0007 -- 07 003 008 006 008 011 014 0048.43 0072.65 0001.50 | ) and not supports_feature(target_versions, Feature.PATTERN_MATCHING):
0029 0011 0020 0010 0000 0002 0007 -- 07 003 008 006 008 011 014 0048.43 0072.65 0001.50 | # Python 3.7-3.9
0029 0011 0020 0010 0000 0002 0007 -- 07 003 008 006 008 011 014 0048.43 0072.65 0001.50 | grammars.append(pygram.python_grammar_async_keywords)
0029 0011 0020 0010 0000 0002 0007 -- 07 003 008 006 008 011 014 0048.43 0072.65 0001.50 | if not supports_feature(target_versions, Feature.ASYNC_KEYWORDS):
0029 0011 0020 0010 0000 0002 0007 -- 07 003 008 006 008 011 014 0048.43 0072.65 0001.50 | # Python 3.0-3.6
0029 0011 0020 0010 0000 0002 0007 -- 07 003 008 006 008 011 014 0048.43 0072.65 0001.50 | grammars.append(pygram.python_grammar)
0029 0011 0020 0010 0000 0002 0007 -- 07 003 008 006 008 011 014 0048.43 0072.65 0001.50 | if any(Feature.PATTERN_MATCHING in VERSION_TO_FEATURES[v] for v in target_versions):
0029 0011 0020 0010 0000 0002 0007 -- 07 003 008 006 008 011 014 0048.43 0072.65 0001.50 | # Python 3.10+
0029 0011 0020 0010 0000 0002 0007 -- 07 003 008 006 008 011 014 0048.43 0072.65 0001.50 | grammars.append(pygram.python_grammar_soft_keywords)
0029 0011 0020 0010 0000 0002 0007 -- 07 003 008 006 008 011 014 0048.43 0072.65 0001.50 |
0029 0011 0020 0010 0000 0002 0007 -- 07 003 008 006 008 011 014 0048.43 0072.65 0001.50 | # At least one of the above branches must have been taken, because every Python
0029 0011 0020 0010 0000 0002 0007 -- 07 003 008 006 008 011 014 0048.43 0072.65 0001.50 | # version has exactly one of the two 'ASYNC_*' flags
0029 0011 0020 0010 0000 0002 0007 -- 07 003 008 006 008 011 014 0048.43 0072.65 0001.50 | return grammars
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 | def lib2to3_parse(src_txt: str, target_versions: Iterable[TargetVersion] = ()) -> Node:
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 | """Given a string with source, return the lib2to3 Node."""
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 | if not src_txt.endswith("\n"):
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 | src_txt += "\n"
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 |
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 | grammars = get_grammars(set(target_versions))
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 | errors = {}
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 | for grammar in grammars:
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 | drv = driver.Driver(grammar)
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 | try:
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 | result = drv.parse_string(src_txt, True)
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 | break
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 |
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 | except ParseError as pe:
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 | lineno, column = pe.context[1]
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 | lines = src_txt.splitlines()
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 | try:
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 | faulty_line = lines[lineno - 1]
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 | except IndexError:
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 | faulty_line = "<line number missing in source>"
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 | errors[grammar.version] = InvalidInput(
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 | f"Cannot parse: {lineno}:{column}: {faulty_line}"
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 | )
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 |
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 | except TokenError as te:
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 | # In edge cases these are raised; and typically don't have a "faulty_line".
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 | lineno, column = te.args[1]
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 | errors[grammar.version] = InvalidInput(
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 | f"Cannot parse: {lineno}:{column}: {te.args[0]}"
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 | )
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 |
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 | else:
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 | # Choose the latest version when raising the actual parsing error.
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 | assert len(errors) >= 1
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 | exc = errors[max(errors)]
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 | raise exc from None
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 |
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 | if isinstance(result, Leaf):
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 | result = Node(syms.file_input, [result])
0040 0029 0032 0002 0000 0005 0003 -- 08 004 006 004 007 010 011 0036.54 0085.26 0002.33 | return result
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
0008 0008 0008 0000 0000 0000 0000 -- 03 000 000 000 000 000 000 0000.00 0000.00 0000.00 | def matches_grammar(src_txt: str, grammar: Grammar) -> bool:
0008 0008 0008 0000 0000 0000 0000 -- 03 000 000 000 000 000 000 0000.00 0000.00 0000.00 | drv = driver.Driver(grammar)
0008 0008 0008 0000 0000 0000 0000 -- 03 000 000 000 000 000 000 0000.00 0000.00 0000.00 | try:
0008 0008 0008 0000 0000 0000 0000 -- 03 000 000 000 000 000 000 0000.00 0000.00 0000.00 | drv.parse_string(src_txt, True)
0008 0008 0008 0000 0000 0000 0000 -- 03 000 000 000 000 000 000 0000.00 0000.00 0000.00 | except (ParseError, TokenError, IndentationError):
0008 0008 0008 0000 0000 0000 0000 -- 03 000 000 000 000 000 000 0000.00 0000.00 0000.00 | return False
0008 0008 0008 0000 0000 0000 0000 -- 03 000 000 000 000 000 000 0000.00 0000.00 0000.00 | else:
0008 0008 0008 0000 0000 0000 0000 -- 03 000 000 000 000 000 000 0000.00 0000.00 0000.00 | return True
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
0004 0004 0003 0000 0000 0000 0001 -- 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | def lib2to3_unparse(node: Node) -> str:
0004 0004 0003 0000 0000 0000 0001 -- 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | """Given a lib2to3 node, return its string representation."""
0004 0004 0003 0000 0000 0000 0001 -- 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | code = str(node)
0004 0004 0003 0000 0000 0000 0001 -- 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | return code
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
0007 0003 0007 0000 0000 0000 0000 -- 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | def parse_single_version(
0007 0003 0007 0000 0000 0000 0000 -- 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | src: str, version: Tuple[int, int], *, type_comments: bool
0007 0003 0007 0000 0000 0000 0000 -- 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | ) -> ast.AST:
0007 0003 0007 0000 0000 0000 0000 -- 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | filename = "<unknown>"
0007 0003 0007 0000 0000 0000 0000 -- 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | return ast.parse(
0007 0003 0007 0000 0000 0000 0000 -- 01 000 000 000 000 000 000 0000.00 0000.00 0000.00 | src, filename, feature_version=version, type_comments=type_comments
0007 0003 0007 0000 0000 0000 0000 -- -- 000 000 000 000 000 000 0000.00 0000.00 0000.00 | )
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
0020 0015 0015 0002 0000 0003 0002 -- 07 002 003 002 003 005 005 0011.61 0011.61 0001.00 | def parse_ast(src: str) -> ast.AST:
0020 0015 0015 0002 0000 0003 0002 -- 07 002 003 002 003 005 005 0011.61 0011.61 0001.00 | # TODO: support Python 4+ ;)
0020 0015 0015 0002 0000 0003 0002 -- 07 002 003 002 003 005 005 0011.61 0011.61 0001.00 | versions = [(3, minor) for minor in range(3, sys.version_info[1] + 1)]
0020 0015 0015 0002 0000 0003 0002 -- 07 002 003 002 003 005 005 0011.61 0011.61 0001.00 |
0020 0015 0015 0002 0000 0003 0002 -- 07 002 003 002 003 005 005 0011.61 0011.61 0001.00 | first_error = ""
0020 0015 0015 0002 0000 0003 0002 -- 07 002 003 002 003 005 005 0011.61 0011.61 0001.00 | for version in sorted(versions, reverse=True):
0020 0015 0015 0002 0000 0003 0002 -- 07 002 003 002 003 005 005 0011.61 0011.61 0001.00 | try:
0020 0015 0015 0002 0000 0003 0002 -- 07 002 003 002 003 005 005 0011.61 0011.61 0001.00 | return parse_single_version(src, version, type_comments=True)
0020 0015 0015 0002 0000 0003 0002 -- 07 002 003 002 003 005 005 0011.61 0011.61 0001.00 | except SyntaxError as e:
0020 0015 0015 0002 0000 0003 0002 -- 07 002 003 002 003 005 005 0011.61 0011.61 0001.00 | if not first_error:
0020 0015 0015 0002 0000 0003 0002 -- 07 002 003 002 003 005 005 0011.61 0011.61 0001.00 | first_error = str(e)
0020 0015 0015 0002 0000 0003 0002 -- 07 002 003 002 003 005 005 0011.61 0011.61 0001.00 |
0020 0015 0015 0002 0000 0003 0002 -- 07 002 003 002 003 005 005 0011.61 0011.61 0001.00 | # Try to parse without type comments
0020 0015 0015 0002 0000 0003 0002 -- 07 002 003 002 003 005 005 0011.61 0011.61 0001.00 | for version in sorted(versions, reverse=True):
0020 0015 0015 0002 0000 0003 0002 -- 07 002 003 002 003 005 005 0011.61 0011.61 0001.00 | try:
0020 0015 0015 0002 0000 0003 0002 -- 07 002 003 002 003 005 005 0011.61 0011.61 0001.00 | return parse_single_version(src, version, type_comments=False)
0020 0015 0015 0002 0000 0003 0002 -- 07 002 003 002 003 005 005 0011.61 0011.61 0001.00 | except SyntaxError:
0020 0015 0015 0002 0000 0003 0002 -- 07 002 003 002 003 005 005 0011.61 0011.61 0001.00 | pass
0020 0015 0015 0002 0000 0003 0002 -- 07 002 003 002 003 005 005 0011.61 0011.61 0001.00 |
0020 0015 0015 0002 0000 0003 0002 -- 07 002 003 002 003 005 005 0011.61 0011.61 0001.00 | raise SyntaxError(first_error)
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
0008 0005 0004 0004 0000 0000 0004 -- 02 000 000 000 000 000 000 0000.00 0000.00 0000.00 | def _normalize(lineend: str, value: str) -> str:
0008 0005 0004 0004 0000 0000 0004 -- 02 000 000 000 000 000 000 0000.00 0000.00 0000.00 | # To normalize, we strip any leading and trailing space from
0008 0005 0004 0004 0000 0000 0004 -- 02 000 000 000 000 000 000 0000.00 0000.00 0000.00 | # each line...
0008 0005 0004 0004 0000 0000 0004 -- 02 000 000 000 000 000 000 0000.00 0000.00 0000.00 | stripped: List[str] = [i.strip() for i in value.splitlines()]
0008 0005 0004 0004 0000 0000 0004 -- 02 000 000 000 000 000 000 0000.00 0000.00 0000.00 | normalized = lineend.join(stripped)
0008 0005 0004 0004 0000 0000 0004 -- 02 000 000 000 000 000 000 0000.00 0000.00 0000.00 | # ...and remove any blank lines at the beginning and end of
0008 0005 0004 0004 0000 0000 0004 -- 02 000 000 000 000 000 000 0000.00 0000.00 0000.00 | # the whole string
0008 0005 0004 0004 0000 0000 0004 -- 02 000 000 000 000 000 000 0000.00 0000.00 0000.00 | return normalized.strip()
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
---- ---- ---- ---- ---- ---- ---- -- -- --- --- --- --- --- --- ------- ------- ------- |
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | def stringify_ast(node: ast.AST, depth: int = 0) -> Iterator[str]:
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | """Simple visitor generating strings to compare ASTs by content."""
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 |
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | if (
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | isinstance(node, ast.Constant)
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | and isinstance(node.value, str)
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | and node.kind == "u"
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | ):
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | # It's a quirk of history that we strip the u prefix over here. We used to
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | # rewrite the AST nodes for Python version compatibility and we never copied
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | # over the kind
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | node.kind = None
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 |
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | yield f"{' ' * depth}{node.__class__.__name__}("
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 |
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | for field in sorted(node._fields): # noqa: F402
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | # TypeIgnore has only one field 'lineno' which breaks this comparison
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | if isinstance(node, ast.TypeIgnore):
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | break
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 |
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | try:
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | value: object = getattr(node, field)
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | except AttributeError:
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | continue
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 |
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | yield f"{' ' * (depth + 1)}{field}="
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 |
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | if isinstance(value, list):
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | for item in value:
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | # Ignore nested tuples within del statements, because we may insert
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | # parentheses and they change the AST.
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | if (
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | field == "targets"
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | and isinstance(node, ast.Delete)
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | and isinstance(item, ast.Tuple)
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | ):
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | for elt in item.elts:
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | yield from stringify_ast(elt, depth + 2)
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 |
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | elif isinstance(item, ast.AST):
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | yield from stringify_ast(item, depth + 2)
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 |
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | elif isinstance(value, ast.AST):
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | yield from stringify_ast(value, depth + 2)
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 |
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | else:
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | normalized: object
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | if (
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | isinstance(node, ast.Constant)
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | and field == "value"
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | and isinstance(value, str)
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | ):
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | # Constant strings may be indented across newlines, if they are
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | # docstrings; fold spaces after newlines when comparing. Similarly,
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | # trailing and leading space may be removed.
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | normalized = _normalize("\n", value)
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | elif field == "type_comment" and isinstance(value, str):
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | # Trailing whitespace in type comments is removed.
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | normalized = value.rstrip()
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | else:
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | normalized = value
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | yield f"{' ' * (depth + 2)}{normalized!r}, # {value.__class__.__name__}"
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 |
0064 0034 0043 0011 0000 0010 0011 -- 20 004 023 017 037 027 054 0256.76 0826.11 0003.22 | yield f"{' ' * depth}) # /{node.__class__.__name__}"