diff options
-rw-r--r-- | ChangeLog | 11 | ||||
-rw-r--r-- | astroid/node_classes.py | 27 | ||||
-rw-r--r-- | astroid/protocols.py | 33 | ||||
-rw-r--r-- | astroid/scoped_nodes.py | 1 | ||||
-rw-r--r-- | astroid/tests/unittest_protocols.py | 22 |
5 files changed, 79 insertions, 15 deletions
@@ -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() |