summaryrefslogtreecommitdiff
path: root/astroid
diff options
context:
space:
mode:
authorhippo91 <guillaume.peillex@gmail.com>2021-02-07 16:29:50 +0100
committerGitHub <noreply@github.com>2021-02-07 16:29:50 +0100
commitd60eb5e207427599fe2f4a8d44e4cb0bebcf3c9f (patch)
treeafca46ee9a4b30effa5a6d36657b64b7f492c651 /astroid
parent529cd943ea05e1c263b3e07366973bc9a32ff171 (diff)
parent599fe72a0c0e07e2a7720237c40800aa3c611708 (diff)
downloadastroid-git-d60eb5e207427599fe2f4a8d44e4cb0bebcf3c9f.tar.gz
Merge branch 'master' into master
Diffstat (limited to 'astroid')
-rw-r--r--astroid/brain/brain_builtin_inference.py2
-rw-r--r--astroid/brain/brain_collections.py8
-rw-r--r--astroid/brain/brain_numpy_core_umath.py2
-rw-r--r--astroid/brain/brain_six.py39
-rw-r--r--astroid/brain/brain_subprocess.py7
-rw-r--r--astroid/brain/brain_type.py64
-rw-r--r--astroid/context.py10
-rw-r--r--astroid/node_classes.py5
-rw-r--r--astroid/rebuilder.py8
9 files changed, 137 insertions, 8 deletions
diff --git a/astroid/brain/brain_builtin_inference.py b/astroid/brain/brain_builtin_inference.py
index 0008244c..b7659cc9 100644
--- a/astroid/brain/brain_builtin_inference.py
+++ b/astroid/brain/brain_builtin_inference.py
@@ -223,6 +223,8 @@ def _container_generic_transform(arg, context, klass, iterables, build_elts):
# TODO: Does not handle deduplication for sets.
elts = []
for element in arg.elts:
+ if not element:
+ continue
inferred = helpers.safe_infer(element, context=context)
if inferred:
evaluated_object = nodes.EvaluatedObject(
diff --git a/astroid/brain/brain_collections.py b/astroid/brain/brain_collections.py
index 6594e0c7..229969c5 100644
--- a/astroid/brain/brain_collections.py
+++ b/astroid/brain/brain_collections.py
@@ -4,6 +4,7 @@
# Copyright (c) 2017 Derek Gustafson <degustaf@gmail.com>
# Copyright (c) 2018 Ioana Tagirta <ioana.tagirta@gmail.com>
# Copyright (c) 2019 Hugo van Kemenade <hugovk@users.noreply.github.com>
+# Copyright (c) 2021 Julien Palard <julien@palard.fr>
# 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
@@ -12,6 +13,9 @@ import sys
import astroid
+PY39 = sys.version_info >= (3, 9)
+
+
def _collections_transform():
return astroid.parse(
"""
@@ -61,6 +65,10 @@ def _deque_mock():
def __mul__(self, other): pass
def __imul__(self, other): pass
def __rmul__(self, other): pass"""
+ if PY39:
+ base_deque_class += """
+ @classmethod
+ def __class_getitem__(self, item): pass"""
return base_deque_class
diff --git a/astroid/brain/brain_numpy_core_umath.py b/astroid/brain/brain_numpy_core_umath.py
index 1955e80e..73613b86 100644
--- a/astroid/brain/brain_numpy_core_umath.py
+++ b/astroid/brain/brain_numpy_core_umath.py
@@ -106,6 +106,7 @@ def numpy_core_umath_transform():
trunc = FakeUfuncOneArg()
# Two args functions with optional kwargs
+ add = FakeUfuncTwoArgs()
bitwise_and = FakeUfuncTwoArgs()
bitwise_or = FakeUfuncTwoArgs()
bitwise_xor = FakeUfuncTwoArgs()
@@ -133,6 +134,7 @@ def numpy_core_umath_transform():
logical_xor = FakeUfuncTwoArgs()
maximum = FakeUfuncTwoArgs()
minimum = FakeUfuncTwoArgs()
+ multiply = FakeUfuncTwoArgs()
nextafter = FakeUfuncTwoArgs()
not_equal = FakeUfuncTwoArgs()
power = FakeUfuncTwoArgs()
diff --git a/astroid/brain/brain_six.py b/astroid/brain/brain_six.py
index 389037f2..a998213f 100644
--- a/astroid/brain/brain_six.py
+++ b/astroid/brain/brain_six.py
@@ -22,6 +22,7 @@ from astroid import nodes
SIX_ADD_METACLASS = "six.add_metaclass"
+SIX_WITH_METACLASS = "six.with_metaclass"
def _indent(text, prefix, predicate=None):
@@ -190,6 +191,39 @@ def transform_six_add_metaclass(node):
return node
+def _looks_like_nested_from_six_with_metaclass(node):
+ if len(node.bases) != 1:
+ return False
+ base = node.bases[0]
+ if not isinstance(base, nodes.Call):
+ return False
+ try:
+ if hasattr(base.func, "expr"):
+ # format when explicit 'six.with_metaclass' is used
+ mod = base.func.expr.name
+ func = base.func.attrname
+ func = "{}.{}".format(mod, func)
+ else:
+ # format when 'with_metaclass' is used directly (local import from six)
+ # check reference module to avoid 'with_metaclass' name clashes
+ mod = base.parent.parent
+ import_from = mod.locals["with_metaclass"][0]
+ func = "{}.{}".format(import_from.modname, base.func.name)
+ except (AttributeError, KeyError, IndexError):
+ return False
+ return func == SIX_WITH_METACLASS
+
+
+def transform_six_with_metaclass(node):
+ """Check if the given class node is defined with *six.with_metaclass*
+
+ If so, inject its argument as the metaclass of the underlying class.
+ """
+ call = node.bases[0]
+ node._metaclass = call.args[0]
+ node.bases = call.args[1:]
+
+
register_module_extender(MANAGER, "six", six_moves_transform)
register_module_extender(
MANAGER, "requests.packages.urllib3.packages.six", six_moves_transform
@@ -200,3 +234,8 @@ MANAGER.register_transform(
transform_six_add_metaclass,
_looks_like_decorated_with_six_add_metaclass,
)
+MANAGER.register_transform(
+ nodes.ClassDef,
+ transform_six_with_metaclass,
+ _looks_like_nested_from_six_with_metaclass,
+)
diff --git a/astroid/brain/brain_subprocess.py b/astroid/brain/brain_subprocess.py
index bc35704f..c19b32b1 100644
--- a/astroid/brain/brain_subprocess.py
+++ b/astroid/brain/brain_subprocess.py
@@ -14,6 +14,7 @@ import textwrap
import astroid
+PY39 = sys.version_info >= (3, 9)
PY37 = sys.version_info >= (3, 7)
PY36 = sys.version_info >= (3, 6)
@@ -147,6 +148,12 @@ def _subprocess_transform():
"py3_args": py3_args,
}
)
+ if PY39:
+ code += """
+ @classmethod
+ def __class_getitem__(cls, item):
+ pass
+ """
init_lines = textwrap.dedent(init).splitlines()
indented_init = "\n".join(" " * 4 + line for line in init_lines)
diff --git a/astroid/brain/brain_type.py b/astroid/brain/brain_type.py
new file mode 100644
index 00000000..4e82813f
--- /dev/null
+++ b/astroid/brain/brain_type.py
@@ -0,0 +1,64 @@
+# -*- coding: utf-8 -*-
+"""
+Astroid hooks for type support.
+
+Starting from python3.9, type object behaves as it had __class_getitem__ method.
+However it was not possible to simply add this method inside type's body, otherwise
+all types would also have this method. In this case it would have been possible
+to write str[int].
+Guido Van Rossum proposed a hack to handle this in the interpreter:
+https://github.com/python/cpython/blob/master/Objects/abstract.c#L186-L189
+
+This brain follows the same logic. It is no wise to add permanently the __class_getitem__ method
+to the type object. Instead we choose to add it only in the case of a subscript node
+which inside name node is type.
+Doing this type[int] is allowed whereas str[int] is not.
+
+Thanks to Lukasz Langa for fruitful discussion.
+"""
+import sys
+
+from astroid import MANAGER, extract_node, inference_tip, nodes
+
+
+PY39 = sys.version_info >= (3, 9)
+
+
+def _looks_like_type_subscript(node):
+ """
+ Try to figure out if a Name node is used inside a type related subscript
+
+ :param node: node to check
+ :type node: astroid.node_classes.NodeNG
+ :return: true if the node is a Name node inside a type related subscript
+ :rtype: bool
+ """
+ if isinstance(node, nodes.Name) and isinstance(node.parent, nodes.Subscript):
+ return node.name == "type"
+ return False
+
+
+def infer_type_sub(node, context=None):
+ """
+ Infer a type[...] subscript
+
+ :param node: node to infer
+ :type node: astroid.node_classes.NodeNG
+ :param context: inference context
+ :type context: astroid.context.InferenceContext
+ :return: the inferred node
+ :rtype: nodes.NodeNG
+ """
+ class_src = """
+ class type:
+ def __class_getitem__(cls, key):
+ return cls
+ """
+ node = extract_node(class_src)
+ return node.infer(context=context)
+
+
+if PY39:
+ MANAGER.register_transform(
+ nodes.Name, inference_tip(infer_type_sub), _looks_like_type_subscript
+ )
diff --git a/astroid/context.py b/astroid/context.py
index f1e06974..4bda945f 100644
--- a/astroid/context.py
+++ b/astroid/context.py
@@ -29,8 +29,10 @@ class InferenceContext:
"extra_context",
)
+ maximum_path_visit = 3
+
def __init__(self, path=None, inferred=None):
- self.path = path or set()
+ self.path = path or dict()
"""
:type: set(tuple(NodeNG, optional(str)))
@@ -87,10 +89,10 @@ class InferenceContext:
Allows one to see if the given node has already
been looked at for this inference context"""
name = self.lookupname
- if (node, name) in self.path:
+ if self.path.get((node, name), 0) >= self.maximum_path_visit:
return True
- self.path.add((node, name))
+ self.path[(node, name)] = self.path.setdefault((node, name), 0) + 1
return False
def clone(self):
@@ -108,7 +110,7 @@ class InferenceContext:
@contextlib.contextmanager
def restore_path(self):
- path = set(self.path)
+ path = dict(self.path)
yield
self.path = path
diff --git a/astroid/node_classes.py b/astroid/node_classes.py
index 86529e5a..62438e62 100644
--- a/astroid/node_classes.py
+++ b/astroid/node_classes.py
@@ -2125,6 +2125,11 @@ class AugAssign(mixins.AssignTypeMixin, Statement):
yield self.target
yield self.value
+ def _get_yield_nodes_skip_lambdas(self):
+ """An AugAssign node can contain a Yield node in the value"""
+ yield from self.value._get_yield_nodes_skip_lambdas()
+ yield from super()._get_yield_nodes_skip_lambdas()
+
class Repr(NodeNG):
"""Class representing an :class:`ast.Repr` node.
diff --git a/astroid/rebuilder.py b/astroid/rebuilder.py
index 3fc1a83f..e56abbf8 100644
--- a/astroid/rebuilder.py
+++ b/astroid/rebuilder.py
@@ -238,7 +238,7 @@ class TreeRebuilder:
return type_object.value
- def check_function_type_comment(self, node):
+ def check_function_type_comment(self, node, parent):
type_comment = getattr(node, "type_comment", None)
if not type_comment:
return None
@@ -251,10 +251,10 @@ class TreeRebuilder:
returns = None
argtypes = [
- self.visit(elem, node) for elem in (type_comment_ast.argtypes or [])
+ self.visit(elem, parent) for elem in (type_comment_ast.argtypes or [])
]
if type_comment_ast.returns:
- returns = self.visit(type_comment_ast.returns, node)
+ returns = self.visit(type_comment_ast.returns, parent)
return returns, argtypes
@@ -615,7 +615,7 @@ class TreeRebuilder:
returns = None
type_comment_args = type_comment_returns = None
- type_comment_annotation = self.check_function_type_comment(node)
+ type_comment_annotation = self.check_function_type_comment(node, newnode)
if type_comment_annotation:
type_comment_returns, type_comment_args = type_comment_annotation
newnode.postinit(