Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/astroid/nodes/node_ng.py: 56%
347 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:53 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:53 +0000
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
51# Types for 'NodeNG.nodes_of_class()'
52_NodesT = TypeVar("_NodesT", bound="NodeNG")
53_NodesT2 = TypeVar("_NodesT2", bound="NodeNG")
54_NodesT3 = TypeVar("_NodesT3", bound="NodeNG")
55SkipKlassT = Union[None, Type["NodeNG"], Tuple[Type["NodeNG"], ...]]
58class NodeNG:
59 """A node of the new Abstract Syntax Tree (AST).
61 This is the base class for all Astroid node classes.
62 """
64 is_statement: ClassVar[bool] = False
65 """Whether this node indicates a statement."""
66 optional_assign: ClassVar[
67 bool
68 ] = False # True for For (and for Comprehension if py <3.0)
69 """Whether this node optionally assigns a variable.
71 This is for loop assignments because loop won't necessarily perform an
72 assignment if the loop has no iterations.
73 This is also the case from comprehensions in Python 2.
74 """
75 is_function: ClassVar[bool] = False # True for FunctionDef nodes
76 """Whether this node indicates a function."""
77 is_lambda: ClassVar[bool] = False
79 # Attributes below are set by the builder module or by raw factories
80 _astroid_fields: ClassVar[tuple[str, ...]] = ()
81 """Node attributes that contain child nodes.
83 This is redefined in most concrete classes.
84 """
85 _other_fields: ClassVar[tuple[str, ...]] = ()
86 """Node attributes that do not contain child nodes."""
87 _other_other_fields: ClassVar[tuple[str, ...]] = ()
88 """Attributes that contain AST-dependent fields."""
89 # instance specific inference function infer(node, context)
90 _explicit_inference: InferFn[Self] | None = None
92 def __init__(
93 self,
94 lineno: int | None,
95 col_offset: int | None,
96 parent: NodeNG | None,
97 *,
98 end_lineno: int | None,
99 end_col_offset: int | None,
100 ) -> None:
101 self.lineno = lineno
102 """The line that this node appears on in the source code."""
104 self.col_offset = col_offset
105 """The column that this node appears on in the source code."""
107 self.parent = parent
108 """The parent node in the syntax tree."""
110 self.end_lineno = end_lineno
111 """The last line this node appears on in the source code."""
113 self.end_col_offset = end_col_offset
114 """The end column this node appears on in the source code.
116 Note: This is after the last symbol.
117 """
119 self.position: Position | None = None
120 """Position of keyword(s) and name.
122 Used as fallback for block nodes which might not provide good
123 enough positional information. E.g. ClassDef, FunctionDef.
124 """
126 def infer(
127 self, context: InferenceContext | None = None, **kwargs: Any
128 ) -> Generator[InferenceResult, None, None]:
129 """Get a generator of the inferred values.
131 This is the main entry point to the inference system.
133 .. seealso:: :ref:`inference`
135 If the instance has some explicit inference function set, it will be
136 called instead of the default interface.
138 :returns: The inferred values.
139 :rtype: iterable
140 """
141 if context is not None:
142 context = context.extra_context.get(self, context)
143 if self._explicit_inference is not None:
144 # explicit_inference is not bound, give it self explicitly
145 try:
146 if context is None:
147 yield from self._explicit_inference(
148 self, # type: ignore[arg-type]
149 context,
150 **kwargs,
151 )
152 return
153 for result in self._explicit_inference(
154 self, # type: ignore[arg-type]
155 context,
156 **kwargs,
157 ):
158 context.nodes_inferred += 1
159 yield result
160 return
161 except UseInferenceDefault:
162 pass
164 if not context:
165 # nodes_inferred?
166 yield from self._infer(context=context, **kwargs)
167 return
169 key = (self, context.lookupname, context.callcontext, context.boundnode)
170 if key in context.inferred:
171 yield from context.inferred[key]
172 return
174 results = []
176 # Limit inference amount to help with performance issues with
177 # exponentially exploding possible results.
178 limit = AstroidManager.max_inferable_values
179 for i, result in enumerate(self._infer(context=context, **kwargs)):
180 if i >= limit or (context.nodes_inferred > context.max_inferred):
181 results.append(util.Uninferable)
182 yield util.Uninferable
183 break
184 results.append(result)
185 yield result
186 context.nodes_inferred += 1
188 # Cache generated results for subsequent inferences of the
189 # same node using the same context
190 context.inferred[key] = tuple(results)
191 return
193 def repr_name(self) -> str:
194 """Get a name for nice representation.
196 This is either :attr:`name`, :attr:`attrname`, or the empty string.
197 """
198 if all(name not in self._astroid_fields for name in ("name", "attrname")):
199 return getattr(self, "name", "") or getattr(self, "attrname", "")
200 return ""
202 def __str__(self) -> str:
203 rname = self.repr_name()
204 cname = type(self).__name__
205 if rname:
206 string = "%(cname)s.%(rname)s(%(fields)s)"
207 alignment = len(cname) + len(rname) + 2
208 else:
209 string = "%(cname)s(%(fields)s)"
210 alignment = len(cname) + 1
211 result = []
212 for field in self._other_fields + self._astroid_fields:
213 value = getattr(self, field)
214 width = 80 - len(field) - alignment
215 lines = pprint.pformat(value, indent=2, width=width).splitlines(True)
217 inner = [lines[0]]
218 for line in lines[1:]:
219 inner.append(" " * alignment + line)
220 result.append(f"{field}={''.join(inner)}")
222 return string % {
223 "cname": cname,
224 "rname": rname,
225 "fields": (",\n" + " " * alignment).join(result),
226 }
228 def __repr__(self) -> str:
229 rname = self.repr_name()
230 if rname:
231 string = "<%(cname)s.%(rname)s l.%(lineno)s at 0x%(id)x>"
232 else:
233 string = "<%(cname)s l.%(lineno)s at 0x%(id)x>"
234 return string % {
235 "cname": type(self).__name__,
236 "rname": rname,
237 "lineno": self.fromlineno,
238 "id": id(self),
239 }
241 def accept(self, visitor):
242 """Visit this node using the given visitor."""
243 func = getattr(visitor, "visit_" + self.__class__.__name__.lower())
244 return func(self)
246 def get_children(self) -> Iterator[NodeNG]:
247 """Get the child nodes below this node."""
248 for field in self._astroid_fields:
249 attr = getattr(self, field)
250 if attr is None:
251 continue
252 if isinstance(attr, (list, tuple)):
253 yield from attr
254 else:
255 yield attr
256 yield from ()
258 def last_child(self) -> NodeNG | None:
259 """An optimized version of list(get_children())[-1]."""
260 for field in self._astroid_fields[::-1]:
261 attr = getattr(self, field)
262 if not attr: # None or empty list / tuple
263 continue
264 if isinstance(attr, (list, tuple)):
265 return attr[-1]
266 return attr
267 return None
269 def node_ancestors(self) -> Iterator[NodeNG]:
270 """Yield parent, grandparent, etc until there are no more."""
271 parent = self.parent
272 while parent is not None:
273 yield parent
274 parent = parent.parent
276 def parent_of(self, node) -> bool:
277 """Check if this node is the parent of the given node.
279 :param node: The node to check if it is the child.
280 :type node: NodeNG
282 :returns: Whether this node is the parent of the given node.
283 """
284 return any(self is parent for parent in node.node_ancestors())
286 @overload
287 def statement(self, *, future: None = ...) -> nodes.Statement | nodes.Module:
288 ...
290 @overload
291 def statement(self, *, future: Literal[True]) -> nodes.Statement:
292 ...
294 def statement(
295 self, *, future: Literal[None, True] = None
296 ) -> nodes.Statement | nodes.Module:
297 """The first parent node, including self, marked as statement node.
299 TODO: Deprecate the future parameter and only raise StatementMissing and return
300 nodes.Statement
302 :raises AttributeError: If self has no parent attribute
303 :raises StatementMissing: If self has no parent attribute and future is True
304 """
305 if self.is_statement:
306 return cast("nodes.Statement", self)
307 if not self.parent:
308 if future:
309 raise StatementMissing(target=self)
310 warnings.warn(
311 "In astroid 3.0.0 NodeNG.statement() will return either a nodes.Statement "
312 "or raise a StatementMissing exception. AttributeError will no longer be raised. "
313 "This behaviour can already be triggered "
314 "by passing 'future=True' to a statement() call.",
315 DeprecationWarning,
316 stacklevel=2,
317 )
318 raise AttributeError(f"{self} object has no attribute 'parent'")
319 return self.parent.statement(future=future)
321 def frame(
322 self, *, future: Literal[None, True] = None
323 ) -> nodes.FunctionDef | nodes.Module | nodes.ClassDef | nodes.Lambda:
324 """The first parent frame node.
326 A frame node is a :class:`Module`, :class:`FunctionDef`,
327 :class:`ClassDef` or :class:`Lambda`.
329 :returns: The first parent frame node.
330 """
331 if self.parent is None:
332 if future:
333 raise ParentMissingError(target=self)
334 warnings.warn(
335 "In astroid 3.0.0 NodeNG.frame() will return either a Frame node, "
336 "or raise ParentMissingError. AttributeError will no longer be raised. "
337 "This behaviour can already be triggered "
338 "by passing 'future=True' to a frame() call.",
339 DeprecationWarning,
340 stacklevel=2,
341 )
342 raise AttributeError(f"{self} object has no attribute 'parent'")
344 return self.parent.frame(future=future)
346 def scope(self) -> nodes.LocalsDictNodeNG:
347 """The first parent node defining a new scope.
349 These can be Module, FunctionDef, ClassDef, Lambda, or GeneratorExp nodes.
351 :returns: The first parent scope node.
352 """
353 if not self.parent:
354 raise ParentMissingError(target=self)
355 return self.parent.scope()
357 def root(self) -> nodes.Module:
358 """Return the root node of the syntax tree.
360 :returns: The root node.
361 """
362 if self.parent:
363 return self.parent.root()
364 return self # type: ignore[return-value] # Only 'Module' does not have a parent node.
366 def child_sequence(self, child):
367 """Search for the sequence that contains this child.
369 :param child: The child node to search sequences for.
370 :type child: NodeNG
372 :returns: The sequence containing the given child node.
373 :rtype: iterable(NodeNG)
375 :raises AstroidError: If no sequence could be found that contains
376 the given child.
377 """
378 for field in self._astroid_fields:
379 node_or_sequence = getattr(self, field)
380 if node_or_sequence is child:
381 return [node_or_sequence]
382 # /!\ compiler.ast Nodes have an __iter__ walking over child nodes
383 if (
384 isinstance(node_or_sequence, (tuple, list))
385 and child in node_or_sequence
386 ):
387 return node_or_sequence
389 msg = "Could not find %s in %s's children"
390 raise AstroidError(msg % (repr(child), repr(self)))
392 def locate_child(self, child):
393 """Find the field of this node that contains the given child.
395 :param child: The child node to search fields for.
396 :type child: NodeNG
398 :returns: A tuple of the name of the field that contains the child,
399 and the sequence or node that contains the child node.
400 :rtype: tuple(str, iterable(NodeNG) or NodeNG)
402 :raises AstroidError: If no field could be found that contains
403 the given child.
404 """
405 for field in self._astroid_fields:
406 node_or_sequence = getattr(self, field)
407 # /!\ compiler.ast Nodes have an __iter__ walking over child nodes
408 if child is node_or_sequence:
409 return field, child
410 if (
411 isinstance(node_or_sequence, (tuple, list))
412 and child in node_or_sequence
413 ):
414 return field, node_or_sequence
415 msg = "Could not find %s in %s's children"
416 raise AstroidError(msg % (repr(child), repr(self)))
418 # FIXME : should we merge child_sequence and locate_child ? locate_child
419 # is only used in are_exclusive, child_sequence one time in pylint.
421 def next_sibling(self):
422 """The next sibling statement node.
424 :returns: The next sibling statement node.
425 :rtype: NodeNG or None
426 """
427 return self.parent.next_sibling()
429 def previous_sibling(self):
430 """The previous sibling statement.
432 :returns: The previous sibling statement node.
433 :rtype: NodeNG or None
434 """
435 return self.parent.previous_sibling()
437 # these are lazy because they're relatively expensive to compute for every
438 # single node, and they rarely get looked at
440 @cached_property
441 def fromlineno(self) -> int:
442 """The first line that this node appears on in the source code.
444 Can also return 0 if the line can not be determined.
445 """
446 if self.lineno is None:
447 return self._fixed_source_line()
448 return self.lineno
450 @cached_property
451 def tolineno(self) -> int:
452 """The last line that this node appears on in the source code.
454 Can also return 0 if the line can not be determined.
455 """
456 if self.end_lineno is not None:
457 return self.end_lineno
458 if not self._astroid_fields:
459 # can't have children
460 last_child = None
461 else:
462 last_child = self.last_child()
463 if last_child is None:
464 return self.fromlineno
465 return last_child.tolineno
467 def _fixed_source_line(self) -> int:
468 """Attempt to find the line that this node appears on.
470 We need this method since not all nodes have :attr:`lineno` set.
471 Will return 0 if the line number can not be determined.
472 """
473 line = self.lineno
474 _node = self
475 try:
476 while line is None:
477 _node = next(_node.get_children())
478 line = _node.lineno
479 except StopIteration:
480 parent = self.parent
481 while parent and line is None:
482 line = parent.lineno
483 parent = parent.parent
484 return line or 0
486 def block_range(self, lineno: int) -> tuple[int, int]:
487 """Get a range from the given line number to where this node ends.
489 :param lineno: The line number to start the range at.
491 :returns: The range of line numbers that this node belongs to,
492 starting at the given line number.
493 """
494 return lineno, self.tolineno
496 def set_local(self, name: str, stmt: NodeNG) -> None:
497 """Define that the given name is declared in the given statement node.
499 This definition is stored on the parent scope node.
501 .. seealso:: :meth:`scope`
503 :param name: The name that is being defined.
505 :param stmt: The statement that defines the given name.
506 """
507 assert self.parent
508 self.parent.set_local(name, stmt)
510 @overload
511 def nodes_of_class(
512 self,
513 klass: type[_NodesT],
514 skip_klass: SkipKlassT = ...,
515 ) -> Iterator[_NodesT]:
516 ...
518 @overload
519 def nodes_of_class(
520 self,
521 klass: tuple[type[_NodesT], type[_NodesT2]],
522 skip_klass: SkipKlassT = ...,
523 ) -> Iterator[_NodesT] | Iterator[_NodesT2]:
524 ...
526 @overload
527 def nodes_of_class(
528 self,
529 klass: tuple[type[_NodesT], type[_NodesT2], type[_NodesT3]],
530 skip_klass: SkipKlassT = ...,
531 ) -> Iterator[_NodesT] | Iterator[_NodesT2] | Iterator[_NodesT3]:
532 ...
534 @overload
535 def nodes_of_class(
536 self,
537 klass: tuple[type[_NodesT], ...],
538 skip_klass: SkipKlassT = ...,
539 ) -> Iterator[_NodesT]:
540 ...
542 def nodes_of_class( # type: ignore[misc] # mypy doesn't correctly recognize the overloads
543 self,
544 klass: (
545 type[_NodesT]
546 | tuple[type[_NodesT], type[_NodesT2]]
547 | tuple[type[_NodesT], type[_NodesT2], type[_NodesT3]]
548 | tuple[type[_NodesT], ...]
549 ),
550 skip_klass: SkipKlassT = None,
551 ) -> Iterator[_NodesT] | Iterator[_NodesT2] | Iterator[_NodesT3]:
552 """Get the nodes (including this one or below) of the given types.
554 :param klass: The types of node to search for.
556 :param skip_klass: The types of node to ignore. This is useful to ignore
557 subclasses of :attr:`klass`.
559 :returns: The node of the given types.
560 """
561 if isinstance(self, klass):
562 yield self
564 if skip_klass is None:
565 for child_node in self.get_children():
566 yield from child_node.nodes_of_class(klass, skip_klass)
568 return
570 for child_node in self.get_children():
571 if isinstance(child_node, skip_klass):
572 continue
573 yield from child_node.nodes_of_class(klass, skip_klass)
575 @cached_property
576 def _assign_nodes_in_scope(self) -> list[nodes.Assign]:
577 return []
579 def _get_name_nodes(self):
580 for child_node in self.get_children():
581 yield from child_node._get_name_nodes()
583 def _get_return_nodes_skip_functions(self):
584 yield from ()
586 def _get_yield_nodes_skip_lambdas(self):
587 yield from ()
589 def _infer_name(self, frame, name):
590 # overridden for ImportFrom, Import, Global, TryExcept, TryStar and Arguments
591 pass
593 def _infer(
594 self, context: InferenceContext | None = None, **kwargs: Any
595 ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
596 """We don't know how to resolve a statement by default."""
597 # this method is overridden by most concrete classes
598 raise InferenceError(
599 "No inference function for {node!r}.", node=self, context=context
600 )
602 def inferred(self):
603 """Get a list of the inferred values.
605 .. seealso:: :ref:`inference`
607 :returns: The inferred values.
608 :rtype: list
609 """
610 return list(self.infer())
612 def instantiate_class(self):
613 """Instantiate an instance of the defined class.
615 .. note::
617 On anything other than a :class:`ClassDef` this will return self.
619 :returns: An instance of the defined class.
620 :rtype: object
621 """
622 return self
624 def has_base(self, node) -> bool:
625 """Check if this node inherits from the given type.
627 :param node: The node defining the base to look for.
628 Usually this is a :class:`Name` node.
629 :type node: NodeNG
630 """
631 return False
633 def callable(self) -> bool:
634 """Whether this node defines something that is callable.
636 :returns: Whether this defines something that is callable.
637 """
638 return False
640 def eq(self, value) -> bool:
641 return False
643 def as_string(self) -> str:
644 """Get the source code that this node represents."""
645 return AsStringVisitor()(self)
647 def repr_tree(
648 self,
649 ids=False,
650 include_linenos=False,
651 ast_state=False,
652 indent=" ",
653 max_depth=0,
654 max_width=80,
655 ) -> str:
656 """Get a string representation of the AST from this node.
658 :param ids: If true, includes the ids with the node type names.
659 :type ids: bool
661 :param include_linenos: If true, includes the line numbers and
662 column offsets.
663 :type include_linenos: bool
665 :param ast_state: If true, includes information derived from
666 the whole AST like local and global variables.
667 :type ast_state: bool
669 :param indent: A string to use to indent the output string.
670 :type indent: str
672 :param max_depth: If set to a positive integer, won't return
673 nodes deeper than max_depth in the string.
674 :type max_depth: int
676 :param max_width: Attempt to format the output string to stay
677 within this number of characters, but can exceed it under some
678 circumstances. Only positive integer values are valid, the default is 80.
679 :type max_width: int
681 :returns: The string representation of the AST.
682 :rtype: str
683 """
685 @_singledispatch
686 def _repr_tree(node, result, done, cur_indent="", depth=1):
687 """Outputs a representation of a non-tuple/list, non-node that's
688 contained within an AST, including strings.
689 """
690 lines = pprint.pformat(
691 node, width=max(max_width - len(cur_indent), 1)
692 ).splitlines(True)
693 result.append(lines[0])
694 result.extend([cur_indent + line for line in lines[1:]])
695 return len(lines) != 1
697 # pylint: disable=unused-variable,useless-suppression; doesn't understand singledispatch
698 @_repr_tree.register(tuple)
699 @_repr_tree.register(list)
700 def _repr_seq(node, result, done, cur_indent="", depth=1):
701 """Outputs a representation of a sequence that's contained within an
702 AST.
703 """
704 cur_indent += indent
705 result.append("[")
706 if not node:
707 broken = False
708 elif len(node) == 1:
709 broken = _repr_tree(node[0], result, done, cur_indent, depth)
710 elif len(node) == 2:
711 broken = _repr_tree(node[0], result, done, cur_indent, depth)
712 if not broken:
713 result.append(", ")
714 else:
715 result.append(",\n")
716 result.append(cur_indent)
717 broken = _repr_tree(node[1], result, done, cur_indent, depth) or broken
718 else:
719 result.append("\n")
720 result.append(cur_indent)
721 for child in node[:-1]:
722 _repr_tree(child, result, done, cur_indent, depth)
723 result.append(",\n")
724 result.append(cur_indent)
725 _repr_tree(node[-1], result, done, cur_indent, depth)
726 broken = True
727 result.append("]")
728 return broken
730 # pylint: disable=unused-variable,useless-suppression; doesn't understand singledispatch
731 @_repr_tree.register(NodeNG)
732 def _repr_node(node, result, done, cur_indent="", depth=1):
733 """Outputs a strings representation of an astroid node."""
734 if node in done:
735 result.append(
736 indent + f"<Recursion on {type(node).__name__} with id={id(node)}"
737 )
738 return False
739 done.add(node)
741 if max_depth and depth > max_depth:
742 result.append("...")
743 return False
744 depth += 1
745 cur_indent += indent
746 if ids:
747 result.append(f"{type(node).__name__}<0x{id(node):x}>(\n")
748 else:
749 result.append(f"{type(node).__name__}(")
750 fields = []
751 if include_linenos:
752 fields.extend(("lineno", "col_offset"))
753 fields.extend(node._other_fields)
754 fields.extend(node._astroid_fields)
755 if ast_state:
756 fields.extend(node._other_other_fields)
757 if not fields:
758 broken = False
759 elif len(fields) == 1:
760 result.append(f"{fields[0]}=")
761 broken = _repr_tree(
762 getattr(node, fields[0]), result, done, cur_indent, depth
763 )
764 else:
765 result.append("\n")
766 result.append(cur_indent)
767 for field in fields[:-1]:
768 # TODO: Remove this after removal of the 'doc' attribute
769 if field == "doc":
770 continue
771 result.append(f"{field}=")
772 _repr_tree(getattr(node, field), result, done, cur_indent, depth)
773 result.append(",\n")
774 result.append(cur_indent)
775 result.append(f"{fields[-1]}=")
776 _repr_tree(getattr(node, fields[-1]), result, done, cur_indent, depth)
777 broken = True
778 result.append(")")
779 return broken
781 result: list[str] = []
782 _repr_tree(self, result, set())
783 return "".join(result)
785 def bool_value(self, context: InferenceContext | None = None):
786 """Determine the boolean value of this node.
788 The boolean value of a node can have three
789 possible values:
791 * False: For instance, empty data structures,
792 False, empty strings, instances which return
793 explicitly False from the __nonzero__ / __bool__
794 method.
795 * True: Most of constructs are True by default:
796 classes, functions, modules etc
797 * Uninferable: The inference engine is uncertain of the
798 node's value.
800 :returns: The boolean value of this node.
801 :rtype: bool or Uninferable
802 """
803 return util.Uninferable
805 def op_precedence(self):
806 # Look up by class name or default to highest precedence
807 return OP_PRECEDENCE.get(self.__class__.__name__, len(OP_PRECEDENCE))
809 def op_left_associative(self) -> bool:
810 # Everything is left associative except `**` and IfExp
811 return True