Fix python 3 crash on importing from a non existing module. Closes #83749

Pb is actually silented in python 2 since YES objet are seen as iterable since next() is seen as a proper attribute (returning itself, see _Yes.__getattribute__), while it becomes a function in python 3.

authorFELD Boris <lothiraldan@gmail.com>
changeset394a0b80b094
branchdefault
phasepublic
hiddenno
parent revision#26f211952721 Make Starred node extends ParentAssignTypeMixin as it can be part of Assign ast node in python-3k. Closes #83138.
child revision#e3b8e6d27ea0 backport stable, #c98dccf1a17b default is stable
files modified by this revision
bases.py
node_classes.py
test/unittest_nodes.py
# HG changeset patch
# User FELD Boris <lothiraldan@gmail.com>
# Date 1343724029 -7200
# Tue Jul 31 10:40:29 2012 +0200
# Node ID 394a0b80b094e087ed09835297903d7814e1be9f
# Parent 26f211952721c2fdff9b48562fe9a8a918e7e591
Fix python 3 crash on importing from a non existing module. Closes #83749

Pb is actually silented in python 2 since YES objet are seen as iterable
since next() is seen as a proper attribute (returning itself, see
_Yes.__getattribute__), while it becomes a function in python 3.

diff --git a/bases.py b/bases.py
@@ -129,10 +129,12 @@
1  class _Yes(object):
2      """a yes object"""
3      def __repr__(self):
4          return 'YES'
5      def __getattribute__(self, name):
6 +        if name == 'next':
7 +            raise AttributeError('next method should not be called')
8          if name.startswith('__') and name.endswith('__'):
9              # to avoid inspection pb
10              return super(_Yes, self).__getattribute__(name)
11          return self
12      def __call__(self, *args, **kwargs):
diff --git a/node_classes.py b/node_classes.py
@@ -1,6 +1,6 @@
13 -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
14 +# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
15  # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
16  # copyright 2003-2010 Sylvain Thenault, all rights reserved.
17  # contact mailto:thenault@gmail.com
18  #
19  # This file is part of logilab-astng.
@@ -37,17 +37,22 @@
20      if isinstance(stmt, (List, Tuple)):
21          for elt in stmt.elts:
22              for infered_elt in unpack_infer(elt, context):
23                  yield infered_elt
24          return
25 +    # if infered is a final node, return it and stop
26      infered = stmt.infer(context).next()
27 -    if infered is stmt or infered is YES:
28 +    if infered is stmt:
29          yield infered
30          return
31 +    # else, infer recursivly, except YES object that should be returned as is
32      for infered in stmt.infer(context):
33 -        for inf_inf in unpack_infer(infered, context):
34 -            yield inf_inf
35 +        if infered is YES:
36 +            yield infered
37 +        else:
38 +            for inf_inf in unpack_infer(infered, context):
39 +                yield inf_inf
40 
41 
42  def are_exclusive(stmt1, stmt2, exceptions=None):
43      """return true if the two given statements are mutually exclusive
44 
diff --git a/test/unittest_nodes.py b/test/unittest_nodes.py
@@ -32,10 +32,12 @@
45  """tests for specific behaviour of astng nodes
46  """
47  import sys
48 
49  from logilab.common import testlib
50 +from logilab.astng.node_classes import unpack_infer
51 +from logilab.astng.bases import YES
52  from logilab.astng.exceptions import ASTNGBuildingException, NotFoundError
53  from logilab.astng import BUILTINS_MODULE, builder, nodes
54  from logilab.astng.as_string import as_string
55 
56  from data import module as test_module
@@ -276,10 +278,36 @@
57  from .store import bread
58  from ..cave import wine\n\n"""
59          ast = abuilder.string_build(code)
60          self.assertMultiLineEqual(ast.as_string(), code)
61 
62 +    def test_bad_import_inference(self):
63 +        # Explication of bug
64 +        '''When we import PickleError from nonexistent, a call to the infer
65 +        method of this From node will be made by unpack_infer.
66 +        inference.infer_from will try to import this module, which will fail and
67 +        raise a InferenceException (by mixins.do_import_module). The infer_name
68 +        will catch this exception and yield and YES instead.
69 +        '''
70 +
71 +        code = '''try:
72 +    from pickle import PickleError
73 +except ImportError:
74 +    from nonexistent import PickleError
75 +
76 +try:
77 +    pass
78 +except PickleError:
79 +    pass
80 +        '''
81 +
82 +        astng = abuilder.string_build(code)
83 +        from_node = astng.body[1].handlers[0].body[0]
84 +        handler_type = astng.body[1].handlers[0].type
85 +
86 +        excs = list(unpack_infer(handler_type))
87 +
88  class CmpNodeTC(testlib.TestCase):
89      def test_as_string(self):
90          ast = abuilder.string_build("a == 2").body[0]
91          self.assertEqual(as_string(ast), "a == 2")
92