]> 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:

7466670be5a9864669790b0bd6948217441c435b
[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 is_arith_like(node: LN) -> bool:
535     """Whether node is an arithmetic or a binary arithmetic expression"""
536     return node.type in {
537         syms.arith_expr,
538         syms.shift_expr,
539         syms.xor_expr,
540         syms.and_expr,
541     }
542
543
544 def is_docstring(leaf: Leaf) -> bool:
545     if prev_siblings_are(
546         leaf.parent, [None, token.NEWLINE, token.INDENT, syms.simple_stmt]
547     ):
548         return True
549
550     # Multiline docstring on the same line as the `def`.
551     if prev_siblings_are(leaf.parent, [syms.parameters, token.COLON, syms.simple_stmt]):
552         # `syms.parameters` is only used in funcdefs and async_funcdefs in the Python
553         # grammar. We're safe to return True without further checks.
554         return True
555
556     return False
557
558
559 def is_empty_tuple(node: LN) -> bool:
560     """Return True if `node` holds an empty tuple."""
561     return (
562         node.type == syms.atom
563         and len(node.children) == 2
564         and node.children[0].type == token.LPAR
565         and node.children[1].type == token.RPAR
566     )
567
568
569 def is_one_tuple(node: LN) -> bool:
570     """Return True if `node` holds a tuple with one element, with or without parens."""
571     if node.type == syms.atom:
572         gexp = unwrap_singleton_parenthesis(node)
573         if gexp is None or gexp.type != syms.testlist_gexp:
574             return False
575
576         return len(gexp.children) == 2 and gexp.children[1].type == token.COMMA
577
578     return (
579         node.type in IMPLICIT_TUPLE
580         and len(node.children) == 2
581         and node.children[1].type == token.COMMA
582     )
583
584
585 def is_one_tuple_between(opening: Leaf, closing: Leaf, leaves: List[Leaf]) -> bool:
586     """Return True if content between `opening` and `closing` looks like a one-tuple."""
587     if opening.type != token.LPAR and closing.type != token.RPAR:
588         return False
589
590     depth = closing.bracket_depth + 1
591     for _opening_index, leaf in enumerate(leaves):
592         if leaf is opening:
593             break
594
595     else:
596         raise LookupError("Opening paren not found in `leaves`")
597
598     commas = 0
599     _opening_index += 1
600     for leaf in leaves[_opening_index:]:
601         if leaf is closing:
602             break
603
604         bracket_depth = leaf.bracket_depth
605         if bracket_depth == depth and leaf.type == token.COMMA:
606             commas += 1
607             if leaf.parent and leaf.parent.type in {
608                 syms.arglist,
609                 syms.typedargslist,
610             }:
611                 commas += 1
612                 break
613
614     return commas < 2
615
616
617 def is_walrus_assignment(node: LN) -> bool:
618     """Return True iff `node` is of the shape ( test := test )"""
619     inner = unwrap_singleton_parenthesis(node)
620     return inner is not None and inner.type == syms.namedexpr_test
621
622
623 def is_simple_decorator_trailer(node: LN, last: bool = False) -> bool:
624     """Return True iff `node` is a trailer valid in a simple decorator"""
625     return node.type == syms.trailer and (
626         (
627             len(node.children) == 2
628             and node.children[0].type == token.DOT
629             and node.children[1].type == token.NAME
630         )
631         # last trailer can be an argument-less parentheses pair
632         or (
633             last
634             and len(node.children) == 2
635             and node.children[0].type == token.LPAR
636             and node.children[1].type == token.RPAR
637         )
638         # last trailer can be arguments
639         or (
640             last
641             and len(node.children) == 3
642             and node.children[0].type == token.LPAR
643             # and node.children[1].type == syms.argument
644             and node.children[2].type == token.RPAR
645         )
646     )
647
648
649 def is_simple_decorator_expression(node: LN) -> bool:
650     """Return True iff `node` could be a 'dotted name' decorator
651
652     This function takes the node of the 'namedexpr_test' of the new decorator
653     grammar and test if it would be valid under the old decorator grammar.
654
655     The old grammar was: decorator: @ dotted_name [arguments] NEWLINE
656     The new grammar is : decorator: @ namedexpr_test NEWLINE
657     """
658     if node.type == token.NAME:
659         return True
660     if node.type == syms.power:
661         if node.children:
662             return (
663                 node.children[0].type == token.NAME
664                 and all(map(is_simple_decorator_trailer, node.children[1:-1]))
665                 and (
666                     len(node.children) < 2
667                     or is_simple_decorator_trailer(node.children[-1], last=True)
668                 )
669             )
670     return False
671
672
673 def is_yield(node: LN) -> bool:
674     """Return True if `node` holds a `yield` or `yield from` expression."""
675     if node.type == syms.yield_expr:
676         return True
677
678     if is_name_token(node) and node.value == "yield":
679         return True
680
681     if node.type != syms.atom:
682         return False
683
684     if len(node.children) != 3:
685         return False
686
687     lpar, expr, rpar = node.children
688     if lpar.type == token.LPAR and rpar.type == token.RPAR:
689         return is_yield(expr)
690
691     return False
692
693
694 def is_vararg(leaf: Leaf, within: Set[NodeType]) -> bool:
695     """Return True if `leaf` is a star or double star in a vararg or kwarg.
696
697     If `within` includes VARARGS_PARENTS, this applies to function signatures.
698     If `within` includes UNPACKING_PARENTS, it applies to right hand-side
699     extended iterable unpacking (PEP 3132) and additional unpacking
700     generalizations (PEP 448).
701     """
702     if leaf.type not in VARARGS_SPECIALS or not leaf.parent:
703         return False
704
705     p = leaf.parent
706     if p.type == syms.star_expr:
707         # Star expressions are also used as assignment targets in extended
708         # iterable unpacking (PEP 3132).  See what its parent is instead.
709         if not p.parent:
710             return False
711
712         p = p.parent
713
714     return p.type in within
715
716
717 def is_multiline_string(leaf: Leaf) -> bool:
718     """Return True if `leaf` is a multiline string that actually spans many lines."""
719     return has_triple_quotes(leaf.value) and "\n" in leaf.value
720
721
722 def is_stub_suite(node: Node) -> bool:
723     """Return True if `node` is a suite with a stub body."""
724     if (
725         len(node.children) != 4
726         or node.children[0].type != token.NEWLINE
727         or node.children[1].type != token.INDENT
728         or node.children[3].type != token.DEDENT
729     ):
730         return False
731
732     return is_stub_body(node.children[2])
733
734
735 def is_stub_body(node: LN) -> bool:
736     """Return True if `node` is a simple statement containing an ellipsis."""
737     if not isinstance(node, Node) or node.type != syms.simple_stmt:
738         return False
739
740     if len(node.children) != 2:
741         return False
742
743     child = node.children[0]
744     return (
745         child.type == syms.atom
746         and len(child.children) == 3
747         and all(leaf == Leaf(token.DOT, ".") for leaf in child.children)
748     )
749
750
751 def is_atom_with_invisible_parens(node: LN) -> bool:
752     """Given a `LN`, determines whether it's an atom `node` with invisible
753     parens. Useful in dedupe-ing and normalizing parens.
754     """
755     if isinstance(node, Leaf) or node.type != syms.atom:
756         return False
757
758     first, last = node.children[0], node.children[-1]
759     return (
760         isinstance(first, Leaf)
761         and first.type == token.LPAR
762         and first.value == ""
763         and isinstance(last, Leaf)
764         and last.type == token.RPAR
765         and last.value == ""
766     )
767
768
769 def is_empty_par(leaf: Leaf) -> bool:
770     return is_empty_lpar(leaf) or is_empty_rpar(leaf)
771
772
773 def is_empty_lpar(leaf: Leaf) -> bool:
774     return leaf.type == token.LPAR and leaf.value == ""
775
776
777 def is_empty_rpar(leaf: Leaf) -> bool:
778     return leaf.type == token.RPAR and leaf.value == ""
779
780
781 def is_import(leaf: Leaf) -> bool:
782     """Return True if the given leaf starts an import statement."""
783     p = leaf.parent
784     t = leaf.type
785     v = leaf.value
786     return bool(
787         t == token.NAME
788         and (
789             (v == "import" and p and p.type == syms.import_name)
790             or (v == "from" and p and p.type == syms.import_from)
791         )
792     )
793
794
795 def is_type_comment(leaf: Leaf, suffix: str = "") -> bool:
796     """Return True if the given leaf is a special comment.
797     Only returns true for type comments for now."""
798     t = leaf.type
799     v = leaf.value
800     return t in {token.COMMENT, STANDALONE_COMMENT} and v.startswith("# type:" + suffix)
801
802
803 def wrap_in_parentheses(parent: Node, child: LN, *, visible: bool = True) -> None:
804     """Wrap `child` in parentheses.
805
806     This replaces `child` with an atom holding the parentheses and the old
807     child.  That requires moving the prefix.
808
809     If `visible` is False, the leaves will be valueless (and thus invisible).
810     """
811     lpar = Leaf(token.LPAR, "(" if visible else "")
812     rpar = Leaf(token.RPAR, ")" if visible else "")
813     prefix = child.prefix
814     child.prefix = ""
815     index = child.remove() or 0
816     new_child = Node(syms.atom, [lpar, child, rpar])
817     new_child.prefix = prefix
818     parent.insert_child(index, new_child)
819
820
821 def unwrap_singleton_parenthesis(node: LN) -> Optional[LN]:
822     """Returns `wrapped` if `node` is of the shape ( wrapped ).
823
824     Parenthesis can be optional. Returns None otherwise"""
825     if len(node.children) != 3:
826         return None
827
828     lpar, wrapped, rpar = node.children
829     if not (lpar.type == token.LPAR and rpar.type == token.RPAR):
830         return None
831
832     return wrapped
833
834
835 def ensure_visible(leaf: Leaf) -> None:
836     """Make sure parentheses are visible.
837
838     They could be invisible as part of some statements (see
839     :func:`normalize_invisible_parens` and :func:`visit_import_from`).
840     """
841     if leaf.type == token.LPAR:
842         leaf.value = "("
843     elif leaf.type == token.RPAR:
844         leaf.value = ")"
845
846
847 def is_name_token(nl: NL) -> TypeGuard[Leaf]:
848     return nl.type == token.NAME
849
850
851 def is_lpar_token(nl: NL) -> TypeGuard[Leaf]:
852     return nl.type == token.LPAR
853
854
855 def is_rpar_token(nl: NL) -> TypeGuard[Leaf]:
856     return nl.type == token.RPAR
857
858
859 def is_string_token(nl: NL) -> TypeGuard[Leaf]:
860     return nl.type == token.STRING