Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/astroid/rebuilder.py: 63%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
2# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
3# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt
5"""This module contains utilities for rebuilding an _ast tree in
6order to get a single Astroid representation.
7"""
9from __future__ import annotations
11import ast
12import sys
13import token
14from collections.abc import Callable, Generator
15from io import StringIO
16from tokenize import TokenInfo, generate_tokens
17from typing import TYPE_CHECKING, Final, TypeVar, Union, cast, overload
19from astroid import nodes
20from astroid._ast import ParserModule, get_parser_module, parse_function_type_comment
21from astroid.const import IS_PYPY, PY38, PY39_PLUS, PY312_PLUS, Context
22from astroid.manager import AstroidManager
23from astroid.nodes import NodeNG
24from astroid.nodes.node_classes import AssignName
25from astroid.nodes.utils import Position
26from astroid.typing import InferenceResult
28REDIRECT: Final[dict[str, str]] = {
29 "arguments": "Arguments",
30 "comprehension": "Comprehension",
31 "ListCompFor": "Comprehension",
32 "GenExprFor": "Comprehension",
33 "excepthandler": "ExceptHandler",
34 "keyword": "Keyword",
35 "match_case": "MatchCase",
36}
39T_Doc = TypeVar(
40 "T_Doc",
41 "ast.Module",
42 "ast.ClassDef",
43 Union["ast.FunctionDef", "ast.AsyncFunctionDef"],
44)
45_FunctionT = TypeVar("_FunctionT", nodes.FunctionDef, nodes.AsyncFunctionDef)
46_ForT = TypeVar("_ForT", nodes.For, nodes.AsyncFor)
47_WithT = TypeVar("_WithT", nodes.With, nodes.AsyncWith)
48NodesWithDocsType = Union[nodes.Module, nodes.ClassDef, nodes.FunctionDef]
51# noinspection PyMethodMayBeStatic
52class TreeRebuilder:
53 """Rebuilds the _ast tree to become an Astroid tree."""
55 def __init__(
56 self,
57 manager: AstroidManager,
58 parser_module: ParserModule | None = None,
59 data: str | None = None,
60 ) -> None:
61 self._manager = manager
62 self._data = data.split("\n") if data else None
63 self._global_names: list[dict[str, list[nodes.Global]]] = []
64 self._import_from_nodes: list[nodes.ImportFrom] = []
65 self._delayed_assattr: list[nodes.AssignAttr] = []
66 self._visit_meths: dict[type[ast.AST], Callable[[ast.AST, NodeNG], NodeNG]] = {}
68 if parser_module is None:
69 self._parser_module = get_parser_module()
70 else:
71 self._parser_module = parser_module
73 def _get_doc(self, node: T_Doc) -> tuple[T_Doc, ast.Constant | ast.Str | None]:
74 """Return the doc ast node."""
75 try:
76 if node.body and isinstance(node.body[0], ast.Expr):
77 first_value = node.body[0].value
78 if isinstance(first_value, ast.Constant) and isinstance(
79 first_value.value, str
80 ):
81 doc_ast_node = first_value
82 node.body = node.body[1:]
83 # The ast parser of python < 3.8 sets col_offset of multi-line strings to -1
84 # as it is unable to determine the value correctly. We reset this to None.
85 if doc_ast_node.col_offset == -1:
86 doc_ast_node.col_offset = None
87 return node, doc_ast_node
88 except IndexError:
89 pass # ast built from scratch
90 return node, None
92 def _get_context(
93 self,
94 node: (
95 ast.Attribute
96 | ast.List
97 | ast.Name
98 | ast.Subscript
99 | ast.Starred
100 | ast.Tuple
101 ),
102 ) -> Context:
103 return self._parser_module.context_classes.get(type(node.ctx), Context.Load)
105 def _get_position_info(
106 self,
107 node: ast.ClassDef | ast.FunctionDef | ast.AsyncFunctionDef,
108 parent: nodes.ClassDef | nodes.FunctionDef | nodes.AsyncFunctionDef,
109 ) -> Position | None:
110 """Return position information for ClassDef and FunctionDef nodes.
112 In contrast to AST positions, these only include the actual keyword(s)
113 and the class / function name.
115 >>> @decorator
116 >>> async def some_func(var: int) -> None:
117 >>> ^^^^^^^^^^^^^^^^^^^
118 """
119 if not self._data:
120 return None
121 end_lineno = node.end_lineno
122 if node.body:
123 end_lineno = node.body[0].lineno
124 # pylint: disable-next=unsubscriptable-object
125 data = "\n".join(self._data[node.lineno - 1 : end_lineno])
127 start_token: TokenInfo | None = None
128 keyword_tokens: tuple[int, ...] = (token.NAME,)
129 if isinstance(parent, nodes.AsyncFunctionDef):
130 search_token = "async"
131 elif isinstance(parent, nodes.FunctionDef):
132 search_token = "def"
133 else:
134 search_token = "class"
136 for t in generate_tokens(StringIO(data).readline):
137 if (
138 start_token is not None
139 and t.type == token.NAME
140 and t.string == node.name
141 ):
142 break
143 if t.type in keyword_tokens:
144 if t.string == search_token:
145 start_token = t
146 continue
147 if t.string in {"def"}:
148 continue
149 start_token = None
150 else:
151 return None
153 return Position(
154 lineno=node.lineno + start_token.start[0] - 1,
155 col_offset=start_token.start[1],
156 end_lineno=node.lineno + t.end[0] - 1,
157 end_col_offset=t.end[1],
158 )
160 def _reset_end_lineno(self, newnode: nodes.NodeNG) -> None:
161 """Reset end_lineno and end_col_offset attributes for PyPy 3.8.
163 For some nodes, these are either set to -1 or only partially assigned.
164 To keep consistency across astroid and pylint, reset all.
166 This has been fixed in PyPy 3.9.
167 For reference, an (incomplete) list of nodes with issues:
168 - ClassDef - For
169 - FunctionDef - While
170 - Call - If
171 - Decorators - Try
172 - With - Assign
173 """
174 newnode.end_lineno = None
175 newnode.end_col_offset = None
176 for child_node in newnode.get_children():
177 self._reset_end_lineno(child_node)
179 def visit_module(
180 self, node: ast.Module, modname: str, modpath: str, package: bool
181 ) -> nodes.Module:
182 """Visit a Module node by returning a fresh instance of it.
184 Note: Method not called by 'visit'
185 """
186 node, doc_ast_node = self._get_doc(node)
187 newnode = nodes.Module(
188 name=modname,
189 file=modpath,
190 path=[modpath],
191 package=package,
192 )
193 newnode.postinit(
194 [self.visit(child, newnode) for child in node.body],
195 doc_node=self.visit(doc_ast_node, newnode),
196 )
197 if IS_PYPY and PY38:
198 self._reset_end_lineno(newnode)
199 return newnode
201 if TYPE_CHECKING: # noqa: C901
203 @overload
204 def visit(self, node: ast.arg, parent: NodeNG) -> nodes.AssignName: ...
206 @overload
207 def visit(self, node: ast.arguments, parent: NodeNG) -> nodes.Arguments: ...
209 @overload
210 def visit(self, node: ast.Assert, parent: NodeNG) -> nodes.Assert: ...
212 @overload
213 def visit(
214 self, node: ast.AsyncFunctionDef, parent: NodeNG
215 ) -> nodes.AsyncFunctionDef: ...
217 @overload
218 def visit(self, node: ast.AsyncFor, parent: NodeNG) -> nodes.AsyncFor: ...
220 @overload
221 def visit(self, node: ast.Await, parent: NodeNG) -> nodes.Await: ...
223 @overload
224 def visit(self, node: ast.AsyncWith, parent: NodeNG) -> nodes.AsyncWith: ...
226 @overload
227 def visit(self, node: ast.Assign, parent: NodeNG) -> nodes.Assign: ...
229 @overload
230 def visit(self, node: ast.AnnAssign, parent: NodeNG) -> nodes.AnnAssign: ...
232 @overload
233 def visit(self, node: ast.AugAssign, parent: NodeNG) -> nodes.AugAssign: ...
235 @overload
236 def visit(self, node: ast.BinOp, parent: NodeNG) -> nodes.BinOp: ...
238 @overload
239 def visit(self, node: ast.BoolOp, parent: NodeNG) -> nodes.BoolOp: ...
241 @overload
242 def visit(self, node: ast.Break, parent: NodeNG) -> nodes.Break: ...
244 @overload
245 def visit(self, node: ast.Call, parent: NodeNG) -> nodes.Call: ...
247 @overload
248 def visit(self, node: ast.ClassDef, parent: NodeNG) -> nodes.ClassDef: ...
250 @overload
251 def visit(self, node: ast.Continue, parent: NodeNG) -> nodes.Continue: ...
253 @overload
254 def visit(self, node: ast.Compare, parent: NodeNG) -> nodes.Compare: ...
256 @overload
257 def visit(
258 self, node: ast.comprehension, parent: NodeNG
259 ) -> nodes.Comprehension: ...
261 @overload
262 def visit(self, node: ast.Delete, parent: NodeNG) -> nodes.Delete: ...
264 @overload
265 def visit(self, node: ast.Dict, parent: NodeNG) -> nodes.Dict: ...
267 @overload
268 def visit(self, node: ast.DictComp, parent: NodeNG) -> nodes.DictComp: ...
270 @overload
271 def visit(self, node: ast.Expr, parent: NodeNG) -> nodes.Expr: ...
273 @overload
274 def visit(
275 self, node: ast.ExceptHandler, parent: NodeNG
276 ) -> nodes.ExceptHandler: ...
278 @overload
279 def visit(self, node: ast.For, parent: NodeNG) -> nodes.For: ...
281 @overload
282 def visit(self, node: ast.ImportFrom, parent: NodeNG) -> nodes.ImportFrom: ...
284 @overload
285 def visit(self, node: ast.FunctionDef, parent: NodeNG) -> nodes.FunctionDef: ...
287 @overload
288 def visit(
289 self, node: ast.GeneratorExp, parent: NodeNG
290 ) -> nodes.GeneratorExp: ...
292 @overload
293 def visit(self, node: ast.Attribute, parent: NodeNG) -> nodes.Attribute: ...
295 @overload
296 def visit(self, node: ast.Global, parent: NodeNG) -> nodes.Global: ...
298 @overload
299 def visit(self, node: ast.If, parent: NodeNG) -> nodes.If: ...
301 @overload
302 def visit(self, node: ast.IfExp, parent: NodeNG) -> nodes.IfExp: ...
304 @overload
305 def visit(self, node: ast.Import, parent: NodeNG) -> nodes.Import: ...
307 @overload
308 def visit(self, node: ast.JoinedStr, parent: NodeNG) -> nodes.JoinedStr: ...
310 @overload
311 def visit(
312 self, node: ast.FormattedValue, parent: NodeNG
313 ) -> nodes.FormattedValue: ...
315 @overload
316 def visit(self, node: ast.NamedExpr, parent: NodeNG) -> nodes.NamedExpr: ...
318 if sys.version_info < (3, 9):
319 # Not used in Python 3.9+
320 @overload
321 def visit(
322 self, node: ast.ExtSlice, parent: nodes.Subscript
323 ) -> nodes.Tuple: ...
325 @overload
326 def visit(self, node: ast.Index, parent: nodes.Subscript) -> NodeNG: ...
328 @overload
329 def visit(self, node: ast.keyword, parent: NodeNG) -> nodes.Keyword: ...
331 @overload
332 def visit(self, node: ast.Lambda, parent: NodeNG) -> nodes.Lambda: ...
334 @overload
335 def visit(self, node: ast.List, parent: NodeNG) -> nodes.List: ...
337 @overload
338 def visit(self, node: ast.ListComp, parent: NodeNG) -> nodes.ListComp: ...
340 @overload
341 def visit(
342 self, node: ast.Name, parent: NodeNG
343 ) -> nodes.Name | nodes.Const | nodes.AssignName | nodes.DelName: ...
345 @overload
346 def visit(self, node: ast.Nonlocal, parent: NodeNG) -> nodes.Nonlocal: ...
348 @overload
349 def visit(self, node: ast.Constant, parent: NodeNG) -> nodes.Const: ...
351 if sys.version_info >= (3, 12):
353 @overload
354 def visit(self, node: ast.ParamSpec, parent: NodeNG) -> nodes.ParamSpec: ...
356 @overload
357 def visit(self, node: ast.Pass, parent: NodeNG) -> nodes.Pass: ...
359 @overload
360 def visit(self, node: ast.Raise, parent: NodeNG) -> nodes.Raise: ...
362 @overload
363 def visit(self, node: ast.Return, parent: NodeNG) -> nodes.Return: ...
365 @overload
366 def visit(self, node: ast.Set, parent: NodeNG) -> nodes.Set: ...
368 @overload
369 def visit(self, node: ast.SetComp, parent: NodeNG) -> nodes.SetComp: ...
371 @overload
372 def visit(self, node: ast.Slice, parent: nodes.Subscript) -> nodes.Slice: ...
374 @overload
375 def visit(self, node: ast.Subscript, parent: NodeNG) -> nodes.Subscript: ...
377 @overload
378 def visit(self, node: ast.Starred, parent: NodeNG) -> nodes.Starred: ...
380 @overload
381 def visit(self, node: ast.Try, parent: NodeNG) -> nodes.Try: ...
383 if sys.version_info >= (3, 11):
385 @overload
386 def visit(self, node: ast.TryStar, parent: NodeNG) -> nodes.TryStar: ...
388 @overload
389 def visit(self, node: ast.Tuple, parent: NodeNG) -> nodes.Tuple: ...
391 if sys.version_info >= (3, 12):
393 @overload
394 def visit(self, node: ast.TypeAlias, parent: NodeNG) -> nodes.TypeAlias: ...
396 @overload
397 def visit(self, node: ast.TypeVar, parent: NodeNG) -> nodes.TypeVar: ...
399 @overload
400 def visit(
401 self, node: ast.TypeVarTuple, parent: NodeNG
402 ) -> nodes.TypeVarTuple: ...
404 @overload
405 def visit(self, node: ast.UnaryOp, parent: NodeNG) -> nodes.UnaryOp: ...
407 @overload
408 def visit(self, node: ast.While, parent: NodeNG) -> nodes.While: ...
410 @overload
411 def visit(self, node: ast.With, parent: NodeNG) -> nodes.With: ...
413 @overload
414 def visit(self, node: ast.Yield, parent: NodeNG) -> nodes.Yield: ...
416 @overload
417 def visit(self, node: ast.YieldFrom, parent: NodeNG) -> nodes.YieldFrom: ...
419 if sys.version_info >= (3, 10):
421 @overload
422 def visit(self, node: ast.Match, parent: NodeNG) -> nodes.Match: ...
424 @overload
425 def visit(
426 self, node: ast.match_case, parent: NodeNG
427 ) -> nodes.MatchCase: ...
429 @overload
430 def visit(
431 self, node: ast.MatchValue, parent: NodeNG
432 ) -> nodes.MatchValue: ...
434 @overload
435 def visit(
436 self, node: ast.MatchSingleton, parent: NodeNG
437 ) -> nodes.MatchSingleton: ...
439 @overload
440 def visit(
441 self, node: ast.MatchSequence, parent: NodeNG
442 ) -> nodes.MatchSequence: ...
444 @overload
445 def visit(
446 self, node: ast.MatchMapping, parent: NodeNG
447 ) -> nodes.MatchMapping: ...
449 @overload
450 def visit(
451 self, node: ast.MatchClass, parent: NodeNG
452 ) -> nodes.MatchClass: ...
454 @overload
455 def visit(self, node: ast.MatchStar, parent: NodeNG) -> nodes.MatchStar: ...
457 @overload
458 def visit(self, node: ast.MatchAs, parent: NodeNG) -> nodes.MatchAs: ...
460 @overload
461 def visit(self, node: ast.MatchOr, parent: NodeNG) -> nodes.MatchOr: ...
463 @overload
464 def visit(self, node: ast.pattern, parent: NodeNG) -> nodes.Pattern: ...
466 @overload
467 def visit(self, node: ast.AST, parent: NodeNG) -> NodeNG: ...
469 @overload
470 def visit(self, node: None, parent: NodeNG) -> None: ...
472 def visit(self, node: ast.AST | None, parent: NodeNG) -> NodeNG | None:
473 if node is None:
474 return None
475 cls = node.__class__
476 if cls in self._visit_meths:
477 visit_method = self._visit_meths[cls]
478 else:
479 cls_name = cls.__name__
480 visit_name = "visit_" + REDIRECT.get(cls_name, cls_name).lower()
481 visit_method = getattr(self, visit_name)
482 self._visit_meths[cls] = visit_method
483 return visit_method(node, parent)
485 def _save_assignment(self, node: nodes.AssignName | nodes.DelName) -> None:
486 """Save assignment situation since node.parent is not available yet."""
487 if self._global_names and node.name in self._global_names[-1]:
488 node.root().set_local(node.name, node)
489 else:
490 assert node.parent
491 assert node.name
492 node.parent.set_local(node.name, node)
494 def visit_arg(self, node: ast.arg, parent: NodeNG) -> nodes.AssignName:
495 """Visit an arg node by returning a fresh AssName instance."""
496 return self.visit_assignname(node, parent, node.arg)
498 def visit_arguments(self, node: ast.arguments, parent: NodeNG) -> nodes.Arguments:
499 """Visit an Arguments node by returning a fresh instance of it."""
500 vararg: str | None = None
501 kwarg: str | None = None
502 vararg_node = node.vararg
503 kwarg_node = node.kwarg
505 newnode = nodes.Arguments(
506 node.vararg.arg if node.vararg else None,
507 node.kwarg.arg if node.kwarg else None,
508 parent,
509 (
510 AssignName(
511 vararg_node.arg,
512 vararg_node.lineno,
513 vararg_node.col_offset,
514 parent,
515 end_lineno=vararg_node.end_lineno,
516 end_col_offset=vararg_node.end_col_offset,
517 )
518 if vararg_node
519 else None
520 ),
521 (
522 AssignName(
523 kwarg_node.arg,
524 kwarg_node.lineno,
525 kwarg_node.col_offset,
526 parent,
527 end_lineno=kwarg_node.end_lineno,
528 end_col_offset=kwarg_node.end_col_offset,
529 )
530 if kwarg_node
531 else None
532 ),
533 )
534 args = [self.visit(child, newnode) for child in node.args]
535 defaults = [self.visit(child, newnode) for child in node.defaults]
536 varargannotation: NodeNG | None = None
537 kwargannotation: NodeNG | None = None
538 if node.vararg:
539 vararg = node.vararg.arg
540 varargannotation = self.visit(node.vararg.annotation, newnode)
541 if node.kwarg:
542 kwarg = node.kwarg.arg
543 kwargannotation = self.visit(node.kwarg.annotation, newnode)
545 if PY38:
546 # In Python 3.8 'end_lineno' and 'end_col_offset'
547 # for 'kwonlyargs' don't include the annotation.
548 for arg in node.kwonlyargs:
549 if arg.annotation is not None:
550 arg.end_lineno = arg.annotation.end_lineno
551 arg.end_col_offset = arg.annotation.end_col_offset
553 kwonlyargs = [self.visit(child, newnode) for child in node.kwonlyargs]
554 kw_defaults = [self.visit(child, newnode) for child in node.kw_defaults]
555 annotations = [self.visit(arg.annotation, newnode) for arg in node.args]
556 kwonlyargs_annotations = [
557 self.visit(arg.annotation, newnode) for arg in node.kwonlyargs
558 ]
560 posonlyargs = [self.visit(child, newnode) for child in node.posonlyargs]
561 posonlyargs_annotations = [
562 self.visit(arg.annotation, newnode) for arg in node.posonlyargs
563 ]
564 type_comment_args = [
565 self.check_type_comment(child, parent=newnode) for child in node.args
566 ]
567 type_comment_kwonlyargs = [
568 self.check_type_comment(child, parent=newnode) for child in node.kwonlyargs
569 ]
570 type_comment_posonlyargs = [
571 self.check_type_comment(child, parent=newnode) for child in node.posonlyargs
572 ]
574 newnode.postinit(
575 args=args,
576 defaults=defaults,
577 kwonlyargs=kwonlyargs,
578 posonlyargs=posonlyargs,
579 kw_defaults=kw_defaults,
580 annotations=annotations,
581 kwonlyargs_annotations=kwonlyargs_annotations,
582 posonlyargs_annotations=posonlyargs_annotations,
583 varargannotation=varargannotation,
584 kwargannotation=kwargannotation,
585 type_comment_args=type_comment_args,
586 type_comment_kwonlyargs=type_comment_kwonlyargs,
587 type_comment_posonlyargs=type_comment_posonlyargs,
588 )
589 # save argument names in locals:
590 assert newnode.parent
591 if vararg:
592 newnode.parent.set_local(vararg, newnode)
593 if kwarg:
594 newnode.parent.set_local(kwarg, newnode)
595 return newnode
597 def visit_assert(self, node: ast.Assert, parent: NodeNG) -> nodes.Assert:
598 """Visit a Assert node by returning a fresh instance of it."""
599 newnode = nodes.Assert(
600 lineno=node.lineno,
601 col_offset=node.col_offset,
602 end_lineno=node.end_lineno,
603 end_col_offset=node.end_col_offset,
604 parent=parent,
605 )
606 msg: NodeNG | None = None
607 if node.msg:
608 msg = self.visit(node.msg, newnode)
609 newnode.postinit(self.visit(node.test, newnode), msg)
610 return newnode
612 def check_type_comment(
613 self,
614 node: ast.Assign | ast.arg | ast.For | ast.AsyncFor | ast.With | ast.AsyncWith,
615 parent: (
616 nodes.Assign
617 | nodes.Arguments
618 | nodes.For
619 | nodes.AsyncFor
620 | nodes.With
621 | nodes.AsyncWith
622 ),
623 ) -> NodeNG | None:
624 if not node.type_comment:
625 return None
627 try:
628 type_comment_ast = self._parser_module.parse(node.type_comment)
629 except SyntaxError:
630 # Invalid type comment, just skip it.
631 return None
633 # For '# type: # any comment' ast.parse returns a Module node,
634 # without any nodes in the body.
635 if not type_comment_ast.body:
636 return None
638 type_object = self.visit(type_comment_ast.body[0], parent=parent)
639 if not isinstance(type_object, nodes.Expr):
640 return None
642 return type_object.value
644 def check_function_type_comment(
645 self, node: ast.FunctionDef | ast.AsyncFunctionDef, parent: NodeNG
646 ) -> tuple[NodeNG | None, list[NodeNG]] | None:
647 if not node.type_comment:
648 return None
650 try:
651 type_comment_ast = parse_function_type_comment(node.type_comment)
652 except SyntaxError:
653 # Invalid type comment, just skip it.
654 return None
656 if not type_comment_ast:
657 return None
659 returns: NodeNG | None = None
660 argtypes: list[NodeNG] = [
661 self.visit(elem, parent) for elem in (type_comment_ast.argtypes or [])
662 ]
663 if type_comment_ast.returns:
664 returns = self.visit(type_comment_ast.returns, parent)
666 return returns, argtypes
668 def visit_asyncfunctiondef(
669 self, node: ast.AsyncFunctionDef, parent: NodeNG
670 ) -> nodes.AsyncFunctionDef:
671 return self._visit_functiondef(nodes.AsyncFunctionDef, node, parent)
673 def visit_asyncfor(self, node: ast.AsyncFor, parent: NodeNG) -> nodes.AsyncFor:
674 return self._visit_for(nodes.AsyncFor, node, parent)
676 def visit_await(self, node: ast.Await, parent: NodeNG) -> nodes.Await:
677 newnode = nodes.Await(
678 lineno=node.lineno,
679 col_offset=node.col_offset,
680 end_lineno=node.end_lineno,
681 end_col_offset=node.end_col_offset,
682 parent=parent,
683 )
684 newnode.postinit(value=self.visit(node.value, newnode))
685 return newnode
687 def visit_asyncwith(self, node: ast.AsyncWith, parent: NodeNG) -> nodes.AsyncWith:
688 return self._visit_with(nodes.AsyncWith, node, parent)
690 def visit_assign(self, node: ast.Assign, parent: NodeNG) -> nodes.Assign:
691 """Visit a Assign node by returning a fresh instance of it."""
692 newnode = nodes.Assign(
693 lineno=node.lineno,
694 col_offset=node.col_offset,
695 end_lineno=node.end_lineno,
696 end_col_offset=node.end_col_offset,
697 parent=parent,
698 )
699 type_annotation = self.check_type_comment(node, parent=newnode)
700 newnode.postinit(
701 targets=[self.visit(child, newnode) for child in node.targets],
702 value=self.visit(node.value, newnode),
703 type_annotation=type_annotation,
704 )
705 return newnode
707 def visit_annassign(self, node: ast.AnnAssign, parent: NodeNG) -> nodes.AnnAssign:
708 """Visit an AnnAssign node by returning a fresh instance of it."""
709 newnode = nodes.AnnAssign(
710 lineno=node.lineno,
711 col_offset=node.col_offset,
712 end_lineno=node.end_lineno,
713 end_col_offset=node.end_col_offset,
714 parent=parent,
715 )
716 newnode.postinit(
717 target=self.visit(node.target, newnode),
718 annotation=self.visit(node.annotation, newnode),
719 simple=node.simple,
720 value=self.visit(node.value, newnode),
721 )
722 return newnode
724 @overload
725 def visit_assignname(
726 self, node: ast.AST, parent: NodeNG, node_name: str
727 ) -> nodes.AssignName: ...
729 @overload
730 def visit_assignname(
731 self, node: ast.AST, parent: NodeNG, node_name: None
732 ) -> None: ...
734 def visit_assignname(
735 self, node: ast.AST, parent: NodeNG, node_name: str | None
736 ) -> nodes.AssignName | None:
737 """Visit a node and return a AssignName node.
739 Note: Method not called by 'visit'
740 """
741 if node_name is None:
742 return None
743 newnode = nodes.AssignName(
744 name=node_name,
745 lineno=node.lineno,
746 col_offset=node.col_offset,
747 end_lineno=node.end_lineno,
748 end_col_offset=node.end_col_offset,
749 parent=parent,
750 )
751 self._save_assignment(newnode)
752 return newnode
754 def visit_augassign(self, node: ast.AugAssign, parent: NodeNG) -> nodes.AugAssign:
755 """Visit a AugAssign node by returning a fresh instance of it."""
756 newnode = nodes.AugAssign(
757 op=self._parser_module.bin_op_classes[type(node.op)] + "=",
758 lineno=node.lineno,
759 col_offset=node.col_offset,
760 end_lineno=node.end_lineno,
761 end_col_offset=node.end_col_offset,
762 parent=parent,
763 )
764 newnode.postinit(
765 self.visit(node.target, newnode), self.visit(node.value, newnode)
766 )
767 return newnode
769 def visit_binop(self, node: ast.BinOp, parent: NodeNG) -> nodes.BinOp:
770 """Visit a BinOp node by returning a fresh instance of it."""
771 newnode = nodes.BinOp(
772 op=self._parser_module.bin_op_classes[type(node.op)],
773 lineno=node.lineno,
774 col_offset=node.col_offset,
775 end_lineno=node.end_lineno,
776 end_col_offset=node.end_col_offset,
777 parent=parent,
778 )
779 newnode.postinit(
780 self.visit(node.left, newnode), self.visit(node.right, newnode)
781 )
782 return newnode
784 def visit_boolop(self, node: ast.BoolOp, parent: NodeNG) -> nodes.BoolOp:
785 """Visit a BoolOp node by returning a fresh instance of it."""
786 newnode = nodes.BoolOp(
787 op=self._parser_module.bool_op_classes[type(node.op)],
788 lineno=node.lineno,
789 col_offset=node.col_offset,
790 end_lineno=node.end_lineno,
791 end_col_offset=node.end_col_offset,
792 parent=parent,
793 )
794 newnode.postinit([self.visit(child, newnode) for child in node.values])
795 return newnode
797 def visit_break(self, node: ast.Break, parent: NodeNG) -> nodes.Break:
798 """Visit a Break node by returning a fresh instance of it."""
799 return nodes.Break(
800 lineno=node.lineno,
801 col_offset=node.col_offset,
802 end_lineno=node.end_lineno,
803 end_col_offset=node.end_col_offset,
804 parent=parent,
805 )
807 def visit_call(self, node: ast.Call, parent: NodeNG) -> nodes.Call:
808 """Visit a CallFunc node by returning a fresh instance of it."""
809 newnode = nodes.Call(
810 lineno=node.lineno,
811 col_offset=node.col_offset,
812 end_lineno=node.end_lineno,
813 end_col_offset=node.end_col_offset,
814 parent=parent,
815 )
816 newnode.postinit(
817 func=self.visit(node.func, newnode),
818 args=[self.visit(child, newnode) for child in node.args],
819 keywords=[self.visit(child, newnode) for child in node.keywords],
820 )
821 return newnode
823 def visit_classdef(
824 self, node: ast.ClassDef, parent: NodeNG, newstyle: bool = True
825 ) -> nodes.ClassDef:
826 """Visit a ClassDef node to become astroid."""
827 node, doc_ast_node = self._get_doc(node)
828 newnode = nodes.ClassDef(
829 name=node.name,
830 lineno=node.lineno,
831 col_offset=node.col_offset,
832 end_lineno=node.end_lineno,
833 end_col_offset=node.end_col_offset,
834 parent=parent,
835 )
836 metaclass = None
837 for keyword in node.keywords:
838 if keyword.arg == "metaclass":
839 metaclass = self.visit(keyword, newnode).value
840 break
841 decorators = self.visit_decorators(node, newnode)
842 newnode.postinit(
843 [self.visit(child, newnode) for child in node.bases],
844 [self.visit(child, newnode) for child in node.body],
845 decorators,
846 newstyle,
847 metaclass,
848 [
849 self.visit(kwd, newnode)
850 for kwd in node.keywords
851 if kwd.arg != "metaclass"
852 ],
853 position=self._get_position_info(node, newnode),
854 doc_node=self.visit(doc_ast_node, newnode),
855 type_params=(
856 [self.visit(param, newnode) for param in node.type_params]
857 if PY312_PLUS
858 else []
859 ),
860 )
861 return newnode
863 def visit_continue(self, node: ast.Continue, parent: NodeNG) -> nodes.Continue:
864 """Visit a Continue node by returning a fresh instance of it."""
865 return nodes.Continue(
866 lineno=node.lineno,
867 col_offset=node.col_offset,
868 end_lineno=node.end_lineno,
869 end_col_offset=node.end_col_offset,
870 parent=parent,
871 )
873 def visit_compare(self, node: ast.Compare, parent: NodeNG) -> nodes.Compare:
874 """Visit a Compare node by returning a fresh instance of it."""
875 newnode = nodes.Compare(
876 lineno=node.lineno,
877 col_offset=node.col_offset,
878 end_lineno=node.end_lineno,
879 end_col_offset=node.end_col_offset,
880 parent=parent,
881 )
882 newnode.postinit(
883 self.visit(node.left, newnode),
884 [
885 (
886 self._parser_module.cmp_op_classes[op.__class__],
887 self.visit(expr, newnode),
888 )
889 for (op, expr) in zip(node.ops, node.comparators)
890 ],
891 )
892 return newnode
894 def visit_comprehension(
895 self, node: ast.comprehension, parent: NodeNG
896 ) -> nodes.Comprehension:
897 """Visit a Comprehension node by returning a fresh instance of it."""
898 newnode = nodes.Comprehension(
899 parent=parent,
900 # Comprehension nodes don't have these attributes
901 # see https://docs.python.org/3/library/ast.html#abstract-grammar
902 lineno=None,
903 col_offset=None,
904 end_lineno=None,
905 end_col_offset=None,
906 )
907 newnode.postinit(
908 self.visit(node.target, newnode),
909 self.visit(node.iter, newnode),
910 [self.visit(child, newnode) for child in node.ifs],
911 bool(node.is_async),
912 )
913 return newnode
915 def visit_decorators(
916 self,
917 node: ast.ClassDef | ast.FunctionDef | ast.AsyncFunctionDef,
918 parent: NodeNG,
919 ) -> nodes.Decorators | None:
920 """Visit a Decorators node by returning a fresh instance of it.
922 Note: Method not called by 'visit'
923 """
924 if not node.decorator_list:
925 return None
926 # /!\ node is actually an _ast.FunctionDef node while
927 # parent is an astroid.nodes.FunctionDef node
929 # Set the line number of the first decorator for Python 3.8+.
930 lineno = node.decorator_list[0].lineno
931 end_lineno = node.decorator_list[-1].end_lineno
932 end_col_offset = node.decorator_list[-1].end_col_offset
934 newnode = nodes.Decorators(
935 lineno=lineno,
936 col_offset=node.col_offset,
937 end_lineno=end_lineno,
938 end_col_offset=end_col_offset,
939 parent=parent,
940 )
941 newnode.postinit([self.visit(child, newnode) for child in node.decorator_list])
942 return newnode
944 def visit_delete(self, node: ast.Delete, parent: NodeNG) -> nodes.Delete:
945 """Visit a Delete node by returning a fresh instance of it."""
946 newnode = nodes.Delete(
947 lineno=node.lineno,
948 col_offset=node.col_offset,
949 end_lineno=node.end_lineno,
950 end_col_offset=node.end_col_offset,
951 parent=parent,
952 )
953 newnode.postinit([self.visit(child, newnode) for child in node.targets])
954 return newnode
956 def _visit_dict_items(
957 self, node: ast.Dict, parent: NodeNG, newnode: nodes.Dict
958 ) -> Generator[tuple[NodeNG, NodeNG], None, None]:
959 for key, value in zip(node.keys, node.values):
960 rebuilt_key: NodeNG
961 rebuilt_value = self.visit(value, newnode)
962 if not key:
963 # Extended unpacking
964 rebuilt_key = nodes.DictUnpack(
965 lineno=rebuilt_value.lineno,
966 col_offset=rebuilt_value.col_offset,
967 end_lineno=rebuilt_value.end_lineno,
968 end_col_offset=rebuilt_value.end_col_offset,
969 parent=parent,
970 )
971 else:
972 rebuilt_key = self.visit(key, newnode)
973 yield rebuilt_key, rebuilt_value
975 def visit_dict(self, node: ast.Dict, parent: NodeNG) -> nodes.Dict:
976 """Visit a Dict node by returning a fresh instance of it."""
977 newnode = nodes.Dict(
978 lineno=node.lineno,
979 col_offset=node.col_offset,
980 end_lineno=node.end_lineno,
981 end_col_offset=node.end_col_offset,
982 parent=parent,
983 )
984 items: list[tuple[InferenceResult, InferenceResult]] = list(
985 self._visit_dict_items(node, parent, newnode)
986 )
987 newnode.postinit(items)
988 return newnode
990 def visit_dictcomp(self, node: ast.DictComp, parent: NodeNG) -> nodes.DictComp:
991 """Visit a DictComp node by returning a fresh instance of it."""
992 newnode = nodes.DictComp(
993 lineno=node.lineno,
994 col_offset=node.col_offset,
995 end_lineno=node.end_lineno,
996 end_col_offset=node.end_col_offset,
997 parent=parent,
998 )
999 newnode.postinit(
1000 self.visit(node.key, newnode),
1001 self.visit(node.value, newnode),
1002 [self.visit(child, newnode) for child in node.generators],
1003 )
1004 return newnode
1006 def visit_expr(self, node: ast.Expr, parent: NodeNG) -> nodes.Expr:
1007 """Visit a Expr node by returning a fresh instance of it."""
1008 newnode = nodes.Expr(
1009 lineno=node.lineno,
1010 col_offset=node.col_offset,
1011 end_lineno=node.end_lineno,
1012 end_col_offset=node.end_col_offset,
1013 parent=parent,
1014 )
1015 newnode.postinit(self.visit(node.value, newnode))
1016 return newnode
1018 def visit_excepthandler(
1019 self, node: ast.ExceptHandler, parent: NodeNG
1020 ) -> nodes.ExceptHandler:
1021 """Visit an ExceptHandler node by returning a fresh instance of it."""
1022 newnode = nodes.ExceptHandler(
1023 lineno=node.lineno,
1024 col_offset=node.col_offset,
1025 end_lineno=node.end_lineno,
1026 end_col_offset=node.end_col_offset,
1027 parent=parent,
1028 )
1029 newnode.postinit(
1030 self.visit(node.type, newnode),
1031 self.visit_assignname(node, newnode, node.name),
1032 [self.visit(child, newnode) for child in node.body],
1033 )
1034 return newnode
1036 @overload
1037 def _visit_for(
1038 self, cls: type[nodes.For], node: ast.For, parent: NodeNG
1039 ) -> nodes.For: ...
1041 @overload
1042 def _visit_for(
1043 self, cls: type[nodes.AsyncFor], node: ast.AsyncFor, parent: NodeNG
1044 ) -> nodes.AsyncFor: ...
1046 def _visit_for(
1047 self, cls: type[_ForT], node: ast.For | ast.AsyncFor, parent: NodeNG
1048 ) -> _ForT:
1049 """Visit a For node by returning a fresh instance of it."""
1050 col_offset = node.col_offset
1051 if IS_PYPY and not PY39_PLUS and isinstance(node, ast.AsyncFor) and self._data:
1052 # pylint: disable-next=unsubscriptable-object
1053 col_offset = self._data[node.lineno - 1].index("async")
1055 newnode = cls(
1056 lineno=node.lineno,
1057 col_offset=col_offset,
1058 end_lineno=node.end_lineno,
1059 end_col_offset=node.end_col_offset,
1060 parent=parent,
1061 )
1062 type_annotation = self.check_type_comment(node, parent=newnode)
1063 newnode.postinit(
1064 target=self.visit(node.target, newnode),
1065 iter=self.visit(node.iter, newnode),
1066 body=[self.visit(child, newnode) for child in node.body],
1067 orelse=[self.visit(child, newnode) for child in node.orelse],
1068 type_annotation=type_annotation,
1069 )
1070 return newnode
1072 def visit_for(self, node: ast.For, parent: NodeNG) -> nodes.For:
1073 return self._visit_for(nodes.For, node, parent)
1075 def visit_importfrom(
1076 self, node: ast.ImportFrom, parent: NodeNG
1077 ) -> nodes.ImportFrom:
1078 """Visit an ImportFrom node by returning a fresh instance of it."""
1079 names = [(alias.name, alias.asname) for alias in node.names]
1080 newnode = nodes.ImportFrom(
1081 fromname=node.module or "",
1082 names=names,
1083 level=node.level or None,
1084 lineno=node.lineno,
1085 col_offset=node.col_offset,
1086 end_lineno=node.end_lineno,
1087 end_col_offset=node.end_col_offset,
1088 parent=parent,
1089 )
1090 # store From names to add them to locals after building
1091 self._import_from_nodes.append(newnode)
1092 return newnode
1094 @overload
1095 def _visit_functiondef(
1096 self, cls: type[nodes.FunctionDef], node: ast.FunctionDef, parent: NodeNG
1097 ) -> nodes.FunctionDef: ...
1099 @overload
1100 def _visit_functiondef(
1101 self,
1102 cls: type[nodes.AsyncFunctionDef],
1103 node: ast.AsyncFunctionDef,
1104 parent: NodeNG,
1105 ) -> nodes.AsyncFunctionDef: ...
1107 def _visit_functiondef(
1108 self,
1109 cls: type[_FunctionT],
1110 node: ast.FunctionDef | ast.AsyncFunctionDef,
1111 parent: NodeNG,
1112 ) -> _FunctionT:
1113 """Visit an FunctionDef node to become astroid."""
1114 self._global_names.append({})
1115 node, doc_ast_node = self._get_doc(node)
1117 lineno = node.lineno
1118 if node.decorator_list:
1119 # Python 3.8 sets the line number of a decorated function
1120 # to be the actual line number of the function, but the
1121 # previous versions expected the decorator's line number instead.
1122 # We reset the function's line number to that of the
1123 # first decorator to maintain backward compatibility.
1124 # It's not ideal but this discrepancy was baked into
1125 # the framework for *years*.
1126 lineno = node.decorator_list[0].lineno
1128 newnode = cls(
1129 name=node.name,
1130 lineno=lineno,
1131 col_offset=node.col_offset,
1132 end_lineno=node.end_lineno,
1133 end_col_offset=node.end_col_offset,
1134 parent=parent,
1135 )
1136 decorators = self.visit_decorators(node, newnode)
1137 returns: NodeNG | None
1138 if node.returns:
1139 returns = self.visit(node.returns, newnode)
1140 else:
1141 returns = None
1143 type_comment_args = type_comment_returns = None
1144 type_comment_annotation = self.check_function_type_comment(node, newnode)
1145 if type_comment_annotation:
1146 type_comment_returns, type_comment_args = type_comment_annotation
1147 newnode.postinit(
1148 args=self.visit(node.args, newnode),
1149 body=[self.visit(child, newnode) for child in node.body],
1150 decorators=decorators,
1151 returns=returns,
1152 type_comment_returns=type_comment_returns,
1153 type_comment_args=type_comment_args,
1154 position=self._get_position_info(node, newnode),
1155 doc_node=self.visit(doc_ast_node, newnode),
1156 type_params=(
1157 [self.visit(param, newnode) for param in node.type_params]
1158 if PY312_PLUS
1159 else []
1160 ),
1161 )
1162 self._global_names.pop()
1163 return newnode
1165 def visit_functiondef(
1166 self, node: ast.FunctionDef, parent: NodeNG
1167 ) -> nodes.FunctionDef:
1168 return self._visit_functiondef(nodes.FunctionDef, node, parent)
1170 def visit_generatorexp(
1171 self, node: ast.GeneratorExp, parent: NodeNG
1172 ) -> nodes.GeneratorExp:
1173 """Visit a GeneratorExp node by returning a fresh instance of it."""
1174 newnode = nodes.GeneratorExp(
1175 lineno=node.lineno,
1176 col_offset=node.col_offset,
1177 end_lineno=node.end_lineno,
1178 end_col_offset=node.end_col_offset,
1179 parent=parent,
1180 )
1181 newnode.postinit(
1182 self.visit(node.elt, newnode),
1183 [self.visit(child, newnode) for child in node.generators],
1184 )
1185 return newnode
1187 def visit_attribute(
1188 self, node: ast.Attribute, parent: NodeNG
1189 ) -> nodes.Attribute | nodes.AssignAttr | nodes.DelAttr:
1190 """Visit an Attribute node by returning a fresh instance of it."""
1191 context = self._get_context(node)
1192 newnode: nodes.Attribute | nodes.AssignAttr | nodes.DelAttr
1193 if context == Context.Del:
1194 # FIXME : maybe we should reintroduce and visit_delattr ?
1195 # for instance, deactivating assign_ctx
1196 newnode = nodes.DelAttr(
1197 attrname=node.attr,
1198 lineno=node.lineno,
1199 col_offset=node.col_offset,
1200 end_lineno=node.end_lineno,
1201 end_col_offset=node.end_col_offset,
1202 parent=parent,
1203 )
1204 elif context == Context.Store:
1205 newnode = nodes.AssignAttr(
1206 attrname=node.attr,
1207 lineno=node.lineno,
1208 col_offset=node.col_offset,
1209 end_lineno=node.end_lineno,
1210 end_col_offset=node.end_col_offset,
1211 parent=parent,
1212 )
1213 # Prohibit a local save if we are in an ExceptHandler.
1214 if not isinstance(parent, nodes.ExceptHandler):
1215 # mypy doesn't recognize that newnode has to be AssignAttr because it
1216 # doesn't support ParamSpec
1217 # See https://github.com/python/mypy/issues/8645
1218 self._delayed_assattr.append(newnode) # type: ignore[arg-type]
1219 else:
1220 newnode = nodes.Attribute(
1221 attrname=node.attr,
1222 lineno=node.lineno,
1223 col_offset=node.col_offset,
1224 end_lineno=node.end_lineno,
1225 end_col_offset=node.end_col_offset,
1226 parent=parent,
1227 )
1228 newnode.postinit(self.visit(node.value, newnode))
1229 return newnode
1231 def visit_global(self, node: ast.Global, parent: NodeNG) -> nodes.Global:
1232 """Visit a Global node to become astroid."""
1233 newnode = nodes.Global(
1234 names=node.names,
1235 lineno=node.lineno,
1236 col_offset=node.col_offset,
1237 end_lineno=node.end_lineno,
1238 end_col_offset=node.end_col_offset,
1239 parent=parent,
1240 )
1241 if self._global_names: # global at the module level, no effect
1242 for name in node.names:
1243 self._global_names[-1].setdefault(name, []).append(newnode)
1244 return newnode
1246 def visit_if(self, node: ast.If, parent: NodeNG) -> nodes.If:
1247 """Visit an If node by returning a fresh instance of it."""
1248 newnode = nodes.If(
1249 lineno=node.lineno,
1250 col_offset=node.col_offset,
1251 end_lineno=node.end_lineno,
1252 end_col_offset=node.end_col_offset,
1253 parent=parent,
1254 )
1255 newnode.postinit(
1256 self.visit(node.test, newnode),
1257 [self.visit(child, newnode) for child in node.body],
1258 [self.visit(child, newnode) for child in node.orelse],
1259 )
1260 return newnode
1262 def visit_ifexp(self, node: ast.IfExp, parent: NodeNG) -> nodes.IfExp:
1263 """Visit a IfExp node by returning a fresh instance of it."""
1264 newnode = nodes.IfExp(
1265 lineno=node.lineno,
1266 col_offset=node.col_offset,
1267 end_lineno=node.end_lineno,
1268 end_col_offset=node.end_col_offset,
1269 parent=parent,
1270 )
1271 newnode.postinit(
1272 self.visit(node.test, newnode),
1273 self.visit(node.body, newnode),
1274 self.visit(node.orelse, newnode),
1275 )
1276 return newnode
1278 def visit_import(self, node: ast.Import, parent: NodeNG) -> nodes.Import:
1279 """Visit a Import node by returning a fresh instance of it."""
1280 names = [(alias.name, alias.asname) for alias in node.names]
1281 newnode = nodes.Import(
1282 names=names,
1283 lineno=node.lineno,
1284 col_offset=node.col_offset,
1285 end_lineno=node.end_lineno,
1286 end_col_offset=node.end_col_offset,
1287 parent=parent,
1288 )
1289 # save import names in parent's locals:
1290 for name, asname in newnode.names:
1291 name = asname or name
1292 parent.set_local(name.split(".")[0], newnode)
1293 return newnode
1295 def visit_joinedstr(self, node: ast.JoinedStr, parent: NodeNG) -> nodes.JoinedStr:
1296 newnode = nodes.JoinedStr(
1297 lineno=node.lineno,
1298 col_offset=node.col_offset,
1299 end_lineno=node.end_lineno,
1300 end_col_offset=node.end_col_offset,
1301 parent=parent,
1302 )
1303 newnode.postinit([self.visit(child, newnode) for child in node.values])
1304 return newnode
1306 def visit_formattedvalue(
1307 self, node: ast.FormattedValue, parent: NodeNG
1308 ) -> nodes.FormattedValue:
1309 newnode = nodes.FormattedValue(
1310 lineno=node.lineno,
1311 col_offset=node.col_offset,
1312 end_lineno=node.end_lineno,
1313 end_col_offset=node.end_col_offset,
1314 parent=parent,
1315 )
1316 newnode.postinit(
1317 value=self.visit(node.value, newnode),
1318 conversion=node.conversion,
1319 format_spec=self.visit(node.format_spec, newnode),
1320 )
1321 return newnode
1323 def visit_namedexpr(self, node: ast.NamedExpr, parent: NodeNG) -> nodes.NamedExpr:
1324 newnode = nodes.NamedExpr(
1325 lineno=node.lineno,
1326 col_offset=node.col_offset,
1327 end_lineno=node.end_lineno,
1328 end_col_offset=node.end_col_offset,
1329 parent=parent,
1330 )
1331 newnode.postinit(
1332 self.visit(node.target, newnode), self.visit(node.value, newnode)
1333 )
1334 return newnode
1336 if sys.version_info < (3, 9):
1337 # Not used in Python 3.9+.
1338 def visit_extslice(
1339 self, node: ast.ExtSlice, parent: nodes.Subscript
1340 ) -> nodes.Tuple:
1341 """Visit an ExtSlice node by returning a fresh instance of Tuple."""
1342 # ExtSlice doesn't have lineno or col_offset information
1343 newnode = nodes.Tuple(ctx=Context.Load, parent=parent)
1344 newnode.postinit([self.visit(dim, newnode) for dim in node.dims])
1345 return newnode
1347 def visit_index(self, node: ast.Index, parent: nodes.Subscript) -> NodeNG:
1348 """Visit a Index node by returning a fresh instance of NodeNG."""
1349 return self.visit(node.value, parent)
1351 def visit_keyword(self, node: ast.keyword, parent: NodeNG) -> nodes.Keyword:
1352 """Visit a Keyword node by returning a fresh instance of it."""
1353 newnode = nodes.Keyword(
1354 arg=node.arg,
1355 # position attributes added in 3.9
1356 lineno=getattr(node, "lineno", None),
1357 col_offset=getattr(node, "col_offset", None),
1358 end_lineno=getattr(node, "end_lineno", None),
1359 end_col_offset=getattr(node, "end_col_offset", None),
1360 parent=parent,
1361 )
1362 newnode.postinit(self.visit(node.value, newnode))
1363 return newnode
1365 def visit_lambda(self, node: ast.Lambda, parent: NodeNG) -> nodes.Lambda:
1366 """Visit a Lambda node by returning a fresh instance of it."""
1367 newnode = nodes.Lambda(
1368 lineno=node.lineno,
1369 col_offset=node.col_offset,
1370 end_lineno=node.end_lineno,
1371 end_col_offset=node.end_col_offset,
1372 parent=parent,
1373 )
1374 newnode.postinit(self.visit(node.args, newnode), self.visit(node.body, newnode))
1375 return newnode
1377 def visit_list(self, node: ast.List, parent: NodeNG) -> nodes.List:
1378 """Visit a List node by returning a fresh instance of it."""
1379 context = self._get_context(node)
1380 newnode = nodes.List(
1381 ctx=context,
1382 lineno=node.lineno,
1383 col_offset=node.col_offset,
1384 end_lineno=node.end_lineno,
1385 end_col_offset=node.end_col_offset,
1386 parent=parent,
1387 )
1388 newnode.postinit([self.visit(child, newnode) for child in node.elts])
1389 return newnode
1391 def visit_listcomp(self, node: ast.ListComp, parent: NodeNG) -> nodes.ListComp:
1392 """Visit a ListComp node by returning a fresh instance of it."""
1393 newnode = nodes.ListComp(
1394 lineno=node.lineno,
1395 col_offset=node.col_offset,
1396 end_lineno=node.end_lineno,
1397 end_col_offset=node.end_col_offset,
1398 parent=parent,
1399 )
1400 newnode.postinit(
1401 self.visit(node.elt, newnode),
1402 [self.visit(child, newnode) for child in node.generators],
1403 )
1404 return newnode
1406 def visit_name(
1407 self, node: ast.Name, parent: NodeNG
1408 ) -> nodes.Name | nodes.AssignName | nodes.DelName:
1409 """Visit a Name node by returning a fresh instance of it."""
1410 context = self._get_context(node)
1411 newnode: nodes.Name | nodes.AssignName | nodes.DelName
1412 if context == Context.Del:
1413 newnode = nodes.DelName(
1414 name=node.id,
1415 lineno=node.lineno,
1416 col_offset=node.col_offset,
1417 end_lineno=node.end_lineno,
1418 end_col_offset=node.end_col_offset,
1419 parent=parent,
1420 )
1421 elif context == Context.Store:
1422 newnode = nodes.AssignName(
1423 name=node.id,
1424 lineno=node.lineno,
1425 col_offset=node.col_offset,
1426 end_lineno=node.end_lineno,
1427 end_col_offset=node.end_col_offset,
1428 parent=parent,
1429 )
1430 else:
1431 newnode = nodes.Name(
1432 name=node.id,
1433 lineno=node.lineno,
1434 col_offset=node.col_offset,
1435 end_lineno=node.end_lineno,
1436 end_col_offset=node.end_col_offset,
1437 parent=parent,
1438 )
1439 # XXX REMOVE me :
1440 if context in (Context.Del, Context.Store): # 'Aug' ??
1441 newnode = cast(Union[nodes.AssignName, nodes.DelName], newnode)
1442 self._save_assignment(newnode)
1443 return newnode
1445 def visit_nonlocal(self, node: ast.Nonlocal, parent: NodeNG) -> nodes.Nonlocal:
1446 """Visit a Nonlocal node and return a new instance of it."""
1447 return nodes.Nonlocal(
1448 names=node.names,
1449 lineno=node.lineno,
1450 col_offset=node.col_offset,
1451 end_lineno=node.end_lineno,
1452 end_col_offset=node.end_col_offset,
1453 parent=parent,
1454 )
1456 def visit_constant(self, node: ast.Constant, parent: NodeNG) -> nodes.Const:
1457 """Visit a Constant node by returning a fresh instance of Const."""
1458 return nodes.Const(
1459 value=node.value,
1460 kind=node.kind,
1461 lineno=node.lineno,
1462 col_offset=node.col_offset,
1463 end_lineno=node.end_lineno,
1464 end_col_offset=node.end_col_offset,
1465 parent=parent,
1466 )
1468 def visit_paramspec(self, node: ast.ParamSpec, parent: NodeNG) -> nodes.ParamSpec:
1469 """Visit a ParamSpec node by returning a fresh instance of it."""
1470 newnode = nodes.ParamSpec(
1471 lineno=node.lineno,
1472 col_offset=node.col_offset,
1473 end_lineno=node.end_lineno,
1474 end_col_offset=node.end_col_offset,
1475 parent=parent,
1476 )
1477 # Add AssignName node for 'node.name'
1478 # https://bugs.python.org/issue43994
1479 newnode.postinit(name=self.visit_assignname(node, newnode, node.name))
1480 return newnode
1482 def visit_pass(self, node: ast.Pass, parent: NodeNG) -> nodes.Pass:
1483 """Visit a Pass node by returning a fresh instance of it."""
1484 return nodes.Pass(
1485 lineno=node.lineno,
1486 col_offset=node.col_offset,
1487 end_lineno=node.end_lineno,
1488 end_col_offset=node.end_col_offset,
1489 parent=parent,
1490 )
1492 def visit_raise(self, node: ast.Raise, parent: NodeNG) -> nodes.Raise:
1493 """Visit a Raise node by returning a fresh instance of it."""
1494 newnode = nodes.Raise(
1495 lineno=node.lineno,
1496 col_offset=node.col_offset,
1497 end_lineno=node.end_lineno,
1498 end_col_offset=node.end_col_offset,
1499 parent=parent,
1500 )
1501 # no traceback; anyway it is not used in Pylint
1502 newnode.postinit(
1503 exc=self.visit(node.exc, newnode),
1504 cause=self.visit(node.cause, newnode),
1505 )
1506 return newnode
1508 def visit_return(self, node: ast.Return, parent: NodeNG) -> nodes.Return:
1509 """Visit a Return node by returning a fresh instance of it."""
1510 newnode = nodes.Return(
1511 lineno=node.lineno,
1512 col_offset=node.col_offset,
1513 end_lineno=node.end_lineno,
1514 end_col_offset=node.end_col_offset,
1515 parent=parent,
1516 )
1517 newnode.postinit(self.visit(node.value, newnode))
1518 return newnode
1520 def visit_set(self, node: ast.Set, parent: NodeNG) -> nodes.Set:
1521 """Visit a Set node by returning a fresh instance of it."""
1522 newnode = nodes.Set(
1523 lineno=node.lineno,
1524 col_offset=node.col_offset,
1525 end_lineno=node.end_lineno,
1526 end_col_offset=node.end_col_offset,
1527 parent=parent,
1528 )
1529 newnode.postinit([self.visit(child, newnode) for child in node.elts])
1530 return newnode
1532 def visit_setcomp(self, node: ast.SetComp, parent: NodeNG) -> nodes.SetComp:
1533 """Visit a SetComp node by returning a fresh instance of it."""
1534 newnode = nodes.SetComp(
1535 lineno=node.lineno,
1536 col_offset=node.col_offset,
1537 end_lineno=node.end_lineno,
1538 end_col_offset=node.end_col_offset,
1539 parent=parent,
1540 )
1541 newnode.postinit(
1542 self.visit(node.elt, newnode),
1543 [self.visit(child, newnode) for child in node.generators],
1544 )
1545 return newnode
1547 def visit_slice(self, node: ast.Slice, parent: nodes.Subscript) -> nodes.Slice:
1548 """Visit a Slice node by returning a fresh instance of it."""
1549 newnode = nodes.Slice(
1550 # position attributes added in 3.9
1551 lineno=getattr(node, "lineno", None),
1552 col_offset=getattr(node, "col_offset", None),
1553 end_lineno=getattr(node, "end_lineno", None),
1554 end_col_offset=getattr(node, "end_col_offset", None),
1555 parent=parent,
1556 )
1557 newnode.postinit(
1558 lower=self.visit(node.lower, newnode),
1559 upper=self.visit(node.upper, newnode),
1560 step=self.visit(node.step, newnode),
1561 )
1562 return newnode
1564 def visit_subscript(self, node: ast.Subscript, parent: NodeNG) -> nodes.Subscript:
1565 """Visit a Subscript node by returning a fresh instance of it."""
1566 context = self._get_context(node)
1567 newnode = nodes.Subscript(
1568 ctx=context,
1569 lineno=node.lineno,
1570 col_offset=node.col_offset,
1571 end_lineno=node.end_lineno,
1572 end_col_offset=node.end_col_offset,
1573 parent=parent,
1574 )
1575 newnode.postinit(
1576 self.visit(node.value, newnode), self.visit(node.slice, newnode)
1577 )
1578 return newnode
1580 def visit_starred(self, node: ast.Starred, parent: NodeNG) -> nodes.Starred:
1581 """Visit a Starred node and return a new instance of it."""
1582 context = self._get_context(node)
1583 newnode = nodes.Starred(
1584 ctx=context,
1585 lineno=node.lineno,
1586 col_offset=node.col_offset,
1587 end_lineno=node.end_lineno,
1588 end_col_offset=node.end_col_offset,
1589 parent=parent,
1590 )
1591 newnode.postinit(self.visit(node.value, newnode))
1592 return newnode
1594 def visit_try(self, node: ast.Try, parent: NodeNG) -> nodes.Try:
1595 """Visit a Try node by returning a fresh instance of it"""
1596 newnode = nodes.Try(
1597 lineno=node.lineno,
1598 col_offset=node.col_offset,
1599 end_lineno=node.end_lineno,
1600 end_col_offset=node.end_col_offset,
1601 parent=parent,
1602 )
1603 newnode.postinit(
1604 body=[self.visit(child, newnode) for child in node.body],
1605 handlers=[self.visit(child, newnode) for child in node.handlers],
1606 orelse=[self.visit(child, newnode) for child in node.orelse],
1607 finalbody=[self.visit(child, newnode) for child in node.finalbody],
1608 )
1609 return newnode
1611 def visit_trystar(self, node: ast.TryStar, parent: NodeNG) -> nodes.TryStar:
1612 newnode = nodes.TryStar(
1613 lineno=node.lineno,
1614 col_offset=node.col_offset,
1615 end_lineno=node.end_lineno,
1616 end_col_offset=node.end_col_offset,
1617 parent=parent,
1618 )
1619 newnode.postinit(
1620 body=[self.visit(n, newnode) for n in node.body],
1621 handlers=[self.visit(n, newnode) for n in node.handlers],
1622 orelse=[self.visit(n, newnode) for n in node.orelse],
1623 finalbody=[self.visit(n, newnode) for n in node.finalbody],
1624 )
1625 return newnode
1627 def visit_tuple(self, node: ast.Tuple, parent: NodeNG) -> nodes.Tuple:
1628 """Visit a Tuple node by returning a fresh instance of it."""
1629 context = self._get_context(node)
1630 newnode = nodes.Tuple(
1631 ctx=context,
1632 lineno=node.lineno,
1633 col_offset=node.col_offset,
1634 end_lineno=node.end_lineno,
1635 end_col_offset=node.end_col_offset,
1636 parent=parent,
1637 )
1638 newnode.postinit([self.visit(child, newnode) for child in node.elts])
1639 return newnode
1641 def visit_typealias(self, node: ast.TypeAlias, parent: NodeNG) -> nodes.TypeAlias:
1642 """Visit a TypeAlias node by returning a fresh instance of it."""
1643 newnode = nodes.TypeAlias(
1644 lineno=node.lineno,
1645 col_offset=node.col_offset,
1646 end_lineno=node.end_lineno,
1647 end_col_offset=node.end_col_offset,
1648 parent=parent,
1649 )
1650 newnode.postinit(
1651 name=self.visit(node.name, newnode),
1652 type_params=[self.visit(p, newnode) for p in node.type_params],
1653 value=self.visit(node.value, newnode),
1654 )
1655 return newnode
1657 def visit_typevar(self, node: ast.TypeVar, parent: NodeNG) -> nodes.TypeVar:
1658 """Visit a TypeVar node by returning a fresh instance of it."""
1659 newnode = nodes.TypeVar(
1660 lineno=node.lineno,
1661 col_offset=node.col_offset,
1662 end_lineno=node.end_lineno,
1663 end_col_offset=node.end_col_offset,
1664 parent=parent,
1665 )
1666 # Add AssignName node for 'node.name'
1667 # https://bugs.python.org/issue43994
1668 newnode.postinit(
1669 name=self.visit_assignname(node, newnode, node.name),
1670 bound=self.visit(node.bound, newnode),
1671 )
1672 return newnode
1674 def visit_typevartuple(
1675 self, node: ast.TypeVarTuple, parent: NodeNG
1676 ) -> nodes.TypeVarTuple:
1677 """Visit a TypeVarTuple node by returning a fresh instance of it."""
1678 newnode = nodes.TypeVarTuple(
1679 lineno=node.lineno,
1680 col_offset=node.col_offset,
1681 end_lineno=node.end_lineno,
1682 end_col_offset=node.end_col_offset,
1683 parent=parent,
1684 )
1685 # Add AssignName node for 'node.name'
1686 # https://bugs.python.org/issue43994
1687 newnode.postinit(name=self.visit_assignname(node, newnode, node.name))
1688 return newnode
1690 def visit_unaryop(self, node: ast.UnaryOp, parent: NodeNG) -> nodes.UnaryOp:
1691 """Visit a UnaryOp node by returning a fresh instance of it."""
1692 newnode = nodes.UnaryOp(
1693 op=self._parser_module.unary_op_classes[node.op.__class__],
1694 lineno=node.lineno,
1695 col_offset=node.col_offset,
1696 end_lineno=node.end_lineno,
1697 end_col_offset=node.end_col_offset,
1698 parent=parent,
1699 )
1700 newnode.postinit(self.visit(node.operand, newnode))
1701 return newnode
1703 def visit_while(self, node: ast.While, parent: NodeNG) -> nodes.While:
1704 """Visit a While node by returning a fresh instance of it."""
1705 newnode = nodes.While(
1706 lineno=node.lineno,
1707 col_offset=node.col_offset,
1708 end_lineno=node.end_lineno,
1709 end_col_offset=node.end_col_offset,
1710 parent=parent,
1711 )
1712 newnode.postinit(
1713 self.visit(node.test, newnode),
1714 [self.visit(child, newnode) for child in node.body],
1715 [self.visit(child, newnode) for child in node.orelse],
1716 )
1717 return newnode
1719 @overload
1720 def _visit_with(
1721 self, cls: type[nodes.With], node: ast.With, parent: NodeNG
1722 ) -> nodes.With: ...
1724 @overload
1725 def _visit_with(
1726 self, cls: type[nodes.AsyncWith], node: ast.AsyncWith, parent: NodeNG
1727 ) -> nodes.AsyncWith: ...
1729 def _visit_with(
1730 self,
1731 cls: type[_WithT],
1732 node: ast.With | ast.AsyncWith,
1733 parent: NodeNG,
1734 ) -> _WithT:
1735 col_offset = node.col_offset
1736 if IS_PYPY and not PY39_PLUS and isinstance(node, ast.AsyncWith) and self._data:
1737 # pylint: disable-next=unsubscriptable-object
1738 col_offset = self._data[node.lineno - 1].index("async")
1740 newnode = cls(
1741 lineno=node.lineno,
1742 col_offset=col_offset,
1743 end_lineno=node.end_lineno,
1744 end_col_offset=node.end_col_offset,
1745 parent=parent,
1746 )
1748 def visit_child(child: ast.withitem) -> tuple[NodeNG, NodeNG | None]:
1749 expr = self.visit(child.context_expr, newnode)
1750 var = self.visit(child.optional_vars, newnode)
1751 return expr, var
1753 type_annotation = self.check_type_comment(node, parent=newnode)
1754 newnode.postinit(
1755 items=[visit_child(child) for child in node.items],
1756 body=[self.visit(child, newnode) for child in node.body],
1757 type_annotation=type_annotation,
1758 )
1759 return newnode
1761 def visit_with(self, node: ast.With, parent: NodeNG) -> NodeNG:
1762 return self._visit_with(nodes.With, node, parent)
1764 def visit_yield(self, node: ast.Yield, parent: NodeNG) -> NodeNG:
1765 """Visit a Yield node by returning a fresh instance of it."""
1766 newnode = nodes.Yield(
1767 lineno=node.lineno,
1768 col_offset=node.col_offset,
1769 end_lineno=node.end_lineno,
1770 end_col_offset=node.end_col_offset,
1771 parent=parent,
1772 )
1773 newnode.postinit(self.visit(node.value, newnode))
1774 return newnode
1776 def visit_yieldfrom(self, node: ast.YieldFrom, parent: NodeNG) -> NodeNG:
1777 newnode = nodes.YieldFrom(
1778 lineno=node.lineno,
1779 col_offset=node.col_offset,
1780 end_lineno=node.end_lineno,
1781 end_col_offset=node.end_col_offset,
1782 parent=parent,
1783 )
1784 newnode.postinit(self.visit(node.value, newnode))
1785 return newnode
1787 if sys.version_info >= (3, 10):
1789 def visit_match(self, node: ast.Match, parent: NodeNG) -> nodes.Match:
1790 newnode = nodes.Match(
1791 lineno=node.lineno,
1792 col_offset=node.col_offset,
1793 end_lineno=node.end_lineno,
1794 end_col_offset=node.end_col_offset,
1795 parent=parent,
1796 )
1797 newnode.postinit(
1798 subject=self.visit(node.subject, newnode),
1799 cases=[self.visit(case, newnode) for case in node.cases],
1800 )
1801 return newnode
1803 def visit_matchcase(
1804 self, node: ast.match_case, parent: NodeNG
1805 ) -> nodes.MatchCase:
1806 newnode = nodes.MatchCase(parent=parent)
1807 newnode.postinit(
1808 pattern=self.visit(node.pattern, newnode),
1809 guard=self.visit(node.guard, newnode),
1810 body=[self.visit(child, newnode) for child in node.body],
1811 )
1812 return newnode
1814 def visit_matchvalue(
1815 self, node: ast.MatchValue, parent: NodeNG
1816 ) -> nodes.MatchValue:
1817 newnode = nodes.MatchValue(
1818 lineno=node.lineno,
1819 col_offset=node.col_offset,
1820 end_lineno=node.end_lineno,
1821 end_col_offset=node.end_col_offset,
1822 parent=parent,
1823 )
1824 newnode.postinit(value=self.visit(node.value, newnode))
1825 return newnode
1827 def visit_matchsingleton(
1828 self, node: ast.MatchSingleton, parent: NodeNG
1829 ) -> nodes.MatchSingleton:
1830 return nodes.MatchSingleton(
1831 value=node.value,
1832 lineno=node.lineno,
1833 col_offset=node.col_offset,
1834 end_lineno=node.end_lineno,
1835 end_col_offset=node.end_col_offset,
1836 parent=parent,
1837 )
1839 def visit_matchsequence(
1840 self, node: ast.MatchSequence, parent: NodeNG
1841 ) -> nodes.MatchSequence:
1842 newnode = nodes.MatchSequence(
1843 lineno=node.lineno,
1844 col_offset=node.col_offset,
1845 end_lineno=node.end_lineno,
1846 end_col_offset=node.end_col_offset,
1847 parent=parent,
1848 )
1849 newnode.postinit(
1850 patterns=[self.visit(pattern, newnode) for pattern in node.patterns]
1851 )
1852 return newnode
1854 def visit_matchmapping(
1855 self, node: ast.MatchMapping, parent: NodeNG
1856 ) -> nodes.MatchMapping:
1857 newnode = nodes.MatchMapping(
1858 lineno=node.lineno,
1859 col_offset=node.col_offset,
1860 end_lineno=node.end_lineno,
1861 end_col_offset=node.end_col_offset,
1862 parent=parent,
1863 )
1864 # Add AssignName node for 'node.name'
1865 # https://bugs.python.org/issue43994
1866 newnode.postinit(
1867 keys=[self.visit(child, newnode) for child in node.keys],
1868 patterns=[self.visit(pattern, newnode) for pattern in node.patterns],
1869 rest=self.visit_assignname(node, newnode, node.rest),
1870 )
1871 return newnode
1873 def visit_matchclass(
1874 self, node: ast.MatchClass, parent: NodeNG
1875 ) -> nodes.MatchClass:
1876 newnode = nodes.MatchClass(
1877 lineno=node.lineno,
1878 col_offset=node.col_offset,
1879 end_lineno=node.end_lineno,
1880 end_col_offset=node.end_col_offset,
1881 parent=parent,
1882 )
1883 newnode.postinit(
1884 cls=self.visit(node.cls, newnode),
1885 patterns=[self.visit(pattern, newnode) for pattern in node.patterns],
1886 kwd_attrs=node.kwd_attrs,
1887 kwd_patterns=[
1888 self.visit(pattern, newnode) for pattern in node.kwd_patterns
1889 ],
1890 )
1891 return newnode
1893 def visit_matchstar(
1894 self, node: ast.MatchStar, parent: NodeNG
1895 ) -> nodes.MatchStar:
1896 newnode = nodes.MatchStar(
1897 lineno=node.lineno,
1898 col_offset=node.col_offset,
1899 end_lineno=node.end_lineno,
1900 end_col_offset=node.end_col_offset,
1901 parent=parent,
1902 )
1903 # Add AssignName node for 'node.name'
1904 # https://bugs.python.org/issue43994
1905 newnode.postinit(name=self.visit_assignname(node, newnode, node.name))
1906 return newnode
1908 def visit_matchas(self, node: ast.MatchAs, parent: NodeNG) -> nodes.MatchAs:
1909 newnode = nodes.MatchAs(
1910 lineno=node.lineno,
1911 col_offset=node.col_offset,
1912 end_lineno=node.end_lineno,
1913 end_col_offset=node.end_col_offset,
1914 parent=parent,
1915 )
1916 # Add AssignName node for 'node.name'
1917 # https://bugs.python.org/issue43994
1918 newnode.postinit(
1919 pattern=self.visit(node.pattern, newnode),
1920 name=self.visit_assignname(node, newnode, node.name),
1921 )
1922 return newnode
1924 def visit_matchor(self, node: ast.MatchOr, parent: NodeNG) -> nodes.MatchOr:
1925 newnode = nodes.MatchOr(
1926 lineno=node.lineno,
1927 col_offset=node.col_offset,
1928 end_lineno=node.end_lineno,
1929 end_col_offset=node.end_col_offset,
1930 parent=parent,
1931 )
1932 newnode.postinit(
1933 patterns=[self.visit(pattern, newnode) for pattern in node.patterns]
1934 )
1935 return newnode