All patches and comments are welcome. Please squash your changes to logical
commits before using git-format-patch and git-send-email to
patches@git.madduck.net.
If you'd read over the Git project's submission guidelines and adhered to them,
I'd be especially grateful.
2 blib2to3 Node/Leaf transformation-related utility functions.
17 if sys.version_info >= (3, 8):
18 from typing import Final
20 from typing_extensions import Final
21 if sys.version_info >= (3, 10):
22 from typing import TypeGuard
24 from typing_extensions import TypeGuard
26 from mypy_extensions import mypyc_attr
29 from blib2to3.pytree import Node, Leaf, type_repr, NL
30 from blib2to3 import pygram
31 from blib2to3.pgen2 import token
33 from black.cache import CACHE_DIR
34 from black.strings import has_triple_quotes
37 pygram.initialize(CACHE_DIR)
38 syms: Final = pygram.python_symbols
43 LN = Union[Leaf, Node]
48 WHITESPACE: Final = {token.DEDENT, token.INDENT, token.NEWLINE}
61 STANDALONE_COMMENT: Final = 153
62 token.tok_name[STANDALONE_COMMENT] = "STANDALONE_COMMENT"
63 LOGIC_OPERATORS: Final = {"and", "or"}
64 COMPARATORS: Final = {
72 MATH_OPERATORS: Final = {
88 STARS: Final = {token.STAR, token.DOUBLESTAR}
89 VARARGS_SPECIALS: Final = STARS | {token.SLASH}
90 VARARGS_PARENTS: Final = {
92 syms.argument, # double star in arglist
93 syms.trailer, # single argument to call
95 syms.varargslist, # lambdas
97 UNPACKING_PARENTS: Final = {
98 syms.atom, # single element of a list or set literal
102 syms.testlist_star_expr,
106 TEST_DESCENDANTS: Final = {
123 TYPED_NAMES: Final = {syms.tname, syms.tname_star}
124 ASSIGNMENTS: Final = {
141 IMPLICIT_TUPLE: Final = {syms.testlist, syms.testlist_star_expr, syms.exprlist}
143 token.LPAR: token.RPAR,
144 token.LSQB: token.RSQB,
145 token.LBRACE: token.RBRACE,
147 OPENING_BRACKETS: Final = set(BRACKET.keys())
148 CLOSING_BRACKETS: Final = set(BRACKET.values())
149 BRACKETS: Final = OPENING_BRACKETS | CLOSING_BRACKETS
150 ALWAYS_NO_SPACE: Final = CLOSING_BRACKETS | {token.COMMA, STANDALONE_COMMENT}
155 @mypyc_attr(allow_interpreted_subclasses=True)
156 class Visitor(Generic[T]):
157 """Basic lib2to3 visitor that yields things of type `T` on `visit()`."""
159 def visit(self, node: LN) -> Iterator[T]:
160 """Main method to visit `node` and its children.
162 It tries to find a `visit_*()` method for the given `node.type`, like
163 `visit_simple_stmt` for Node objects or `visit_INDENT` for Leaf objects.
164 If no dedicated `visit_*()` method is found, chooses `visit_default()`
167 Then yields objects of type `T` from the selected visitor.
170 name = token.tok_name[node.type]
172 name = str(type_repr(node.type))
173 # We explicitly branch on whether a visitor exists (instead of
174 # using self.visit_default as the default arg to getattr) in order
175 # to save needing to create a bound method object and so mypyc can
176 # generate a native call to visit_default.
177 visitf = getattr(self, f"visit_{name}", None)
179 yield from visitf(node)
181 yield from self.visit_default(node)
183 def visit_default(self, node: LN) -> Iterator[T]:
184 """Default `visit_*()` implementation. Recurses to children of `node`."""
185 if isinstance(node, Node):
186 for child in node.children:
187 yield from self.visit(child)
190 def whitespace(leaf: Leaf, *, complex_subscript: bool) -> str: # noqa: C901
191 """Return whitespace prefix if needed for the given `leaf`.
193 `complex_subscript` signals whether the given leaf is part of a subscription
194 which has non-trivial arguments, like arithmetic expressions or function calls.
198 DOUBLESPACE: Final = " "
202 if t in ALWAYS_NO_SPACE:
205 if t == token.COMMENT:
208 assert p is not None, f"INTERNAL ERROR: hand-made leaf without parent: {leaf!r}"
209 if t == token.COLON and p.type not in {
216 prev = leaf.prev_sibling
218 prevp = preceding_leaf(p)
219 if not prevp or prevp.type in OPENING_BRACKETS:
223 if prevp.type == token.COLON:
226 elif prevp.type != token.COMMA and not complex_subscript:
231 if prevp.type == token.EQUAL:
233 if prevp.parent.type in {
241 elif prevp.parent.type == syms.typedargslist:
242 # A bit hacky: if the equal sign has whitespace, it means we
243 # previously found it's a typed argument. So, we're using
248 prevp.type == token.STAR
249 and parent_type(prevp) == syms.star_expr
250 and parent_type(prevp.parent) == syms.subscriptlist
252 # No space between typevar tuples.
255 elif prevp.type in VARARGS_SPECIALS:
256 if is_vararg(prevp, within=VARARGS_PARENTS | UNPACKING_PARENTS):
259 elif prevp.type == token.COLON:
260 if prevp.parent and prevp.parent.type in {syms.subscript, syms.sliceop}:
261 return SPACE if complex_subscript else NO
265 and prevp.parent.type == syms.factor
266 and prevp.type in MATH_OPERATORS
270 elif prevp.type == token.AT and p.parent and p.parent.type == syms.decorator:
271 # no space in decorators
274 elif prev.type in OPENING_BRACKETS:
277 if p.type in {syms.parameters, syms.arglist}:
278 # untyped function signatures or calls
279 if not prev or prev.type != token.COMMA:
282 elif p.type == syms.varargslist:
284 if prev and prev.type != token.COMMA:
287 elif p.type == syms.typedargslist:
288 # typed function signatures
293 if prev.type not in TYPED_NAMES:
296 elif prev.type == token.EQUAL:
297 # A bit hacky: if the equal sign has whitespace, it means we
298 # previously found it's a typed argument. So, we're using that, too.
301 elif prev.type != token.COMMA:
304 elif p.type in TYPED_NAMES:
307 prevp = preceding_leaf(p)
308 if not prevp or prevp.type != token.COMMA:
311 elif p.type == syms.trailer:
312 # attributes and calls
313 if t == token.LPAR or t == token.RPAR:
317 if t == token.DOT or t == token.LSQB:
320 elif prev.type != token.COMMA:
323 elif p.type == syms.argument:
329 prevp = preceding_leaf(p)
330 if not prevp or prevp.type == token.LPAR:
333 elif prev.type in {token.EQUAL} | VARARGS_SPECIALS:
336 elif p.type == syms.decorator:
340 elif p.type == syms.dotted_name:
344 prevp = preceding_leaf(p)
345 if not prevp or prevp.type == token.AT or prevp.type == token.DOT:
348 elif p.type == syms.classdef:
352 if prev and prev.type == token.LPAR:
355 elif p.type in {syms.subscript, syms.sliceop}:
358 assert p.parent is not None, "subscripts are always parented"
359 if p.parent.type == syms.subscriptlist:
364 elif not complex_subscript:
367 elif p.type == syms.atom:
368 if prev and t == token.DOT:
369 # dots, but not the first one.
372 elif p.type == syms.dictsetmaker:
374 if prev and prev.type == token.DOUBLESTAR:
377 elif p.type in {syms.factor, syms.star_expr}:
380 prevp = preceding_leaf(p)
381 if not prevp or prevp.type in OPENING_BRACKETS:
384 prevp_parent = prevp.parent
385 assert prevp_parent is not None
386 if prevp.type == token.COLON and prevp_parent.type in {
392 elif prevp.type == token.EQUAL and prevp_parent.type == syms.argument:
395 elif t in {token.NAME, token.NUMBER, token.STRING}:
398 elif p.type == syms.import_from:
400 if prev and prev.type == token.DOT:
403 elif t == token.NAME:
407 if prev and prev.type == token.DOT:
410 elif p.type == syms.sliceop:
413 elif p.type == syms.except_clause:
420 def preceding_leaf(node: Optional[LN]) -> Optional[Leaf]:
421 """Return the first leaf that precedes `node`, if any."""
423 res = node.prev_sibling
425 if isinstance(res, Leaf):
429 return list(res.leaves())[-1]
438 def prev_siblings_are(node: Optional[LN], tokens: List[Optional[NodeType]]) -> bool:
439 """Return if the `node` and its previous siblings match types against the provided
440 list of tokens; the provided `node`has its type matched against the last element in
441 the list. `None` can be used as the first element to declare that the start of the
442 list is anchored at the start of its parent's children."""
445 if tokens[-1] is None:
449 if node.type != tokens[-1]:
451 return prev_siblings_are(node.prev_sibling, tokens[:-1])
454 def parent_type(node: Optional[LN]) -> Optional[NodeType]:
457 @node.parent.type, if @node is not None and has a parent.
461 if node is None or node.parent is None:
464 return node.parent.type
467 def child_towards(ancestor: Node, descendant: LN) -> Optional[LN]:
468 """Return the child of `ancestor` that contains `descendant`."""
469 node: Optional[LN] = descendant
470 while node and node.parent != ancestor:
475 def replace_child(old_child: LN, new_child: LN) -> None:
478 * If @old_child.parent is set, replace @old_child with @new_child in
479 @old_child's underlying Node structure.
481 * Otherwise, this function does nothing.
483 parent = old_child.parent
487 child_idx = old_child.remove()
488 if child_idx is not None:
489 parent.insert_child(child_idx, new_child)
492 def container_of(leaf: Leaf) -> LN:
493 """Return `leaf` or one of its ancestors that is the topmost container of it.
495 By "container" we mean a node where `leaf` is the very first child.
497 same_prefix = leaf.prefix
500 parent = container.parent
504 if parent.children[0].prefix != same_prefix:
507 if parent.type == syms.file_input:
510 if parent.prev_sibling is not None and parent.prev_sibling.type in BRACKETS:
517 def first_leaf_column(node: Node) -> Optional[int]:
518 """Returns the column of the first leaf child of a node."""
519 for child in node.children:
520 if isinstance(child, Leaf):
525 def is_arith_like(node: LN) -> bool:
526 """Whether node is an arithmetic or a binary arithmetic expression"""
527 return node.type in {
535 def is_docstring(leaf: Leaf) -> bool:
536 if prev_siblings_are(
537 leaf.parent, [None, token.NEWLINE, token.INDENT, syms.simple_stmt]
541 # Multiline docstring on the same line as the `def`.
542 if prev_siblings_are(leaf.parent, [syms.parameters, token.COLON, syms.simple_stmt]):
543 # `syms.parameters` is only used in funcdefs and async_funcdefs in the Python
544 # grammar. We're safe to return True without further checks.
550 def is_empty_tuple(node: LN) -> bool:
551 """Return True if `node` holds an empty tuple."""
553 node.type == syms.atom
554 and len(node.children) == 2
555 and node.children[0].type == token.LPAR
556 and node.children[1].type == token.RPAR
560 def is_one_tuple(node: LN) -> bool:
561 """Return True if `node` holds a tuple with one element, with or without parens."""
562 if node.type == syms.atom:
563 gexp = unwrap_singleton_parenthesis(node)
564 if gexp is None or gexp.type != syms.testlist_gexp:
567 return len(gexp.children) == 2 and gexp.children[1].type == token.COMMA
570 node.type in IMPLICIT_TUPLE
571 and len(node.children) == 2
572 and node.children[1].type == token.COMMA
576 def is_one_sequence_between(
580 brackets: Tuple[int, int] = (token.LPAR, token.RPAR),
582 """Return True if content between `opening` and `closing` is a one-sequence."""
583 if (opening.type, closing.type) != brackets:
586 depth = closing.bracket_depth + 1
587 for _opening_index, leaf in enumerate(leaves):
592 raise LookupError("Opening paren not found in `leaves`")
596 for leaf in leaves[_opening_index:]:
600 bracket_depth = leaf.bracket_depth
601 if bracket_depth == depth and leaf.type == token.COMMA:
603 if leaf.parent and leaf.parent.type in {
613 def is_walrus_assignment(node: LN) -> bool:
614 """Return True iff `node` is of the shape ( test := test )"""
615 inner = unwrap_singleton_parenthesis(node)
616 return inner is not None and inner.type == syms.namedexpr_test
619 def is_simple_decorator_trailer(node: LN, last: bool = False) -> bool:
620 """Return True iff `node` is a trailer valid in a simple decorator"""
621 return node.type == syms.trailer and (
623 len(node.children) == 2
624 and node.children[0].type == token.DOT
625 and node.children[1].type == token.NAME
627 # last trailer can be an argument-less parentheses pair
630 and len(node.children) == 2
631 and node.children[0].type == token.LPAR
632 and node.children[1].type == token.RPAR
634 # last trailer can be arguments
637 and len(node.children) == 3
638 and node.children[0].type == token.LPAR
639 # and node.children[1].type == syms.argument
640 and node.children[2].type == token.RPAR
645 def is_simple_decorator_expression(node: LN) -> bool:
646 """Return True iff `node` could be a 'dotted name' decorator
648 This function takes the node of the 'namedexpr_test' of the new decorator
649 grammar and test if it would be valid under the old decorator grammar.
651 The old grammar was: decorator: @ dotted_name [arguments] NEWLINE
652 The new grammar is : decorator: @ namedexpr_test NEWLINE
654 if node.type == token.NAME:
656 if node.type == syms.power:
659 node.children[0].type == token.NAME
660 and all(map(is_simple_decorator_trailer, node.children[1:-1]))
662 len(node.children) < 2
663 or is_simple_decorator_trailer(node.children[-1], last=True)
669 def is_yield(node: LN) -> bool:
670 """Return True if `node` holds a `yield` or `yield from` expression."""
671 if node.type == syms.yield_expr:
674 if is_name_token(node) and node.value == "yield":
677 if node.type != syms.atom:
680 if len(node.children) != 3:
683 lpar, expr, rpar = node.children
684 if lpar.type == token.LPAR and rpar.type == token.RPAR:
685 return is_yield(expr)
690 def is_vararg(leaf: Leaf, within: Set[NodeType]) -> bool:
691 """Return True if `leaf` is a star or double star in a vararg or kwarg.
693 If `within` includes VARARGS_PARENTS, this applies to function signatures.
694 If `within` includes UNPACKING_PARENTS, it applies to right hand-side
695 extended iterable unpacking (PEP 3132) and additional unpacking
696 generalizations (PEP 448).
698 if leaf.type not in VARARGS_SPECIALS or not leaf.parent:
702 if p.type == syms.star_expr:
703 # Star expressions are also used as assignment targets in extended
704 # iterable unpacking (PEP 3132). See what its parent is instead.
710 return p.type in within
713 def is_multiline_string(leaf: Leaf) -> bool:
714 """Return True if `leaf` is a multiline string that actually spans many lines."""
715 return has_triple_quotes(leaf.value) and "\n" in leaf.value
718 def is_stub_suite(node: Node) -> bool:
719 """Return True if `node` is a suite with a stub body."""
721 len(node.children) != 4
722 or node.children[0].type != token.NEWLINE
723 or node.children[1].type != token.INDENT
724 or node.children[3].type != token.DEDENT
728 return is_stub_body(node.children[2])
731 def is_stub_body(node: LN) -> bool:
732 """Return True if `node` is a simple statement containing an ellipsis."""
733 if not isinstance(node, Node) or node.type != syms.simple_stmt:
736 if len(node.children) != 2:
739 child = node.children[0]
741 child.type == syms.atom
742 and len(child.children) == 3
743 and all(leaf == Leaf(token.DOT, ".") for leaf in child.children)
747 def is_atom_with_invisible_parens(node: LN) -> bool:
748 """Given a `LN`, determines whether it's an atom `node` with invisible
749 parens. Useful in dedupe-ing and normalizing parens.
751 if isinstance(node, Leaf) or node.type != syms.atom:
754 first, last = node.children[0], node.children[-1]
756 isinstance(first, Leaf)
757 and first.type == token.LPAR
758 and first.value == ""
759 and isinstance(last, Leaf)
760 and last.type == token.RPAR
765 def is_empty_par(leaf: Leaf) -> bool:
766 return is_empty_lpar(leaf) or is_empty_rpar(leaf)
769 def is_empty_lpar(leaf: Leaf) -> bool:
770 return leaf.type == token.LPAR and leaf.value == ""
773 def is_empty_rpar(leaf: Leaf) -> bool:
774 return leaf.type == token.RPAR and leaf.value == ""
777 def is_import(leaf: Leaf) -> bool:
778 """Return True if the given leaf starts an import statement."""
785 (v == "import" and p and p.type == syms.import_name)
786 or (v == "from" and p and p.type == syms.import_from)
791 def is_type_comment(leaf: Leaf, suffix: str = "") -> bool:
792 """Return True if the given leaf is a special comment.
793 Only returns true for type comments for now."""
796 return t in {token.COMMENT, STANDALONE_COMMENT} and v.startswith("# type:" + suffix)
799 def wrap_in_parentheses(parent: Node, child: LN, *, visible: bool = True) -> None:
800 """Wrap `child` in parentheses.
802 This replaces `child` with an atom holding the parentheses and the old
803 child. That requires moving the prefix.
805 If `visible` is False, the leaves will be valueless (and thus invisible).
807 lpar = Leaf(token.LPAR, "(" if visible else "")
808 rpar = Leaf(token.RPAR, ")" if visible else "")
809 prefix = child.prefix
811 index = child.remove() or 0
812 new_child = Node(syms.atom, [lpar, child, rpar])
813 new_child.prefix = prefix
814 parent.insert_child(index, new_child)
817 def unwrap_singleton_parenthesis(node: LN) -> Optional[LN]:
818 """Returns `wrapped` if `node` is of the shape ( wrapped ).
820 Parenthesis can be optional. Returns None otherwise"""
821 if len(node.children) != 3:
824 lpar, wrapped, rpar = node.children
825 if not (lpar.type == token.LPAR and rpar.type == token.RPAR):
831 def ensure_visible(leaf: Leaf) -> None:
832 """Make sure parentheses are visible.
834 They could be invisible as part of some statements (see
835 :func:`normalize_invisible_parens` and :func:`visit_import_from`).
837 if leaf.type == token.LPAR:
839 elif leaf.type == token.RPAR:
843 def is_name_token(nl: NL) -> TypeGuard[Leaf]:
844 return nl.type == token.NAME
847 def is_lpar_token(nl: NL) -> TypeGuard[Leaf]:
848 return nl.type == token.LPAR
851 def is_rpar_token(nl: NL) -> TypeGuard[Leaf]:
852 return nl.type == token.RPAR
855 def is_string_token(nl: NL) -> TypeGuard[Leaf]:
856 return nl.type == token.STRING
859 def is_number_token(nl: NL) -> TypeGuard[Leaf]:
860 return nl.type == token.NUMBER