mirror of
https://github.com/PiBrewing/craftbeerpi4.git
synced 2024-11-26 08:55:28 +01:00
4862 lines
139 KiB
Python
4862 lines
139 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Copyright (c) 2009-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
|
|
# Copyright (c) 2010 Daniel Harding <dharding@gmail.com>
|
|
# Copyright (c) 2012 FELD Boris <lothiraldan@gmail.com>
|
|
# Copyright (c) 2013-2014 Google, Inc.
|
|
# Copyright (c) 2014-2020 Claudiu Popa <pcmanticore@gmail.com>
|
|
# Copyright (c) 2014 Eevee (Alex Munroe) <amunroe@yelp.com>
|
|
# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
|
|
# Copyright (c) 2015 Florian Bruhin <me@the-compiler.org>
|
|
# Copyright (c) 2016-2017 Derek Gustafson <degustaf@gmail.com>
|
|
# Copyright (c) 2016 Jared Garst <jgarst@users.noreply.github.com>
|
|
# Copyright (c) 2016 Jakub Wilk <jwilk@jwilk.net>
|
|
# Copyright (c) 2016 Dave Baum <dbaum@google.com>
|
|
# Copyright (c) 2017-2020 Ashley Whetter <ashley@awhetter.co.uk>
|
|
# Copyright (c) 2017, 2019 Łukasz Rogalski <rogalski.91@gmail.com>
|
|
# Copyright (c) 2017 rr- <rr-@sakuya.pl>
|
|
# Copyright (c) 2018-2019 hippo91 <guillaume.peillex@gmail.com>
|
|
# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
|
|
# Copyright (c) 2018 Ville Skyttä <ville.skytta@iki.fi>
|
|
# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
|
|
# Copyright (c) 2018 brendanator <brendan.maginnis@gmail.com>
|
|
# Copyright (c) 2018 HoverHell <hoverhell@gmail.com>
|
|
# Copyright (c) 2019 kavins14 <kavin.singh@mail.utoronto.ca>
|
|
# Copyright (c) 2019 kavins14 <kavinsingh@hotmail.com>
|
|
|
|
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
|
|
# For details: https://github.com/PyCQA/astroid/blob/master/COPYING.LESSER
|
|
|
|
# pylint: disable=too-many-lines; https://github.com/PyCQA/astroid/issues/465
|
|
|
|
"""Module for some node classes. More nodes in scoped_nodes.py
|
|
"""
|
|
|
|
import abc
|
|
import builtins as builtins_mod
|
|
import itertools
|
|
import pprint
|
|
import sys
|
|
from functools import lru_cache, singledispatch as _singledispatch
|
|
|
|
from astroid import as_string
|
|
from astroid import bases
|
|
from astroid import context as contextmod
|
|
from astroid import decorators
|
|
from astroid import exceptions
|
|
from astroid import manager
|
|
from astroid import mixins
|
|
from astroid import util
|
|
|
|
|
|
BUILTINS = builtins_mod.__name__
|
|
MANAGER = manager.AstroidManager()
|
|
PY38 = sys.version_info[:2] >= (3, 8)
|
|
|
|
|
|
def _is_const(value):
|
|
return isinstance(value, tuple(CONST_CLS))
|
|
|
|
|
|
@decorators.raise_if_nothing_inferred
|
|
def unpack_infer(stmt, context=None):
|
|
"""recursively generate nodes inferred by the given statement.
|
|
If the inferred value is a list or a tuple, recurse on the elements
|
|
"""
|
|
if isinstance(stmt, (List, Tuple)):
|
|
for elt in stmt.elts:
|
|
if elt is util.Uninferable:
|
|
yield elt
|
|
continue
|
|
yield from unpack_infer(elt, context)
|
|
return dict(node=stmt, context=context)
|
|
# if inferred is a final node, return it and stop
|
|
inferred = next(stmt.infer(context))
|
|
if inferred is stmt:
|
|
yield inferred
|
|
return dict(node=stmt, context=context)
|
|
# else, infer recursively, except Uninferable object that should be returned as is
|
|
for inferred in stmt.infer(context):
|
|
if inferred is util.Uninferable:
|
|
yield inferred
|
|
else:
|
|
yield from unpack_infer(inferred, context)
|
|
|
|
return dict(node=stmt, context=context)
|
|
|
|
|
|
def are_exclusive(
|
|
stmt1, stmt2, exceptions=None
|
|
): # pylint: disable=redefined-outer-name
|
|
"""return true if the two given statements are mutually exclusive
|
|
|
|
`exceptions` may be a list of exception names. If specified, discard If
|
|
branches and check one of the statement is in an exception handler catching
|
|
one of the given exceptions.
|
|
|
|
algorithm :
|
|
1) index stmt1's parents
|
|
2) climb among stmt2's parents until we find a common parent
|
|
3) if the common parent is a If or TryExcept statement, look if nodes are
|
|
in exclusive branches
|
|
"""
|
|
# index stmt1's parents
|
|
stmt1_parents = {}
|
|
children = {}
|
|
node = stmt1.parent
|
|
previous = stmt1
|
|
while node:
|
|
stmt1_parents[node] = 1
|
|
children[node] = previous
|
|
previous = node
|
|
node = node.parent
|
|
# climb among stmt2's parents until we find a common parent
|
|
node = stmt2.parent
|
|
previous = stmt2
|
|
while node:
|
|
if node in stmt1_parents:
|
|
# if the common parent is a If or TryExcept statement, look if
|
|
# nodes are in exclusive branches
|
|
if isinstance(node, If) and exceptions is None:
|
|
if (
|
|
node.locate_child(previous)[1]
|
|
is not node.locate_child(children[node])[1]
|
|
):
|
|
return True
|
|
elif isinstance(node, TryExcept):
|
|
c2attr, c2node = node.locate_child(previous)
|
|
c1attr, c1node = node.locate_child(children[node])
|
|
if c1node is not c2node:
|
|
first_in_body_caught_by_handlers = (
|
|
c2attr == "handlers"
|
|
and c1attr == "body"
|
|
and previous.catch(exceptions)
|
|
)
|
|
second_in_body_caught_by_handlers = (
|
|
c2attr == "body"
|
|
and c1attr == "handlers"
|
|
and children[node].catch(exceptions)
|
|
)
|
|
first_in_else_other_in_handlers = (
|
|
c2attr == "handlers" and c1attr == "orelse"
|
|
)
|
|
second_in_else_other_in_handlers = (
|
|
c2attr == "orelse" and c1attr == "handlers"
|
|
)
|
|
if any(
|
|
(
|
|
first_in_body_caught_by_handlers,
|
|
second_in_body_caught_by_handlers,
|
|
first_in_else_other_in_handlers,
|
|
second_in_else_other_in_handlers,
|
|
)
|
|
):
|
|
return True
|
|
elif c2attr == "handlers" and c1attr == "handlers":
|
|
return previous is not children[node]
|
|
return False
|
|
previous = node
|
|
node = node.parent
|
|
return False
|
|
|
|
|
|
# getitem() helpers.
|
|
|
|
_SLICE_SENTINEL = object()
|
|
|
|
|
|
def _slice_value(index, context=None):
|
|
"""Get the value of the given slice index."""
|
|
|
|
if isinstance(index, Const):
|
|
if isinstance(index.value, (int, type(None))):
|
|
return index.value
|
|
elif index is None:
|
|
return None
|
|
else:
|
|
# Try to infer what the index actually is.
|
|
# Since we can't return all the possible values,
|
|
# we'll stop at the first possible value.
|
|
try:
|
|
inferred = next(index.infer(context=context))
|
|
except exceptions.InferenceError:
|
|
pass
|
|
else:
|
|
if isinstance(inferred, Const):
|
|
if isinstance(inferred.value, (int, type(None))):
|
|
return inferred.value
|
|
|
|
# Use a sentinel, because None can be a valid
|
|
# value that this function can return,
|
|
# as it is the case for unspecified bounds.
|
|
return _SLICE_SENTINEL
|
|
|
|
|
|
def _infer_slice(node, context=None):
|
|
lower = _slice_value(node.lower, context)
|
|
upper = _slice_value(node.upper, context)
|
|
step = _slice_value(node.step, context)
|
|
if all(elem is not _SLICE_SENTINEL for elem in (lower, upper, step)):
|
|
return slice(lower, upper, step)
|
|
|
|
raise exceptions.AstroidTypeError(
|
|
message="Could not infer slice used in subscript",
|
|
node=node,
|
|
index=node.parent,
|
|
context=context,
|
|
)
|
|
|
|
|
|
def _container_getitem(instance, elts, index, context=None):
|
|
"""Get a slice or an item, using the given *index*, for the given sequence."""
|
|
try:
|
|
if isinstance(index, Slice):
|
|
index_slice = _infer_slice(index, context=context)
|
|
new_cls = instance.__class__()
|
|
new_cls.elts = elts[index_slice]
|
|
new_cls.parent = instance.parent
|
|
return new_cls
|
|
if isinstance(index, Const):
|
|
return elts[index.value]
|
|
except IndexError as exc:
|
|
raise exceptions.AstroidIndexError(
|
|
message="Index {index!s} out of range",
|
|
node=instance,
|
|
index=index,
|
|
context=context,
|
|
) from exc
|
|
except TypeError as exc:
|
|
raise exceptions.AstroidTypeError(
|
|
message="Type error {error!r}", node=instance, index=index, context=context
|
|
) from exc
|
|
|
|
raise exceptions.AstroidTypeError("Could not use %s as subscript index" % index)
|
|
|
|
|
|
OP_PRECEDENCE = {
|
|
op: precedence
|
|
for precedence, ops in enumerate(
|
|
[
|
|
["Lambda"], # lambda x: x + 1
|
|
["IfExp"], # 1 if True else 2
|
|
["or"],
|
|
["and"],
|
|
["not"],
|
|
["Compare"], # in, not in, is, is not, <, <=, >, >=, !=, ==
|
|
["|"],
|
|
["^"],
|
|
["&"],
|
|
["<<", ">>"],
|
|
["+", "-"],
|
|
["*", "@", "/", "//", "%"],
|
|
["UnaryOp"], # +, -, ~
|
|
["**"],
|
|
["Await"],
|
|
]
|
|
)
|
|
for op in ops
|
|
}
|
|
|
|
|
|
class NodeNG:
|
|
""" A node of the new Abstract Syntax Tree (AST).
|
|
|
|
This is the base class for all Astroid node classes.
|
|
"""
|
|
|
|
is_statement = False
|
|
"""Whether this node indicates a statement.
|
|
|
|
:type: bool
|
|
"""
|
|
optional_assign = False # True for For (and for Comprehension if py <3.0)
|
|
"""Whether this node optionally assigns a variable.
|
|
|
|
This is for loop assignments because loop won't necessarily perform an
|
|
assignment if the loop has no iterations.
|
|
This is also the case from comprehensions in Python 2.
|
|
|
|
:type: bool
|
|
"""
|
|
is_function = False # True for FunctionDef nodes
|
|
"""Whether this node indicates a function.
|
|
|
|
:type: bool
|
|
"""
|
|
is_lambda = False
|
|
# Attributes below are set by the builder module or by raw factories
|
|
lineno = None
|
|
"""The line that this node appears on in the source code.
|
|
|
|
:type: int or None
|
|
"""
|
|
col_offset = None
|
|
"""The column that this node appears on in the source code.
|
|
|
|
:type: int or None
|
|
"""
|
|
parent = None
|
|
"""The parent node in the syntax tree.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
_astroid_fields = ()
|
|
"""Node attributes that contain child nodes.
|
|
|
|
This is redefined in most concrete classes.
|
|
|
|
:type: tuple(str)
|
|
"""
|
|
_other_fields = ()
|
|
"""Node attributes that do not contain child nodes.
|
|
|
|
:type: tuple(str)
|
|
"""
|
|
_other_other_fields = ()
|
|
"""Attributes that contain AST-dependent fields.
|
|
|
|
:type: tuple(str)
|
|
"""
|
|
# instance specific inference function infer(node, context)
|
|
_explicit_inference = None
|
|
|
|
def __init__(self, lineno=None, col_offset=None, parent=None):
|
|
"""
|
|
:param lineno: The line that this node appears on in the source code.
|
|
:type lineno: int or None
|
|
|
|
:param col_offset: The column that this node appears on in the
|
|
source code.
|
|
:type col_offset: int or None
|
|
|
|
:param parent: The parent node in the syntax tree.
|
|
:type parent: NodeNG or None
|
|
"""
|
|
self.lineno = lineno
|
|
self.col_offset = col_offset
|
|
self.parent = parent
|
|
|
|
def infer(self, context=None, **kwargs):
|
|
"""Get a generator of the inferred values.
|
|
|
|
This is the main entry point to the inference system.
|
|
|
|
.. seealso:: :ref:`inference`
|
|
|
|
If the instance has some explicit inference function set, it will be
|
|
called instead of the default interface.
|
|
|
|
:returns: The inferred values.
|
|
:rtype: iterable
|
|
"""
|
|
if context is not None:
|
|
context = context.extra_context.get(self, context)
|
|
if self._explicit_inference is not None:
|
|
# explicit_inference is not bound, give it self explicitly
|
|
try:
|
|
# pylint: disable=not-callable
|
|
return self._explicit_inference(self, context, **kwargs)
|
|
except exceptions.UseInferenceDefault:
|
|
pass
|
|
|
|
if not context:
|
|
return self._infer(context, **kwargs)
|
|
|
|
key = (self, context.lookupname, context.callcontext, context.boundnode)
|
|
if key in context.inferred:
|
|
return iter(context.inferred[key])
|
|
|
|
gen = context.cache_generator(key, self._infer(context, **kwargs))
|
|
return util.limit_inference(gen, MANAGER.max_inferable_values)
|
|
|
|
def _repr_name(self):
|
|
"""Get a name for nice representation.
|
|
|
|
This is either :attr:`name`, :attr:`attrname`, or the empty string.
|
|
|
|
:returns: The nice name.
|
|
:rtype: str
|
|
"""
|
|
names = {"name", "attrname"}
|
|
if all(name not in self._astroid_fields for name in names):
|
|
return getattr(self, "name", getattr(self, "attrname", ""))
|
|
return ""
|
|
|
|
def __str__(self):
|
|
rname = self._repr_name()
|
|
cname = type(self).__name__
|
|
if rname:
|
|
string = "%(cname)s.%(rname)s(%(fields)s)"
|
|
alignment = len(cname) + len(rname) + 2
|
|
else:
|
|
string = "%(cname)s(%(fields)s)"
|
|
alignment = len(cname) + 1
|
|
result = []
|
|
for field in self._other_fields + self._astroid_fields:
|
|
value = getattr(self, field)
|
|
width = 80 - len(field) - alignment
|
|
lines = pprint.pformat(value, indent=2, width=width).splitlines(True)
|
|
|
|
inner = [lines[0]]
|
|
for line in lines[1:]:
|
|
inner.append(" " * alignment + line)
|
|
result.append("%s=%s" % (field, "".join(inner)))
|
|
|
|
return string % {
|
|
"cname": cname,
|
|
"rname": rname,
|
|
"fields": (",\n" + " " * alignment).join(result),
|
|
}
|
|
|
|
def __repr__(self):
|
|
rname = self._repr_name()
|
|
if rname:
|
|
string = "<%(cname)s.%(rname)s l.%(lineno)s at 0x%(id)x>"
|
|
else:
|
|
string = "<%(cname)s l.%(lineno)s at 0x%(id)x>"
|
|
return string % {
|
|
"cname": type(self).__name__,
|
|
"rname": rname,
|
|
"lineno": self.fromlineno,
|
|
"id": id(self),
|
|
}
|
|
|
|
def accept(self, visitor):
|
|
"""Visit this node using the given visitor."""
|
|
func = getattr(visitor, "visit_" + self.__class__.__name__.lower())
|
|
return func(self)
|
|
|
|
def get_children(self):
|
|
"""Get the child nodes below this node.
|
|
|
|
:returns: The children.
|
|
:rtype: iterable(NodeNG)
|
|
"""
|
|
for field in self._astroid_fields:
|
|
attr = getattr(self, field)
|
|
if attr is None:
|
|
continue
|
|
if isinstance(attr, (list, tuple)):
|
|
yield from attr
|
|
else:
|
|
yield attr
|
|
|
|
def last_child(self):
|
|
"""An optimized version of list(get_children())[-1]
|
|
|
|
:returns: The last child, or None if no children exist.
|
|
:rtype: NodeNG or None
|
|
"""
|
|
for field in self._astroid_fields[::-1]:
|
|
attr = getattr(self, field)
|
|
if not attr: # None or empty listy / tuple
|
|
continue
|
|
if isinstance(attr, (list, tuple)):
|
|
return attr[-1]
|
|
|
|
return attr
|
|
return None
|
|
|
|
def parent_of(self, node):
|
|
"""Check if this node is the parent of the given node.
|
|
|
|
:param node: The node to check if it is the child.
|
|
:type node: NodeNG
|
|
|
|
:returns: True if this node is the parent of the given node,
|
|
False otherwise.
|
|
:rtype: bool
|
|
"""
|
|
parent = node.parent
|
|
while parent is not None:
|
|
if self is parent:
|
|
return True
|
|
parent = parent.parent
|
|
return False
|
|
|
|
def statement(self):
|
|
"""The first parent node, including self, marked as statement node.
|
|
|
|
:returns: The first parent statement.
|
|
:rtype: NodeNG
|
|
"""
|
|
if self.is_statement:
|
|
return self
|
|
return self.parent.statement()
|
|
|
|
def frame(self):
|
|
"""The first parent frame node.
|
|
|
|
A frame node is a :class:`Module`, :class:`FunctionDef`,
|
|
or :class:`ClassDef`.
|
|
|
|
:returns: The first parent frame node.
|
|
:rtype: Module or FunctionDef or ClassDef
|
|
"""
|
|
return self.parent.frame()
|
|
|
|
def scope(self):
|
|
"""The first parent node defining a new scope.
|
|
|
|
:returns: The first parent scope node.
|
|
:rtype: Module or FunctionDef or ClassDef or Lambda or GenExpr
|
|
"""
|
|
if self.parent:
|
|
return self.parent.scope()
|
|
return None
|
|
|
|
def root(self):
|
|
"""Return the root node of the syntax tree.
|
|
|
|
:returns: The root node.
|
|
:rtype: Module
|
|
"""
|
|
if self.parent:
|
|
return self.parent.root()
|
|
return self
|
|
|
|
def child_sequence(self, child):
|
|
"""Search for the sequence that contains this child.
|
|
|
|
:param child: The child node to search sequences for.
|
|
:type child: NodeNG
|
|
|
|
:returns: The sequence containing the given child node.
|
|
:rtype: iterable(NodeNG)
|
|
|
|
:raises AstroidError: If no sequence could be found that contains
|
|
the given child.
|
|
"""
|
|
for field in self._astroid_fields:
|
|
node_or_sequence = getattr(self, field)
|
|
if node_or_sequence is child:
|
|
return [node_or_sequence]
|
|
# /!\ compiler.ast Nodes have an __iter__ walking over child nodes
|
|
if (
|
|
isinstance(node_or_sequence, (tuple, list))
|
|
and child in node_or_sequence
|
|
):
|
|
return node_or_sequence
|
|
|
|
msg = "Could not find %s in %s's children"
|
|
raise exceptions.AstroidError(msg % (repr(child), repr(self)))
|
|
|
|
def locate_child(self, child):
|
|
"""Find the field of this node that contains the given child.
|
|
|
|
:param child: The child node to search fields for.
|
|
:type child: NodeNG
|
|
|
|
:returns: A tuple of the name of the field that contains the child,
|
|
and the sequence or node that contains the child node.
|
|
:rtype: tuple(str, iterable(NodeNG) or NodeNG)
|
|
|
|
:raises AstroidError: If no field could be found that contains
|
|
the given child.
|
|
"""
|
|
for field in self._astroid_fields:
|
|
node_or_sequence = getattr(self, field)
|
|
# /!\ compiler.ast Nodes have an __iter__ walking over child nodes
|
|
if child is node_or_sequence:
|
|
return field, child
|
|
if (
|
|
isinstance(node_or_sequence, (tuple, list))
|
|
and child in node_or_sequence
|
|
):
|
|
return field, node_or_sequence
|
|
msg = "Could not find %s in %s's children"
|
|
raise exceptions.AstroidError(msg % (repr(child), repr(self)))
|
|
|
|
# FIXME : should we merge child_sequence and locate_child ? locate_child
|
|
# is only used in are_exclusive, child_sequence one time in pylint.
|
|
|
|
def next_sibling(self):
|
|
"""The next sibling statement node.
|
|
|
|
:returns: The next sibling statement node.
|
|
:rtype: NodeNG or None
|
|
"""
|
|
return self.parent.next_sibling()
|
|
|
|
def previous_sibling(self):
|
|
"""The previous sibling statement.
|
|
|
|
:returns: The previous sibling statement node.
|
|
:rtype: NodeNG or None
|
|
"""
|
|
return self.parent.previous_sibling()
|
|
|
|
# these are lazy because they're relatively expensive to compute for every
|
|
# single node, and they rarely get looked at
|
|
|
|
@decorators.cachedproperty
|
|
def fromlineno(self):
|
|
"""The first line that this node appears on in the source code.
|
|
|
|
:type: int or None
|
|
"""
|
|
if self.lineno is None:
|
|
return self._fixed_source_line()
|
|
|
|
return self.lineno
|
|
|
|
@decorators.cachedproperty
|
|
def tolineno(self):
|
|
"""The last line that this node appears on in the source code.
|
|
|
|
:type: int or None
|
|
"""
|
|
if not self._astroid_fields:
|
|
# can't have children
|
|
lastchild = None
|
|
else:
|
|
lastchild = self.last_child()
|
|
if lastchild is None:
|
|
return self.fromlineno
|
|
|
|
return lastchild.tolineno
|
|
|
|
def _fixed_source_line(self):
|
|
"""Attempt to find the line that this node appears on.
|
|
|
|
We need this method since not all nodes have :attr:`lineno` set.
|
|
|
|
:returns: The line number of this node,
|
|
or None if this could not be determined.
|
|
:rtype: int or None
|
|
"""
|
|
line = self.lineno
|
|
_node = self
|
|
try:
|
|
while line is None:
|
|
_node = next(_node.get_children())
|
|
line = _node.lineno
|
|
except StopIteration:
|
|
_node = self.parent
|
|
while _node and line is None:
|
|
line = _node.lineno
|
|
_node = _node.parent
|
|
return line
|
|
|
|
def block_range(self, lineno):
|
|
"""Get a range from the given line number to where this node ends.
|
|
|
|
:param lineno: The line number to start the range at.
|
|
:type lineno: int
|
|
|
|
:returns: The range of line numbers that this node belongs to,
|
|
starting at the given line number.
|
|
:rtype: tuple(int, int or None)
|
|
"""
|
|
return lineno, self.tolineno
|
|
|
|
def set_local(self, name, stmt):
|
|
"""Define that the given name is declared in the given statement node.
|
|
|
|
This definition is stored on the parent scope node.
|
|
|
|
.. seealso:: :meth:`scope`
|
|
|
|
:param name: The name that is being defined.
|
|
:type name: str
|
|
|
|
:param stmt: The statement that defines the given name.
|
|
:type stmt: NodeNG
|
|
"""
|
|
self.parent.set_local(name, stmt)
|
|
|
|
def nodes_of_class(self, klass, skip_klass=None):
|
|
"""Get the nodes (including this one or below) of the given types.
|
|
|
|
:param klass: The types of node to search for.
|
|
:type klass: builtins.type or tuple(builtins.type)
|
|
|
|
:param skip_klass: The types of node to ignore. This is useful to ignore
|
|
subclasses of :attr:`klass`.
|
|
:type skip_klass: builtins.type or tuple(builtins.type)
|
|
|
|
:returns: The node of the given types.
|
|
:rtype: iterable(NodeNG)
|
|
"""
|
|
if isinstance(self, klass):
|
|
yield self
|
|
|
|
if skip_klass is None:
|
|
for child_node in self.get_children():
|
|
yield from child_node.nodes_of_class(klass, skip_klass)
|
|
|
|
return
|
|
|
|
for child_node in self.get_children():
|
|
if isinstance(child_node, skip_klass):
|
|
continue
|
|
yield from child_node.nodes_of_class(klass, skip_klass)
|
|
|
|
@decorators.cached
|
|
def _get_assign_nodes(self):
|
|
return []
|
|
|
|
def _get_name_nodes(self):
|
|
for child_node in self.get_children():
|
|
yield from child_node._get_name_nodes()
|
|
|
|
def _get_return_nodes_skip_functions(self):
|
|
yield from ()
|
|
|
|
def _get_yield_nodes_skip_lambdas(self):
|
|
yield from ()
|
|
|
|
def _infer_name(self, frame, name):
|
|
# overridden for ImportFrom, Import, Global, TryExcept and Arguments
|
|
pass
|
|
|
|
def _infer(self, context=None):
|
|
"""we don't know how to resolve a statement by default"""
|
|
# this method is overridden by most concrete classes
|
|
raise exceptions.InferenceError(
|
|
"No inference function for {node!r}.", node=self, context=context
|
|
)
|
|
|
|
def inferred(self):
|
|
"""Get a list of the inferred values.
|
|
|
|
.. seealso:: :ref:`inference`
|
|
|
|
:returns: The inferred values.
|
|
:rtype: list
|
|
"""
|
|
return list(self.infer())
|
|
|
|
def instantiate_class(self):
|
|
"""Instantiate an instance of the defined class.
|
|
|
|
.. note::
|
|
|
|
On anything other than a :class:`ClassDef` this will return self.
|
|
|
|
:returns: An instance of the defined class.
|
|
:rtype: object
|
|
"""
|
|
return self
|
|
|
|
def has_base(self, node):
|
|
"""Check if this node inherits from the given type.
|
|
|
|
:param node: The node defining the base to look for.
|
|
Usually this is a :class:`Name` node.
|
|
:type node: NodeNG
|
|
"""
|
|
return False
|
|
|
|
def callable(self):
|
|
"""Whether this node defines something that is callable.
|
|
|
|
:returns: True if this defines something that is callable,
|
|
False otherwise.
|
|
:rtype: bool
|
|
"""
|
|
return False
|
|
|
|
def eq(self, value):
|
|
return False
|
|
|
|
def as_string(self):
|
|
"""Get the source code that this node represents.
|
|
|
|
:returns: The source code.
|
|
:rtype: str
|
|
"""
|
|
return as_string.to_code(self)
|
|
|
|
def repr_tree(
|
|
self,
|
|
ids=False,
|
|
include_linenos=False,
|
|
ast_state=False,
|
|
indent=" ",
|
|
max_depth=0,
|
|
max_width=80,
|
|
):
|
|
"""Get a string representation of the AST from this node.
|
|
|
|
:param ids: If true, includes the ids with the node type names.
|
|
:type ids: bool
|
|
|
|
:param include_linenos: If true, includes the line numbers and
|
|
column offsets.
|
|
:type include_linenos: bool
|
|
|
|
:param ast_state: If true, includes information derived from
|
|
the whole AST like local and global variables.
|
|
:type ast_state: bool
|
|
|
|
:param indent: A string to use to indent the output string.
|
|
:type indent: str
|
|
|
|
:param max_depth: If set to a positive integer, won't return
|
|
nodes deeper than max_depth in the string.
|
|
:type max_depth: int
|
|
|
|
:param max_width: Attempt to format the output string to stay
|
|
within this number of characters, but can exceed it under some
|
|
circumstances. Only positive integer values are valid, the default is 80.
|
|
:type max_width: int
|
|
|
|
:returns: The string representation of the AST.
|
|
:rtype: str
|
|
"""
|
|
# pylint: disable=too-many-statements
|
|
@_singledispatch
|
|
def _repr_tree(node, result, done, cur_indent="", depth=1):
|
|
"""Outputs a representation of a non-tuple/list, non-node that's
|
|
contained within an AST, including strings.
|
|
"""
|
|
lines = pprint.pformat(
|
|
node, width=max(max_width - len(cur_indent), 1)
|
|
).splitlines(True)
|
|
result.append(lines[0])
|
|
result.extend([cur_indent + line for line in lines[1:]])
|
|
return len(lines) != 1
|
|
|
|
# pylint: disable=unused-variable; doesn't understand singledispatch
|
|
@_repr_tree.register(tuple)
|
|
@_repr_tree.register(list)
|
|
def _repr_seq(node, result, done, cur_indent="", depth=1):
|
|
"""Outputs a representation of a sequence that's contained within an AST."""
|
|
cur_indent += indent
|
|
result.append("[")
|
|
if not node:
|
|
broken = False
|
|
elif len(node) == 1:
|
|
broken = _repr_tree(node[0], result, done, cur_indent, depth)
|
|
elif len(node) == 2:
|
|
broken = _repr_tree(node[0], result, done, cur_indent, depth)
|
|
if not broken:
|
|
result.append(", ")
|
|
else:
|
|
result.append(",\n")
|
|
result.append(cur_indent)
|
|
broken = _repr_tree(node[1], result, done, cur_indent, depth) or broken
|
|
else:
|
|
result.append("\n")
|
|
result.append(cur_indent)
|
|
for child in node[:-1]:
|
|
_repr_tree(child, result, done, cur_indent, depth)
|
|
result.append(",\n")
|
|
result.append(cur_indent)
|
|
_repr_tree(node[-1], result, done, cur_indent, depth)
|
|
broken = True
|
|
result.append("]")
|
|
return broken
|
|
|
|
# pylint: disable=unused-variable; doesn't understand singledispatch
|
|
@_repr_tree.register(NodeNG)
|
|
def _repr_node(node, result, done, cur_indent="", depth=1):
|
|
"""Outputs a strings representation of an astroid node."""
|
|
if node in done:
|
|
result.append(
|
|
indent
|
|
+ "<Recursion on %s with id=%s" % (type(node).__name__, id(node))
|
|
)
|
|
return False
|
|
done.add(node)
|
|
|
|
if max_depth and depth > max_depth:
|
|
result.append("...")
|
|
return False
|
|
depth += 1
|
|
cur_indent += indent
|
|
if ids:
|
|
result.append("%s<0x%x>(\n" % (type(node).__name__, id(node)))
|
|
else:
|
|
result.append("%s(" % type(node).__name__)
|
|
fields = []
|
|
if include_linenos:
|
|
fields.extend(("lineno", "col_offset"))
|
|
fields.extend(node._other_fields)
|
|
fields.extend(node._astroid_fields)
|
|
if ast_state:
|
|
fields.extend(node._other_other_fields)
|
|
if not fields:
|
|
broken = False
|
|
elif len(fields) == 1:
|
|
result.append("%s=" % fields[0])
|
|
broken = _repr_tree(
|
|
getattr(node, fields[0]), result, done, cur_indent, depth
|
|
)
|
|
else:
|
|
result.append("\n")
|
|
result.append(cur_indent)
|
|
for field in fields[:-1]:
|
|
result.append("%s=" % field)
|
|
_repr_tree(getattr(node, field), result, done, cur_indent, depth)
|
|
result.append(",\n")
|
|
result.append(cur_indent)
|
|
result.append("%s=" % fields[-1])
|
|
_repr_tree(getattr(node, fields[-1]), result, done, cur_indent, depth)
|
|
broken = True
|
|
result.append(")")
|
|
return broken
|
|
|
|
result = []
|
|
_repr_tree(self, result, set())
|
|
return "".join(result)
|
|
|
|
def bool_value(self, context=None):
|
|
"""Determine the boolean value of this node.
|
|
|
|
The boolean value of a node can have three
|
|
possible values:
|
|
|
|
* False: For instance, empty data structures,
|
|
False, empty strings, instances which return
|
|
explicitly False from the __nonzero__ / __bool__
|
|
method.
|
|
* True: Most of constructs are True by default:
|
|
classes, functions, modules etc
|
|
* Uninferable: The inference engine is uncertain of the
|
|
node's value.
|
|
|
|
:returns: The boolean value of this node.
|
|
:rtype: bool or Uninferable
|
|
"""
|
|
return util.Uninferable
|
|
|
|
def op_precedence(self):
|
|
# Look up by class name or default to highest precedence
|
|
return OP_PRECEDENCE.get(self.__class__.__name__, len(OP_PRECEDENCE))
|
|
|
|
def op_left_associative(self):
|
|
# Everything is left associative except `**` and IfExp
|
|
return True
|
|
|
|
|
|
class Statement(NodeNG):
|
|
"""Statement node adding a few attributes"""
|
|
|
|
is_statement = True
|
|
"""Whether this node indicates a statement.
|
|
|
|
:type: bool
|
|
"""
|
|
|
|
def next_sibling(self):
|
|
"""The next sibling statement node.
|
|
|
|
:returns: The next sibling statement node.
|
|
:rtype: NodeNG or None
|
|
"""
|
|
stmts = self.parent.child_sequence(self)
|
|
index = stmts.index(self)
|
|
try:
|
|
return stmts[index + 1]
|
|
except IndexError:
|
|
pass
|
|
|
|
def previous_sibling(self):
|
|
"""The previous sibling statement.
|
|
|
|
:returns: The previous sibling statement node.
|
|
:rtype: NodeNG or None
|
|
"""
|
|
stmts = self.parent.child_sequence(self)
|
|
index = stmts.index(self)
|
|
if index >= 1:
|
|
return stmts[index - 1]
|
|
return None
|
|
|
|
|
|
class _BaseContainer(
|
|
mixins.ParentAssignTypeMixin, NodeNG, bases.Instance, metaclass=abc.ABCMeta
|
|
):
|
|
"""Base class for Set, FrozenSet, Tuple and List."""
|
|
|
|
_astroid_fields = ("elts",)
|
|
|
|
def __init__(self, lineno=None, col_offset=None, parent=None):
|
|
"""
|
|
:param lineno: The line that this node appears on in the source code.
|
|
:type lineno: int or None
|
|
|
|
:param col_offset: The column that this node appears on in the
|
|
source code.
|
|
:type col_offset: int or None
|
|
|
|
:param parent: The parent node in the syntax tree.
|
|
:type parent: NodeNG or None
|
|
"""
|
|
self.elts = []
|
|
"""The elements in the node.
|
|
|
|
:type: list(NodeNG)
|
|
"""
|
|
|
|
super().__init__(lineno, col_offset, parent)
|
|
|
|
def postinit(self, elts):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param elts: The list of elements the that node contains.
|
|
:type elts: list(NodeNG)
|
|
"""
|
|
self.elts = elts
|
|
|
|
@classmethod
|
|
def from_elements(cls, elts=None):
|
|
"""Create a node of this type from the given list of elements.
|
|
|
|
:param elts: The list of elements that the node should contain.
|
|
:type elts: list(NodeNG)
|
|
|
|
:returns: A new node containing the given elements.
|
|
:rtype: NodeNG
|
|
"""
|
|
node = cls()
|
|
if elts is None:
|
|
node.elts = []
|
|
else:
|
|
node.elts = [const_factory(e) if _is_const(e) else e for e in elts]
|
|
return node
|
|
|
|
def itered(self):
|
|
"""An iterator over the elements this node contains.
|
|
|
|
:returns: The contents of this node.
|
|
:rtype: iterable(NodeNG)
|
|
"""
|
|
return self.elts
|
|
|
|
def bool_value(self, context=None):
|
|
"""Determine the boolean value of this node.
|
|
|
|
:returns: The boolean value of this node.
|
|
:rtype: bool or Uninferable
|
|
"""
|
|
return bool(self.elts)
|
|
|
|
@abc.abstractmethod
|
|
def pytype(self):
|
|
"""Get the name of the type that this node represents.
|
|
|
|
:returns: The name of the type.
|
|
:rtype: str
|
|
"""
|
|
|
|
def get_children(self):
|
|
yield from self.elts
|
|
|
|
|
|
class LookupMixIn:
|
|
"""Mixin to look up a name in the right scope."""
|
|
|
|
@lru_cache(maxsize=None)
|
|
def lookup(self, name):
|
|
"""Lookup where the given variable is assigned.
|
|
|
|
The lookup starts from self's scope. If self is not a frame itself
|
|
and the name is found in the inner frame locals, statements will be
|
|
filtered to remove ignorable statements according to self's location.
|
|
|
|
:param name: The name of the variable to find assignments for.
|
|
:type name: str
|
|
|
|
:returns: The scope node and the list of assignments associated to the
|
|
given name according to the scope where it has been found (locals,
|
|
globals or builtin).
|
|
:rtype: tuple(str, list(NodeNG))
|
|
"""
|
|
return self.scope().scope_lookup(self, name)
|
|
|
|
def ilookup(self, name):
|
|
"""Lookup the inferred values of the given variable.
|
|
|
|
:param name: The variable name to find values for.
|
|
:type name: str
|
|
|
|
:returns: The inferred values of the statements returned from
|
|
:meth:`lookup`.
|
|
:rtype: iterable
|
|
"""
|
|
frame, stmts = self.lookup(name)
|
|
context = contextmod.InferenceContext()
|
|
return bases._infer_stmts(stmts, context, frame)
|
|
|
|
def _get_filtered_node_statements(self, nodes):
|
|
statements = [(node, node.statement()) for node in nodes]
|
|
# Next we check if we have ExceptHandlers that are parent
|
|
# of the underlying variable, in which case the last one survives
|
|
if len(statements) > 1 and all(
|
|
isinstance(stmt, ExceptHandler) for _, stmt in statements
|
|
):
|
|
statements = [
|
|
(node, stmt) for node, stmt in statements if stmt.parent_of(self)
|
|
]
|
|
return statements
|
|
|
|
def _filter_stmts(self, stmts, frame, offset):
|
|
"""Filter the given list of statements to remove ignorable statements.
|
|
|
|
If self is not a frame itself and the name is found in the inner
|
|
frame locals, statements will be filtered to remove ignorable
|
|
statements according to self's location.
|
|
|
|
:param stmts: The statements to filter.
|
|
:type stmts: list(NodeNG)
|
|
|
|
:param frame: The frame that all of the given statements belong to.
|
|
:type frame: NodeNG
|
|
|
|
:param offset: The line offset to filter statements up to.
|
|
:type offset: int
|
|
|
|
:returns: The filtered statements.
|
|
:rtype: list(NodeNG)
|
|
"""
|
|
# if offset == -1, my actual frame is not the inner frame but its parent
|
|
#
|
|
# class A(B): pass
|
|
#
|
|
# we need this to resolve B correctly
|
|
if offset == -1:
|
|
myframe = self.frame().parent.frame()
|
|
else:
|
|
myframe = self.frame()
|
|
# If the frame of this node is the same as the statement
|
|
# of this node, then the node is part of a class or
|
|
# a function definition and the frame of this node should be the
|
|
# the upper frame, not the frame of the definition.
|
|
# For more information why this is important,
|
|
# see Pylint issue #295.
|
|
# For example, for 'b', the statement is the same
|
|
# as the frame / scope:
|
|
#
|
|
# def test(b=1):
|
|
# ...
|
|
|
|
if self.statement() is myframe and myframe.parent:
|
|
myframe = myframe.parent.frame()
|
|
mystmt = self.statement()
|
|
# line filtering if we are in the same frame
|
|
#
|
|
# take care node may be missing lineno information (this is the case for
|
|
# nodes inserted for living objects)
|
|
if myframe is frame and mystmt.fromlineno is not None:
|
|
assert mystmt.fromlineno is not None, mystmt
|
|
mylineno = mystmt.fromlineno + offset
|
|
else:
|
|
# disabling lineno filtering
|
|
mylineno = 0
|
|
|
|
_stmts = []
|
|
_stmt_parents = []
|
|
statements = self._get_filtered_node_statements(stmts)
|
|
|
|
for node, stmt in statements:
|
|
# line filtering is on and we have reached our location, break
|
|
if stmt.fromlineno > mylineno > 0:
|
|
break
|
|
# Ignore decorators with the same name as the
|
|
# decorated function
|
|
# Fixes issue #375
|
|
if mystmt is stmt and is_from_decorator(self):
|
|
continue
|
|
assert hasattr(node, "assign_type"), (
|
|
node,
|
|
node.scope(),
|
|
node.scope().locals,
|
|
)
|
|
assign_type = node.assign_type()
|
|
if node.has_base(self):
|
|
break
|
|
|
|
_stmts, done = assign_type._get_filtered_stmts(self, node, _stmts, mystmt)
|
|
if done:
|
|
break
|
|
|
|
optional_assign = assign_type.optional_assign
|
|
if optional_assign and assign_type.parent_of(self):
|
|
# we are inside a loop, loop var assignment is hiding previous
|
|
# assignment
|
|
_stmts = [node]
|
|
_stmt_parents = [stmt.parent]
|
|
continue
|
|
|
|
if isinstance(assign_type, NamedExpr):
|
|
_stmts = [node]
|
|
continue
|
|
|
|
# XXX comment various branches below!!!
|
|
try:
|
|
pindex = _stmt_parents.index(stmt.parent)
|
|
except ValueError:
|
|
pass
|
|
else:
|
|
# we got a parent index, this means the currently visited node
|
|
# is at the same block level as a previously visited node
|
|
if _stmts[pindex].assign_type().parent_of(assign_type):
|
|
# both statements are not at the same block level
|
|
continue
|
|
# if currently visited node is following previously considered
|
|
# assignment and both are not exclusive, we can drop the
|
|
# previous one. For instance in the following code ::
|
|
#
|
|
# if a:
|
|
# x = 1
|
|
# else:
|
|
# x = 2
|
|
# print x
|
|
#
|
|
# we can't remove neither x = 1 nor x = 2 when looking for 'x'
|
|
# of 'print x'; while in the following ::
|
|
#
|
|
# x = 1
|
|
# x = 2
|
|
# print x
|
|
#
|
|
# we can remove x = 1 when we see x = 2
|
|
#
|
|
# moreover, on loop assignment types, assignment won't
|
|
# necessarily be done if the loop has no iteration, so we don't
|
|
# want to clear previous assignments if any (hence the test on
|
|
# optional_assign)
|
|
if not (optional_assign or are_exclusive(_stmts[pindex], node)):
|
|
if (
|
|
# In case of partial function node, if the statement is different
|
|
# from the origin function then it can be deleted otherwise it should
|
|
# remain to be able to correctly infer the call to origin function.
|
|
not node.is_function
|
|
or node.qname() != "PartialFunction"
|
|
or node.name != _stmts[pindex].name
|
|
):
|
|
del _stmt_parents[pindex]
|
|
del _stmts[pindex]
|
|
if isinstance(node, AssignName):
|
|
if not optional_assign and stmt.parent is mystmt.parent:
|
|
_stmts = []
|
|
_stmt_parents = []
|
|
elif isinstance(node, DelName):
|
|
_stmts = []
|
|
_stmt_parents = []
|
|
continue
|
|
if not are_exclusive(self, node):
|
|
_stmts.append(node)
|
|
_stmt_parents.append(stmt.parent)
|
|
return _stmts
|
|
|
|
|
|
# Name classes
|
|
|
|
|
|
class AssignName(
|
|
mixins.NoChildrenMixin, LookupMixIn, mixins.ParentAssignTypeMixin, NodeNG
|
|
):
|
|
"""Variation of :class:`ast.Assign` representing assignment to a name.
|
|
|
|
An :class:`AssignName` is the name of something that is assigned to.
|
|
This includes variables defined in a function signature or in a loop.
|
|
|
|
>>> node = astroid.extract_node('variable = range(10)')
|
|
>>> node
|
|
<Assign l.1 at 0x7effe1db8550>
|
|
>>> list(node.get_children())
|
|
[<AssignName.variable l.1 at 0x7effe1db8748>, <Call l.1 at 0x7effe1db8630>]
|
|
>>> list(node.get_children())[0].as_string()
|
|
'variable'
|
|
"""
|
|
|
|
_other_fields = ("name",)
|
|
|
|
def __init__(self, name=None, lineno=None, col_offset=None, parent=None):
|
|
"""
|
|
:param name: The name that is assigned to.
|
|
:type name: str or None
|
|
|
|
:param lineno: The line that this node appears on in the source code.
|
|
:type lineno: int or None
|
|
|
|
:param col_offset: The column that this node appears on in the
|
|
source code.
|
|
:type col_offset: int or None
|
|
|
|
:param parent: The parent node in the syntax tree.
|
|
:type parent: NodeNG or None
|
|
"""
|
|
self.name = name
|
|
"""The name that is assigned to.
|
|
|
|
:type: str or None
|
|
"""
|
|
|
|
super().__init__(lineno, col_offset, parent)
|
|
|
|
|
|
class DelName(
|
|
mixins.NoChildrenMixin, LookupMixIn, mixins.ParentAssignTypeMixin, NodeNG
|
|
):
|
|
"""Variation of :class:`ast.Delete` representing deletion of a name.
|
|
|
|
A :class:`DelName` is the name of something that is deleted.
|
|
|
|
>>> node = astroid.extract_node("del variable #@")
|
|
>>> list(node.get_children())
|
|
[<DelName.variable l.1 at 0x7effe1da4d30>]
|
|
>>> list(node.get_children())[0].as_string()
|
|
'variable'
|
|
"""
|
|
|
|
_other_fields = ("name",)
|
|
|
|
def __init__(self, name=None, lineno=None, col_offset=None, parent=None):
|
|
"""
|
|
:param name: The name that is being deleted.
|
|
:type name: str or None
|
|
|
|
:param lineno: The line that this node appears on in the source code.
|
|
:type lineno: int or None
|
|
|
|
:param col_offset: The column that this node appears on in the
|
|
source code.
|
|
:type col_offset: int or None
|
|
|
|
:param parent: The parent node in the syntax tree.
|
|
:type parent: NodeNG or None
|
|
"""
|
|
self.name = name
|
|
"""The name that is being deleted.
|
|
|
|
:type: str or None
|
|
"""
|
|
|
|
super().__init__(lineno, col_offset, parent)
|
|
|
|
|
|
class Name(mixins.NoChildrenMixin, LookupMixIn, NodeNG):
|
|
"""Class representing an :class:`ast.Name` node.
|
|
|
|
A :class:`Name` node is something that is named, but not covered by
|
|
:class:`AssignName` or :class:`DelName`.
|
|
|
|
>>> node = astroid.extract_node('range(10)')
|
|
>>> node
|
|
<Call l.1 at 0x7effe1db8710>
|
|
>>> list(node.get_children())
|
|
[<Name.range l.1 at 0x7effe1db86a0>, <Const.int l.1 at 0x7effe1db8518>]
|
|
>>> list(node.get_children())[0].as_string()
|
|
'range'
|
|
"""
|
|
|
|
_other_fields = ("name",)
|
|
|
|
def __init__(self, name=None, lineno=None, col_offset=None, parent=None):
|
|
"""
|
|
:param name: The name that this node refers to.
|
|
:type name: str or None
|
|
|
|
:param lineno: The line that this node appears on in the source code.
|
|
:type lineno: int or None
|
|
|
|
:param col_offset: The column that this node appears on in the
|
|
source code.
|
|
:type col_offset: int or None
|
|
|
|
:param parent: The parent node in the syntax tree.
|
|
:type parent: NodeNG or None
|
|
"""
|
|
self.name = name
|
|
"""The name that this node refers to.
|
|
|
|
:type: str or None
|
|
"""
|
|
|
|
super().__init__(lineno, col_offset, parent)
|
|
|
|
def _get_name_nodes(self):
|
|
yield self
|
|
|
|
for child_node in self.get_children():
|
|
yield from child_node._get_name_nodes()
|
|
|
|
|
|
class Arguments(mixins.AssignTypeMixin, NodeNG):
|
|
"""Class representing an :class:`ast.arguments` node.
|
|
|
|
An :class:`Arguments` node represents that arguments in a
|
|
function definition.
|
|
|
|
>>> node = astroid.extract_node('def foo(bar): pass')
|
|
>>> node
|
|
<FunctionDef.foo l.1 at 0x7effe1db8198>
|
|
>>> node.args
|
|
<Arguments l.1 at 0x7effe1db82e8>
|
|
"""
|
|
|
|
# Python 3.4+ uses a different approach regarding annotations,
|
|
# each argument is a new class, _ast.arg, which exposes an
|
|
# 'annotation' attribute. In astroid though, arguments are exposed
|
|
# as is in the Arguments node and the only way to expose annotations
|
|
# is by using something similar with Python 3.3:
|
|
# - we expose 'varargannotation' and 'kwargannotation' of annotations
|
|
# of varargs and kwargs.
|
|
# - we expose 'annotation', a list with annotations for
|
|
# for each normal argument. If an argument doesn't have an
|
|
# annotation, its value will be None.
|
|
# pylint: disable=too-many-instance-attributes
|
|
_astroid_fields = (
|
|
"args",
|
|
"defaults",
|
|
"kwonlyargs",
|
|
"posonlyargs",
|
|
"posonlyargs_annotations",
|
|
"kw_defaults",
|
|
"annotations",
|
|
"varargannotation",
|
|
"kwargannotation",
|
|
"kwonlyargs_annotations",
|
|
"type_comment_args",
|
|
"type_comment_kwonlyargs",
|
|
"type_comment_posonlyargs",
|
|
)
|
|
varargannotation = None
|
|
"""The type annotation for the variable length arguments.
|
|
|
|
:type: NodeNG
|
|
"""
|
|
kwargannotation = None
|
|
"""The type annotation for the variable length keyword arguments.
|
|
|
|
:type: NodeNG
|
|
"""
|
|
|
|
_other_fields = ("vararg", "kwarg")
|
|
|
|
def __init__(self, vararg=None, kwarg=None, parent=None):
|
|
"""
|
|
:param vararg: The name of the variable length arguments.
|
|
:type vararg: str or None
|
|
|
|
:param kwarg: The name of the variable length keyword arguments.
|
|
:type kwarg: str or None
|
|
|
|
:param parent: The parent node in the syntax tree.
|
|
:type parent: NodeNG or None
|
|
"""
|
|
super().__init__(parent=parent)
|
|
self.vararg = vararg
|
|
"""The name of the variable length arguments.
|
|
|
|
:type: str or None
|
|
"""
|
|
|
|
self.kwarg = kwarg
|
|
"""The name of the variable length keyword arguments.
|
|
|
|
:type: str or None
|
|
"""
|
|
|
|
self.args = []
|
|
"""The names of the required arguments.
|
|
|
|
:type: list(AssignName)
|
|
"""
|
|
|
|
self.defaults = []
|
|
"""The default values for arguments that can be passed positionally.
|
|
|
|
:type: list(NodeNG)
|
|
"""
|
|
|
|
self.kwonlyargs = []
|
|
"""The keyword arguments that cannot be passed positionally.
|
|
|
|
:type: list(AssignName)
|
|
"""
|
|
|
|
self.posonlyargs = []
|
|
"""The arguments that can only be passed positionally.
|
|
|
|
:type: list(AssignName)
|
|
"""
|
|
|
|
self.kw_defaults = []
|
|
"""The default values for keyword arguments that cannot be passed positionally.
|
|
|
|
:type: list(NodeNG)
|
|
"""
|
|
|
|
self.annotations = []
|
|
"""The type annotations of arguments that can be passed positionally.
|
|
|
|
:type: list(NodeNG)
|
|
"""
|
|
|
|
self.posonlyargs_annotations = []
|
|
"""The type annotations of arguments that can only be passed positionally.
|
|
|
|
:type: list(NodeNG)
|
|
"""
|
|
|
|
self.kwonlyargs_annotations = []
|
|
"""The type annotations of arguments that cannot be passed positionally.
|
|
|
|
:type: list(NodeNG)
|
|
"""
|
|
|
|
self.type_comment_args = []
|
|
"""The type annotation, passed by a type comment, of each argument.
|
|
|
|
If an argument does not have a type comment,
|
|
the value for that argument will be None.
|
|
|
|
:type: list(NodeNG or None)
|
|
"""
|
|
|
|
self.type_comment_kwonlyargs = []
|
|
"""The type annotation, passed by a type comment, of each keyword only argument.
|
|
|
|
If an argument does not have a type comment,
|
|
the value for that argument will be None.
|
|
|
|
:type: list(NodeNG or None)
|
|
"""
|
|
|
|
self.type_comment_posonlyargs = []
|
|
"""The type annotation, passed by a type comment, of each positional argument.
|
|
|
|
If an argument does not have a type comment,
|
|
the value for that argument will be None.
|
|
|
|
:type: list(NodeNG or None)
|
|
"""
|
|
|
|
# pylint: disable=too-many-arguments
|
|
def postinit(
|
|
self,
|
|
args,
|
|
defaults,
|
|
kwonlyargs,
|
|
kw_defaults,
|
|
annotations,
|
|
posonlyargs=None,
|
|
kwonlyargs_annotations=None,
|
|
posonlyargs_annotations=None,
|
|
varargannotation=None,
|
|
kwargannotation=None,
|
|
type_comment_args=None,
|
|
type_comment_kwonlyargs=None,
|
|
type_comment_posonlyargs=None,
|
|
):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param args: The names of the required arguments.
|
|
:type args: list(AssignName)
|
|
|
|
:param defaults: The default values for arguments that can be passed
|
|
positionally.
|
|
:type defaults: list(NodeNG)
|
|
|
|
:param kwonlyargs: The keyword arguments that cannot be passed
|
|
positionally.
|
|
:type kwonlyargs: list(AssignName)
|
|
|
|
:param posonlyargs: The arguments that can only be passed
|
|
positionally.
|
|
:type kwonlyargs: list(AssignName)
|
|
|
|
:param kw_defaults: The default values for keyword arguments that
|
|
cannot be passed positionally.
|
|
:type kw_defaults: list(NodeNG)
|
|
|
|
:param annotations: The type annotations of arguments that can be
|
|
passed positionally.
|
|
:type annotations: list(NodeNG)
|
|
|
|
:param kwonlyargs_annotations: The type annotations of arguments that
|
|
cannot be passed positionally. This should always be passed in
|
|
Python 3.
|
|
:type kwonlyargs_annotations: list(NodeNG)
|
|
|
|
:param posonlyargs_annotations: The type annotations of arguments that
|
|
can only be passed positionally. This should always be passed in
|
|
Python 3.
|
|
:type posonlyargs_annotations: list(NodeNG)
|
|
|
|
:param varargannotation: The type annotation for the variable length
|
|
arguments.
|
|
:type varargannotation: NodeNG
|
|
|
|
:param kwargannotation: The type annotation for the variable length
|
|
keyword arguments.
|
|
:type kwargannotation: NodeNG
|
|
|
|
:param type_comment_args: The type annotation,
|
|
passed by a type comment, of each argument.
|
|
:type type_comment_args: list(NodeNG or None)
|
|
|
|
:param type_comment_args: The type annotation,
|
|
passed by a type comment, of each keyword only argument.
|
|
:type type_comment_args: list(NodeNG or None)
|
|
|
|
:param type_comment_args: The type annotation,
|
|
passed by a type comment, of each positional argument.
|
|
:type type_comment_args: list(NodeNG or None)
|
|
"""
|
|
self.args = args
|
|
self.defaults = defaults
|
|
self.kwonlyargs = kwonlyargs
|
|
self.posonlyargs = posonlyargs
|
|
self.kw_defaults = kw_defaults
|
|
self.annotations = annotations
|
|
self.kwonlyargs_annotations = kwonlyargs_annotations
|
|
self.posonlyargs_annotations = posonlyargs_annotations
|
|
self.varargannotation = varargannotation
|
|
self.kwargannotation = kwargannotation
|
|
self.type_comment_args = type_comment_args
|
|
self.type_comment_kwonlyargs = type_comment_kwonlyargs
|
|
self.type_comment_posonlyargs = type_comment_posonlyargs
|
|
|
|
# pylint: disable=too-many-arguments
|
|
|
|
def _infer_name(self, frame, name):
|
|
if self.parent is frame:
|
|
return name
|
|
return None
|
|
|
|
@decorators.cachedproperty
|
|
def fromlineno(self):
|
|
"""The first line that this node appears on in the source code.
|
|
|
|
:type: int or None
|
|
"""
|
|
lineno = super().fromlineno
|
|
return max(lineno, self.parent.fromlineno or 0)
|
|
|
|
@decorators.cachedproperty
|
|
def arguments(self):
|
|
"""Get all the arguments for this node, including positional only and positional and keyword"""
|
|
return list(itertools.chain((self.posonlyargs or ()), self.args or ()))
|
|
|
|
def format_args(self):
|
|
"""Get the arguments formatted as string.
|
|
|
|
:returns: The formatted arguments.
|
|
:rtype: str
|
|
"""
|
|
result = []
|
|
positional_only_defaults = []
|
|
positional_or_keyword_defaults = self.defaults
|
|
if self.defaults:
|
|
args = self.args or []
|
|
positional_or_keyword_defaults = self.defaults[-len(args) :]
|
|
positional_only_defaults = self.defaults[: len(self.defaults) - len(args)]
|
|
|
|
if self.posonlyargs:
|
|
result.append(
|
|
_format_args(
|
|
self.posonlyargs,
|
|
positional_only_defaults,
|
|
self.posonlyargs_annotations,
|
|
)
|
|
)
|
|
result.append("/")
|
|
if self.args:
|
|
result.append(
|
|
_format_args(
|
|
self.args,
|
|
positional_or_keyword_defaults,
|
|
getattr(self, "annotations", None),
|
|
)
|
|
)
|
|
if self.vararg:
|
|
result.append("*%s" % self.vararg)
|
|
if self.kwonlyargs:
|
|
if not self.vararg:
|
|
result.append("*")
|
|
result.append(
|
|
_format_args(
|
|
self.kwonlyargs, self.kw_defaults, self.kwonlyargs_annotations
|
|
)
|
|
)
|
|
if self.kwarg:
|
|
result.append("**%s" % self.kwarg)
|
|
return ", ".join(result)
|
|
|
|
def default_value(self, argname):
|
|
"""Get the default value for an argument.
|
|
|
|
:param argname: The name of the argument to get the default value for.
|
|
:type argname: str
|
|
|
|
:raises NoDefault: If there is no default value defined for the
|
|
given argument.
|
|
"""
|
|
args = self.arguments
|
|
index = _find_arg(argname, args)[0]
|
|
if index is not None:
|
|
idx = index - (len(args) - len(self.defaults))
|
|
if idx >= 0:
|
|
return self.defaults[idx]
|
|
index = _find_arg(argname, self.kwonlyargs)[0]
|
|
if index is not None and self.kw_defaults[index] is not None:
|
|
return self.kw_defaults[index]
|
|
raise exceptions.NoDefault(func=self.parent, name=argname)
|
|
|
|
def is_argument(self, name):
|
|
"""Check if the given name is defined in the arguments.
|
|
|
|
:param name: The name to check for.
|
|
:type name: str
|
|
|
|
:returns: True if the given name is defined in the arguments,
|
|
False otherwise.
|
|
:rtype: bool
|
|
"""
|
|
if name == self.vararg:
|
|
return True
|
|
if name == self.kwarg:
|
|
return True
|
|
return (
|
|
self.find_argname(name, rec=True)[1] is not None
|
|
or self.kwonlyargs
|
|
and _find_arg(name, self.kwonlyargs, rec=True)[1] is not None
|
|
)
|
|
|
|
def find_argname(self, argname, rec=False):
|
|
"""Get the index and :class:`AssignName` node for given name.
|
|
|
|
:param argname: The name of the argument to search for.
|
|
:type argname: str
|
|
|
|
:param rec: Whether or not to include arguments in unpacked tuples
|
|
in the search.
|
|
:type rec: bool
|
|
|
|
:returns: The index and node for the argument.
|
|
:rtype: tuple(str or None, AssignName or None)
|
|
"""
|
|
if self.arguments:
|
|
return _find_arg(argname, self.arguments, rec)
|
|
return None, None
|
|
|
|
def get_children(self):
|
|
yield from self.posonlyargs or ()
|
|
|
|
for elt in self.posonlyargs_annotations:
|
|
if elt is not None:
|
|
yield elt
|
|
|
|
yield from self.args or ()
|
|
|
|
yield from self.defaults
|
|
yield from self.kwonlyargs
|
|
|
|
for elt in self.kw_defaults:
|
|
if elt is not None:
|
|
yield elt
|
|
|
|
for elt in self.annotations:
|
|
if elt is not None:
|
|
yield elt
|
|
|
|
if self.varargannotation is not None:
|
|
yield self.varargannotation
|
|
|
|
if self.kwargannotation is not None:
|
|
yield self.kwargannotation
|
|
|
|
for elt in self.kwonlyargs_annotations:
|
|
if elt is not None:
|
|
yield elt
|
|
|
|
|
|
def _find_arg(argname, args, rec=False):
|
|
for i, arg in enumerate(args):
|
|
if isinstance(arg, Tuple):
|
|
if rec:
|
|
found = _find_arg(argname, arg.elts)
|
|
if found[0] is not None:
|
|
return found
|
|
elif arg.name == argname:
|
|
return i, arg
|
|
return None, None
|
|
|
|
|
|
def _format_args(args, defaults=None, annotations=None):
|
|
values = []
|
|
if args is None:
|
|
return ""
|
|
if annotations is None:
|
|
annotations = []
|
|
if defaults is not None:
|
|
default_offset = len(args) - len(defaults)
|
|
packed = itertools.zip_longest(args, annotations)
|
|
for i, (arg, annotation) in enumerate(packed):
|
|
if isinstance(arg, Tuple):
|
|
values.append("(%s)" % _format_args(arg.elts))
|
|
else:
|
|
argname = arg.name
|
|
default_sep = "="
|
|
if annotation is not None:
|
|
argname += ": " + annotation.as_string()
|
|
default_sep = " = "
|
|
values.append(argname)
|
|
|
|
if defaults is not None and i >= default_offset:
|
|
if defaults[i - default_offset] is not None:
|
|
values[-1] += default_sep + defaults[i - default_offset].as_string()
|
|
return ", ".join(values)
|
|
|
|
|
|
class AssignAttr(mixins.ParentAssignTypeMixin, NodeNG):
|
|
"""Variation of :class:`ast.Assign` representing assignment to an attribute.
|
|
|
|
>>> node = astroid.extract_node('self.attribute = range(10)')
|
|
>>> node
|
|
<Assign l.1 at 0x7effe1d521d0>
|
|
>>> list(node.get_children())
|
|
[<AssignAttr.attribute l.1 at 0x7effe1d52320>, <Call l.1 at 0x7effe1d522e8>]
|
|
>>> list(node.get_children())[0].as_string()
|
|
'self.attribute'
|
|
"""
|
|
|
|
_astroid_fields = ("expr",)
|
|
_other_fields = ("attrname",)
|
|
expr = None
|
|
"""What has the attribute that is being assigned to.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
|
|
def __init__(self, attrname=None, lineno=None, col_offset=None, parent=None):
|
|
"""
|
|
:param attrname: The name of the attribute being assigned to.
|
|
:type attrname: str or None
|
|
|
|
:param lineno: The line that this node appears on in the source code.
|
|
:type lineno: int or None
|
|
|
|
:param col_offset: The column that this node appears on in the
|
|
source code.
|
|
:type col_offset: int or None
|
|
|
|
:param parent: The parent node in the syntax tree.
|
|
:type parent: NodeNG or None
|
|
"""
|
|
self.attrname = attrname
|
|
"""The name of the attribute being assigned to.
|
|
|
|
:type: str or None
|
|
"""
|
|
|
|
super().__init__(lineno, col_offset, parent)
|
|
|
|
def postinit(self, expr=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param expr: What has the attribute that is being assigned to.
|
|
:type expr: NodeNG or None
|
|
"""
|
|
self.expr = expr
|
|
|
|
def get_children(self):
|
|
yield self.expr
|
|
|
|
|
|
class Assert(Statement):
|
|
"""Class representing an :class:`ast.Assert` node.
|
|
|
|
An :class:`Assert` node represents an assert statement.
|
|
|
|
>>> node = astroid.extract_node('assert len(things) == 10, "Not enough things"')
|
|
>>> node
|
|
<Assert l.1 at 0x7effe1d527b8>
|
|
"""
|
|
|
|
_astroid_fields = ("test", "fail")
|
|
test = None
|
|
"""The test that passes or fails the assertion.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
fail = None
|
|
"""The message shown when the assertion fails.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
|
|
def postinit(self, test=None, fail=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param test: The test that passes or fails the assertion.
|
|
:type test: NodeNG or None
|
|
|
|
:param fail: The message shown when the assertion fails.
|
|
:type fail: NodeNG or None
|
|
"""
|
|
self.fail = fail
|
|
self.test = test
|
|
|
|
def get_children(self):
|
|
yield self.test
|
|
|
|
if self.fail is not None:
|
|
yield self.fail
|
|
|
|
|
|
class Assign(mixins.AssignTypeMixin, Statement):
|
|
"""Class representing an :class:`ast.Assign` node.
|
|
|
|
An :class:`Assign` is a statement where something is explicitly
|
|
asssigned to.
|
|
|
|
>>> node = astroid.extract_node('variable = range(10)')
|
|
>>> node
|
|
<Assign l.1 at 0x7effe1db8550>
|
|
"""
|
|
|
|
_astroid_fields = ("targets", "value")
|
|
_other_other_fields = ("type_annotation",)
|
|
targets = None
|
|
"""What is being assigned to.
|
|
|
|
:type: list(NodeNG) or None
|
|
"""
|
|
value = None
|
|
"""The value being assigned to the variables.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
type_annotation = None
|
|
"""If present, this will contain the type annotation passed by a type comment
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
|
|
def postinit(self, targets=None, value=None, type_annotation=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param targets: What is being assigned to.
|
|
:type targets: list(NodeNG) or None
|
|
|
|
:param value: The value being assigned to the variables.
|
|
:type: NodeNG or None
|
|
"""
|
|
self.targets = targets
|
|
self.value = value
|
|
self.type_annotation = type_annotation
|
|
|
|
def get_children(self):
|
|
yield from self.targets
|
|
|
|
yield self.value
|
|
|
|
@decorators.cached
|
|
def _get_assign_nodes(self):
|
|
return [self] + list(self.value._get_assign_nodes())
|
|
|
|
def _get_yield_nodes_skip_lambdas(self):
|
|
yield from self.value._get_yield_nodes_skip_lambdas()
|
|
|
|
|
|
class AnnAssign(mixins.AssignTypeMixin, Statement):
|
|
"""Class representing an :class:`ast.AnnAssign` node.
|
|
|
|
An :class:`AnnAssign` is an assignment with a type annotation.
|
|
|
|
>>> node = astroid.extract_node('variable: List[int] = range(10)')
|
|
>>> node
|
|
<AnnAssign l.1 at 0x7effe1d4c630>
|
|
"""
|
|
|
|
_astroid_fields = ("target", "annotation", "value")
|
|
_other_fields = ("simple",)
|
|
target = None
|
|
"""What is being assigned to.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
annotation = None
|
|
"""The type annotation of what is being assigned to.
|
|
|
|
:type: NodeNG
|
|
"""
|
|
value = None
|
|
"""The value being assigned to the variables.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
simple = None
|
|
"""Whether :attr:`target` is a pure name or a complex statement.
|
|
|
|
:type: int
|
|
"""
|
|
|
|
def postinit(self, target, annotation, simple, value=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param target: What is being assigned to.
|
|
:type target: NodeNG
|
|
|
|
:param annotation: The type annotation of what is being assigned to.
|
|
:type: NodeNG
|
|
|
|
:param simple: Whether :attr:`target` is a pure name
|
|
or a complex statement.
|
|
:type simple: int
|
|
|
|
:param value: The value being assigned to the variables.
|
|
:type: NodeNG or None
|
|
"""
|
|
self.target = target
|
|
self.annotation = annotation
|
|
self.value = value
|
|
self.simple = simple
|
|
|
|
def get_children(self):
|
|
yield self.target
|
|
yield self.annotation
|
|
|
|
if self.value is not None:
|
|
yield self.value
|
|
|
|
|
|
class AugAssign(mixins.AssignTypeMixin, Statement):
|
|
"""Class representing an :class:`ast.AugAssign` node.
|
|
|
|
An :class:`AugAssign` is an assignment paired with an operator.
|
|
|
|
>>> node = astroid.extract_node('variable += 1')
|
|
>>> node
|
|
<AugAssign l.1 at 0x7effe1db4d68>
|
|
"""
|
|
|
|
_astroid_fields = ("target", "value")
|
|
_other_fields = ("op",)
|
|
target = None
|
|
"""What is being assigned to.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
value = None
|
|
"""The value being assigned to the variable.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
|
|
def __init__(self, op=None, lineno=None, col_offset=None, parent=None):
|
|
"""
|
|
:param op: The operator that is being combined with the assignment.
|
|
This includes the equals sign.
|
|
:type op: str or None
|
|
|
|
:param lineno: The line that this node appears on in the source code.
|
|
:type lineno: int or None
|
|
|
|
:param col_offset: The column that this node appears on in the
|
|
source code.
|
|
:type col_offset: int or None
|
|
|
|
:param parent: The parent node in the syntax tree.
|
|
:type parent: NodeNG or None
|
|
"""
|
|
self.op = op
|
|
"""The operator that is being combined with the assignment.
|
|
|
|
This includes the equals sign.
|
|
|
|
:type: str or None
|
|
"""
|
|
|
|
super().__init__(lineno, col_offset, parent)
|
|
|
|
def postinit(self, target=None, value=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param target: What is being assigned to.
|
|
:type target: NodeNG or None
|
|
|
|
:param value: The value being assigned to the variable.
|
|
:type: NodeNG or None
|
|
"""
|
|
self.target = target
|
|
self.value = value
|
|
|
|
# This is set by inference.py
|
|
def _infer_augassign(self, context=None):
|
|
raise NotImplementedError
|
|
|
|
def type_errors(self, context=None):
|
|
"""Get a list of type errors which can occur during inference.
|
|
|
|
Each TypeError is represented by a :class:`BadBinaryOperationMessage` ,
|
|
which holds the original exception.
|
|
|
|
:returns: The list of possible type errors.
|
|
:rtype: list(BadBinaryOperationMessage)
|
|
"""
|
|
try:
|
|
results = self._infer_augassign(context=context)
|
|
return [
|
|
result
|
|
for result in results
|
|
if isinstance(result, util.BadBinaryOperationMessage)
|
|
]
|
|
except exceptions.InferenceError:
|
|
return []
|
|
|
|
def get_children(self):
|
|
yield self.target
|
|
yield self.value
|
|
|
|
|
|
class Repr(NodeNG):
|
|
"""Class representing an :class:`ast.Repr` node.
|
|
|
|
A :class:`Repr` node represents the backtick syntax,
|
|
which is a deprecated alias for :func:`repr` removed in Python 3.
|
|
|
|
>>> node = astroid.extract_node('`variable`')
|
|
>>> node
|
|
<Repr l.1 at 0x7fa0951d75d0>
|
|
"""
|
|
|
|
_astroid_fields = ("value",)
|
|
value = None
|
|
"""What is having :func:`repr` called on it.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
|
|
def postinit(self, value=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param value: What is having :func:`repr` called on it.
|
|
:type value: NodeNG or None
|
|
"""
|
|
self.value = value
|
|
|
|
|
|
class BinOp(NodeNG):
|
|
"""Class representing an :class:`ast.BinOp` node.
|
|
|
|
A :class:`BinOp` node is an application of a binary operator.
|
|
|
|
>>> node = astroid.extract_node('a + b')
|
|
>>> node
|
|
<BinOp l.1 at 0x7f23b2e8cfd0>
|
|
"""
|
|
|
|
_astroid_fields = ("left", "right")
|
|
_other_fields = ("op",)
|
|
left = None
|
|
"""What is being applied to the operator on the left side.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
right = None
|
|
"""What is being applied to the operator on the right side.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
|
|
def __init__(self, op=None, lineno=None, col_offset=None, parent=None):
|
|
"""
|
|
:param op: The operator.
|
|
:type: str or None
|
|
|
|
:param lineno: The line that this node appears on in the source code.
|
|
:type lineno: int or None
|
|
|
|
:param col_offset: The column that this node appears on in the
|
|
source code.
|
|
:type col_offset: int or None
|
|
|
|
:param parent: The parent node in the syntax tree.
|
|
:type parent: NodeNG or None
|
|
"""
|
|
self.op = op
|
|
"""The operator.
|
|
|
|
:type: str or None
|
|
"""
|
|
|
|
super().__init__(lineno, col_offset, parent)
|
|
|
|
def postinit(self, left=None, right=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param left: What is being applied to the operator on the left side.
|
|
:type left: NodeNG or None
|
|
|
|
:param right: What is being applied to the operator on the right side.
|
|
:type right: NodeNG or None
|
|
"""
|
|
self.left = left
|
|
self.right = right
|
|
|
|
# This is set by inference.py
|
|
def _infer_binop(self, context=None):
|
|
raise NotImplementedError
|
|
|
|
def type_errors(self, context=None):
|
|
"""Get a list of type errors which can occur during inference.
|
|
|
|
Each TypeError is represented by a :class:`BadBinaryOperationMessage`,
|
|
which holds the original exception.
|
|
|
|
:returns: The list of possible type errors.
|
|
:rtype: list(BadBinaryOperationMessage)
|
|
"""
|
|
try:
|
|
results = self._infer_binop(context=context)
|
|
return [
|
|
result
|
|
for result in results
|
|
if isinstance(result, util.BadBinaryOperationMessage)
|
|
]
|
|
except exceptions.InferenceError:
|
|
return []
|
|
|
|
def get_children(self):
|
|
yield self.left
|
|
yield self.right
|
|
|
|
def op_precedence(self):
|
|
return OP_PRECEDENCE[self.op]
|
|
|
|
def op_left_associative(self):
|
|
# 2**3**4 == 2**(3**4)
|
|
return self.op != "**"
|
|
|
|
|
|
class BoolOp(NodeNG):
|
|
"""Class representing an :class:`ast.BoolOp` node.
|
|
|
|
A :class:`BoolOp` is an application of a boolean operator.
|
|
|
|
>>> node = astroid.extract_node('a and b')
|
|
>>> node
|
|
<BinOp l.1 at 0x7f23b2e71c50>
|
|
"""
|
|
|
|
_astroid_fields = ("values",)
|
|
_other_fields = ("op",)
|
|
values = None
|
|
"""The values being applied to the operator.
|
|
|
|
:type: list(NodeNG) or None
|
|
"""
|
|
|
|
def __init__(self, op=None, lineno=None, col_offset=None, parent=None):
|
|
"""
|
|
:param op: The operator.
|
|
:type: str or None
|
|
|
|
:param lineno: The line that this node appears on in the source code.
|
|
:type lineno: int or None
|
|
|
|
:param col_offset: The column that this node appears on in the
|
|
source code.
|
|
:type col_offset: int or None
|
|
|
|
:param parent: The parent node in the syntax tree.
|
|
:type parent: NodeNG or None
|
|
"""
|
|
self.op = op
|
|
"""The operator.
|
|
|
|
:type: str or None
|
|
"""
|
|
|
|
super().__init__(lineno, col_offset, parent)
|
|
|
|
def postinit(self, values=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param values: The values being applied to the operator.
|
|
:type values: list(NodeNG) or None
|
|
"""
|
|
self.values = values
|
|
|
|
def get_children(self):
|
|
yield from self.values
|
|
|
|
def op_precedence(self):
|
|
return OP_PRECEDENCE[self.op]
|
|
|
|
|
|
class Break(mixins.NoChildrenMixin, Statement):
|
|
"""Class representing an :class:`ast.Break` node.
|
|
|
|
>>> node = astroid.extract_node('break')
|
|
>>> node
|
|
<Break l.1 at 0x7f23b2e9e5c0>
|
|
"""
|
|
|
|
|
|
class Call(NodeNG):
|
|
"""Class representing an :class:`ast.Call` node.
|
|
|
|
A :class:`Call` node is a call to a function, method, etc.
|
|
|
|
>>> node = astroid.extract_node('function()')
|
|
>>> node
|
|
<Call l.1 at 0x7f23b2e71eb8>
|
|
"""
|
|
|
|
_astroid_fields = ("func", "args", "keywords")
|
|
func = None
|
|
"""What is being called.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
args = None
|
|
"""The positional arguments being given to the call.
|
|
|
|
:type: list(NodeNG) or None
|
|
"""
|
|
keywords = None
|
|
"""The keyword arguments being given to the call.
|
|
|
|
:type: list(NodeNG) or None
|
|
"""
|
|
|
|
def postinit(self, func=None, args=None, keywords=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param func: What is being called.
|
|
:type func: NodeNG or None
|
|
|
|
:param args: The positional arguments being given to the call.
|
|
:type args: list(NodeNG) or None
|
|
|
|
:param keywords: The keyword arguments being given to the call.
|
|
:type keywords: list(NodeNG) or None
|
|
"""
|
|
self.func = func
|
|
self.args = args
|
|
self.keywords = keywords
|
|
|
|
@property
|
|
def starargs(self):
|
|
"""The positional arguments that unpack something.
|
|
|
|
:type: list(Starred)
|
|
"""
|
|
args = self.args or []
|
|
return [arg for arg in args if isinstance(arg, Starred)]
|
|
|
|
@property
|
|
def kwargs(self):
|
|
"""The keyword arguments that unpack something.
|
|
|
|
:type: list(Keyword)
|
|
"""
|
|
keywords = self.keywords or []
|
|
return [keyword for keyword in keywords if keyword.arg is None]
|
|
|
|
def get_children(self):
|
|
yield self.func
|
|
|
|
yield from self.args
|
|
|
|
yield from self.keywords or ()
|
|
|
|
|
|
class Compare(NodeNG):
|
|
"""Class representing an :class:`ast.Compare` node.
|
|
|
|
A :class:`Compare` node indicates a comparison.
|
|
|
|
>>> node = astroid.extract_node('a <= b <= c')
|
|
>>> node
|
|
<Compare l.1 at 0x7f23b2e9e6d8>
|
|
>>> node.ops
|
|
[('<=', <Name.b l.1 at 0x7f23b2e9e2b0>), ('<=', <Name.c l.1 at 0x7f23b2e9e390>)]
|
|
"""
|
|
|
|
_astroid_fields = ("left", "ops")
|
|
left = None
|
|
"""The value at the left being applied to a comparison operator.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
ops = None
|
|
"""The remainder of the operators and their relevant right hand value.
|
|
|
|
:type: list(tuple(str, NodeNG)) or None
|
|
"""
|
|
|
|
def postinit(self, left=None, ops=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param left: The value at the left being applied to a comparison
|
|
operator.
|
|
:type left: NodeNG or None
|
|
|
|
:param ops: The remainder of the operators
|
|
and their relevant right hand value.
|
|
:type ops: list(tuple(str, NodeNG)) or None
|
|
"""
|
|
self.left = left
|
|
self.ops = ops
|
|
|
|
def get_children(self):
|
|
"""Get the child nodes below this node.
|
|
|
|
Overridden to handle the tuple fields and skip returning the operator
|
|
strings.
|
|
|
|
:returns: The children.
|
|
:rtype: iterable(NodeNG)
|
|
"""
|
|
yield self.left
|
|
for _, comparator in self.ops:
|
|
yield comparator # we don't want the 'op'
|
|
|
|
def last_child(self):
|
|
"""An optimized version of list(get_children())[-1]
|
|
|
|
:returns: The last child.
|
|
:rtype: NodeNG
|
|
"""
|
|
# XXX maybe if self.ops:
|
|
return self.ops[-1][1]
|
|
# return self.left
|
|
|
|
|
|
class Comprehension(NodeNG):
|
|
"""Class representing an :class:`ast.comprehension` node.
|
|
|
|
A :class:`Comprehension` indicates the loop inside any type of
|
|
comprehension including generator expressions.
|
|
|
|
>>> node = astroid.extract_node('[x for x in some_values]')
|
|
>>> list(node.get_children())
|
|
[<Name.x l.1 at 0x7f23b2e352b0>, <Comprehension l.1 at 0x7f23b2e35320>]
|
|
>>> list(node.get_children())[1].as_string()
|
|
'for x in some_values'
|
|
"""
|
|
|
|
_astroid_fields = ("target", "iter", "ifs")
|
|
_other_fields = ("is_async",)
|
|
target = None
|
|
"""What is assigned to by the comprehension.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
iter = None
|
|
"""What is iterated over by the comprehension.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
ifs = None
|
|
"""The contents of any if statements that filter the comprehension.
|
|
|
|
:type: list(NodeNG) or None
|
|
"""
|
|
is_async = None
|
|
"""Whether this is an asynchronous comprehension or not.
|
|
|
|
:type: bool or None
|
|
"""
|
|
|
|
def __init__(self, parent=None):
|
|
"""
|
|
:param parent: The parent node in the syntax tree.
|
|
:type parent: NodeNG or None
|
|
"""
|
|
super().__init__()
|
|
self.parent = parent
|
|
|
|
# pylint: disable=redefined-builtin; same name as builtin ast module.
|
|
def postinit(self, target=None, iter=None, ifs=None, is_async=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param target: What is assigned to by the comprehension.
|
|
:type target: NodeNG or None
|
|
|
|
:param iter: What is iterated over by the comprehension.
|
|
:type iter: NodeNG or None
|
|
|
|
:param ifs: The contents of any if statements that filter
|
|
the comprehension.
|
|
:type ifs: list(NodeNG) or None
|
|
|
|
:param is_async: Whether this is an asynchronous comprehension or not.
|
|
:type: bool or None
|
|
"""
|
|
self.target = target
|
|
self.iter = iter
|
|
self.ifs = ifs
|
|
self.is_async = is_async
|
|
|
|
optional_assign = True
|
|
"""Whether this node optionally assigns a variable.
|
|
|
|
:type: bool
|
|
"""
|
|
|
|
def assign_type(self):
|
|
"""The type of assignment that this node performs.
|
|
|
|
:returns: The assignment type.
|
|
:rtype: NodeNG
|
|
"""
|
|
return self
|
|
|
|
def _get_filtered_stmts(self, lookup_node, node, stmts, mystmt):
|
|
"""method used in filter_stmts"""
|
|
if self is mystmt:
|
|
if isinstance(lookup_node, (Const, Name)):
|
|
return [lookup_node], True
|
|
|
|
elif self.statement() is mystmt:
|
|
# original node's statement is the assignment, only keeps
|
|
# current node (gen exp, list comp)
|
|
|
|
return [node], True
|
|
|
|
return stmts, False
|
|
|
|
def get_children(self):
|
|
yield self.target
|
|
yield self.iter
|
|
|
|
yield from self.ifs
|
|
|
|
|
|
class Const(mixins.NoChildrenMixin, NodeNG, bases.Instance):
|
|
"""Class representing any constant including num, str, bool, None, bytes.
|
|
|
|
>>> node = astroid.extract_node('(5, "This is a string.", True, None, b"bytes")')
|
|
>>> node
|
|
<Tuple.tuple l.1 at 0x7f23b2e358d0>
|
|
>>> list(node.get_children())
|
|
[<Const.int l.1 at 0x7f23b2e35940>,
|
|
<Const.str l.1 at 0x7f23b2e35978>,
|
|
<Const.bool l.1 at 0x7f23b2e359b0>,
|
|
<Const.NoneType l.1 at 0x7f23b2e359e8>,
|
|
<Const.bytes l.1 at 0x7f23b2e35a20>]
|
|
"""
|
|
|
|
_other_fields = ("value",)
|
|
|
|
def __init__(self, value, lineno=None, col_offset=None, parent=None):
|
|
"""
|
|
:param value: The value that the constant represents.
|
|
:type value: object
|
|
|
|
:param lineno: The line that this node appears on in the source code.
|
|
:type lineno: int or None
|
|
|
|
:param col_offset: The column that this node appears on in the
|
|
source code.
|
|
:type col_offset: int or None
|
|
|
|
:param parent: The parent node in the syntax tree.
|
|
:type parent: NodeNG or None
|
|
"""
|
|
self.value = value
|
|
"""The value that the constant represents.
|
|
|
|
:type: object
|
|
"""
|
|
|
|
super().__init__(lineno, col_offset, parent)
|
|
|
|
def __getattr__(self, name):
|
|
# This is needed because of Proxy's __getattr__ method.
|
|
# Calling object.__new__ on this class without calling
|
|
# __init__ would result in an infinite loop otherwise
|
|
# since __getattr__ is called when an attribute doesn't
|
|
# exist and self._proxied indirectly calls self.value
|
|
# and Proxy __getattr__ calls self.value
|
|
if name == "value":
|
|
raise AttributeError
|
|
return super().__getattr__(name)
|
|
|
|
def getitem(self, index, context=None):
|
|
"""Get an item from this node if subscriptable.
|
|
|
|
:param index: The node to use as a subscript index.
|
|
:type index: Const or Slice
|
|
|
|
:raises AstroidTypeError: When the given index cannot be used as a
|
|
subscript index, or if this node is not subscriptable.
|
|
"""
|
|
if isinstance(index, Const):
|
|
index_value = index.value
|
|
elif isinstance(index, Slice):
|
|
index_value = _infer_slice(index, context=context)
|
|
|
|
else:
|
|
raise exceptions.AstroidTypeError(
|
|
"Could not use type {} as subscript index".format(type(index))
|
|
)
|
|
|
|
try:
|
|
if isinstance(self.value, (str, bytes)):
|
|
return Const(self.value[index_value])
|
|
except IndexError as exc:
|
|
raise exceptions.AstroidIndexError(
|
|
message="Index {index!r} out of range",
|
|
node=self,
|
|
index=index,
|
|
context=context,
|
|
) from exc
|
|
except TypeError as exc:
|
|
raise exceptions.AstroidTypeError(
|
|
message="Type error {error!r}", node=self, index=index, context=context
|
|
) from exc
|
|
|
|
raise exceptions.AstroidTypeError("%r (value=%s)" % (self, self.value))
|
|
|
|
def has_dynamic_getattr(self):
|
|
"""Check if the node has a custom __getattr__ or __getattribute__.
|
|
|
|
:returns: True if the class has a custom
|
|
__getattr__ or __getattribute__, False otherwise.
|
|
For a :class:`Const` this is always ``False``.
|
|
:rtype: bool
|
|
"""
|
|
return False
|
|
|
|
def itered(self):
|
|
"""An iterator over the elements this node contains.
|
|
|
|
:returns: The contents of this node.
|
|
:rtype: iterable(Const)
|
|
|
|
:raises TypeError: If this node does not represent something that is iterable.
|
|
"""
|
|
if isinstance(self.value, str):
|
|
return [const_factory(elem) for elem in self.value]
|
|
raise TypeError("Cannot iterate over type {!r}".format(type(self.value)))
|
|
|
|
def pytype(self):
|
|
"""Get the name of the type that this node represents.
|
|
|
|
:returns: The name of the type.
|
|
:rtype: str
|
|
"""
|
|
return self._proxied.qname()
|
|
|
|
def bool_value(self, context=None):
|
|
"""Determine the boolean value of this node.
|
|
|
|
:returns: The boolean value of this node.
|
|
:rtype: bool
|
|
"""
|
|
return bool(self.value)
|
|
|
|
|
|
class Continue(mixins.NoChildrenMixin, Statement):
|
|
"""Class representing an :class:`ast.Continue` node.
|
|
|
|
>>> node = astroid.extract_node('continue')
|
|
>>> node
|
|
<Continue l.1 at 0x7f23b2e35588>
|
|
"""
|
|
|
|
|
|
class Decorators(NodeNG):
|
|
"""A node representing a list of decorators.
|
|
|
|
A :class:`Decorators` is the decorators that are applied to
|
|
a method or function.
|
|
|
|
>>> node = astroid.extract_node('''
|
|
@property
|
|
def my_property(self):
|
|
return 3
|
|
''')
|
|
>>> node
|
|
<FunctionDef.my_property l.2 at 0x7f23b2e35d30>
|
|
>>> list(node.get_children())[0]
|
|
<Decorators l.1 at 0x7f23b2e35d68>
|
|
"""
|
|
|
|
_astroid_fields = ("nodes",)
|
|
nodes = None
|
|
"""The decorators that this node contains.
|
|
|
|
:type: list(Name or Call) or None
|
|
"""
|
|
|
|
def postinit(self, nodes):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param nodes: The decorators that this node contains.
|
|
:type nodes: list(Name or Call)
|
|
"""
|
|
self.nodes = nodes
|
|
|
|
def scope(self):
|
|
"""The first parent node defining a new scope.
|
|
|
|
:returns: The first parent scope node.
|
|
:rtype: Module or FunctionDef or ClassDef or Lambda or GenExpr
|
|
"""
|
|
# skip the function node to go directly to the upper level scope
|
|
return self.parent.parent.scope()
|
|
|
|
def get_children(self):
|
|
yield from self.nodes
|
|
|
|
|
|
class DelAttr(mixins.ParentAssignTypeMixin, NodeNG):
|
|
"""Variation of :class:`ast.Delete` representing deletion of an attribute.
|
|
|
|
>>> node = astroid.extract_node('del self.attr')
|
|
>>> node
|
|
<Delete l.1 at 0x7f23b2e35f60>
|
|
>>> list(node.get_children())[0]
|
|
<DelAttr.attr l.1 at 0x7f23b2e411d0>
|
|
"""
|
|
|
|
_astroid_fields = ("expr",)
|
|
_other_fields = ("attrname",)
|
|
expr = None
|
|
"""The name that this node represents.
|
|
|
|
:type: Name or None
|
|
"""
|
|
|
|
def __init__(self, attrname=None, lineno=None, col_offset=None, parent=None):
|
|
"""
|
|
:param attrname: The name of the attribute that is being deleted.
|
|
:type attrname: str or None
|
|
|
|
:param lineno: The line that this node appears on in the source code.
|
|
:type lineno: int or None
|
|
|
|
:param col_offset: The column that this node appears on in the
|
|
source code.
|
|
:type col_offset: int or None
|
|
|
|
:param parent: The parent node in the syntax tree.
|
|
:type parent: NodeNG or None
|
|
"""
|
|
self.attrname = attrname
|
|
"""The name of the attribute that is being deleted.
|
|
|
|
:type: str or None
|
|
"""
|
|
|
|
super().__init__(lineno, col_offset, parent)
|
|
|
|
def postinit(self, expr=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param expr: The name that this node represents.
|
|
:type expr: Name or None
|
|
"""
|
|
self.expr = expr
|
|
|
|
def get_children(self):
|
|
yield self.expr
|
|
|
|
|
|
class Delete(mixins.AssignTypeMixin, Statement):
|
|
"""Class representing an :class:`ast.Delete` node.
|
|
|
|
A :class:`Delete` is a ``del`` statement this is deleting something.
|
|
|
|
>>> node = astroid.extract_node('del self.attr')
|
|
>>> node
|
|
<Delete l.1 at 0x7f23b2e35f60>
|
|
"""
|
|
|
|
_astroid_fields = ("targets",)
|
|
targets = None
|
|
"""What is being deleted.
|
|
|
|
:type: list(NodeNG) or None
|
|
"""
|
|
|
|
def postinit(self, targets=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param targets: What is being deleted.
|
|
:type targets: list(NodeNG) or None
|
|
"""
|
|
self.targets = targets
|
|
|
|
def get_children(self):
|
|
yield from self.targets
|
|
|
|
|
|
class Dict(NodeNG, bases.Instance):
|
|
"""Class representing an :class:`ast.Dict` node.
|
|
|
|
A :class:`Dict` is a dictionary that is created with ``{}`` syntax.
|
|
|
|
>>> node = astroid.extract_node('{1: "1"}')
|
|
>>> node
|
|
<Dict.dict l.1 at 0x7f23b2e35cc0>
|
|
"""
|
|
|
|
_astroid_fields = ("items",)
|
|
|
|
def __init__(self, lineno=None, col_offset=None, parent=None):
|
|
"""
|
|
:param lineno: The line that this node appears on in the source code.
|
|
:type lineno: int or None
|
|
|
|
:param col_offset: The column that this node appears on in the
|
|
source code.
|
|
:type col_offset: int or None
|
|
|
|
:param parent: The parent node in the syntax tree.
|
|
:type parent: NodeNG or None
|
|
"""
|
|
self.items = []
|
|
"""The key-value pairs contained in the dictionary.
|
|
|
|
:type: list(tuple(NodeNG, NodeNG))
|
|
"""
|
|
|
|
super().__init__(lineno, col_offset, parent)
|
|
|
|
def postinit(self, items):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param items: The key-value pairs contained in the dictionary.
|
|
:type items: list(tuple(NodeNG, NodeNG))
|
|
"""
|
|
self.items = items
|
|
|
|
@classmethod
|
|
def from_elements(cls, items=None):
|
|
"""Create a :class:`Dict` of constants from a live dictionary.
|
|
|
|
:param items: The items to store in the node.
|
|
:type items: dict
|
|
|
|
:returns: The created dictionary node.
|
|
:rtype: Dict
|
|
"""
|
|
node = cls()
|
|
if items is None:
|
|
node.items = []
|
|
else:
|
|
node.items = [
|
|
(const_factory(k), const_factory(v) if _is_const(v) else v)
|
|
for k, v in items.items()
|
|
# The keys need to be constants
|
|
if _is_const(k)
|
|
]
|
|
return node
|
|
|
|
def pytype(self):
|
|
"""Get the name of the type that this node represents.
|
|
|
|
:returns: The name of the type.
|
|
:rtype: str
|
|
"""
|
|
return "%s.dict" % BUILTINS
|
|
|
|
def get_children(self):
|
|
"""Get the key and value nodes below this node.
|
|
|
|
Children are returned in the order that they are defined in the source
|
|
code, key first then the value.
|
|
|
|
:returns: The children.
|
|
:rtype: iterable(NodeNG)
|
|
"""
|
|
for key, value in self.items:
|
|
yield key
|
|
yield value
|
|
|
|
def last_child(self):
|
|
"""An optimized version of list(get_children())[-1]
|
|
|
|
:returns: The last child, or None if no children exist.
|
|
:rtype: NodeNG or None
|
|
"""
|
|
if self.items:
|
|
return self.items[-1][1]
|
|
return None
|
|
|
|
def itered(self):
|
|
"""An iterator over the keys this node contains.
|
|
|
|
:returns: The keys of this node.
|
|
:rtype: iterable(NodeNG)
|
|
"""
|
|
return [key for (key, _) in self.items]
|
|
|
|
def getitem(self, index, context=None):
|
|
"""Get an item from this node.
|
|
|
|
:param index: The node to use as a subscript index.
|
|
:type index: Const or Slice
|
|
|
|
:raises AstroidTypeError: When the given index cannot be used as a
|
|
subscript index, or if this node is not subscriptable.
|
|
:raises AstroidIndexError: If the given index does not exist in the
|
|
dictionary.
|
|
"""
|
|
for key, value in self.items:
|
|
# TODO(cpopa): no support for overriding yet, {1:2, **{1: 3}}.
|
|
if isinstance(key, DictUnpack):
|
|
try:
|
|
return value.getitem(index, context)
|
|
except (exceptions.AstroidTypeError, exceptions.AstroidIndexError):
|
|
continue
|
|
for inferredkey in key.infer(context):
|
|
if inferredkey is util.Uninferable:
|
|
continue
|
|
if isinstance(inferredkey, Const) and isinstance(index, Const):
|
|
if inferredkey.value == index.value:
|
|
return value
|
|
|
|
raise exceptions.AstroidIndexError(index)
|
|
|
|
def bool_value(self, context=None):
|
|
"""Determine the boolean value of this node.
|
|
|
|
:returns: The boolean value of this node.
|
|
:rtype: bool
|
|
"""
|
|
return bool(self.items)
|
|
|
|
|
|
class Expr(Statement):
|
|
"""Class representing an :class:`ast.Expr` node.
|
|
|
|
An :class:`Expr` is any expression that does not have its value used or
|
|
stored.
|
|
|
|
>>> node = astroid.extract_node('method()')
|
|
>>> node
|
|
<Call l.1 at 0x7f23b2e352b0>
|
|
>>> node.parent
|
|
<Expr l.1 at 0x7f23b2e35278>
|
|
"""
|
|
|
|
_astroid_fields = ("value",)
|
|
value = None
|
|
"""What the expression does.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
|
|
def postinit(self, value=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param value: What the expression does.
|
|
:type value: NodeNG or None
|
|
"""
|
|
self.value = value
|
|
|
|
def get_children(self):
|
|
yield self.value
|
|
|
|
def _get_yield_nodes_skip_lambdas(self):
|
|
if not self.value.is_lambda:
|
|
yield from self.value._get_yield_nodes_skip_lambdas()
|
|
|
|
|
|
class Ellipsis(mixins.NoChildrenMixin, NodeNG): # pylint: disable=redefined-builtin
|
|
"""Class representing an :class:`ast.Ellipsis` node.
|
|
|
|
An :class:`Ellipsis` is the ``...`` syntax.
|
|
|
|
>>> node = astroid.extract_node('...')
|
|
>>> node
|
|
<Ellipsis l.1 at 0x7f23b2e35160>
|
|
"""
|
|
|
|
def bool_value(self, context=None):
|
|
"""Determine the boolean value of this node.
|
|
|
|
:returns: The boolean value of this node.
|
|
For an :class:`Ellipsis` this is always ``True``.
|
|
:rtype: bool
|
|
"""
|
|
return True
|
|
|
|
|
|
class EmptyNode(mixins.NoChildrenMixin, NodeNG):
|
|
"""Holds an arbitrary object in the :attr:`LocalsDictNodeNG.locals`."""
|
|
|
|
object = None
|
|
|
|
|
|
class ExceptHandler(mixins.MultiLineBlockMixin, mixins.AssignTypeMixin, Statement):
|
|
"""Class representing an :class:`ast.ExceptHandler`. node.
|
|
|
|
An :class:`ExceptHandler` is an ``except`` block on a try-except.
|
|
|
|
>>> node = astroid.extract_node('''
|
|
try:
|
|
do_something()
|
|
except Exception as error:
|
|
print("Error!")
|
|
''')
|
|
>>> node
|
|
<TryExcept l.2 at 0x7f23b2e9d908>
|
|
>>> >>> node.handlers
|
|
[<ExceptHandler l.4 at 0x7f23b2e9e860>]
|
|
"""
|
|
|
|
_astroid_fields = ("type", "name", "body")
|
|
_multi_line_block_fields = ("body",)
|
|
type = None
|
|
"""The types that the block handles.
|
|
|
|
:type: Tuple or NodeNG or None
|
|
"""
|
|
name = None
|
|
"""The name that the caught exception is assigned to.
|
|
|
|
:type: AssignName or None
|
|
"""
|
|
body = None
|
|
"""The contents of the block.
|
|
|
|
:type: list(NodeNG) or None
|
|
"""
|
|
|
|
def get_children(self):
|
|
if self.type is not None:
|
|
yield self.type
|
|
|
|
if self.name is not None:
|
|
yield self.name
|
|
|
|
yield from self.body
|
|
|
|
# pylint: disable=redefined-builtin; had to use the same name as builtin ast module.
|
|
def postinit(self, type=None, name=None, body=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param type: The types that the block handles.
|
|
:type type: Tuple or NodeNG or None
|
|
|
|
:param name: The name that the caught exception is assigned to.
|
|
:type name: AssignName or None
|
|
|
|
:param body:The contents of the block.
|
|
:type body: list(NodeNG) or None
|
|
"""
|
|
self.type = type
|
|
self.name = name
|
|
self.body = body
|
|
|
|
@decorators.cachedproperty
|
|
def blockstart_tolineno(self):
|
|
"""The line on which the beginning of this block ends.
|
|
|
|
:type: int
|
|
"""
|
|
if self.name:
|
|
return self.name.tolineno
|
|
if self.type:
|
|
return self.type.tolineno
|
|
return self.lineno
|
|
|
|
def catch(self, exceptions): # pylint: disable=redefined-outer-name
|
|
"""Check if this node handles any of the given exceptions.
|
|
|
|
If ``exceptions`` is empty, this will default to ``True``.
|
|
|
|
:param exceptions: The name of the exceptions to check for.
|
|
:type exceptions: list(str)
|
|
"""
|
|
if self.type is None or exceptions is None:
|
|
return True
|
|
for node in self.type._get_name_nodes():
|
|
if node.name in exceptions:
|
|
return True
|
|
return False
|
|
|
|
|
|
class Exec(Statement):
|
|
"""Class representing the ``exec`` statement.
|
|
|
|
>>> node = astroid.extract_node('exec "True"')
|
|
>>> node
|
|
<Exec l.1 at 0x7f0e8106c6d0>
|
|
"""
|
|
|
|
_astroid_fields = ("expr", "globals", "locals")
|
|
expr = None
|
|
"""The expression to be executed.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
globals = None
|
|
"""The globals dictionary to execute with.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
locals = None
|
|
"""The locals dictionary to execute with.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
|
|
# pylint: disable=redefined-builtin; had to use the same name as builtin ast module.
|
|
def postinit(self, expr=None, globals=None, locals=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param expr: The expression to be executed.
|
|
:type expr: NodeNG or None
|
|
|
|
:param globals:The globals dictionary to execute with.
|
|
:type globals: NodeNG or None
|
|
|
|
:param locals: The locals dictionary to execute with.
|
|
:type locals: NodeNG or None
|
|
"""
|
|
self.expr = expr
|
|
self.globals = globals
|
|
self.locals = locals
|
|
|
|
|
|
class ExtSlice(NodeNG):
|
|
"""Class representing an :class:`ast.ExtSlice` node.
|
|
|
|
An :class:`ExtSlice` is a complex slice expression.
|
|
|
|
>>> node = astroid.extract_node('l[1:3, 5]')
|
|
>>> node
|
|
<Subscript l.1 at 0x7f23b2e9e550>
|
|
>>> node.slice
|
|
<ExtSlice l.1 at 0x7f23b7b05ef0>
|
|
"""
|
|
|
|
_astroid_fields = ("dims",)
|
|
dims = None
|
|
"""The simple dimensions that form the complete slice.
|
|
|
|
:type: list(NodeNG) or None
|
|
"""
|
|
|
|
def postinit(self, dims=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param dims: The simple dimensions that form the complete slice.
|
|
:type dims: list(NodeNG) or None
|
|
"""
|
|
self.dims = dims
|
|
|
|
|
|
class For(
|
|
mixins.MultiLineBlockMixin,
|
|
mixins.BlockRangeMixIn,
|
|
mixins.AssignTypeMixin,
|
|
Statement,
|
|
):
|
|
"""Class representing an :class:`ast.For` node.
|
|
|
|
>>> node = astroid.extract_node('for thing in things: print(thing)')
|
|
>>> node
|
|
<For l.1 at 0x7f23b2e8cf28>
|
|
"""
|
|
|
|
_astroid_fields = ("target", "iter", "body", "orelse")
|
|
_other_other_fields = ("type_annotation",)
|
|
_multi_line_block_fields = ("body", "orelse")
|
|
target = None
|
|
"""What the loop assigns to.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
iter = None
|
|
"""What the loop iterates over.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
body = None
|
|
"""The contents of the body of the loop.
|
|
|
|
:type: list(NodeNG) or None
|
|
"""
|
|
orelse = None
|
|
"""The contents of the ``else`` block of the loop.
|
|
|
|
:type: list(NodeNG) or None
|
|
"""
|
|
type_annotation = None
|
|
"""If present, this will contain the type annotation passed by a type comment
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
|
|
# pylint: disable=redefined-builtin; had to use the same name as builtin ast module.
|
|
def postinit(
|
|
self, target=None, iter=None, body=None, orelse=None, type_annotation=None
|
|
):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param target: What the loop assigns to.
|
|
:type target: NodeNG or None
|
|
|
|
:param iter: What the loop iterates over.
|
|
:type iter: NodeNG or None
|
|
|
|
:param body: The contents of the body of the loop.
|
|
:type body: list(NodeNG) or None
|
|
|
|
:param orelse: The contents of the ``else`` block of the loop.
|
|
:type orelse: list(NodeNG) or None
|
|
"""
|
|
self.target = target
|
|
self.iter = iter
|
|
self.body = body
|
|
self.orelse = orelse
|
|
self.type_annotation = type_annotation
|
|
|
|
optional_assign = True
|
|
"""Whether this node optionally assigns a variable.
|
|
|
|
This is always ``True`` for :class:`For` nodes.
|
|
|
|
:type: bool
|
|
"""
|
|
|
|
@decorators.cachedproperty
|
|
def blockstart_tolineno(self):
|
|
"""The line on which the beginning of this block ends.
|
|
|
|
:type: int
|
|
"""
|
|
return self.iter.tolineno
|
|
|
|
def get_children(self):
|
|
yield self.target
|
|
yield self.iter
|
|
|
|
yield from self.body
|
|
yield from self.orelse
|
|
|
|
|
|
class AsyncFor(For):
|
|
"""Class representing an :class:`ast.AsyncFor` node.
|
|
|
|
An :class:`AsyncFor` is an asynchronous :class:`For` built with
|
|
the ``async`` keyword.
|
|
|
|
>>> node = astroid.extract_node('''
|
|
async def func(things):
|
|
async for thing in things:
|
|
print(thing)
|
|
''')
|
|
>>> node
|
|
<AsyncFunctionDef.func l.2 at 0x7f23b2e416d8>
|
|
>>> node.body[0]
|
|
<AsyncFor l.3 at 0x7f23b2e417b8>
|
|
"""
|
|
|
|
|
|
class Await(NodeNG):
|
|
"""Class representing an :class:`ast.Await` node.
|
|
|
|
An :class:`Await` is the ``await`` keyword.
|
|
|
|
>>> node = astroid.extract_node('''
|
|
async def func(things):
|
|
await other_func()
|
|
''')
|
|
>>> node
|
|
<AsyncFunctionDef.func l.2 at 0x7f23b2e41748>
|
|
>>> node.body[0]
|
|
<Expr l.3 at 0x7f23b2e419e8>
|
|
>>> list(node.body[0].get_children())[0]
|
|
<Await l.3 at 0x7f23b2e41a20>
|
|
"""
|
|
|
|
_astroid_fields = ("value",)
|
|
value = None
|
|
"""What to wait for.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
|
|
def postinit(self, value=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param value: What to wait for.
|
|
:type value: NodeNG or None
|
|
"""
|
|
self.value = value
|
|
|
|
def get_children(self):
|
|
yield self.value
|
|
|
|
|
|
class ImportFrom(mixins.NoChildrenMixin, mixins.ImportFromMixin, Statement):
|
|
"""Class representing an :class:`ast.ImportFrom` node.
|
|
|
|
>>> node = astroid.extract_node('from my_package import my_module')
|
|
>>> node
|
|
<ImportFrom l.1 at 0x7f23b2e415c0>
|
|
"""
|
|
|
|
_other_fields = ("modname", "names", "level")
|
|
|
|
def __init__(
|
|
self, fromname, names, level=0, lineno=None, col_offset=None, parent=None
|
|
):
|
|
"""
|
|
:param fromname: The module that is being imported from.
|
|
:type fromname: str or None
|
|
|
|
:param names: What is being imported from the module.
|
|
:type names: list(tuple(str, str or None))
|
|
|
|
:param level: The level of relative import.
|
|
:type level: int
|
|
|
|
:param lineno: The line that this node appears on in the source code.
|
|
:type lineno: int or None
|
|
|
|
:param col_offset: The column that this node appears on in the
|
|
source code.
|
|
:type col_offset: int or None
|
|
|
|
:param parent: The parent node in the syntax tree.
|
|
:type parent: NodeNG or None
|
|
"""
|
|
self.modname = fromname
|
|
"""The module that is being imported from.
|
|
|
|
This is ``None`` for relative imports.
|
|
|
|
:type: str or None
|
|
"""
|
|
|
|
self.names = names
|
|
"""What is being imported from the module.
|
|
|
|
Each entry is a :class:`tuple` of the name being imported,
|
|
and the alias that the name is assigned to (if any).
|
|
|
|
:type: list(tuple(str, str or None))
|
|
"""
|
|
|
|
self.level = level
|
|
"""The level of relative import.
|
|
|
|
Essentially this is the number of dots in the import.
|
|
This is always 0 for absolute imports.
|
|
|
|
:type: int
|
|
"""
|
|
|
|
super().__init__(lineno, col_offset, parent)
|
|
|
|
|
|
class Attribute(NodeNG):
|
|
"""Class representing an :class:`ast.Attribute` node."""
|
|
|
|
_astroid_fields = ("expr",)
|
|
_other_fields = ("attrname",)
|
|
expr = None
|
|
"""The name that this node represents.
|
|
|
|
:type: Name or None
|
|
"""
|
|
|
|
def __init__(self, attrname=None, lineno=None, col_offset=None, parent=None):
|
|
"""
|
|
:param attrname: The name of the attribute.
|
|
:type attrname: str or None
|
|
|
|
:param lineno: The line that this node appears on in the source code.
|
|
:type lineno: int or None
|
|
|
|
:param col_offset: The column that this node appears on in the
|
|
source code.
|
|
:type col_offset: int or None
|
|
|
|
:param parent: The parent node in the syntax tree.
|
|
:type parent: NodeNG or None
|
|
"""
|
|
self.attrname = attrname
|
|
"""The name of the attribute.
|
|
|
|
:type: str or None
|
|
"""
|
|
|
|
super().__init__(lineno, col_offset, parent)
|
|
|
|
def postinit(self, expr=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param expr: The name that this node represents.
|
|
:type expr: Name or None
|
|
"""
|
|
self.expr = expr
|
|
|
|
def get_children(self):
|
|
yield self.expr
|
|
|
|
|
|
class Global(mixins.NoChildrenMixin, Statement):
|
|
"""Class representing an :class:`ast.Global` node.
|
|
|
|
>>> node = astroid.extract_node('global a_global')
|
|
>>> node
|
|
<Global l.1 at 0x7f23b2e9de10>
|
|
"""
|
|
|
|
_other_fields = ("names",)
|
|
|
|
def __init__(self, names, lineno=None, col_offset=None, parent=None):
|
|
"""
|
|
:param names: The names being declared as global.
|
|
:type names: list(str)
|
|
|
|
:param lineno: The line that this node appears on in the source code.
|
|
:type lineno: int or None
|
|
|
|
:param col_offset: The column that this node appears on in the
|
|
source code.
|
|
:type col_offset: int or None
|
|
|
|
:param parent: The parent node in the syntax tree.
|
|
:type parent: NodeNG or None
|
|
"""
|
|
self.names = names
|
|
"""The names being declared as global.
|
|
|
|
:type: list(str)
|
|
"""
|
|
|
|
super().__init__(lineno, col_offset, parent)
|
|
|
|
def _infer_name(self, frame, name):
|
|
return name
|
|
|
|
|
|
class If(mixins.MultiLineBlockMixin, mixins.BlockRangeMixIn, Statement):
|
|
"""Class representing an :class:`ast.If` node.
|
|
|
|
>>> node = astroid.extract_node('if condition: print(True)')
|
|
>>> node
|
|
<If l.1 at 0x7f23b2e9dd30>
|
|
"""
|
|
|
|
_astroid_fields = ("test", "body", "orelse")
|
|
_multi_line_block_fields = ("body", "orelse")
|
|
test = None
|
|
"""The condition that the statement tests.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
body = None
|
|
"""The contents of the block.
|
|
|
|
:type: list(NodeNG) or None
|
|
"""
|
|
orelse = None
|
|
"""The contents of the ``else`` block.
|
|
|
|
:type: list(NodeNG) or None
|
|
"""
|
|
|
|
def postinit(self, test=None, body=None, orelse=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param test: The condition that the statement tests.
|
|
:type test: NodeNG or None
|
|
|
|
:param body: The contents of the block.
|
|
:type body: list(NodeNG) or None
|
|
|
|
:param orelse: The contents of the ``else`` block.
|
|
:type orelse: list(NodeNG) or None
|
|
"""
|
|
self.test = test
|
|
self.body = body
|
|
self.orelse = orelse
|
|
|
|
@decorators.cachedproperty
|
|
def blockstart_tolineno(self):
|
|
"""The line on which the beginning of this block ends.
|
|
|
|
:type: int
|
|
"""
|
|
return self.test.tolineno
|
|
|
|
def block_range(self, lineno):
|
|
"""Get a range from the given line number to where this node ends.
|
|
|
|
:param lineno: The line number to start the range at.
|
|
:type lineno: int
|
|
|
|
:returns: The range of line numbers that this node belongs to,
|
|
starting at the given line number.
|
|
:rtype: tuple(int, int)
|
|
"""
|
|
if lineno == self.body[0].fromlineno:
|
|
return lineno, lineno
|
|
if lineno <= self.body[-1].tolineno:
|
|
return lineno, self.body[-1].tolineno
|
|
return self._elsed_block_range(lineno, self.orelse, self.body[0].fromlineno - 1)
|
|
|
|
def get_children(self):
|
|
yield self.test
|
|
|
|
yield from self.body
|
|
yield from self.orelse
|
|
|
|
def has_elif_block(self):
|
|
return len(self.orelse) == 1 and isinstance(self.orelse[0], If)
|
|
|
|
|
|
class IfExp(NodeNG):
|
|
"""Class representing an :class:`ast.IfExp` node.
|
|
|
|
>>> node = astroid.extract_node('value if condition else other')
|
|
>>> node
|
|
<IfExp l.1 at 0x7f23b2e9dbe0>
|
|
"""
|
|
|
|
_astroid_fields = ("test", "body", "orelse")
|
|
test = None
|
|
"""The condition that the statement tests.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
body = None
|
|
"""The contents of the block.
|
|
|
|
:type: list(NodeNG) or None
|
|
"""
|
|
orelse = None
|
|
"""The contents of the ``else`` block.
|
|
|
|
:type: list(NodeNG) or None
|
|
"""
|
|
|
|
def postinit(self, test=None, body=None, orelse=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param test: The condition that the statement tests.
|
|
:type test: NodeNG or None
|
|
|
|
:param body: The contents of the block.
|
|
:type body: list(NodeNG) or None
|
|
|
|
:param orelse: The contents of the ``else`` block.
|
|
:type orelse: list(NodeNG) or None
|
|
"""
|
|
self.test = test
|
|
self.body = body
|
|
self.orelse = orelse
|
|
|
|
def get_children(self):
|
|
yield self.test
|
|
yield self.body
|
|
yield self.orelse
|
|
|
|
def op_left_associative(self):
|
|
# `1 if True else 2 if False else 3` is parsed as
|
|
# `1 if True else (2 if False else 3)`
|
|
return False
|
|
|
|
|
|
class Import(mixins.NoChildrenMixin, mixins.ImportFromMixin, Statement):
|
|
"""Class representing an :class:`ast.Import` node.
|
|
|
|
>>> node = astroid.extract_node('import astroid')
|
|
>>> node
|
|
<Import l.1 at 0x7f23b2e4e5c0>
|
|
"""
|
|
|
|
_other_fields = ("names",)
|
|
|
|
def __init__(self, names=None, lineno=None, col_offset=None, parent=None):
|
|
"""
|
|
:param names: The names being imported.
|
|
:type names: list(tuple(str, str or None)) or None
|
|
|
|
:param lineno: The line that this node appears on in the source code.
|
|
:type lineno: int or None
|
|
|
|
:param col_offset: The column that this node appears on in the
|
|
source code.
|
|
:type col_offset: int or None
|
|
|
|
:param parent: The parent node in the syntax tree.
|
|
:type parent: NodeNG or None
|
|
"""
|
|
self.names = names
|
|
"""The names being imported.
|
|
|
|
Each entry is a :class:`tuple` of the name being imported,
|
|
and the alias that the name is assigned to (if any).
|
|
|
|
:type: list(tuple(str, str or None)) or None
|
|
"""
|
|
|
|
super().__init__(lineno, col_offset, parent)
|
|
|
|
|
|
class Index(NodeNG):
|
|
"""Class representing an :class:`ast.Index` node.
|
|
|
|
An :class:`Index` is a simple subscript.
|
|
|
|
>>> node = astroid.extract_node('things[1]')
|
|
>>> node
|
|
<Subscript l.1 at 0x7f23b2e9e2b0>
|
|
>>> node.slice
|
|
<Index l.1 at 0x7f23b2e9e6a0>
|
|
"""
|
|
|
|
_astroid_fields = ("value",)
|
|
value = None
|
|
"""The value to subscript with.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
|
|
def postinit(self, value=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param value: The value to subscript with.
|
|
:type value: NodeNG or None
|
|
"""
|
|
self.value = value
|
|
|
|
def get_children(self):
|
|
yield self.value
|
|
|
|
|
|
class Keyword(NodeNG):
|
|
"""Class representing an :class:`ast.keyword` node.
|
|
|
|
>>> node = astroid.extract_node('function(a_kwarg=True)')
|
|
>>> node
|
|
<Call l.1 at 0x7f23b2e9e320>
|
|
>>> node.keywords
|
|
[<Keyword l.1 at 0x7f23b2e9e9b0>]
|
|
"""
|
|
|
|
_astroid_fields = ("value",)
|
|
_other_fields = ("arg",)
|
|
value = None
|
|
"""The value being assigned to the keyword argument.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
|
|
def __init__(self, arg=None, lineno=None, col_offset=None, parent=None):
|
|
"""
|
|
:param arg: The argument being assigned to.
|
|
:type arg: Name or None
|
|
|
|
:param lineno: The line that this node appears on in the source code.
|
|
:type lineno: int or None
|
|
|
|
:param col_offset: The column that this node appears on in the
|
|
source code.
|
|
:type col_offset: int or None
|
|
|
|
:param parent: The parent node in the syntax tree.
|
|
:type parent: NodeNG or None
|
|
"""
|
|
self.arg = arg
|
|
"""The argument being assigned to.
|
|
|
|
:type: Name or None
|
|
"""
|
|
|
|
super().__init__(lineno, col_offset, parent)
|
|
|
|
def postinit(self, value=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param value: The value being assigned to the ketword argument.
|
|
:type value: NodeNG or None
|
|
"""
|
|
self.value = value
|
|
|
|
def get_children(self):
|
|
yield self.value
|
|
|
|
|
|
class List(_BaseContainer):
|
|
"""Class representing an :class:`ast.List` node.
|
|
|
|
>>> node = astroid.extract_node('[1, 2, 3]')
|
|
>>> node
|
|
<List.list l.1 at 0x7f23b2e9e128>
|
|
"""
|
|
|
|
_other_fields = ("ctx",)
|
|
|
|
def __init__(self, ctx=None, lineno=None, col_offset=None, parent=None):
|
|
"""
|
|
:param ctx: Whether the list is assigned to or loaded from.
|
|
:type ctx: Context or None
|
|
|
|
:param lineno: The line that this node appears on in the source code.
|
|
:type lineno: int or None
|
|
|
|
:param col_offset: The column that this node appears on in the
|
|
source code.
|
|
:type col_offset: int or None
|
|
|
|
:param parent: The parent node in the syntax tree.
|
|
:type parent: NodeNG or None
|
|
"""
|
|
self.ctx = ctx
|
|
"""Whether the list is assigned to or loaded from.
|
|
|
|
:type: Context or None
|
|
"""
|
|
|
|
super().__init__(lineno, col_offset, parent)
|
|
|
|
def pytype(self):
|
|
"""Get the name of the type that this node represents.
|
|
|
|
:returns: The name of the type.
|
|
:rtype: str
|
|
"""
|
|
return "%s.list" % BUILTINS
|
|
|
|
def getitem(self, index, context=None):
|
|
"""Get an item from this node.
|
|
|
|
:param index: The node to use as a subscript index.
|
|
:type index: Const or Slice
|
|
"""
|
|
return _container_getitem(self, self.elts, index, context=context)
|
|
|
|
|
|
class Nonlocal(mixins.NoChildrenMixin, Statement):
|
|
"""Class representing an :class:`ast.Nonlocal` node.
|
|
|
|
>>> node = astroid.extract_node('''
|
|
def function():
|
|
nonlocal var
|
|
''')
|
|
>>> node
|
|
<FunctionDef.function l.2 at 0x7f23b2e9e208>
|
|
>>> node.body[0]
|
|
<Nonlocal l.3 at 0x7f23b2e9e908>
|
|
"""
|
|
|
|
_other_fields = ("names",)
|
|
|
|
def __init__(self, names, lineno=None, col_offset=None, parent=None):
|
|
"""
|
|
:param names: The names being declared as not local.
|
|
:type names: list(str)
|
|
|
|
:param lineno: The line that this node appears on in the source code.
|
|
:type lineno: int or None
|
|
|
|
:param col_offset: The column that this node appears on in the
|
|
source code.
|
|
:type col_offset: int or None
|
|
|
|
:param parent: The parent node in the syntax tree.
|
|
:type parent: NodeNG or None
|
|
"""
|
|
self.names = names
|
|
"""The names being declared as not local.
|
|
|
|
:type: list(str)
|
|
"""
|
|
|
|
super().__init__(lineno, col_offset, parent)
|
|
|
|
def _infer_name(self, frame, name):
|
|
return name
|
|
|
|
|
|
class Pass(mixins.NoChildrenMixin, Statement):
|
|
"""Class representing an :class:`ast.Pass` node.
|
|
|
|
>>> node = astroid.extract_node('pass')
|
|
>>> node
|
|
<Pass l.1 at 0x7f23b2e9e748>
|
|
"""
|
|
|
|
|
|
class Print(Statement):
|
|
"""Class representing an :class:`ast.Print` node.
|
|
|
|
>>> node = astroid.extract_node('print "A message"')
|
|
>>> node
|
|
<Print l.1 at 0x7f0e8101d290>
|
|
"""
|
|
|
|
_astroid_fields = ("dest", "values")
|
|
dest = None
|
|
"""Where to print to.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
values = None
|
|
"""What to print.
|
|
|
|
:type: list(NodeNG) or None
|
|
"""
|
|
|
|
def __init__(self, nl=None, lineno=None, col_offset=None, parent=None):
|
|
"""
|
|
:param nl: Whether to print a new line.
|
|
:type nl: bool or None
|
|
|
|
:param lineno: The line that this node appears on in the source code.
|
|
:type lineno: int or None
|
|
|
|
:param col_offset: The column that this node appears on in the
|
|
source code.
|
|
:type col_offset: int or None
|
|
|
|
:param parent: The parent node in the syntax tree.
|
|
:type parent: NodeNG or None
|
|
"""
|
|
self.nl = nl
|
|
"""Whether to print a new line.
|
|
|
|
:type: bool or None
|
|
"""
|
|
|
|
super().__init__(lineno, col_offset, parent)
|
|
|
|
def postinit(self, dest=None, values=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param dest: Where to print to.
|
|
:type dest: NodeNG or None
|
|
|
|
:param values: What to print.
|
|
:type values: list(NodeNG) or None
|
|
"""
|
|
self.dest = dest
|
|
self.values = values
|
|
|
|
|
|
class Raise(Statement):
|
|
"""Class representing an :class:`ast.Raise` node.
|
|
|
|
>>> node = astroid.extract_node('raise RuntimeError("Something bad happened!")')
|
|
>>> node
|
|
<Raise l.1 at 0x7f23b2e9e828>
|
|
"""
|
|
|
|
exc = None
|
|
"""What is being raised.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
_astroid_fields = ("exc", "cause")
|
|
cause = None
|
|
"""The exception being used to raise this one.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
|
|
def postinit(self, exc=None, cause=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param exc: What is being raised.
|
|
:type exc: NodeNG or None
|
|
|
|
:param cause: The exception being used to raise this one.
|
|
:type cause: NodeNG or None
|
|
"""
|
|
self.exc = exc
|
|
self.cause = cause
|
|
|
|
def raises_not_implemented(self):
|
|
"""Check if this node raises a :class:`NotImplementedError`.
|
|
|
|
:returns: True if this node raises a :class:`NotImplementedError`,
|
|
False otherwise.
|
|
:rtype: bool
|
|
"""
|
|
if not self.exc:
|
|
return False
|
|
for name in self.exc._get_name_nodes():
|
|
if name.name == "NotImplementedError":
|
|
return True
|
|
return False
|
|
|
|
def get_children(self):
|
|
if self.exc is not None:
|
|
yield self.exc
|
|
|
|
if self.cause is not None:
|
|
yield self.cause
|
|
|
|
|
|
class Return(Statement):
|
|
"""Class representing an :class:`ast.Return` node.
|
|
|
|
>>> node = astroid.extract_node('return True')
|
|
>>> node
|
|
<Return l.1 at 0x7f23b8211908>
|
|
"""
|
|
|
|
_astroid_fields = ("value",)
|
|
value = None
|
|
"""The value being returned.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
|
|
def postinit(self, value=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param value: The value being returned.
|
|
:type value: NodeNG or None
|
|
"""
|
|
self.value = value
|
|
|
|
def get_children(self):
|
|
if self.value is not None:
|
|
yield self.value
|
|
|
|
def is_tuple_return(self):
|
|
return isinstance(self.value, Tuple)
|
|
|
|
def _get_return_nodes_skip_functions(self):
|
|
yield self
|
|
|
|
|
|
class Set(_BaseContainer):
|
|
"""Class representing an :class:`ast.Set` node.
|
|
|
|
>>> node = astroid.extract_node('{1, 2, 3}')
|
|
>>> node
|
|
<Set.set l.1 at 0x7f23b2e71d68>
|
|
"""
|
|
|
|
def pytype(self):
|
|
"""Get the name of the type that this node represents.
|
|
|
|
:returns: The name of the type.
|
|
:rtype: str
|
|
"""
|
|
return "%s.set" % BUILTINS
|
|
|
|
|
|
class Slice(NodeNG):
|
|
"""Class representing an :class:`ast.Slice` node.
|
|
|
|
>>> node = astroid.extract_node('things[1:3]')
|
|
>>> node
|
|
<Subscript l.1 at 0x7f23b2e71f60>
|
|
>>> node.slice
|
|
<Slice l.1 at 0x7f23b2e71e80>
|
|
"""
|
|
|
|
_astroid_fields = ("lower", "upper", "step")
|
|
lower = None
|
|
"""The lower index in the slice.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
upper = None
|
|
"""The upper index in the slice.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
step = None
|
|
"""The step to take between indexes.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
|
|
def postinit(self, lower=None, upper=None, step=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param lower: The lower index in the slice.
|
|
:value lower: NodeNG or None
|
|
|
|
:param upper: The upper index in the slice.
|
|
:value upper: NodeNG or None
|
|
|
|
:param step: The step to take between index.
|
|
:param step: NodeNG or None
|
|
"""
|
|
self.lower = lower
|
|
self.upper = upper
|
|
self.step = step
|
|
|
|
def _wrap_attribute(self, attr):
|
|
"""Wrap the empty attributes of the Slice in a Const node."""
|
|
if not attr:
|
|
const = const_factory(attr)
|
|
const.parent = self
|
|
return const
|
|
return attr
|
|
|
|
@decorators.cachedproperty
|
|
def _proxied(self):
|
|
builtins = MANAGER.builtins_module
|
|
return builtins.getattr("slice")[0]
|
|
|
|
def pytype(self):
|
|
"""Get the name of the type that this node represents.
|
|
|
|
:returns: The name of the type.
|
|
:rtype: str
|
|
"""
|
|
return "%s.slice" % BUILTINS
|
|
|
|
def igetattr(self, attrname, context=None):
|
|
"""Infer the possible values of the given attribute on the slice.
|
|
|
|
:param attrname: The name of the attribute to infer.
|
|
:type attrname: str
|
|
|
|
:returns: The inferred possible values.
|
|
:rtype: iterable(NodeNG)
|
|
"""
|
|
if attrname == "start":
|
|
yield self._wrap_attribute(self.lower)
|
|
elif attrname == "stop":
|
|
yield self._wrap_attribute(self.upper)
|
|
elif attrname == "step":
|
|
yield self._wrap_attribute(self.step)
|
|
else:
|
|
yield from self.getattr(attrname, context=context)
|
|
|
|
def getattr(self, attrname, context=None):
|
|
return self._proxied.getattr(attrname, context)
|
|
|
|
def get_children(self):
|
|
if self.lower is not None:
|
|
yield self.lower
|
|
|
|
if self.upper is not None:
|
|
yield self.upper
|
|
|
|
if self.step is not None:
|
|
yield self.step
|
|
|
|
|
|
class Starred(mixins.ParentAssignTypeMixin, NodeNG):
|
|
"""Class representing an :class:`ast.Starred` node.
|
|
|
|
>>> node = astroid.extract_node('*args')
|
|
>>> node
|
|
<Starred l.1 at 0x7f23b2e41978>
|
|
"""
|
|
|
|
_astroid_fields = ("value",)
|
|
_other_fields = ("ctx",)
|
|
value = None
|
|
"""What is being unpacked.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
|
|
def __init__(self, ctx=None, lineno=None, col_offset=None, parent=None):
|
|
"""
|
|
:param ctx: Whether the list is assigned to or loaded from.
|
|
:type ctx: Context or None
|
|
|
|
:param lineno: The line that this node appears on in the source code.
|
|
:type lineno: int or None
|
|
|
|
:param col_offset: The column that this node appears on in the
|
|
source code.
|
|
:type col_offset: int or None
|
|
|
|
:param parent: The parent node in the syntax tree.
|
|
:type parent: NodeNG or None
|
|
"""
|
|
self.ctx = ctx
|
|
"""Whether the starred item is assigned to or loaded from.
|
|
|
|
:type: Context or None
|
|
"""
|
|
|
|
super().__init__(lineno=lineno, col_offset=col_offset, parent=parent)
|
|
|
|
def postinit(self, value=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param value: What is being unpacked.
|
|
:type value: NodeNG or None
|
|
"""
|
|
self.value = value
|
|
|
|
def get_children(self):
|
|
yield self.value
|
|
|
|
|
|
class Subscript(NodeNG):
|
|
"""Class representing an :class:`ast.Subscript` node.
|
|
|
|
>>> node = astroid.extract_node('things[1:3]')
|
|
>>> node
|
|
<Subscript l.1 at 0x7f23b2e71f60>
|
|
"""
|
|
|
|
_astroid_fields = ("value", "slice")
|
|
_other_fields = ("ctx",)
|
|
value = None
|
|
"""What is being indexed.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
slice = None
|
|
"""The slice being used to lookup.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
|
|
def __init__(self, ctx=None, lineno=None, col_offset=None, parent=None):
|
|
"""
|
|
:param ctx: Whether the subscripted item is assigned to or loaded from.
|
|
:type ctx: Context or None
|
|
|
|
:param lineno: The line that this node appears on in the source code.
|
|
:type lineno: int or None
|
|
|
|
:param col_offset: The column that this node appears on in the
|
|
source code.
|
|
:type col_offset: int or None
|
|
|
|
:param parent: The parent node in the syntax tree.
|
|
:type parent: NodeNG or None
|
|
"""
|
|
self.ctx = ctx
|
|
"""Whether the subscripted item is assigned to or loaded from.
|
|
|
|
:type: Context or None
|
|
"""
|
|
|
|
super().__init__(lineno=lineno, col_offset=col_offset, parent=parent)
|
|
|
|
# pylint: disable=redefined-builtin; had to use the same name as builtin ast module.
|
|
def postinit(self, value=None, slice=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param value: What is being indexed.
|
|
:type value: NodeNG or None
|
|
|
|
:param slice: The slice being used to lookup.
|
|
:type slice: NodeNG or None
|
|
"""
|
|
self.value = value
|
|
self.slice = slice
|
|
|
|
def get_children(self):
|
|
yield self.value
|
|
yield self.slice
|
|
|
|
|
|
class TryExcept(mixins.MultiLineBlockMixin, mixins.BlockRangeMixIn, Statement):
|
|
"""Class representing an :class:`ast.TryExcept` node.
|
|
|
|
>>> node = astroid.extract_node('''
|
|
try:
|
|
do_something()
|
|
except Exception as error:
|
|
print("Error!")
|
|
''')
|
|
>>> node
|
|
<TryExcept l.2 at 0x7f23b2e9d908>
|
|
"""
|
|
|
|
_astroid_fields = ("body", "handlers", "orelse")
|
|
_multi_line_block_fields = ("body", "handlers", "orelse")
|
|
body = None
|
|
"""The contents of the block to catch exceptions from.
|
|
|
|
:type: list(NodeNG) or None
|
|
"""
|
|
handlers = None
|
|
"""The exception handlers.
|
|
|
|
:type: list(ExceptHandler) or None
|
|
"""
|
|
orelse = None
|
|
"""The contents of the ``else`` block.
|
|
|
|
:type: list(NodeNG) or None
|
|
"""
|
|
|
|
def postinit(self, body=None, handlers=None, orelse=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param body: The contents of the block to catch exceptions from.
|
|
:type body: list(NodeNG) or None
|
|
|
|
:param handlers: The exception handlers.
|
|
:type handlers: list(ExceptHandler) or None
|
|
|
|
:param orelse: The contents of the ``else`` block.
|
|
:type orelse: list(NodeNG) or None
|
|
"""
|
|
self.body = body
|
|
self.handlers = handlers
|
|
self.orelse = orelse
|
|
|
|
def _infer_name(self, frame, name):
|
|
return name
|
|
|
|
def block_range(self, lineno):
|
|
"""Get a range from the given line number to where this node ends.
|
|
|
|
:param lineno: The line number to start the range at.
|
|
:type lineno: int
|
|
|
|
:returns: The range of line numbers that this node belongs to,
|
|
starting at the given line number.
|
|
:rtype: tuple(int, int)
|
|
"""
|
|
last = None
|
|
for exhandler in self.handlers:
|
|
if exhandler.type and lineno == exhandler.type.fromlineno:
|
|
return lineno, lineno
|
|
if exhandler.body[0].fromlineno <= lineno <= exhandler.body[-1].tolineno:
|
|
return lineno, exhandler.body[-1].tolineno
|
|
if last is None:
|
|
last = exhandler.body[0].fromlineno - 1
|
|
return self._elsed_block_range(lineno, self.orelse, last)
|
|
|
|
def get_children(self):
|
|
yield from self.body
|
|
|
|
yield from self.handlers or ()
|
|
yield from self.orelse or ()
|
|
|
|
|
|
class TryFinally(mixins.MultiLineBlockMixin, mixins.BlockRangeMixIn, Statement):
|
|
"""Class representing an :class:`ast.TryFinally` node.
|
|
|
|
>>> node = astroid.extract_node('''
|
|
try:
|
|
do_something()
|
|
except Exception as error:
|
|
print("Error!")
|
|
finally:
|
|
print("Cleanup!")
|
|
''')
|
|
>>> node
|
|
<TryFinally l.2 at 0x7f23b2e41d68>
|
|
"""
|
|
|
|
_astroid_fields = ("body", "finalbody")
|
|
_multi_line_block_fields = ("body", "finalbody")
|
|
body = None
|
|
"""The try-except that the finally is attached to.
|
|
|
|
:type: list(TryExcept) or None
|
|
"""
|
|
finalbody = None
|
|
"""The contents of the ``finally`` block.
|
|
|
|
:type: list(NodeNG) or None
|
|
"""
|
|
|
|
def postinit(self, body=None, finalbody=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param body: The try-except that the finally is attached to.
|
|
:type body: list(TryExcept) or None
|
|
|
|
:param finalbody: The contents of the ``finally`` block.
|
|
:type finalbody: list(NodeNG) or None
|
|
"""
|
|
self.body = body
|
|
self.finalbody = finalbody
|
|
|
|
def block_range(self, lineno):
|
|
"""Get a range from the given line number to where this node ends.
|
|
|
|
:param lineno: The line number to start the range at.
|
|
:type lineno: int
|
|
|
|
:returns: The range of line numbers that this node belongs to,
|
|
starting at the given line number.
|
|
:rtype: tuple(int, int)
|
|
"""
|
|
child = self.body[0]
|
|
# py2.5 try: except: finally:
|
|
if (
|
|
isinstance(child, TryExcept)
|
|
and child.fromlineno == self.fromlineno
|
|
and child.tolineno >= lineno > self.fromlineno
|
|
):
|
|
return child.block_range(lineno)
|
|
return self._elsed_block_range(lineno, self.finalbody)
|
|
|
|
def get_children(self):
|
|
yield from self.body
|
|
yield from self.finalbody
|
|
|
|
|
|
class Tuple(_BaseContainer):
|
|
"""Class representing an :class:`ast.Tuple` node.
|
|
|
|
>>> node = astroid.extract_node('(1, 2, 3)')
|
|
>>> node
|
|
<Tuple.tuple l.1 at 0x7f23b2e41780>
|
|
"""
|
|
|
|
_other_fields = ("ctx",)
|
|
|
|
def __init__(self, ctx=None, lineno=None, col_offset=None, parent=None):
|
|
"""
|
|
:param ctx: Whether the tuple is assigned to or loaded from.
|
|
:type ctx: Context or None
|
|
|
|
:param lineno: The line that this node appears on in the source code.
|
|
:type lineno: int or None
|
|
|
|
:param col_offset: The column that this node appears on in the
|
|
source code.
|
|
:type col_offset: int or None
|
|
|
|
:param parent: The parent node in the syntax tree.
|
|
:type parent: NodeNG or None
|
|
"""
|
|
self.ctx = ctx
|
|
"""Whether the tuple is assigned to or loaded from.
|
|
|
|
:type: Context or None
|
|
"""
|
|
|
|
super().__init__(lineno, col_offset, parent)
|
|
|
|
def pytype(self):
|
|
"""Get the name of the type that this node represents.
|
|
|
|
:returns: The name of the type.
|
|
:rtype: str
|
|
"""
|
|
return "%s.tuple" % BUILTINS
|
|
|
|
def getitem(self, index, context=None):
|
|
"""Get an item from this node.
|
|
|
|
:param index: The node to use as a subscript index.
|
|
:type index: Const or Slice
|
|
"""
|
|
return _container_getitem(self, self.elts, index, context=context)
|
|
|
|
|
|
class UnaryOp(NodeNG):
|
|
"""Class representing an :class:`ast.UnaryOp` node.
|
|
|
|
>>> node = astroid.extract_node('-5')
|
|
>>> node
|
|
<UnaryOp l.1 at 0x7f23b2e4e198>
|
|
"""
|
|
|
|
_astroid_fields = ("operand",)
|
|
_other_fields = ("op",)
|
|
operand = None
|
|
"""What the unary operator is applied to.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
|
|
def __init__(self, op=None, lineno=None, col_offset=None, parent=None):
|
|
"""
|
|
:param op: The operator.
|
|
:type: str or None
|
|
|
|
:param lineno: The line that this node appears on in the source code.
|
|
:type lineno: int or None
|
|
|
|
:param col_offset: The column that this node appears on in the
|
|
source code.
|
|
:type col_offset: int or None
|
|
|
|
:param parent: The parent node in the syntax tree.
|
|
:type parent: NodeNG or None
|
|
"""
|
|
self.op = op
|
|
"""The operator.
|
|
|
|
:type: str or None
|
|
"""
|
|
|
|
super().__init__(lineno, col_offset, parent)
|
|
|
|
def postinit(self, operand=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param operand: What the unary operator is applied to.
|
|
:type operand: NodeNG or None
|
|
"""
|
|
self.operand = operand
|
|
|
|
# This is set by inference.py
|
|
def _infer_unaryop(self, context=None):
|
|
raise NotImplementedError
|
|
|
|
def type_errors(self, context=None):
|
|
"""Get a list of type errors which can occur during inference.
|
|
|
|
Each TypeError is represented by a :class:`BadBinaryOperationMessage`,
|
|
which holds the original exception.
|
|
|
|
:returns: The list of possible type errors.
|
|
:rtype: list(BadBinaryOperationMessage)
|
|
"""
|
|
try:
|
|
results = self._infer_unaryop(context=context)
|
|
return [
|
|
result
|
|
for result in results
|
|
if isinstance(result, util.BadUnaryOperationMessage)
|
|
]
|
|
except exceptions.InferenceError:
|
|
return []
|
|
|
|
def get_children(self):
|
|
yield self.operand
|
|
|
|
def op_precedence(self):
|
|
if self.op == "not":
|
|
return OP_PRECEDENCE[self.op]
|
|
|
|
return super().op_precedence()
|
|
|
|
|
|
class While(mixins.MultiLineBlockMixin, mixins.BlockRangeMixIn, Statement):
|
|
"""Class representing an :class:`ast.While` node.
|
|
|
|
>>> node = astroid.extract_node('''
|
|
while condition():
|
|
print("True")
|
|
''')
|
|
>>> node
|
|
<While l.2 at 0x7f23b2e4e390>
|
|
"""
|
|
|
|
_astroid_fields = ("test", "body", "orelse")
|
|
_multi_line_block_fields = ("body", "orelse")
|
|
test = None
|
|
"""The condition that the loop tests.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
body = None
|
|
"""The contents of the loop.
|
|
|
|
:type: list(NodeNG) or None
|
|
"""
|
|
orelse = None
|
|
"""The contents of the ``else`` block.
|
|
|
|
:type: list(NodeNG) or None
|
|
"""
|
|
|
|
def postinit(self, test=None, body=None, orelse=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param test: The condition that the loop tests.
|
|
:type test: NodeNG or None
|
|
|
|
:param body: The contents of the loop.
|
|
:type body: list(NodeNG) or None
|
|
|
|
:param orelse: The contents of the ``else`` block.
|
|
:type orelse: list(NodeNG) or None
|
|
"""
|
|
self.test = test
|
|
self.body = body
|
|
self.orelse = orelse
|
|
|
|
@decorators.cachedproperty
|
|
def blockstart_tolineno(self):
|
|
"""The line on which the beginning of this block ends.
|
|
|
|
:type: int
|
|
"""
|
|
return self.test.tolineno
|
|
|
|
def block_range(self, lineno):
|
|
"""Get a range from the given line number to where this node ends.
|
|
|
|
:param lineno: The line number to start the range at.
|
|
:type lineno: int
|
|
|
|
:returns: The range of line numbers that this node belongs to,
|
|
starting at the given line number.
|
|
:rtype: tuple(int, int)
|
|
"""
|
|
return self._elsed_block_range(lineno, self.orelse)
|
|
|
|
def get_children(self):
|
|
yield self.test
|
|
|
|
yield from self.body
|
|
yield from self.orelse
|
|
|
|
def _get_yield_nodes_skip_lambdas(self):
|
|
"""A While node can contain a Yield node in the test"""
|
|
yield from self.test._get_yield_nodes_skip_lambdas()
|
|
yield from super()._get_yield_nodes_skip_lambdas()
|
|
|
|
|
|
class With(
|
|
mixins.MultiLineBlockMixin,
|
|
mixins.BlockRangeMixIn,
|
|
mixins.AssignTypeMixin,
|
|
Statement,
|
|
):
|
|
"""Class representing an :class:`ast.With` node.
|
|
|
|
>>> node = astroid.extract_node('''
|
|
with open(file_path) as file_:
|
|
print(file_.read())
|
|
''')
|
|
>>> node
|
|
<With l.2 at 0x7f23b2e4e710>
|
|
"""
|
|
|
|
_astroid_fields = ("items", "body")
|
|
_other_other_fields = ("type_annotation",)
|
|
_multi_line_block_fields = ("body",)
|
|
items = None
|
|
"""The pairs of context managers and the names they are assigned to.
|
|
|
|
:type: list(tuple(NodeNG, AssignName or None)) or None
|
|
"""
|
|
body = None
|
|
"""The contents of the ``with`` block.
|
|
|
|
:type: list(NodeNG) or None
|
|
"""
|
|
type_annotation = None
|
|
"""If present, this will contain the type annotation passed by a type comment
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
|
|
def postinit(self, items=None, body=None, type_annotation=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param items: The pairs of context managers and the names
|
|
they are assigned to.
|
|
:type items: list(tuple(NodeNG, AssignName or None)) or None
|
|
|
|
:param body: The contents of the ``with`` block.
|
|
:type body: list(NodeNG) or None
|
|
"""
|
|
self.items = items
|
|
self.body = body
|
|
self.type_annotation = type_annotation
|
|
|
|
@decorators.cachedproperty
|
|
def blockstart_tolineno(self):
|
|
"""The line on which the beginning of this block ends.
|
|
|
|
:type: int
|
|
"""
|
|
return self.items[-1][0].tolineno
|
|
|
|
def get_children(self):
|
|
"""Get the child nodes below this node.
|
|
|
|
:returns: The children.
|
|
:rtype: iterable(NodeNG)
|
|
"""
|
|
for expr, var in self.items:
|
|
yield expr
|
|
if var:
|
|
yield var
|
|
yield from self.body
|
|
|
|
|
|
class AsyncWith(With):
|
|
"""Asynchronous ``with`` built with the ``async`` keyword."""
|
|
|
|
|
|
class Yield(NodeNG):
|
|
"""Class representing an :class:`ast.Yield` node.
|
|
|
|
>>> node = astroid.extract_node('yield True')
|
|
>>> node
|
|
<Yield l.1 at 0x7f23b2e4e5f8>
|
|
"""
|
|
|
|
_astroid_fields = ("value",)
|
|
value = None
|
|
"""The value to yield.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
|
|
def postinit(self, value=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param value: The value to yield.
|
|
:type value: NodeNG or None
|
|
"""
|
|
self.value = value
|
|
|
|
def get_children(self):
|
|
if self.value is not None:
|
|
yield self.value
|
|
|
|
def _get_yield_nodes_skip_lambdas(self):
|
|
yield self
|
|
|
|
|
|
class YieldFrom(Yield):
|
|
"""Class representing an :class:`ast.YieldFrom` node."""
|
|
|
|
|
|
class DictUnpack(mixins.NoChildrenMixin, NodeNG):
|
|
"""Represents the unpacking of dicts into dicts using :pep:`448`."""
|
|
|
|
|
|
class FormattedValue(NodeNG):
|
|
"""Class representing an :class:`ast.FormattedValue` node.
|
|
|
|
Represents a :pep:`498` format string.
|
|
|
|
>>> node = astroid.extract_node('f"Format {type_}"')
|
|
>>> node
|
|
<JoinedStr l.1 at 0x7f23b2e4ed30>
|
|
>>> node.values
|
|
[<Const.str l.1 at 0x7f23b2e4eda0>, <FormattedValue l.1 at 0x7f23b2e4edd8>]
|
|
"""
|
|
|
|
_astroid_fields = ("value", "format_spec")
|
|
value = None
|
|
"""The value to be formatted into the string.
|
|
|
|
:type: NodeNG or None
|
|
"""
|
|
conversion = None
|
|
"""The type of formatting to be applied to the value.
|
|
|
|
.. seealso::
|
|
:class:`ast.FormattedValue`
|
|
|
|
:type: int or None
|
|
"""
|
|
format_spec = None
|
|
"""The formatting to be applied to the value.
|
|
|
|
.. seealso::
|
|
:class:`ast.FormattedValue`
|
|
|
|
:type: JoinedStr or None
|
|
"""
|
|
|
|
def postinit(self, value, conversion=None, format_spec=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param value: The value to be formatted into the string.
|
|
:type value: NodeNG
|
|
|
|
:param conversion: The type of formatting to be applied to the value.
|
|
:type conversion: int or None
|
|
|
|
:param format_spec: The formatting to be applied to the value.
|
|
:type format_spec: JoinedStr or None
|
|
"""
|
|
self.value = value
|
|
self.conversion = conversion
|
|
self.format_spec = format_spec
|
|
|
|
def get_children(self):
|
|
yield self.value
|
|
|
|
if self.format_spec is not None:
|
|
yield self.format_spec
|
|
|
|
|
|
class JoinedStr(NodeNG):
|
|
"""Represents a list of string expressions to be joined.
|
|
|
|
>>> node = astroid.extract_node('f"Format {type_}"')
|
|
>>> node
|
|
<JoinedStr l.1 at 0x7f23b2e4ed30>
|
|
"""
|
|
|
|
_astroid_fields = ("values",)
|
|
values = None
|
|
"""The string expressions to be joined.
|
|
|
|
:type: list(FormattedValue or Const) or None
|
|
"""
|
|
|
|
def postinit(self, values=None):
|
|
"""Do some setup after initialisation.
|
|
|
|
:param value: The string expressions to be joined.
|
|
|
|
:type: list(FormattedValue or Const) or None
|
|
"""
|
|
self.values = values
|
|
|
|
def get_children(self):
|
|
yield from self.values
|
|
|
|
|
|
class NamedExpr(mixins.AssignTypeMixin, NodeNG):
|
|
"""Represents the assignment from the assignment expression
|
|
|
|
>>> module = astroid.parse('if a := 1: pass')
|
|
>>> module.body[0].test
|
|
<NamedExpr l.1 at 0x7f23b2e4ed30>
|
|
"""
|
|
|
|
_astroid_fields = ("target", "value")
|
|
target = None
|
|
"""The assignment target
|
|
|
|
:type: Name
|
|
"""
|
|
value = None
|
|
"""The value that gets assigned in the expression
|
|
|
|
:type: NodeNG
|
|
"""
|
|
|
|
def postinit(self, target, value):
|
|
self.target = target
|
|
self.value = value
|
|
|
|
|
|
class Unknown(mixins.AssignTypeMixin, NodeNG):
|
|
"""This node represents a node in a constructed AST where
|
|
introspection is not possible. At the moment, it's only used in
|
|
the args attribute of FunctionDef nodes where function signature
|
|
introspection failed.
|
|
"""
|
|
|
|
name = "Unknown"
|
|
|
|
def qname(self):
|
|
return "Unknown"
|
|
|
|
def infer(self, context=None, **kwargs):
|
|
"""Inference on an Unknown node immediately terminates."""
|
|
yield util.Uninferable
|
|
|
|
|
|
class EvaluatedObject(NodeNG):
|
|
"""Contains an object that has already been inferred
|
|
|
|
This class is useful to pre-evaluate a particular node,
|
|
with the resulting class acting as the non-evaluated node.
|
|
"""
|
|
|
|
name = "EvaluatedObject"
|
|
_astroid_fields = ("original",)
|
|
_other_fields = ("value",)
|
|
|
|
original = None
|
|
"""The original node that has already been evaluated
|
|
|
|
:type: NodeNG
|
|
"""
|
|
|
|
value = None
|
|
"""The inferred value
|
|
|
|
:type: Union[Uninferable, NodeNG]
|
|
"""
|
|
|
|
def __init__(self, original, value):
|
|
self.original = original
|
|
self.value = value
|
|
super().__init__(
|
|
lineno=self.original.lineno,
|
|
col_offset=self.original.col_offset,
|
|
parent=self.original.parent,
|
|
)
|
|
|
|
def infer(self, context=None, **kwargs):
|
|
yield self.value
|
|
|
|
|
|
# constants ##############################################################
|
|
|
|
CONST_CLS = {
|
|
list: List,
|
|
tuple: Tuple,
|
|
dict: Dict,
|
|
set: Set,
|
|
type(None): Const,
|
|
type(NotImplemented): Const,
|
|
}
|
|
if PY38:
|
|
CONST_CLS[type(...)] = Const
|
|
|
|
|
|
def _update_const_classes():
|
|
"""update constant classes, so the keys of CONST_CLS can be reused"""
|
|
klasses = (bool, int, float, complex, str, bytes)
|
|
for kls in klasses:
|
|
CONST_CLS[kls] = Const
|
|
|
|
|
|
_update_const_classes()
|
|
|
|
|
|
def _two_step_initialization(cls, value):
|
|
instance = cls()
|
|
instance.postinit(value)
|
|
return instance
|
|
|
|
|
|
def _dict_initialization(cls, value):
|
|
if isinstance(value, dict):
|
|
value = tuple(value.items())
|
|
return _two_step_initialization(cls, value)
|
|
|
|
|
|
_CONST_CLS_CONSTRUCTORS = {
|
|
List: _two_step_initialization,
|
|
Tuple: _two_step_initialization,
|
|
Dict: _dict_initialization,
|
|
Set: _two_step_initialization,
|
|
Const: lambda cls, value: cls(value),
|
|
}
|
|
|
|
|
|
def const_factory(value):
|
|
"""return an astroid node for a python value"""
|
|
# XXX we should probably be stricter here and only consider stuff in
|
|
# CONST_CLS or do better treatment: in case where value is not in CONST_CLS,
|
|
# we should rather recall the builder on this value than returning an empty
|
|
# node (another option being that const_factory shouldn't be called with something
|
|
# not in CONST_CLS)
|
|
assert not isinstance(value, NodeNG)
|
|
|
|
# Hack for ignoring elements of a sequence
|
|
# or a mapping, in order to avoid transforming
|
|
# each element to an AST. This is fixed in 2.0
|
|
# and this approach is a temporary hack.
|
|
if isinstance(value, (list, set, tuple, dict)):
|
|
elts = []
|
|
else:
|
|
elts = value
|
|
|
|
try:
|
|
initializer_cls = CONST_CLS[value.__class__]
|
|
initializer = _CONST_CLS_CONSTRUCTORS[initializer_cls]
|
|
return initializer(initializer_cls, elts)
|
|
except (KeyError, AttributeError):
|
|
node = EmptyNode()
|
|
node.object = value
|
|
return node
|
|
|
|
|
|
def is_from_decorator(node):
|
|
"""Return True if the given node is the child of a decorator"""
|
|
parent = node.parent
|
|
while parent is not None:
|
|
if isinstance(parent, Decorators):
|
|
return True
|
|
parent = parent.parent
|
|
return False
|