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

Fix feature detection for positional-only arguments in lambdas (#2532)
[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_extensions import Final
20 else:
21     from typing import Final
22
23 # lib2to3 fork
24 from blib2to3.pytree import Node, Leaf, type_repr
25 from blib2to3 import pygram
26 from blib2to3.pgen2 import token
27
28 from black.cache import CACHE_DIR
29 from black.strings import has_triple_quotes
30
31
32 pygram.initialize(CACHE_DIR)
33 syms = pygram.python_symbols
34
35
36 # types
37 T = TypeVar("T")
38 LN = Union[Leaf, Node]
39 LeafID = int
40 NodeType = int
41
42
43 WHITESPACE: Final = {token.DEDENT, token.INDENT, token.NEWLINE}
44 STATEMENT: Final = {
45     syms.if_stmt,
46     syms.while_stmt,
47     syms.for_stmt,
48     syms.try_stmt,
49     syms.except_clause,
50     syms.with_stmt,
51     syms.funcdef,
52     syms.classdef,
53 }
54 STANDALONE_COMMENT: Final = 153
55 token.tok_name[STANDALONE_COMMENT] = "STANDALONE_COMMENT"
56 LOGIC_OPERATORS: Final = {"and", "or"}
57 COMPARATORS: Final = {
58     token.LESS,
59     token.GREATER,
60     token.EQEQUAL,
61     token.NOTEQUAL,
62     token.LESSEQUAL,
63     token.GREATEREQUAL,
64 }
65 MATH_OPERATORS: Final = {
66     token.VBAR,
67     token.CIRCUMFLEX,
68     token.AMPER,
69     token.LEFTSHIFT,
70     token.RIGHTSHIFT,
71     token.PLUS,
72     token.MINUS,
73     token.STAR,
74     token.SLASH,
75     token.DOUBLESLASH,
76     token.PERCENT,
77     token.AT,
78     token.TILDE,
79     token.DOUBLESTAR,
80 }
81 STARS: Final = {token.STAR, token.DOUBLESTAR}
82 VARARGS_SPECIALS: Final = STARS | {token.SLASH}
83 VARARGS_PARENTS: Final = {
84     syms.arglist,
85     syms.argument,  # double star in arglist
86     syms.trailer,  # single argument to call
87     syms.typedargslist,
88     syms.varargslist,  # lambdas
89 }
90 UNPACKING_PARENTS: Final = {
91     syms.atom,  # single element of a list or set literal
92     syms.dictsetmaker,
93     syms.listmaker,
94     syms.testlist_gexp,
95     syms.testlist_star_expr,
96 }
97 TEST_DESCENDANTS: Final = {
98     syms.test,
99     syms.lambdef,
100     syms.or_test,
101     syms.and_test,
102     syms.not_test,
103     syms.comparison,
104     syms.star_expr,
105     syms.expr,
106     syms.xor_expr,
107     syms.and_expr,
108     syms.shift_expr,
109     syms.arith_expr,
110     syms.trailer,
111     syms.term,
112     syms.power,
113 }
114 ASSIGNMENTS: Final = {
115     "=",
116     "+=",
117     "-=",
118     "*=",
119     "@=",
120     "/=",
121     "%=",
122     "&=",
123     "|=",
124     "^=",
125     "<<=",
126     ">>=",
127     "**=",
128     "//=",
129 }
130
131 IMPLICIT_TUPLE = {syms.testlist, syms.testlist_star_expr, syms.exprlist}
132 BRACKET = {token.LPAR: token.RPAR, token.LSQB: token.RSQB, token.LBRACE: token.RBRACE}
133 OPENING_BRACKETS = set(BRACKET.keys())
134 CLOSING_BRACKETS = set(BRACKET.values())
135 BRACKETS = OPENING_BRACKETS | CLOSING_BRACKETS
136 ALWAYS_NO_SPACE = CLOSING_BRACKETS | {token.COMMA, STANDALONE_COMMENT}
137
138 RARROW = 55
139
140
141 class Visitor(Generic[T]):
142     """Basic lib2to3 visitor that yields things of type `T` on `visit()`."""
143
144     def visit(self, node: LN) -> Iterator[T]:
145         """Main method to visit `node` and its children.
146
147         It tries to find a `visit_*()` method for the given `node.type`, like
148         `visit_simple_stmt` for Node objects or `visit_INDENT` for Leaf objects.
149         If no dedicated `visit_*()` method is found, chooses `visit_default()`
150         instead.
151
152         Then yields objects of type `T` from the selected visitor.
153         """
154         if node.type < 256:
155             name = token.tok_name[node.type]
156         else:
157             name = str(type_repr(node.type))
158         # We explicitly branch on whether a visitor exists (instead of
159         # using self.visit_default as the default arg to getattr) in order
160         # to save needing to create a bound method object and so mypyc can
161         # generate a native call to visit_default.
162         visitf = getattr(self, f"visit_{name}", None)
163         if visitf:
164             yield from visitf(node)
165         else:
166             yield from self.visit_default(node)
167
168     def visit_default(self, node: LN) -> Iterator[T]:
169         """Default `visit_*()` implementation. Recurses to children of `node`."""
170         if isinstance(node, Node):
171             for child in node.children:
172                 yield from self.visit(child)
173
174
175 def whitespace(leaf: Leaf, *, complex_subscript: bool) -> str:  # noqa: C901
176     """Return whitespace prefix if needed for the given `leaf`.
177
178     `complex_subscript` signals whether the given leaf is part of a subscription
179     which has non-trivial arguments, like arithmetic expressions or function calls.
180     """
181     NO = ""
182     SPACE = " "
183     DOUBLESPACE = "  "
184     t = leaf.type
185     p = leaf.parent
186     v = leaf.value
187     if t in ALWAYS_NO_SPACE:
188         return NO
189
190     if t == token.COMMENT:
191         return DOUBLESPACE
192
193     assert p is not None, f"INTERNAL ERROR: hand-made leaf without parent: {leaf!r}"
194     if t == token.COLON and p.type not in {
195         syms.subscript,
196         syms.subscriptlist,
197         syms.sliceop,
198     }:
199         return NO
200
201     prev = leaf.prev_sibling
202     if not prev:
203         prevp = preceding_leaf(p)
204         if not prevp or prevp.type in OPENING_BRACKETS:
205             return NO
206
207         if t == token.COLON:
208             if prevp.type == token.COLON:
209                 return NO
210
211             elif prevp.type != token.COMMA and not complex_subscript:
212                 return NO
213
214             return SPACE
215
216         if prevp.type == token.EQUAL:
217             if prevp.parent:
218                 if prevp.parent.type in {
219                     syms.arglist,
220                     syms.argument,
221                     syms.parameters,
222                     syms.varargslist,
223                 }:
224                     return NO
225
226                 elif prevp.parent.type == syms.typedargslist:
227                     # A bit hacky: if the equal sign has whitespace, it means we
228                     # previously found it's a typed argument.  So, we're using
229                     # that, too.
230                     return prevp.prefix
231
232         elif prevp.type in VARARGS_SPECIALS:
233             if is_vararg(prevp, within=VARARGS_PARENTS | UNPACKING_PARENTS):
234                 return NO
235
236         elif prevp.type == token.COLON:
237             if prevp.parent and prevp.parent.type in {syms.subscript, syms.sliceop}:
238                 return SPACE if complex_subscript else NO
239
240         elif (
241             prevp.parent
242             and prevp.parent.type == syms.factor
243             and prevp.type in MATH_OPERATORS
244         ):
245             return NO
246
247         elif (
248             prevp.type == token.RIGHTSHIFT
249             and prevp.parent
250             and prevp.parent.type == syms.shift_expr
251             and prevp.prev_sibling
252             and prevp.prev_sibling.type == token.NAME
253             and prevp.prev_sibling.value == "print"  # type: ignore
254         ):
255             # Python 2 print chevron
256             return NO
257         elif prevp.type == token.AT and p.parent and p.parent.type == syms.decorator:
258             # no space in decorators
259             return NO
260
261     elif prev.type in OPENING_BRACKETS:
262         return NO
263
264     if p.type in {syms.parameters, syms.arglist}:
265         # untyped function signatures or calls
266         if not prev or prev.type != token.COMMA:
267             return NO
268
269     elif p.type == syms.varargslist:
270         # lambdas
271         if prev and prev.type != token.COMMA:
272             return NO
273
274     elif p.type == syms.typedargslist:
275         # typed function signatures
276         if not prev:
277             return NO
278
279         if t == token.EQUAL:
280             if prev.type != syms.tname:
281                 return NO
282
283         elif prev.type == token.EQUAL:
284             # A bit hacky: if the equal sign has whitespace, it means we
285             # previously found it's a typed argument.  So, we're using that, too.
286             return prev.prefix
287
288         elif prev.type != token.COMMA:
289             return NO
290
291     elif p.type == syms.tname:
292         # type names
293         if not prev:
294             prevp = preceding_leaf(p)
295             if not prevp or prevp.type != token.COMMA:
296                 return NO
297
298     elif p.type == syms.trailer:
299         # attributes and calls
300         if t == token.LPAR or t == token.RPAR:
301             return NO
302
303         if not prev:
304             if t == token.DOT:
305                 prevp = preceding_leaf(p)
306                 if not prevp or prevp.type != token.NUMBER:
307                     return NO
308
309             elif 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 = None
445     last = 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 node.type == token.NAME and node.value == "yield":  # type: ignore
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 = ")"