Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/black/nodes.py: 76%
407 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:15 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:15 +0000
1"""
2blib2to3 Node/Leaf transformation-related utility functions.
3"""
5import sys
6from typing import Generic, Iterator, List, Optional, Set, Tuple, TypeVar, Union
8if sys.version_info >= (3, 8):
9 from typing import Final
10else:
11 from typing_extensions import Final
12if sys.version_info >= (3, 10):
13 from typing import TypeGuard
14else:
15 from typing_extensions import TypeGuard
17from mypy_extensions import mypyc_attr
19from black.cache import CACHE_DIR
20from black.strings import has_triple_quotes
21from blib2to3 import pygram
22from blib2to3.pgen2 import token
23from blib2to3.pytree import NL, Leaf, Node, type_repr
25pygram.initialize(CACHE_DIR)
26syms: Final = pygram.python_symbols
29# types
30T = TypeVar("T")
31LN = Union[Leaf, Node]
32LeafID = int
33NodeType = int
36WHITESPACE: Final = {token.DEDENT, token.INDENT, token.NEWLINE}
37STATEMENT: Final = {
38 syms.if_stmt,
39 syms.while_stmt,
40 syms.for_stmt,
41 syms.try_stmt,
42 syms.except_clause,
43 syms.with_stmt,
44 syms.funcdef,
45 syms.classdef,
46 syms.match_stmt,
47 syms.case_block,
48}
49STANDALONE_COMMENT: Final = 153
50token.tok_name[STANDALONE_COMMENT] = "STANDALONE_COMMENT"
51LOGIC_OPERATORS: Final = {"and", "or"}
52COMPARATORS: Final = {
53 token.LESS,
54 token.GREATER,
55 token.EQEQUAL,
56 token.NOTEQUAL,
57 token.LESSEQUAL,
58 token.GREATEREQUAL,
59}
60MATH_OPERATORS: Final = {
61 token.VBAR,
62 token.CIRCUMFLEX,
63 token.AMPER,
64 token.LEFTSHIFT,
65 token.RIGHTSHIFT,
66 token.PLUS,
67 token.MINUS,
68 token.STAR,
69 token.SLASH,
70 token.DOUBLESLASH,
71 token.PERCENT,
72 token.AT,
73 token.TILDE,
74 token.DOUBLESTAR,
75}
76STARS: Final = {token.STAR, token.DOUBLESTAR}
77VARARGS_SPECIALS: Final = STARS | {token.SLASH}
78VARARGS_PARENTS: Final = {
79 syms.arglist,
80 syms.argument, # double star in arglist
81 syms.trailer, # single argument to call
82 syms.typedargslist,
83 syms.varargslist, # lambdas
84}
85UNPACKING_PARENTS: Final = {
86 syms.atom, # single element of a list or set literal
87 syms.dictsetmaker,
88 syms.listmaker,
89 syms.testlist_gexp,
90 syms.testlist_star_expr,
91 syms.subject_expr,
92 syms.pattern,
93}
94TEST_DESCENDANTS: Final = {
95 syms.test,
96 syms.lambdef,
97 syms.or_test,
98 syms.and_test,
99 syms.not_test,
100 syms.comparison,
101 syms.star_expr,
102 syms.expr,
103 syms.xor_expr,
104 syms.and_expr,
105 syms.shift_expr,
106 syms.arith_expr,
107 syms.trailer,
108 syms.term,
109 syms.power,
110}
111TYPED_NAMES: Final = {syms.tname, syms.tname_star}
112ASSIGNMENTS: Final = {
113 "=",
114 "+=",
115 "-=",
116 "*=",
117 "@=",
118 "/=",
119 "%=",
120 "&=",
121 "|=",
122 "^=",
123 "<<=",
124 ">>=",
125 "**=",
126 "//=",
127}
129IMPLICIT_TUPLE: Final = {syms.testlist, syms.testlist_star_expr, syms.exprlist}
130BRACKET: Final = {
131 token.LPAR: token.RPAR,
132 token.LSQB: token.RSQB,
133 token.LBRACE: token.RBRACE,
134}
135OPENING_BRACKETS: Final = set(BRACKET.keys())
136CLOSING_BRACKETS: Final = set(BRACKET.values())
137BRACKETS: Final = OPENING_BRACKETS | CLOSING_BRACKETS
138ALWAYS_NO_SPACE: Final = CLOSING_BRACKETS | {token.COMMA, STANDALONE_COMMENT}
140RARROW = 55
143@mypyc_attr(allow_interpreted_subclasses=True)
144class Visitor(Generic[T]):
145 """Basic lib2to3 visitor that yields things of type `T` on `visit()`."""
147 def visit(self, node: LN) -> Iterator[T]:
148 """Main method to visit `node` and its children.
150 It tries to find a `visit_*()` method for the given `node.type`, like
151 `visit_simple_stmt` for Node objects or `visit_INDENT` for Leaf objects.
152 If no dedicated `visit_*()` method is found, chooses `visit_default()`
153 instead.
155 Then yields objects of type `T` from the selected visitor.
156 """
157 if node.type < 256:
158 name = token.tok_name[node.type]
159 else:
160 name = str(type_repr(node.type))
161 # We explicitly branch on whether a visitor exists (instead of
162 # using self.visit_default as the default arg to getattr) in order
163 # to save needing to create a bound method object and so mypyc can
164 # generate a native call to visit_default.
165 visitf = getattr(self, f"visit_{name}", None)
166 if visitf:
167 yield from visitf(node)
168 else:
169 yield from self.visit_default(node)
171 def visit_default(self, node: LN) -> Iterator[T]:
172 """Default `visit_*()` implementation. Recurses to children of `node`."""
173 if isinstance(node, Node):
174 for child in node.children:
175 yield from self.visit(child)
178def whitespace(leaf: Leaf, *, complex_subscript: bool) -> str: # noqa: C901
179 """Return whitespace prefix if needed for the given `leaf`.
181 `complex_subscript` signals whether the given leaf is part of a subscription
182 which has non-trivial arguments, like arithmetic expressions or function calls.
183 """
184 NO: Final[str] = ""
185 SPACE: Final[str] = " "
186 DOUBLESPACE: Final[str] = " "
187 t = leaf.type
188 p = leaf.parent
189 v = leaf.value
190 if t in ALWAYS_NO_SPACE:
191 return NO
193 if t == token.COMMENT:
194 return DOUBLESPACE
196 assert p is not None, f"INTERNAL ERROR: hand-made leaf without parent: {leaf!r}"
197 if t == token.COLON and p.type not in {
198 syms.subscript,
199 syms.subscriptlist,
200 syms.sliceop,
201 }:
202 return NO
204 prev = leaf.prev_sibling
205 if not prev:
206 prevp = preceding_leaf(p)
207 if not prevp or prevp.type in OPENING_BRACKETS:
208 return NO
210 if t == token.COLON:
211 if prevp.type == token.COLON:
212 return NO
214 elif prevp.type != token.COMMA and not complex_subscript:
215 return NO
217 return SPACE
219 if prevp.type == token.EQUAL:
220 if prevp.parent:
221 if prevp.parent.type in {
222 syms.arglist,
223 syms.argument,
224 syms.parameters,
225 syms.varargslist,
226 }:
227 return NO
229 elif prevp.parent.type == syms.typedargslist:
230 # A bit hacky: if the equal sign has whitespace, it means we
231 # previously found it's a typed argument. So, we're using
232 # that, too.
233 return prevp.prefix
235 elif (
236 prevp.type == token.STAR
237 and parent_type(prevp) == syms.star_expr
238 and parent_type(prevp.parent) == syms.subscriptlist
239 ):
240 # No space between typevar tuples.
241 return NO
243 elif prevp.type in VARARGS_SPECIALS:
244 if is_vararg(prevp, within=VARARGS_PARENTS | UNPACKING_PARENTS):
245 return NO
247 elif prevp.type == token.COLON:
248 if prevp.parent and prevp.parent.type in {syms.subscript, syms.sliceop}:
249 return SPACE if complex_subscript else NO
251 elif (
252 prevp.parent
253 and prevp.parent.type == syms.factor
254 and prevp.type in MATH_OPERATORS
255 ):
256 return NO
258 elif prevp.type == token.AT and p.parent and p.parent.type == syms.decorator:
259 # no space in decorators
260 return NO
262 elif prev.type in OPENING_BRACKETS:
263 return NO
265 if p.type in {syms.parameters, syms.arglist}:
266 # untyped function signatures or calls
267 if not prev or prev.type != token.COMMA:
268 return NO
270 elif p.type == syms.varargslist:
271 # lambdas
272 if prev and prev.type != token.COMMA:
273 return NO
275 elif p.type == syms.typedargslist:
276 # typed function signatures
277 if not prev:
278 return NO
280 if t == token.EQUAL:
281 if prev.type not in TYPED_NAMES:
282 return NO
284 elif prev.type == token.EQUAL:
285 # A bit hacky: if the equal sign has whitespace, it means we
286 # previously found it's a typed argument. So, we're using that, too.
287 return prev.prefix
289 elif prev.type != token.COMMA:
290 return NO
292 elif p.type in TYPED_NAMES:
293 # type names
294 if not prev:
295 prevp = preceding_leaf(p)
296 if not prevp or prevp.type != token.COMMA:
297 return NO
299 elif p.type == syms.trailer:
300 # attributes and calls
301 if t == token.LPAR or t == token.RPAR:
302 return NO
304 if not prev:
305 if t == token.DOT or t == token.LSQB:
306 return NO
308 elif prev.type != token.COMMA:
309 return NO
311 elif p.type == syms.argument:
312 # single argument
313 if t == token.EQUAL:
314 return NO
316 if not prev:
317 prevp = preceding_leaf(p)
318 if not prevp or prevp.type == token.LPAR:
319 return NO
321 elif prev.type in {token.EQUAL} | VARARGS_SPECIALS:
322 return NO
324 elif p.type == syms.decorator:
325 # decorators
326 return NO
328 elif p.type == syms.dotted_name:
329 if prev:
330 return NO
332 prevp = preceding_leaf(p)
333 if not prevp or prevp.type == token.AT or prevp.type == token.DOT:
334 return NO
336 elif p.type == syms.classdef:
337 if t == token.LPAR:
338 return NO
340 if prev and prev.type == token.LPAR:
341 return NO
343 elif p.type in {syms.subscript, syms.sliceop}:
344 # indexing
345 if not prev:
346 assert p.parent is not None, "subscripts are always parented"
347 if p.parent.type == syms.subscriptlist:
348 return SPACE
350 return NO
352 elif not complex_subscript:
353 return NO
355 elif p.type == syms.atom:
356 if prev and t == token.DOT:
357 # dots, but not the first one.
358 return NO
360 elif p.type == syms.dictsetmaker:
361 # dict unpacking
362 if prev and prev.type == token.DOUBLESTAR:
363 return NO
365 elif p.type in {syms.factor, syms.star_expr}:
366 # unary ops
367 if not prev:
368 prevp = preceding_leaf(p)
369 if not prevp or prevp.type in OPENING_BRACKETS:
370 return NO
372 prevp_parent = prevp.parent
373 assert prevp_parent is not None
374 if prevp.type == token.COLON and prevp_parent.type in {
375 syms.subscript,
376 syms.sliceop,
377 }:
378 return NO
380 elif prevp.type == token.EQUAL and prevp_parent.type == syms.argument:
381 return NO
383 elif t in {token.NAME, token.NUMBER, token.STRING}:
384 return NO
386 elif p.type == syms.import_from:
387 if t == token.DOT:
388 if prev and prev.type == token.DOT:
389 return NO
391 elif t == token.NAME:
392 if v == "import":
393 return SPACE
395 if prev and prev.type == token.DOT:
396 return NO
398 elif p.type == syms.sliceop:
399 return NO
401 elif p.type == syms.except_clause:
402 if t == token.STAR:
403 return NO
405 return SPACE
408def 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
416 try:
417 return list(res.leaves())[-1]
419 except IndexError:
420 return None
422 node = node.parent
423 return None
426def 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])
442def parent_type(node: Optional[LN]) -> Optional[NodeType]:
443 """
444 Returns:
445 @node.parent.type, if @node is not None and has a parent.
446 OR
447 None, otherwise.
448 """
449 if node is None or node.parent is None:
450 return None
452 return node.parent.type
455def child_towards(ancestor: Node, descendant: LN) -> Optional[LN]:
456 """Return the child of `ancestor` that contains `descendant`."""
457 node: Optional[LN] = descendant
458 while node and node.parent != ancestor:
459 node = node.parent
460 return node
463def replace_child(old_child: LN, new_child: LN) -> None:
464 """
465 Side Effects:
466 * If @old_child.parent is set, replace @old_child with @new_child in
467 @old_child's underlying Node structure.
468 OR
469 * Otherwise, this function does nothing.
470 """
471 parent = old_child.parent
472 if not parent:
473 return
475 child_idx = old_child.remove()
476 if child_idx is not None:
477 parent.insert_child(child_idx, new_child)
480def container_of(leaf: Leaf) -> LN:
481 """Return `leaf` or one of its ancestors that is the topmost container of it.
483 By "container" we mean a node where `leaf` is the very first child.
484 """
485 same_prefix = leaf.prefix
486 container: LN = leaf
487 while container:
488 parent = container.parent
489 if parent is None:
490 break
492 if parent.children[0].prefix != same_prefix:
493 break
495 if parent.type == syms.file_input:
496 break
498 if parent.prev_sibling is not None and parent.prev_sibling.type in BRACKETS:
499 break
501 container = parent
502 return container
505def first_leaf_of(node: LN) -> Optional[Leaf]:
506 """Returns the first leaf of the node tree."""
507 if isinstance(node, Leaf):
508 return node
509 if node.children:
510 return first_leaf_of(node.children[0])
511 else:
512 return None
515def is_arith_like(node: LN) -> bool:
516 """Whether node is an arithmetic or a binary arithmetic expression"""
517 return node.type in {
518 syms.arith_expr,
519 syms.shift_expr,
520 syms.xor_expr,
521 syms.and_expr,
522 }
525def is_docstring(leaf: Leaf) -> bool:
526 if prev_siblings_are(
527 leaf.parent, [None, token.NEWLINE, token.INDENT, syms.simple_stmt]
528 ):
529 return True
531 # Multiline docstring on the same line as the `def`.
532 if prev_siblings_are(leaf.parent, [syms.parameters, token.COLON, syms.simple_stmt]):
533 # `syms.parameters` is only used in funcdefs and async_funcdefs in the Python
534 # grammar. We're safe to return True without further checks.
535 return True
537 return False
540def is_empty_tuple(node: LN) -> bool:
541 """Return True if `node` holds an empty tuple."""
542 return (
543 node.type == syms.atom
544 and len(node.children) == 2
545 and node.children[0].type == token.LPAR
546 and node.children[1].type == token.RPAR
547 )
550def is_one_tuple(node: LN) -> bool:
551 """Return True if `node` holds a tuple with one element, with or without parens."""
552 if node.type == syms.atom:
553 gexp = unwrap_singleton_parenthesis(node)
554 if gexp is None or gexp.type != syms.testlist_gexp:
555 return False
557 return len(gexp.children) == 2 and gexp.children[1].type == token.COMMA
559 return (
560 node.type in IMPLICIT_TUPLE
561 and len(node.children) == 2
562 and node.children[1].type == token.COMMA
563 )
566def is_tuple_containing_walrus(node: LN) -> bool:
567 """Return True if `node` holds a tuple that contains a walrus operator."""
568 if node.type != syms.atom:
569 return False
570 gexp = unwrap_singleton_parenthesis(node)
571 if gexp is None or gexp.type != syms.testlist_gexp:
572 return False
574 return any(child.type == syms.namedexpr_test for child in gexp.children)
577def is_one_sequence_between(
578 opening: Leaf,
579 closing: Leaf,
580 leaves: List[Leaf],
581 brackets: Tuple[int, int] = (token.LPAR, token.RPAR),
582) -> bool:
583 """Return True if content between `opening` and `closing` is a one-sequence."""
584 if (opening.type, closing.type) != brackets:
585 return False
587 depth = closing.bracket_depth + 1
588 for _opening_index, leaf in enumerate(leaves):
589 if leaf is opening:
590 break
592 else:
593 raise LookupError("Opening paren not found in `leaves`")
595 commas = 0
596 _opening_index += 1
597 for leaf in leaves[_opening_index:]:
598 if leaf is closing:
599 break
601 bracket_depth = leaf.bracket_depth
602 if bracket_depth == depth and leaf.type == token.COMMA:
603 commas += 1
604 if leaf.parent and leaf.parent.type in {
605 syms.arglist,
606 syms.typedargslist,
607 }:
608 commas += 1
609 break
611 return commas < 2
614def is_walrus_assignment(node: LN) -> bool:
615 """Return True iff `node` is of the shape ( test := test )"""
616 inner = unwrap_singleton_parenthesis(node)
617 return inner is not None and inner.type == syms.namedexpr_test
620def is_simple_decorator_trailer(node: LN, last: bool = False) -> bool:
621 """Return True iff `node` is a trailer valid in a simple decorator"""
622 return node.type == syms.trailer and (
623 (
624 len(node.children) == 2
625 and node.children[0].type == token.DOT
626 and node.children[1].type == token.NAME
627 )
628 # last trailer can be an argument-less parentheses pair
629 or (
630 last
631 and len(node.children) == 2
632 and node.children[0].type == token.LPAR
633 and node.children[1].type == token.RPAR
634 )
635 # last trailer can be arguments
636 or (
637 last
638 and len(node.children) == 3
639 and node.children[0].type == token.LPAR
640 # and node.children[1].type == syms.argument
641 and node.children[2].type == token.RPAR
642 )
643 )
646def is_simple_decorator_expression(node: LN) -> bool:
647 """Return True iff `node` could be a 'dotted name' decorator
649 This function takes the node of the 'namedexpr_test' of the new decorator
650 grammar and test if it would be valid under the old decorator grammar.
652 The old grammar was: decorator: @ dotted_name [arguments] NEWLINE
653 The new grammar is : decorator: @ namedexpr_test NEWLINE
654 """
655 if node.type == token.NAME:
656 return True
657 if node.type == syms.power:
658 if node.children:
659 return (
660 node.children[0].type == token.NAME
661 and all(map(is_simple_decorator_trailer, node.children[1:-1]))
662 and (
663 len(node.children) < 2
664 or is_simple_decorator_trailer(node.children[-1], last=True)
665 )
666 )
667 return False
670def is_yield(node: LN) -> bool:
671 """Return True if `node` holds a `yield` or `yield from` expression."""
672 if node.type == syms.yield_expr:
673 return True
675 if is_name_token(node) and node.value == "yield":
676 return True
678 if node.type != syms.atom:
679 return False
681 if len(node.children) != 3:
682 return False
684 lpar, expr, rpar = node.children
685 if lpar.type == token.LPAR and rpar.type == token.RPAR:
686 return is_yield(expr)
688 return False
691def is_vararg(leaf: Leaf, within: Set[NodeType]) -> bool:
692 """Return True if `leaf` is a star or double star in a vararg or kwarg.
694 If `within` includes VARARGS_PARENTS, this applies to function signatures.
695 If `within` includes UNPACKING_PARENTS, it applies to right hand-side
696 extended iterable unpacking (PEP 3132) and additional unpacking
697 generalizations (PEP 448).
698 """
699 if leaf.type not in VARARGS_SPECIALS or not leaf.parent:
700 return False
702 p = leaf.parent
703 if p.type == syms.star_expr:
704 # Star expressions are also used as assignment targets in extended
705 # iterable unpacking (PEP 3132). See what its parent is instead.
706 if not p.parent:
707 return False
709 p = p.parent
711 return p.type in within
714def is_multiline_string(leaf: Leaf) -> bool:
715 """Return True if `leaf` is a multiline string that actually spans many lines."""
716 return has_triple_quotes(leaf.value) and "\n" in leaf.value
719def is_stub_suite(node: Node) -> bool:
720 """Return True if `node` is a suite with a stub body."""
721 if (
722 len(node.children) != 4
723 or node.children[0].type != token.NEWLINE
724 or node.children[1].type != token.INDENT
725 or node.children[3].type != token.DEDENT
726 ):
727 return False
729 return is_stub_body(node.children[2])
732def is_stub_body(node: LN) -> bool:
733 """Return True if `node` is a simple statement containing an ellipsis."""
734 if not isinstance(node, Node) or node.type != syms.simple_stmt:
735 return False
737 if len(node.children) != 2:
738 return False
740 child = node.children[0]
741 return (
742 child.type == syms.atom
743 and len(child.children) == 3
744 and all(leaf == Leaf(token.DOT, ".") for leaf in child.children)
745 )
748def is_atom_with_invisible_parens(node: LN) -> bool:
749 """Given a `LN`, determines whether it's an atom `node` with invisible
750 parens. Useful in dedupe-ing and normalizing parens.
751 """
752 if isinstance(node, Leaf) or node.type != syms.atom:
753 return False
755 first, last = node.children[0], node.children[-1]
756 return (
757 isinstance(first, Leaf)
758 and first.type == token.LPAR
759 and first.value == ""
760 and isinstance(last, Leaf)
761 and last.type == token.RPAR
762 and last.value == ""
763 )
766def is_empty_par(leaf: Leaf) -> bool:
767 return is_empty_lpar(leaf) or is_empty_rpar(leaf)
770def is_empty_lpar(leaf: Leaf) -> bool:
771 return leaf.type == token.LPAR and leaf.value == ""
774def is_empty_rpar(leaf: Leaf) -> bool:
775 return leaf.type == token.RPAR and leaf.value == ""
778def is_import(leaf: Leaf) -> bool:
779 """Return True if the given leaf starts an import statement."""
780 p = leaf.parent
781 t = leaf.type
782 v = leaf.value
783 return bool(
784 t == token.NAME
785 and (
786 (v == "import" and p and p.type == syms.import_name)
787 or (v == "from" and p and p.type == syms.import_from)
788 )
789 )
792def is_with_or_async_with_stmt(leaf: Leaf) -> bool:
793 """Return True if the given leaf starts a with or async with statement."""
794 return bool(
795 leaf.type == token.NAME
796 and leaf.value == "with"
797 and leaf.parent
798 and leaf.parent.type == syms.with_stmt
799 ) or bool(
800 leaf.type == token.ASYNC
801 and leaf.next_sibling
802 and leaf.next_sibling.type == syms.with_stmt
803 )
806def is_async_stmt_or_funcdef(leaf: Leaf) -> bool:
807 """Return True if the given leaf starts an async def/for/with statement.
809 Note that `async def` can be either an `async_stmt` or `async_funcdef`,
810 the latter is used when it has decorators.
811 """
812 return bool(
813 leaf.type == token.ASYNC
814 and leaf.parent
815 and leaf.parent.type in {syms.async_stmt, syms.async_funcdef}
816 )
819def is_type_comment(leaf: Leaf, suffix: str = "") -> bool:
820 """Return True if the given leaf is a special comment.
821 Only returns true for type comments for now."""
822 t = leaf.type
823 v = leaf.value
824 return t in {token.COMMENT, STANDALONE_COMMENT} and v.startswith("# type:" + suffix)
827def wrap_in_parentheses(parent: Node, child: LN, *, visible: bool = True) -> None:
828 """Wrap `child` in parentheses.
830 This replaces `child` with an atom holding the parentheses and the old
831 child. That requires moving the prefix.
833 If `visible` is False, the leaves will be valueless (and thus invisible).
834 """
835 lpar = Leaf(token.LPAR, "(" if visible else "")
836 rpar = Leaf(token.RPAR, ")" if visible else "")
837 prefix = child.prefix
838 child.prefix = ""
839 index = child.remove() or 0
840 new_child = Node(syms.atom, [lpar, child, rpar])
841 new_child.prefix = prefix
842 parent.insert_child(index, new_child)
845def unwrap_singleton_parenthesis(node: LN) -> Optional[LN]:
846 """Returns `wrapped` if `node` is of the shape ( wrapped ).
848 Parenthesis can be optional. Returns None otherwise"""
849 if len(node.children) != 3:
850 return None
852 lpar, wrapped, rpar = node.children
853 if not (lpar.type == token.LPAR and rpar.type == token.RPAR):
854 return None
856 return wrapped
859def ensure_visible(leaf: Leaf) -> None:
860 """Make sure parentheses are visible.
862 They could be invisible as part of some statements (see
863 :func:`normalize_invisible_parens` and :func:`visit_import_from`).
864 """
865 if leaf.type == token.LPAR:
866 leaf.value = "("
867 elif leaf.type == token.RPAR:
868 leaf.value = ")"
871def is_name_token(nl: NL) -> TypeGuard[Leaf]:
872 return nl.type == token.NAME
875def is_lpar_token(nl: NL) -> TypeGuard[Leaf]:
876 return nl.type == token.LPAR
879def is_rpar_token(nl: NL) -> TypeGuard[Leaf]:
880 return nl.type == token.RPAR
883def is_string_token(nl: NL) -> TypeGuard[Leaf]:
884 return nl.type == token.STRING
887def is_number_token(nl: NL) -> TypeGuard[Leaf]:
888 return nl.type == token.NUMBER
891def is_part_of_annotation(leaf: Leaf) -> bool:
892 """Returns whether this leaf is part of type annotations."""
893 ancestor = leaf.parent
894 while ancestor is not None:
895 if ancestor.prev_sibling and ancestor.prev_sibling.type == token.RARROW:
896 return True
897 if ancestor.parent and ancestor.parent.type == syms.tname:
898 return True
899 ancestor = ancestor.parent
900 return False