summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaudiu Popa <pcmanticore@gmail.com>2015-12-05 13:25:34 +0200
committerClaudiu Popa <pcmanticore@gmail.com>2015-12-05 13:25:34 +0200
commitcf052851cab3176fc6442114cbdc8194b24b5b2e (patch)
tree80f8e86b479cf9a87f344054fcac70183f519551
parent9c21c9d3846b50d8fa4e612ed695b53dd43520dd (diff)
downloadastroid-cf052851cab3176fc6442114cbdc8194b24b5b2e.tar.gz
assigned_stmts methods have the same signature from now on.
They used to have different signatures and each one made assumptions about what could be passed to other implementations, leading to various possible crashes when one or more arguments weren't given. Closes issue #277.
-rw-r--r--ChangeLog11
-rw-r--r--astroid/node_classes.py27
-rw-r--r--astroid/protocols.py33
-rw-r--r--astroid/scoped_nodes.py1
-rw-r--r--astroid/tests/unittest_protocols.py22
5 files changed, 79 insertions, 15 deletions
diff --git a/ChangeLog b/ChangeLog
index bda1bca..5e2b17b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,10 +1,21 @@
Change log for the astroid package (used to be astng)
=====================================================
+
* Revert to using printf-style formatting in as_string, in order
to avoid a potential problem with encodings when using .format.
Closes issue #273. Patch by notsqrt.
+ * assigned_stmts methods have the same signature from now on.
+
+ They used to have different signatures and each one made
+ assumptions about what could be passed to other implementations,
+ leading to various possible crashes when one or more arguments
+ weren't given. Closes issue #277.
+
+
+2015-11-29 -- 1.4.1
+
* Add support for handling Uninferable nodes when calling as_string
Some object, for instance List or Tuple can have, after inference,
diff --git a/astroid/node_classes.py b/astroid/node_classes.py
index d248ae9..b004785 100644
--- a/astroid/node_classes.py
+++ b/astroid/node_classes.py
@@ -1847,6 +1847,27 @@ def _update_const_classes():
_update_const_classes()
+def _two_step_initialization(cls, value):
+ instance = cls()
+ instance.postinit(value)
+ return instance
+
+
+def _dict_initialization(cls, value):
+ if isinstance(value, dict):
+ value = tuple(value.items())
+ return _two_step_initialization(cls, value)
+
+
+_CONST_CLS_CONSTRUCTORS = {
+ List: _two_step_initialization,
+ Tuple: _two_step_initialization,
+ Dict: _dict_initialization,
+ Set: _two_step_initialization,
+ Const: lambda cls, value: cls(value)
+}
+
+
def const_factory(value):
"""return an astroid node for a python value"""
# XXX we should probably be stricter here and only consider stuff in
@@ -1856,8 +1877,10 @@ def const_factory(value):
# not in CONST_CLS)
assert not isinstance(value, NodeNG)
try:
- return CONST_CLS[value.__class__](value)
- except (KeyError, AttributeError):
+ initializer_cls = CONST_CLS[value.__class__]
+ initializer = _CONST_CLS_CONSTRUCTORS[initializer_cls]
+ return initializer(initializer_cls, value)
+ except (KeyError, AttributeError) as exc:
node = EmptyNode()
node.object = value
return node
diff --git a/astroid/protocols.py b/astroid/protocols.py
index f1d9284..66ca978 100644
--- a/astroid/protocols.py
+++ b/astroid/protocols.py
@@ -246,7 +246,7 @@ def _resolve_looppart(parts, asspath, context):
break
@decorators.raise_if_nothing_inferred
-def for_assigned_stmts(self, node, context=None, asspath=None):
+def for_assigned_stmts(self, node=None, context=None, asspath=None):
if asspath is None:
for lst in self.iter.infer(context):
if isinstance(lst, (nodes.Tuple, nodes.List)):
@@ -265,18 +265,25 @@ nodes.For.assigned_stmts = for_assigned_stmts
nodes.Comprehension.assigned_stmts = for_assigned_stmts
-def mulass_assigned_stmts(self, node, context=None, asspath=None):
+def sequence_assigned_stmts(self, node=None, context=None, asspath=None):
if asspath is None:
asspath = []
- asspath.insert(0, self.elts.index(node))
- return self.parent.assigned_stmts(self, context, asspath)
+ try:
+ index = self.elts.index(node)
+ except ValueError:
+ util.reraise(exceptions.InferenceError(
+ 'Tried to retrieve a node {node!r} which does not exist',
+ node=self, assign_path=asspath, context=context))
+
+ asspath.insert(0, index)
+ return self.parent.assigned_stmts(node=self, context=context, asspath=asspath)
-nodes.Tuple.assigned_stmts = mulass_assigned_stmts
-nodes.List.assigned_stmts = mulass_assigned_stmts
+nodes.Tuple.assigned_stmts = sequence_assigned_stmts
+nodes.List.assigned_stmts = sequence_assigned_stmts
-def assend_assigned_stmts(self, context=None):
- return self.parent.assigned_stmts(self, context=context)
+def assend_assigned_stmts(self, node=None, context=None, asspath=None):
+ return self.parent.assigned_stmts(node=self, context=context)
nodes.AssignName.assigned_stmts = assend_assigned_stmts
nodes.AssignAttr.assigned_stmts = assend_assigned_stmts
@@ -329,7 +336,7 @@ def _arguments_infer_argname(self, name, context):
yield util.Uninferable
-def arguments_assigned_stmts(self, node, context, asspath=None):
+def arguments_assigned_stmts(self, node=None, context=None, asspath=None):
if context.callcontext:
# reset call context/name
callcontext = context.callcontext
@@ -343,7 +350,7 @@ nodes.Arguments.assigned_stmts = arguments_assigned_stmts
@decorators.raise_if_nothing_inferred
-def assign_assigned_stmts(self, node, context=None, asspath=None):
+def assign_assigned_stmts(self, node=None, context=None, asspath=None):
if not asspath:
yield self.value
return
@@ -388,7 +395,7 @@ def _resolve_asspart(parts, asspath, context):
@decorators.raise_if_nothing_inferred
-def excepthandler_assigned_stmts(self, node, context=None, asspath=None):
+def excepthandler_assigned_stmts(self, node=None, context=None, asspath=None):
for assigned in node_classes.unpack_infer(self.type):
if isinstance(assigned, nodes.ClassDef):
assigned = bases.Instance(assigned)
@@ -449,7 +456,7 @@ def _infer_context_manager(self, mgr, context):
@decorators.raise_if_nothing_inferred
-def with_assigned_stmts(self, node, context=None, asspath=None):
+def with_assigned_stmts(self, node=None, context=None, asspath=None):
"""Infer names and other nodes from a *with* statement.
This enables only inference for name binding in a *with* statement.
@@ -554,7 +561,7 @@ def starred_assigned_stmts(self, node=None, context=None, asspath=None):
# be the list of values which the Starred node will represent
# This is done in two steps, from left to right to remove
# anything before the starred node and from right to left
- # to remvoe anything after the starred node.
+ # to remove anything after the starred node.
for index, node in enumerate(lhs.elts):
if not isinstance(node, nodes.Starred):
diff --git a/astroid/scoped_nodes.py b/astroid/scoped_nodes.py
index 475cb38..a5680ad 100644
--- a/astroid/scoped_nodes.py
+++ b/astroid/scoped_nodes.py
@@ -467,6 +467,7 @@ class Module(LocalsDictNodeNG):
all = self['__all__']
except KeyError:
return default
+
try:
explicit = next(all.assigned_stmts())
except exceptions.InferenceError:
diff --git a/astroid/tests/unittest_protocols.py b/astroid/tests/unittest_protocols.py
index 6d6bce6..3b7b4fd 100644
--- a/astroid/tests/unittest_protocols.py
+++ b/astroid/tests/unittest_protocols.py
@@ -16,8 +16,10 @@
# You should have received a copy of the GNU Lesser General Public License along
# with astroid. If not, see <http://www.gnu.org/licenses/>.
+import contextlib
import unittest
+import astroid
from astroid.test_utils import extract_node, require_version
from astroid import InferenceError
from astroid import nodes
@@ -25,6 +27,15 @@ from astroid import util
from astroid.node_classes import AssignName, Const, Name, Starred
+@contextlib.contextmanager
+def _add_transform(manager, node, transform, predicate=None):
+ manager.register_transform(node, transform, predicate)
+ try:
+ yield
+ finally:
+ manager.unregister_transform(node, transform, predicate)
+
+
class ProtocolTests(unittest.TestCase):
def assertConstNodesEqual(self, nodes_list_expected, nodes_list_got):
@@ -149,6 +160,17 @@ class ProtocolTests(unittest.TestCase):
assigned = list(simple_mul_assnode_2.assigned_stmts())
self.assertNameNodesEqual(['c'], assigned)
+ def test_sequence_assigned_stmts_not_accepting_empty_node(self):
+ def transform(node):
+ node.root().locals['__all__'] = [node.value]
+
+ manager = astroid.MANAGER
+ with _add_transform(manager, astroid.Assign, transform):
+ module = astroid.parse('''
+ __all__ = ['a']
+ ''')
+ module.wildcard_import_names()
+
if __name__ == '__main__':
unittest.main()