summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--.travis.yml2
-rw-r--r--ChangeLog85
-rw-r--r--appveyor.yml3
-rw-r--r--astroid/__init__.py2
-rw-r--r--astroid/__pkginfo__.py9
-rw-r--r--astroid/arguments.py5
-rw-r--r--astroid/brain/brain_builtin_inference.py176
-rw-r--r--astroid/brain/brain_collections.py2
-rw-r--r--astroid/brain/brain_dateutil.py2
-rw-r--r--astroid/brain/brain_fstrings.py7
-rw-r--r--astroid/brain/brain_functools.py13
-rw-r--r--astroid/brain/brain_gi.py2
-rw-r--r--astroid/brain/brain_hashlib.py2
-rw-r--r--astroid/brain/brain_http.py2
-rw-r--r--astroid/brain/brain_hypothesis.py53
-rw-r--r--astroid/brain/brain_io.py2
-rw-r--r--astroid/brain/brain_mechanize.py3
-rw-r--r--astroid/brain/brain_multiprocessing.py2
-rw-r--r--astroid/brain/brain_namedtuple_enum.py43
-rw-r--r--astroid/brain/brain_nose.py2
-rw-r--r--astroid/brain/brain_numpy_core_fromnumeric.py1
-rw-r--r--astroid/brain/brain_numpy_core_function_base.py1
-rw-r--r--astroid/brain/brain_numpy_core_multiarray.py1
-rw-r--r--astroid/brain/brain_numpy_core_numeric.py1
-rw-r--r--astroid/brain/brain_numpy_core_numerictypes.py7
-rw-r--r--astroid/brain/brain_numpy_core_umath.py6
-rw-r--r--astroid/brain/brain_numpy_ndarray.py10
-rw-r--r--astroid/brain/brain_numpy_random_mtrand.py2
-rw-r--r--astroid/brain/brain_numpy_utils.py2
-rw-r--r--astroid/brain/brain_pytest.py2
-rw-r--r--astroid/brain/brain_qt.py2
-rw-r--r--astroid/brain/brain_random.py4
-rwxr-xr-xastroid/brain/brain_scipy_signal.py1
-rw-r--r--astroid/brain/brain_six.py5
-rw-r--r--astroid/brain/brain_ssl.py2
-rw-r--r--astroid/brain/brain_subprocess.py8
-rw-r--r--astroid/brain/brain_threading.py2
-rw-r--r--astroid/brain/brain_type.py64
-rw-r--r--astroid/brain/brain_uuid.py2
-rw-r--r--astroid/builder.py2
-rw-r--r--astroid/context.py14
-rw-r--r--astroid/decorators.py7
-rw-r--r--astroid/exceptions.py2
-rw-r--r--astroid/helpers.py24
-rw-r--r--astroid/interpreter/_import/spec.py124
-rw-r--r--astroid/interpreter/objectmodel.py3
-rw-r--r--astroid/manager.py9
-rw-r--r--astroid/modutils.py124
-rw-r--r--astroid/node_classes.py39
-rw-r--r--astroid/protocols.py9
-rw-r--r--astroid/raw_building.py8
-rw-r--r--astroid/scoped_nodes.py8
-rw-r--r--astroid/test_utils.py2
-rw-r--r--astroid/util.py25
-rw-r--r--setup.py7
-rw-r--r--tests/resources.py3
-rw-r--r--tests/testdata/python3/data/nonregr.py4
-rw-r--r--tests/unittest_brain.py93
-rw-r--r--tests/unittest_brain_numpy_core_fromnumeric.py1
-rw-r--r--tests/unittest_brain_numpy_core_function_base.py1
-rw-r--r--tests/unittest_brain_numpy_core_multiarray.py1
-rw-r--r--tests/unittest_brain_numpy_core_numeric.py1
-rw-r--r--tests/unittest_brain_numpy_core_numerictypes.py2
-rw-r--r--tests/unittest_brain_numpy_core_umath.py3
-rw-r--r--tests/unittest_brain_numpy_ndarray.py4
-rw-r--r--tests/unittest_brain_numpy_random_mtrand.py2
-rw-r--r--tests/unittest_builder.py2
-rw-r--r--tests/unittest_inference.py83
-rw-r--r--tests/unittest_lookup.py2
-rw-r--r--tests/unittest_manager.py2
-rw-r--r--tests/unittest_modutils.py20
-rw-r--r--tests/unittest_nodes.py11
-rw-r--r--tests/unittest_protocols.py2
-rw-r--r--tests/unittest_python3.py2
-rw-r--r--tests/unittest_raw_building.py2
-rw-r--r--tests/unittest_regrtest.py11
-rw-r--r--tests/unittest_scoped_nodes.py17
-rw-r--r--tests/unittest_transforms.py2
-rw-r--r--tests/unittest_utils.py2
-rw-r--r--tox.ini10
81 files changed, 873 insertions, 358 deletions
diff --git a/.gitignore b/.gitignore
index 66d64d3e..4b0ef308 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,3 +13,4 @@ astroid.egg-info/
.cache/
.eggs/
.pytest_cache/
+.mypy_cache/ \ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
index 638720d3..d30c9386 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -19,6 +19,8 @@ jobs:
env: TOXENV=py37
- python: 3.8
env: TOXENV=py38
+ - python: 3.9
+ env: TOXENV=py39
before_install:
- python --version
- uname -a
diff --git a/ChangeLog b/ChangeLog
index 1e7d2a77..02852c69 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -10,15 +10,72 @@ astroid's ChangeLog
What's New in astroid 2.5.0?
============================
Release Date: TBA
+* Adds a brain for type object so that it is possible to write `type[int]` in annotation.
+
+ Fixes PyCQA/pylint#4001
+
+* Add ``__class_getitem__`` method to ``subprocess.Popen`` brain under Python 3.9 so that it is seen as subscriptable by pylint.
+
+ Fixes PyCQA/pylint#4034
+
+* Adds `degrees`, `radians`, which are `numpy ufunc` functions, in the `numpy` brain. Adds `random` function in the `numpy.random` brain.
+
+ Fixes PyCQA/pylint#3856
+
+* Fix deprecated importlib methods
+
+ Closes #703
+
+* Fix a crash in inference caused by `Uninferable` container elements
+
+ Close #866
+
+* Add `python 3.9` support.
+
+* The flat attribute of ``numpy.ndarray`` is now inferred as an ``numpy.ndarray`` itself.
+ It should be a ``numpy.flatiter`` instance, but this class is not yet available in the numpy brain.
+
+ Fixes PyCQA/pylint#3640
+
+* Fix a bug for dunder methods inference of function objects
+
+ Fixes #819
+
+* Fixes a bug in the signature of the ``ndarray.__or__`` method,
+ in the ``brain_numpy_ndarray.py`` module.
+
+ Fixes #815
+
+* Fixes a to-list cast bug in ``starred_assigned_stmts`` method,
+ in the ``protocols.py` module.
+
+* Added a brain for ``hypothesis.strategies.composite``
+
+* The transpose of a ``numpy.ndarray`` is also a ``numpy.ndarray``
+
+ Fixes PyCQA/pylint#3387
* Added a brain for ``sqlalchemy.orm.session``
+* Separate string and bytes classes patching
+
+ Fixes PyCQA/pylint#3599
+
+* Prevent recursion error for self referential length calls
+
+ Close #777
+
* Added missing methods to the brain for ``mechanize``, to fix pylint false positives
Close #793
* Added more supported parameters to ``subprocess.check_output``
+* Fix recursion errors with pandas
+
+ Fixes PyCQA/pylint#2843
+ Fixes PyCQA/pylint#2811
+
* Added exception inference for `UnicodeDecodeError`
Close PyCQA/pylint#3639
@@ -27,15 +84,37 @@ Release Date: TBA
Close PyCQA/pylint#3583
+* Fixed exception-chaining error messages.
-What's New in astroid 2.4.3?
-============================
-Release Date: TBA
+* Fix failure to infer base class type with multiple inheritance and qualified names
+
+ Fixes #843
+
+* Reduce memory usage of astroid's module cache.
+
+* Remove dependency on `imp`.
+
+ Close #594
+ Close #681
+
+* Do not crash when encountering starred assignments in enums.
+
+ Close #835
+
+* Fix a crash in functools.partial inference when the arguments cannot be determined
+
+ Close PyCQA/pylint#3776
* Fix a crash caused by a lookup of a monkey-patched method
Close PyCQA/pylint#3686
+* ``is_generator`` correctly considers `Yield` nodes in `AugAssign` nodes
+
+ This fixes a false positive with the `assignment-from-no-return` pylint check.
+
+ Close PyCQA/pylint#3904
+
What's New in astroid 2.4.2?
============================
diff --git a/appveyor.yml b/appveyor.yml
index b99fff18..dd6e193a 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -13,6 +13,9 @@ environment:
- PYTHON: "C:\\Python37"
TOXENV: "py37"
+ - PYTHON: "C:\\Python38"
+ TOXENV: "py38"
+
init:
- ps: echo $env:TOXENV
- ps: ls C:\Python*
diff --git a/astroid/__init__.py b/astroid/__init__.py
index e869274e..de4b39a8 100644
--- a/astroid/__init__.py
+++ b/astroid/__init__.py
@@ -1,7 +1,7 @@
# Copyright (c) 2006-2013, 2015 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
# Copyright (c) 2014 Google, Inc.
# Copyright (c) 2014 Eevee (Alex Munroe) <amunroe@yelp.com>
-# Copyright (c) 2015-2016, 2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015-2016, 2018, 2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
# Copyright (c) 2016 Derek Gustafson <degustaf@gmail.com>
# Copyright (c) 2016 Moises Lopez <moylop260@vauxoo.com>
diff --git a/astroid/__pkginfo__.py b/astroid/__pkginfo__.py
index aa48c537..150b11c9 100644
--- a/astroid/__pkginfo__.py
+++ b/astroid/__pkginfo__.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
-# Copyright (c) 2014-2019 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014-2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2014 Google, Inc.
# Copyright (c) 2015-2017 Ceridwen <ceridwenv@gmail.com>
# Copyright (c) 2015 Florian Bruhin <me@the-compiler.org>
@@ -14,6 +14,8 @@
# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
# Copyright (c) 2019 Uilian Ries <uilianries@gmail.com>
# Copyright (c) 2019 Thomas Hisch <t.hisch@gmail.com>
+# Copyright (c) 2020 Konrad Weihmann <kweihmann@outlook.com>
+# Copyright (c) 2020 Felix Mölder <felix.moelder@uni-due.de>
# Copyright (c) 2020 Michael <michael-k@users.noreply.github.com>
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
@@ -26,9 +28,9 @@ numversion = tuple(int(elem) for elem in version.split(".") if elem.isdigit())
extras_require = {}
install_requires = [
- "lazy_object_proxy==1.4.*",
+ "lazy_object_proxy>=1.4.0",
"six~=1.12",
- "wrapt~=1.11",
+ "wrapt>=1.11,<1.13",
'typed-ast>=1.4.0,<1.5;implementation_name== "cpython" and python_version<"3.8"',
]
@@ -51,6 +53,7 @@ classifiers = [
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
+ "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
]
diff --git a/astroid/arguments.py b/astroid/arguments.py
index 5f4d9092..a783311c 100644
--- a/astroid/arguments.py
+++ b/astroid/arguments.py
@@ -3,6 +3,7 @@
# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
# Copyright (c) 2018 Anthony Sottile <asottile@umich.edu>
+# Copyright (c) 2020 hippo91 <guillaume.peillex@gmail.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
@@ -173,7 +174,7 @@ class CallSite:
# Too many arguments given and no variable arguments.
if len(self.positional_arguments) > len(funcnode.args.args):
- if not funcnode.args.vararg:
+ if not funcnode.args.vararg and not funcnode.args.posonlyargs:
raise exceptions.InferenceError(
"Too many positional arguments "
"passed to {func!r} that does "
@@ -292,7 +293,7 @@ class CallSite:
except exceptions.NoDefault:
pass
raise exceptions.InferenceError(
- "No value found for argument {name} to " "{func!r}",
+ "No value found for argument {arg} to {func!r}",
call_site=self,
func=funcnode,
arg=name,
diff --git a/astroid/brain/brain_builtin_inference.py b/astroid/brain/brain_builtin_inference.py
index 4b07ac5c..b1f811d4 100644
--- a/astroid/brain/brain_builtin_inference.py
+++ b/astroid/brain/brain_builtin_inference.py
@@ -5,10 +5,11 @@
# Copyright (c) 2015 Rene Zhang <rz99@cornell.edu>
# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
# Copyright (c) 2018 Ville Skyttä <ville.skytta@iki.fi>
+# Copyright (c) 2019-2020 Bryce Guinta <bryce.guinta@protonmail.com>
# Copyright (c) 2019 Stanislav Levin <slev@altlinux.org>
# Copyright (c) 2019 David Liu <david@cs.toronto.edu>
-# Copyright (c) 2019 Bryce Guinta <bryce.guinta@protonmail.com>
# Copyright (c) 2019 Frédéric Chapoton <fchapoton2@gmail.com>
+# Copyright (c) 2020 Ram Rachum <ram@rachum.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
@@ -40,52 +41,90 @@ from astroid import util
OBJECT_DUNDER_NEW = "object.__new__"
-
-def _extend_str(class_node, rvalue):
+STR_CLASS = """
+class whatever(object):
+ def join(self, iterable):
+ return {rvalue}
+ def replace(self, old, new, count=None):
+ return {rvalue}
+ def format(self, *args, **kwargs):
+ return {rvalue}
+ def encode(self, encoding='ascii', errors=None):
+ return b''
+ def decode(self, encoding='ascii', errors=None):
+ return u''
+ def capitalize(self):
+ return {rvalue}
+ def title(self):
+ return {rvalue}
+ def lower(self):
+ return {rvalue}
+ def upper(self):
+ return {rvalue}
+ def swapcase(self):
+ return {rvalue}
+ def index(self, sub, start=None, end=None):
+ return 0
+ def find(self, sub, start=None, end=None):
+ return 0
+ def count(self, sub, start=None, end=None):
+ return 0
+ def strip(self, chars=None):
+ return {rvalue}
+ def lstrip(self, chars=None):
+ return {rvalue}
+ def rstrip(self, chars=None):
+ return {rvalue}
+ def rjust(self, width, fillchar=None):
+ return {rvalue}
+ def center(self, width, fillchar=None):
+ return {rvalue}
+ def ljust(self, width, fillchar=None):
+ return {rvalue}
+"""
+
+
+BYTES_CLASS = """
+class whatever(object):
+ def join(self, iterable):
+ return {rvalue}
+ def replace(self, old, new, count=None):
+ return {rvalue}
+ def decode(self, encoding='ascii', errors=None):
+ return u''
+ def capitalize(self):
+ return {rvalue}
+ def title(self):
+ return {rvalue}
+ def lower(self):
+ return {rvalue}
+ def upper(self):
+ return {rvalue}
+ def swapcase(self):
+ return {rvalue}
+ def index(self, sub, start=None, end=None):
+ return 0
+ def find(self, sub, start=None, end=None):
+ return 0
+ def count(self, sub, start=None, end=None):
+ return 0
+ def strip(self, chars=None):
+ return {rvalue}
+ def lstrip(self, chars=None):
+ return {rvalue}
+ def rstrip(self, chars=None):
+ return {rvalue}
+ def rjust(self, width, fillchar=None):
+ return {rvalue}
+ def center(self, width, fillchar=None):
+ return {rvalue}
+ def ljust(self, width, fillchar=None):
+ return {rvalue}
+"""
+
+
+def _extend_string_class(class_node, code, rvalue):
"""function to extend builtin str/unicode class"""
- code = dedent(
- """
- class whatever(object):
- def join(self, iterable):
- return {rvalue}
- def replace(self, old, new, count=None):
- return {rvalue}
- def format(self, *args, **kwargs):
- return {rvalue}
- def encode(self, encoding='ascii', errors=None):
- return ''
- def decode(self, encoding='ascii', errors=None):
- return u''
- def capitalize(self):
- return {rvalue}
- def title(self):
- return {rvalue}
- def lower(self):
- return {rvalue}
- def upper(self):
- return {rvalue}
- def swapcase(self):
- return {rvalue}
- def index(self, sub, start=None, end=None):
- return 0
- def find(self, sub, start=None, end=None):
- return 0
- def count(self, sub, start=None, end=None):
- return 0
- def strip(self, chars=None):
- return {rvalue}
- def lstrip(self, chars=None):
- return {rvalue}
- def rstrip(self, chars=None):
- return {rvalue}
- def rjust(self, width, fillchar=None):
- return {rvalue}
- def center(self, width, fillchar=None):
- return {rvalue}
- def ljust(self, width, fillchar=None):
- return {rvalue}
- """
- )
code = code.format(rvalue=rvalue)
fake = AstroidBuilder(MANAGER).string_build(code)["whatever"]
for method in fake.mymethods():
@@ -106,8 +145,8 @@ def _extend_builtins(class_transforms):
_extend_builtins(
{
- "bytes": partial(_extend_str, rvalue="b''"),
- "str": partial(_extend_str, rvalue="''"),
+ "bytes": partial(_extend_string_class, code=BYTES_CLASS, rvalue="b''"),
+ "str": partial(_extend_string_class, code=STR_CLASS, rvalue="''"),
}
)
@@ -165,13 +204,13 @@ def _container_generic_inference(node, context, node_type, transform):
if not transformed:
try:
inferred = next(arg.infer(context=context))
- except (InferenceError, StopIteration):
- raise UseInferenceDefault()
+ except (InferenceError, StopIteration) as exc:
+ raise UseInferenceDefault from exc
if inferred is util.Uninferable:
- raise UseInferenceDefault()
+ raise UseInferenceDefault
transformed = transform(inferred)
if not transformed or transformed is util.Uninferable:
- raise UseInferenceDefault()
+ raise UseInferenceDefault
return transformed
@@ -185,6 +224,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(
@@ -267,8 +308,8 @@ def _get_elts(arg, context):
is_iterable = lambda n: isinstance(n, (nodes.List, nodes.Tuple, nodes.Set))
try:
inferred = next(arg.infer(context))
- except (InferenceError, NameInferenceError):
- raise UseInferenceDefault()
+ except (InferenceError, NameInferenceError) as exc:
+ raise UseInferenceDefault from exc
if isinstance(inferred, nodes.Dict):
items = inferred.items
elif is_iterable(inferred):
@@ -371,12 +412,12 @@ def infer_super(node, context=None):
else:
try:
mro_pointer = next(node.args[0].infer(context=context))
- except InferenceError:
- raise UseInferenceDefault
+ except InferenceError as exc:
+ raise UseInferenceDefault from exc
try:
mro_type = next(node.args[1].infer(context=context))
- except InferenceError:
- raise UseInferenceDefault
+ except InferenceError as exc:
+ raise UseInferenceDefault from exc
if mro_pointer is util.Uninferable or mro_type is util.Uninferable:
# No way we could understand this.
@@ -397,8 +438,8 @@ def _infer_getattr_args(node, context):
try:
obj = next(node.args[0].infer(context=context))
attr = next(node.args[1].infer(context=context))
- except InferenceError:
- raise UseInferenceDefault
+ except InferenceError as exc:
+ raise UseInferenceDefault from exc
if obj is util.Uninferable or attr is util.Uninferable:
# If one of the arguments is something we can't infer,
@@ -437,8 +478,8 @@ def infer_getattr(node, context=None):
# Try to infer the default and return it instead.
try:
return next(node.args[2].infer(context=context))
- except InferenceError:
- raise UseInferenceDefault
+ except InferenceError as exc:
+ raise UseInferenceDefault from exc
raise UseInferenceDefault
@@ -505,8 +546,8 @@ def infer_property(node, context=None):
getter = node.args[0]
try:
inferred = next(getter.infer(context=context))
- except InferenceError:
- raise UseInferenceDefault
+ except InferenceError as exc:
+ raise UseInferenceDefault from exc
if not isinstance(inferred, (nodes.FunctionDef, nodes.Lambda)):
raise UseInferenceDefault
@@ -673,12 +714,12 @@ def infer_isinstance(callnode, context=None):
class_container = _class_or_tuple_to_container(
class_or_tuple_node, context=context
)
- except InferenceError:
- raise UseInferenceDefault
+ except InferenceError as exc:
+ raise UseInferenceDefault from exc
try:
isinstance_bool = helpers.object_isinstance(obj_node, class_container, context)
except AstroidTypeError as exc:
- raise UseInferenceDefault("TypeError: " + str(exc))
+ raise UseInferenceDefault("TypeError: " + str(exc)) from exc
except MroError as exc:
raise UseInferenceDefault from exc
if isinstance_bool is util.Uninferable:
@@ -721,6 +762,7 @@ def infer_len(node, context=None):
"({len}) given".format(len=len(call.positional_arguments))
)
[argument_node] = call.positional_arguments
+
try:
return nodes.Const(helpers.object_len(argument_node, context=context))
except (AstroidTypeError, InferenceError) as exc:
diff --git a/astroid/brain/brain_collections.py b/astroid/brain/brain_collections.py
index 669c6ca4..6594e0c7 100644
--- a/astroid/brain/brain_collections.py
+++ b/astroid/brain/brain_collections.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright (c) 2016, 2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2016, 2018, 2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2016-2017 Łukasz Rogalski <rogalski.91@gmail.com>
# Copyright (c) 2017 Derek Gustafson <degustaf@gmail.com>
# Copyright (c) 2018 Ioana Tagirta <ioana.tagirta@gmail.com>
diff --git a/astroid/brain/brain_dateutil.py b/astroid/brain/brain_dateutil.py
index 9fdb9fde..47b443f5 100644
--- a/astroid/brain/brain_dateutil.py
+++ b/astroid/brain/brain_dateutil.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2015-2016, 2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015-2016, 2018, 2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2015 raylu <lurayl@gmail.com>
# Copyright (c) 2016 Ceridwen <ceridwenv@gmail.com>
diff --git a/astroid/brain/brain_fstrings.py b/astroid/brain/brain_fstrings.py
index 298d58af..fe9911a7 100644
--- a/astroid/brain/brain_fstrings.py
+++ b/astroid/brain/brain_fstrings.py
@@ -1,8 +1,9 @@
-# Copyright (c) 2017-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2017-2018, 2020 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2020 Karthikeyan Singaravelan <tir.karthi@gmail.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
-import collections
+import collections.abc
import sys
import astroid
@@ -19,7 +20,7 @@ def _clone_node_with_lineno(node, parent, lineno):
new_node = cls(**init_params)
if hasattr(node, "postinit") and _astroid_fields:
for param, child in postinit_params.items():
- if child and not isinstance(child, collections.Sequence):
+ if child and not isinstance(child, collections.abc.Sequence):
cloned_child = _clone_node_with_lineno(
node=child, lineno=new_node.lineno, parent=new_node
)
diff --git a/astroid/brain/brain_functools.py b/astroid/brain/brain_functools.py
index d6c60691..f943f71a 100644
--- a/astroid/brain/brain_functools.py
+++ b/astroid/brain/brain_functools.py
@@ -86,11 +86,14 @@ def _functools_partial_inference(node, context=None):
# Determine if the passed keywords into the callsite are supported
# by the wrapped function.
- function_parameters = chain(
- inferred_wrapped_function.args.args or (),
- inferred_wrapped_function.args.posonlyargs or (),
- inferred_wrapped_function.args.kwonlyargs or (),
- )
+ if not inferred_wrapped_function.args:
+ function_parameters = []
+ else:
+ function_parameters = chain(
+ inferred_wrapped_function.args.args or (),
+ inferred_wrapped_function.args.posonlyargs or (),
+ inferred_wrapped_function.args.kwonlyargs or (),
+ )
parameter_names = set(
param.name
for param in function_parameters
diff --git a/astroid/brain/brain_gi.py b/astroid/brain/brain_gi.py
index e49f3a22..2c65d9fb 100644
--- a/astroid/brain/brain_gi.py
+++ b/astroid/brain/brain_gi.py
@@ -2,7 +2,7 @@
# Copyright (c) 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
# Copyright (c) 2014 Google, Inc.
# Copyright (c) 2014 Cole Robinson <crobinso@redhat.com>
-# Copyright (c) 2015-2016, 2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015-2016, 2018, 2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
# Copyright (c) 2015 David Shea <dshea@redhat.com>
# Copyright (c) 2016 Jakub Wilk <jwilk@jwilk.net>
diff --git a/astroid/brain/brain_hashlib.py b/astroid/brain/brain_hashlib.py
index eb34e159..95893628 100644
--- a/astroid/brain/brain_hashlib.py
+++ b/astroid/brain/brain_hashlib.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2016, 2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2016, 2018, 2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2018 David Poirier <david-poirier-csn@users.noreply.github.com>
# Copyright (c) 2018 wgehalo <wgehalo@gmail.com>
# Copyright (c) 2018 Ioana Tagirta <ioana.tagirta@gmail.com>
diff --git a/astroid/brain/brain_http.py b/astroid/brain/brain_http.py
index b16464e8..f4158756 100644
--- a/astroid/brain/brain_http.py
+++ b/astroid/brain/brain_http.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2018-2019 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2019-2020 Claudiu Popa <pcmanticore@gmail.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
diff --git a/astroid/brain/brain_hypothesis.py b/astroid/brain/brain_hypothesis.py
new file mode 100644
index 00000000..be791514
--- /dev/null
+++ b/astroid/brain/brain_hypothesis.py
@@ -0,0 +1,53 @@
+# 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
+"""
+Astroid hook for the Hypothesis library.
+
+Without this hook pylint reports no-value-for-parameter for use of strategies
+defined using the `@hypothesis.strategies.composite` decorator. For example:
+
+ from hypothesis import strategies as st
+
+ @st.composite
+ def a_strategy(draw):
+ return draw(st.integers())
+
+ a_strategy()
+
+"""
+
+import astroid
+
+COMPOSITE_NAMES = (
+ "composite",
+ "st.composite",
+ "strategies.composite",
+ "hypothesis.strategies.composite",
+)
+
+
+def is_decorated_with_st_composite(node):
+ """Return True if a decorated node has @st.composite applied."""
+ if node.decorators and node.args.args and node.args.args[0].name == "draw":
+ for decorator_attribute in node.decorators.nodes:
+ if decorator_attribute.as_string() in COMPOSITE_NAMES:
+ return True
+ return False
+
+
+def remove_draw_parameter_from_composite_strategy(node):
+ """Given that the FunctionDef is decorated with @st.composite, remove the
+ first argument (`draw`) - it's always supplied by Hypothesis so we don't
+ need to emit the no-value-for-parameter lint.
+ """
+ del node.args.args[0]
+ del node.args.annotations[0]
+ del node.args.type_comment_args[0]
+ return node
+
+
+astroid.MANAGER.register_transform(
+ node_class=astroid.FunctionDef,
+ transform=remove_draw_parameter_from_composite_strategy,
+ predicate=is_decorated_with_st_composite,
+)
diff --git a/astroid/brain/brain_io.py b/astroid/brain/brain_io.py
index c7453112..884544b1 100644
--- a/astroid/brain/brain_io.py
+++ b/astroid/brain/brain_io.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2016, 2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2016, 2018, 2020 Claudiu Popa <pcmanticore@gmail.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
diff --git a/astroid/brain/brain_mechanize.py b/astroid/brain/brain_mechanize.py
index 88623a82..91a9ce01 100644
--- a/astroid/brain/brain_mechanize.py
+++ b/astroid/brain/brain_mechanize.py
@@ -1,7 +1,8 @@
# Copyright (c) 2012-2013 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
# Copyright (c) 2014 Google, Inc.
-# Copyright (c) 2015-2016, 2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015-2016, 2018, 2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2016 Ceridwen <ceridwenv@gmail.com>
+# Copyright (c) 2020 Peter Kolbus <peter.kolbus@gmail.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
diff --git a/astroid/brain/brain_multiprocessing.py b/astroid/brain/brain_multiprocessing.py
index 3629b032..4ffd9780 100644
--- a/astroid/brain/brain_multiprocessing.py
+++ b/astroid/brain/brain_multiprocessing.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2016, 2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2016, 2018, 2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2019 Hugo van Kemenade <hugovk@users.noreply.github.com>
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
diff --git a/astroid/brain/brain_namedtuple_enum.py b/astroid/brain/brain_namedtuple_enum.py
index 13fcf793..03877938 100644
--- a/astroid/brain/brain_namedtuple_enum.py
+++ b/astroid/brain/brain_namedtuple_enum.py
@@ -13,6 +13,8 @@
# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
# Copyright (c) 2018 Ville Skyttä <ville.skytta@iki.fi>
# Copyright (c) 2019 Ashley Whetter <ashley@awhetter.co.uk>
+# Copyright (c) 2020 hippo91 <guillaume.peillex@gmail.com>
+# Copyright (c) 2020 Ram Rachum <ram@rachum.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
@@ -51,8 +53,8 @@ def _infer_first(node, context):
raise UseInferenceDefault()
else:
return value
- except StopIteration:
- raise InferenceError()
+ except StopIteration as exc:
+ raise InferenceError from exc
def _find_func_form_arguments(node, context):
@@ -88,7 +90,7 @@ def infer_func_form(node, base_type, context=None, enum=False):
name, names = _find_func_form_arguments(node, context)
try:
attributes = names.value.replace(",", " ").split()
- except AttributeError:
+ except AttributeError as exc:
if not enum:
attributes = [
_infer_first(const, context).value for const in names.elts
@@ -117,11 +119,11 @@ def infer_func_form(node, base_type, context=None, enum=False):
_infer_first(const, context).value for const in names.elts
]
else:
- raise AttributeError
+ raise AttributeError from exc
if not attributes:
- raise AttributeError
- except (AttributeError, exceptions.InferenceError):
- raise UseInferenceDefault()
+ raise AttributeError from exc
+ except (AttributeError, exceptions.InferenceError) as exc:
+ raise UseInferenceDefault from exc
attributes = [attr for attr in attributes if " " not in attr]
@@ -312,7 +314,6 @@ def infer_enum_class(node):
if any(not isinstance(value, nodes.AssignName) for value in values):
continue
- targets = []
stmt = values[0].statement()
if isinstance(stmt, nodes.Assign):
if isinstance(stmt.targets[0], nodes.Tuple):
@@ -336,6 +337,8 @@ def infer_enum_class(node):
new_targets = []
for target in targets:
+ if isinstance(target, nodes.Starred):
+ continue
# Replace all the assignments with our mocked class.
classdef = dedent(
"""
@@ -399,14 +402,29 @@ def infer_typing_namedtuple_class(class_node, context=None):
return iter((generated_class_node,))
+def infer_typing_namedtuple_function(node, context=None):
+ """
+ Starting with python3.9, NamedTuple is a function of the typing module.
+ The class NamedTuple is build dynamically through a call to `type` during
+ initialization of the `_NamedTuple` variable.
+ """
+ klass = extract_node(
+ """
+ from typing import _NamedTuple
+ _NamedTuple
+ """
+ )
+ return klass.infer(context)
+
+
def infer_typing_namedtuple(node, context=None):
"""Infer a typing.NamedTuple(...) call."""
# This is essentially a namedtuple with different arguments
# so we extract the args and infer a named tuple.
try:
func = next(node.func.infer())
- except InferenceError:
- raise UseInferenceDefault
+ except InferenceError as exc:
+ raise UseInferenceDefault from exc
if func.qname() != "typing.NamedTuple":
raise UseInferenceDefault
@@ -451,5 +469,10 @@ MANAGER.register_transform(
nodes.ClassDef, inference_tip(infer_typing_namedtuple_class), _has_namedtuple_base
)
MANAGER.register_transform(
+ nodes.FunctionDef,
+ inference_tip(infer_typing_namedtuple_function),
+ lambda node: node.name == "NamedTuple" and node.parent.name == "typing",
+)
+MANAGER.register_transform(
nodes.Call, inference_tip(infer_typing_namedtuple), _looks_like_typing_namedtuple
)
diff --git a/astroid/brain/brain_nose.py b/astroid/brain/brain_nose.py
index d0280a3f..5d7d83a6 100644
--- a/astroid/brain/brain_nose.py
+++ b/astroid/brain/brain_nose.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2015-2016, 2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015-2016, 2018, 2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2016 Ceridwen <ceridwenv@gmail.com>
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
diff --git a/astroid/brain/brain_numpy_core_fromnumeric.py b/astroid/brain/brain_numpy_core_fromnumeric.py
index 62dfe991..b66fc83a 100644
--- a/astroid/brain/brain_numpy_core_fromnumeric.py
+++ b/astroid/brain/brain_numpy_core_fromnumeric.py
@@ -1,4 +1,5 @@
# Copyright (c) 2019 hippo91 <guillaume.peillex@gmail.com>
+# Copyright (c) 2020 Claudiu Popa <pcmanticore@gmail.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
diff --git a/astroid/brain/brain_numpy_core_function_base.py b/astroid/brain/brain_numpy_core_function_base.py
index 58aa0a98..534ae872 100644
--- a/astroid/brain/brain_numpy_core_function_base.py
+++ b/astroid/brain/brain_numpy_core_function_base.py
@@ -1,4 +1,5 @@
# Copyright (c) 2019 hippo91 <guillaume.peillex@gmail.com>
+# Copyright (c) 2020 Claudiu Popa <pcmanticore@gmail.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
diff --git a/astroid/brain/brain_numpy_core_multiarray.py b/astroid/brain/brain_numpy_core_multiarray.py
index b2e32bc8..55be31e0 100644
--- a/astroid/brain/brain_numpy_core_multiarray.py
+++ b/astroid/brain/brain_numpy_core_multiarray.py
@@ -1,4 +1,5 @@
# Copyright (c) 2019-2020 hippo91 <guillaume.peillex@gmail.com>
+# Copyright (c) 2020 Claudiu Popa <pcmanticore@gmail.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
diff --git a/astroid/brain/brain_numpy_core_numeric.py b/astroid/brain/brain_numpy_core_numeric.py
index 2a6f37e3..53e3a94e 100644
--- a/astroid/brain/brain_numpy_core_numeric.py
+++ b/astroid/brain/brain_numpy_core_numeric.py
@@ -1,4 +1,5 @@
# Copyright (c) 2019 hippo91 <guillaume.peillex@gmail.com>
+# Copyright (c) 2020 Claudiu Popa <pcmanticore@gmail.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
diff --git a/astroid/brain/brain_numpy_core_numerictypes.py b/astroid/brain/brain_numpy_core_numerictypes.py
index 6ac4a146..c903cbd6 100644
--- a/astroid/brain/brain_numpy_core_numerictypes.py
+++ b/astroid/brain/brain_numpy_core_numerictypes.py
@@ -1,4 +1,5 @@
# Copyright (c) 2019-2020 hippo91 <guillaume.peillex@gmail.com>
+# Copyright (c) 2020 Claudiu Popa <pcmanticore@gmail.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
@@ -20,12 +21,14 @@ def numpy_core_numerictypes_transform():
# different types defined in numerictypes.py
class generic(object):
def __init__(self, value):
- self.T = None
+ self.T = np.ndarray([0, 0])
self.base = None
self.data = None
self.dtype = None
self.flags = None
- self.flat = None
+ # Should be a numpy.flatiter instance but not available for now
+ # Putting an array instead so that iteration and indexing are authorized
+ self.flat = np.ndarray([0, 0])
self.imag = None
self.itemsize = None
self.nbytes = None
diff --git a/astroid/brain/brain_numpy_core_umath.py b/astroid/brain/brain_numpy_core_umath.py
index d76161d0..73613b86 100644
--- a/astroid/brain/brain_numpy_core_umath.py
+++ b/astroid/brain/brain_numpy_core_umath.py
@@ -1,9 +1,11 @@
# Copyright (c) 2019 hippo91 <guillaume.peillex@gmail.com>
+# Copyright (c) 2020 Claudiu Popa <pcmanticore@gmail.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
-
+#  Note: starting with version 1.18 numpy module has `__getattr__` method which prevent `pylint` to emit `no-member` message for
+#  all numpy's attributes. (see pylint's module typecheck in `_emit_no_member` function)
"""Astroid hooks for numpy.core.umath module."""
import astroid
@@ -76,6 +78,7 @@ def numpy_core_umath_transform():
conjugate = FakeUfuncOneArg()
cosh = FakeUfuncOneArg()
deg2rad = FakeUfuncOneArg()
+ degrees = FakeUfuncOneArg()
exp2 = FakeUfuncOneArg()
expm1 = FakeUfuncOneArg()
fabs = FakeUfuncOneArg()
@@ -90,6 +93,7 @@ def numpy_core_umath_transform():
negative = FakeUfuncOneArg()
positive = FakeUfuncOneArg()
rad2deg = FakeUfuncOneArg()
+ radians = FakeUfuncOneArg()
reciprocal = FakeUfuncOneArg()
rint = FakeUfuncOneArg()
sign = FakeUfuncOneArg()
diff --git a/astroid/brain/brain_numpy_ndarray.py b/astroid/brain/brain_numpy_ndarray.py
index d40a7dd0..39ad8114 100644
--- a/astroid/brain/brain_numpy_ndarray.py
+++ b/astroid/brain/brain_numpy_ndarray.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2015-2016, 2018-2019 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015-2016, 2018-2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2016 Ceridwen <ceridwenv@gmail.com>
# Copyright (c) 2017-2020 hippo91 <guillaume.peillex@gmail.com>
@@ -17,13 +17,15 @@ def infer_numpy_ndarray(node, context=None):
class ndarray(object):
def __init__(self, shape, dtype=float, buffer=None, offset=0,
strides=None, order=None):
- self.T = None
+ self.T = numpy.ndarray([0, 0])
self.base = None
self.ctypes = None
self.data = None
self.dtype = None
self.flags = None
- self.flat = None
+ # Should be a numpy.flatiter instance but not available for now
+ # Putting an array instead so that iteration and indexing are authorized
+ self.flat = np.ndarray([0, 0])
self.imag = np.ndarray([0, 0])
self.itemsize = None
self.nbytes = None
@@ -71,7 +73,7 @@ def infer_numpy_ndarray(node, context=None):
def __mul__(self, value): return numpy.ndarray([0, 0])
def __ne__(self, value): return numpy.ndarray([0, 0])
def __neg__(self): return numpy.ndarray([0, 0])
- def __or__(self): return numpy.ndarray([0, 0])
+ def __or__(self, value): return numpy.ndarray([0, 0])
def __pos__(self): return numpy.ndarray([0, 0])
def __pow__(self): return numpy.ndarray([0, 0])
def __repr__(self): return str()
diff --git a/astroid/brain/brain_numpy_random_mtrand.py b/astroid/brain/brain_numpy_random_mtrand.py
index cffdceef..70d11ea4 100644
--- a/astroid/brain/brain_numpy_random_mtrand.py
+++ b/astroid/brain/brain_numpy_random_mtrand.py
@@ -1,4 +1,5 @@
# Copyright (c) 2019 hippo91 <guillaume.peillex@gmail.com>
+# Copyright (c) 2020 Claudiu Popa <pcmanticore@gmail.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
@@ -44,6 +45,7 @@ def numpy_random_mtrand_transform():
import numpy
return numpy.ndarray((1,1))
def randn(*args): return uninferable
+ def random(size=None): return uninferable
def random_integers(low, high=None, size=None): return uninferable
def random_sample(size=None): return uninferable
def rayleigh(scale=1.0, size=None): return uninferable
diff --git a/astroid/brain/brain_numpy_utils.py b/astroid/brain/brain_numpy_utils.py
index b29d2719..96e98bb1 100644
--- a/astroid/brain/brain_numpy_utils.py
+++ b/astroid/brain/brain_numpy_utils.py
@@ -1,5 +1,5 @@
+# Copyright (c) 2019-2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2019-2020 hippo91 <guillaume.peillex@gmail.com>
-# Copyright (c) 2019 Claudiu Popa <pcmanticore@gmail.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
diff --git a/astroid/brain/brain_pytest.py b/astroid/brain/brain_pytest.py
index 56202ab8..a1f9412c 100644
--- a/astroid/brain/brain_pytest.py
+++ b/astroid/brain/brain_pytest.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2014-2016, 2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014-2016, 2018, 2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2014 Jeff Quast <contact@jeffquast.com>
# Copyright (c) 2014 Google, Inc.
# Copyright (c) 2016 Florian Bruhin <me@the-compiler.org>
diff --git a/astroid/brain/brain_qt.py b/astroid/brain/brain_qt.py
index b703b373..b59f72df 100644
--- a/astroid/brain/brain_qt.py
+++ b/astroid/brain/brain_qt.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2015-2016, 2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015-2016, 2018, 2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2016 Ceridwen <ceridwenv@gmail.com>
# Copyright (c) 2017 Roy Wright <roy@wright.org>
# Copyright (c) 2018 Ashley Whetter <ashley@awhetter.co.uk>
diff --git a/astroid/brain/brain_random.py b/astroid/brain/brain_random.py
index 5ec858a1..ee116474 100644
--- a/astroid/brain/brain_random.py
+++ b/astroid/brain/brain_random.py
@@ -47,8 +47,8 @@ def infer_random_sample(node, context=None):
try:
elts = random.sample(inferred_sequence.elts, length.value)
- except ValueError:
- raise astroid.UseInferenceDefault
+ except ValueError as exc:
+ raise astroid.UseInferenceDefault from exc
new_node = astroid.List(
lineno=node.lineno, col_offset=node.col_offset, parent=node.scope()
diff --git a/astroid/brain/brain_scipy_signal.py b/astroid/brain/brain_scipy_signal.py
index 996300d4..31908b8c 100755
--- a/astroid/brain/brain_scipy_signal.py
+++ b/astroid/brain/brain_scipy_signal.py
@@ -1,4 +1,5 @@
# Copyright (c) 2019 Valentin Valls <valentin.valls@esrf.fr>
+# Copyright (c) 2020 Claudiu Popa <pcmanticore@gmail.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
diff --git a/astroid/brain/brain_six.py b/astroid/brain/brain_six.py
index 46d9fa32..389037f2 100644
--- a/astroid/brain/brain_six.py
+++ b/astroid/brain/brain_six.py
@@ -1,6 +1,7 @@
# Copyright (c) 2014-2016, 2018, 2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
+# Copyright (c) 2020 Ram Rachum <ram@rachum.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
@@ -145,8 +146,8 @@ def _six_fail_hook(modname):
attribute = modname[start_index:].lstrip(".").replace(".", "_")
try:
import_attr = module.getattr(attribute)[0]
- except AttributeInferenceError:
- raise AstroidBuildingError(modname=modname)
+ except AttributeInferenceError as exc:
+ raise AstroidBuildingError(modname=modname) from exc
if isinstance(import_attr, nodes.Import):
submodule = MANAGER.ast_from_module_name(import_attr.names[0][0])
return submodule
diff --git a/astroid/brain/brain_ssl.py b/astroid/brain/brain_ssl.py
index 2ae21c35..febf8cb6 100644
--- a/astroid/brain/brain_ssl.py
+++ b/astroid/brain/brain_ssl.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2016, 2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2016, 2018, 2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2016 Ceridwen <ceridwenv@gmail.com>
# Copyright (c) 2019 Benjamin Elven <25181435+S3ntinelX@users.noreply.github.com>
diff --git a/astroid/brain/brain_subprocess.py b/astroid/brain/brain_subprocess.py
index 2769a039..c19b32b1 100644
--- a/astroid/brain/brain_subprocess.py
+++ b/astroid/brain/brain_subprocess.py
@@ -3,6 +3,7 @@
# Copyright (c) 2018 Peter Talley <peterctalley@gmail.com>
# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
# Copyright (c) 2019 Hugo van Kemenade <hugovk@users.noreply.github.com>
+# Copyright (c) 2020 Peter Pentchev <roam@ringlet.net>
# 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
@@ -13,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)
@@ -146,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_threading.py b/astroid/brain/brain_threading.py
index ba3085b5..2c4c36f7 100644
--- a/astroid/brain/brain_threading.py
+++ b/astroid/brain/brain_threading.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright (c) 2016, 2018-2019 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2016, 2018-2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
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/brain/brain_uuid.py b/astroid/brain/brain_uuid.py
index 5a33fc25..192accfc 100644
--- a/astroid/brain/brain_uuid.py
+++ b/astroid/brain/brain_uuid.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2017-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2017-2018, 2020 Claudiu Popa <pcmanticore@gmail.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
diff --git a/astroid/builder.py b/astroid/builder.py
index 142764b1..51ce56fd 100644
--- a/astroid/builder.py
+++ b/astroid/builder.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2006-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
# Copyright (c) 2013 Phil Schaf <flying-sheep@web.de>
-# Copyright (c) 2014-2019 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014-2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2014-2015 Google, Inc.
# Copyright (c) 2014 Alexander Presnyakov <flagist0@gmail.com>
# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
diff --git a/astroid/context.py b/astroid/context.py
index 82a80a06..4bda945f 100644
--- a/astroid/context.py
+++ b/astroid/context.py
@@ -1,7 +1,8 @@
-# Copyright (c) 2015-2016, 2018-2019 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015-2016, 2018-2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
+# Copyright (c) 2020 Bryce Guinta <bryce.guinta@protonmail.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
@@ -107,17 +108,6 @@ class InferenceContext:
clone.extra_context = self.extra_context
return clone
- def cache_generator(self, key, generator):
- """Cache result of generator into dictionary
-
- Used to cache inference results"""
- results = []
- for result in generator:
- results.append(result)
- yield result
-
- self.inferred[key] = tuple(results)
-
@contextlib.contextmanager
def restore_path(self):
path = dict(self.path)
diff --git a/astroid/decorators.py b/astroid/decorators.py
index 0f3632c4..cd223120 100644
--- a/astroid/decorators.py
+++ b/astroid/decorators.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2015-2016, 2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015-2016, 2018, 2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
# Copyright (c) 2015 Florian Bruhin <me@the-compiler.org>
# Copyright (c) 2016 Derek Gustafson <degustaf@gmail.com>
@@ -7,6 +7,7 @@
# Copyright (c) 2018 Ashley Whetter <ashley@awhetter.co.uk>
# Copyright (c) 2018 HoverHell <hoverhell@gmail.com>
# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
+# Copyright (c) 2020 Ram Rachum <ram@rachum.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
@@ -134,9 +135,9 @@ def raise_if_nothing_inferred(func, instance, args, kwargs):
# generator is empty
if error.args:
# pylint: disable=not-a-mapping
- raise exceptions.InferenceError(**error.args[0])
+ raise exceptions.InferenceError(**error.args[0]) from error
raise exceptions.InferenceError(
"StopIteration raised without any error information."
- )
+ ) from error
yield from generator
diff --git a/astroid/exceptions.py b/astroid/exceptions.py
index 08e72c13..6801a16a 100644
--- a/astroid/exceptions.py
+++ b/astroid/exceptions.py
@@ -1,6 +1,6 @@
# Copyright (c) 2007, 2009-2010, 2013 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
# Copyright (c) 2014 Google, Inc.
-# Copyright (c) 2015-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015-2018, 2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
# Copyright (c) 2016 Derek Gustafson <degustaf@gmail.com>
# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
diff --git a/astroid/helpers.py b/astroid/helpers.py
index 8ab68799..7a9fae17 100644
--- a/astroid/helpers.py
+++ b/astroid/helpers.py
@@ -1,6 +1,9 @@
# Copyright (c) 2015-2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
+# Copyright (c) 2020 Simon Hewitt <si@sjhewitt.co.uk>
+# Copyright (c) 2020 Bryce Guinta <bryce.guinta@protonmail.com>
+# Copyright (c) 2020 Ram Rachum <ram@rachum.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
@@ -237,13 +240,32 @@ def object_len(node, context=None):
:raises AstroidTypeError: If an invalid node is returned
from __len__ method or no __len__ method exists
:raises InferenceError: If the given node cannot be inferred
- or if multiple nodes are inferred
+ or if multiple nodes are inferred or if the code executed in python
+ would result in a infinite recursive check for length
:rtype int: Integer length of node
"""
# pylint: disable=import-outside-toplevel; circular import
from astroid.objects import FrozenSet
inferred_node = safe_infer(node, context=context)
+
+ # prevent self referential length calls from causing a recursion error
+ # see https://github.com/PyCQA/astroid/issues/777
+ node_frame = node.frame()
+ if (
+ isinstance(node_frame, scoped_nodes.FunctionDef)
+ and node_frame.name == "__len__"
+ and inferred_node is not None
+ and inferred_node._proxied == node_frame.parent
+ ):
+ message = (
+ "Self referential __len__ function will "
+ "cause a RecursionError on line {} of {}".format(
+ node.lineno, node.root().file
+ )
+ )
+ raise exceptions.InferenceError(message)
+
if inferred_node is None or inferred_node is util.Uninferable:
raise exceptions.InferenceError(node=node)
if isinstance(inferred_node, nodes.Const) and isinstance(
diff --git a/astroid/interpreter/_import/spec.py b/astroid/interpreter/_import/spec.py
index 3cf5fea5..95b069e0 100644
--- a/astroid/interpreter/_import/spec.py
+++ b/astroid/interpreter/_import/spec.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2016-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2016-2018, 2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2016 Derek Gustafson <degustaf@gmail.com>
# Copyright (c) 2017 Chris Philip <chrisp533@gmail.com>
# Copyright (c) 2017 Hugo <hugovk@users.noreply.github.com>
@@ -7,22 +7,19 @@
# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
# Copyright (c) 2019 Hugo van Kemenade <hugovk@users.noreply.github.com>
# Copyright (c) 2019 Ashley Whetter <ashley@awhetter.co.uk>
+# Copyright (c) 2020 Gergely Kalmar <GergelyKalmar@users.noreply.github.com>
+# Copyright (c) 2020 Peter Kolbus <peter.kolbus@gmail.com>
+# Copyright (c) 2020 Raphael Gaschignard <raphael@rtpg.co>
import abc
import collections
import distutils
import enum
-import imp
import os
import sys
import zipimport
-try:
- import importlib.machinery
-
- _HAS_MACHINERY = True
-except ImportError:
- _HAS_MACHINERY = False
+import importlib.machinery
try:
from functools import lru_cache
@@ -37,22 +34,6 @@ ModuleType = enum.Enum(
"PY_CODERESOURCE PY_COMPILED PY_FROZEN PY_RESOURCE "
"PY_SOURCE PY_ZIPMODULE PY_NAMESPACE",
)
-_ImpTypes = {
- imp.C_BUILTIN: ModuleType.C_BUILTIN,
- imp.C_EXTENSION: ModuleType.C_EXTENSION,
- imp.PKG_DIRECTORY: ModuleType.PKG_DIRECTORY,
- imp.PY_COMPILED: ModuleType.PY_COMPILED,
- imp.PY_FROZEN: ModuleType.PY_FROZEN,
- imp.PY_SOURCE: ModuleType.PY_SOURCE,
-}
-if hasattr(imp, "PY_RESOURCE"):
- _ImpTypes[imp.PY_RESOURCE] = ModuleType.PY_RESOURCE
-if hasattr(imp, "PY_CODERESOURCE"):
- _ImpTypes[imp.PY_CODERESOURCE] = ModuleType.PY_CODERESOURCE
-
-
-def _imp_type_to_module_type(imp_type):
- return _ImpTypes[imp_type]
_ModuleSpec = collections.namedtuple(
@@ -114,26 +95,59 @@ class Finder:
"""Get a list of extra paths where this finder can search."""
-class ImpFinder(Finder):
- """A finder based on the imp module."""
+class ImportlibFinder(Finder):
+ """A finder based on the importlib module."""
+
+ _SUFFIXES = (
+ [(s, ModuleType.C_EXTENSION) for s in importlib.machinery.EXTENSION_SUFFIXES]
+ + [(s, ModuleType.PY_SOURCE) for s in importlib.machinery.SOURCE_SUFFIXES]
+ + [(s, ModuleType.PY_COMPILED) for s in importlib.machinery.BYTECODE_SUFFIXES]
+ )
def find_module(self, modname, module_parts, processed, submodule_path):
+ if not isinstance(modname, str):
+ raise TypeError("'modname' must be a str, not {}".format(type(modname)))
if submodule_path is not None:
submodule_path = list(submodule_path)
- try:
- stream, mp_filename, mp_desc = imp.find_module(modname, submodule_path)
- except ImportError:
- return None
-
- # Close resources.
- if stream:
- stream.close()
-
- return ModuleSpec(
- name=modname,
- location=mp_filename,
- module_type=_imp_type_to_module_type(mp_desc[2]),
- )
+ else:
+ try:
+ spec = importlib.util.find_spec(modname)
+ if spec:
+ if spec.loader is importlib.machinery.BuiltinImporter:
+ return ModuleSpec(
+ name=modname,
+ location=None,
+ module_type=ModuleType.C_BUILTIN,
+ )
+ if spec.loader is importlib.machinery.FrozenImporter:
+ return ModuleSpec(
+ name=modname,
+ location=None,
+ module_type=ModuleType.PY_FROZEN,
+ )
+ except ValueError:
+ pass
+ submodule_path = sys.path
+
+ for entry in submodule_path:
+ package_directory = os.path.join(entry, modname)
+ for suffix in [".py", importlib.machinery.BYTECODE_SUFFIXES[0]]:
+ package_file_name = "__init__" + suffix
+ file_path = os.path.join(package_directory, package_file_name)
+ if os.path.isfile(file_path):
+ return ModuleSpec(
+ name=modname,
+ location=package_directory,
+ module_type=ModuleType.PKG_DIRECTORY,
+ )
+ for suffix, type_ in ImportlibFinder._SUFFIXES:
+ file_name = modname + suffix
+ file_path = os.path.join(entry, file_name)
+ if os.path.isfile(file_path):
+ return ModuleSpec(
+ name=modname, location=file_path, module_type=type_
+ )
+ return None
def contribute_to_path(self, spec, processed):
if spec.location is None:
@@ -159,7 +173,7 @@ class ImpFinder(Finder):
return path
-class ExplicitNamespacePackageFinder(ImpFinder):
+class ExplicitNamespacePackageFinder(ImportlibFinder):
"""A finder for the explicit namespace packages, generated through pkg_resources."""
def find_module(self, modname, module_parts, processed, submodule_path):
@@ -229,10 +243,12 @@ class PathSpecFinder(Finder):
return None
-_SPEC_FINDERS = (ImpFinder, ZipFinder)
-if _HAS_MACHINERY:
- _SPEC_FINDERS += (PathSpecFinder,)
-_SPEC_FINDERS += (ExplicitNamespacePackageFinder,)
+_SPEC_FINDERS = (
+ ImportlibFinder,
+ ZipFinder,
+ PathSpecFinder,
+ ExplicitNamespacePackageFinder,
+)
def _is_setuptools_namespace(location):
@@ -240,7 +256,7 @@ def _is_setuptools_namespace(location):
with open(os.path.join(location, "__init__.py"), "rb") as stream:
data = stream.read(4096)
except IOError:
- pass
+ return None
else:
extend_path = b"pkgutil" in data and b"extend_path" in data
declare_namespace = (
@@ -257,6 +273,16 @@ def _cached_set_diff(left, right):
def _precache_zipimporters(path=None):
+ """
+ For each path that has not been already cached
+ in the sys.path_importer_cache, create a new zipimporter
+ instance and add it into the cache.
+ Return a dict associating all paths, stored in the cache, to corresponding
+ zipimporter instances.
+
+ :param path: paths that has to be added into the cache
+ :return: association between paths stored in the cache and zipimporter instances
+ """
pic = sys.path_importer_cache
# When measured, despite having the same complexity (O(n)),
@@ -272,7 +298,11 @@ def _precache_zipimporters(path=None):
pic[entry_path] = zipimport.zipimporter(entry_path)
except zipimport.ZipImportError:
continue
- return pic
+ return {
+ key: value
+ for key, value in pic.items()
+ if isinstance(value, zipimport.zipimporter)
+ }
def _search_zip(modpath, pic):
diff --git a/astroid/interpreter/objectmodel.py b/astroid/interpreter/objectmodel.py
index 10c659f6..ae48ac11 100644
--- a/astroid/interpreter/objectmodel.py
+++ b/astroid/interpreter/objectmodel.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright (c) 2016-2019 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2016-2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2016 Derek Gustafson <degustaf@gmail.com>
# Copyright (c) 2017-2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
# Copyright (c) 2017 Ceridwen <ceridwenv@gmail.com>
@@ -324,6 +324,7 @@ class FunctionModel(ObjectModel):
doc=func.doc,
lineno=func.lineno,
col_offset=func.col_offset,
+ parent=func.parent,
)
# pylint: disable=no-member
new_func.postinit(func.args, func.body, func.decorators, func.returns)
diff --git a/astroid/manager.py b/astroid/manager.py
index 82208adf..04950329 100644
--- a/astroid/manager.py
+++ b/astroid/manager.py
@@ -1,5 +1,5 @@
# Copyright (c) 2006-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
-# Copyright (c) 2014-2019 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014-2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2014 BioGeek <jeroen.vangoey@gmail.com>
# Copyright (c) 2014 Google, Inc.
# Copyright (c) 2014 Eevee (Alex Munroe) <amunroe@yelp.com>
@@ -9,6 +9,7 @@
# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
# Copyright (c) 2019 Raphael Gaschignard <raphael@makeleaps.com>
+# Copyright (c) 2020 Raphael Gaschignard <raphael@rtpg.co>
# Copyright (c) 2020 Anubhav <35621759+anubh-v@users.noreply.github.com>
# Copyright (c) 2020 Ashley Whetter <ashley@awhetter.co.uk>
@@ -236,11 +237,13 @@ class AstroidManager:
value = exceptions.AstroidImportError(
"Failed to import module {modname} with error:\n{error}.",
modname=modname,
- error=ex,
+ # we remove the traceback here to save on memory usage (since these exceptions are cached)
+ error=ex.with_traceback(None),
)
self._mod_file_cache[(modname, contextfile)] = value
if isinstance(value, exceptions.AstroidBuildingError):
- raise value
+ # we remove the traceback here to save on memory usage (since these exceptions are cached)
+ raise value.with_traceback(None)
return value
def ast_from_module(self, module, modname=None):
diff --git a/astroid/modutils.py b/astroid/modutils.py
index 4e6ed86b..4ec8ce95 100644
--- a/astroid/modutils.py
+++ b/astroid/modutils.py
@@ -16,6 +16,8 @@
# Copyright (c) 2019 Hugo van Kemenade <hugovk@users.noreply.github.com>
# Copyright (c) 2019 markmcclain <markmcclain@users.noreply.github.com>
# Copyright (c) 2019 BasPH <BasPH@users.noreply.github.com>
+# Copyright (c) 2020 hippo91 <guillaume.peillex@gmail.com>
+# Copyright (c) 2020 Peter Kolbus <peter.kolbus@gmail.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
@@ -31,7 +33,7 @@
:type BUILTIN_MODULES: dict
:var BUILTIN_MODULES: dictionary with builtin module names has key
"""
-import imp
+import importlib.util
import os
import platform
import sys
@@ -44,7 +46,6 @@ from distutils.errors import DistutilsPlatformError
# distutils is replaced by virtualenv with a module that does
# weird path manipulations in order to get to the
# real distutils module.
-from typing import Optional, List
from .interpreter._import import spec
from .interpreter._import import util
@@ -90,11 +91,23 @@ if os.name == "nt":
pass
if platform.python_implementation() == "PyPy":
+ # The get_python_lib(standard_lib=True) function does not give valid
+ # result with pypy in a virtualenv.
+ # In a virtual environment, with CPython implementation the call to this function returns a path toward
+ # the binary (its libraries) which has been used to create the virtual environment.
+ # Not with pypy implementation.
+ # The only way to retrieve such information is to use the sys.base_prefix hint.
+ # It's worth noticing that under CPython implementation the return values of
+ # get_python_lib(standard_lib=True) and get_python_lib(santdard_lib=True, prefix=sys.base_prefix)
+ # are the same.
+ # In the lines above, we could have replace the call to get_python_lib(standard=True)
+ # with the one using prefix=sys.base_prefix but we prefer modifying only what deals with pypy.
+ STD_LIB_DIRS.add(get_python_lib(standard_lib=True, prefix=sys.base_prefix))
_root = os.path.join(sys.prefix, "lib_pypy")
STD_LIB_DIRS.add(_root)
try:
# real_prefix is defined when running inside virtualenv.
- STD_LIB_DIRS.add(os.path.join(sys.real_prefix, "lib_pypy"))
+ STD_LIB_DIRS.add(os.path.join(sys.base_prefix, "lib_pypy"))
except AttributeError:
pass
del _root
@@ -178,110 +191,53 @@ def _cache_normalize_path(path):
return result
-def load_module_from_name(dotted_name, path=None, use_sys=True):
+def load_module_from_name(dotted_name):
"""Load a Python module from its name.
:type dotted_name: str
:param dotted_name: python name of a module or package
- :type path: list or None
- :param path:
- optional list of path where the module or package should be
- searched (use sys.path if nothing or None is given)
-
- :type use_sys: bool
- :param use_sys:
- boolean indicating whether the sys.modules dictionary should be
- used or not
-
-
:raise ImportError: if the module or package is not found
:rtype: module
:return: the loaded module
"""
- return load_module_from_modpath(dotted_name.split("."), path, use_sys)
+ try:
+ return sys.modules[dotted_name]
+ except KeyError:
+ pass
+ return importlib.import_module(dotted_name)
-def load_module_from_modpath(parts, path: Optional[List[str]] = None, use_sys=1):
+
+def load_module_from_modpath(parts):
"""Load a python module from its split name.
:type parts: list(str) or tuple(str)
:param parts:
python name of a module or package split on '.'
- :param path:
- Optional list of path where the module or package should be
- searched (use sys.path if nothing or None is given)
-
- :type use_sys: bool
- :param use_sys:
- boolean indicating whether the sys.modules dictionary should be used or not
-
:raise ImportError: if the module or package is not found
:rtype: module
:return: the loaded module
"""
- if use_sys:
- try:
- return sys.modules[".".join(parts)]
- except KeyError:
- pass
- modpath = []
- prevmodule = None
- for part in parts:
- modpath.append(part)
- curname = ".".join(modpath)
- module = None
- if len(modpath) != len(parts):
- # even with use_sys=False, should try to get outer packages from sys.modules
- module = sys.modules.get(curname)
- elif use_sys:
- # because it may have been indirectly loaded through a parent
- module = sys.modules.get(curname)
- if module is None:
- mp_file, mp_filename, mp_desc = imp.find_module(part, path)
- module = imp.load_module(curname, mp_file, mp_filename, mp_desc)
- # mp_file still needs to be closed.
- if mp_file:
- mp_file.close()
- if prevmodule:
- setattr(prevmodule, part, module)
- _file = getattr(module, "__file__", "")
- prevmodule = module
- if not _file and util.is_namespace(curname):
- continue
- if not _file and len(modpath) != len(parts):
- raise ImportError("no module in %s" % ".".join(parts[len(modpath) :]))
- path = [os.path.dirname(_file)]
- return module
+ return load_module_from_name(".".join(parts))
-def load_module_from_file(
- filepath: str, path: Optional[List[str]] = None, use_sys=True
-):
+def load_module_from_file(filepath: str):
"""Load a Python module from it's path.
:type filepath: str
:param filepath: path to the python module or package
- :param Optional[List[str]] path:
- Optional list of path where the module or package should be
- searched (use sys.path if nothing or None is given)
-
- :type use_sys: bool
- :param use_sys:
- boolean indicating whether the sys.modules dictionary should be
- used or not
-
:raise ImportError: if the module or package is not found
:rtype: module
:return: the loaded module
"""
modpath = modpath_from_file(filepath)
- return load_module_from_modpath(modpath, path, use_sys)
+ return load_module_from_modpath(modpath)
def check_modpath_has_init(path, mod_path):
@@ -418,7 +374,9 @@ def file_info_from_modpath(modpath, path=None, context_file=None):
elif modpath == ["os", "path"]:
# FIXME: currently ignoring search_path...
return spec.ModuleSpec(
- name="os.path", location=os.path.__file__, module_type=imp.PY_SOURCE
+ name="os.path",
+ location=os.path.__file__,
+ module_type=spec.ModuleType.PY_SOURCE,
)
return _spec_from_modpath(modpath, path, context)
@@ -614,16 +572,22 @@ def is_relative(modname, from_file):
from_file = os.path.dirname(from_file)
if from_file in sys.path:
return False
- try:
- stream, _, _ = imp.find_module(modname.split(".")[0], [from_file])
-
- # Close the stream to avoid ResourceWarnings.
- if stream:
- stream.close()
- return True
- except ImportError:
+ name = os.path.basename(from_file)
+ file_path = os.path.dirname(from_file)
+ parent_spec = importlib.util.find_spec(name, from_file)
+ while parent_spec is None and len(file_path) > 0:
+ name = os.path.basename(file_path) + "." + name
+ file_path = os.path.dirname(file_path)
+ parent_spec = importlib.util.find_spec(name, from_file)
+
+ if parent_spec is None:
return False
+ submodule_spec = importlib.util.find_spec(
+ name + "." + modname.split(".")[0], parent_spec.submodule_search_locations
+ )
+ return submodule_spec is not None
+
# internal only functions #####################################################
diff --git a/astroid/node_classes.py b/astroid/node_classes.py
index 59b1e31c..62438e62 100644
--- a/astroid/node_classes.py
+++ b/astroid/node_classes.py
@@ -14,14 +14,16 @@
# 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 Bryce Guinta <bryce.paul.guinta@gmail.com>
# 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>
+# Copyright (c) 2020 Raphael Gaschignard <raphael@rtpg.co>
+# Copyright (c) 2020 Bryce Guinta <bryce.guinta@protonmail.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
@@ -354,19 +356,37 @@ class NodeNG:
# explicit_inference is not bound, give it self explicitly
try:
# pylint: disable=not-callable
- return self._explicit_inference(self, context, **kwargs)
+ yield from self._explicit_inference(self, context, **kwargs)
+ return
except exceptions.UseInferenceDefault:
pass
if not context:
- return self._infer(context, **kwargs)
+ yield from self._infer(context, **kwargs)
+ return
key = (self, context.lookupname, context.callcontext, context.boundnode)
if key in context.inferred:
- return iter(context.inferred[key])
+ yield from context.inferred[key]
+ return
+
+ generator = self._infer(context, **kwargs)
+ results = []
+
+ # Limit inference amount to help with performance issues with
+ # exponentially exploding possible results.
+ limit = MANAGER.max_inferable_values
+ for i, result in enumerate(generator):
+ if i >= limit:
+ yield util.Uninferable
+ break
+ results.append(result)
+ yield result
- gen = context.cache_generator(key, self._infer(context, **kwargs))
- return util.limit_inference(gen, MANAGER.max_inferable_values)
+ # Cache generated results for subsequent inferences of the
+ # same node using the same context
+ context.inferred[key] = tuple(results)
+ return
def _repr_name(self):
"""Get a name for nice representation.
@@ -950,7 +970,7 @@ class Statement(NodeNG):
try:
return stmts[index + 1]
except IndexError:
- pass
+ return None
def previous_sibling(self):
"""The previous sibling statement.
@@ -2105,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/protocols.py b/astroid/protocols.py
index 2cdf5548..57e5a231 100644
--- a/astroid/protocols.py
+++ b/astroid/protocols.py
@@ -14,6 +14,8 @@
# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
# Copyright (c) 2018 HoverHell <hoverhell@gmail.com>
# Copyright (c) 2019 Hugo van Kemenade <hugovk@users.noreply.github.com>
+# Copyright (c) 2020 Vilnis Termanis <vilnis.termanis@iotics.com>
+# Copyright (c) 2020 Ram Rachum <ram@rachum.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
@@ -498,8 +500,8 @@ def _infer_context_manager(self, mgr, context):
elif isinstance(inferred, bases.Instance):
try:
enter = next(inferred.igetattr("__enter__", context=context))
- except (exceptions.InferenceError, exceptions.AttributeInferenceError):
- raise exceptions.InferenceError(node=inferred)
+ except (exceptions.InferenceError, exceptions.AttributeInferenceError) as exc:
+ raise exceptions.InferenceError(node=inferred) from exc
if not isinstance(enter, bases.BoundMethod):
raise exceptions.InferenceError(node=enter)
yield from enter.infer_call_result(self, context)
@@ -686,11 +688,10 @@ def starred_assigned_stmts(self, node=None, context=None, assign_path=None):
continue
# We're done unpacking.
- elts = list(elts)
packed = nodes.List(
ctx=Store, parent=self, lineno=lhs.lineno, col_offset=lhs.col_offset
)
- packed.postinit(elts=elts)
+ packed.postinit(elts=list(elts))
yield packed
break
diff --git a/astroid/raw_building.py b/astroid/raw_building.py
index b2612778..cecae989 100644
--- a/astroid/raw_building.py
+++ b/astroid/raw_building.py
@@ -11,6 +11,7 @@
# Copyright (c) 2018 Ville Skyttä <ville.skytta@iki.fi>
# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
+# Copyright (c) 2020 Becker Awqatty <bawqatty@mide.com>
# Copyright (c) 2020 Robin Jarry <robin.jarry@6wind.com>
# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
@@ -25,6 +26,7 @@ import inspect
import os
import sys
import types
+import warnings
from astroid import bases
from astroid import manager
@@ -324,8 +326,10 @@ class InspectBuilder:
self._done[obj] = node
for name in dir(obj):
try:
- member = getattr(obj, name)
- except AttributeError:
+ with warnings.catch_warnings():
+ warnings.filterwarnings("error")
+ member = getattr(obj, name)
+ except (AttributeError, DeprecationWarning):
# damned ExtensionClass.Base, I know you're there !
attach_dummy_node(node, name)
continue
diff --git a/astroid/scoped_nodes.py b/astroid/scoped_nodes.py
index 8561e745..6e899e17 100644
--- a/astroid/scoped_nodes.py
+++ b/astroid/scoped_nodes.py
@@ -20,6 +20,9 @@
# Copyright (c) 2018 HoverHell <hoverhell@gmail.com>
# Copyright (c) 2019 Hugo van Kemenade <hugovk@users.noreply.github.com>
# Copyright (c) 2019 Peter de Blanc <peter@standard.ai>
+# Copyright (c) 2020 Peter Kolbus <peter.kolbus@gmail.com>
+# Copyright (c) 2020 Tim Martin <tim@asymptotic.co.uk>
+# Copyright (c) 2020 Ram Rachum <ram@rachum.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
@@ -1448,6 +1451,7 @@ class FunctionDef(mixins.MultiLineBlockMixin, node_classes.Statement, Lambda):
decorators.append(assign.value)
return decorators
+ # pylint: disable=invalid-overridden-method
@decorators_mod.cachedproperty
def type(
self
@@ -2571,7 +2575,7 @@ class ClassDef(mixins.FilterStmtsMixin, LocalsDictNodeNG, node_classes.Statement
else:
raise exceptions.InferenceError(
error.message, target=self, attribute=name, context=context
- )
+ ) from error
def has_dynamic_getattr(self, context=None):
"""Check if the class has a custom __getattr__ or __getattribute__.
@@ -2858,7 +2862,7 @@ class ClassDef(mixins.FilterStmtsMixin, LocalsDictNodeNG, node_classes.Statement
for stmt in self.bases:
try:
- baseobj = next(stmt.infer(context=context))
+ baseobj = next(stmt.infer(context=context.clone()))
except exceptions.InferenceError:
continue
if isinstance(baseobj, bases.Instance):
diff --git a/astroid/test_utils.py b/astroid/test_utils.py
index e22c7a4f..5948d4d8 100644
--- a/astroid/test_utils.py
+++ b/astroid/test_utils.py
@@ -1,6 +1,6 @@
# Copyright (c) 2013-2014 Google, Inc.
# Copyright (c) 2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
-# Copyright (c) 2015-2016, 2018-2019 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015-2016, 2018-2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
# Copyright (c) 2016 Jakub Wilk <jwilk@jwilk.net>
# Copyright (c) 2018 Anthony Sottile <asottile@umich.edu>
diff --git a/astroid/util.py b/astroid/util.py
index 3ab75615..cb3f0ddf 100644
--- a/astroid/util.py
+++ b/astroid/util.py
@@ -2,12 +2,12 @@
# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
+# Copyright (c) 2020 Bryce Guinta <bryce.guinta@protonmail.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
import warnings
-from itertools import islice
import importlib
import lazy_object_proxy
@@ -139,26 +139,3 @@ def proxy_alias(alias_name, node_type):
},
)
return proxy(lambda: node_type)
-
-
-def limit_inference(iterator, size):
- """Limit inference amount.
-
- Limit inference amount to help with performance issues with
- exponentially exploding possible results.
-
- :param iterator: Inference generator to limit
- :type iterator: Iterator(NodeNG)
-
- :param size: Maximum mount of nodes yielded plus an
- Uninferable at the end if limit reached
- :type size: int
-
- :yields: A possibly modified generator
- :rtype param: Iterable
- """
- yield from islice(iterator, size)
- has_more = next(iterator, False)
- if has_more is not False:
- yield Uninferable
- return
diff --git a/setup.py b/setup.py
index 016fce18..ffbacf70 100644
--- a/setup.py
+++ b/setup.py
@@ -1,11 +1,12 @@
#!/usr/bin/env python
# Copyright (c) 2006, 2009-2010, 2012-2013 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
# Copyright (c) 2010-2011 Julien Jehannet <julien.jehannet@logilab.fr>
-# Copyright (c) 2014-2016, 2018-2019 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014-2016, 2018-2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2014 Google, Inc.
# Copyright (c) 2017 Hugo <hugovk@users.noreply.github.com>
# Copyright (c) 2018-2019 Ashley Whetter <ashley@awhetter.co.uk>
# Copyright (c) 2019 Enji Cooper <yaneurabeya@gmail.com>
+# Copyright (c) 2020 Colin Kennedy <colinvfx@gmail.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
@@ -14,10 +15,14 @@
"""Setup script for astroid."""
import os
import sys
+import warnings
from setuptools import find_packages, setup
from setuptools.command import easy_install # pylint: disable=unused-import
from setuptools.command import install_lib # pylint: disable=unused-import
+if sys.version_info.major == 3 and sys.version_info.minor <=5:
+ warnings.warn("You will soon need to upgrade to python 3.6 in order to use the latest version of Astroid.", DeprecationWarning)
+
real_path = os.path.realpath(__file__)
astroid_dir = os.path.dirname(real_path)
pkginfo = os.path.join(astroid_dir, "astroid", "__pkginfo__.py")
diff --git a/tests/resources.py b/tests/resources.py
index 7113f2b1..20bd4113 100644
--- a/tests/resources.py
+++ b/tests/resources.py
@@ -1,8 +1,9 @@
# Copyright (c) 2014 Google, Inc.
-# Copyright (c) 2015-2016, 2018-2019 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015-2016, 2018-2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2016 Ceridwen <ceridwenv@gmail.com>
# Copyright (c) 2018 Nick Drozd <nicholasdrozd@gmail.com>
# Copyright (c) 2019 Ashley Whetter <ashley@awhetter.co.uk>
+# Copyright (c) 2020 David Cain <davidjosephcain@gmail.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
diff --git a/tests/testdata/python3/data/nonregr.py b/tests/testdata/python3/data/nonregr.py
index 78765c85..073135d2 100644
--- a/tests/testdata/python3/data/nonregr.py
+++ b/tests/testdata/python3/data/nonregr.py
@@ -16,9 +16,7 @@ def toto(value):
print(v.get('yo'))
-import imp
-fp, mpath, desc = imp.find_module('optparse',a)
-s_opt = imp.load_module('std_optparse', fp, mpath, desc)
+import optparse as s_opt
class OptionParser(s_opt.OptionParser):
diff --git a/tests/unittest_brain.py b/tests/unittest_brain.py
index 0a833664..e6ff6928 100644
--- a/tests/unittest_brain.py
+++ b/tests/unittest_brain.py
@@ -18,11 +18,12 @@
# Copyright (c) 2018 Anthony Sottile <asottile@umich.edu>
# Copyright (c) 2018 Ioana Tagirta <ioana.tagirta@gmail.com>
# Copyright (c) 2018 Ahmed Azzaoui <ahmed.azzaoui@engie.com>
+# Copyright (c) 2019-2020 Bryce Guinta <bryce.guinta@protonmail.com>
# Copyright (c) 2019 Ashley Whetter <ashley@awhetter.co.uk>
# Copyright (c) 2019 Tomas Novak <ext.Tomas.Novak@skoda-auto.cz>
# Copyright (c) 2019 Hugo van Kemenade <hugovk@users.noreply.github.com>
# Copyright (c) 2019 Grygorii Iermolenko <gyermolenko@gmail.com>
-# Copyright (c) 2019 Bryce Guinta <bryce.guinta@protonmail.com>
+# Copyright (c) 2020 Peter Kolbus <peter.kolbus@gmail.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
@@ -540,8 +541,10 @@ class MultiprocessingBrainTest(unittest.TestCase):
obj = next(module[attr].infer())
self.assertEqual(obj.qname(), "{}.{}".format(bases.BUILTINS, attr))
- array = next(module["array"].infer())
- self.assertEqual(array.qname(), "array.array")
+ # pypy's implementation of array.__spec__ return None. This causes problems for this inference.
+ if not hasattr(sys, "pypy_version_info"):
+ array = next(module["array"].infer())
+ self.assertEqual(array.qname(), "array.array")
manager = next(module["manager"].infer())
# Verify that we have these attributes
@@ -839,6 +842,16 @@ class EnumBrainTest(unittest.TestCase):
assert inferred_tuple_node.as_string() == "(1, 2)"
assert inferred_list_node.as_string() == "[2, 4]"
+ def test_enum_starred_is_skipped(self):
+ code = """
+ from enum import Enum
+ class ContentType(Enum):
+ TEXT, PHOTO, VIDEO, GIF, YOUTUBE, *_ = [1, 2, 3, 4, 5, 6]
+ ContentType.TEXT #@
+ """
+ node = astroid.extract_node(code)
+ next(node.infer())
+
@unittest.skipUnless(HAS_DATEUTIL, "This test requires the dateutil library.")
class DateutilBrainTest(unittest.TestCase):
@@ -924,6 +937,42 @@ class IOBrainTest(unittest.TestCase):
self.assertEqual(raw.name, "FileIO")
+@test_utils.require_version("3.9")
+class TypeBrain(unittest.TestCase):
+ def test_type_subscript(self):
+ """
+ Check that type object has the __class_getitem__ method
+ when it is used as a subscript
+ """
+ src = builder.extract_node(
+ """
+ a: type[int] = int
+ """
+ )
+ val_inf = src.annotation.value.inferred()[0]
+ self.assertIsInstance(val_inf, astroid.ClassDef)
+ self.assertEqual(val_inf.name, "type")
+ meth_inf = val_inf.getattr("__class_getitem__")[0]
+ self.assertIsInstance(meth_inf, astroid.FunctionDef)
+
+ def test_invalid_type_subscript(self):
+ """
+ Check that a type (str for example) that inherits
+ from type does not have __class_getitem__ method even
+ when it is used as a subscript
+ """
+ src = builder.extract_node(
+ """
+ a: str[int] = "abc"
+ """
+ )
+ val_inf = src.annotation.value.inferred()[0]
+ self.assertIsInstance(val_inf, astroid.ClassDef)
+ self.assertEqual(val_inf.name, "str")
+ with self.assertRaises(astroid.exceptions.AttributeInferenceError):
+ meth_inf = val_inf.getattr("__class_getitem__")[0]
+
+
@test_utils.require_version("3.6")
class TypingBrain(unittest.TestCase):
def test_namedtuple_base(self):
@@ -1279,6 +1328,13 @@ class SubprocessTest(unittest.TestCase):
assert isinstance(inferred, astroid.Const)
assert isinstance(inferred.value, (str, bytes))
+ @test_utils.require_version("3.9")
+ def test_popen_does_not_have_class_getitem(self):
+ code = """import subprocess; subprocess.Popen"""
+ node = astroid.extract_node(code)
+ inferred = next(node.infer())
+ assert "__class_getitem__" in inferred
+
class TestIsinstanceInference:
"""Test isinstance builtin inference"""
@@ -2020,5 +2076,36 @@ def test_dataclasses():
assert isinstance(name[0], astroid.Unknown)
+@pytest.mark.parametrize(
+ "code,expected_class,expected_value",
+ [
+ ("'hey'.encode()", astroid.Const, b""),
+ ("b'hey'.decode()", astroid.Const, ""),
+ ("'hey'.encode().decode()", astroid.Const, ""),
+ ],
+)
+def test_str_and_bytes(code, expected_class, expected_value):
+ node = astroid.extract_node(code)
+ inferred = next(node.infer())
+ assert isinstance(inferred, expected_class)
+ assert inferred.value == expected_value
+
+
+def test_no_recursionerror_on_self_referential_length_check():
+ """
+ Regression test for https://github.com/PyCQA/astroid/issues/777
+ """
+ with pytest.raises(astroid.InferenceError):
+ node = astroid.extract_node(
+ """
+ class Crash:
+ def __len__(self) -> int:
+ return len(self)
+ len(Crash()) #@
+ """
+ )
+ node.inferred()
+
+
if __name__ == "__main__":
unittest.main()
diff --git a/tests/unittest_brain_numpy_core_fromnumeric.py b/tests/unittest_brain_numpy_core_fromnumeric.py
index fd571f30..73ecccc2 100644
--- a/tests/unittest_brain_numpy_core_fromnumeric.py
+++ b/tests/unittest_brain_numpy_core_fromnumeric.py
@@ -1,6 +1,7 @@
# -*- encoding=utf-8 -*-
# Copyright (c) 2019 Ashley Whetter <ashley@awhetter.co.uk>
# Copyright (c) 2019 hippo91 <guillaume.peillex@gmail.com>
+# Copyright (c) 2020 Claudiu Popa <pcmanticore@gmail.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
diff --git a/tests/unittest_brain_numpy_core_function_base.py b/tests/unittest_brain_numpy_core_function_base.py
index 109238a2..df44b645 100644
--- a/tests/unittest_brain_numpy_core_function_base.py
+++ b/tests/unittest_brain_numpy_core_function_base.py
@@ -1,6 +1,7 @@
# -*- encoding=utf-8 -*-
# Copyright (c) 2019 Ashley Whetter <ashley@awhetter.co.uk>
# Copyright (c) 2019 hippo91 <guillaume.peillex@gmail.com>
+# Copyright (c) 2020 Claudiu Popa <pcmanticore@gmail.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
diff --git a/tests/unittest_brain_numpy_core_multiarray.py b/tests/unittest_brain_numpy_core_multiarray.py
index 9e945d21..cd68db06 100644
--- a/tests/unittest_brain_numpy_core_multiarray.py
+++ b/tests/unittest_brain_numpy_core_multiarray.py
@@ -1,6 +1,7 @@
# -*- encoding=utf-8 -*-
# Copyright (c) 2019 hippo91 <guillaume.peillex@gmail.com>
# Copyright (c) 2019 Ashley Whetter <ashley@awhetter.co.uk>
+# Copyright (c) 2020 Claudiu Popa <pcmanticore@gmail.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
diff --git a/tests/unittest_brain_numpy_core_numeric.py b/tests/unittest_brain_numpy_core_numeric.py
index a39fb19e..47894ca9 100644
--- a/tests/unittest_brain_numpy_core_numeric.py
+++ b/tests/unittest_brain_numpy_core_numeric.py
@@ -1,6 +1,7 @@
# -*- encoding=utf-8 -*-
# Copyright (c) 2019 Ashley Whetter <ashley@awhetter.co.uk>
# Copyright (c) 2019 hippo91 <guillaume.peillex@gmail.com>
+# Copyright (c) 2020 Claudiu Popa <pcmanticore@gmail.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
diff --git a/tests/unittest_brain_numpy_core_numerictypes.py b/tests/unittest_brain_numpy_core_numerictypes.py
index db4fdd23..e05f8eb7 100644
--- a/tests/unittest_brain_numpy_core_numerictypes.py
+++ b/tests/unittest_brain_numpy_core_numerictypes.py
@@ -1,6 +1,6 @@
# -*- encoding=utf-8 -*-
+# Copyright (c) 2017-2018, 2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2017-2020 hippo91 <guillaume.peillex@gmail.com>
-# Copyright (c) 2017-2018 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
# Copyright (c) 2019 Ashley Whetter <ashley@awhetter.co.uk>
diff --git a/tests/unittest_brain_numpy_core_umath.py b/tests/unittest_brain_numpy_core_umath.py
index 3db3f149..acfaeb70 100644
--- a/tests/unittest_brain_numpy_core_umath.py
+++ b/tests/unittest_brain_numpy_core_umath.py
@@ -1,6 +1,7 @@
# -*- encoding=utf-8 -*-
# Copyright (c) 2019 hippo91 <guillaume.peillex@gmail.com>
# Copyright (c) 2019 Ashley Whetter <ashley@awhetter.co.uk>
+# Copyright (c) 2020 Claudiu Popa <pcmanticore@gmail.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
@@ -36,6 +37,7 @@ class NumpyBrainCoreUmathTest(unittest.TestCase):
"conjugate",
"cosh",
"deg2rad",
+ "degrees",
"exp2",
"expm1",
"fabs",
@@ -50,6 +52,7 @@ class NumpyBrainCoreUmathTest(unittest.TestCase):
"negative",
"positive",
"rad2deg",
+ "radians",
"reciprocal",
"rint",
"sign",
diff --git a/tests/unittest_brain_numpy_ndarray.py b/tests/unittest_brain_numpy_ndarray.py
index defce47d..e53ce540 100644
--- a/tests/unittest_brain_numpy_ndarray.py
+++ b/tests/unittest_brain_numpy_ndarray.py
@@ -1,6 +1,6 @@
# -*- encoding=utf-8 -*-
# Copyright (c) 2017-2020 hippo91 <guillaume.peillex@gmail.com>
-# Copyright (c) 2017-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2017-2018, 2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
# Copyright (c) 2019 Ashley Whetter <ashley@awhetter.co.uk>
@@ -154,7 +154,7 @@ class NumpyBrainNdarrayTest(unittest.TestCase):
Test that some numpy ndarray attributes are inferred as numpy.ndarray
"""
licit_array_types = ".ndarray"
- for attr_ in ("real", "imag"):
+ for attr_ in ("real", "imag", "shape", "T"):
with self.subTest(typ=attr_):
inferred_values = list(self._inferred_ndarray_attribute(attr_))
self.assertTrue(
diff --git a/tests/unittest_brain_numpy_random_mtrand.py b/tests/unittest_brain_numpy_random_mtrand.py
index 20a5d310..ec2bfcf7 100644
--- a/tests/unittest_brain_numpy_random_mtrand.py
+++ b/tests/unittest_brain_numpy_random_mtrand.py
@@ -1,6 +1,7 @@
# -*- encoding=utf-8 -*-
# Copyright (c) 2019 Ashley Whetter <ashley@awhetter.co.uk>
# Copyright (c) 2019 hippo91 <guillaume.peillex@gmail.com>
+# Copyright (c) 2020 Claudiu Popa <pcmanticore@gmail.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
@@ -55,6 +56,7 @@ class NumpyBrainRandomMtrandTest(unittest.TestCase):
"rand": (["args"], []),
"randint": (["low", "high", "size", "dtype"], [None, None, "l"]),
"randn": (["args"], []),
+ "random": (["size"], [None]),
"random_integers": (["low", "high", "size"], [None, None]),
"random_sample": (["size"], [None]),
"rayleigh": (["scale", "size"], [1.0, None]),
diff --git a/tests/unittest_builder.py b/tests/unittest_builder.py
index 22e49e60..8f1205d6 100644
--- a/tests/unittest_builder.py
+++ b/tests/unittest_builder.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2006-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
-# Copyright (c) 2014-2019 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014-2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2014-2015 Google, Inc.
# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
# Copyright (c) 2015 Florian Bruhin <me@the-compiler.org>
diff --git a/tests/unittest_inference.py b/tests/unittest_inference.py
index b3df5f6c..7b80b530 100644
--- a/tests/unittest_inference.py
+++ b/tests/unittest_inference.py
@@ -14,14 +14,18 @@
# Copyright (c) 2017 Calen Pennington <calen.pennington@gmail.com>
# Copyright (c) 2017 David Euresti <david@dropbox.com>
# Copyright (c) 2017 Derek Gustafson <degustaf@gmail.com>
+# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
# Copyright (c) 2018 Daniel Martin <daniel.martin@crowdstrike.com>
# Copyright (c) 2018 Ville Skyttä <ville.skytta@iki.fi>
-# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
# Copyright (c) 2018 Anthony Sottile <asottile@umich.edu>
# Copyright (c) 2019 Stanislav Levin <slev@altlinux.org>
# Copyright (c) 2019 David Liu <david@cs.toronto.edu>
# Copyright (c) 2019 Ashley Whetter <ashley@awhetter.co.uk>
# Copyright (c) 2019 Hugo van Kemenade <hugovk@users.noreply.github.com>
+# Copyright (c) 2020 Peter Kolbus <peter.kolbus@gmail.com>
+# Copyright (c) 2020 hippo91 <guillaume.peillex@gmail.com>
+# Copyright (c) 2020 Karthikeyan Singaravelan <tir.karthi@gmail.com>
+# Copyright (c) 2020 Bryce Guinta <bryce.guinta@protonmail.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
@@ -428,7 +432,7 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
del undefined_attr
"""
delete = extract_node(code, __name__)
- self.assertRaises(InferenceError, delete.infer)
+ self.assertRaises(InferenceError, next, delete.infer())
def test_del2(self):
code = """
@@ -541,18 +545,6 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
self.assertEqual(ancestor.root().name, BUILTINS)
self.assertRaises(StopIteration, partial(next, ancestors))
- def test_qqch(self):
- code = """
- from astroid.modutils import load_module_from_name
- xxx = load_module_from_name('__pkginfo__')
- """
- ast = parse(code, __name__)
- xxx = ast["xxx"]
- self.assertSetEqual(
- {n.__class__ for n in xxx.inferred()},
- {nodes.Const, util.Uninferable.__class__},
- )
-
def test_method_argument(self):
code = '''
class ErudiEntitySchema:
@@ -2083,8 +2075,6 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
def test_str_methods(self):
code = """
' '.decode() #@
-
- ' '.encode() #@
' '.join('abcd') #@
' '.replace('a', 'b') #@
' '.format('a') #@
@@ -2106,15 +2096,13 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
"""
ast = extract_node(code, __name__)
self.assertInferConst(ast[0], "")
- for i in range(1, 16):
+ for i in range(1, 15):
self.assertInferConst(ast[i], "")
- for i in range(16, 19):
+ for i in range(15, 18):
self.assertInferConst(ast[i], 0)
def test_unicode_methods(self):
code = """
- u' '.encode() #@
-
u' '.decode() #@
u' '.join('abcd') #@
u' '.replace('a', 'b') #@
@@ -2137,9 +2125,9 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
"""
ast = extract_node(code, __name__)
self.assertInferConst(ast[0], "")
- for i in range(1, 16):
+ for i in range(1, 15):
self.assertInferConst(ast[i], "")
- for i in range(16, 19):
+ for i in range(15, 18):
self.assertInferConst(ast[i], 0)
def test_scope_lookup_same_attributes(self):
@@ -2459,7 +2447,6 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
1 ** (lambda x: x) #@
{} * {} #@
{} - {} #@
- {} | {} #@
{} >> {} #@
[] + () #@
() + [] #@
@@ -2504,7 +2491,6 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
msg.format(op="**", lhs="int", rhs="function"),
msg.format(op="*", lhs="dict", rhs="dict"),
msg.format(op="-", lhs="dict", rhs="dict"),
- msg.format(op="|", lhs="dict", rhs="dict"),
msg.format(op=">>", lhs="dict", rhs="dict"),
msg.format(op="+", lhs="list", rhs="tuple"),
msg.format(op="+", lhs="tuple", rhs="list"),
@@ -2519,6 +2505,12 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
msg.format(op="+=", lhs="int", rhs="A"),
msg.format(op="+=", lhs="int", rhs="list"),
]
+
+ # PEP-584 supports | for dictionary union
+ if sys.version_info < (3, 9):
+ ast_nodes.append(extract_node("{} | {} #@"))
+ expected.append(msg.format(op="|", lhs="dict", rhs="dict"))
+
for node, expected_value in zip(ast_nodes, expected):
errors = node.type_errors()
self.assertEqual(len(errors), 1)
@@ -5668,6 +5660,10 @@ def test_custom_decorators_for_classmethod_and_staticmethods(code, obj, obj_type
@pytest.mark.skipif(sys.version_info < (3, 8), reason="Needs dataclasses available")
+@pytest.mark.skipif(
+ sys.version_info >= (3, 9),
+ reason="Exact inference with dataclasses (replace function) in python3.9",
+)
def test_dataclasses_subscript_inference_recursion_error():
code = """
from dataclasses import dataclass, replace
@@ -5688,6 +5684,31 @@ def test_dataclasses_subscript_inference_recursion_error():
assert helpers.safe_infer(node) is None
+@pytest.mark.skipif(
+ sys.version_info < (3, 9),
+ reason="Exact inference with dataclasses (replace function) in python3.9",
+)
+def test_dataclasses_subscript_inference_recursion_error_39():
+ code = """
+ from dataclasses import dataclass, replace
+
+ @dataclass
+ class ProxyConfig:
+ auth: str = "/auth"
+
+
+ a = ProxyConfig("")
+ test_dict = {"proxy" : {"auth" : "", "bla" : "f"}}
+
+ foo = test_dict['proxy']
+ replace(a, **test_dict['proxy']) # This fails
+ """
+ node = extract_node(code)
+ infer_val = helpers.safe_infer(node)
+ assert isinstance(infer_val, Instance)
+ assert infer_val.pytype() == ".ProxyConfig"
+
+
def test_self_reference_infer_does_not_trigger_recursion_error():
# Prevents https://github.com/PyCQA/pylint/issues/1285
code = """
@@ -5864,5 +5885,19 @@ def test_infer_generated_setter():
assert list(inferred.nodes_of_class(nodes.Const)) == []
+def test_infer_list_of_uninferables_does_not_crash():
+ code = """
+ x = [A] * 1
+ f = [x, [A] * 2]
+ x = list(f) + [] # List[Uninferable]
+ tuple(x[0])
+ """
+ node = extract_node(code)
+ inferred = next(node.infer())
+ assert isinstance(inferred, nodes.Tuple)
+ # Would not be able to infer the first element.
+ assert not inferred.elts
+
+
if __name__ == "__main__":
unittest.main()
diff --git a/tests/unittest_lookup.py b/tests/unittest_lookup.py
index 84fd5433..bf30b796 100644
--- a/tests/unittest_lookup.py
+++ b/tests/unittest_lookup.py
@@ -1,6 +1,6 @@
# Copyright (c) 2007-2013 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
# Copyright (c) 2010 Daniel Harding <dharding@gmail.com>
-# Copyright (c) 2014-2016, 2018-2019 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014-2016, 2018-2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2014 Google, Inc.
# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
# Copyright (c) 2019 Ashley Whetter <ashley@awhetter.co.uk>
diff --git a/tests/unittest_manager.py b/tests/unittest_manager.py
index d7878b59..91a781c6 100644
--- a/tests/unittest_manager.py
+++ b/tests/unittest_manager.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2006, 2009-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
# Copyright (c) 2013 AndroWiiid <androwiiid@gmail.com>
-# Copyright (c) 2014-2019 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014-2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2014 Google, Inc.
# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
# Copyright (c) 2017 Chris Philip <chrisp533@gmail.com>
diff --git a/tests/unittest_modutils.py b/tests/unittest_modutils.py
index b5c41bf0..9b3cecf4 100644
--- a/tests/unittest_modutils.py
+++ b/tests/unittest_modutils.py
@@ -10,6 +10,7 @@
# Copyright (c) 2019 Ashley Whetter <ashley@awhetter.co.uk>
# Copyright (c) 2019 Hugo van Kemenade <hugovk@users.noreply.github.com>
# Copyright (c) 2019 markmcclain <markmcclain@users.noreply.github.com>
+# Copyright (c) 2020 Peter Kolbus <peter.kolbus@gmail.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
@@ -82,7 +83,7 @@ class LoadModuleFromNameTest(unittest.TestCase):
def test_raise_load_module_from_name_1(self):
self.assertRaises(
- ImportError, modutils.load_module_from_name, "os.path", use_sys=0
+ ImportError, modutils.load_module_from_name, "_this_module_does_not_exist_"
)
@@ -297,6 +298,23 @@ class IsRelativeTest(unittest.TestCase):
def test_knownValues_is_relative_3(self):
self.assertFalse(modutils.is_relative("astroid", astroid.__path__[0]))
+ def test_deep_relative(self):
+ self.assertTrue(modutils.is_relative("ElementTree", xml.etree.__path__[0]))
+
+ def test_deep_relative2(self):
+ self.assertFalse(modutils.is_relative("ElementTree", xml.__path__[0]))
+
+ def test_deep_relative3(self):
+ self.assertTrue(modutils.is_relative("etree.ElementTree", xml.__path__[0]))
+
+ def test_deep_relative4(self):
+ self.assertTrue(modutils.is_relative("etree.gibberish", xml.__path__[0]))
+
+ def test_is_relative_bad_path(self):
+ self.assertFalse(
+ modutils.is_relative("ElementTree", os.path.join(xml.__path__[0], "ftree"))
+ )
+
class GetModuleFilesTest(unittest.TestCase):
def test_get_module_files_1(self):
diff --git a/tests/unittest_nodes.py b/tests/unittest_nodes.py
index 5b6a39e3..d138ee17 100644
--- a/tests/unittest_nodes.py
+++ b/tests/unittest_nodes.py
@@ -1360,5 +1360,16 @@ def test_is_generator_for_yield_in_if():
assert bool(node.is_generator())
+def test_is_generator_for_yield_in_aug_assign():
+ code = """
+ def test():
+ buf = ''
+ while True:
+ buf += yield
+ """
+ node = astroid.extract_node(code)
+ assert bool(node.is_generator())
+
+
if __name__ == "__main__":
unittest.main()
diff --git a/tests/unittest_protocols.py b/tests/unittest_protocols.py
index babff51e..4f9bfbfc 100644
--- a/tests/unittest_protocols.py
+++ b/tests/unittest_protocols.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright (c) 2015-2019 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015-2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
# Copyright (c) 2016 Jakub Wilk <jwilk@jwilk.net>
# Copyright (c) 2017 Łukasz Rogalski <rogalski.91@gmail.com>
diff --git a/tests/unittest_python3.py b/tests/unittest_python3.py
index b1759f03..db2d233a 100644
--- a/tests/unittest_python3.py
+++ b/tests/unittest_python3.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2010, 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
# Copyright (c) 2012 FELD Boris <lothiraldan@gmail.com>
-# Copyright (c) 2013-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2013-2018, 2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2014 Google, Inc.
# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
# Copyright (c) 2016 Jared Garst <jgarst@users.noreply.github.com>
diff --git a/tests/unittest_raw_building.py b/tests/unittest_raw_building.py
index f782cdb2..160c88d7 100644
--- a/tests/unittest_raw_building.py
+++ b/tests/unittest_raw_building.py
@@ -1,5 +1,5 @@
# Copyright (c) 2013 AndroWiiid <androwiiid@gmail.com>
-# Copyright (c) 2014-2016, 2018-2019 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2014-2016, 2018-2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2014 Google, Inc.
# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
# Copyright (c) 2018 Anthony Sottile <asottile@umich.edu>
diff --git a/tests/unittest_regrtest.py b/tests/unittest_regrtest.py
index 79a86cb2..1444da30 100644
--- a/tests/unittest_regrtest.py
+++ b/tests/unittest_regrtest.py
@@ -343,5 +343,16 @@ def test_ancestor_looking_up_redefined_function():
assert isinstance(found[0], nodes.FunctionDef)
+def test_crash_in_dunder_inference_prevented():
+ code = """
+ class MyClass():
+ def fu(self, objects):
+ delitem = dict.__delitem__.__get__(self, dict)
+ delitem #@
+ """
+ inferred = next(extract_node(code).infer())
+ assert "builtins.dict.__delitem__" == inferred.qname()
+
+
if __name__ == "__main__":
unittest.main()
diff --git a/tests/unittest_scoped_nodes.py b/tests/unittest_scoped_nodes.py
index c4597fa6..b28605e6 100644
--- a/tests/unittest_scoped_nodes.py
+++ b/tests/unittest_scoped_nodes.py
@@ -19,6 +19,7 @@
# Copyright (c) 2019 Hugo van Kemenade <hugovk@users.noreply.github.com>
# Copyright (c) 2019 Peter de Blanc <peter@standard.ai>
# Copyright (c) 2019 hippo91 <guillaume.peillex@gmail.com>
+# Copyright (c) 2020 Tim Martin <tim@asymptotic.co.uk>
# 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
@@ -1417,6 +1418,22 @@ class ClassNodeTest(ModuleLoader, unittest.TestCase):
],
)
+ def test_mro_with_attribute_classes(self):
+ cls = builder.extract_node(
+ """
+ class A:
+ pass
+ class B:
+ pass
+ scope = object()
+ scope.A = A
+ scope.B = B
+ class C(scope.A, scope.B):
+ pass
+ """
+ )
+ self.assertEqualMro(cls, ["C", "A", "B", "object"])
+
def test_generator_from_infer_call_result_parent(self):
func = builder.extract_node(
"""
diff --git a/tests/unittest_transforms.py b/tests/unittest_transforms.py
index 922f10f9..13d220a1 100644
--- a/tests/unittest_transforms.py
+++ b/tests/unittest_transforms.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2015-2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015-2018, 2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com>
# Copyright (c) 2016 Jakub Wilk <jwilk@jwilk.net>
# Copyright (c) 2018 Bryce Guinta <bryce.paul.guinta@gmail.com>
diff --git a/tests/unittest_utils.py b/tests/unittest_utils.py
index 428026b1..1cc9afb6 100644
--- a/tests/unittest_utils.py
+++ b/tests/unittest_utils.py
@@ -1,6 +1,6 @@
# Copyright (c) 2008-2010, 2013 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
# Copyright (c) 2014 Google, Inc.
-# Copyright (c) 2015-2016, 2018 Claudiu Popa <pcmanticore@gmail.com>
+# Copyright (c) 2015-2016, 2018, 2020 Claudiu Popa <pcmanticore@gmail.com>
# Copyright (c) 2016 Ceridwen <ceridwenv@gmail.com>
# Copyright (c) 2016 Dave Baum <dbaum@google.com>
# Copyright (c) 2019 Ashley Whetter <ashley@awhetter.co.uk>
diff --git a/tox.ini b/tox.ini
index 3ec33526..0e5d5a5a 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
[tox]
-envlist = py35, py36, py37, py38, pypy, pylint
+envlist = py35, py36, py37, py38, py39, pypy, pylint
skip_missing_interpreters = true
[testenv:pylint]
@@ -18,14 +18,14 @@ deps =
; we have a brain for nose
; we use pytest for tests
nose
- py35,py36,py37: numpy
- py35,py36,py37: attr
- py35,py36,py37: typed_ast>=1.4.0,<1.5
+ py35,py36,py37,py38,py39: numpy
+ py35,py36,py37,py38,py39: attr
+ py35,py36,py37,py38,py39: typed_ast>=1.4.0,<1.5
pytest
python-dateutil
pypy: singledispatch
six~=1.12
- wrapt~=1.11
+ wrapt>=1.11,<1.13
coverage<5
setenv =