summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaudiu Popa <pcmanticore@gmail.com>2020-03-06 13:21:12 +0100
committerClaudiu Popa <pcmanticore@gmail.com>2020-03-06 13:22:41 +0100
commit7120d894635662d49b05c0cc5d664b5a25544605 (patch)
tree1176b2a0febe1a796d2254013456d24442166327
parent17a5ee681bcf4aacffcc4ec5afbc3436cfdc4537 (diff)
downloadastroid-git-7120d894635662d49b05c0cc5d664b5a25544605.tar.gz
Raise ``AttributeInferenceError`` when ``getattr()`` receives an empty name
If `Module.getattr` received an empty string (as a result of inference for example), `astroid` would have returned the same Module again, which leads to false positives in pylint, since the expected output was of a different type. Rather than allowing empty names to pass through `getattr()`, we simply raise an error earlier. Close PyCQA/pylint#2991
-rw-r--r--ChangeLog4
-rw-r--r--astroid/scoped_nodes.py14
-rw-r--r--tests/unittest_inference.py14
3 files changed, 32 insertions, 0 deletions
diff --git a/ChangeLog b/ChangeLog
index 2e712db2..7485f123 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -10,6 +10,10 @@ Release Date: TBA
Close PyCQA/pylint#3417
+* Raise ``AttributeInferenceError`` when ``getattr()`` receives an empty name
+
+ Close PyCQA/pylint#2991
+
* ``NodeNG.bool_value()`` gained an optional ``context`` parameter
We need to pass an inference context downstream when inferring the boolean
diff --git a/astroid/scoped_nodes.py b/astroid/scoped_nodes.py
index 922f4c58..8070f17d 100644
--- a/astroid/scoped_nodes.py
+++ b/astroid/scoped_nodes.py
@@ -527,6 +527,11 @@ class Module(LocalsDictNodeNG):
return "Module"
def getattr(self, name, context=None, ignore_locals=False):
+ if not name:
+ raise exceptions.AttributeInferenceError(
+ target=self, attribute=name, context=context
+ )
+
result = []
name_in_locals = name in self.locals
@@ -1551,6 +1556,10 @@ class FunctionDef(mixins.MultiLineBlockMixin, node_classes.Statement, Lambda):
"""this method doesn't look in the instance_attrs dictionary since it's
done by an Instance proxy at inference time.
"""
+ if not name:
+ raise exceptions.AttributeInferenceError(
+ target=self, attribute=name, context=context
+ )
if name in self.instance_attrs:
return self.instance_attrs[name]
if name in self.special_attributes:
@@ -2406,6 +2415,11 @@ class ClassDef(mixins.FilterStmtsMixin, LocalsDictNodeNG, node_classes.Statement
:raises AttributeInferenceError: If the attribute cannot be inferred.
"""
+ if not name:
+ raise exceptions.AttributeInferenceError(
+ target=self, attribute=name, context=context
+ )
+
values = self.locals.get(name, [])
if name in self.special_attributes and class_context and not values:
result = [self.special_attributes.lookup(name)]
diff --git a/tests/unittest_inference.py b/tests/unittest_inference.py
index 49ff6cdc..7f86de4d 100644
--- a/tests/unittest_inference.py
+++ b/tests/unittest_inference.py
@@ -5729,5 +5729,19 @@ def test_inferring_properties_multiple_time_does_not_mutate_locals_multiple_time
assert len(a_locals) == 2
+def test_getattr_fails_on_empty_values():
+ code = """
+ import collections
+ collections
+ """
+ node = extract_node(code)
+ inferred = next(node.infer())
+ with pytest.raises(exceptions.InferenceError):
+ next(inferred.igetattr(""))
+
+ with pytest.raises(exceptions.AttributeInferenceError):
+ inferred.getattr("")
+
+
if __name__ == "__main__":
unittest.main()