default is stable

authorSylvain Th?nault <sylvain.thenault@logilab.fr>
changesetc98dccf1a17b
branchstable
phasepublic
hiddenno
parent revision#394a0b80b094 Fix python 3 crash on importing from a non existing module. Closes #83749, #b261077ba24c default is stable
child revision#de03a89f3826 properly define file_encoding Module attribute. Closes #104041
files modified by this revision
.hgtags
ChangeLog
MANIFEST.in
__init__.py
__pkginfo__.py
bases.py
brain/py2stdlib.py
debian/changelog
node_classes.py
test/unittest_nodes.py
test/unittest_python3.py
# HG changeset patch
# User Sylvain Thénault <sylvain.thenault@logilab.fr>
# Date 1343835689 -7200
# Wed Aug 01 17:41:29 2012 +0200
# Branch stable
# Node ID c98dccf1a17bf534f6c49cdfdb8c9a48038938f9
# Parent b261077ba24cbfabafb31f883b91ff416914c492
# Parent 394a0b80b094e087ed09835297903d7814e1be9f
default is stable

diff --git a/.hgtags b/.hgtags
@@ -33,5 +33,7 @@
1  688b0d8d1f06b3e8c1ccfbfa279ca8da76d5462a logilab-astng-debian-version-0.22.0-1
2  fd80e67a98016c455479ac80bb387c08970aae57 logilab-astng-version-0.23.0
3  8b510baa9baad004af644d464bd42e93ed695725 logilab-astng-debian-version-0.23.0-1
4  652e0a150ddac980779e4cafa7da0aa1af98cbb1 logilab-astng-version-0.23.1
5  abf75e6ca8ae4e48084ed1ee72a6cfa571c5db85 logilab-astng-debian-version-0.23.1-1
6 +e2ba7d936faf27fe8d680572265a99345f47faed logilab-astng-version-0.24.0
7 +d517d5a9bac963fac4777eecc34f09c1263c076c logilab-astng-debian-version-0.24.0-1
diff --git a/ChangeLog b/ChangeLog
@@ -1,13 +1,21 @@
8  Change log for the astng package
9  ================================
10 
11    --
12      * #92362: fix pyreverse crash on relative import
13 +
14 +2012-07-18  --  0.24.0
15 +    * include pylint brain extension, describing some stuff not properly understood until then.
16 +      (#100013, #53049, #23986, #72355)
17 +
18      * #99583: fix raw_building.object_build for pypy implementation
19 +
20      * use `open` rather than `file` in scoped_nodes as 2to3 miss it
21 
22 +
23 +
24  2011-12-08  --  0.23.1
25      * #62295: avoid "OSError: Too many open files" by moving
26        .file_stream as a Module property opening the file only when needed
27 
28      * Lambda nodes should have a `name` attribute
diff --git a/MANIFEST.in b/MANIFEST.in
@@ -4,5 +4,6 @@
29  include COPYING.LESSER
30  include test/fulltest.sh
31  recursive-include test/data *.py *.zip *.egg
32  recursive-include test/data2 *.py
33  recursive-include test/regrtest_data *.py
34 +recursive-include brain *.py
diff --git a/__init__.py b/__init__.py
@@ -1,6 +1,6 @@
35 -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
36 +# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
37  # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
38  # copyright 2003-2010 Sylvain Thenault, all rights reserved.
39  # contact mailto:thenault@gmail.com
40  #
41  # This file is part of logilab-astng.
@@ -69,5 +69,17 @@
42  # make a manager instance (borg) as well as Project and Package classes
43  # accessible from astng package
44  from logilab.astng.manager import ASTNGManager, Project
45  MANAGER = ASTNGManager()
46  del ASTNGManager
47 +
48 +# load brain plugins
49 +from os import listdir
50 +from os.path import join, dirname
51 +BRAIN_MODULES_DIR = join(dirname(__file__), 'brain')
52 +if BRAIN_MODULES_DIR not in sys.path:
53 +    # add it to the end of the list so user path take precedence
54 +    sys.path.append(BRAIN_MODULES_DIR)
55 +# load modules in this directory
56 +for module in listdir(BRAIN_MODULES_DIR):
57 +    if module.endswith('.py'):
58 +        __import__(module[:-3])
diff --git a/__pkginfo__.py b/__pkginfo__.py
@@ -22,11 +22,11 @@
59  distname = 'logilab-astng'
60 
61  modname = 'astng'
62  subpackage_of = 'logilab'
63 
64 -numversion = (0, 23, 1)
65 +numversion = (0, 24, 0)
66  version = '.'.join([str(num) for num in numversion])
67 
68  install_requires = ['logilab-common >= 0.53.0']
69 
70  license = 'LGPL'
@@ -38,11 +38,12 @@
71  ftp = "ftp://ftp.logilab.org/pub/%s" % modname
72 
73  description = "rebuild a new abstract syntax tree from Python's ast"
74 
75  from os.path import join
76 -include_dirs = [join('test', 'regrtest_data'),
77 +include_dirs = ['brain',
78 +                join('test', 'regrtest_data'),
79                  join('test', 'data'), join('test', 'data2')]
80 
81  classifiers = ["Topic :: Software Development :: Libraries :: Python Modules",
82                 "Topic :: Software Development :: Quality Assurance",
83                 "Programming Language :: Python",
diff --git a/bases.py b/bases.py
@@ -129,10 +129,12 @@
84  class _Yes(object):
85      """a yes object"""
86      def __repr__(self):
87          return 'YES'
88      def __getattribute__(self, name):
89 +        if name == 'next':
90 +            raise AttributeError('next method should not be called')
91          if name.startswith('__') and name.endswith('__'):
92              # to avoid inspection pb
93              return super(_Yes, self).__getattribute__(name)
94          return self
95      def __call__(self, *args, **kwargs):
diff --git a/brain/py2stdlib.py b/brain/py2stdlib.py
@@ -0,0 +1,119 @@
96 +"""ASTNG hooks for the Python 2 standard library.
97 +
98 +Currently help understanding of :
99 +
100 +* hashlib.md5 and hashlib.sha1
101 +"""
102 +
103 +from logilab.astng import MANAGER
104 +from logilab.astng.builder import ASTNGBuilder
105 +
106 +MODULE_TRANSFORMS = {}
107 +
108 +def hashlib_transform(module):
109 +    fake = ASTNGBuilder(MANAGER).string_build('''
110 +
111 +class md5(object):
112 +  def __init__(self, value): pass
113 +  def hexdigest(self):
114 +    return u''
115 +
116 +class sha1(object):
117 +  def __init__(self, value): pass
118 +  def hexdigest(self):
119 +    return u''
120 +
121 +''')
122 +    for hashfunc in ('sha1', 'md5'):
123 +        module.locals[hashfunc] = fake.locals[hashfunc]
124 +
125 +def collections_transform(module):
126 +    fake = ASTNGBuilder(MANAGER).string_build('''
127 +
128 +class defaultdict(dict):
129 +    default_factory = None
130 +    def __missing__(self, key): pass
131 +
132 +class deque(object):
133 +    maxlen = 0
134 +    def __init__(iterable=None, maxlen=None): pass
135 +    def append(self, x): pass
136 +    def appendleft(self, x): pass
137 +    def clear(self): pass
138 +    def count(self, x): return 0
139 +    def extend(self, iterable): pass
140 +    def extendleft(self, iterable): pass
141 +    def pop(self): pass
142 +    def popleft(self): pass
143 +    def remove(self, value): pass
144 +    def reverse(self): pass
145 +    def rotate(self, n): pass
146 +
147 +''')
148 +
149 +    for klass in ('deque', 'defaultdict'):
150 +        module.locals[klass] = fake.locals[klass]
151 +
152 +def pkg_resources_transform(module):
153 +    fake = ASTNGBuilder(MANAGER).string_build('''
154 +
155 +def resource_exists(package_or_requirement, resource_name):
156 +    pass
157 +
158 +def resource_isdir(package_or_requirement, resource_name):
159 +    pass
160 +
161 +def resource_filename(package_or_requirement, resource_name):
162 +    pass
163 +
164 +def resource_stream(package_or_requirement, resource_name):
165 +    pass
166 +
167 +def resource_string(package_or_requirement, resource_name):
168 +    pass
169 +
170 +def resource_listdir(package_or_requirement, resource_name):
171 +    pass
172 +
173 +def extraction_error():
174 +    pass
175 +
176 +def get_cache_path(archive_name, names=()):
177 +    pass
178 +
179 +def postprocess(tempname, filename):
180 +    pass
181 +
182 +def set_extraction_path(path):
183 +    pass
184 +
185 +def cleanup_resources(force=False):
186 +    pass
187 +
188 +''')
189 +
190 +    for func_name, func in fake.locals.items():
191 +        module.locals[func_name] = func
192 +
193 +    # for func in ('resource_exists', 'resource_isdir', 'resource_filename',
194 +    #     'resource_stream', 'resource_string', 'resource_listdir',
195 +    #     'extraction_error', 'get_cache_path', 'postprocess',
196 +    #     'set_extraction_path', 'cleanup_resources'):
197 +
198 +    #     module.locals[func] = fake.locals[func]
199 +
200 +MODULE_TRANSFORMS['hashlib'] = hashlib_transform
201 +MODULE_TRANSFORMS['collections'] = collections_transform
202 +MODULE_TRANSFORMS['pkg_resources'] = pkg_resources_transform
203 +
204 +
205 +def transform(module):
206 +    try:
207 +        tr = MODULE_TRANSFORMS[module.name]
208 +    except KeyError:
209 +        pass
210 +    else:
211 +        tr(module)
212 +
213 +from logilab.astng import MANAGER
214 +MANAGER.register_transformer(transform)
diff --git a/debian/changelog b/debian/changelog
@@ -1,5 +1,11 @@
215 +logilab-astng (0.24.0-1) unstable; urgency=low
216 +
217 +  * new upstream release
218 +
219 + -- Sylvain Thénault <sylvain.thenault@logilab.fr>  Wed, 18 Jul 2012 08:16:33 +0200
220 +
221  logilab-astng (0.23.1-1) unstable; urgency=low
222 
223    * new upstream release
224 
225   -- Sylvain Thénault <sylvain.thenault@logilab.fr>  Thu, 08 Dec 2011 15:25:45 +0100
diff --git a/node_classes.py b/node_classes.py
@@ -1,6 +1,6 @@
226 -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
227 +# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
228  # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
229  # copyright 2003-2010 Sylvain Thenault, all rights reserved.
230  # contact mailto:thenault@gmail.com
231  #
232  # This file is part of logilab-astng.
@@ -37,17 +37,22 @@
233      if isinstance(stmt, (List, Tuple)):
234          for elt in stmt.elts:
235              for infered_elt in unpack_infer(elt, context):
236                  yield infered_elt
237          return
238 +    # if infered is a final node, return it and stop
239      infered = stmt.infer(context).next()
240 -    if infered is stmt or infered is YES:
241 +    if infered is stmt:
242          yield infered
243          return
244 +    # else, infer recursivly, except YES object that should be returned as is
245      for infered in stmt.infer(context):
246 -        for inf_inf in unpack_infer(infered, context):
247 -            yield inf_inf
248 +        if infered is YES:
249 +            yield infered
250 +        else:
251 +            for inf_inf in unpack_infer(infered, context):
252 +                yield inf_inf
253 
254 
255  def are_exclusive(stmt1, stmt2, exceptions=None):
256      """return true if the two given statements are mutually exclusive
257 
@@ -743,11 +748,11 @@
258      _astng_fields = ('lower', 'upper', 'step')
259      lower = None
260      upper = None
261      step = None
262 
263 -class Starred(NodeNG):
264 +class Starred(NodeNG, ParentAssignTypeMixin):
265      """class representing a Starred node"""
266      _astng_fields = ('value',)
267      value = None
268 
269 
diff --git a/test/unittest_nodes.py b/test/unittest_nodes.py
@@ -32,10 +32,12 @@
270  """tests for specific behaviour of astng nodes
271  """
272  import sys
273 
274  from logilab.common import testlib
275 +from logilab.astng.node_classes import unpack_infer
276 +from logilab.astng.bases import YES
277  from logilab.astng.exceptions import ASTNGBuildingException, NotFoundError
278  from logilab.astng import BUILTINS_MODULE, builder, nodes
279  from logilab.astng.as_string import as_string
280 
281  from data import module as test_module
@@ -276,10 +278,36 @@
282  from .store import bread
283  from ..cave import wine\n\n"""
284          ast = abuilder.string_build(code)
285          self.assertMultiLineEqual(ast.as_string(), code)
286 
287 +    def test_bad_import_inference(self):
288 +        # Explication of bug
289 +        '''When we import PickleError from nonexistent, a call to the infer
290 +        method of this From node will be made by unpack_infer.
291 +        inference.infer_from will try to import this module, which will fail and
292 +        raise a InferenceException (by mixins.do_import_module). The infer_name
293 +        will catch this exception and yield and YES instead.
294 +        '''
295 +
296 +        code = '''try:
297 +    from pickle import PickleError
298 +except ImportError:
299 +    from nonexistent import PickleError
300 +
301 +try:
302 +    pass
303 +except PickleError:
304 +    pass
305 +        '''
306 +
307 +        astng = abuilder.string_build(code)
308 +        from_node = astng.body[1].handlers[0].body[0]
309 +        handler_type = astng.body[1].handlers[0].type
310 +
311 +        excs = list(unpack_infer(handler_type))
312 +
313  class CmpNodeTC(testlib.TestCase):
314      def test_as_string(self):
315          ast = abuilder.string_build("a == 2").body[0]
316          self.assertEqual(as_string(ast), "a == 2")
317 
diff --git a/test/unittest_python3.py b/test/unittest_python3.py
@@ -0,0 +1,46 @@
318 +# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
319 +# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
320 +# copyright 2003-2010 Sylvain Thenault, all rights reserved.
321 +# contact mailto:thenault@gmail.com
322 +#
323 +# This file is part of logilab-astng.
324 +#
325 +# logilab-astng is free software: you can redistribute it and/or modify it
326 +# under the terms of the GNU Lesser General Public License as published by the
327 +# Free Software Foundation, either version 2.1 of the License, or (at your
328 +# option) any later version.
329 +#
330 +# logilab-astng is distributed in the hope that it will be useful, but
331 +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
332 +# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
333 +# for more details.
334 +#
335 +# You should have received a copy of the GNU Lesser General Public License along
336 +# with logilab-astng. If not, see <http://www.gnu.org/licenses/>.
337 +import sys
338 +
339 +from logilab.common.testlib import TestCase, unittest_main
340 +
341 +from logilab.astng.node_classes import Assign
342 +from logilab.astng.manager import ASTNGManager
343 +from logilab.astng.builder import ASTNGBuilder
344 +
345 +
346 +class Python3TC(TestCase):
347 +    def setUp(self):
348 +        self.manager = ASTNGManager()
349 +        self.builder = ASTNGBuilder(self.manager)
350 +        self.manager.astng_cache.clear()
351 +
352 +    def test_starred_notation(self):
353 +        if sys.version_info < (3, 0):
354 +            self.skipTest("test python 3k specific")
355 +        astng = self.builder.string_build("*a, b = [1, 2, 3]", 'test', 'test')
356 +
357 +        # Get the star node
358 +        node = next(next(next(astng.get_children()).get_children()).get_children())
359 +
360 +        self.assertTrue(isinstance(node.ass_type(), Assign))
361 +
362 +if __name__ == '__main__':
363 +    unittest_main()