]> git.madduck.net Git - etc/vim.git/blob - src/black/nodes.py

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

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.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

51d4cb8618d0a6a2c1f8ac1d55318ed2579fb5d9
[etc/vim.git] / src / black / nodes.py
1 """
2 blib2to3 Node/Leaf transformation-related utility functions.
3 """
4
5 import sys
6 from typing import (
7     Collection,
8     Generic,
9     Iterator,
10     List,
11     Optional,
12     Set,
13     Tuple,
14     TypeVar,
15     Union,
16 )
17
18 if sys.version_info >= (3, 8):
19     from typing import Final
20 else:
21     from typing_extensions import Final
22 if sys.version_info >= (3, 10):
23     from typing import TypeGuard
24 else:
25     from typing_extensions import TypeGuard
26
27 from mypy_extensions import mypyc_attr
28
29 # lib2to3 fork
30 from blib2to3.pytree import Node, Leaf, type_repr, NL
31 from blib2to3 import pygram
32 from blib2to3.pgen2 import token
33
34 from black.cache import CACHE_DIR
35 from black.strings import has_triple_quotes
36
37
38 pygram.initialize(CACHE_DIR)
39 syms: Final = pygram.python_symbols
40
41
42 # types
43 T = TypeVar("T")
44 LN = Union[Leaf, Node]
45 LeafID = int
46 NodeType = int
47
48
49 WHITESPACE: Final = {token.DEDENT, token.INDENT, token.NEWLINE}
50 STATEMENT: Final = {
51     syms.if_stmt,
52     syms.while_stmt,
53     syms.for_stmt,
54     syms.try_stmt,
55     syms.except_clause,
56     syms.with_stmt,
57     syms.funcdef,
58     syms.classdef,
59     syms.match_stmt,
60     syms.case_block,
61 }
62 STANDALONE_COMMENT: Final = 153
63 token.tok_name[STANDALONE_COMMENT] = "STANDALONE_COMMENT"
64 LOGIC_OPERATORS: Final = {"and", "or"}
65 COMPARATORS: Final = {
66     token.LESS,
67     token.GREATER,
68     token.EQEQUAL,
69     token.NOTEQUAL,
70     token.LESSEQUAL,
71     token.GREATEREQUAL,
72 }
73 MATH_OPERATORS: Final = {
74     token.VBAR,
75     token.CIRCUMFLEX,
76     token.AMPER,
77     token.LEFTSHIFT,
78     token.RIGHTSHIFT,
79     token.PLUS,
80     token.MINUS,
81     token.STAR,
82     token.SLASH,
83     token.DOUBLESLASH,
84     token.PERCENT,
85     token.AT,
86     token.TILDE,
87     token.DOUBLESTAR,
88 }
89 STARS: Final = {token.STAR, token.DOUBLESTAR}
90 VARARGS_SPECIALS: Final = STARS | {token.SLASH}
91 VARARGS_PARENTS: Final = {
92     syms.arglist,
93     syms.argument,  # double star in arglist
94     syms.trailer,  # single argument to call
95     syms.typedargslist,
96     syms.varargslist,  # lambdas
97 }
98 UNPACKING_PARENTS: Final = {
99     syms.atom,  # single element of a list or set literal
100     syms.dictsetmaker,
101     syms.listmaker,
102     syms.testlist_gexp,
103     syms.testlist_star_expr,
104     syms.subject_expr,
105     syms.pattern,
106 }
107 TEST_DESCENDANTS: Final = {
108     syms.test,
109     syms.lambdef,
110     syms.or_test,
111     syms.and_test,
112     syms.not_test,
113     syms.comparison,
114     syms.star_expr,
115     syms.expr,
116     syms.xor_expr,
117     syms.and_expr,
118     syms.shift_expr,
119     syms.arith_expr,
120     syms.trailer,
121     syms.term,
122     syms.power,
123 }
124 ASSIGNMENTS: Final = {
125     "=",
126     "+=",
127     "-=",
128     "*=",
129     "@=",
130     "/=",
131     "%=",
132     "&=",
133     "|=",
134     "^=",
135     "<<=",
136     ">>=",
137     "**=",
138     "//=",
139 }
140
141 IMPLICIT_TUPLE: Final = {syms.testlist, syms.testlist_star_expr, syms.exprlist}
142 BRACKET: Final = {
143     token.LPAR: token.RPAR,
144     token.LSQB: token.RSQB,
145     token.LBRACE: token.RBRACE,
146 }
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}
151
152 RARROW = 55
153
154
155 @mypyc_attr(allow_interpreted_subclasses=True)
156 class Visitor(Generic[T]):
157     """Basic lib2to3 visitor that yields things of type `T` on `visit()`."""
158
159     def visit(self, node: LN) -> Iterator[T]:
160         """Main method to visit `node` and its children.
161
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()`
165         instead.
166
167         Then yields objects of type `T` from the selected visitor.
168         """
169         if node.type < 256:
170             name = token.tok_name[node.type]
171         else:
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)
178         if visitf:
179             yield from visitf(node)
180         else:
181             yield from self.visit_default(node)
182
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)
188
189
190 def whitespace(leaf: Leaf, *, complex_subscript: bool) -> str:  # noqa: C901
191     """Return whitespace prefix if needed for the given `leaf`.
192
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.
195     """
196     NO: Final = ""
197     SPACE: Final = " "
198     DOUBLESPACE: Final = "  "
199     t = leaf.type
200     p = leaf.parent
201     v = leaf.value
202     if t in ALWAYS_NO_SPACE:
203         return NO
204
205     if t == token.COMMENT:
206         return DOUBLESPACE
207
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 {
210         syms.subscript,
211         syms.subscriptlist,
212         syms.sliceop,
213     }:
214         return NO
215
216     prev = leaf.prev_sibling
217     if not prev:
218         prevp = preceding_leaf(p)
219         if not prevp or prevp.type in OPENING_BRACKETS:
220             return NO
221
222         if t == token.COLON:
223             if prevp.type == token.COLON:
224                 return NO
225
226             elif prevp.type != token.COMMA and not complex_subscript:
227                 return NO
228
229             return SPACE
230
231         if prevp.type == token.EQUAL:
232             if prevp.parent:
233                 if prevp.parent.type in {
234                     syms.arglist,
235                     syms.argument,
236                     syms.parameters,
237                     syms.varargslist,
238                 }:
239                     return NO
240
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
244                     # that, too.
245                     return prevp.prefix
246
247         elif prevp.type in VARARGS_SPECIALS:
248             if is_vararg(prevp, within=VARARGS_PARENTS | UNPACKING_PARENTS):
249                 return NO
250
251         elif prevp.type == token.COLON:
252             if prevp.parent and prevp.parent.type in {syms.subscript, syms.sliceop}:
253                 return SPACE if complex_subscript else NO
254
255         elif (
256             prevp.parent
257             and prevp.parent.type == syms.factor
258             and prevp.type in MATH_OPERATORS
259         ):
260             return NO
261
262         elif prevp.type == token.AT and p.parent and p.parent.type == syms.decorator:
263             # no space in decorators
264             return NO
265
266     elif prev.type in OPENING_BRACKETS:
267         return NO
268
269     if p.type in {syms.parameters, syms.arglist}:
270         # untyped function signatures or calls
271         if not prev or prev.type != token.COMMA:
272             return NO
273
274     elif p.type == syms.varargslist:
275         # lambdas
276         if prev and prev.type != token.COMMA:
277             return NO
278
279     elif p.type == syms.typedargslist:
280         # typed function signatures
281         if not prev:
282             return NO
283
284         if t == token.EQUAL:
285             if prev.type != syms.tname:
286                 return NO
287
288         elif prev.type == token.EQUAL:
289             # A bit hacky: if the equal sign has whitespace, it means we
290             # previously found it's a typed argument.  So, we're using that, too.
291             return prev.prefix
292
293         elif prev.type != token.COMMA:
294             return NO
295
296     elif p.type == syms.tname:
297         # type names
298         if not prev:
299             prevp = preceding_leaf(p)
300             if not prevp or prevp.type != token.COMMA:
301                 return NO
302
303     elif p.type == syms.trailer:
304         # attributes and calls
305         if t == token.LPAR or t == token.RPAR:
306             return NO
307
308         if not prev:
309             if t == token.DOT or t == token.LSQB:
310                 return NO
311
312         elif prev.type != token.COMMA:
313             return NO
314
315     elif p.type == syms.argument:
316         # single argument
317         if t == token.EQUAL:
318             return NO
319
320         if not prev:
321             prevp = preceding_leaf(p)
322             if not prevp or prevp.type == token.LPAR:
323                 return NO
324
325         elif prev.type in {token.EQUAL} | VARARGS_SPECIALS:
326             return NO
327
328     elif p.type == syms.decorator:
329         # decorators
330         return NO
331
332     elif p.type == syms.dotted_name:
333         if prev:
334             return NO
335
336         prevp = preceding_leaf(p)
337         if not prevp or prevp.type == token.AT or prevp.type == token.DOT:
338             return NO
339
340     elif p.type == syms.classdef:
341         if t == token.LPAR:
342             return NO
343
344         if prev and prev.type == token.LPAR:
345             return NO
346
347     elif p.type in {syms.subscript, syms.sliceop}:
348         # indexing
349         if not prev:
350             assert p.parent is not None, "subscripts are always parented"
351             if p.parent.type == syms.subscriptlist:
352                 return SPACE
353
354             return NO
355
356         elif not complex_subscript:
357             return NO
358
359     elif p.type == syms.atom:
360         if prev and t == token.DOT:
361             # dots, but not the first one.
362             return NO
363
364     elif p.type == syms.dictsetmaker:
365         # dict unpacking
366         if prev and prev.type == token.DOUBLESTAR:
367             return NO
368
369     elif p.type in {syms.factor, syms.star_expr}:
370         # unary ops
371         if not prev:
372             prevp = preceding_leaf(p)
373             if not prevp or prevp.type in OPENING_BRACKETS:
374                 return NO
375
376             prevp_parent = prevp.parent
377             assert prevp_parent is not None
378             if prevp.type == token.COLON and prevp_parent.type in {
379                 syms.subscript,
380                 syms.sliceop,
381             }:
382                 return NO
383
384             elif prevp.type == token.EQUAL and prevp_parent.type == syms.argument:
385                 return NO
386
387         elif t in {token.NAME, token.NUMBER, token.STRING}:
388             return NO
389
390     elif p.type == syms.import_from:
391         if t == token.DOT:
392             if prev and prev.type == token.DOT:
393                 return NO
394
395         elif t == token.NAME:
396             if v == "import":
397                 return SPACE
398
399             if prev and prev.type == token.DOT:
400                 return NO
401
402     elif p.type == syms.sliceop:
403         return NO
404
405     return SPACE
406
407
408 def preceding_leaf(node: Optional[LN]) -> Optional[Leaf]:
409     """Return the first leaf that precedes `node`, if any."""
410     while node:
411         res = node.prev_sibling
412         if res:
413             if isinstance(res, Leaf):
414                 return res
415
416             try:
417                 return list(res.leaves())[-1]
418
419             except IndexError:
420                 return None
421
422         node = node.parent
423     return None
424
425
426 def prev_siblings_are(node: Optional[LN], tokens: List[Optional[NodeType]]) -> bool:
427     """Return if the `node` and its previous siblings match types against the provided
428     list of tokens; the provided `node`has its type matched against the last element in
429     the list.  `None` can be used as the first element to declare that the start of the
430     list is anchored at the start of its parent's children."""
431     if not tokens:
432         return True
433     if tokens[-1] is None:
434         return node is None
435     if not node:
436         return False
437     if node.type != tokens[-1]:
438         return False
439     return prev_siblings_are(node.prev_sibling, tokens[:-1])
440
441
442 def last_two_except(leaves: List[Leaf], omit: Collection[LeafID]) -> Tuple[Leaf, Leaf]:
443     """Return (penultimate, last) leaves skipping brackets in `omit` and contents."""
444     stop_after: Optional[Leaf] = None
445     last: Optional[Leaf] = None
446     for leaf in reversed(leaves):
447         if stop_after:
448             if leaf is stop_after:
449                 stop_after = None
450             continue
451
452         if last:
453             return leaf, last
454
455         if id(leaf) in omit:
456             stop_after = leaf.opening_bracket
457         else:
458             last = leaf
459     else:
460         raise LookupError("Last two leaves were also skipped")
461
462
463 def parent_type(node: Optional[LN]) -> Optional[NodeType]:
464     """
465     Returns:
466         @node.parent.type, if @node is not None and has a parent.
467             OR
468         None, otherwise.
469     """
470     if node is None or node.parent is None:
471         return None
472
473     return node.parent.type
474
475
476 def child_towards(ancestor: Node, descendant: LN) -> Optional[LN]:
477     """Return the child of `ancestor` that contains `descendant`."""
478     node: Optional[LN] = descendant
479     while node and node.parent != ancestor:
480         node = node.parent
481     return node
482
483
484 def replace_child(old_child: LN, new_child: LN) -> None:
485     """
486     Side Effects:
487         * If @old_child.parent is set, replace @old_child with @new_child in
488         @old_child's underlying Node structure.
489             OR
490         * Otherwise, this function does nothing.
491     """
492     parent = old_child.parent
493     if not parent:
494         return
495
496     child_idx = old_child.remove()
497     if child_idx is not None:
498         parent.insert_child(child_idx, new_child)
499
500
501 def container_of(leaf: Leaf) -> LN:
502     """Return `leaf` or one of its ancestors that is the topmost container of it.
503
504     By "container" we mean a node where `leaf` is the very first child.
505     """
506     same_prefix = leaf.prefix
507     container: LN = leaf
508     while container:
509         parent = container.parent
510         if parent is None:
511             break
512
513         if parent.children[0].prefix != same_prefix:
514             break
515
516         if parent.type == syms.file_input:
517             break
518
519         if parent.prev_sibling is not None and parent.prev_sibling.type in BRACKETS:
520             break
521
522         container = parent
523     return container
524
525
526 def first_leaf_column(node: Node) -> Optional[int]:
527     """Returns the column of the first leaf child of a node."""
528     for child in node.children:
529         if isinstance(child, Leaf):
530             return child.column
531     return None
532
533
534 def first_child_is_arith(node: Node) -> bool:
535     """Whether first child is an arithmetic or a binary arithmetic expression"""
536     expr_types = {
537         syms.arith_expr,
538         syms.shift_expr,
539         syms.xor_expr,
540         syms.and_expr,
541     }
542     return bool(node.children and node.children[0].type in expr_types)
543
544
545 def is_docstring(leaf: Leaf) -> bool:
546     if prev_siblings_are(
547         leaf.parent, [None, token.NEWLINE, token.INDENT, syms.simple_stmt]
548     ):
549         return True
550
551     # Multiline docstring on the same line as the `def`.
552     if prev_siblings_are(leaf.parent, [syms.parameters, token.COLON, syms.simple_stmt]):
553         # `syms.parameters` is only used in funcdefs and async_funcdefs in the Python
554         # grammar. We're safe to return True without further checks.
555         return True
556
557     return False
558
559
560 def is_empty_tuple(node: LN) -> bool:
561     """Return True if `node` holds an empty tuple."""
562     return (
563         node.type == syms.atom
564         and len(node.children) == 2
565         and node.children[0].type == token.LPAR
566         and node.children[1].type == token.RPAR
567     )
568
569
570 def is_one_tuple(node: LN) -> bool:
571     """Return True if `node` holds a tuple with one element, with or without parens."""
572     if node.type == syms.atom:
573         gexp = unwrap_singleton_parenthesis(node)
574         if gexp is None or gexp.type != syms.testlist_gexp:
575             return False
576
577         return len(gexp.children) == 2 and gexp.children[1].type == token.COMMA
578
579     return (
580         node.type in IMPLICIT_TUPLE
581         and len(node.children) == 2
582         and node.children[1].type == token.COMMA
583     )
584
585
586 def is_one_tuple_between(opening: Leaf, closing: Leaf, leaves: List[Leaf]) -> bool:
587     """Return True if content between `opening` and `closing` looks like a one-tuple."""
588     if opening.type != token.LPAR and closing.type != token.RPAR:
589         return False
590
591     depth = closing.bracket_depth + 1
592     for _opening_index, leaf in enumerate(leaves):
593         if leaf is opening:
594             break
595
596     else:
597         raise LookupError("Opening paren not found in `leaves`")
598
599     commas = 0
600     _opening_index += 1
601     for leaf in leaves[_opening_index:]:
602         if leaf is closing:
603             break
604
605         bracket_depth = leaf.bracket_depth
606         if bracket_depth == depth and leaf.type == token.COMMA:
607             commas += 1
608             if leaf.parent and leaf.parent.type in {
609                 syms.arglist,
610                 syms.typedargslist,
611             }:
612                 commas += 1
613                 break
614
615     return commas < 2
616
617
618 def is_walrus_assignment(node: LN) -> bool:
619     """Return True iff `node` is of the shape ( test := test )"""
620     inner = unwrap_singleton_parenthesis(node)
621     return inner is not None and inner.type == syms.namedexpr_test
622
623
624 def is_simple_decorator_trailer(node: LN, last: bool = False) -> bool:
625     """Return True iff `node` is a trailer valid in a simple decorator"""
626     return node.type == syms.trailer and (
627         (
628             len(node.children) == 2
629             and node.children[0].type == token.DOT
630             and node.children[1].type == token.NAME
631         )
632         # last trailer can be an argument-less parentheses pair
633         or (
634             last
635             and len(node.children) == 2
636             and node.children[0].type == token.LPAR
637             and node.children[1].type == token.RPAR
638         )
639         # last trailer can be arguments
640         or (
641             last
642             and len(node.children) == 3
643             and node.children[0].type == token.LPAR
644             # and node.children[1].type == syms.argument
645             and node.children[2].type == token.RPAR
646         )
647     )
648
649
650 def is_simple_decorator_expression(node: LN) -> bool:
651     """Return True iff `node` could be a 'dotted name' decorator
652
653     This function takes the node of the 'namedexpr_test' of the new decorator
654     grammar and test if it would be valid under the old decorator grammar.
655
656     The old grammar was: decorator: @ dotted_name [arguments] NEWLINE
657     The new grammar is : decorator: @ namedexpr_test NEWLINE
658     """
659     if node.type == token.NAME:
660         return True
661     if node.type == syms.power:
662         if node.children:
663             return (
664                 node.children[0].type == token.NAME
665                 and all(map(is_simple_decorator_trailer, node.children[1:-1]))
666                 and (
667                     len(node.children) < 2
668                     or is_simple_decorator_trailer(node.children[-1], last=True)
669                 )
670             )
671     return False
672
673
674 def is_yield(node: LN) -> bool:
675     """Return True if `node` holds a `yield` or `yield from` expression."""
676     if node.type == syms.yield_expr:
677         return True
678
679     if is_name_token(node) and node.value == "yield":
680         return True
681
682     if node.type != syms.atom:
683         return False
684
685     if len(node.children) != 3:
686         return False
687
688     lpar, expr, rpar = node.children
689     if lpar.type == token.LPAR and rpar.type == token.RPAR:
690         return is_yield(expr)
691
692     return False
693
694
695 def is_vararg(leaf: Leaf, within: Set[NodeType]) -> bool:
696     """Return True if `leaf` is a star or double star in a vararg or kwarg.
697
698     If `within` includes VARARGS_PARENTS, this applies to function signatures.
699     If `within` includes UNPACKING_PARENTS, it applies to right hand-side
700     extended iterable unpacking (PEP 3132) and additional unpacking
701     generalizations (PEP 448).
702     """
703     if leaf.type not in VARARGS_SPECIALS or not leaf.parent:
704         return False
705
706     p = leaf.parent
707     if p.type == syms.star_expr:
708         # Star expressions are also used as assignment targets in extended
709         # iterable unpacking (PEP 3132).  See what its parent is instead.
710         if not p.parent:
711             return False
712
713         p = p.parent
714
715     return p.type in within
716
717
718 def is_multiline_string(leaf: Leaf) -> bool:
719     """Return True if `leaf` is a multiline string that actually spans many lines."""
720     return has_triple_quotes(leaf.value) and "\n" in leaf.value
721
722
723 def is_stub_suite(node: Node) -> bool:
724     """Return True if `node` is a suite with a stub body."""
725     if (
726         len(node.children) != 4
727         or node.children[0].type != token.NEWLINE
728         or node.children[1].type != token.INDENT
729         or node.children[3].type != token.DEDENT
730     ):
731         return False
732
733     return is_stub_body(node.children[2])
734
735
736 def is_stub_body(node: LN) -> bool:
737     """Return True if `node` is a simple statement containing an ellipsis."""
738     if not isinstance(node, Node) or node.type != syms.simple_stmt:
739         return False
740
741     if len(node.children) != 2:
742         return False
743
744     child = node.children[0]
745     return (
746         child.type == syms.atom
747         and len(child.children) == 3
748         and all(leaf == Leaf(token.DOT, ".") for leaf in child.children)
749     )
750
751
752 def is_atom_with_invisible_parens(node: LN) -> bool:
753     """Given a `LN`, determines whether it's an atom `node` with invisible
754     parens. Useful in dedupe-ing and normalizing parens.
755     """
756     if isinstance(node, Leaf) or node.type != syms.atom:
757         return False
758
759     first, last = node.children[0], node.children[-1]
760     return (
761         isinstance(first, Leaf)
762         and first.type == token.LPAR
763         and first.value == ""
764         and isinstance(last, Leaf)
765         and last.type == token.RPAR
766         and last.value == ""
767     )
768
769
770 def is_empty_par(leaf: Leaf) -> bool:
771     return is_empty_lpar(leaf) or is_empty_rpar(leaf)
772
773
774 def is_empty_lpar(leaf: Leaf) -> bool:
775     return leaf.type == token.LPAR and leaf.value == ""
776
777
778 def is_empty_rpar(leaf: Leaf) -> bool:
779     return leaf.type == token.RPAR and leaf.value == ""
780
781
782 def is_import(leaf: Leaf) -> bool:
783     """Return True if the given leaf starts an import statement."""
784     p = leaf.parent
785     t = leaf.type
786     v = leaf.value
787     return bool(
788         t == token.NAME
789         and (
790             (v == "import" and p and p.type == syms.import_name)
791             or (v == "from" and p and p.type == syms.import_from)
792         )
793     )
794
795
796 def is_type_comment(leaf: Leaf, suffix: str = "") -> bool:
797     """Return True if the given leaf is a special comment.
798     Only returns true for type comments for now."""
799     t = leaf.type
800     v = leaf.value
801     return t in {token.COMMENT, STANDALONE_COMMENT} and v.startswith("# type:" + suffix)
802
803
804 def wrap_in_parentheses(parent: Node, child: LN, *, visible: bool = True) -> None:
805     """Wrap `child` in parentheses.
806
807     This replaces `child` with an atom holding the parentheses and the old
808     child.  That requires moving the prefix.
809
810     If `visible` is False, the leaves will be valueless (and thus invisible).
811     """
812     lpar = Leaf(token.LPAR, "(" if visible else "")
813     rpar = Leaf(token.RPAR, ")" if visible else "")
814     prefix = child.prefix
815     child.prefix = ""
816     index = child.remove() or 0
817     new_child = Node(syms.atom, [lpar, child, rpar])
818     new_child.prefix = prefix
819     parent.insert_child(index, new_child)
820
821
822 def unwrap_singleton_parenthesis(node: LN) -> Optional[LN]:
823     """Returns `wrapped` if `node` is of the shape ( wrapped ).
824
825     Parenthesis can be optional. Returns None otherwise"""
826     if len(node.children) != 3:
827         return None
828
829     lpar, wrapped, rpar = node.children
830     if not (lpar.type == token.LPAR and rpar.type == token.RPAR):
831         return None
832
833     return wrapped
834
835
836 def ensure_visible(leaf: Leaf) -> None:
837     """Make sure parentheses are visible.
838
839     They could be invisible as part of some statements (see
840     :func:`normalize_invisible_parens` and :func:`visit_import_from`).
841     """
842     if leaf.type == token.LPAR:
843         leaf.value = "("
844     elif leaf.type == token.RPAR:
845         leaf.value = ")"
846
847
848 def is_name_token(nl: NL) -> TypeGuard[Leaf]:
849     return nl.type == token.NAME
850
851
852 def is_lpar_token(nl: NL) -> TypeGuard[Leaf]:
853     return nl.type == token.LPAR
854
855
856 def is_rpar_token(nl: NL) -> TypeGuard[Leaf]:
857     return nl.type == token.RPAR
858
859
860 def is_string_token(nl: NL) -> TypeGuard[Leaf]:
861     return nl.type == token.STRING