summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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()