Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/astroid/nodes/node_ng.py: 64%
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
5from __future__ import annotations
7import pprint
8import sys
9import warnings
10from collections.abc import Generator, Iterator
11from functools import cached_property
12from functools import singledispatch as _singledispatch
13from typing import (
14 TYPE_CHECKING,
15 Any,
16 ClassVar,
17 Literal,
18 Tuple,
19 Type,
20 TypeVar,
21 Union,
22 cast,
23 overload,
24)
26from astroid import util
27from astroid.context import InferenceContext
28from astroid.exceptions import (
29 AstroidError,
30 InferenceError,
31 ParentMissingError,
32 StatementMissing,
33 UseInferenceDefault,
34)
35from astroid.manager import AstroidManager
36from astroid.nodes.as_string import AsStringVisitor
37from astroid.nodes.const import OP_PRECEDENCE
38from astroid.nodes.utils import Position
39from astroid.typing import InferenceErrorInfo, InferenceResult, InferFn
41if sys.version_info >= (3, 11):
42 from typing import Self
43else:
44 from typing_extensions import Self
47if TYPE_CHECKING:
48 from astroid import nodes
49 from astroid.nodes import _base_nodes
52# Types for 'NodeNG.nodes_of_class()'
53_NodesT = TypeVar("_NodesT", bound="NodeNG")
54_NodesT2 = TypeVar("_NodesT2", bound="NodeNG")
55_NodesT3 = TypeVar("_NodesT3", bound="NodeNG")
56SkipKlassT = Union[None, Type["NodeNG"], Tuple[Type["NodeNG"], ...]]
59class NodeNG:
60 """A node of the new Abstract Syntax Tree (AST).
62 This is the base class for all Astroid node classes.
63 """
65 is_statement: ClassVar[bool] = False
66 """Whether this node indicates a statement."""
67 optional_assign: ClassVar[bool] = (
68 False # True for For (and for Comprehension if py <3.0)
69 )
70 """Whether this node optionally assigns a variable.
72 This is for loop assignments because loop won't necessarily perform an
73 assignment if the loop has no iterations.
74 This is also the case from comprehensions in Python 2.
75 """
76 is_function: ClassVar[bool] = False # True for FunctionDef nodes
77 """Whether this node indicates a function."""
78 is_lambda: ClassVar[bool] = False
80 # Attributes below are set by the builder module or by raw factories
81 _astroid_fields: ClassVar[tuple[str, ...]] = ()
82 """Node attributes that contain child nodes.
84 This is redefined in most concrete classes.
85 """
86 _other_fields: ClassVar[tuple[str, ...]] = ()
87 """Node attributes that do not contain child nodes."""
88 _other_other_fields: ClassVar[tuple[str, ...]] = ()
89 """Attributes that contain AST-dependent fields."""
90 # instance specific inference function infer(node, context)
91 _explicit_inference: InferFn[Self] | None = None
93 def __init__(
94 self,
95 lineno: int | None,
96 col_offset: int | None,
97 parent: NodeNG | None,
98 *,
99 end_lineno: int | None,
100 end_col_offset: int | None,
101 ) -> None:
102 self.lineno = lineno
103 """The line that this node appears on in the source code."""
105 self.col_offset = col_offset
106 """The column that this node appears on in the source code."""
108 self.parent = parent
109 """The parent node in the syntax tree."""
111 self.end_lineno = end_lineno
112 """The last line this node appears on in the source code."""
114 self.end_col_offset = end_col_offset
115 """The end column this node appears on in the source code.
117 Note: This is after the last symbol.
118 """
120 self.position: Position | None = None
121 """Position of keyword(s) and name.
123 Used as fallback for block nodes which might not provide good
124 enough positional information. E.g. ClassDef, FunctionDef.
125 """
127 def infer(
128 self, context: InferenceContext | None = None, **kwargs: Any
129 ) -> Generator[InferenceResult, None, None]:
130 """Get a generator of the inferred values.
132 This is the main entry point to the inference system.
134 .. seealso:: :ref:`inference`
136 If the instance has some explicit inference function set, it will be
137 called instead of the default interface.
139 :returns: The inferred values.
140 :rtype: iterable
141 """
142 if context is None:
143 context = InferenceContext()
144 else:
145 context = context.extra_context.get(self, context)
146 if self._explicit_inference is not None:
147 # explicit_inference is not bound, give it self explicitly
148 try:
149 for result in self._explicit_inference(
150 self, # type: ignore[arg-type]
151 context,
152 **kwargs,
153 ):
154 context.nodes_inferred += 1
155 yield result
156 return
157 except UseInferenceDefault:
158 pass
160 key = (self, context.lookupname, context.callcontext, context.boundnode)
161 if key in context.inferred:
162 yield from context.inferred[key]
163 return
165 results = []
167 # Limit inference amount to help with performance issues with
168 # exponentially exploding possible results.
169 limit = AstroidManager().max_inferable_values
170 for i, result in enumerate(self._infer(context=context, **kwargs)):
171 if i >= limit or (context.nodes_inferred > context.max_inferred):
172 results.append(util.Uninferable)
173 yield util.Uninferable
174 break
175 results.append(result)
176 yield result
177 context.nodes_inferred += 1
179 # Cache generated results for subsequent inferences of the
180 # same node using the same context
181 context.inferred[key] = tuple(results)
182 return
184 def repr_name(self) -> str:
185 """Get a name for nice representation.
187 This is either :attr:`name`, :attr:`attrname`, or the empty string.
188 """
189 if all(name not in self._astroid_fields for name in ("name", "attrname")):
190 return getattr(self, "name", "") or getattr(self, "attrname", "")
191 return ""
193 def __str__(self) -> str:
194 rname = self.repr_name()
195 cname = type(self).__name__
196 if rname:
197 string = "%(cname)s.%(rname)s(%(fields)s)"
198 alignment = len(cname) + len(rname) + 2
199 else:
200 string = "%(cname)s(%(fields)s)"
201 alignment = len(cname) + 1
202 result = []
203 for field in self._other_fields + self._astroid_fields:
204 value = getattr(self, field, "Unknown")
205 width = 80 - len(field) - alignment
206 lines = pprint.pformat(value, indent=2, width=width).splitlines(True)
208 inner = [lines[0]]
209 for line in lines[1:]:
210 inner.append(" " * alignment + line)
211 result.append(f"{field}={''.join(inner)}")
213 return string % {
214 "cname": cname,
215 "rname": rname,
216 "fields": (",\n" + " " * alignment).join(result),
217 }
219 def __repr__(self) -> str:
220 rname = self.repr_name()
221 # The dependencies used to calculate fromlineno (if not cached) may not exist at the time
222 try:
223 lineno = self.fromlineno
224 except AttributeError:
225 lineno = 0
226 if rname:
227 string = "<%(cname)s.%(rname)s l.%(lineno)s at 0x%(id)x>"
228 else:
229 string = "<%(cname)s l.%(lineno)s at 0x%(id)x>"
230 return string % {
231 "cname": type(self).__name__,
232 "rname": rname,
233 "lineno": lineno,
234 "id": id(self),
235 }
237 def accept(self, visitor: AsStringVisitor) -> str:
238 """Visit this node using the given visitor."""
239 func = getattr(visitor, "visit_" + self.__class__.__name__.lower())
240 return func(self)
242 def get_children(self) -> Iterator[NodeNG]:
243 """Get the child nodes below this node."""
244 for field in self._astroid_fields:
245 attr = getattr(self, field)
246 if attr is None:
247 continue
248 if isinstance(attr, (list, tuple)):
249 yield from attr
250 else:
251 yield attr
252 yield from ()
254 def last_child(self) -> NodeNG | None:
255 """An optimized version of list(get_children())[-1]."""
256 for field in self._astroid_fields[::-1]:
257 attr = getattr(self, field)
258 if not attr: # None or empty list / tuple
259 continue
260 if isinstance(attr, (list, tuple)):
261 return attr[-1]
262 return attr
263 return None
265 def node_ancestors(self) -> Iterator[NodeNG]:
266 """Yield parent, grandparent, etc until there are no more."""
267 parent = self.parent
268 while parent is not None:
269 yield parent
270 parent = parent.parent
272 def parent_of(self, node) -> bool:
273 """Check if this node is the parent of the given node.
275 :param node: The node to check if it is the child.
276 :type node: NodeNG
278 :returns: Whether this node is the parent of the given node.
279 """
280 return any(self is parent for parent in node.node_ancestors())
282 def statement(self, *, future: Literal[None, True] = None) -> _base_nodes.Statement:
283 """The first parent node, including self, marked as statement node.
285 :raises StatementMissing: If self has no parent attribute.
286 """
287 if future is not None:
288 warnings.warn(
289 "The future arg will be removed in astroid 4.0.",
290 DeprecationWarning,
291 stacklevel=2,
292 )
293 if self.is_statement:
294 return cast("_base_nodes.Statement", self)
295 if not self.parent:
296 raise StatementMissing(target=self)
297 return self.parent.statement()
299 def frame(
300 self, *, future: Literal[None, True] = None
301 ) -> nodes.FunctionDef | nodes.Module | nodes.ClassDef | nodes.Lambda:
302 """The first parent frame node.
304 A frame node is a :class:`Module`, :class:`FunctionDef`,
305 :class:`ClassDef` or :class:`Lambda`.
307 :returns: The first parent frame node.
308 :raises ParentMissingError: If self has no parent attribute.
309 """
310 if future is not None:
311 warnings.warn(
312 "The future arg will be removed in astroid 4.0.",
313 DeprecationWarning,
314 stacklevel=2,
315 )
316 if self.parent is None:
317 raise ParentMissingError(target=self)
318 return self.parent.frame(future=future)
320 def scope(self) -> nodes.LocalsDictNodeNG:
321 """The first parent node defining a new scope.
323 These can be Module, FunctionDef, ClassDef, Lambda, or GeneratorExp nodes.
325 :returns: The first parent scope node.
326 """
327 if not self.parent:
328 raise ParentMissingError(target=self)
329 return self.parent.scope()
331 def root(self) -> nodes.Module:
332 """Return the root node of the syntax tree.
334 :returns: The root node.
335 """
336 if not (parent := self.parent):
337 return self # type: ignore[return-value] # Only 'Module' does not have a parent node.
339 while parent.parent:
340 parent = parent.parent
341 return parent # type: ignore[return-value] # Only 'Module' does not have a parent node.
343 def child_sequence(self, child):
344 """Search for the sequence that contains this child.
346 :param child: The child node to search sequences for.
347 :type child: NodeNG
349 :returns: The sequence containing the given child node.
350 :rtype: iterable(NodeNG)
352 :raises AstroidError: If no sequence could be found that contains
353 the given child.
354 """
355 for field in self._astroid_fields:
356 node_or_sequence = getattr(self, field)
357 if node_or_sequence is child:
358 return [node_or_sequence]
359 # /!\ compiler.ast Nodes have an __iter__ walking over child nodes
360 if (
361 isinstance(node_or_sequence, (tuple, list))
362 and child in node_or_sequence
363 ):
364 return node_or_sequence
366 msg = "Could not find %s in %s's children"
367 raise AstroidError(msg % (repr(child), repr(self)))
369 def locate_child(self, child):
370 """Find the field of this node that contains the given child.
372 :param child: The child node to search fields for.
373 :type child: NodeNG
375 :returns: A tuple of the name of the field that contains the child,
376 and the sequence or node that contains the child node.
377 :rtype: tuple(str, iterable(NodeNG) or NodeNG)
379 :raises AstroidError: If no field could be found that contains
380 the given child.
381 """
382 for field in self._astroid_fields:
383 node_or_sequence = getattr(self, field)
384 # /!\ compiler.ast Nodes have an __iter__ walking over child nodes
385 if child is node_or_sequence:
386 return field, child
387 if (
388 isinstance(node_or_sequence, (tuple, list))
389 and child in node_or_sequence
390 ):
391 return field, node_or_sequence
392 msg = "Could not find %s in %s's children"
393 raise AstroidError(msg % (repr(child), repr(self)))
395 # FIXME : should we merge child_sequence and locate_child ? locate_child
396 # is only used in are_exclusive, child_sequence one time in pylint.
398 def next_sibling(self):
399 """The next sibling statement node.
401 :returns: The next sibling statement node.
402 :rtype: NodeNG or None
403 """
404 return self.parent.next_sibling()
406 def previous_sibling(self):
407 """The previous sibling statement.
409 :returns: The previous sibling statement node.
410 :rtype: NodeNG or None
411 """
412 return self.parent.previous_sibling()
414 # these are lazy because they're relatively expensive to compute for every
415 # single node, and they rarely get looked at
417 @cached_property
418 def fromlineno(self) -> int:
419 """The first line that this node appears on in the source code.
421 Can also return 0 if the line can not be determined.
422 """
423 if self.lineno is None:
424 return self._fixed_source_line()
425 return self.lineno
427 @cached_property
428 def tolineno(self) -> int:
429 """The last line that this node appears on in the source code.
431 Can also return 0 if the line can not be determined.
432 """
433 if self.end_lineno is not None:
434 return self.end_lineno
435 if not self._astroid_fields:
436 # can't have children
437 last_child = None
438 else:
439 last_child = self.last_child()
440 if last_child is None:
441 return self.fromlineno
442 return last_child.tolineno
444 def _fixed_source_line(self) -> int:
445 """Attempt to find the line that this node appears on.
447 We need this method since not all nodes have :attr:`lineno` set.
448 Will return 0 if the line number can not be determined.
449 """
450 line = self.lineno
451 _node = self
452 try:
453 while line is None:
454 _node = next(_node.get_children())
455 line = _node.lineno
456 except StopIteration:
457 parent = self.parent
458 while parent and line is None:
459 line = parent.lineno
460 parent = parent.parent
461 return line or 0
463 def block_range(self, lineno: int) -> tuple[int, int]:
464 """Get a range from the given line number to where this node ends.
466 :param lineno: The line number to start the range at.
468 :returns: The range of line numbers that this node belongs to,
469 starting at the given line number.
470 """
471 return lineno, self.tolineno
473 def set_local(self, name: str, stmt: NodeNG) -> None:
474 """Define that the given name is declared in the given statement node.
476 This definition is stored on the parent scope node.
478 .. seealso:: :meth:`scope`
480 :param name: The name that is being defined.
482 :param stmt: The statement that defines the given name.
483 """
484 assert self.parent
485 self.parent.set_local(name, stmt)
487 @overload
488 def nodes_of_class(
489 self,
490 klass: type[_NodesT],
491 skip_klass: SkipKlassT = ...,
492 ) -> Iterator[_NodesT]: ...
494 @overload
495 def nodes_of_class(
496 self,
497 klass: tuple[type[_NodesT], type[_NodesT2]],
498 skip_klass: SkipKlassT = ...,
499 ) -> Iterator[_NodesT] | Iterator[_NodesT2]: ...
501 @overload
502 def nodes_of_class(
503 self,
504 klass: tuple[type[_NodesT], type[_NodesT2], type[_NodesT3]],
505 skip_klass: SkipKlassT = ...,
506 ) -> Iterator[_NodesT] | Iterator[_NodesT2] | Iterator[_NodesT3]: ...
508 @overload
509 def nodes_of_class(
510 self,
511 klass: tuple[type[_NodesT], ...],
512 skip_klass: SkipKlassT = ...,
513 ) -> Iterator[_NodesT]: ...
515 def nodes_of_class( # type: ignore[misc] # mypy doesn't correctly recognize the overloads
516 self,
517 klass: (
518 type[_NodesT]
519 | tuple[type[_NodesT], type[_NodesT2]]
520 | tuple[type[_NodesT], type[_NodesT2], type[_NodesT3]]
521 | tuple[type[_NodesT], ...]
522 ),
523 skip_klass: SkipKlassT = None,
524 ) -> Iterator[_NodesT] | Iterator[_NodesT2] | Iterator[_NodesT3]:
525 """Get the nodes (including this one or below) of the given types.
527 :param klass: The types of node to search for.
529 :param skip_klass: The types of node to ignore. This is useful to ignore
530 subclasses of :attr:`klass`.
532 :returns: The node of the given types.
533 """
534 if isinstance(self, klass):
535 yield self
537 if skip_klass is None:
538 for child_node in self.get_children():
539 yield from child_node.nodes_of_class(klass, skip_klass)
541 return
543 for child_node in self.get_children():
544 if isinstance(child_node, skip_klass):
545 continue
546 yield from child_node.nodes_of_class(klass, skip_klass)
548 @cached_property
549 def _assign_nodes_in_scope(self) -> list[nodes.Assign]:
550 return []
552 def _get_name_nodes(self):
553 for child_node in self.get_children():
554 yield from child_node._get_name_nodes()
556 def _get_return_nodes_skip_functions(self):
557 yield from ()
559 def _get_yield_nodes_skip_functions(self):
560 yield from ()
562 def _get_yield_nodes_skip_lambdas(self):
563 yield from ()
565 def _infer_name(self, frame, name):
566 # overridden for ImportFrom, Import, Global, Try, TryStar and Arguments
567 pass
569 def _infer(
570 self, context: InferenceContext | None = None, **kwargs: Any
571 ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
572 """We don't know how to resolve a statement by default."""
573 # this method is overridden by most concrete classes
574 raise InferenceError(
575 "No inference function for {node!r}.", node=self, context=context
576 )
578 def inferred(self):
579 """Get a list of the inferred values.
581 .. seealso:: :ref:`inference`
583 :returns: The inferred values.
584 :rtype: list
585 """
586 return list(self.infer())
588 def instantiate_class(self):
589 """Instantiate an instance of the defined class.
591 .. note::
593 On anything other than a :class:`ClassDef` this will return self.
595 :returns: An instance of the defined class.
596 :rtype: object
597 """
598 return self
600 def has_base(self, node) -> bool:
601 """Check if this node inherits from the given type.
603 :param node: The node defining the base to look for.
604 Usually this is a :class:`Name` node.
605 :type node: NodeNG
606 """
607 return False
609 def callable(self) -> bool:
610 """Whether this node defines something that is callable.
612 :returns: Whether this defines something that is callable.
613 """
614 return False
616 def eq(self, value) -> bool:
617 return False
619 def as_string(self) -> str:
620 """Get the source code that this node represents."""
621 return AsStringVisitor()(self)
623 def repr_tree(
624 self,
625 ids=False,
626 include_linenos=False,
627 ast_state=False,
628 indent=" ",
629 max_depth=0,
630 max_width=80,
631 ) -> str:
632 """Get a string representation of the AST from this node.
634 :param ids: If true, includes the ids with the node type names.
635 :type ids: bool
637 :param include_linenos: If true, includes the line numbers and
638 column offsets.
639 :type include_linenos: bool
641 :param ast_state: If true, includes information derived from
642 the whole AST like local and global variables.
643 :type ast_state: bool
645 :param indent: A string to use to indent the output string.
646 :type indent: str
648 :param max_depth: If set to a positive integer, won't return
649 nodes deeper than max_depth in the string.
650 :type max_depth: int
652 :param max_width: Attempt to format the output string to stay
653 within this number of characters, but can exceed it under some
654 circumstances. Only positive integer values are valid, the default is 80.
655 :type max_width: int
657 :returns: The string representation of the AST.
658 :rtype: str
659 """
661 @_singledispatch
662 def _repr_tree(node, result, done, cur_indent="", depth=1):
663 """Outputs a representation of a non-tuple/list, non-node that's
664 contained within an AST, including strings.
665 """
666 lines = pprint.pformat(
667 node, width=max(max_width - len(cur_indent), 1)
668 ).splitlines(True)
669 result.append(lines[0])
670 result.extend([cur_indent + line for line in lines[1:]])
671 return len(lines) != 1
673 # pylint: disable=unused-variable,useless-suppression; doesn't understand singledispatch
674 @_repr_tree.register(tuple)
675 @_repr_tree.register(list)
676 def _repr_seq(node, result, done, cur_indent="", depth=1):
677 """Outputs a representation of a sequence that's contained within an
678 AST.
679 """
680 cur_indent += indent
681 result.append("[")
682 if not node:
683 broken = False
684 elif len(node) == 1:
685 broken = _repr_tree(node[0], result, done, cur_indent, depth)
686 elif len(node) == 2:
687 broken = _repr_tree(node[0], result, done, cur_indent, depth)
688 if not broken:
689 result.append(", ")
690 else:
691 result.append(",\n")
692 result.append(cur_indent)
693 broken = _repr_tree(node[1], result, done, cur_indent, depth) or broken
694 else:
695 result.append("\n")
696 result.append(cur_indent)
697 for child in node[:-1]:
698 _repr_tree(child, result, done, cur_indent, depth)
699 result.append(",\n")
700 result.append(cur_indent)
701 _repr_tree(node[-1], result, done, cur_indent, depth)
702 broken = True
703 result.append("]")
704 return broken
706 # pylint: disable=unused-variable,useless-suppression; doesn't understand singledispatch
707 @_repr_tree.register(NodeNG)
708 def _repr_node(node, result, done, cur_indent="", depth=1):
709 """Outputs a strings representation of an astroid node."""
710 if node in done:
711 result.append(
712 indent + f"<Recursion on {type(node).__name__} with id={id(node)}"
713 )
714 return False
715 done.add(node)
717 if max_depth and depth > max_depth:
718 result.append("...")
719 return False
720 depth += 1
721 cur_indent += indent
722 if ids:
723 result.append(f"{type(node).__name__}<0x{id(node):x}>(\n")
724 else:
725 result.append(f"{type(node).__name__}(")
726 fields = []
727 if include_linenos:
728 fields.extend(("lineno", "col_offset"))
729 fields.extend(node._other_fields)
730 fields.extend(node._astroid_fields)
731 if ast_state:
732 fields.extend(node._other_other_fields)
733 if not fields:
734 broken = False
735 elif len(fields) == 1:
736 result.append(f"{fields[0]}=")
737 broken = _repr_tree(
738 getattr(node, fields[0]), result, done, cur_indent, depth
739 )
740 else:
741 result.append("\n")
742 result.append(cur_indent)
743 for field in fields[:-1]:
744 # TODO: Remove this after removal of the 'doc' attribute
745 if field == "doc":
746 continue
747 result.append(f"{field}=")
748 _repr_tree(getattr(node, field), result, done, cur_indent, depth)
749 result.append(",\n")
750 result.append(cur_indent)
751 result.append(f"{fields[-1]}=")
752 _repr_tree(getattr(node, fields[-1]), result, done, cur_indent, depth)
753 broken = True
754 result.append(")")
755 return broken
757 result: list[str] = []
758 _repr_tree(self, result, set())
759 return "".join(result)
761 def bool_value(self, context: InferenceContext | None = None):
762 """Determine the boolean value of this node.
764 The boolean value of a node can have three
765 possible values:
767 * False: For instance, empty data structures,
768 False, empty strings, instances which return
769 explicitly False from the __nonzero__ / __bool__
770 method.
771 * True: Most of constructs are True by default:
772 classes, functions, modules etc
773 * Uninferable: The inference engine is uncertain of the
774 node's value.
776 :returns: The boolean value of this node.
777 :rtype: bool or Uninferable
778 """
779 return util.Uninferable
781 def op_precedence(self):
782 # Look up by class name or default to highest precedence
783 return OP_PRECEDENCE.get(self.__class__.__name__, len(OP_PRECEDENCE))
785 def op_left_associative(self) -> bool:
786 # Everything is left associative except `**` and IfExp
787 return True