logilab-astng-0.24.3/ 0000775 0000151 0000155 00000000000 12133517272 013650 5 ustar narval narval logilab-astng-0.24.3/test/ 0000775 0000151 0000155 00000000000 12133517272 014627 5 ustar narval narval logilab-astng-0.24.3/test/unittest_python3.py 0000644 0000151 0000155 00000003114 12133517265 020543 0 ustar narval narval # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of logilab-astng.
#
# logilab-astng is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 2.1 of the License, or (at your
# option) any later version.
#
# logilab-astng is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see .
import sys
from logilab.common.testlib import TestCase, unittest_main, require_version
from logilab.astng.node_classes import Assign
from logilab.astng.manager import ASTNGManager
from logilab.astng.builder import ASTNGBuilder
class Python3TC(TestCase):
def setUp(self):
self.manager = ASTNGManager()
self.builder = ASTNGBuilder(self.manager)
self.manager.astng_cache.clear()
@require_version('3.0')
def test_starred_notation(self):
astng = self.builder.string_build("*a, b = [1, 2, 3]", 'test', 'test')
# Get the star node
node = next(next(next(astng.get_children()).get_children()).get_children())
self.assertTrue(isinstance(node.ass_type(), Assign))
if __name__ == '__main__':
unittest_main()
logilab-astng-0.24.3/test/data2/ 0000775 0000151 0000155 00000000000 12133517272 015622 5 ustar narval narval logilab-astng-0.24.3/test/data2/__init__.py 0000644 0000151 0000155 00000000000 12133517265 017721 0 ustar narval narval logilab-astng-0.24.3/test/data2/clientmodule_test.py 0000644 0000151 0000155 00000001441 12133517265 021717 0 ustar narval narval """ docstring for file clientmodule.py """
from data2.suppliermodule_test import Interface as IFace, DoNothing
class Toto: pass
class Ancestor:
""" Ancestor method """
__implements__ = (IFace,)
def __init__(self, value):
local_variable = 0
self.attr = 'this method shouldn\'t have a docstring'
self.__value = value
def get_value(self):
""" nice docstring ;-) """
return self.__value
def set_value(self, value):
self.__value = value
return 'this method shouldn\'t have a docstring'
class Specialization(Ancestor):
TYPE = 'final class'
top = 'class'
def __init__(self, value, _id):
Ancestor.__init__(self, value)
self._id = _id
self.relation = DoNothing()
self.toto = Toto()
logilab-astng-0.24.3/test/data2/suppliermodule_test.py 0000644 0000151 0000155 00000000354 12133517265 022306 0 ustar narval narval """ file suppliermodule.py """
class NotImplemented(Exception):
pass
class Interface:
def get_value(self):
raise NotImplemented()
def set_value(self, value):
raise NotImplemented()
class DoNothing : pass
logilab-astng-0.24.3/test/unittest_manager.py 0000644 0000151 0000155 00000010740 12133517265 020554 0 ustar narval narval # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of logilab-astng.
#
# logilab-astng is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 2.1 of the License, or (at your
# option) any later version.
#
# logilab-astng is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see .
from logilab.common.testlib import TestCase, unittest_main
import sys
from os.path import join, abspath, dirname
from logilab.astng.manager import ASTNGManager, _silent_no_wrap
from logilab.astng.bases import BUILTINS
DATA = join(dirname(abspath(__file__)), 'data')
class ASTNGManagerTC(TestCase):
def setUp(self):
self.manager = ASTNGManager()
self.manager.astng_cache.clear()
def test_astng_from_module(self):
import unittest
astng = self.manager.astng_from_module(unittest)
self.assertEqual(astng.pure_python, True)
import time
astng = self.manager.astng_from_module(time)
self.assertEqual(astng.pure_python, False)
def test_astng_from_class(self):
astng = self.manager.astng_from_class(int)
self.assertEqual(astng.name, 'int')
self.assertEqual(astng.parent.frame().name, BUILTINS)
astng = self.manager.astng_from_class(object)
self.assertEqual(astng.name, 'object')
self.assertEqual(astng.parent.frame().name, BUILTINS)
self.assertIn('__setattr__', astng)
def _test_astng_from_zip(self, archive):
origpath = sys.path[:]
sys.modules.pop('mypypa', None)
archive_path = join(DATA, archive)
sys.path.insert(0, archive_path)
try:
module = self.manager.astng_from_module_name('mypypa')
self.assertEqual(module.name, 'mypypa')
self.assertTrue(module.file.endswith('%s/mypypa' % archive),
module.file)
finally:
# remove the module, else after importing egg, we don't get the zip
if 'mypypa' in self.manager.astng_cache:
del self.manager.astng_cache['mypypa']
del self.manager._mod_file_cache[('mypypa', None)]
if archive_path in sys.path_importer_cache:
del sys.path_importer_cache[archive_path]
sys.path = origpath
def test_astng_from_module_name_egg(self):
self._test_astng_from_zip('MyPyPa-0.1.0-py2.5.egg')
def test_astng_from_module_name_zip(self):
self._test_astng_from_zip('MyPyPa-0.1.0-py2.5.zip')
def test_from_directory(self):
obj = self.manager.project_from_files([DATA], _silent_no_wrap, 'data')
self.assertEqual(obj.name, 'data')
self.assertEqual(obj.path, join(DATA, '__init__.py'))
def test_project_node(self):
obj = self.manager.project_from_files([DATA], _silent_no_wrap, 'data')
expected = set(['SSL1', '__init__', 'all', 'appl', 'format', 'module',
'module2', 'noendingnewline', 'nonregr', 'notall'])
expected = ['data', 'data.SSL1', 'data.SSL1.Connection1',
'data.absimport', 'data.all',
'data.appl', 'data.appl.myConnection', 'data.email', 'data.format',
'data.module', 'data.module2', 'data.noendingnewline',
'data.nonregr', 'data.notall']
self.assertListEqual(sorted(k for k in obj.keys()), expected)
def test_do_not_expose_main(self):
obj = self.manager.astng_from_module_name('__main__')
self.assertEqual(obj.name, '__main__')
self.assertEqual(obj.items(), [])
class BorgASTNGManagerTC(TestCase):
def test_borg(self):
"""test that the ASTNGManager is really a borg, i.e. that two different
instances has same cache"""
first_manager = ASTNGManager()
built = first_manager.astng_from_module_name(BUILTINS)
second_manager = ASTNGManager()
second_built = first_manager.astng_from_module_name(BUILTINS)
self.assertIs(built, second_built)
if __name__ == '__main__':
unittest_main()
logilab-astng-0.24.3/test/unittest_builder.py 0000644 0000151 0000155 00000063036 12133517265 020576 0 ustar narval narval # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of logilab-astng.
#
# logilab-astng is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 2.1 of the License, or (at your
# option) any later version.
#
# logilab-astng is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see .
"""tests for the astng builder and rebuilder module"""
import unittest
import sys
from os.path import join, abspath, dirname
from logilab.common.testlib import TestCase, unittest_main
from pprint import pprint
from logilab.astng import builder, nodes, InferenceError, NotFoundError
from logilab.astng.nodes import Module
from logilab.astng.bases import YES, BUILTINS
from logilab.astng.manager import ASTNGManager
MANAGER = ASTNGManager()
from unittest_inference import get_name_node
import data
from data import module as test_module
DATA = join(dirname(abspath(__file__)), 'data')
class FromToLineNoTC(TestCase):
astng = builder.ASTNGBuilder().file_build(join(DATA, 'format.py'))
def test_callfunc_lineno(self):
stmts = self.astng.body
# on line 4:
# function('aeozrijz\
# earzer', hop)
discard = stmts[0]
self.assertIsInstance(discard, nodes.Discard)
self.assertEqual(discard.fromlineno, 4)
self.assertEqual(discard.tolineno, 5)
callfunc = discard.value
self.assertIsInstance(callfunc, nodes.CallFunc)
self.assertEqual(callfunc.fromlineno, 4)
self.assertEqual(callfunc.tolineno, 5)
name = callfunc.func
self.assertIsInstance(name, nodes.Name)
self.assertEqual(name.fromlineno, 4)
self.assertEqual(name.tolineno, 4)
strarg = callfunc.args[0]
self.assertIsInstance(strarg, nodes.Const)
self.assertEqual(strarg.fromlineno, 5) # no way for this one (is 4 actually)
self.assertEqual(strarg.tolineno, 5)
namearg = callfunc.args[1]
self.assertIsInstance(namearg, nodes.Name)
self.assertEqual(namearg.fromlineno, 5)
self.assertEqual(namearg.tolineno, 5)
# on line 10:
# fonction(1,
# 2,
# 3,
# 4)
discard = stmts[2]
self.assertIsInstance(discard, nodes.Discard)
self.assertEqual(discard.fromlineno, 10)
self.assertEqual(discard.tolineno, 13)
callfunc = discard.value
self.assertIsInstance(callfunc, nodes.CallFunc)
self.assertEqual(callfunc.fromlineno, 10)
self.assertEqual(callfunc.tolineno, 13)
name = callfunc.func
self.assertIsInstance(name, nodes.Name)
self.assertEqual(name.fromlineno, 10)
self.assertEqual(name.tolineno, 10)
for i, arg in enumerate(callfunc.args):
self.assertIsInstance(arg, nodes.Const)
self.assertEqual(arg.fromlineno, 10+i)
self.assertEqual(arg.tolineno, 10+i)
def test_function_lineno(self):
stmts = self.astng.body
# on line 15:
# def definition(a,
# b,
# c):
# return a + b + c
function = stmts[3]
self.assertIsInstance(function, nodes.Function)
self.assertEqual(function.fromlineno, 15)
self.assertEqual(function.tolineno, 18)
return_ = function.body[0]
self.assertIsInstance(return_, nodes.Return)
self.assertEqual(return_.fromlineno, 18)
self.assertEqual(return_.tolineno, 18)
if sys.version_info < (3, 0):
self.assertEqual(function.blockstart_tolineno, 17)
else:
self.skipTest('FIXME http://bugs.python.org/issue10445 '
'(no line number on function args)')
def test_decorated_function_lineno(self):
astng = builder.ASTNGBuilder().string_build('''
@decorator
def function(
arg):
print (arg)
''', __name__, __file__)
function = astng['function']
self.assertEqual(function.fromlineno, 3) # XXX discussable, but that's what is expected by pylint right now
self.assertEqual(function.tolineno, 5)
self.assertEqual(function.decorators.fromlineno, 2)
self.assertEqual(function.decorators.tolineno, 2)
if sys.version_info < (3, 0):
self.assertEqual(function.blockstart_tolineno, 4)
else:
self.skipTest('FIXME http://bugs.python.org/issue10445 '
'(no line number on function args)')
def test_class_lineno(self):
stmts = self.astng.body
# on line 20:
# class debile(dict,
# object):
# pass
class_ = stmts[4]
self.assertIsInstance(class_, nodes.Class)
self.assertEqual(class_.fromlineno, 20)
self.assertEqual(class_.tolineno, 22)
self.assertEqual(class_.blockstart_tolineno, 21)
pass_ = class_.body[0]
self.assertIsInstance(pass_, nodes.Pass)
self.assertEqual(pass_.fromlineno, 22)
self.assertEqual(pass_.tolineno, 22)
def test_if_lineno(self):
stmts = self.astng.body
# on line 20:
# if aaaa: pass
# else:
# aaaa,bbbb = 1,2
# aaaa,bbbb = bbbb,aaaa
if_ = stmts[5]
self.assertIsInstance(if_, nodes.If)
self.assertEqual(if_.fromlineno, 24)
self.assertEqual(if_.tolineno, 27)
self.assertEqual(if_.blockstart_tolineno, 24)
self.assertEqual(if_.orelse[0].fromlineno, 26)
self.assertEqual(if_.orelse[1].tolineno, 27)
def test_for_while_lineno(self):
for code in ('''
for a in range(4):
print (a)
break
else:
print ("bouh")
''', '''
while a:
print (a)
break
else:
print ("bouh")
''',
):
astng = builder.ASTNGBuilder().string_build(code, __name__, __file__)
stmt = astng.body[0]
self.assertEqual(stmt.fromlineno, 2)
self.assertEqual(stmt.tolineno, 6)
self.assertEqual(stmt.blockstart_tolineno, 2)
self.assertEqual(stmt.orelse[0].fromlineno, 6) # XXX
self.assertEqual(stmt.orelse[0].tolineno, 6)
def test_try_except_lineno(self):
astng = builder.ASTNGBuilder().string_build('''
try:
print (a)
except:
pass
else:
print ("bouh")
''', __name__, __file__)
try_ = astng.body[0]
self.assertEqual(try_.fromlineno, 2)
self.assertEqual(try_.tolineno, 7)
self.assertEqual(try_.blockstart_tolineno, 2)
self.assertEqual(try_.orelse[0].fromlineno, 7) # XXX
self.assertEqual(try_.orelse[0].tolineno, 7)
hdlr = try_.handlers[0]
self.assertEqual(hdlr.fromlineno, 4)
self.assertEqual(hdlr.tolineno, 5)
self.assertEqual(hdlr.blockstart_tolineno, 4)
def test_try_finally_lineno(self):
astng = builder.ASTNGBuilder().string_build('''
try:
print (a)
finally:
print ("bouh")
''', __name__, __file__)
try_ = astng.body[0]
self.assertEqual(try_.fromlineno, 2)
self.assertEqual(try_.tolineno, 5)
self.assertEqual(try_.blockstart_tolineno, 2)
self.assertEqual(try_.finalbody[0].fromlineno, 5) # XXX
self.assertEqual(try_.finalbody[0].tolineno, 5)
def test_try_finally_25_lineno(self):
astng = builder.ASTNGBuilder().string_build('''
try:
print (a)
except:
pass
finally:
print ("bouh")
''', __name__, __file__)
try_ = astng.body[0]
self.assertEqual(try_.fromlineno, 2)
self.assertEqual(try_.tolineno, 7)
self.assertEqual(try_.blockstart_tolineno, 2)
self.assertEqual(try_.finalbody[0].fromlineno, 7) # XXX
self.assertEqual(try_.finalbody[0].tolineno, 7)
def test_with_lineno(self):
astng = builder.ASTNGBuilder().string_build('''
from __future__ import with_statement
with file("/tmp/pouet") as f:
print (f)
''', __name__, __file__)
with_ = astng.body[1]
self.assertEqual(with_.fromlineno, 3)
self.assertEqual(with_.tolineno, 4)
self.assertEqual(with_.blockstart_tolineno, 3)
class BuilderTC(TestCase):
def setUp(self):
self.builder = builder.ASTNGBuilder()
def test_border_cases(self):
"""check that a file with no trailing new line is parseable"""
self.builder.file_build(join(DATA, 'noendingnewline.py'), 'data.noendingnewline')
self.assertRaises(builder.ASTNGBuildingException,
self.builder.file_build, join(DATA, 'inexistant.py'), 'whatever')
def test_inspect_build0(self):
"""test astng tree build from a living object"""
builtin_astng = MANAGER.astng_from_module_name(BUILTINS)
if sys.version_info < (3, 0):
fclass = builtin_astng['file']
self.assertIn('name', fclass)
self.assertIn('mode', fclass)
self.assertIn('read', fclass)
self.assertTrue(fclass.newstyle)
self.assertTrue(fclass.pytype(), '%s.type' % BUILTINS)
self.assertIsInstance(fclass['read'], nodes.Function)
# check builtin function has args.args == None
dclass = builtin_astng['dict']
self.assertIsNone(dclass['has_key'].args.args)
# just check type and object are there
builtin_astng.getattr('type')
objectastng = builtin_astng.getattr('object')[0]
self.assertIsInstance(objectastng.getattr('__new__')[0], nodes.Function)
# check open file alias
builtin_astng.getattr('open')
# check 'help' is there (defined dynamically by site.py)
builtin_astng.getattr('help')
# check property has __init__
pclass = builtin_astng['property']
self.assertIn('__init__', pclass)
self.assertIsInstance(builtin_astng['None'], nodes.Const)
self.assertIsInstance(builtin_astng['True'], nodes.Const)
self.assertIsInstance(builtin_astng['False'], nodes.Const)
if sys.version_info < (3, 0):
self.assertIsInstance(builtin_astng['Exception'], nodes.From)
self.assertIsInstance(builtin_astng['NotImplementedError'], nodes.From)
else:
self.assertIsInstance(builtin_astng['Exception'], nodes.Class)
self.assertIsInstance(builtin_astng['NotImplementedError'], nodes.Class)
def test_inspect_build1(self):
time_astng = MANAGER.astng_from_module_name('time')
self.assertTrue(time_astng)
self.assertEqual(time_astng['time'].args.defaults, [])
def test_inspect_build2(self):
"""test astng tree build from a living object"""
try:
from mx import DateTime
except ImportError:
self.skipTest('test skipped: mxDateTime is not available')
else:
dt_astng = self.builder.inspect_build(DateTime)
dt_astng.getattr('DateTime')
# this one is failing since DateTimeType.__module__ = 'builtins' !
#dt_astng.getattr('DateTimeType')
def test_inspect_build3(self):
self.builder.inspect_build(unittest)
def test_inspect_build_instance(self):
"""test astng tree build from a living object"""
if sys.version_info >= (3, 0):
self.skipTest('The module "exceptions" is gone in py3.x')
import exceptions
builtin_astng = self.builder.inspect_build(exceptions)
fclass = builtin_astng['OSError']
# things like OSError.strerror are now (2.5) data descriptors on the
# class instead of entries in the __dict__ of an instance
container = fclass
self.assertIn('errno', container)
self.assertIn('strerror', container)
self.assertIn('filename', container)
def test_inspect_build_type_object(self):
builtin_astng = MANAGER.astng_from_module_name(BUILTINS)
infered = list(builtin_astng.igetattr('object'))
self.assertEqual(len(infered), 1)
infered = infered[0]
self.assertEqual(infered.name, 'object')
infered.as_string() # no crash test
infered = list(builtin_astng.igetattr('type'))
self.assertEqual(len(infered), 1)
infered = infered[0]
self.assertEqual(infered.name, 'type')
infered.as_string() # no crash test
def test_package_name(self):
"""test base properties and method of a astng module"""
datap = self.builder.file_build(join(DATA, '__init__.py'), 'data')
self.assertEqual(datap.name, 'data')
self.assertEqual(datap.package, 1)
datap = self.builder.file_build(join(DATA, '__init__.py'), 'data.__init__')
self.assertEqual(datap.name, 'data')
self.assertEqual(datap.package, 1)
def test_yield_parent(self):
"""check if we added discard nodes as yield parent (w/ compiler)"""
data = """
def yiell():
yield 0
if noe:
yield more
"""
func = self.builder.string_build(data).body[0]
self.assertIsInstance(func, nodes.Function)
stmt = func.body[0]
self.assertIsInstance(stmt, nodes.Discard)
self.assertIsInstance(stmt.value, nodes.Yield)
self.assertIsInstance(func.body[1].body[0], nodes.Discard)
self.assertIsInstance(func.body[1].body[0].value, nodes.Yield)
def test_object(self):
obj_astng = self.builder.inspect_build(object)
self.assertIn('__setattr__', obj_astng)
def test_newstyle_detection(self):
data = '''
class A:
"old style"
class B(A):
"old style"
class C(object):
"new style"
class D(C):
"new style"
__metaclass__ = type
class E(A):
"old style"
class F:
"new style"
'''
mod_astng = self.builder.string_build(data, __name__, __file__)
self.assertFalse(mod_astng['A'].newstyle)
self.assertFalse(mod_astng['B'].newstyle)
self.assertTrue(mod_astng['C'].newstyle)
self.assertTrue(mod_astng['D'].newstyle)
self.assertFalse(mod_astng['E'].newstyle)
self.assertTrue(mod_astng['F'].newstyle)
def test_globals(self):
data = '''
CSTE = 1
def update_global():
global CSTE
CSTE += 1
def global_no_effect():
global CSTE2
print (CSTE)
'''
astng = self.builder.string_build(data, __name__, __file__)
self.assertEqual(len(astng.getattr('CSTE')), 2)
self.assertIsInstance(astng.getattr('CSTE')[0], nodes.AssName)
self.assertEqual(astng.getattr('CSTE')[0].fromlineno, 2)
self.assertEqual(astng.getattr('CSTE')[1].fromlineno, 6)
self.assertRaises(NotFoundError,
astng.getattr, 'CSTE2')
self.assertRaises(InferenceError,
astng['global_no_effect'].ilookup('CSTE2').next)
def test_socket_build(self):
import socket
astng = self.builder.module_build(socket)
# XXX just check the first one. Actually 3 objects are inferred (look at
# the socket module) but the last one as those attributes dynamically
# set and astng is missing this.
for fclass in astng.igetattr('socket'):
#print fclass.root().name, fclass.name, fclass.lineno
self.assertIn('connect', fclass)
self.assertIn('send', fclass)
self.assertIn('close', fclass)
break
def test_gen_expr_var_scope(self):
data = 'l = list(n for n in range(10))\n'
astng = self.builder.string_build(data, __name__, __file__)
# n unavailable outside gen expr scope
self.assertNotIn('n', astng)
# test n is inferable anyway
n = get_name_node(astng, 'n')
self.assertIsNot(n.scope(), astng)
self.assertEqual([i.__class__ for i in n.infer()],
[YES.__class__])
class FileBuildTC(TestCase):
module = builder.ASTNGBuilder().file_build(join(DATA, 'module.py'), 'data.module')
def test_module_base_props(self):
"""test base properties and method of a astng module"""
module = self.module
self.assertEqual(module.name, 'data.module')
self.assertEqual(module.doc, "test module for astng\n")
self.assertEqual(module.fromlineno, 0)
self.assertIsNone(module.parent)
self.assertEqual(module.frame(), module)
self.assertEqual(module.root(), module)
self.assertEqual(module.file, join(abspath(data.__path__[0]), 'module.py'))
self.assertEqual(module.pure_python, 1)
self.assertEqual(module.package, 0)
self.assertFalse(module.is_statement)
self.assertEqual(module.statement(), module)
self.assertEqual(module.statement(), module)
def test_module_locals(self):
"""test the 'locals' dictionary of a astng module"""
module = self.module
_locals = module.locals
self.assertIs(_locals, module.globals)
keys = sorted(_locals.keys())
should = ['MY_DICT', 'YO', 'YOUPI',
'__revision__', 'global_access','modutils', 'four_args',
'os', 'redirect', 'spawn', 'LocalsVisitor', 'ASTWalker']
should.sort()
self.assertEqual(keys, should)
def test_function_base_props(self):
"""test base properties and method of a astng function"""
module = self.module
function = module['global_access']
self.assertEqual(function.name, 'global_access')
self.assertEqual(function.doc, 'function test')
self.assertEqual(function.fromlineno, 11)
self.assertTrue(function.parent)
self.assertEqual(function.frame(), function)
self.assertEqual(function.parent.frame(), module)
self.assertEqual(function.root(), module)
self.assertEqual([n.name for n in function.args.args], ['key', 'val'])
self.assertEqual(function.type, 'function')
def test_function_locals(self):
"""test the 'locals' dictionary of a astng function"""
_locals = self.module['global_access'].locals
self.assertEqual(len(_locals), 4)
keys = sorted(_locals.keys())
self.assertEqual(keys, ['i', 'key', 'local', 'val'])
def test_class_base_props(self):
"""test base properties and method of a astng class"""
module = self.module
klass = module['YO']
self.assertEqual(klass.name, 'YO')
self.assertEqual(klass.doc, 'hehe')
self.assertEqual(klass.fromlineno, 25)
self.assertTrue(klass.parent)
self.assertEqual(klass.frame(), klass)
self.assertEqual(klass.parent.frame(), module)
self.assertEqual(klass.root(), module)
self.assertEqual(klass.basenames, [])
self.assertEqual(klass.newstyle, False)
def test_class_locals(self):
"""test the 'locals' dictionary of a astng class"""
module = self.module
klass1 = module['YO']
locals1 = klass1.locals
keys = sorted(locals1.keys())
self.assertEqual(keys, ['__init__', 'a'])
klass2 = module['YOUPI']
locals2 = klass2.locals
keys = locals2.keys()
keys.sort()
self.assertEqual(keys, ['__init__', 'class_attr', 'class_method',
'method', 'static_method'])
def test_class_instance_attrs(self):
module = self.module
klass1 = module['YO']
klass2 = module['YOUPI']
self.assertEqual(klass1.instance_attrs.keys(), ['yo'])
self.assertEqual(klass2.instance_attrs.keys(), ['member'])
def test_class_basenames(self):
module = self.module
klass1 = module['YO']
klass2 = module['YOUPI']
self.assertEqual(klass1.basenames, [])
self.assertEqual(klass2.basenames, ['YO'])
def test_method_base_props(self):
"""test base properties and method of a astng method"""
klass2 = self.module['YOUPI']
# "normal" method
method = klass2['method']
self.assertEqual(method.name, 'method')
self.assertEqual([n.name for n in method.args.args], ['self'])
self.assertEqual(method.doc, 'method test')
self.assertEqual(method.fromlineno, 47)
self.assertEqual(method.type, 'method')
# class method
method = klass2['class_method']
self.assertEqual([n.name for n in method.args.args], ['cls'])
self.assertEqual(method.type, 'classmethod')
# static method
method = klass2['static_method']
self.assertEqual(method.args.args, [])
self.assertEqual(method.type, 'staticmethod')
def test_method_locals(self):
"""test the 'locals' dictionary of a astng method"""
method = self.module['YOUPI']['method']
_locals = method.locals
keys = sorted(_locals)
if sys.version_info < (3, 0):
self.assertEqual(len(_locals), 5)
self.assertEqual(keys, ['a', 'autre', 'b', 'local', 'self'])
else:# ListComp variables are no more accessible outside
self.assertEqual(len(_locals), 3)
self.assertEqual(keys, ['autre', 'local', 'self'])
class ModuleBuildTC(FileBuildTC):
def setUp(self):
abuilder = builder.ASTNGBuilder()
self.module = abuilder.module_build(test_module)
class MoreTC(TestCase):
def setUp(self):
self.builder = builder.ASTNGBuilder()
def test_infered_build(self):
code = '''class A: pass
A.type = "class"
def A_ass_type(self):
print (self)
A.ass_type = A_ass_type
'''
astng = self.builder.string_build(code)
lclass = list(astng.igetattr('A'))
self.assertEqual(len(lclass), 1)
lclass = lclass[0]
self.assertIn('ass_type', lclass.locals)
self.assertIn('type', lclass.locals)
def test_augassign_attr(self):
astng = self.builder.string_build("""class Counter:
v = 0
def inc(self):
self.v += 1
""", __name__, __file__)
# Check self.v += 1 generate AugAssign(AssAttr(...)), not AugAssign(GetAttr(AssName...))
def test_dumb_module(self):
astng = self.builder.string_build("pouet")
def test_infered_dont_pollute(self):
code = '''
def func(a=None):
a.custom_attr = 0
def func2(a={}):
a.custom_attr = 0
'''
astng = self.builder.string_build(code)
nonetype = nodes.const_factory(None)
self.assertNotIn('custom_attr', nonetype.locals)
self.assertNotIn('custom_attr', nonetype.instance_attrs)
nonetype = nodes.const_factory({})
self.assertNotIn('custom_attr', nonetype.locals)
self.assertNotIn('custom_attr', nonetype.instance_attrs)
def test_asstuple(self):
code = 'a, b = range(2)'
astng = self.builder.string_build(code)
self.assertIn('b', astng.locals)
code = '''
def visit_if(self, node):
node.test, body = node.tests[0]
'''
astng = self.builder.string_build(code)
self.assertIn('body', astng['visit_if'].locals)
def test_build_constants(self):
'''test expected values of constants after rebuilding'''
code = '''
def func():
return None
return
return 'None'
'''
astng = self.builder.string_build(code)
none, nothing, chain = [ret.value for ret in astng.body[0].body]
self.assertIsInstance(none, nodes.Const)
self.assertIsNone(none.value)
self.assertIsNone(nothing)
self.assertIsInstance(chain, nodes.Const)
self.assertEqual(chain.value, 'None')
def test_lgc_classproperty(self):
'''test expected values of constants after rebuilding'''
code = '''
from logilab.common.decorators import classproperty
class A(object):
@classproperty
def hop(cls):
return None
'''
astng = self.builder.string_build(code)
self.assertEqual(astng['A']['hop'].type, 'classmethod')
if sys.version_info < (3, 0):
guess_encoding = builder._guess_encoding
class TestGuessEncoding(TestCase):
def testEmacs(self):
e = guess_encoding('# -*- coding: UTF-8 -*-')
self.assertEqual(e, 'UTF-8')
e = guess_encoding('# -*- coding:UTF-8 -*-')
self.assertEqual(e, 'UTF-8')
e = guess_encoding('''
### -*- coding: ISO-8859-1 -*-
''')
self.assertEqual(e, 'ISO-8859-1')
e = guess_encoding('''
### -*- coding: ISO-8859-1 -*-
''')
self.assertIsNone(e)
def testVim(self):
e = guess_encoding('# vim:fileencoding=UTF-8')
self.assertEqual(e, 'UTF-8')
e = guess_encoding('''
### vim:fileencoding=ISO-8859-1
''')
self.assertEqual(e, 'ISO-8859-1')
e = guess_encoding('''
### vim:fileencoding= ISO-8859-1
''')
self.assertIsNone(e)
def test_wrong_coding(self):
# setting "coding" varaible
e = guess_encoding("coding = UTF-8")
self.assertIsNone(e)
# setting a dictionnary entry
e = guess_encoding("coding:UTF-8")
self.assertIsNone(e)
# setting an arguement
e = guess_encoding("def do_something(a_word_with_coding=None):")
self.assertIsNone(e)
def testUTF8(self):
e = guess_encoding('\xef\xbb\xbf any UTF-8 data')
self.assertEqual(e, 'UTF-8')
e = guess_encoding(' any UTF-8 data \xef\xbb\xbf')
self.assertIsNone(e)
if __name__ == '__main__':
unittest_main()
logilab-astng-0.24.3/test/data/ 0000775 0000151 0000155 00000000000 12133517272 015540 5 ustar narval narval logilab-astng-0.24.3/test/data/appl/ 0000775 0000151 0000155 00000000000 12133517272 016474 5 ustar narval narval logilab-astng-0.24.3/test/data/appl/__init__.py 0000644 0000151 0000155 00000000015 12133517265 020601 0 ustar narval narval """
Init
"""
logilab-astng-0.24.3/test/data/appl/myConnection.py 0000644 0000151 0000155 00000000376 12133517265 021521 0 ustar narval narval import SSL1
class MyConnection(SSL1.Connection):
"""An SSL connection."""
def __init__(self, dummy):
print 'MyConnection init'
if __name__ == '__main__':
myConnection = MyConnection(' ')
raw_input('Press Enter to continue...')
logilab-astng-0.24.3/test/data/module.py 0000644 0000151 0000155 00000003456 12133517265 017407 0 ustar narval narval """test module for astng
"""
__revision__ = '$Id: module.py,v 1.2 2005-11-02 11:56:54 syt Exp $'
from logilab.common import modutils
from logilab.common.shellutils import Execute as spawn
from logilab.astng.utils import *
import os.path
MY_DICT = {}
def global_access(key, val):
"""function test"""
local = 1
MY_DICT[key] = val
for i in val:
if i:
del MY_DICT[i]
continue
else:
break
else:
print '!!!'
class YO:
"""hehe"""
a = 1
def __init__(self):
try:
self.yo = 1
except ValueError, ex:
pass
except (NameError, TypeError):
raise XXXError()
except:
raise
class YOUPI(YO):
class_attr = None
def __init__(self):
self.member = None
def method(self):
"""method test"""
global MY_DICT
try:
MY_DICT = {}
local = None
autre = [a for (a, b) in MY_DICT if b]
if b in autre:
print 'yo',
else:
if a in autre:
print 'hehe'
global_access(local, val=autre)
finally:
return local
def static_method():
"""static method test"""
assert MY_DICT, '???'
static_method = staticmethod(static_method)
def class_method(cls):
"""class method test"""
exec a in b
class_method = classmethod(class_method)
def four_args(a, b, c, d):
"""four arguments (was nested_args)"""
print a, b, c, d
while 1:
if a:
break
a += +1
else:
b += -2
if c:
d = ((a) and (b)) or (c)
else:
c = ((a) and (b)) or (d)
map(lambda x, y: (y, x), a)
redirect = four_args
logilab-astng-0.24.3/test/data/nonregr.py 0000644 0000151 0000155 00000002155 12133517265 017567 0 ustar narval narval from __future__ import generators
try:
enumerate = enumerate
except NameError:
def enumerate(iterable):
"""emulates the python2.3 enumerate() function"""
i = 0
for val in iterable:
yield i, val
i += 1
def toto(value):
for k, v in value:
print v.get('yo')
import imp
fp, mpath, desc = imp.find_module('optparse',a)
s_opt = imp.load_module('std_optparse', fp, mpath, desc)
class OptionParser(s_opt.OptionParser):
def parse_args(self, args=None, values=None, real_optparse=False):
if real_optparse:
pass
## return super(OptionParser, self).parse_args()
else:
import optcomp
optcomp.completion(self)
class Aaa(object):
"""docstring"""
def __init__(self):
self.__setattr__('a','b')
pass
def one_public(self):
"""docstring"""
pass
def another_public(self):
"""docstring"""
pass
class Ccc(Aaa):
"""docstring"""
class Ddd(Aaa):
"""docstring"""
pass
class Eee(Ddd):
"""docstring"""
pass
logilab-astng-0.24.3/test/data/notall.py 0000644 0000151 0000155 00000000112 12133517265 017375 0 ustar narval narval
name = 'a'
_bla = 2
other = 'o'
class Aaa: pass
def func(): print 'yo'
logilab-astng-0.24.3/test/data/MyPyPa-0.1.0-py2.5.zip 0000644 0000151 0000155 00000002306 12133517265 020751 0 ustar narval narval PK Ay:5\Bã‚ ¢ mypypa/__init__.pycÛü‰—+êÆIÏd(`b .æ)Q@’‘!Xƒ $Æ$ôý4@ªJ¸D||YjQqf~^|¼H;‚(ÖI¥™9)úI)™Å%z9™y¥º™ffú©ééú¹••‰úññ™y™%ññz•Å@6¹ù)¥9©v ŠAæ PK Ay:ê\sj mypypa/__init__.py‹/K-*ÎÌÏ‹W°UP7Ð3Tç PK Ay:K–Œ“[ Ž EGG-INFO/SOURCES.txt+N-)-Ð+¨äÒÓÏÌË,‰ñ|+*õRÓÓu3óÒòõ¼Ýu=ýÜü1$‚ýCƒœ]ƒõJ*J0äRRRóRRó’+ãs2󲋱**É/ˆÏI-KÍÉ PK Ay:#z| EGG-INFO/top_level.txtË,¨,Hä PK Ay:“×2 EGG-INFO/dependency_links.txtã PK Ay:“×2 EGG-INFO/zip-safeã PK Ay:ÂSA4~ · EGG-INFO/PKG-INFOóM-ILI,IÔ
K-*ÎÌϳR0Ô3àòKÌMµRð¨Hä‚Ëèä‚Kss‹*RòòòK22óÒ¹<òsSuÓšBý¼ýüÃý¸KK2ò‹Ðùº©¹‰™9QŸÌäÔ¼b$m.©ÅÉE™%`a‚9‰%iùE¹ PK Ay:5\Bã‚ ¢ ¤ mypypa/__init__.pycPK Ay:ê\sj ¤³ mypypa/__init__.pyPK Ay:K–Œ“[ Ž ¤ù EGG-INFO/SOURCES.txtPK Ay:#z| ¤† EGG-INFO/top_level.txtPK Ay:“×2 ¤Ã EGG-INFO/dependency_links.txtPK Ay:“×2 ¤ EGG-INFO/zip-safePK Ay:ÂSA4~ · ¤3 EGG-INFO/PKG-INFOPK Ð à logilab-astng-0.24.3/test/data/email.py 0000644 0000151 0000155 00000000106 12133517265 017176 0 ustar narval narval """fake email module to test absolute import doesn't grab this one"""
logilab-astng-0.24.3/test/data/noendingnewline.py 0000644 0000151 0000155 00000000767 12133517265 021307 0 ustar narval narval import unittest
class TestCase(unittest.TestCase):
def setUp(self):
unittest.TestCase.setUp(self)
def tearDown(self):
unittest.TestCase.tearDown(self)
def testIt(self):
self.a = 10
self.xxx()
def xxx(self):
if False:
pass
print 'a'
if False:
pass
pass
if False:
pass
print 'rara'
if __name__ == '__main__':
print 'test2'
unittest.main()
logilab-astng-0.24.3/test/data/format.py 0000644 0000151 0000155 00000000645 12133517265 017407 0 ustar narval narval """A multiline string
"""
function('aeozrijz\
earzer', hop)
# XXX write test
x = [i for i in range(5)
if i % 4]
fonction(1,
2,
3,
4)
def definition(a,
b,
c):
return a + b + c
class debile(dict,
object):
pass
if aaaa: pass
else:
aaaa,bbbb = 1,2
aaaa,bbbb = bbbb,aaaa
# XXX write test
hop = \
aaaa
__revision__.lower();
logilab-astng-0.24.3/test/data/__init__.py 0000644 0000151 0000155 00000000104 12133517265 017644 0 ustar narval narval __revision__="$Id: __init__.py,v 1.1 2005-06-13 20:55:20 syt Exp $"
logilab-astng-0.24.3/test/data/SSL1/ 0000775 0000151 0000155 00000000000 12133517272 016262 5 ustar narval narval logilab-astng-0.24.3/test/data/SSL1/__init__.py 0000644 0000151 0000155 00000000043 12133517265 020370 0 ustar narval narval from Connection1 import Connection
logilab-astng-0.24.3/test/data/SSL1/Connection1.py 0000644 0000151 0000155 00000000461 12133517265 021015 0 ustar narval narval """M2Crypto.SSL.Connection
Copyright (c) 1999-2004 Ng Pheng Siong. All rights reserved."""
RCS_id='$Id: Connection1.py,v 1.1 2005-06-13 20:55:22 syt Exp $'
#Some code deleted here
class Connection:
"""An SSL connection."""
def __init__(self, ctx, sock=None):
print 'init Connection'
logilab-astng-0.24.3/test/data/MyPyPa-0.1.0-py2.5.egg 0000644 0000151 0000155 00000002306 12133517265 020711 0 ustar narval narval PK Ay:5\Bã‚ ¢ mypypa/__init__.pycÛü‰—+êÆIÏd(`b .æ)Q@’‘!Xƒ $Æ$ôý4@ªJ¸D||YjQqf~^|¼H;‚(ÖI¥™9)úI)™Å%z9™y¥º™ffú©ééú¹••‰úññ™y™%ññz•Å@6¹ù)¥9©v ŠAæ PK Ay:ê\sj mypypa/__init__.py‹/K-*ÎÌÏ‹W°UP7Ð3Tç PK Ay:K–Œ“[ Ž EGG-INFO/SOURCES.txt+N-)-Ð+¨äÒÓÏÌË,‰ñ|+*õRÓÓu3óÒòõ¼Ýu=ýÜü1$‚ýCƒœ]ƒõJ*J0äRRRóRRó’+ãs2󲋱**É/ˆÏI-KÍÉ PK Ay:#z| EGG-INFO/top_level.txtË,¨,Hä PK Ay:“×2 EGG-INFO/dependency_links.txtã PK Ay:“×2 EGG-INFO/zip-safeã PK Ay:ÂSA4~ · EGG-INFO/PKG-INFOóM-ILI,IÔ
K-*ÎÌϳR0Ô3àòKÌMµRð¨Hä‚Ëèä‚Kss‹*RòòòK22óÒ¹<òsSuÓšBý¼ýüÃý¸KK2ò‹Ðùº©¹‰™9QŸÌäÔ¼b$m.©ÅÉE™%`a‚9‰%iùE¹ PK Ay:5\Bã‚ ¢ ¤ mypypa/__init__.pycPK Ay:ê\sj ¤³ mypypa/__init__.pyPK Ay:K–Œ“[ Ž ¤ù EGG-INFO/SOURCES.txtPK Ay:#z| ¤† EGG-INFO/top_level.txtPK Ay:“×2 ¤Ã EGG-INFO/dependency_links.txtPK Ay:“×2 ¤ EGG-INFO/zip-safePK Ay:ÂSA4~ · ¤3 EGG-INFO/PKG-INFOPK Ð à logilab-astng-0.24.3/test/data/absimport.py 0000644 0000151 0000155 00000000116 12133517265 020110 0 ustar narval narval from __future__ import absolute_import
import email
from email import message
logilab-astng-0.24.3/test/data/all.py 0000644 0000151 0000155 00000000152 12133517265 016660 0 ustar narval narval
name = 'a'
_bla = 2
other = 'o'
class Aaa: pass
def func(): print 'yo'
__all__ = 'Aaa', '_bla', 'name'
logilab-astng-0.24.3/test/data/module2.py 0000644 0000151 0000155 00000003444 12133517265 017466 0 ustar narval narval from data.module import YO, YOUPI
import data
class Specialization(YOUPI, YO):
pass
class Metaclass(type):
pass
class Interface:
pass
class MyIFace(Interface):
pass
class AnotherIFace(Interface):
pass
class MyException(Exception):
pass
class MyError(MyException):
pass
class AbstractClass(object):
def to_override(self, whatever):
raise NotImplementedError()
def return_something(self, param):
if param:
return 'toto'
return
class Concrete0:
__implements__ = MyIFace
class Concrete1:
__implements__ = (MyIFace, AnotherIFace)
class Concrete2:
__implements__ = (MyIFace, AnotherIFace)
class Concrete23(Concrete1):
pass
del YO.member
del YO
[SYN1, SYN2] = (Concrete0, Concrete1)
assert `1`
b = (1) | (((2) & (3)) ^ (8))
bb = ((1) | (two)) | (6)
ccc = ((one) & (two)) & (three)
dddd = ((x) ^ (o)) ^ (r)
exec 'c = 3'
exec 'c = 3' in {}, {}
def raise_string(a=2, *args, **kwargs):
raise Exception, 'yo'
yield 'coucou'
yield
a = (b) + (2)
c = (b) * (2)
c = (b) / (2)
c = (b) // (2)
c = (b) - (2)
c = (b) % (2)
c = (b) ** (2)
c = (b) << (2)
c = (b) >> (2)
c = ~b
c = not b
d = [c]
e = d[:]
e = d[a:b:c]
raise_string(*args, **kwargs)
print >> stream, 'bonjour'
print >> stream, 'salut',
def make_class(any, base=data.module.YO, *args, **kwargs):
"""check base is correctly resolved to Concrete0"""
class Aaaa(base):
"""dynamic class"""
return Aaaa
from os.path import abspath
import os as myos
class A:
pass
class A(A):
pass
def generator():
"""A generator."""
yield
def not_a_generator():
"""A function that contains generator, but is not one."""
def generator():
yield
genl = lambda : (yield)
logilab-astng-0.24.3/test/fulltest.sh 0000755 0000151 0000155 00000001013 12133517265 017023 0 ustar narval narval #!/bin/sh
if [ $@ ] ; then
PYVERSIONS=$@
else
PYVERSIONS="2.3 2.4 2.5 2.6"
fi
PYTEST=`which pytest`
for ver in $PYVERSIONS; do
echo "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
echo `python$ver -V`
echo "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"
python$ver $PYTEST
echo "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"
echo `python$ver -V` -OO
python$ver -OO $PYTEST
done
logilab-astng-0.24.3/test/unittest_utils.py 0000644 0000151 0000155 00000006427 12133517265 020311 0 ustar narval narval # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of logilab-astng.
#
# logilab-astng is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 2.1 of the License, or (at your
# option) any later version.
#
# logilab-astng is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see .
from logilab.common.testlib import TestCase, unittest_main
from logilab.astng import builder, nodes
from logilab.astng.node_classes import are_exclusive
builder = builder.ASTNGBuilder()
class AreExclusiveTC(TestCase):
def test_not_exclusive(self):
astng = builder.string_build("""
x = 10
for x in range(5):
print (x)
if x > 0:
print ('#' * x)
""", __name__, __file__)
xass1 = astng.locals['x'][0]
assert xass1.lineno == 2
xnames = [n for n in astng.nodes_of_class(nodes.Name) if n.name == 'x']
assert len(xnames) == 3
assert xnames[1].lineno == 6
self.assertEqual(are_exclusive(xass1, xnames[1]), False)
self.assertEqual(are_exclusive(xass1, xnames[2]), False)
def test_if(self):
astng = builder.string_build('''
if 1:
a = 1
a = 2
elif 2:
a = 12
a = 13
else:
a = 3
a = 4
''')
a1 = astng.locals['a'][0]
a2 = astng.locals['a'][1]
a3 = astng.locals['a'][2]
a4 = astng.locals['a'][3]
a5 = astng.locals['a'][4]
a6 = astng.locals['a'][5]
self.assertEqual(are_exclusive(a1, a2), False)
self.assertEqual(are_exclusive(a1, a3), True)
self.assertEqual(are_exclusive(a1, a5), True)
self.assertEqual(are_exclusive(a3, a5), True)
self.assertEqual(are_exclusive(a3, a4), False)
self.assertEqual(are_exclusive(a5, a6), False)
def test_try_except(self):
astng = builder.string_build('''
try:
def exclusive_func2():
"docstring"
except TypeError:
def exclusive_func2():
"docstring"
except:
def exclusive_func2():
"docstring"
else:
def exclusive_func2():
"this one redefine the one defined line 42"
''')
f1 = astng.locals['exclusive_func2'][0]
f2 = astng.locals['exclusive_func2'][1]
f3 = astng.locals['exclusive_func2'][2]
f4 = astng.locals['exclusive_func2'][3]
self.assertEqual(are_exclusive(f1, f2), True)
self.assertEqual(are_exclusive(f1, f3), True)
self.assertEqual(are_exclusive(f1, f4), False)
self.assertEqual(are_exclusive(f2, f4), True)
self.assertEqual(are_exclusive(f3, f4), True)
self.assertEqual(are_exclusive(f3, f2), True)
self.assertEqual(are_exclusive(f2, f1), True)
self.assertEqual(are_exclusive(f4, f1), False)
self.assertEqual(are_exclusive(f4, f2), True)
if __name__ == '__main__':
unittest_main()
logilab-astng-0.24.3/test/regrtest_data/ 0000775 0000151 0000155 00000000000 12133517272 017457 5 ustar narval narval logilab-astng-0.24.3/test/regrtest_data/descriptor_crash.py 0000644 0000151 0000155 00000000331 12133517265 023364 0 ustar narval narval
import urllib
class Page(object):
_urlOpen = staticmethod(urllib.urlopen)
def getPage(self, url):
handle = self._urlOpen(url)
data = handle.read()
handle.close()
return data
logilab-astng-0.24.3/test/regrtest_data/absimp/ 0000775 0000151 0000155 00000000000 12133517272 020732 5 ustar narval narval logilab-astng-0.24.3/test/regrtest_data/absimp/sidepackage/ 0000775 0000151 0000155 00000000000 12133517272 023172 5 ustar narval narval logilab-astng-0.24.3/test/regrtest_data/absimp/sidepackage/__init__.py 0000644 0000151 0000155 00000000052 12133517265 025300 0 ustar narval narval """a side package with nothing in it
"""
logilab-astng-0.24.3/test/regrtest_data/absimp/string.py 0000644 0000151 0000155 00000000102 12133517265 022603 0 ustar narval narval from __future__ import absolute_import
import string
print string
logilab-astng-0.24.3/test/regrtest_data/absimp/__init__.py 0000644 0000151 0000155 00000000131 12133517265 023036 0 ustar narval narval """a package with absolute import activated
"""
from __future__ import absolute_import
logilab-astng-0.24.3/test/regrtest_data/package/ 0000775 0000151 0000155 00000000000 12133517272 021052 5 ustar narval narval logilab-astng-0.24.3/test/regrtest_data/package/hello.py 0000644 0000151 0000155 00000000024 12133517265 022523 0 ustar narval narval """hello module"""
logilab-astng-0.24.3/test/regrtest_data/package/subpackage/ 0000775 0000151 0000155 00000000000 12133517272 023157 5 ustar narval narval logilab-astng-0.24.3/test/regrtest_data/package/subpackage/module.py 0000644 0000151 0000155 00000000040 12133517265 025010 0 ustar narval narval """package.subpackage.module"""
logilab-astng-0.24.3/test/regrtest_data/package/subpackage/__init__.py 0000644 0000151 0000155 00000000031 12133517265 025262 0 ustar narval narval """package.subpackage"""
logilab-astng-0.24.3/test/regrtest_data/package/__init__.py 0000644 0000151 0000155 00000000062 12133517265 023161 0 ustar narval narval """package's __init__ file"""
import subpackage
logilab-astng-0.24.3/test/regrtest_data/package/import_package_subpackage_module.py 0000644 0000151 0000155 00000004246 12133517265 030151 0 ustar narval narval # pylint: disable-msg=I0011,C0301,W0611
"""I found some of my scripts trigger off an AttributeError in pylint
0.8.1 (with common 0.12.0 and astng 0.13.1).
Traceback (most recent call last):
File "/usr/bin/pylint", line 4, in ?
lint.Run(sys.argv[1:])
File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 729, in __init__
linter.check(args)
File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 412, in check
self.check_file(filepath, modname, checkers)
File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 426, in check_file
astng = self._check_file(filepath, modname, checkers)
File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 450, in _check_file
self.check_astng_module(astng, checkers)
File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 494, in check_astng_module
self.astng_events(astng, [checker for checker in checkers
File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 511, in astng_events
self.astng_events(child, checkers, _reversed_checkers)
File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 511, in astng_events
self.astng_events(child, checkers, _reversed_checkers)
File "/usr/lib/python2.4/site-packages/pylint/lint.py", line 508, in astng_events
checker.visit(astng)
File "/usr/lib/python2.4/site-packages/logilab/astng/utils.py", line 84, in visit
method(node)
File "/usr/lib/python2.4/site-packages/pylint/checkers/variables.py", line 295, in visit_import
self._check_module_attrs(node, module, name_parts[1:])
File "/usr/lib/python2.4/site-packages/pylint/checkers/variables.py", line 357, in _check_module_attrs
self.add_message('E0611', args=(name, module.name),
AttributeError: Import instance has no attribute 'name'
You can reproduce it by:
(1) create package structure like the following:
package/
__init__.py
subpackage/
__init__.py
module.py
(2) in package/__init__.py write:
import subpackage
(3) run pylint with a script importing package.subpackage.module.
"""
__revision__ = '$Id: import_package_subpackage_module.py,v 1.1 2005-11-10 15:59:32 syt Exp $'
import package.subpackage.module
logilab-astng-0.24.3/test/regrtest_data/package/absimport.py 0000644 0000151 0000155 00000000233 12133517265 023422 0 ustar narval narval from __future__ import absolute_import
import import_package_subpackage_module # fail
print import_package_subpackage_module
from . import hello as hola
logilab-astng-0.24.3/test/unittest_scoped_nodes.py 0000644 0000151 0000155 00000060655 12133517265 021621 0 ustar narval narval # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of logilab-astng.
#
# logilab-astng is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 2.1 of the License, or (at your
# option) any later version.
#
# logilab-astng is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see .
"""tests for specific behaviour of astng scoped nodes (i.e. module, class and
function)
"""
import sys
from os.path import join, abspath, dirname
from logilab.common.testlib import TestCase, unittest_main
from logilab.astng import builder, nodes, scoped_nodes, \
InferenceError, NotFoundError, NoDefault
from logilab.astng.bases import BUILTINS, Instance, BoundMethod, UnboundMethod
abuilder = builder.ASTNGBuilder()
DATA = join(dirname(abspath(__file__)), 'data')
REGRTEST_DATA = join(dirname(abspath(__file__)), 'regrtest_data')
MODULE = abuilder.file_build(join(DATA, 'module.py'), 'data.module')
MODULE2 = abuilder.file_build(join(DATA, 'module2.py'), 'data.module2')
NONREGR = abuilder.file_build(join(DATA, 'nonregr.py'), 'data.nonregr')
PACK = abuilder.file_build(join(DATA, '__init__.py'), 'data')
def _test_dict_interface(self, node, test_attr):
self.assertIs(node[test_attr], node[test_attr])
self.assertIn(test_attr, node)
node.keys()
node.values()
node.items()
iter(node)
class ModuleNodeTC(TestCase):
def test_special_attributes(self):
self.assertEqual(len(MODULE.getattr('__name__')), 1)
self.assertIsInstance(MODULE.getattr('__name__')[0], nodes.Const)
self.assertEqual(MODULE.getattr('__name__')[0].value, 'data.module')
self.assertEqual(len(MODULE.getattr('__doc__')), 1)
self.assertIsInstance(MODULE.getattr('__doc__')[0], nodes.Const)
self.assertEqual(MODULE.getattr('__doc__')[0].value, 'test module for astng\n')
self.assertEqual(len(MODULE.getattr('__file__')), 1)
self.assertIsInstance(MODULE.getattr('__file__')[0], nodes.Const)
self.assertEqual(MODULE.getattr('__file__')[0].value, join(DATA, 'module.py'))
self.assertEqual(len(MODULE.getattr('__dict__')), 1)
self.assertIsInstance(MODULE.getattr('__dict__')[0], nodes.Dict)
self.assertRaises(NotFoundError, MODULE.getattr, '__path__')
self.assertEqual(len(PACK.getattr('__path__')), 1)
self.assertIsInstance(PACK.getattr('__path__')[0], nodes.List)
def test_dict_interface(self):
_test_dict_interface(self, MODULE, 'YO')
def test_getattr(self):
yo = MODULE.getattr('YO')[0]
self.assertIsInstance(yo, nodes.Class)
self.assertEqual(yo.name, 'YO')
red = MODULE.igetattr('redirect').next()
self.assertIsInstance(red, nodes.Function)
self.assertEqual(red.name, 'four_args')
spawn = MODULE.igetattr('spawn').next()
self.assertIsInstance(spawn, nodes.Class)
self.assertEqual(spawn.name, 'Execute')
# resolve packageredirection
sys.path.insert(1, DATA)
mod = abuilder.file_build(join(DATA, 'appl/myConnection.py'),
'appl.myConnection')
try:
ssl = mod.igetattr('SSL1').next()
cnx = ssl.igetattr('Connection').next()
self.assertEqual(cnx.__class__, nodes.Class)
self.assertEqual(cnx.name, 'Connection')
self.assertEqual(cnx.root().name, 'SSL1.Connection1')
finally:
del sys.path[1]
self.assertEqual(len(NONREGR.getattr('enumerate')), 2)
# raise ResolveError
self.assertRaises(InferenceError, MODULE.igetattr, 'YOAA')
def test_wildard_import_names(self):
m = abuilder.file_build(join(DATA, 'all.py'), 'all')
self.assertEqual(m.wildcard_import_names(), ['Aaa', '_bla', 'name'])
m = abuilder.file_build(join(DATA, 'notall.py'), 'notall')
res = sorted(m.wildcard_import_names())
self.assertEqual(res, ['Aaa', 'func', 'name', 'other'])
def test_module_getattr(self):
data = '''
appli = application
appli += 2
del appli
'''
astng = abuilder.string_build(data, __name__, __file__)
# test del statement not returned by getattr
self.assertEqual(len(astng.getattr('appli')), 2,
astng.getattr('appli'))
def test_relative_to_absolute_name(self):
# package
mod = nodes.Module('very.multi.package', 'doc')
mod.package = True
modname = mod.relative_to_absolute_name('utils', 1)
self.assertEqual(modname, 'very.multi.package.utils')
modname = mod.relative_to_absolute_name('utils', 2)
self.assertEqual(modname, 'very.multi.utils')
modname = mod.relative_to_absolute_name('utils', 0)
self.assertEqual(modname, 'very.multi.package.utils')
modname = mod.relative_to_absolute_name('', 1)
self.assertEqual(modname, 'very.multi.package')
# non package
mod = nodes.Module('very.multi.module', 'doc')
mod.package = False
modname = mod.relative_to_absolute_name('utils', 0)
self.assertEqual(modname, 'very.multi.utils')
modname = mod.relative_to_absolute_name('utils', 1)
self.assertEqual(modname, 'very.multi.utils')
modname = mod.relative_to_absolute_name('utils', 2)
self.assertEqual(modname, 'very.utils')
modname = mod.relative_to_absolute_name('', 1)
self.assertEqual(modname, 'very.multi')
def test_import_1(self):
data = '''from . import subpackage'''
astng = abuilder.string_build(data, 'package', join(REGRTEST_DATA, 'package', '__init__.py'))
sys.path.insert(1, REGRTEST_DATA)
try:
m = astng.import_module('', level=1)
self.assertEqual(m.name, 'package')
infered = list(astng.igetattr('subpackage'))
self.assertEqual(len(infered), 1)
self.assertEqual(infered[0].name, 'package.subpackage')
finally:
del sys.path[1]
def test_import_2(self):
data = '''from . import subpackage as pouet'''
astng = abuilder.string_build(data, 'package', join(dirname(abspath(__file__)), 'regrtest_data', 'package', '__init__.py'))
sys.path.insert(1, REGRTEST_DATA)
try:
m = astng.import_module('', level=1)
self.assertEqual(m.name, 'package')
infered = list(astng.igetattr('pouet'))
self.assertEqual(len(infered), 1)
self.assertEqual(infered[0].name, 'package.subpackage')
finally:
del sys.path[1]
class FunctionNodeTC(TestCase):
def test_special_attributes(self):
func = MODULE2['make_class']
self.assertEqual(len(func.getattr('__name__')), 1)
self.assertIsInstance(func.getattr('__name__')[0], nodes.Const)
self.assertEqual(func.getattr('__name__')[0].value, 'make_class')
self.assertEqual(len(func.getattr('__doc__')), 1)
self.assertIsInstance(func.getattr('__doc__')[0], nodes.Const)
self.assertEqual(func.getattr('__doc__')[0].value, 'check base is correctly resolved to Concrete0')
self.assertEqual(len(MODULE.getattr('__dict__')), 1)
self.assertIsInstance(MODULE.getattr('__dict__')[0], nodes.Dict)
def test_dict_interface(self):
_test_dict_interface(self, MODULE['global_access'], 'local')
def test_default_value(self):
func = MODULE2['make_class']
self.assertIsInstance(func.args.default_value('base'), nodes.Getattr)
self.assertRaises(NoDefault, func.args.default_value, 'args')
self.assertRaises(NoDefault, func.args.default_value, 'kwargs')
self.assertRaises(NoDefault, func.args.default_value, 'any')
#self.assertIsInstance(func.mularg_class('args'), nodes.Tuple)
#self.assertIsInstance(func.mularg_class('kwargs'), nodes.Dict)
#self.assertIsNone(func.mularg_class('base'))
def test_navigation(self):
function = MODULE['global_access']
self.assertEqual(function.statement(), function)
l_sibling = function.previous_sibling()
# check taking parent if child is not a stmt
self.assertIsInstance(l_sibling, nodes.Assign)
child = function.args.args[0]
self.assertIs(l_sibling, child.previous_sibling())
r_sibling = function.next_sibling()
self.assertIsInstance(r_sibling, nodes.Class)
self.assertEqual(r_sibling.name, 'YO')
self.assertIs(r_sibling, child.next_sibling())
last = r_sibling.next_sibling().next_sibling().next_sibling()
self.assertIsInstance(last, nodes.Assign)
self.assertIsNone(last.next_sibling())
first = l_sibling.previous_sibling().previous_sibling().previous_sibling().previous_sibling().previous_sibling()
self.assertIsNone(first.previous_sibling())
def test_nested_args(self):
if sys.version_info >= (3, 0):
self.skipTest("nested args has been removed in py3.x")
code = '''
def nested_args(a, (b, c, d)):
"nested arguments test"
'''
tree = abuilder.string_build(code)
func = tree['nested_args']
self.assertEqual(sorted(func.locals), ['a', 'b', 'c', 'd'])
self.assertEqual(func.args.format_args(), 'a, (b, c, d)')
def test_four_args(self):
func = MODULE['four_args']
#self.assertEqual(func.args.args, ['a', ('b', 'c', 'd')])
local = sorted(func.keys())
self.assertEqual(local, ['a', 'b', 'c', 'd'])
self.assertEqual(func.type, 'function')
def test_format_args(self):
func = MODULE2['make_class']
self.assertEqual(func.args.format_args(), 'any, base=data.module.YO, *args, **kwargs')
func = MODULE['four_args']
self.assertEqual(func.args.format_args(), 'a, b, c, d')
def test_is_generator(self):
self.assertTrue(MODULE2['generator'].is_generator())
self.assertFalse(MODULE2['not_a_generator'].is_generator())
self.assertFalse(MODULE2['make_class'].is_generator())
def test_is_abstract(self):
method = MODULE2['AbstractClass']['to_override']
self.assertTrue(method.is_abstract(pass_is_abstract=False))
self.assertEqual(method.qname(), 'data.module2.AbstractClass.to_override')
self.assertEqual(method.pytype(), '%s.instancemethod' % BUILTINS)
method = MODULE2['AbstractClass']['return_something']
self.assertFalse(method.is_abstract(pass_is_abstract=False))
# non regression : test raise "string" doesn't cause an exception in is_abstract
func = MODULE2['raise_string']
self.assertFalse(func.is_abstract(pass_is_abstract=False))
## def test_raises(self):
## method = MODULE2['AbstractClass']['to_override']
## self.assertEqual([str(term) for term in method.raises()],
## ["CallFunc(Name('NotImplementedError'), [], None, None)"] )
## def test_returns(self):
## method = MODULE2['AbstractClass']['return_something']
## # use string comp since Node doesn't handle __cmp__
## self.assertEqual([str(term) for term in method.returns()],
## ["Const('toto')", "Const(None)"])
def test_lambda_pytype(self):
data = '''
def f():
g = lambda: None
'''
astng = abuilder.string_build(data, __name__, __file__)
g = list(astng['f'].ilookup('g'))[0]
self.assertEqual(g.pytype(), '%s.function' % BUILTINS)
def test_lambda_qname(self):
astng = abuilder.string_build('''
lmbd = lambda: None
''', __name__, __file__)
self.assertEqual('%s.' % __name__, astng['lmbd'].parent.value.qname())
def test_is_method(self):
data = '''
class A:
def meth1(self):
return 1
@classmethod
def meth2(cls):
return 2
@staticmethod
def meth3():
return 3
def function():
return 0
@staticmethod
def sfunction():
return -1
'''
astng = abuilder.string_build(data, __name__, __file__)
self.assertTrue(astng['A']['meth1'].is_method())
self.assertTrue(astng['A']['meth2'].is_method())
self.assertTrue(astng['A']['meth3'].is_method())
self.assertFalse(astng['function'].is_method())
self.assertFalse(astng['sfunction'].is_method())
def test_argnames(self):
if sys.version_info < (3, 0):
code = 'def f(a, (b, c), *args, **kwargs): pass'
else:
code = 'def f(a, b, c, *args, **kwargs): pass'
astng = abuilder.string_build(code, __name__, __file__)
self.assertEqual(astng['f'].argnames(), ['a', 'b', 'c', 'args', 'kwargs'])
def test_return_nothing(self):
"""test infered value on a function with empty return"""
data = '''
def func():
return
a = func()
'''
astng = abuilder.string_build(data, __name__, __file__)
call = astng.body[1].value
func_vals = call.infered()
self.assertEqual(len(func_vals), 1)
self.assertIsInstance(func_vals[0], nodes.Const)
self.assertIsNone(func_vals[0].value)
def test_func_instance_attr(self):
"""test instance attributes for functions"""
data= """
def test():
print(test.bar)
test.bar = 1
test()
"""
astng = abuilder.string_build(data, 'mod', __file__)
func = astng.body[2].value.func.infered()[0]
self.assertIsInstance(func, nodes.Function)
self.assertEqual(func.name, 'test')
one = func.getattr('bar')[0].infered()[0]
self.assertIsInstance(one, nodes.Const)
self.assertEqual(one.value, 1)
class ClassNodeTC(TestCase):
def test_dict_interface(self):
_test_dict_interface(self, MODULE['YOUPI'], 'method')
def test_cls_special_attributes_1(self):
cls = MODULE['YO']
self.assertEqual(len(cls.getattr('__bases__')), 1)
self.assertEqual(len(cls.getattr('__name__')), 1)
self.assertIsInstance(cls.getattr('__name__')[0], nodes.Const)
self.assertEqual(cls.getattr('__name__')[0].value, 'YO')
self.assertEqual(len(cls.getattr('__doc__')), 1)
self.assertIsInstance(cls.getattr('__doc__')[0], nodes.Const)
self.assertEqual(cls.getattr('__doc__')[0].value, 'hehe')
self.assertEqual(len(cls.getattr('__module__')), 1)
self.assertIsInstance(cls.getattr('__module__')[0], nodes.Const)
self.assertEqual(cls.getattr('__module__')[0].value, 'data.module')
self.assertEqual(len(cls.getattr('__dict__')), 1)
self.assertRaises(NotFoundError, cls.getattr, '__mro__')
for cls in (nodes.List._proxied, nodes.Const(1)._proxied):
self.assertEqual(len(cls.getattr('__bases__')), 1)
self.assertEqual(len(cls.getattr('__name__')), 1)
self.assertEqual(len(cls.getattr('__doc__')), 1, (cls, cls.getattr('__doc__')))
self.assertEqual(cls.getattr('__doc__')[0].value, cls.doc)
self.assertEqual(len(cls.getattr('__module__')), 1)
self.assertEqual(len(cls.getattr('__dict__')), 1)
self.assertEqual(len(cls.getattr('__mro__')), 1)
def test_cls_special_attributes_2(self):
astng = abuilder.string_build('''
class A: pass
class B: pass
A.__bases__ += (B,)
''', __name__, __file__)
self.assertEqual(len(astng['A'].getattr('__bases__')), 2)
self.assertIsInstance(astng['A'].getattr('__bases__')[0], nodes.Tuple)
self.assertIsInstance(astng['A'].getattr('__bases__')[1], nodes.AssAttr)
def test_instance_special_attributes(self):
for inst in (Instance(MODULE['YO']), nodes.List(), nodes.Const(1)):
self.assertRaises(NotFoundError, inst.getattr, '__mro__')
self.assertRaises(NotFoundError, inst.getattr, '__bases__')
self.assertRaises(NotFoundError, inst.getattr, '__name__')
self.assertEqual(len(inst.getattr('__dict__')), 1)
self.assertEqual(len(inst.getattr('__doc__')), 1)
def test_navigation(self):
klass = MODULE['YO']
self.assertEqual(klass.statement(), klass)
l_sibling = klass.previous_sibling()
self.assertTrue(isinstance(l_sibling, nodes.Function), l_sibling)
self.assertEqual(l_sibling.name, 'global_access')
r_sibling = klass.next_sibling()
self.assertIsInstance(r_sibling, nodes.Class)
self.assertEqual(r_sibling.name, 'YOUPI')
def test_local_attr_ancestors(self):
klass2 = MODULE['YOUPI']
it = klass2.local_attr_ancestors('__init__')
anc_klass = it.next()
self.assertIsInstance(anc_klass, nodes.Class)
self.assertEqual(anc_klass.name, 'YO')
self.assertRaises(StopIteration, it.next)
it = klass2.local_attr_ancestors('method')
self.assertRaises(StopIteration, it.next)
def test_instance_attr_ancestors(self):
klass2 = MODULE['YOUPI']
it = klass2.instance_attr_ancestors('yo')
anc_klass = it.next()
self.assertIsInstance(anc_klass, nodes.Class)
self.assertEqual(anc_klass.name, 'YO')
self.assertRaises(StopIteration, it.next)
klass2 = MODULE['YOUPI']
it = klass2.instance_attr_ancestors('member')
self.assertRaises(StopIteration, it.next)
def test_methods(self):
klass2 = MODULE['YOUPI']
methods = sorted([m.name for m in klass2.methods()])
self.assertEqual(methods, ['__init__', 'class_method',
'method', 'static_method'])
methods = [m.name for m in klass2.mymethods()]
methods.sort()
self.assertEqual(methods, ['__init__', 'class_method',
'method', 'static_method'])
klass2 = MODULE2['Specialization']
methods = [m.name for m in klass2.mymethods()]
methods.sort()
self.assertEqual(methods, [])
method_locals = klass2.local_attr('method')
self.assertEqual(len(method_locals), 1)
self.assertEqual(method_locals[0].name, 'method')
self.assertRaises(NotFoundError, klass2.local_attr, 'nonexistant')
methods = [m.name for m in klass2.methods()]
methods.sort()
self.assertEqual(methods, ['__init__', 'class_method',
'method', 'static_method'])
#def test_rhs(self):
# my_dict = MODULE['MY_DICT']
# self.assertIsInstance(my_dict.rhs(), nodes.Dict)
# a = MODULE['YO']['a']
# value = a.rhs()
# self.assertIsInstance(value, nodes.Const)
# self.assertEqual(value.value, 1)
def test_ancestors(self):
klass = MODULE['YOUPI']
ancs = [a.name for a in klass.ancestors()]
self.assertEqual(ancs, ['YO'])
klass = MODULE2['Specialization']
ancs = [a.name for a in klass.ancestors()]
self.assertEqual(ancs, ['YOUPI', 'YO'])
def test_type(self):
klass = MODULE['YOUPI']
self.assertEqual(klass.type, 'class')
klass = MODULE2['Metaclass']
self.assertEqual(klass.type, 'metaclass')
klass = MODULE2['MyException']
self.assertEqual(klass.type, 'exception')
klass = MODULE2['MyIFace']
self.assertEqual(klass.type, 'interface')
klass = MODULE2['MyError']
self.assertEqual(klass.type, 'exception')
def test_interfaces(self):
for klass, interfaces in (('Concrete0', ['MyIFace']),
('Concrete1', ['MyIFace', 'AnotherIFace']),
('Concrete2', ['MyIFace', 'AnotherIFace']),
('Concrete23', ['MyIFace', 'AnotherIFace'])):
klass = MODULE2[klass]
self.assertEqual([i.name for i in klass.interfaces()],
interfaces)
def test_concat_interfaces(self):
astng = abuilder.string_build('''
class IMachin: pass
class Correct2:
"""docstring"""
__implements__ = (IMachin,)
class BadArgument:
"""docstring"""
__implements__ = (IMachin,)
class InterfaceCanNowBeFound:
"""docstring"""
__implements__ = BadArgument.__implements__ + Correct2.__implements__
''')
self.assertEqual([i.name for i in astng['InterfaceCanNowBeFound'].interfaces()],
['IMachin'])
def test_inner_classes(self):
eee = NONREGR['Ccc']['Eee']
self.assertEqual([n.name for n in eee.ancestors()], ['Ddd', 'Aaa', 'object'])
def test_classmethod_attributes(self):
data = '''
class WebAppObject(object):
def registered(cls, application):
cls.appli = application
cls.schema = application.schema
cls.config = application.config
return cls
registered = classmethod(registered)
'''
astng = abuilder.string_build(data, __name__, __file__)
cls = astng['WebAppObject']
self.assertEqual(sorted(cls.locals.keys()),
['appli', 'config', 'registered', 'schema'])
def test_class_getattr(self):
data = '''
class WebAppObject(object):
appli = application
appli += 2
del self.appli
'''
astng = abuilder.string_build(data, __name__, __file__)
cls = astng['WebAppObject']
# test del statement not returned by getattr
self.assertEqual(len(cls.getattr('appli')), 2)
def test_instance_getattr(self):
data = '''
class WebAppObject(object):
def __init__(self, application):
self.appli = application
self.appli += 2
del self.appli
'''
astng = abuilder.string_build(data, __name__, __file__)
inst = Instance(astng['WebAppObject'])
# test del statement not returned by getattr
self.assertEqual(len(inst.getattr('appli')), 2)
def test_instance_getattr_with_class_attr(self):
data = '''
class Parent:
aa = 1
cc = 1
class Klass(Parent):
aa = 0
bb = 0
def incr(self, val):
self.cc = self.aa
if val > self.aa:
val = self.aa
if val < self.bb:
val = self.bb
self.aa += val
'''
astng = abuilder.string_build(data, __name__, __file__)
inst = Instance(astng['Klass'])
self.assertEqual(len(inst.getattr('aa')), 3, inst.getattr('aa'))
self.assertEqual(len(inst.getattr('bb')), 1, inst.getattr('bb'))
self.assertEqual(len(inst.getattr('cc')), 2, inst.getattr('cc'))
def test_getattr_method_transform(self):
data = '''
class Clazz(object):
def m1(self, value):
self.value = value
m2 = m1
def func(arg1, arg2):
"function that will be used as a method"
return arg1.value + arg2
Clazz.m3 = func
inst = Clazz()
inst.m4 = func
'''
astng = abuilder.string_build(data, __name__, __file__)
cls = astng['Clazz']
# test del statement not returned by getattr
for method in ('m1', 'm2', 'm3'):
inferred = list(cls.igetattr(method))
self.assertEqual(len(inferred), 1)
self.assertIsInstance(inferred[0], UnboundMethod)
inferred = list(Instance(cls).igetattr(method))
self.assertEqual(len(inferred), 1)
self.assertIsInstance(inferred[0], BoundMethod)
inferred = list(Instance(cls).igetattr('m4'))
self.assertEqual(len(inferred), 1)
self.assertIsInstance(inferred[0], nodes.Function)
def test_getattr_from_grandpa(self):
data = '''
class Future:
attr = 1
class Present(Future):
pass
class Past(Present):
pass
'''
astng = abuilder.string_build(data)
past = astng['Past']
attr = past.getattr('attr')
self.assertEqual(len(attr), 1)
attr1 = attr[0]
self.assertIsInstance(attr1, nodes.AssName)
self.assertEqual(attr1.name, 'attr')
def test_function_with_decorator_lineno(self):
data = '''
@f(a=2,
b=3)
def g1(x):
print(x)
@f(a=2,
b=3)
def g2():
pass
'''
astng = abuilder.string_build(data)
self.assertEqual(astng['g1'].fromlineno, 4)
self.assertEqual(astng['g1'].tolineno, 5)
self.assertEqual(astng['g2'].fromlineno, 9)
self.assertEqual(astng['g2'].tolineno, 10)
__all__ = ('ModuleNodeTC', 'ImportNodeTC', 'FunctionNodeTC', 'ClassNodeTC')
if __name__ == '__main__':
unittest_main()
logilab-astng-0.24.3/test/unittest_lookup.py 0000644 0000151 0000155 00000027231 12133517265 020456 0 ustar narval narval # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of logilab-astng.
#
# logilab-astng is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 2.1 of the License, or (at your
# option) any later version.
#
# logilab-astng is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see .
"""tests for the astng variable lookup capabilities
"""
import sys
from os.path import join, abspath, dirname
from logilab.common.testlib import TestCase, unittest_main, require_version
from logilab.astng import builder, nodes, scoped_nodes, \
InferenceError, NotFoundError, UnresolvableName
from logilab.astng.scoped_nodes import builtin_lookup, Function
from logilab.astng.bases import YES
from unittest_inference import get_name_node
builder = builder.ASTNGBuilder()
DATA = join(dirname(abspath(__file__)), 'data')
MODULE = builder.file_build(join(DATA, 'module.py'), 'data.module')
MODULE2 = builder.file_build(join(DATA, 'module2.py'), 'data.module2')
NONREGR = builder.file_build(join(DATA, 'nonregr.py'), 'data.nonregr')
class LookupTC(TestCase):
def test_limit(self):
code = '''
l = [a
for a,b in list]
a = 1
b = a
a = None
def func():
c = 1
'''
astng = builder.string_build(code, __name__, __file__)
# a & b
a = astng.nodes_of_class(nodes.Name).next()
self.assertEqual(a.lineno, 2)
if sys.version_info < (3, 0):
self.assertEqual(len(astng.lookup('b')[1]), 2)
self.assertEqual(len(astng.lookup('a')[1]), 3)
b = astng.locals['b'][1]
else:
self.assertEqual(len(astng.lookup('b')[1]), 1)
self.assertEqual(len(astng.lookup('a')[1]), 2)
b = astng.locals['b'][0]
stmts = a.lookup('a')[1]
self.assertEqual(len(stmts), 1)
self.assertEqual(b.lineno, 6)
b_infer = b.infer()
b_value = b_infer.next()
self.assertEqual(b_value.value, 1)
# c
self.assertRaises(StopIteration, b_infer.next)
func = astng.locals['func'][0]
self.assertEqual(len(func.lookup('c')[1]), 1)
def test_module(self):
astng = builder.string_build('pass', __name__, __file__)
# built-in objects
none = astng.ilookup('None').next()
self.assertIsNone(none.value)
obj = astng.ilookup('object').next()
self.assertIsInstance(obj, nodes.Class)
self.assertEqual(obj.name, 'object')
self.assertRaises(InferenceError, astng.ilookup('YOAA').next)
# XXX
self.assertEqual(len(list(NONREGR.ilookup('enumerate'))), 2)
def test_class_ancestor_name(self):
code = '''
class A:
pass
class A(A):
pass
'''
astng = builder.string_build(code, __name__, __file__)
cls1 = astng.locals['A'][0]
cls2 = astng.locals['A'][1]
name = cls2.nodes_of_class(nodes.Name).next()
self.assertEqual(name.infer().next(), cls1)
### backport those test to inline code
def test_method(self):
method = MODULE['YOUPI']['method']
my_dict = method.ilookup('MY_DICT').next()
self.assertTrue(isinstance(my_dict, nodes.Dict), my_dict)
none = method.ilookup('None').next()
self.assertIsNone(none.value)
self.assertRaises(InferenceError, method.ilookup('YOAA').next)
def test_function_argument_with_default(self):
make_class = MODULE2['make_class']
base = make_class.ilookup('base').next()
self.assertTrue(isinstance(base, nodes.Class), base.__class__)
self.assertEqual(base.name, 'YO')
self.assertEqual(base.root().name, 'data.module')
def test_class(self):
klass = MODULE['YOUPI']
my_dict = klass.ilookup('MY_DICT').next()
self.assertIsInstance(my_dict, nodes.Dict)
none = klass.ilookup('None').next()
self.assertIsNone(none.value)
obj = klass.ilookup('object').next()
self.assertIsInstance(obj, nodes.Class)
self.assertEqual(obj.name, 'object')
self.assertRaises(InferenceError, klass.ilookup('YOAA').next)
def test_inner_classes(self):
ddd = list(NONREGR['Ccc'].ilookup('Ddd'))
self.assertEqual(ddd[0].name, 'Ddd')
def test_loopvar_hiding(self):
astng = builder.string_build("""
x = 10
for x in range(5):
print (x)
if x > 0:
print ('#' * x)
""", __name__, __file__)
xnames = [n for n in astng.nodes_of_class(nodes.Name) if n.name == 'x']
# inside the loop, only one possible assignment
self.assertEqual(len(xnames[0].lookup('x')[1]), 1)
# outside the loop, two possible assignments
self.assertEqual(len(xnames[1].lookup('x')[1]), 2)
self.assertEqual(len(xnames[2].lookup('x')[1]), 2)
def test_list_comps(self):
astng = builder.string_build("""
print ([ i for i in range(10) ])
print ([ i for i in range(10) ])
print ( list( i for i in range(10) ) )
""", __name__, __file__)
xnames = [n for n in astng.nodes_of_class(nodes.Name) if n.name == 'i']
self.assertEqual(len(xnames[0].lookup('i')[1]), 1)
self.assertEqual(xnames[0].lookup('i')[1][0].lineno, 2)
self.assertEqual(len(xnames[1].lookup('i')[1]), 1)
self.assertEqual(xnames[1].lookup('i')[1][0].lineno, 3)
self.assertEqual(len(xnames[2].lookup('i')[1]), 1)
self.assertEqual(xnames[2].lookup('i')[1][0].lineno, 4)
def test_list_comp_target(self):
"""test the list comprehension target"""
astng = builder.string_build("""
ten = [ var for var in range(10) ]
var
""")
var = astng.body[1].value
if sys.version_info < (3, 0):
self.assertEqual(var.infered(), [YES])
else:
self.assertRaises(UnresolvableName, var.infered)
@require_version('2.7')
def test_dict_comps(self):
astng = builder.string_build("""
print ({ i: j for i in range(10) for j in range(10) })
print ({ i: j for i in range(10) for j in range(10) })
""", __name__, __file__)
xnames = [n for n in astng.nodes_of_class(nodes.Name) if n.name == 'i']
self.assertEqual(len(xnames[0].lookup('i')[1]), 1)
self.assertEqual(xnames[0].lookup('i')[1][0].lineno, 2)
self.assertEqual(len(xnames[1].lookup('i')[1]), 1)
self.assertEqual(xnames[1].lookup('i')[1][0].lineno, 3)
xnames = [n for n in astng.nodes_of_class(nodes.Name) if n.name == 'j']
self.assertEqual(len(xnames[0].lookup('i')[1]), 1)
self.assertEqual(xnames[0].lookup('i')[1][0].lineno, 2)
self.assertEqual(len(xnames[1].lookup('i')[1]), 1)
self.assertEqual(xnames[1].lookup('i')[1][0].lineno, 3)
@require_version('2.7')
def test_set_comps(self):
astng = builder.string_build("""
print ({ i for i in range(10) })
print ({ i for i in range(10) })
""", __name__, __file__)
xnames = [n for n in astng.nodes_of_class(nodes.Name) if n.name == 'i']
self.assertEqual(len(xnames[0].lookup('i')[1]), 1)
self.assertEqual(xnames[0].lookup('i')[1][0].lineno, 2)
self.assertEqual(len(xnames[1].lookup('i')[1]), 1)
self.assertEqual(xnames[1].lookup('i')[1][0].lineno, 3)
@require_version('2.7')
def test_set_comp_closure(self):
astng = builder.string_build("""
ten = { var for var in range(10) }
var
""")
var = astng.body[1].value
self.assertRaises(UnresolvableName, var.infered)
def test_generator_attributes(self):
tree = builder.string_build("""
def count():
"test"
yield 0
iterer = count()
num = iterer.next()
""")
next = tree.body[2].value.func # Getattr
gener = next.expr.infered()[0] # Generator
self.assertIsInstance(gener.getattr('next')[0], Function)
self.assertIsInstance(gener.getattr('send')[0], Function)
self.assertIsInstance(gener.getattr('throw')[0], Function)
self.assertIsInstance(gener.getattr('close')[0], Function)
def test_explicit___name__(self):
code = '''
class Pouet:
__name__ = "pouet"
p1 = Pouet()
class PouetPouet(Pouet): pass
p2 = Pouet()
class NoName: pass
p3 = NoName()
'''
astng = builder.string_build(code, __name__, __file__)
p1 = astng['p1'].infer().next()
self.assertTrue(p1.getattr('__name__'))
p2 = astng['p2'].infer().next()
self.assertTrue(p2.getattr('__name__'))
self.assertTrue(astng['NoName'].getattr('__name__'))
p3 = astng['p3'].infer().next()
self.assertRaises(NotFoundError, p3.getattr, '__name__')
def test_function_module_special(self):
astng = builder.string_build('''
def initialize(linter):
"""initialize linter with checkers in this package """
package_load(linter, __path__[0])
''', 'data.__init__', 'data/__init__.py')
path = [n for n in astng.nodes_of_class(nodes.Name) if n.name == '__path__'][0]
self.assertEqual(len(path.lookup('__path__')[1]), 1)
def test_builtin_lookup(self):
self.assertEqual(builtin_lookup('__dict__')[1], ())
intstmts = builtin_lookup('int')[1]
self.assertEqual(len(intstmts), 1)
self.assertIsInstance(intstmts[0], nodes.Class)
self.assertEqual(intstmts[0].name, 'int')
self.assertIs(intstmts[0], nodes.const_factory(1)._proxied)
def test_decorator_arguments_lookup(self):
code = '''
def decorator(value):
def wrapper(function):
return function
return wrapper
class foo:
member = 10
@decorator(member) #This will cause pylint to complain
def test(self):
pass
'''
astng = builder.string_build(code, __name__, __file__)
member = get_name_node(astng['foo'], 'member')
it = member.infer()
obj = it.next()
self.assertIsInstance(obj, nodes.Const)
self.assertEqual(obj.value, 10)
self.assertRaises(StopIteration, it.next)
def test_inner_decorator_member_lookup(self):
code = '''
class FileA:
def decorator(bla):
return bla
@decorator
def funcA():
return 4
'''
astng = builder.string_build(code, __name__, __file__)
decname = get_name_node(astng['FileA'], 'decorator')
it = decname.infer()
obj = it.next()
self.assertIsInstance(obj, nodes.Function)
self.assertRaises(StopIteration, it.next)
def test_static_method_lookup(self):
code = '''
class FileA:
@staticmethod
def funcA():
return 4
class Test:
FileA = [1,2,3]
def __init__(self):
print (FileA.funcA())
'''
astng = builder.string_build(code, __name__, __file__)
it = astng['Test']['__init__'].ilookup('FileA')
obj = it.next()
self.assertIsInstance(obj, nodes.Class)
self.assertRaises(StopIteration, it.next)
def test_global_delete(self):
code = '''
def run2():
f = Frobble()
class Frobble:
pass
Frobble.mumble = True
del Frobble
def run1():
f = Frobble()
'''
astng = builder.string_build(code, __name__, __file__)
stmts = astng['run2'].lookup('Frobbel')[1]
self.assertEqual(len(stmts), 0)
stmts = astng['run1'].lookup('Frobbel')[1]
self.assertEqual(len(stmts), 0)
if __name__ == '__main__':
unittest_main()
logilab-astng-0.24.3/test/unittest_inspector.py 0000644 0000151 0000155 00000007163 12133517265 021155 0 ustar narval narval # Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
# http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
"""
for the visitors.diadefs module
"""
import sys
from os.path import join, abspath, dirname
from logilab.common.testlib import TestCase, unittest_main
from logilab.astng import nodes, inspector
from logilab.astng.bases import Instance, YES
from logilab.astng.manager import ASTNGManager, _silent_no_wrap
MANAGER = ASTNGManager()
def astng_wrapper(func, modname):
return func(modname)
DATA2 = join(dirname(abspath(__file__)), 'data2')
class LinkerTC(TestCase):
def setUp(self):
self.project = MANAGER.project_from_files([DATA2], astng_wrapper)
self.linker = inspector.Linker(self.project)
self.linker.visit(self.project)
def test_class_implements(self):
klass = self.project.get_module('data2.clientmodule_test')['Ancestor']
self.assertTrue(hasattr(klass, 'implements'))
self.assertEqual(len(klass.implements), 1)
self.assertTrue(isinstance(klass.implements[0], nodes.Class))
self.assertEqual(klass.implements[0].name, "Interface")
klass = self.project.get_module('data2.clientmodule_test')['Specialization']
self.assertTrue(hasattr(klass, 'implements'))
self.assertEqual(len(klass.implements), 0)
def test_locals_assignment_resolution(self):
klass = self.project.get_module('data2.clientmodule_test')['Specialization']
self.assertTrue(hasattr(klass, 'locals_type'))
type_dict = klass.locals_type
self.assertEqual(len(type_dict), 2)
keys = sorted(type_dict.keys())
self.assertEqual(keys, ['TYPE', 'top'])
self.assertEqual(len(type_dict['TYPE']), 1)
self.assertEqual(type_dict['TYPE'][0].value, 'final class')
self.assertEqual(len(type_dict['top']), 1)
self.assertEqual(type_dict['top'][0].value, 'class')
def test_instance_attrs_resolution(self):
klass = self.project.get_module('data2.clientmodule_test')['Specialization']
self.assertTrue(hasattr(klass, 'instance_attrs_type'))
type_dict = klass.instance_attrs_type
self.assertEqual(len(type_dict), 3)
keys = sorted(type_dict.keys())
self.assertEqual(keys, ['_id', 'relation', 'toto'])
self.assertTrue(isinstance(type_dict['relation'][0], Instance), type_dict['relation'])
self.assertEqual(type_dict['relation'][0].name, 'DoNothing')
self.assertTrue(isinstance(type_dict['toto'][0], Instance), type_dict['toto'])
self.assertEqual(type_dict['toto'][0].name, 'Toto')
self.assertIs(type_dict['_id'][0], YES)
class LinkerTC2(LinkerTC):
def setUp(self):
self.project = MANAGER.project_from_files([DATA2], func_wrapper=_silent_no_wrap)
self.linker = inspector.Linker(self.project)
self.linker.visit(self.project)
__all__ = ('LinkerTC', 'LinkerTC2')
if __name__ == '__main__':
unittest_main()
logilab-astng-0.24.3/test/unittest_nodes.py 0000644 0000151 0000155 00000033002 12133517265 020246 0 ustar narval narval # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of logilab-astng.
#
# logilab-astng is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 2.1 of the License, or (at your
# option) any later version.
#
# logilab-astng is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see .
"""tests for specific behaviour of astng nodes
"""
import sys
from logilab.common import testlib
from logilab.astng.node_classes import unpack_infer
from logilab.astng.bases import BUILTINS, YES, InferenceContext
from logilab.astng.exceptions import ASTNGBuildingException, NotFoundError
from logilab.astng import builder, nodes
from data import module as test_module
from os.path import join, abspath, dirname
DATA = join(dirname(abspath(__file__)), 'data')
abuilder = builder.ASTNGBuilder()
class AsString(testlib.TestCase):
def test_varargs_kwargs_as_string(self):
ast = abuilder.string_build( 'raise_string(*args, **kwargs)').body[0]
self.assertEqual(ast.as_string(), 'raise_string(*args, **kwargs)')
def test_module_as_string(self):
"""check as_string on a whole module prepared to be returned identically
"""
data = open(join(DATA, 'module.py')).read()
self.assertMultiLineEqual(MODULE.as_string(), data)
def test_module2_as_string(self):
"""check as_string on a whole module prepared to be returned identically
"""
data = open(join(DATA, 'module2.py')).read()
self.assertMultiLineEqual(MODULE2.as_string(), data)
@testlib.require_version('2.7')
def test_2_7_as_string(self):
"""check as_string for python syntax >= 2.7"""
code = '''one_two = {1, 2}
b = {v: k for (k, v) in enumerate('string')}
cdd = {k for k in b}\n\n'''
ast = abuilder.string_build(code)
self.assertMultiLineEqual(ast.as_string(), code)
@testlib.require_version('3.0')
def test_3k_as_string(self):
"""check as_string for python 3k syntax"""
code = '''print()
def function(var):
nonlocal counter
try:
hello
except NameError as nexc:
(*hell, o) = b'hello'
raise AttributeError from nexc
\n'''
# TODO : annotations and keywords for class definition are not yet implemented
_todo = '''
def function(var:int):
nonlocal counter
class Language(metaclass=Natural):
"""natural language"""
'''
ast = abuilder.string_build(code)
self.assertEqual(ast.as_string(), code)
class _NodeTC(testlib.TestCase):
"""test transformation of If Node"""
CODE = None
@property
def astng(self):
try:
return self.__class__.__dict__['CODE_ASTNG']
except KeyError:
astng = abuilder.string_build(self.CODE)
self.__class__.CODE_ASTNG = astng
return astng
class IfNodeTC(_NodeTC):
"""test transformation of If Node"""
CODE = """
if 0:
print()
if True:
print()
else:
pass
if "":
print()
elif []:
raise
if 1:
print()
elif True:
print()
elif func():
pass
else:
raise
"""
def test_if_elif_else_node(self):
"""test transformation for If node"""
self.assertEqual(len(self.astng.body), 4)
for stmt in self.astng.body:
self.assertIsInstance( stmt, nodes.If)
self.assertFalse(self.astng.body[0].orelse) # simple If
self.assertIsInstance(self.astng.body[1].orelse[0], nodes.Pass) # If / else
self.assertIsInstance(self.astng.body[2].orelse[0], nodes.If) # If / elif
self.assertIsInstance(self.astng.body[3].orelse[0].orelse[0], nodes.If)
def test_block_range(self):
# XXX ensure expected values
self.assertEqual(self.astng.block_range(1), (0, 22))
self.assertEqual(self.astng.block_range(10), (0, 22)) # XXX (10, 22) ?
self.assertEqual(self.astng.body[1].block_range(5), (5, 6))
self.assertEqual(self.astng.body[1].block_range(6), (6, 6))
self.assertEqual(self.astng.body[1].orelse[0].block_range(7), (7, 8))
self.assertEqual(self.astng.body[1].orelse[0].block_range(8), (8, 8))
class TryExceptNodeTC(_NodeTC):
CODE = """
try:
print ('pouet')
except IOError:
pass
except UnicodeError:
print()
else:
print()
"""
def test_block_range(self):
# XXX ensure expected values
self.assertEqual(self.astng.body[0].block_range(1), (1, 8))
self.assertEqual(self.astng.body[0].block_range(2), (2, 2))
self.assertEqual(self.astng.body[0].block_range(3), (3, 8))
self.assertEqual(self.astng.body[0].block_range(4), (4, 4))
self.assertEqual(self.astng.body[0].block_range(5), (5, 5))
self.assertEqual(self.astng.body[0].block_range(6), (6, 6))
self.assertEqual(self.astng.body[0].block_range(7), (7, 7))
self.assertEqual(self.astng.body[0].block_range(8), (8, 8))
class TryFinallyNodeTC(_NodeTC):
CODE = """
try:
print ('pouet')
finally:
print ('pouet')
"""
def test_block_range(self):
# XXX ensure expected values
self.assertEqual(self.astng.body[0].block_range(1), (1, 4))
self.assertEqual(self.astng.body[0].block_range(2), (2, 2))
self.assertEqual(self.astng.body[0].block_range(3), (3, 4))
self.assertEqual(self.astng.body[0].block_range(4), (4, 4))
class TryFinally25NodeTC(_NodeTC):
CODE = """
try:
print('pouet')
except Exception:
print ('oops')
finally:
print ('pouet')
"""
def test_block_range(self):
# XXX ensure expected values
self.assertEqual(self.astng.body[0].block_range(1), (1, 6))
self.assertEqual(self.astng.body[0].block_range(2), (2, 2))
self.assertEqual(self.astng.body[0].block_range(3), (3, 4))
self.assertEqual(self.astng.body[0].block_range(4), (4, 4))
self.assertEqual(self.astng.body[0].block_range(5), (5, 5))
self.assertEqual(self.astng.body[0].block_range(6), (6, 6))
class TryExcept2xNodeTC(_NodeTC):
CODE = """
try:
hello
except AttributeError, (retval, desc):
pass
"""
def test_tuple_attribute(self):
if sys.version_info >= (3, 0):
self.skipTest('syntax removed from py3.x')
handler = self.astng.body[0].handlers[0]
self.assertIsInstance(handler.name, nodes.Tuple)
MODULE = abuilder.module_build(test_module)
MODULE2 = abuilder.file_build(join(DATA, 'module2.py'), 'data.module2')
class ImportNodeTC(testlib.TestCase):
def test_import_self_resolve(self):
myos = MODULE2.igetattr('myos').next()
self.assertTrue(isinstance(myos, nodes.Module), myos)
self.assertEqual(myos.name, 'os')
self.assertEqual(myos.qname(), 'os')
self.assertEqual(myos.pytype(), '%s.module' % BUILTINS)
def test_from_self_resolve(self):
spawn = MODULE.igetattr('spawn').next()
self.assertTrue(isinstance(spawn, nodes.Class), spawn)
self.assertEqual(spawn.root().name, 'logilab.common.shellutils')
self.assertEqual(spawn.qname(), 'logilab.common.shellutils.Execute')
self.assertEqual(spawn.pytype(), '%s.classobj' % BUILTINS)
abspath = MODULE2.igetattr('abspath').next()
self.assertTrue(isinstance(abspath, nodes.Function), abspath)
self.assertEqual(abspath.root().name, 'os.path')
self.assertEqual(abspath.qname(), 'os.path.abspath')
self.assertEqual(abspath.pytype(), '%s.function' % BUILTINS)
def test_real_name(self):
from_ = MODULE['spawn']
self.assertEqual(from_.real_name('spawn'), 'Execute')
imp_ = MODULE['os']
self.assertEqual(imp_.real_name('os'), 'os')
self.assertRaises(NotFoundError, imp_.real_name, 'os.path')
imp_ = MODULE['spawn']
self.assertEqual(imp_.real_name('spawn'), 'Execute')
self.assertRaises(NotFoundError, imp_.real_name, 'Execute')
imp_ = MODULE2['YO']
self.assertEqual(imp_.real_name('YO'), 'YO')
self.assertRaises(NotFoundError, imp_.real_name, 'data')
def test_as_string(self):
ast = MODULE['modutils']
self.assertEqual(ast.as_string(), "from logilab.common import modutils")
ast = MODULE['spawn']
self.assertEqual(ast.as_string(), "from logilab.common.shellutils import Execute as spawn")
ast = MODULE['os']
self.assertEqual(ast.as_string(), "import os.path")
code = """from . import here
from .. import door
from .store import bread
from ..cave import wine\n\n"""
ast = abuilder.string_build(code)
self.assertMultiLineEqual(ast.as_string(), code)
def test_bad_import_inference(self):
# Explication of bug
'''When we import PickleError from nonexistent, a call to the infer
method of this From node will be made by unpack_infer.
inference.infer_from will try to import this module, which will fail and
raise a InferenceException (by mixins.do_import_module). The infer_name
will catch this exception and yield and YES instead.
'''
code = '''try:
from pickle import PickleError
except ImportError:
from nonexistent import PickleError
try:
pass
except PickleError:
pass
'''
astng = abuilder.string_build(code)
from_node = astng.body[1].handlers[0].body[0]
handler_type = astng.body[1].handlers[0].type
excs = list(unpack_infer(handler_type))
def test_absolute_import(self):
astng = abuilder.file_build(self.datapath('absimport.py'))
ctx = InferenceContext()
ctx.lookupname = 'message'
# will fail if absolute import failed
astng['message'].infer(ctx).next()
ctx.lookupname = 'email'
m = astng['email'].infer(ctx).next()
self.assertFalse(m.file.startswith(self.datapath('email.py')))
class CmpNodeTC(testlib.TestCase):
def test_as_string(self):
ast = abuilder.string_build("a == 2").body[0]
self.assertEqual(ast.as_string(), "a == 2")
class ConstNodeTC(testlib.TestCase):
def _test(self, value):
node = nodes.const_factory(value)
self.assertIsInstance(node._proxied, nodes.Class)
self.assertEqual(node._proxied.name, value.__class__.__name__)
self.assertIs(node.value, value)
self.assertTrue(node._proxied.parent)
self.assertEqual(node._proxied.root().name, value.__class__.__module__)
def test_none(self):
self._test(None)
def test_bool(self):
self._test(True)
def test_int(self):
self._test(1)
def test_float(self):
self._test(1.0)
def test_complex(self):
self._test(1.0j)
def test_str(self):
self._test('a')
def test_unicode(self):
self._test(u'a')
class NameNodeTC(testlib.TestCase):
def test_assign_to_True(self):
"""test that True and False assignements don't crash"""
code = """True = False
def hello(False):
pass
del True
"""
if sys.version_info >= (3, 0):
self.assertRaises(SyntaxError,#might become ASTNGBuildingException
abuilder.string_build, code)
else:
ast = abuilder.string_build(code)
ass_true = ast['True']
self.assertIsInstance(ass_true, nodes.AssName)
self.assertEqual(ass_true.name, "True")
del_true = ast.body[2].targets[0]
self.assertIsInstance(del_true, nodes.DelName)
self.assertEqual(del_true.name, "True")
class ArgumentsNodeTC(testlib.TestCase):
def test_linenumbering(self):
ast = abuilder.string_build('''
def func(a,
b): pass
x = lambda x: None
''')
self.assertEqual(ast['func'].args.fromlineno, 2)
self.assertFalse(ast['func'].args.is_statement)
xlambda = ast['x'].infer().next()
self.assertEqual(xlambda.args.fromlineno, 4)
self.assertEqual(xlambda.args.tolineno, 4)
self.assertFalse(xlambda.args.is_statement)
if sys.version_info < (3, 0):
self.assertEqual(ast['func'].args.tolineno, 3)
else:
self.skipTest('FIXME http://bugs.python.org/issue10445 '
'(no line number on function args)')
class SliceNodeTC(testlib.TestCase):
def test(self):
for code in ('a[0]', 'a[1:3]', 'a[:-1:step]', 'a[:,newaxis]',
'a[newaxis,:]', 'del L[::2]', 'del A[1]', 'del Br[:]'):
ast = abuilder.string_build(code).body[0]
self.assertEqual(ast.as_string(), code)
def test_slice_and_subscripts(self):
code = """a[:1] = bord[2:]
a[:1] = bord[2:]
del bree[3:d]
bord[2:]
del av[d::f], a[df:]
a[:1] = bord[2:]
del SRC[::1,newaxis,1:]
tous[vals] = 1010
del thousand[key]
del a[::2], a[:-1:step]
del Fee.form[left:]
aout.vals = miles.of_stuff
del (ccok, (name.thing, foo.attrib.value)), Fee.form[left:]
if all[1] == bord[0:]:
pass\n\n"""
ast = abuilder.string_build(code)
self.assertEqual(ast.as_string(), code)
class EllipsisNodeTC(testlib.TestCase):
def test(self):
ast = abuilder.string_build('a[...]').body[0]
self.assertEqual(ast.as_string(), 'a[...]')
if __name__ == '__main__':
testlib.unittest_main()
logilab-astng-0.24.3/test/unittest_regrtest.py 0000644 0000151 0000155 00000012226 12133517265 021002 0 ustar narval narval # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of logilab-astng.
#
# logilab-astng is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 2.1 of the License, or (at your
# option) any later version.
#
# logilab-astng is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see .
from logilab.common.testlib import unittest_main, TestCase
from logilab.astng import ResolveError, MANAGER, Instance, nodes, YES, InferenceError
from logilab.astng.builder import ASTNGBuilder
from logilab.astng.raw_building import build_module
from logilab.astng.manager import ASTNGManager
import sys
from os.path import join, abspath, dirname
class NonRegressionTC(TestCase):
def setUp(self):
sys.path.insert(0, join(dirname(abspath(__file__)), 'regrtest_data'))
def tearDown(self):
sys.path.pop(0)
def brainless_manager(self):
manager = ASTNGManager()
# avoid caching into the ASTNGManager borg since we get problems
# with other tests :
manager.__dict__ = {}
manager.astng_cache = {}
manager._mod_file_cache = {}
manager.transformers = {}
return manager
def test_module_path(self):
man = self.brainless_manager()
mod = man.astng_from_module_name('package.import_package_subpackage_module')
package = mod.igetattr('package').next()
self.assertEqual(package.name, 'package')
subpackage = package.igetattr('subpackage').next()
self.assertIsInstance(subpackage, nodes.Module)
self.assertTrue(subpackage.package)
self.assertEqual(subpackage.name, 'package.subpackage')
module = subpackage.igetattr('module').next()
self.assertEqual(module.name, 'package.subpackage.module')
def test_package_sidepackage(self):
manager = self.brainless_manager()
assert 'package.sidepackage' not in MANAGER.astng_cache
package = manager.astng_from_module_name('absimp')
self.assertIsInstance(package, nodes.Module)
self.assertTrue(package.package)
subpackage = package.getattr('sidepackage')[0].infer().next()
self.assertIsInstance(subpackage, nodes.Module)
self.assertTrue(subpackage.package)
self.assertEqual(subpackage.name, 'absimp.sidepackage')
def test_living_property(self):
builder = ASTNGBuilder()
builder._done = {}
builder._module = sys.modules[__name__]
builder.object_build(build_module('module_name', ''), Whatever)
def test_new_style_class_detection(self):
try:
import pygtk
except ImportError:
self.skipTest('test skipped: pygtk is not available')
# XXX may fail on some pygtk version, because objects in
# gobject._gobject have __module__ set to gobject :(
builder = ASTNGBuilder()
data = """
import pygtk
pygtk.require("2.6")
import gobject
class A(gobject.GObject):
pass
"""
astng = builder.string_build(data, __name__, __file__)
a = astng['A']
self.assertTrue(a.newstyle)
def test_pylint_config_attr(self):
try:
from pylint import lint
except ImportError:
self.skipTest('pylint not available')
mod = MANAGER.astng_from_module_name('pylint.lint')
pylinter = mod['PyLinter']
expect = ['OptionsManagerMixIn', 'object', 'MessagesHandlerMixIn',
'ReportsHandlerMixIn', 'BaseRawChecker', 'BaseChecker',
'OptionsProviderMixIn', 'ASTWalker']
self.assertListEqual([c.name for c in pylinter.ancestors()],
expect)
self.assertTrue(list(Instance(pylinter).getattr('config')))
infered = list(Instance(pylinter).igetattr('config'))
self.assertEqual(len(infered), 1)
self.assertEqual(infered[0].root().name, 'optparse')
self.assertEqual(infered[0].name, 'Values')
def test_numpy_crash(self):
"""test don't crash on numpy"""
#a crash occured somewhere in the past, and an
# InferenceError instead of a crash was better, but now we even infer!
try:
import numpy
except ImportError:
self.skipTest('test skipped: numpy is not available')
builder = ASTNGBuilder()
data = """
from numpy import multiply
multiply(1, 2, 3)
"""
astng = builder.string_build(data, __name__, __file__)
callfunc = astng.body[1].value.func
infered = callfunc.infered()
self.assertEqual(len(infered), 1)
self.assertIsInstance(infered[0], Instance)
class Whatever(object):
a = property(lambda x: x, lambda x: x)
if __name__ == '__main__':
unittest_main()
logilab-astng-0.24.3/test/unittest_inference.py 0000644 0000151 0000155 00000120103 12133517265 021073 0 ustar narval narval # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of logilab-astng.
#
# logilab-astng is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 2.1 of the License, or (at your
# option) any later version.
#
# logilab-astng is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see .
"""tests for the astng inference capabilities
"""
from os.path import join, dirname, abspath
import sys
from StringIO import StringIO
from logilab.common.testlib import TestCase, unittest_main, require_version
from logilab.astng import InferenceError, builder, nodes
from logilab.astng.inference import infer_end as inference_infer_end
from logilab.astng.bases import YES, Instance, BoundMethod, UnboundMethod,\
path_wrapper, BUILTINS
def get_name_node(start_from, name, index=0):
return [n for n in start_from.nodes_of_class(nodes.Name) if n.name == name][index]
def get_node_of_class(start_from, klass):
return start_from.nodes_of_class(klass).next()
builder = builder.ASTNGBuilder()
class InferenceUtilsTC(TestCase):
def test_path_wrapper(self):
def infer_default(self, *args):
raise InferenceError
infer_default = path_wrapper(infer_default)
infer_end = path_wrapper(inference_infer_end)
self.assertRaises(InferenceError,
infer_default(1).next)
self.assertEqual(infer_end(1).next(), 1)
if sys.version_info < (3, 0):
EXC_MODULE = 'exceptions'
else:
EXC_MODULE = BUILTINS
class InferenceTC(TestCase):
CODE = '''
class C(object):
"new style"
attr = 4
def meth1(self, arg1, optarg=0):
var = object()
print ("yo", arg1, optarg)
self.iattr = "hop"
return var
def meth2(self):
self.meth1(*self.meth3)
def meth3(self, d=attr):
b = self.attr
c = self.iattr
return b, c
ex = Exception("msg")
v = C().meth1(1)
m_unbound = C.meth1
m_bound = C().meth1
a, b, c = ex, 1, "bonjour"
[d, e, f] = [ex, 1.0, ("bonjour", v)]
g, h = f
i, (j, k) = "glup", f
a, b= b, a # Gasp !
'''
astng = builder.string_build(CODE, __name__, __file__)
def test_module_inference(self):
infered = self.astng.infer()
obj = infered.next()
self.assertEqual(obj.name, __name__)
self.assertEqual(obj.root().name, __name__)
self.assertRaises(StopIteration, infered.next)
def test_class_inference(self):
infered = self.astng['C'].infer()
obj = infered.next()
self.assertEqual(obj.name, 'C')
self.assertEqual(obj.root().name, __name__)
self.assertRaises(StopIteration, infered.next)
def test_function_inference(self):
infered = self.astng['C']['meth1'].infer()
obj = infered.next()
self.assertEqual(obj.name, 'meth1')
self.assertEqual(obj.root().name, __name__)
self.assertRaises(StopIteration, infered.next)
def test_builtin_name_inference(self):
infered = self.astng['C']['meth1']['var'].infer()
var = infered.next()
self.assertEqual(var.name, 'object')
self.assertEqual(var.root().name, BUILTINS)
self.assertRaises(StopIteration, infered.next)
def test_tupleassign_name_inference(self):
infered = self.astng['a'].infer()
exc = infered.next()
self.assertIsInstance(exc, Instance)
self.assertEqual(exc.name, 'Exception')
self.assertEqual(exc.root().name, EXC_MODULE)
self.assertRaises(StopIteration, infered.next)
infered = self.astng['b'].infer()
const = infered.next()
self.assertIsInstance(const, nodes.Const)
self.assertEqual(const.value, 1)
self.assertRaises(StopIteration, infered.next)
infered = self.astng['c'].infer()
const = infered.next()
self.assertIsInstance(const, nodes.Const)
self.assertEqual(const.value, "bonjour")
self.assertRaises(StopIteration, infered.next)
def test_listassign_name_inference(self):
infered = self.astng['d'].infer()
exc = infered.next()
self.assertIsInstance(exc, Instance)
self.assertEqual(exc.name, 'Exception')
self.assertEqual(exc.root().name, EXC_MODULE)
self.assertRaises(StopIteration, infered.next)
infered = self.astng['e'].infer()
const = infered.next()
self.assertIsInstance(const, nodes.Const)
self.assertEqual(const.value, 1.0)
self.assertRaises(StopIteration, infered.next)
infered = self.astng['f'].infer()
const = infered.next()
self.assertIsInstance(const, nodes.Tuple)
self.assertRaises(StopIteration, infered.next)
def test_advanced_tupleassign_name_inference1(self):
infered = self.astng['g'].infer()
const = infered.next()
self.assertIsInstance(const, nodes.Const)
self.assertEqual(const.value, "bonjour")
self.assertRaises(StopIteration, infered.next)
infered = self.astng['h'].infer()
var = infered.next()
self.assertEqual(var.name, 'object')
self.assertEqual(var.root().name, BUILTINS)
self.assertRaises(StopIteration, infered.next)
def test_advanced_tupleassign_name_inference2(self):
infered = self.astng['i'].infer()
const = infered.next()
self.assertIsInstance(const, nodes.Const)
self.assertEqual(const.value, u"glup")
self.assertRaises(StopIteration, infered.next)
infered = self.astng['j'].infer()
const = infered.next()
self.assertIsInstance(const, nodes.Const)
self.assertEqual(const.value, "bonjour")
self.assertRaises(StopIteration, infered.next)
infered = self.astng['k'].infer()
var = infered.next()
self.assertEqual(var.name, 'object')
self.assertEqual(var.root().name, BUILTINS)
self.assertRaises(StopIteration, infered.next)
def test_swap_assign_inference(self):
infered = self.astng.locals['a'][1].infer()
const = infered.next()
self.assertIsInstance(const, nodes.Const)
self.assertEqual(const.value, 1)
self.assertRaises(StopIteration, infered.next)
infered = self.astng.locals['b'][1].infer()
exc = infered.next()
self.assertIsInstance(exc, Instance)
self.assertEqual(exc.name, 'Exception')
self.assertEqual(exc.root().name, EXC_MODULE)
self.assertRaises(StopIteration, infered.next)
def test_getattr_inference1(self):
infered = self.astng['ex'].infer()
exc = infered.next()
self.assertIsInstance(exc, Instance)
self.assertEqual(exc.name, 'Exception')
self.assertEqual(exc.root().name, EXC_MODULE)
self.assertRaises(StopIteration, infered.next)
def test_getattr_inference2(self):
infered = get_node_of_class(self.astng['C']['meth2'], nodes.Getattr).infer()
meth1 = infered.next()
self.assertEqual(meth1.name, 'meth1')
self.assertEqual(meth1.root().name, __name__)
self.assertRaises(StopIteration, infered.next)
def test_getattr_inference3(self):
infered = self.astng['C']['meth3']['b'].infer()
const = infered.next()
self.assertIsInstance(const, nodes.Const)
self.assertEqual(const.value, 4)
self.assertRaises(StopIteration, infered.next)
def test_getattr_inference4(self):
infered = self.astng['C']['meth3']['c'].infer()
const = infered.next()
self.assertIsInstance(const, nodes.Const)
self.assertEqual(const.value, "hop")
self.assertRaises(StopIteration, infered.next)
def test_callfunc_inference(self):
infered = self.astng['v'].infer()
meth1 = infered.next()
self.assertIsInstance(meth1, Instance)
self.assertEqual(meth1.name, 'object')
self.assertEqual(meth1.root().name, BUILTINS)
self.assertRaises(StopIteration, infered.next)
def test_unbound_method_inference(self):
infered = self.astng['m_unbound'].infer()
meth1 = infered.next()
self.assertIsInstance(meth1, UnboundMethod)
self.assertEqual(meth1.name, 'meth1')
self.assertEqual(meth1.parent.frame().name, 'C')
self.assertRaises(StopIteration, infered.next)
def test_bound_method_inference(self):
infered = self.astng['m_bound'].infer()
meth1 = infered.next()
self.assertIsInstance(meth1, BoundMethod)
self.assertEqual(meth1.name, 'meth1')
self.assertEqual(meth1.parent.frame().name, 'C')
self.assertRaises(StopIteration, infered.next)
def test_args_default_inference1(self):
optarg = get_name_node(self.astng['C']['meth1'], 'optarg')
infered = optarg.infer()
obj1 = infered.next()
self.assertIsInstance(obj1, nodes.Const)
self.assertEqual(obj1.value, 0)
obj1 = infered.next()
self.assertIs(obj1, YES, obj1)
self.assertRaises(StopIteration, infered.next)
def test_args_default_inference2(self):
infered = self.astng['C']['meth3'].ilookup('d')
obj1 = infered.next()
self.assertIsInstance(obj1, nodes.Const)
self.assertEqual(obj1.value, 4)
obj1 = infered.next()
self.assertIs(obj1, YES, obj1)
self.assertRaises(StopIteration, infered.next)
def test_inference_restrictions(self):
infered = get_name_node(self.astng['C']['meth1'], 'arg1').infer()
obj1 = infered.next()
self.assertIs(obj1, YES, obj1)
self.assertRaises(StopIteration, infered.next)
def test_ancestors_inference(self):
code = '''
class A:
pass
class A(A):
pass
'''
astng = builder.string_build(code, __name__, __file__)
a1 = astng.locals['A'][0]
a2 = astng.locals['A'][1]
a2_ancestors = list(a2.ancestors())
self.assertEqual(len(a2_ancestors), 1)
self.assertIs(a2_ancestors[0], a1)
def test_ancestors_inference2(self):
code = '''
class A:
pass
class B(A): pass
class A(B):
pass
'''
astng = builder.string_build(code, __name__, __file__)
a1 = astng.locals['A'][0]
a2 = astng.locals['A'][1]
a2_ancestors = list(a2.ancestors())
self.assertEqual(len(a2_ancestors), 2)
self.assertIs(a2_ancestors[0], astng.locals['B'][0])
self.assertIs(a2_ancestors[1], a1)
def test_f_arg_f(self):
code = '''
def f(f=1):
return f
a = f()
'''
astng = builder.string_build(code, __name__, __file__)
a = astng['a']
a_infered = a.infered()
self.assertEqual(a_infered[0].value, 1)
self.assertEqual(len(a_infered), 1)
def test_exc_ancestors(self):
code = '''
def f():
raise NotImplementedError
'''
astng = builder.string_build(code, __name__, __file__)
error = astng.nodes_of_class(nodes.Name).next()
nie = error.infered()[0]
self.assertIsInstance(nie, nodes.Class)
nie_ancestors = [c.name for c in nie.ancestors()]
if sys.version_info < (3, 0):
self.assertEqual(nie_ancestors, ['RuntimeError', 'StandardError', 'Exception', 'BaseException', 'object'])
else:
self.assertEqual(nie_ancestors, ['RuntimeError', 'Exception', 'BaseException', 'object'])
def test_except_inference(self):
code = '''
try:
print (hop)
except NameError, ex:
ex1 = ex
except Exception, ex:
ex2 = ex
raise
'''
if sys.version_info >= (3, 0):
code = code.replace(', ex:', ' as ex:')
astng = builder.string_build(code, __name__, __file__)
ex1 = astng['ex1']
ex1_infer = ex1.infer()
ex1 = ex1_infer.next()
self.assertIsInstance(ex1, Instance)
self.assertEqual(ex1.name, 'NameError')
self.assertRaises(StopIteration, ex1_infer.next)
ex2 = astng['ex2']
ex2_infer = ex2.infer()
ex2 = ex2_infer.next()
self.assertIsInstance(ex2, Instance)
self.assertEqual(ex2.name, 'Exception')
self.assertRaises(StopIteration, ex2_infer.next)
def test_del1(self):
code = '''
del undefined_attr
'''
delete = builder.string_build(code, __name__, __file__).body[0]
self.assertRaises(InferenceError, delete.infer)
def test_del2(self):
code = '''
a = 1
b = a
del a
c = a
a = 2
d = a
'''
astng = builder.string_build(code, __name__, __file__)
n = astng['b']
n_infer = n.infer()
infered = n_infer.next()
self.assertIsInstance(infered, nodes.Const)
self.assertEqual(infered.value, 1)
self.assertRaises(StopIteration, n_infer.next)
n = astng['c']
n_infer = n.infer()
self.assertRaises(InferenceError, n_infer.next)
n = astng['d']
n_infer = n.infer()
infered = n_infer.next()
self.assertIsInstance(infered, nodes.Const)
self.assertEqual(infered.value, 2)
self.assertRaises(StopIteration, n_infer.next)
def test_builtin_types(self):
code = '''
l = [1]
t = (2,)
d = {}
s = ''
s2 = '_'
'''
astng = builder.string_build(code, __name__, __file__)
n = astng['l']
infered = n.infer().next()
self.assertIsInstance(infered, nodes.List)
self.assertIsInstance(infered, Instance)
self.assertEqual(infered.getitem(0).value, 1)
self.assertIsInstance(infered._proxied, nodes.Class)
self.assertEqual(infered._proxied.name, 'list')
self.assertIn('append', infered._proxied.locals)
n = astng['t']
infered = n.infer().next()
self.assertIsInstance(infered, nodes.Tuple)
self.assertIsInstance(infered, Instance)
self.assertEqual(infered.getitem(0).value, 2)
self.assertIsInstance(infered._proxied, nodes.Class)
self.assertEqual(infered._proxied.name, 'tuple')
n = astng['d']
infered = n.infer().next()
self.assertIsInstance(infered, nodes.Dict)
self.assertIsInstance(infered, Instance)
self.assertIsInstance(infered._proxied, nodes.Class)
self.assertEqual(infered._proxied.name, 'dict')
self.assertIn('get', infered._proxied.locals)
n = astng['s']
infered = n.infer().next()
self.assertIsInstance(infered, nodes.Const)
self.assertIsInstance(infered, Instance)
self.assertEqual(infered.name, 'str')
self.assertIn('lower', infered._proxied.locals)
n = astng['s2']
infered = n.infer().next()
self.assertEqual(infered.getitem(0).value, '_')
@require_version('2.7')
def test_builtin_types_py27(self):
code = 's = {1}'
astng = builder.string_build(code, __name__, __file__)
n = astng['s']
infered = n.infer().next()
self.assertIsInstance(infered, nodes.Set)
self.assertIsInstance(infered, Instance)
self.assertEqual(infered.name, 'set')
self.assertIn('remove', infered._proxied.locals)
def test_unicode_type(self):
if sys.version_info >= (3, 0):
self.skipTest('unicode removed on py >= 3.0')
code = '''u = u""'''
astng = builder.string_build(code, __name__, __file__)
n = astng['u']
infered = n.infer().next()
self.assertIsInstance(infered, nodes.Const)
self.assertIsInstance(infered, Instance)
self.assertEqual(infered.name, 'unicode')
self.assertIn('lower', infered._proxied.locals)
def test_descriptor_are_callable(self):
code = '''
class A:
statm = staticmethod(open)
clsm = classmethod('whatever')
'''
astng = builder.string_build(code, __name__, __file__)
statm = astng['A'].igetattr('statm').next()
self.assertTrue(statm.callable())
clsm = astng['A'].igetattr('clsm').next()
self.assertTrue(clsm.callable())
def test_bt_ancestor_crash(self):
code = '''
class Warning(Warning):
pass
'''
astng = builder.string_build(code, __name__, __file__)
w = astng['Warning']
ancestors = w.ancestors()
ancestor = ancestors.next()
self.assertEqual(ancestor.name, 'Warning')
self.assertEqual(ancestor.root().name, EXC_MODULE)
ancestor = ancestors.next()
self.assertEqual(ancestor.name, 'Exception')
self.assertEqual(ancestor.root().name, EXC_MODULE)
ancestor = ancestors.next()
self.assertEqual(ancestor.name, 'BaseException')
self.assertEqual(ancestor.root().name, EXC_MODULE)
ancestor = ancestors.next()
self.assertEqual(ancestor.name, 'object')
self.assertEqual(ancestor.root().name, BUILTINS)
self.assertRaises(StopIteration, ancestors.next)
def test_qqch(self):
code = '''
from logilab.common.modutils import load_module_from_name
xxx = load_module_from_name('__pkginfo__')
'''
astng = builder.string_build(code, __name__, __file__)
xxx = astng['xxx']
self.assertSetEqual(set(n.__class__ for n in xxx.infered()),
set([nodes.Const, YES.__class__]))
def test_method_argument(self):
code = '''
class ErudiEntitySchema:
"""a entity has a type, a set of subject and or object relations"""
def __init__(self, e_type, **kwargs):
kwargs['e_type'] = e_type.capitalize().encode()
def meth(self, e_type, *args, **kwargs):
kwargs['e_type'] = e_type.capitalize().encode()
print(args)
'''
astng = builder.string_build(code, __name__, __file__)
arg = get_name_node(astng['ErudiEntitySchema']['__init__'], 'e_type')
self.assertEqual([n.__class__ for n in arg.infer()],
[YES.__class__])
arg = get_name_node(astng['ErudiEntitySchema']['__init__'], 'kwargs')
self.assertEqual([n.__class__ for n in arg.infer()],
[nodes.Dict])
arg = get_name_node(astng['ErudiEntitySchema']['meth'], 'e_type')
self.assertEqual([n.__class__ for n in arg.infer()],
[YES.__class__])
arg = get_name_node(astng['ErudiEntitySchema']['meth'], 'args')
self.assertEqual([n.__class__ for n in arg.infer()],
[nodes.Tuple])
arg = get_name_node(astng['ErudiEntitySchema']['meth'], 'kwargs')
self.assertEqual([n.__class__ for n in arg.infer()],
[nodes.Dict])
def test_tuple_then_list(self):
code = '''
def test_view(rql, vid, tags=()):
tags = list(tags)
tags.append(vid)
'''
astng = builder.string_build(code, __name__, __file__)
name = get_name_node(astng['test_view'], 'tags', -1)
it = name.infer()
tags = it.next()
self.assertEqual(tags.__class__, Instance)
self.assertEqual(tags._proxied.name, 'list')
self.assertRaises(StopIteration, it.next)
def test_mulassign_inference(self):
code = '''
def first_word(line):
"""Return the first word of a line"""
return line.split()[0]
def last_word(line):
"""Return last word of a line"""
return line.split()[-1]
def process_line(word_pos):
"""Silly function: returns (ok, callable) based on argument.
For test purpose only.
"""
if word_pos > 0:
return (True, first_word)
elif word_pos < 0:
return (True, last_word)
else:
return (False, None)
if __name__ == '__main__':
line_number = 0
for a_line in file('test_callable.py'):
tupletest = process_line(line_number)
(ok, fct) = process_line(line_number)
if ok:
fct(a_line)
'''
astng = builder.string_build(code, __name__, __file__)
self.assertEqual(len(list(astng['process_line'].infer_call_result(
None))), 3)
self.assertEqual(len(list(astng['tupletest'].infer())), 3)
values = ['Function(first_word)', 'Function(last_word)', 'Const(NoneType)']
self.assertEqual([str(infered)
for infered in astng['fct'].infer()], values)
def test_float_complex_ambiguity(self):
code = '''
def no_conjugate_member(magic_flag):
"""should not raise E1101 on something.conjugate"""
if magic_flag:
something = 1.0
else:
something = 1.0j
if isinstance(something, float):
return something
return something.conjugate()
'''
astng = builder.string_build(code, __name__, __file__)
self.assertEqual([i.value for i in
astng['no_conjugate_member'].ilookup('something')], [1.0, 1.0j])
self.assertEqual([i.value for i in
get_name_node(astng, 'something', -1).infer()], [1.0, 1.0j])
def test_lookup_cond_branches(self):
code = '''
def no_conjugate_member(magic_flag):
"""should not raise E1101 on something.conjugate"""
something = 1.0
if magic_flag:
something = 1.0j
return something.conjugate()
'''
astng = builder.string_build(code, __name__, __file__)
self.assertEqual([i.value for i in
get_name_node(astng, 'something', -1).infer()], [1.0, 1.0j])
def test_simple_subscript(self):
code = '''
a = [1, 2, 3][0]
b = (1, 2, 3)[1]
c = (1, 2, 3)[-1]
d = a + b + c
print (d)
e = {'key': 'value'}
f = e['key']
print (f)
'''
astng = builder.string_build(code, __name__, __file__)
self.assertEqual([i.value for i in
get_name_node(astng, 'a', -1).infer()], [1])
self.assertEqual([i.value for i in
get_name_node(astng, 'b', -1).infer()], [2])
self.assertEqual([i.value for i in
get_name_node(astng, 'c', -1).infer()], [3])
self.assertEqual([i.value for i in
get_name_node(astng, 'd', -1).infer()], [6])
self.assertEqual([i.value for i in
get_name_node(astng, 'f', -1).infer()], ['value'])
#def test_simple_tuple(self):
#"""test case for a simple tuple value"""
## XXX tuple inference is not implemented ...
#code = """
#a = (1,)
#b = (22,)
#some = a + b
#"""
#astng = builder.string_build(code, __name__, __file__)
#self.assertEqual(astng['some'].infer.next().as_string(), "(1, 22)")
def test_simple_for(self):
code = '''
for a in [1, 2, 3]:
print (a)
for b,c in [(1,2), (3,4)]:
print (b)
print (c)
print ([(d,e) for e,d in ([1,2], [3,4])])
'''
astng = builder.string_build(code, __name__, __file__)
self.assertEqual([i.value for i in
get_name_node(astng, 'a', -1).infer()], [1, 2, 3])
self.assertEqual([i.value for i in
get_name_node(astng, 'b', -1).infer()], [1, 3])
self.assertEqual([i.value for i in
get_name_node(astng, 'c', -1).infer()], [2, 4])
self.assertEqual([i.value for i in
get_name_node(astng, 'd', -1).infer()], [2, 4])
self.assertEqual([i.value for i in
get_name_node(astng, 'e', -1).infer()], [1, 3])
def test_simple_for_genexpr(self):
code = '''
print ((d,e) for e,d in ([1,2], [3,4]))
'''
astng = builder.string_build(code, __name__, __file__)
self.assertEqual([i.value for i in
get_name_node(astng, 'd', -1).infer()], [2, 4])
self.assertEqual([i.value for i in
get_name_node(astng, 'e', -1).infer()], [1, 3])
def test_builtin_help(self):
code = '''
help()
'''
# XXX failing since __builtin__.help assignment has
# been moved into a function...
astng = builder.string_build(code, __name__, __file__)
node = get_name_node(astng, 'help', -1)
infered = list(node.infer())
self.assertEqual(len(infered), 1, infered)
self.assertIsInstance(infered[0], Instance)
self.assertEqual(str(infered[0]),
'Instance of site._Helper')
def test_builtin_open(self):
code = '''
open("toto.txt")
'''
astng = builder.string_build(code, __name__, __file__)
node = get_name_node(astng, 'open', -1)
infered = list(node.infer())
self.assertEqual(len(infered), 1)
self.assertIsInstance(infered[0], nodes.Function)
self.assertEqual(infered[0].name, 'open')
def test_callfunc_context_func(self):
code = '''
def mirror(arg=None):
return arg
un = mirror(1)
'''
astng = builder.string_build(code, __name__, __file__)
infered = list(astng.igetattr('un'))
self.assertEqual(len(infered), 1)
self.assertIsInstance(infered[0], nodes.Const)
self.assertEqual(infered[0].value, 1)
def test_callfunc_context_lambda(self):
code = '''
mirror = lambda x=None: x
un = mirror(1)
'''
astng = builder.string_build(code, __name__, __file__)
infered = list(astng.igetattr('mirror'))
self.assertEqual(len(infered), 1)
self.assertIsInstance(infered[0], nodes.Lambda)
infered = list(astng.igetattr('un'))
self.assertEqual(len(infered), 1)
self.assertIsInstance(infered[0], nodes.Const)
self.assertEqual(infered[0].value, 1)
def test_factory_method(self):
code = '''
class Super(object):
@classmethod
def instance(cls):
return cls()
class Sub(Super):
def method(self):
print ('method called')
sub = Sub.instance()
'''
astng = builder.string_build(code, __name__, __file__)
infered = list(astng.igetattr('sub'))
self.assertEqual(len(infered), 1)
self.assertIsInstance(infered[0], Instance)
self.assertEqual(infered[0]._proxied.name, 'Sub')
def test_import_as(self):
code = '''
import os.path as osp
print (osp.dirname(__file__))
from os.path import exists as e
assert e(__file__)
from new import code as make_code
print (make_code)
'''
astng = builder.string_build(code, __name__, __file__)
infered = list(astng.igetattr('osp'))
self.assertEqual(len(infered), 1)
self.assertIsInstance(infered[0], nodes.Module)
self.assertEqual(infered[0].name, 'os.path')
infered = list(astng.igetattr('e'))
self.assertEqual(len(infered), 1)
self.assertIsInstance(infered[0], nodes.Function)
self.assertEqual(infered[0].name, 'exists')
if sys.version_info >= (3, 0):
self.skipTest(' module has been removed')
infered = list(astng.igetattr('make_code'))
self.assertEqual(len(infered), 1)
self.assertIsInstance(infered[0], Instance)
self.assertEqual(str(infered[0]),
'Instance of %s.type' % BUILTINS)
def _test_const_infered(self, node, value):
infered = list(node.infer())
self.assertEqual(len(infered), 1)
self.assertIsInstance(infered[0], nodes.Const)
self.assertEqual(infered[0].value, value)
def test_unary_not(self):
for code in ('a = not (1,); b = not ()',
'a = not {1:2}; b = not {}'):
astng = builder.string_build(code, __name__, __file__)
self._test_const_infered(astng['a'], False)
self._test_const_infered(astng['b'], True)
def test_binary_op_int_add(self):
astng = builder.string_build('a = 1 + 2', __name__, __file__)
self._test_const_infered(astng['a'], 3)
def test_binary_op_int_sub(self):
astng = builder.string_build('a = 1 - 2', __name__, __file__)
self._test_const_infered(astng['a'], -1)
def test_binary_op_float_div(self):
astng = builder.string_build('a = 1 / 2.', __name__, __file__)
self._test_const_infered(astng['a'], 1 / 2.)
def test_binary_op_str_mul(self):
astng = builder.string_build('a = "*" * 40', __name__, __file__)
self._test_const_infered(astng['a'], "*" * 40)
def test_binary_op_bitand(self):
astng = builder.string_build('a = 23&20', __name__, __file__)
self._test_const_infered(astng['a'], 23&20)
def test_binary_op_bitor(self):
astng = builder.string_build('a = 23|8', __name__, __file__)
self._test_const_infered(astng['a'], 23|8)
def test_binary_op_bitxor(self):
astng = builder.string_build('a = 23^9', __name__, __file__)
self._test_const_infered(astng['a'], 23^9)
def test_binary_op_shiftright(self):
astng = builder.string_build('a = 23 >>1', __name__, __file__)
self._test_const_infered(astng['a'], 23>>1)
def test_binary_op_shiftleft(self):
astng = builder.string_build('a = 23 <<1', __name__, __file__)
self._test_const_infered(astng['a'], 23<<1)
def test_binary_op_list_mul(self):
for code in ('a = [[]] * 2', 'a = 2 * [[]]'):
astng = builder.string_build(code, __name__, __file__)
infered = list(astng['a'].infer())
self.assertEqual(len(infered), 1)
self.assertIsInstance(infered[0], nodes.List)
self.assertEqual(len(infered[0].elts), 2)
self.assertIsInstance(infered[0].elts[0], nodes.List)
self.assertIsInstance(infered[0].elts[1], nodes.List)
def test_binary_op_list_mul_none(self):
'test correct handling on list multiplied by None'
astng = builder.string_build( 'a = [1] * None\nb = [1] * "r"')
infered = astng['a'].infered()
self.assertEqual(len(infered), 1)
self.assertEqual(infered[0], YES)
infered = astng['b'].infered()
self.assertEqual(len(infered), 1)
self.assertEqual(infered[0], YES)
def test_binary_op_tuple_add(self):
astng = builder.string_build('a = (1,) + (2,)', __name__, __file__)
infered = list(astng['a'].infer())
self.assertEqual(len(infered), 1)
self.assertIsInstance(infered[0], nodes.Tuple)
self.assertEqual(len(infered[0].elts), 2)
self.assertEqual(infered[0].elts[0].value, 1)
self.assertEqual(infered[0].elts[1].value, 2)
def test_binary_op_custom_class(self):
code = '''
class myarray:
def __init__(self, array):
self.array = array
def __mul__(self, x):
return myarray([2,4,6])
def astype(self):
return "ASTYPE"
def randint(maximum):
if maximum is not None:
return myarray([1,2,3]) * 2
else:
return int(5)
x = randint(1)
'''
astng = builder.string_build(code, __name__, __file__)
infered = list(astng.igetattr('x'))
self.assertEqual(len(infered), 2)
value = [str(v) for v in infered]
# The __name__ trick here makes it work when invoked directly
# (__name__ == '__main__') and through pytest (__name__ ==
# 'unittest_inference')
self.assertEqual(value, ['Instance of %s.myarray' % __name__,
'Instance of %s.int' % BUILTINS])
def test_nonregr_lambda_arg(self):
code = '''
def f(g = lambda: None):
g().x
'''
astng = builder.string_build(code, __name__, __file__)
callfuncnode = astng['f'].body[0].value.expr
infered = list(callfuncnode.infer())
self.assertEqual(len(infered), 2, infered)
infered.remove(YES)
self.assertIsInstance(infered[0], nodes.Const)
self.assertIsNone(infered[0].value)
def test_nonregr_getitem_empty_tuple(self):
code = '''
def f(x):
a = ()[x]
'''
astng = builder.string_build(code, __name__, __file__)
infered = list(astng['f'].ilookup('a'))
self.assertEqual(len(infered), 1)
self.assertEqual(infered[0], YES)
def test_python25_generator_exit(self):
sys.stderr = StringIO()
data = "b = {}[str(0)+''].a"
astng = builder.string_build(data, __name__, __file__)
list(astng['b'].infer())
output = sys.stderr.getvalue()
# I have no idea how to test for this in another way...
self.assertNotIn("RuntimeError", output, "Exception exceptions.RuntimeError: 'generator ignored GeneratorExit' in ignored")
sys.stderr = sys.__stderr__
def test_python25_relative_import(self):
data = "from ...common import date; print (date)"
# !! FIXME also this relative import would not work 'in real' (no __init__.py in test/)
# the test works since we pretend we have a package by passing the full modname
astng = builder.string_build(data, 'logilab.astng.test.unittest_inference', __file__)
infered = get_name_node(astng, 'date').infer().next()
self.assertIsInstance(infered, nodes.Module)
self.assertEqual(infered.name, 'logilab.common.date')
def test_python25_no_relative_import(self):
fname = join(abspath(dirname(__file__)), 'regrtest_data', 'package', 'absimport.py')
astng = builder.file_build(fname, 'absimport')
self.assertTrue(astng.absolute_import_activated(), True)
infered = get_name_node(astng, 'import_package_subpackage_module').infer().next()
# failed to import since absolute_import is activated
self.assertIs(infered, YES)
def test_nonregr_absolute_import(self):
fname = join(abspath(dirname(__file__)), 'regrtest_data', 'absimp', 'string.py')
astng = builder.file_build(fname, 'absimp.string')
self.assertTrue(astng.absolute_import_activated(), True)
infered = get_name_node(astng, 'string').infer().next()
self.assertIsInstance(infered, nodes.Module)
self.assertEqual(infered.name, 'string')
self.assertIn('ascii_letters', infered.locals)
def test_mechanize_open(self):
try:
import mechanize
except ImportError:
self.skipTest('require mechanize installed')
data = '''from mechanize import Browser
print (Browser)
b = Browser()
'''
astng = builder.string_build(data, __name__, __file__)
browser = get_name_node(astng, 'Browser').infer().next()
self.assertIsInstance(browser, nodes.Class)
bopen = list(browser.igetattr('open'))
self.skipTest('the commit said: "huum, see that later"')
self.assertEqual(len(bopen), 1)
self.assertIsInstance(bopen[0], nodes.Function)
self.assertTrue(bopen[0].callable())
b = get_name_node(astng, 'b').infer().next()
self.assertIsInstance(b, Instance)
bopen = list(b.igetattr('open'))
self.assertEqual(len(bopen), 1)
self.assertIsInstance(bopen[0], BoundMethod)
self.assertTrue(bopen[0].callable())
def test_property(self):
code = '''
from smtplib import SMTP
class SendMailController(object):
@property
def smtp(self):
return SMTP(mailhost, port)
@property
def me(self):
return self
my_smtp = SendMailController().smtp
my_me = SendMailController().me
'''
decorators = set(['%s.property' % BUILTINS])
astng = builder.string_build(code, __name__, __file__)
self.assertEqual(astng['SendMailController']['smtp'].decoratornames(),
decorators)
propinfered = list(astng.body[2].value.infer())
self.assertEqual(len(propinfered), 1)
propinfered = propinfered[0]
self.assertIsInstance(propinfered, Instance)
self.assertEqual(propinfered.name, 'SMTP')
self.assertEqual(propinfered.root().name, 'smtplib')
self.assertEqual(astng['SendMailController']['me'].decoratornames(),
decorators)
propinfered = list(astng.body[3].value.infer())
self.assertEqual(len(propinfered), 1)
propinfered = propinfered[0]
self.assertIsInstance(propinfered, Instance)
self.assertEqual(propinfered.name, 'SendMailController')
self.assertEqual(propinfered.root().name, __name__)
def test_im_func_unwrap(self):
code = '''
class EnvBasedTC:
def pactions(self):
pass
pactions = EnvBasedTC.pactions.im_func
print (pactions)
class EnvBasedTC2:
pactions = EnvBasedTC.pactions.im_func
print (pactions)
'''
astng = builder.string_build(code, __name__, __file__)
pactions = get_name_node(astng, 'pactions')
infered = list(pactions.infer())
self.assertEqual(len(infered), 1)
self.assertIsInstance(infered[0], nodes.Function)
pactions = get_name_node(astng['EnvBasedTC2'], 'pactions')
infered = list(pactions.infer())
self.assertEqual(len(infered), 1)
self.assertIsInstance(infered[0], nodes.Function)
def test_augassign(self):
code = '''
a = 1
a += 2
print (a)
'''
astng = builder.string_build(code, __name__, __file__)
infered = list(get_name_node(astng, 'a').infer())
self.assertEqual(len(infered), 1)
self.assertIsInstance(infered[0], nodes.Const)
self.assertEqual(infered[0].value, 3)
def test_nonregr_func_arg(self):
code = '''
def foo(self, bar):
def baz():
pass
def qux():
return baz
spam = bar(None, qux)
print (spam)
'''
astng = builder.string_build(code, __name__, __file__)
infered = list(get_name_node(astng['foo'], 'spam').infer())
self.assertEqual(len(infered), 1)
self.assertIs(infered[0], YES)
def test_nonregr_func_global(self):
code = '''
active_application = None
def get_active_application():
global active_application
return active_application
class Application(object):
def __init__(self):
global active_application
active_application = self
class DataManager(object):
def __init__(self, app=None):
self.app = get_active_application()
def test(self):
p = self.app
print (p)
'''
astng = builder.string_build(code, __name__, __file__)
infered = list(Instance(astng['DataManager']).igetattr('app'))
self.assertEqual(len(infered), 2, infered) # None / Instance(Application)
infered = list(get_name_node(astng['DataManager']['test'], 'p').infer())
self.assertEqual(len(infered), 2, infered)
for node in infered:
if isinstance(node, Instance) and node.name == 'Application':
break
else:
self.fail('expected to find an instance of Application in %s' % infered)
def test_list_inference(self):
"""#20464"""
code = '''
import optparse
A = []
B = []
def test():
xyz = [
"foobar=%s" % options.ca,
] + A + B
if options.bind is not None:
xyz.append("bind=%s" % options.bind)
return xyz
def main():
global options
parser = optparse.OptionParser()
(options, args) = parser.parse_args()
Z = test()
'''
astng = builder.string_build(code, __name__, __file__)
infered = list(astng['Z'].infer())
self.assertEqual(len(infered), 1, infered)
self.assertIsInstance(infered[0], Instance)
self.assertIsInstance(infered[0]._proxied, nodes.Class)
self.assertEqual(infered[0]._proxied.name, 'list')
def test__new__(self):
code = '''
class NewTest(object):
"doc"
def __new__(cls, arg):
self = object.__new__(cls)
self.arg = arg
return self
n = NewTest()
'''
astng = builder.string_build(code, __name__, __file__)
self.assertRaises(InferenceError, list, astng['NewTest'].igetattr('arg'))
n = astng['n'].infer().next()
infered = list(n.igetattr('arg'))
self.assertEqual(len(infered), 1, infered)
def test_two_parents_from_same_module(self):
code = '''
from data import nonregr
class Xxx(nonregr.Aaa, nonregr.Ccc):
"doc"
'''
astng = builder.string_build(code, __name__, __file__)
parents = list(astng['Xxx'].ancestors())
self.assertEqual(len(parents), 3, parents) # Aaa, Ccc, object
if __name__ == '__main__':
unittest_main()
logilab-astng-0.24.3/ChangeLog 0000644 0000151 0000155 00000035562 12133517265 015435 0 ustar narval narval Change log for the astng package
================================
2013-04-16 -- 0.24.3
* #124360 [py3.3]: Don't crash on 'yield from' nodes
* #123062 [pylint-brain]: Use correct names for keywords for urlparse
* #123056 [pylint-brain]: Add missing methods for hashlib
* #123068: Fix inference for generator methods to correctly handle yields
in lambdas.
* #123068: Make sure .as_string() returns valid code for yields in
expressions.
* #47957: Set literals are now correctly treated as inference leaves.
* #123074: Add support for inference of subscript operations on dict
literals.
2013-02-27 -- 0.24.2
* pylint-brain: more subprocess.Popen faking (see #46273)
* #109562 [jython]: java modules have no __doc__, causing crash
* #120646 [py3]: fix for python3.3 _ast changes which may cause crash
* #109988 [py3]: test fixes
2012-10-05 -- 0.24.1
* #106191: fix __future__ absolute import w/ From node
* #50395: fix function fromlineno when some decorator is splited on
multiple lines (patch by Mark Gius)
* #92362: fix pyreverse crash on relative import
* #104041: fix crash 'module object has no file_encoding attribute'
* #4294 (pylint-brain): bad inference on mechanize.Browser.open
* #46273 (pylint-brain): bad inference subprocess.Popen.communicate
2012-07-18 -- 0.24.0
* include pylint brain extension, describing some stuff not properly understood until then.
(#100013, #53049, #23986, #72355)
* #99583: fix raw_building.object_build for pypy implementation
* use `open` rather than `file` in scoped_nodes as 2to3 miss it
2011-12-08 -- 0.23.1
* #62295: avoid "OSError: Too many open files" by moving
.file_stream as a Module property opening the file only when needed
* Lambda nodes should have a `name` attribute
* only call transformers if modname specified
2011-10-07 -- 0.23.0
* #77187: ancestor() only returns the first class when inheriting
from two classes coming from the same module
* #76159: putting module's parent directory on the path causes problems
linting when file names clash
* #74746: should return empty module when __main__ is imported (patch by
google)
* #74748: getitem protocal return constant value instead of a Const node
(patch by google)
* #77188: support lgc.decorators.classproperty
* #77253: provide a way for user code to register astng "transformers"
using manager.register_transformer(callable) where callable will be
called after an astng has been built and given the related module node
as argument
2011-07-18 -- 0.22.0
* added column offset information on nodes (patch by fawce)
* #70497: Crash on AttributeError: 'NoneType' object has no attribute '_infer_name'
* #70381: IndendationError in import causes crash
* #70565: absolute imports treated as relative (patch by Jacek Konieczny)
* #70494: fix file encoding detection with python2.x
* py3k: __builtin__ module renamed to builtins, we should consider this to properly
build ast for builtin objects
2011-01-11 -- 0.21.1
* python3: handle file encoding; fix a lot of tests
* fix #52006: "True" and "False" can be assigned as variable in Python2x
* fix #8847: pylint doesn't understand function attributes at all
* fix #8774: iterator / generator / next method
* fix bad building of ast from living object w/ container classes
(eg dict, set, list, tuple): contained elements should be turned to
ast as well (not doing it will much probably cause crash later)
* somewhat fix #57299 and other similar issue: Exception when
trying to validate file using PyQt's PyQt4.QtCore module: we can't
do much about it but at least catch such exception to avoid crash
2010-11-15 -- 0.21.0
* python3.x: first python3.x release
* fix #37105: Crash on AttributeError: 'NoneType' object has no attribute '_infer_name'
* python2.4: drop python < 2.5 support
2010-10-27 -- 0.20.4
* fix #37868 #37665 #33638 #37909: import problems with absolute_import_activated
* fix #8969: false positive when importing from zip-safe eggs
* fix #46131: minimal class decorator support
* minimal python2.7 support (dict and set comprehension)
* important progress on Py3k compatibility
2010-09-28 -- 0.20.3
* restored python 2.3 compatibility
* fix #45959: AttributeError: 'NoneType' object has no attribute 'frame', due
to handling of __class__ when importing from living object (because of missing
source code or C-compiled object)
2010-09-10 -- 0.20.2
* fix astng building bug: we've to set module.package flag at the node
creation time otherwise we'll miss this information when infering relative
import during the build process (this should fix for instance some problems
with numpy)
* added __subclasses__ to special class attribute
* fix Class.interfaces so that no InferenceError raised on empty __implements__
* yield YES on multiplication of tuple/list with non valid operand
2010-05-11 -- 0.20.1
* fix licensing to LGPL
* add ALL_NODES_CLASSES constant to nodes module
* nodes redirection cleanup (possible since refactoring)
* bug fix for python < 2.5: add Delete node on Subscript nodes if we are in a
del context
2010-03-22 -- 0.20.0
* fix #20464: raises “TypeError: '_Yes' object is not iterable†on list inference
* fix #19882: pylint hangs
* fix #20759: crash on pyreverse UNARY_OP_METHOD KeyError '~'
* fix #20760: crash on pyreverse : AttributeError: 'Subscript'
object has no attribute 'infer_lhs'
* fix #21980: [Python-modules-team] Bug#573229 : Pylint hangs;
improving the cache yields a speed improvement on big projects
* major refactoring: rebuild the tree instead of modify / monkey patching
* fix #19641: "maximum recursion depth exceeded" messages w/ python 2.6
this was introduced by a refactoring
* Ned Batchelder patch to properly import eggs with Windows line
endings. This fixes a problem with pylint not being able to
import setuptools.
* Winfried Plapper patches fixing .op attribute value for AugAssign nodes,
visit_ifexp in nodes_as_string
* Edward K. Ream / Tom Fleck patch closes #19641 (maximum recursion depth
exceeded" messages w/ python 2.6), see https://bugs.launchpad.net/pylint/+bug/456870
2009-12-18 -- 0.19.3
* fix name error making 0.19.2 almost useless
2009-12-18 -- 0.19.2
* fix #18773: inference bug on class member (due to bad handling of instance
/ class nodes "bounded" to method calls)
* fix #9515: strange message for non-class "Class baz has no egg member" (due to
bad inference of function call)
* fix #18953: inference fails with augmented assignment (special case for augmented
assignement in infer_ass method)
* fix #13944: false positive for class/instance attributes (Instance.getattr
should return assign nodes on instance classes as well as instance.
* include spelling fixes provided by Dotan Barak
2009-08-27 -- 0.19.1
* fix #8771: crash on yield expression
* fix #10024: line numbering bug with try/except/finally
* fix #10020: when building from living object, __name__ may be None
* fix #9891: help(logilab.astng) throws TypeError
* fix #9588: false positive E1101 for augmented assignment
2009-03-25 -- 0.19.0
* fixed python 2.6 issue (tests ok w/ 2.4, 2.5, 2.6. Anyone using 2.2 / 2.3
to tell us if it works?)
* some understanding of the __builtin__.property decorator
* inference: introduce UnboundMethod / rename InstanceMethod to BoundMethod
2009-03-19 -- 0.18.0
* major api / tree structure changes to make it works with compiler *and*
python >= 2.5 _ast module
* cleanup and refactoring on the way
2008-11-19 -- 0.17.4
* fix #6015: filter statements bug triggering W0631 false positive in pylint
* fix #5571: Function.is_method() should return False on module level
functions decorated by staticmethod/classmethod (avoid some crash in pylint)
* fix #5010: understand python 2.5 explicit relative imports
2008-09-10 -- 0.17.3
* fix #5889: astng crash on certain pyreverse projects
* fix bug w/ loop assignment in .lookup
* apply Maarten patch fixing a crash on TryFinalaly.block_range and fixing
'else'/'final' block line detection
2008-01-14 -- 0.17.2
* "with" statement support, patch provided by Brian Hawthorne
* fixed recursion arguments in nodes_of_class method as notified by
Dave Borowitz
* new InstanceMethod node introduced to wrap bound method (e.g. Function
node), patch provided by Dave Borowitz
2007-06-07 -- 0.17.1
* fix #3651: crash when callable as default arg
* fix #3670: subscription inference crash in some cases
* fix #3673: Lambda instance has no attribute 'pytype'
* fix crash with chained "import as"
* fix crash on numpy
* fix potential InfiniteRecursion error with builtin objects
* include patch from Marien Zwart fixing some test / py 2.5
* be more error resilient when accessing living objects from external
code in the manager
2007-02-22 -- 0.17.0
* api change to be able to infer using a context (used to infer function call
result only for now)
* slightly better inference on astng built from living object by trying to infer
dummy nodes (able to infer 'help' builtin for instance)
* external attribute definition support
* basic math operation inference
* new pytype method on possibly inferred node (e.g. module, classes, const...)
* fix a living object astng building bug, which was making "open" uninferable
* fix lookup of name in method bug (#3289)
* fix decorator lookup bug (#3261)
2006-11-23 -- 0.16.3
* enhance inference for the subscription notation (motivated by a patch from Amaury)
and for unary sub/add
2006-11-15 -- 0.16.2
* grrr, fixed python 2.3 incompatibility introduced by generator expression
scope handling
* upgrade to avoid warnings with logilab-common 0.21.0 (on which now
depends so)
* backported astutils module from logilab-common
2006-09-25 -- 0.16.1
* python 2.5 support, patch provided by Marien Zwart
* fix [Class|Module].block_range method (this fixes pylint's inline
disabling of messages on classes/modules)
* handle class.__bases__ and class.__mro__ (proper metaclass handling
still needed though)
* drop python2.2 support: remove code that was working around python2.2
* fixed generator expression scope bug
* patch transformer to extract correct line information
2006-04-19 -- 0.16.0
* fix living object building to consider classes such as property as
a class instead of a data descriptor
* fix multiple assignment inference which was discarding some solutions
* added some line manipulation methods to handle pylint's block messages
control feature (Node.last_source_line(), None.block_range(lineno)
2006-03-10 -- 0.15.1
* fix avoiding to load everything from living objects... Thanks Amaury!
* fix a possible NameError in Instance.infer_call_result
2006-03-06 -- 0.15.0
* fix possible infinite recursion on global statements (close #10342)
and in various other cases...
* fix locals/globals interactions when the global statement is used
(close #10434)
* multiple inference related bug fixes
* associate List, Tuple and Dict and Const nodes to their respective
classes
* new .ass_type method on assignment related node, returning the
assignment type node (Assign, For, ListCompFor, GenExprFor,
TryExcept)
* more API refactoring... .resolve method has disappeared, now you
have .ilookup on every nodes and .getattr/.igetattr on node
supporting the attribute protocol
* introduced a YES object that may be returned when there is ambiguity
on an inference path (typically function call when we don't know
arguments value)
* builder try to instantiate builtin exceptions subclasses to get their
instance attribute
2006-01-10 -- 0.14.0
* some major inference improvements and refactoring ! The drawback is
the introduction of some non backward compatible change in the API
but it's imho much cleaner and powerful now :)
* new boolean property .newstyle on Class nodes (implements #10073)
* new .import_module method on Module node to help in .resolve
refactoring
* .instance_attrs has list of assignments to instance attribute
dictionary as value instead of one
* added missing GenExprIf and GenExprInner nodes, and implements
as_string for each generator expression related nodes
* specifically catch KeyboardInterrupt to reraise it in some places
* fix so that module names are always absolute
* fix .resolve on package where a subpackage is imported in the
__init__ file
* fix a bug regarding construction of Function node from living object
with earlier version of python 2.4
* fix a NameError on Import and From self_resolve method
* fix a bug occurring when building an astng from a living object with
a property
* lint fixes
2005-11-07 -- 0.13.1
* fix bug on building from living module the same object in
encountered more than once time (e.g. builtins.object) (close #10069)
* fix bug in Class.ancestors() regarding inner classes (close #10072)
* fix .self_resolve() on From and Module nodes to handle package
precedence over module (close #10066)
* locals dict for package contains __path__ definition (close #10065)
* astng provide GenExpr and GenExprFor nodes with python >= 2.4
(close #10063)
* fix python2.2 compatibility (close #9922)
* link .__contains__ to .has_key on scoped node to speed up execution
* remove no more necessary .module_object() method on From and Module
nodes
* normalize parser.ParserError to SyntaxError with python 2.2
2005-10-21 -- 0.13.0
* .locals and .globals on scoped node handle now a list of references
to each assignment statements instead of a single reference to the
first assignment statement.
* fix bug with manager.astng_from_module_name when a context file is
given (notably fix ZODB 3.4 crash with pylint/pyreverse)
* fix Compare.as_string method
* fix bug with lambda object missing the "type" attribute
* some minor refactoring
* This package has been extracted from the logilab-common package, which
will be kept for some time for backward compatibility but will no
longer be maintained (this explains that this package is starting with
the 0.13 version number, since the fork occurs with the version
released in logilab-common 0.12).
logilab-astng-0.24.3/brain/ 0000775 0000151 0000155 00000000000 12133517272 014743 5 ustar narval narval logilab-astng-0.24.3/brain/py2stdlib.py 0000644 0000151 0000155 00000010204 12133517265 017226 0 ustar narval narval """ASTNG hooks for the Python 2 standard library.
Currently help understanding of :
* hashlib.md5 and hashlib.sha1
"""
from logilab.astng import MANAGER
from logilab.astng.builder import ASTNGBuilder
MODULE_TRANSFORMS = {}
def hashlib_transform(module):
fake = ASTNGBuilder(MANAGER).string_build('''
class md5(object):
def __init__(self, value): pass
def digest():
return u''
def update(self, value): pass
def hexdigest(self):
return u''
class sha1(object):
def __init__(self, value): pass
def digest():
return u''
def update(self, value): pass
def hexdigest(self):
return u''
''')
for hashfunc in ('sha1', 'md5'):
module.locals[hashfunc] = fake.locals[hashfunc]
def collections_transform(module):
fake = ASTNGBuilder(MANAGER).string_build('''
class defaultdict(dict):
default_factory = None
def __missing__(self, key): pass
class deque(object):
maxlen = 0
def __init__(iterable=None, maxlen=None): pass
def append(self, x): pass
def appendleft(self, x): pass
def clear(self): pass
def count(self, x): return 0
def extend(self, iterable): pass
def extendleft(self, iterable): pass
def pop(self): pass
def popleft(self): pass
def remove(self, value): pass
def reverse(self): pass
def rotate(self, n): pass
''')
for klass in ('deque', 'defaultdict'):
module.locals[klass] = fake.locals[klass]
def pkg_resources_transform(module):
fake = ASTNGBuilder(MANAGER).string_build('''
def resource_exists(package_or_requirement, resource_name):
pass
def resource_isdir(package_or_requirement, resource_name):
pass
def resource_filename(package_or_requirement, resource_name):
pass
def resource_stream(package_or_requirement, resource_name):
pass
def resource_string(package_or_requirement, resource_name):
pass
def resource_listdir(package_or_requirement, resource_name):
pass
def extraction_error():
pass
def get_cache_path(archive_name, names=()):
pass
def postprocess(tempname, filename):
pass
def set_extraction_path(path):
pass
def cleanup_resources(force=False):
pass
''')
for func_name, func in fake.locals.items():
module.locals[func_name] = func
def urlparse_transform(module):
fake = ASTNGBuilder(MANAGER).string_build('''
def urlparse(url, scheme='', allow_fragments=True):
return ParseResult()
class ParseResult(object):
def __init__(self):
self.scheme = ''
self.netloc = ''
self.path = ''
self.params = ''
self.query = ''
self.fragment = ''
self.username = None
self.password = None
self.hostname = None
self.port = None
def geturl(self):
return ''
''')
for func_name, func in fake.locals.items():
module.locals[func_name] = func
def subprocess_transform(module):
fake = ASTNGBuilder(MANAGER).string_build('''
class Popen(object):
returncode = pid = 0
stdin = stdout = stderr = file()
def __init__(self, args, bufsize=0, executable=None,
stdin=None, stdout=None, stderr=None,
preexec_fn=None, close_fds=False, shell=False,
cwd=None, env=None, universal_newlines=False,
startupinfo=None, creationflags=0):
pass
def communicate(self, input=None):
return ('string', 'string')
def wait(self):
return self.returncode
def poll(self):
return self.returncode
def send_signal(self, signal):
pass
def terminate(self):
pass
def kill(self):
pass
''')
for func_name, func in fake.locals.items():
module.locals[func_name] = func
MODULE_TRANSFORMS['hashlib'] = hashlib_transform
MODULE_TRANSFORMS['collections'] = collections_transform
MODULE_TRANSFORMS['pkg_resources'] = pkg_resources_transform
MODULE_TRANSFORMS['urlparse'] = urlparse_transform
MODULE_TRANSFORMS['subprocess'] = subprocess_transform
def transform(module):
try:
tr = MODULE_TRANSFORMS[module.name]
except KeyError:
pass
else:
tr(module)
from logilab.astng import MANAGER
MANAGER.register_transformer(transform)
logilab-astng-0.24.3/brain/py2qt4.py 0000644 0000151 0000155 00000001023 12133517265 016454 0 ustar narval narval """ASTNG hooks for the Python 2 qt4 module.
Currently help understanding of :
* PyQT4.QtCore
"""
from logilab.astng import MANAGER
from logilab.astng.builder import ASTNGBuilder
def pyqt4_qtcore_transform(module):
fake = ASTNGBuilder(MANAGER).string_build('''
def SIGNAL(signal_name): pass
class QObject(object):
def emit(self, signal): pass
''')
for klass in ('QObject',):
module.locals[klass] = fake.locals[klass]
import py2stdlib
py2stdlib.MODULE_TRANSFORMS['PyQt4.QtCore'] = pyqt4_qtcore_transform
logilab-astng-0.24.3/brain/py2mechanize.py 0000644 0000151 0000155 00000001034 12133517265 017711 0 ustar narval narval from logilab.astng import MANAGER
from logilab.astng.builder import ASTNGBuilder
def mechanize_transform(module):
fake = ASTNGBuilder(MANAGER).string_build('''
class Browser(object):
def open(self, url, data=None, timeout=None):
return None
def open_novisit(self, url, data=None, timeout=None):
return None
def open_local_file(self, filename):
return None
''')
module.locals['Browser'] = fake.locals['Browser']
import py2stdlib
py2stdlib.MODULE_TRANSFORMS['mechanize'] = mechanize_transform
logilab-astng-0.24.3/nodes.py 0000644 0000151 0000155 00000005444 12133517265 015341 0 ustar narval narval # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of logilab-astng.
#
# logilab-astng is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 2.1 of the License, or (at your
# option) any later version.
#
# logilab-astng is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see .
"""
on all nodes :
.is_statement, returning true if the node should be considered as a
statement node
.root(), returning the root node of the tree (i.e. a Module)
.previous_sibling(), returning previous sibling statement node
.next_sibling(), returning next sibling statement node
.statement(), returning the first parent node marked as statement node
.frame(), returning the first node defining a new local scope (i.e.
Module, Function or Class)
.set_local(name, node), define an identifier on the first parent frame,
with the node defining it. This is used by the astng builder and should not
be used from out there.
on From and Import :
.real_name(name),
"""
__docformat__ = "restructuredtext en"
from logilab.astng.node_classes import Arguments, AssAttr, Assert, Assign, \
AssName, AugAssign, Backquote, BinOp, BoolOp, Break, CallFunc, Compare, \
Comprehension, Const, Continue, Decorators, DelAttr, DelName, Delete, \
Dict, Discard, Ellipsis, EmptyNode, ExceptHandler, Exec, ExtSlice, For, \
From, Getattr, Global, If, IfExp, Import, Index, Keyword, \
List, Name, Nonlocal, Pass, Print, Raise, Return, Set, Slice, Starred, Subscript, \
TryExcept, TryFinally, Tuple, UnaryOp, While, With, Yield, \
const_factory
from logilab.astng.scoped_nodes import Module, GenExpr, Lambda, DictComp, \
ListComp, SetComp, Function, Class
ALL_NODE_CLASSES = (
Arguments, AssAttr, Assert, Assign, AssName, AugAssign,
Backquote, BinOp, BoolOp, Break,
CallFunc, Class, Compare, Comprehension, Const, Continue,
Decorators, DelAttr, DelName, Delete,
Dict, DictComp, Discard,
Ellipsis, EmptyNode, ExceptHandler, Exec, ExtSlice,
For, From, Function,
Getattr, GenExpr, Global,
If, IfExp, Import, Index,
Keyword,
Lambda, List, ListComp,
Name, Nonlocal,
Module,
Pass, Print,
Raise, Return,
Set, SetComp, Slice, Starred, Subscript,
TryExcept, TryFinally, Tuple,
UnaryOp,
While, With,
Yield,
)
logilab-astng-0.24.3/builder.py 0000644 0000151 0000155 00000021042 12133517265 015647 0 ustar narval narval # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of logilab-astng.
#
# logilab-astng is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 2.1 of the License, or (at your
# option) any later version.
#
# logilab-astng is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see .
"""The ASTNGBuilder makes astng from living object and / or from _ast
The builder is not thread safe and can't be used to parse different sources
at the same time.
"""
__docformat__ = "restructuredtext en"
import sys
from os.path import splitext, basename, exists, abspath
from logilab.common.modutils import modpath_from_file
from logilab.astng.exceptions import ASTNGBuildingException, InferenceError
from logilab.astng.raw_building import InspectBuilder
from logilab.astng.rebuilder import TreeRebuilder
from logilab.astng.manager import ASTNGManager
from logilab.astng.bases import YES, Instance
from _ast import PyCF_ONLY_AST
def parse(string):
return compile(string, "", 'exec', PyCF_ONLY_AST)
if sys.version_info >= (3, 0):
from tokenize import detect_encoding
def open_source_file(filename):
byte_stream = open(filename, 'bU')
encoding = detect_encoding(byte_stream.readline)[0]
stream = open(filename, 'U', encoding=encoding)
try:
data = stream.read()
except UnicodeError, uex: # wrong encodingg
# detect_encoding returns utf-8 if no encoding specified
msg = 'Wrong (%s) or no encoding specified' % encoding
raise ASTNGBuildingException(msg)
return stream, encoding, data
else:
import re
_ENCODING_RGX = re.compile("\s*#+.*coding[:=]\s*([-\w.]+)")
def _guess_encoding(string):
"""get encoding from a python file as string or return None if not found
"""
# check for UTF-8 byte-order mark
if string.startswith('\xef\xbb\xbf'):
return 'UTF-8'
for line in string.split('\n', 2)[:2]:
# check for encoding declaration
match = _ENCODING_RGX.match(line)
if match is not None:
return match.group(1)
def open_source_file(filename):
"""get data for parsing a file"""
stream = open(filename, 'U')
data = stream.read()
encoding = _guess_encoding(data)
return stream, encoding, data
# ast NG builder ##############################################################
MANAGER = ASTNGManager()
class ASTNGBuilder(InspectBuilder):
"""provide astng building methods"""
rebuilder = TreeRebuilder()
def __init__(self, manager=None):
InspectBuilder.__init__(self)
self._manager = manager or MANAGER
def module_build(self, module, modname=None):
"""build an astng from a living module instance
"""
node = None
path = getattr(module, '__file__', None)
if path is not None:
path_, ext = splitext(module.__file__)
if ext in ('.py', '.pyc', '.pyo') and exists(path_ + '.py'):
node = self.file_build(path_ + '.py', modname)
if node is None:
# this is a built-in module
# get a partial representation by introspection
node = self.inspect_build(module, modname=modname, path=path)
return node
def file_build(self, path, modname=None):
"""build astng from a source code file (i.e. from an ast)
path is expected to be a python source file
"""
try:
stream, encoding, data = open_source_file(path)
except IOError, exc:
msg = 'Unable to load file %r (%s)' % (path, exc)
raise ASTNGBuildingException(msg)
except SyntaxError, exc: # py3k encoding specification error
raise ASTNGBuildingException(exc)
except LookupError, exc: # unknown encoding
raise ASTNGBuildingException(exc)
# get module name if necessary
if modname is None:
try:
modname = '.'.join(modpath_from_file(path))
except ImportError:
modname = splitext(basename(path))[0]
# build astng representation
node = self.string_build(data, modname, path)
node.file_encoding = encoding
return node
def string_build(self, data, modname='', path=None):
"""build astng from source code string and return rebuilded astng"""
module = self._data_build(data, modname, path)
self._manager.astng_cache[module.name] = module
# post tree building steps after we stored the module in the cache:
for from_node in module._from_nodes:
self.add_from_names_to_locals(from_node)
# handle delayed assattr nodes
for delayed in module._delayed_assattr:
self.delayed_assattr(delayed)
if modname:
for transformer in self._manager.transformers:
transformer(module)
return module
def _data_build(self, data, modname, path):
"""build tree node from data and add some informations"""
# this method could be wrapped with a pickle/cache function
node = parse(data + '\n')
if path is not None:
node_file = abspath(path)
else:
node_file = '>'
if modname.endswith('.__init__'):
modname = modname[:-9]
package = True
else:
package = path and path.find('__init__.py') > -1 or False
self.rebuilder.init()
module = self.rebuilder.visit_module(node, modname, package)
module.file = module.path = node_file
module._from_nodes = self.rebuilder._from_nodes
module._delayed_assattr = self.rebuilder._delayed_assattr
return module
def add_from_names_to_locals(self, node):
"""store imported names to the locals;
resort the locals if coming from a delayed node
"""
_key_func = lambda node: node.fromlineno
def sort_locals(my_list):
my_list.sort(key=_key_func)
for (name, asname) in node.names:
if name == '*':
try:
imported = node.root().import_module(node.modname)
except ASTNGBuildingException:
continue
for name in imported.wildcard_import_names():
node.parent.set_local(name, node)
sort_locals(node.parent.scope().locals[name])
else:
node.parent.set_local(asname or name, node)
sort_locals(node.parent.scope().locals[asname or name])
def delayed_assattr(self, node):
"""visit a AssAttr node -> add name to locals, handle members
definition
"""
try:
frame = node.frame()
for infered in node.expr.infer():
if infered is YES:
continue
try:
if infered.__class__ is Instance:
infered = infered._proxied
iattrs = infered.instance_attrs
elif isinstance(infered, Instance):
# Const, Tuple, ... we may be wrong, may be not, but
# anyway we don't want to pollute builtin's namespace
continue
elif infered.is_function:
iattrs = infered.instance_attrs
else:
iattrs = infered.locals
except AttributeError:
# XXX log error
#import traceback
#traceback.print_exc()
continue
values = iattrs.setdefault(node.attrname, [])
if node in values:
continue
# get assign in __init__ first XXX useful ?
if frame.name == '__init__' and values and not \
values[0].frame().name == '__init__':
values.insert(0, node)
else:
values.append(node)
except InferenceError:
pass
logilab-astng-0.24.3/README 0000644 0000151 0000155 00000003306 12133517265 014532 0 ustar narval narval ASTNG
=====
What's this ?
-------------
The aim of this module is to provide a common base representation of
python source code for projects such as pychecker, pyreverse,
pylint... Well, actually the development of this library is essentially
governed by pylint's needs.
It provides a compatible representation which comes from the `_ast` module.
It rebuilds the tree generated by the builtin _ast module by recursively
walking down the AST and building an extended ast (let's call it astng ;). The
new node classes have additional methods and attributes for different usages.
They include some support for static inference and local name scopes.
Furthermore, astng builds partial trees by inspecting living objects.
Main modules are:
* `bases`, `node_classses` and `scoped_nodes` contain the classes for the
different type of nodes of the tree.
* the `manager` contains a high level object to get astng trees from
source files and living objects. It maintains a cache of previously
constructed tree for quick access
Installation
------------
Extract the tarball, jump into the created directory and run ::
python setup.py install
For installation options, see ::
python setup.py install --help
If you have any questions, please mail the
python-project@lists.logilab.org mailing list for support. See
http://lists.logilab.org/mailman/listinfo/python-projects for
subscription information and archives.
Test
----
Tests are in the 'test' subdirectory. To launch the whole tests suite
at once, you may use the 'pytest' utility from logilab-common (simply
type 'pytest' from within this directory) or if you're running python
>= 2.7, using discover, for instance::
python -m unittest discover -p "unittest*.py"
logilab-astng-0.24.3/inference.py 0000644 0000151 0000155 00000032545 12133517265 016171 0 ustar narval narval # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of logilab-astng.
#
# logilab-astng is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 2.1 of the License, or (at your
# option) any later version.
#
# logilab-astng is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see .
"""this module contains a set of functions to handle inference on astng trees
"""
__doctype__ = "restructuredtext en"
from itertools import chain
from logilab.astng import nodes
from logilab.astng.manager import ASTNGManager
from logilab.astng.exceptions import (ASTNGError,
InferenceError, NoDefault, NotFoundError, UnresolvableName)
from logilab.astng.bases import YES, Instance, InferenceContext, \
_infer_stmts, copy_context, path_wrapper, raise_if_nothing_infered
from logilab.astng.protocols import _arguments_infer_argname
MANAGER = ASTNGManager()
class CallContext:
"""when inferring a function call, this class is used to remember values
given as argument
"""
def __init__(self, args, starargs, dstarargs):
self.args = []
self.nargs = {}
for arg in args:
if isinstance(arg, nodes.Keyword):
self.nargs[arg.arg] = arg.value
else:
self.args.append(arg)
self.starargs = starargs
self.dstarargs = dstarargs
def infer_argument(self, funcnode, name, context):
"""infer a function argument value according to the call context"""
# 1. search in named keywords
try:
return self.nargs[name].infer(context)
except KeyError:
# Function.args.args can be None in astng (means that we don't have
# information on argnames)
argindex = funcnode.args.find_argname(name)[0]
if argindex is not None:
# 2. first argument of instance/class method
if argindex == 0 and funcnode.type in ('method', 'classmethod'):
if context.boundnode is not None:
boundnode = context.boundnode
else:
# XXX can do better ?
boundnode = funcnode.parent.frame()
if funcnode.type == 'method':
if not isinstance(boundnode, Instance):
boundnode = Instance(boundnode)
return iter((boundnode,))
if funcnode.type == 'classmethod':
return iter((boundnode,))
# 2. search arg index
try:
return self.args[argindex].infer(context)
except IndexError:
pass
# 3. search in *args (.starargs)
if self.starargs is not None:
its = []
for infered in self.starargs.infer(context):
if infered is YES:
its.append((YES,))
continue
try:
its.append(infered.getitem(argindex, context).infer(context))
except (InferenceError, AttributeError):
its.append((YES,))
except (IndexError, TypeError):
continue
if its:
return chain(*its)
# 4. XXX search in **kwargs (.dstarargs)
if self.dstarargs is not None:
its = []
for infered in self.dstarargs.infer(context):
if infered is YES:
its.append((YES,))
continue
try:
its.append(infered.getitem(name, context).infer(context))
except (InferenceError, AttributeError):
its.append((YES,))
except (IndexError, TypeError):
continue
if its:
return chain(*its)
# 5. */** argument, (Tuple or Dict)
if name == funcnode.args.vararg:
return iter((nodes.const_factory(())))
if name == funcnode.args.kwarg:
return iter((nodes.const_factory({})))
# 6. return default value if any
try:
return funcnode.args.default_value(name).infer(context)
except NoDefault:
raise InferenceError(name)
# .infer method ###############################################################
def infer_end(self, context=None):
"""inference's end for node such as Module, Class, Function, Const...
"""
yield self
nodes.Module.infer = infer_end
nodes.Class.infer = infer_end
nodes.Function.infer = infer_end
nodes.Lambda.infer = infer_end
nodes.Const.infer = infer_end
nodes.List.infer = infer_end
nodes.Tuple.infer = infer_end
nodes.Dict.infer = infer_end
nodes.Set.infer = infer_end
def infer_name(self, context=None):
"""infer a Name: use name lookup rules"""
frame, stmts = self.lookup(self.name)
if not stmts:
raise UnresolvableName(self.name)
context = context.clone()
context.lookupname = self.name
return _infer_stmts(stmts, context, frame)
nodes.Name.infer = path_wrapper(infer_name)
nodes.AssName.infer_lhs = infer_name # won't work with a path wrapper
def infer_callfunc(self, context=None):
"""infer a CallFunc node by trying to guess what the function returns"""
callcontext = context.clone()
callcontext.callcontext = CallContext(self.args, self.starargs, self.kwargs)
callcontext.boundnode = None
for callee in self.func.infer(context):
if callee is YES:
yield callee
continue
try:
if hasattr(callee, 'infer_call_result'):
for infered in callee.infer_call_result(self, callcontext):
yield infered
except InferenceError:
## XXX log error ?
continue
nodes.CallFunc.infer = path_wrapper(raise_if_nothing_infered(infer_callfunc))
def infer_import(self, context=None, asname=True):
"""infer an Import node: return the imported module/object"""
name = context.lookupname
if name is None:
raise InferenceError()
if asname:
yield self.do_import_module(self.real_name(name))
else:
yield self.do_import_module(name)
nodes.Import.infer = path_wrapper(infer_import)
def infer_name_module(self, name):
context = InferenceContext()
context.lookupname = name
return self.infer(context, asname=False)
nodes.Import.infer_name_module = infer_name_module
def infer_from(self, context=None, asname=True):
"""infer a From nodes: return the imported module/object"""
name = context.lookupname
if name is None:
raise InferenceError()
if asname:
name = self.real_name(name)
module = self.do_import_module(self.modname)
try:
context = copy_context(context)
context.lookupname = name
return _infer_stmts(module.getattr(name, ignore_locals=module is self.root()), context)
except NotFoundError:
raise InferenceError(name)
nodes.From.infer = path_wrapper(infer_from)
def infer_getattr(self, context=None):
"""infer a Getattr node by using getattr on the associated object"""
#context = context.clone()
for owner in self.expr.infer(context):
if owner is YES:
yield owner
continue
try:
context.boundnode = owner
for obj in owner.igetattr(self.attrname, context):
yield obj
context.boundnode = None
except (NotFoundError, InferenceError):
context.boundnode = None
except AttributeError:
# XXX method / function
context.boundnode = None
nodes.Getattr.infer = path_wrapper(raise_if_nothing_infered(infer_getattr))
nodes.AssAttr.infer_lhs = raise_if_nothing_infered(infer_getattr) # # won't work with a path wrapper
def infer_global(self, context=None):
if context.lookupname is None:
raise InferenceError()
try:
return _infer_stmts(self.root().getattr(context.lookupname), context)
except NotFoundError:
raise InferenceError()
nodes.Global.infer = path_wrapper(infer_global)
def infer_subscript(self, context=None):
"""infer simple subscription such as [1,2,3][0] or (1,2,3)[-1]"""
value = self.value.infer(context).next()
if value is YES:
yield YES
return
index = self.slice.infer(context).next()
if index is YES:
yield YES
return
if isinstance(index, nodes.Const):
try:
assigned = value.getitem(index.value, context)
except AttributeError:
raise InferenceError()
except (IndexError, TypeError):
yield YES
return
for infered in assigned.infer(context):
yield infered
else:
raise InferenceError()
nodes.Subscript.infer = path_wrapper(infer_subscript)
nodes.Subscript.infer_lhs = raise_if_nothing_infered(infer_subscript)
UNARY_OP_METHOD = {'+': '__pos__',
'-': '__neg__',
'~': '__invert__',
'not': None, # XXX not '__nonzero__'
}
def infer_unaryop(self, context=None):
for operand in self.operand.infer(context):
try:
yield operand.infer_unary_op(self.op)
except TypeError:
continue
except AttributeError:
meth = UNARY_OP_METHOD[self.op]
if meth is None:
yield YES
else:
try:
# XXX just suppose if the type implement meth, returned type
# will be the same
operand.getattr(meth)
yield operand
except GeneratorExit:
raise
except:
yield YES
nodes.UnaryOp.infer = path_wrapper(infer_unaryop)
BIN_OP_METHOD = {'+': '__add__',
'-': '__sub__',
'/': '__div__',
'//': '__floordiv__',
'*': '__mul__',
'**': '__power__',
'%': '__mod__',
'&': '__and__',
'|': '__or__',
'^': '__xor__',
'<<': '__lshift__',
'>>': '__rshift__',
}
def _infer_binop(operator, operand1, operand2, context, failures=None):
if operand1 is YES:
yield operand1
return
try:
for valnode in operand1.infer_binary_op(operator, operand2, context):
yield valnode
except AttributeError:
try:
# XXX just suppose if the type implement meth, returned type
# will be the same
operand1.getattr(BIN_OP_METHOD[operator])
yield operand1
except:
if failures is None:
yield YES
else:
failures.append(operand1)
def infer_binop(self, context=None):
failures = []
for lhs in self.left.infer(context):
for val in _infer_binop(self.op, lhs, self.right, context, failures):
yield val
for lhs in failures:
for rhs in self.right.infer(context):
for val in _infer_binop(self.op, rhs, lhs, context):
yield val
nodes.BinOp.infer = path_wrapper(infer_binop)
def infer_arguments(self, context=None):
name = context.lookupname
if name is None:
raise InferenceError()
return _arguments_infer_argname(self, name, context)
nodes.Arguments.infer = infer_arguments
def infer_ass(self, context=None):
"""infer a AssName/AssAttr: need to inspect the RHS part of the
assign node
"""
stmt = self.statement()
if isinstance(stmt, nodes.AugAssign):
return stmt.infer(context)
stmts = list(self.assigned_stmts(context=context))
return _infer_stmts(stmts, context)
nodes.AssName.infer = path_wrapper(infer_ass)
nodes.AssAttr.infer = path_wrapper(infer_ass)
def infer_augassign(self, context=None):
failures = []
for lhs in self.target.infer_lhs(context):
for val in _infer_binop(self.op, lhs, self.value, context, failures):
yield val
for lhs in failures:
for rhs in self.value.infer(context):
for val in _infer_binop(self.op, rhs, lhs, context):
yield val
nodes.AugAssign.infer = path_wrapper(infer_augassign)
# no infer method on DelName and DelAttr (expected InferenceError)
def infer_empty_node(self, context=None):
if not self.has_underlying_object():
yield YES
else:
try:
for infered in MANAGER.infer_astng_from_something(self.object,
context=context):
yield infered
except ASTNGError:
yield YES
nodes.EmptyNode.infer = path_wrapper(infer_empty_node)
def infer_index(self, context=None):
return self.value.infer(context)
nodes.Index.infer = infer_index
logilab-astng-0.24.3/protocols.py 0000644 0000151 0000155 00000027324 12133517265 016256 0 ustar narval narval # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of logilab-astng.
#
# logilab-astng is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 2.1 of the License, or (at your
# option) any later version.
#
# logilab-astng is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see .
"""this module contains a set of functions to handle python protocols for nodes
where it makes sense.
"""
__doctype__ = "restructuredtext en"
from logilab.astng.exceptions import InferenceError, NoDefault
from logilab.astng.node_classes import unpack_infer
from logilab.astng.bases import copy_context, \
raise_if_nothing_infered, yes_if_nothing_infered, Instance, YES
from logilab.astng.nodes import const_factory
from logilab.astng import nodes
# unary operations ############################################################
def tl_infer_unary_op(self, operator):
if operator == 'not':
return const_factory(not bool(self.elts))
raise TypeError() # XXX log unsupported operation
nodes.Tuple.infer_unary_op = tl_infer_unary_op
nodes.List.infer_unary_op = tl_infer_unary_op
def dict_infer_unary_op(self, operator):
if operator == 'not':
return const_factory(not bool(self.items))
raise TypeError() # XXX log unsupported operation
nodes.Dict.infer_unary_op = dict_infer_unary_op
def const_infer_unary_op(self, operator):
if operator == 'not':
return const_factory(not self.value)
# XXX log potentially raised TypeError
elif operator == '+':
return const_factory(+self.value)
else: # operator == '-':
return const_factory(-self.value)
nodes.Const.infer_unary_op = const_infer_unary_op
# binary operations ###########################################################
BIN_OP_IMPL = {'+': lambda a, b: a + b,
'-': lambda a, b: a - b,
'/': lambda a, b: a / b,
'//': lambda a, b: a // b,
'*': lambda a, b: a * b,
'**': lambda a, b: a ** b,
'%': lambda a, b: a % b,
'&': lambda a, b: a & b,
'|': lambda a, b: a | b,
'^': lambda a, b: a ^ b,
'<<': lambda a, b: a << b,
'>>': lambda a, b: a >> b,
}
for key, impl in BIN_OP_IMPL.items():
BIN_OP_IMPL[key+'='] = impl
def const_infer_binary_op(self, operator, other, context):
for other in other.infer(context):
if isinstance(other, nodes.Const):
try:
impl = BIN_OP_IMPL[operator]
try:
yield const_factory(impl(self.value, other.value))
except Exception:
# ArithmeticError is not enough: float >> float is a TypeError
# TODO : let pylint know about the problem
pass
except TypeError:
# XXX log TypeError
continue
elif other is YES:
yield other
else:
try:
for val in other.infer_binary_op(operator, self, context):
yield val
except AttributeError:
yield YES
nodes.Const.infer_binary_op = yes_if_nothing_infered(const_infer_binary_op)
def tl_infer_binary_op(self, operator, other, context):
for other in other.infer(context):
if isinstance(other, self.__class__) and operator == '+':
node = self.__class__()
elts = [n for elt in self.elts for n in elt.infer(context)
if not n is YES]
elts += [n for elt in other.elts for n in elt.infer(context)
if not n is YES]
node.elts = elts
yield node
elif isinstance(other, nodes.Const) and operator == '*':
if not isinstance(other.value, int):
yield YES
continue
node = self.__class__()
elts = [n for elt in self.elts for n in elt.infer(context)
if not n is YES] * other.value
node.elts = elts
yield node
elif isinstance(other, Instance) and not isinstance(other, nodes.Const):
yield YES
# XXX else log TypeError
nodes.Tuple.infer_binary_op = yes_if_nothing_infered(tl_infer_binary_op)
nodes.List.infer_binary_op = yes_if_nothing_infered(tl_infer_binary_op)
def dict_infer_binary_op(self, operator, other, context):
for other in other.infer(context):
if isinstance(other, Instance) and isinstance(other._proxied, nodes.Class):
yield YES
# XXX else log TypeError
nodes.Dict.infer_binary_op = yes_if_nothing_infered(dict_infer_binary_op)
# assignment ##################################################################
"""the assigned_stmts method is responsible to return the assigned statement
(e.g. not inferred) according to the assignment type.
The `asspath` argument is used to record the lhs path of the original node.
For instance if we want assigned statements for 'c' in 'a, (b,c)', asspath
will be [1, 1] once arrived to the Assign node.
The `context` argument is the current inference context which should be given
to any intermediary inference necessary.
"""
def _resolve_looppart(parts, asspath, context):
"""recursive function to resolve multiple assignments on loops"""
asspath = asspath[:]
index = asspath.pop(0)
for part in parts:
if part is YES:
continue
# XXX handle __iter__ and log potentially detected errors
if not hasattr(part, 'itered'):
continue
try:
itered = part.itered()
except TypeError:
continue # XXX log error
for stmt in itered:
try:
assigned = stmt.getitem(index, context)
except (AttributeError, IndexError):
continue
except TypeError, exc: # stmt is unsubscriptable Const
continue
if not asspath:
# we achieved to resolved the assignment path,
# don't infer the last part
yield assigned
elif assigned is YES:
break
else:
# we are not yet on the last part of the path
# search on each possibly inferred value
try:
for infered in _resolve_looppart(assigned.infer(context),
asspath, context):
yield infered
except InferenceError:
break
def for_assigned_stmts(self, node, context=None, asspath=None):
if asspath is None:
for lst in self.iter.infer(context):
if isinstance(lst, (nodes.Tuple, nodes.List)):
for item in lst.elts:
yield item
else:
for infered in _resolve_looppart(self.iter.infer(context),
asspath, context):
yield infered
nodes.For.assigned_stmts = raise_if_nothing_infered(for_assigned_stmts)
nodes.Comprehension.assigned_stmts = raise_if_nothing_infered(for_assigned_stmts)
def mulass_assigned_stmts(self, node, context=None, asspath=None):
if asspath is None:
asspath = []
asspath.insert(0, self.elts.index(node))
return self.parent.assigned_stmts(self, context, asspath)
nodes.Tuple.assigned_stmts = mulass_assigned_stmts
nodes.List.assigned_stmts = mulass_assigned_stmts
def assend_assigned_stmts(self, context=None):
return self.parent.assigned_stmts(self, context=context)
nodes.AssName.assigned_stmts = assend_assigned_stmts
nodes.AssAttr.assigned_stmts = assend_assigned_stmts
def _arguments_infer_argname(self, name, context):
# arguments information may be missing, in which case we can't do anything
# more
if not (self.args or self.vararg or self.kwarg):
yield YES
return
# first argument of instance/class method
if self.args and getattr(self.args[0], 'name', None) == name:
functype = self.parent.type
if functype == 'method':
yield Instance(self.parent.parent.frame())
return
if functype == 'classmethod':
yield self.parent.parent.frame()
return
if name == self.vararg:
yield const_factory(())
return
if name == self.kwarg:
yield const_factory({})
return
# if there is a default value, yield it. And then yield YES to reflect
# we can't guess given argument value
try:
context = copy_context(context)
for infered in self.default_value(name).infer(context):
yield infered
yield YES
except NoDefault:
yield YES
def arguments_assigned_stmts(self, node, context, asspath=None):
if context.callcontext:
# reset call context/name
callcontext = context.callcontext
context = copy_context(context)
context.callcontext = None
for infered in callcontext.infer_argument(self.parent, node.name, context):
yield infered
return
for infered in _arguments_infer_argname(self, node.name, context):
yield infered
nodes.Arguments.assigned_stmts = arguments_assigned_stmts
def assign_assigned_stmts(self, node, context=None, asspath=None):
if not asspath:
yield self.value
return
for infered in _resolve_asspart(self.value.infer(context), asspath, context):
yield infered
nodes.Assign.assigned_stmts = raise_if_nothing_infered(assign_assigned_stmts)
nodes.AugAssign.assigned_stmts = raise_if_nothing_infered(assign_assigned_stmts)
def _resolve_asspart(parts, asspath, context):
"""recursive function to resolve multiple assignments"""
asspath = asspath[:]
index = asspath.pop(0)
for part in parts:
if hasattr(part, 'getitem'):
try:
assigned = part.getitem(index, context)
# XXX raise a specific exception to avoid potential hiding of
# unexpected exception ?
except (TypeError, IndexError):
return
if not asspath:
# we achieved to resolved the assignment path, don't infer the
# last part
yield assigned
elif assigned is YES:
return
else:
# we are not yet on the last part of the path search on each
# possibly inferred value
try:
for infered in _resolve_asspart(assigned.infer(context),
asspath, context):
yield infered
except InferenceError:
return
def excepthandler_assigned_stmts(self, node, context=None, asspath=None):
for assigned in unpack_infer(self.type):
if isinstance(assigned, nodes.Class):
assigned = Instance(assigned)
yield assigned
nodes.ExceptHandler.assigned_stmts = raise_if_nothing_infered(excepthandler_assigned_stmts)
def with_assigned_stmts(self, node, context=None, asspath=None):
if asspath is None:
for lst in self.vars.infer(context):
if isinstance(lst, (nodes.Tuple, nodes.List)):
for item in lst.nodes:
yield item
nodes.With.assigned_stmts = raise_if_nothing_infered(with_assigned_stmts)
logilab-astng-0.24.3/bases.py 0000644 0000151 0000155 00000046415 12133517265 015331 0 ustar narval narval # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of logilab-astng.
#
# logilab-astng is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 2.1 of the License, or (at your
# option) any later version.
#
# logilab-astng is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see .
"""This module contains base classes and functions for the nodes and some
inference utils.
"""
__docformat__ = "restructuredtext en"
import sys
from contextlib import contextmanager
from logilab.astng.exceptions import (InferenceError, ASTNGError,
NotFoundError, UnresolvableName)
if sys.version_info >= (3, 0):
BUILTINS = 'builtins'
else:
BUILTINS = '__builtin__'
class Proxy(object):
"""a simple proxy object"""
_proxied = None # proxied object may be set by class or by instance
def __init__(self, proxied=None):
if proxied is not None:
self._proxied = proxied
def __getattr__(self, name):
if name == '_proxied':
return getattr(self.__class__, '_proxied')
if name in self.__dict__:
return self.__dict__[name]
return getattr(self._proxied, name)
def infer(self, context=None):
yield self
# Inference ##################################################################
class InferenceContext(object):
__slots__ = ('path', 'lookupname', 'callcontext', 'boundnode')
def __init__(self, path=None):
if path is None:
self.path = set()
else:
self.path = path
self.lookupname = None
self.callcontext = None
self.boundnode = None
def push(self, node):
name = self.lookupname
if (node, name) in self.path:
raise StopIteration()
self.path.add( (node, name) )
def clone(self):
# XXX copy lookupname/callcontext ?
clone = InferenceContext(self.path)
clone.callcontext = self.callcontext
clone.boundnode = self.boundnode
return clone
@contextmanager
def restore_path(self):
path = set(self.path)
yield
self.path = path
def copy_context(context):
if context is not None:
return context.clone()
else:
return InferenceContext()
def _infer_stmts(stmts, context, frame=None):
"""return an iterator on statements inferred by each statement in
"""
stmt = None
infered = False
if context is not None:
name = context.lookupname
context = context.clone()
else:
name = None
context = InferenceContext()
for stmt in stmts:
if stmt is YES:
yield stmt
infered = True
continue
context.lookupname = stmt._infer_name(frame, name)
try:
for infered in stmt.infer(context):
yield infered
infered = True
except UnresolvableName:
continue
except InferenceError:
yield YES
infered = True
if not infered:
raise InferenceError(str(stmt))
# special inference objects (e.g. may be returned as nodes by .infer()) #######
class _Yes(object):
"""a yes object"""
def __repr__(self):
return 'YES'
def __getattribute__(self, name):
if name == 'next':
raise AttributeError('next method should not be called')
if name.startswith('__') and name.endswith('__'):
# to avoid inspection pb
return super(_Yes, self).__getattribute__(name)
return self
def __call__(self, *args, **kwargs):
return self
YES = _Yes()
class Instance(Proxy):
"""a special node representing a class instance"""
def getattr(self, name, context=None, lookupclass=True):
try:
values = self._proxied.instance_attr(name, context)
except NotFoundError:
if name == '__class__':
return [self._proxied]
if lookupclass:
# class attributes not available through the instance
# unless they are explicitly defined
if name in ('__name__', '__bases__', '__mro__', '__subclasses__'):
return self._proxied.local_attr(name)
return self._proxied.getattr(name, context)
raise NotFoundError(name)
# since we've no context information, return matching class members as
# well
if lookupclass:
try:
return values + self._proxied.getattr(name, context)
except NotFoundError:
pass
return values
def igetattr(self, name, context=None):
"""inferred getattr"""
try:
# XXX frame should be self._proxied, or not ?
get_attr = self.getattr(name, context, lookupclass=False)
return _infer_stmts(self._wrap_attr(get_attr, context), context,
frame=self)
except NotFoundError:
try:
# fallback to class'igetattr since it has some logic to handle
# descriptors
return self._wrap_attr(self._proxied.igetattr(name, context),
context)
except NotFoundError:
raise InferenceError(name)
def _wrap_attr(self, attrs, context=None):
"""wrap bound methods of attrs in a InstanceMethod proxies"""
for attr in attrs:
if isinstance(attr, UnboundMethod):
if BUILTINS + '.property' in attr.decoratornames():
for infered in attr.infer_call_result(self, context):
yield infered
else:
yield BoundMethod(attr, self)
else:
yield attr
def infer_call_result(self, caller, context=None):
"""infer what a class instance is returning when called"""
infered = False
for node in self._proxied.igetattr('__call__', context):
for res in node.infer_call_result(caller, context):
infered = True
yield res
if not infered:
raise InferenceError()
def __repr__(self):
return '' % (self._proxied.root().name,
self._proxied.name,
id(self))
def __str__(self):
return 'Instance of %s.%s' % (self._proxied.root().name,
self._proxied.name)
def callable(self):
try:
self._proxied.getattr('__call__')
return True
except NotFoundError:
return False
def pytype(self):
return self._proxied.qname()
def display_type(self):
return 'Instance of'
class UnboundMethod(Proxy):
"""a special node representing a method not bound to an instance"""
def __repr__(self):
frame = self._proxied.parent.frame()
return '<%s %s of %s at 0x%s' % (self.__class__.__name__,
self._proxied.name,
frame.qname(), id(self))
def is_bound(self):
return False
def getattr(self, name, context=None):
if name == 'im_func':
return [self._proxied]
return super(UnboundMethod, self).getattr(name, context)
def igetattr(self, name, context=None):
if name == 'im_func':
return iter((self._proxied,))
return super(UnboundMethod, self).igetattr(name, context)
def infer_call_result(self, caller, context):
# If we're unbound method __new__ of builtin object, the result is an
# instance of the class given as first argument.
if (self._proxied.name == '__new__' and
self._proxied.parent.frame().qname() == '%s.object' % BUILTINS):
return (x is YES and x or Instance(x) for x in caller.args[0].infer())
return self._proxied.infer_call_result(caller, context)
class BoundMethod(UnboundMethod):
"""a special node representing a method bound to an instance"""
def __init__(self, proxy, bound):
UnboundMethod.__init__(self, proxy)
self.bound = bound
def is_bound(self):
return True
def infer_call_result(self, caller, context):
context = context.clone()
context.boundnode = self.bound
return self._proxied.infer_call_result(caller, context)
class Generator(Instance):
"""a special node representing a generator.
Proxied class is set once for all in raw_building.
"""
def callable(self):
return False
def pytype(self):
return '%s.generator' % BUILTINS
def display_type(self):
return 'Generator'
def __repr__(self):
return '' % (self._proxied.name, self.lineno, id(self))
def __str__(self):
return 'Generator(%s)' % (self._proxied.name)
# decorators ##################################################################
def path_wrapper(func):
"""return the given infer function wrapped to handle the path"""
def wrapped(node, context=None, _func=func, **kwargs):
"""wrapper function handling context"""
if context is None:
context = InferenceContext()
context.push(node)
yielded = set()
for res in _func(node, context, **kwargs):
# unproxy only true instance, not const, tuple, dict...
if res.__class__ is Instance:
ares = res._proxied
else:
ares = res
if not ares in yielded:
yield res
yielded.add(ares)
return wrapped
def yes_if_nothing_infered(func):
def wrapper(*args, **kwargs):
infered = False
for node in func(*args, **kwargs):
infered = True
yield node
if not infered:
yield YES
return wrapper
def raise_if_nothing_infered(func):
def wrapper(*args, **kwargs):
infered = False
for node in func(*args, **kwargs):
infered = True
yield node
if not infered:
raise InferenceError()
return wrapper
# Node ######################################################################
class NodeNG(object):
"""Base Class for all ASTNG node classes.
It represents a node of the new abstract syntax tree.
"""
is_statement = False
optional_assign = False # True for For (and for Comprehension if py <3.0)
is_function = False # True for Function nodes
# attributes below are set by the builder module or by raw factories
lineno = None
fromlineno = None
tolineno = None
col_offset = None
# parent node in the tree
parent = None
# attributes containing child node(s) redefined in most concrete classes:
_astng_fields = ()
def _repr_name(self):
"""return self.name or self.attrname or '' for nice representation"""
return getattr(self, 'name', getattr(self, 'attrname', ''))
def __str__(self):
return '%s(%s)' % (self.__class__.__name__, self._repr_name())
def __repr__(self):
return '<%s(%s) l.%s [%s] at Ox%x>' % (self.__class__.__name__,
self._repr_name(),
self.fromlineno,
self.root().name,
id(self))
def accept(self, visitor):
klass = self.__class__.__name__
func = getattr(visitor, "visit_" + self.__class__.__name__.lower())
return func(self)
def get_children(self):
for field in self._astng_fields:
attr = getattr(self, field)
if attr is None:
continue
if isinstance(attr, (list, tuple)):
for elt in attr:
yield elt
else:
yield attr
def last_child(self):
"""an optimized version of list(get_children())[-1]"""
for field in self._astng_fields[::-1]:
attr = getattr(self, field)
if not attr: # None or empty listy / tuple
continue
if isinstance(attr, (list, tuple)):
return attr[-1]
else:
return attr
return None
def parent_of(self, node):
"""return true if i'm a parent of the given node"""
parent = node.parent
while parent is not None:
if self is parent:
return True
parent = parent.parent
return False
def statement(self):
"""return the first parent node marked as statement node"""
if self.is_statement:
return self
return self.parent.statement()
def frame(self):
"""return the first parent frame node (i.e. Module, Function or Class)
"""
return self.parent.frame()
def scope(self):
"""return the first node defining a new scope (i.e. Module, Function,
Class, Lambda but also GenExpr)
"""
return self.parent.scope()
def root(self):
"""return the root node of the tree, (i.e. a Module)"""
if self.parent:
return self.parent.root()
return self
def child_sequence(self, child):
"""search for the right sequence where the child lies in"""
for field in self._astng_fields:
node_or_sequence = getattr(self, field)
if node_or_sequence is child:
return [node_or_sequence]
# /!\ compiler.ast Nodes have an __iter__ walking over child nodes
if isinstance(node_or_sequence, (tuple, list)) and child in node_or_sequence:
return node_or_sequence
else:
msg = 'Could not found %s in %s\'s children'
raise ASTNGError(msg % (repr(child), repr(self)))
def locate_child(self, child):
"""return a 2-uple (child attribute name, sequence or node)"""
for field in self._astng_fields:
node_or_sequence = getattr(self, field)
# /!\ compiler.ast Nodes have an __iter__ walking over child nodes
if child is node_or_sequence:
return field, child
if isinstance(node_or_sequence, (tuple, list)) and child in node_or_sequence:
return field, node_or_sequence
msg = 'Could not found %s in %s\'s children'
raise ASTNGError(msg % (repr(child), repr(self)))
# FIXME : should we merge child_sequence and locate_child ? locate_child
# is only used in are_exclusive, child_sequence one time in pylint.
def next_sibling(self):
"""return the next sibling statement"""
return self.parent.next_sibling()
def previous_sibling(self):
"""return the previous sibling statement"""
return self.parent.previous_sibling()
def nearest(self, nodes):
"""return the node which is the nearest before this one in the
given list of nodes
"""
myroot = self.root()
mylineno = self.fromlineno
nearest = None, 0
for node in nodes:
assert node.root() is myroot, \
'nodes %s and %s are not from the same module' % (self, node)
lineno = node.fromlineno
if node.fromlineno > mylineno:
break
if lineno > nearest[1]:
nearest = node, lineno
# FIXME: raise an exception if nearest is None ?
return nearest[0]
def set_line_info(self, lastchild):
if self.lineno is None:
self.fromlineno = self._fixed_source_line()
else:
self.fromlineno = self.lineno
if lastchild is None:
self.tolineno = self.fromlineno
else:
self.tolineno = lastchild.tolineno
return
# TODO / FIXME:
assert self.fromlineno is not None, self
assert self.tolineno is not None, self
def _fixed_source_line(self):
"""return the line number where the given node appears
we need this method since not all nodes have the lineno attribute
correctly set...
"""
line = self.lineno
_node = self
try:
while line is None:
_node = _node.get_children().next()
line = _node.lineno
except StopIteration:
_node = self.parent
while _node and line is None:
line = _node.lineno
_node = _node.parent
return line
def block_range(self, lineno):
"""handle block line numbers range for non block opening statements
"""
return lineno, self.tolineno
def set_local(self, name, stmt):
"""delegate to a scoped parent handling a locals dictionary"""
self.parent.set_local(name, stmt)
def nodes_of_class(self, klass, skip_klass=None):
"""return an iterator on nodes which are instance of the given class(es)
klass may be a class object or a tuple of class objects
"""
if isinstance(self, klass):
yield self
for child_node in self.get_children():
if skip_klass is not None and isinstance(child_node, skip_klass):
continue
for matching in child_node.nodes_of_class(klass, skip_klass):
yield matching
def _infer_name(self, frame, name):
# overridden for From, Import, Global, TryExcept and Arguments
return None
def infer(self, context=None):
"""we don't know how to resolve a statement by default"""
# this method is overridden by most concrete classes
raise InferenceError(self.__class__.__name__)
def infered(self):
'''return list of infered values for a more simple inference usage'''
return list(self.infer())
def instanciate_class(self):
"""instanciate a node if it is a Class node, else return self"""
return self
def has_base(self, node):
return False
def callable(self):
return False
def eq(self, value):
return False
def as_string(self):
from logilab.astng.as_string import to_code
return to_code(self)
def repr_tree(self, ids=False):
from logilab.astng.as_string import dump
return dump(self)
class Statement(NodeNG):
"""Statement node adding a few attributes"""
is_statement = True
def next_sibling(self):
"""return the next sibling statement"""
stmts = self.parent.child_sequence(self)
index = stmts.index(self)
try:
return stmts[index +1]
except IndexError:
pass
def previous_sibling(self):
"""return the previous sibling statement"""
stmts = self.parent.child_sequence(self)
index = stmts.index(self)
if index >= 1:
return stmts[index -1]
logilab-astng-0.24.3/PKG-INFO 0000664 0000151 0000155 00000005247 12133517272 014755 0 ustar narval narval Metadata-Version: 1.0
Name: logilab-astng
Version: 0.24.3
Summary: rebuild a new abstract syntax tree from Python's ast
Home-page: http://www.logilab.org/project/logilab-astng
Author: Logilab
Author-email: python-projects@lists.logilab.org
License: LGPL
Description: ASTNG
=====
What's this ?
-------------
The aim of this module is to provide a common base representation of
python source code for projects such as pychecker, pyreverse,
pylint... Well, actually the development of this library is essentially
governed by pylint's needs.
It provides a compatible representation which comes from the `_ast` module.
It rebuilds the tree generated by the builtin _ast module by recursively
walking down the AST and building an extended ast (let's call it astng ;). The
new node classes have additional methods and attributes for different usages.
They include some support for static inference and local name scopes.
Furthermore, astng builds partial trees by inspecting living objects.
Main modules are:
* `bases`, `node_classses` and `scoped_nodes` contain the classes for the
different type of nodes of the tree.
* the `manager` contains a high level object to get astng trees from
source files and living objects. It maintains a cache of previously
constructed tree for quick access
Installation
------------
Extract the tarball, jump into the created directory and run ::
python setup.py install
For installation options, see ::
python setup.py install --help
If you have any questions, please mail the
python-project@lists.logilab.org mailing list for support. See
http://lists.logilab.org/mailman/listinfo/python-projects for
subscription information and archives.
Test
----
Tests are in the 'test' subdirectory. To launch the whole tests suite
at once, you may use the 'pytest' utility from logilab-common (simply
type 'pytest' from within this directory) or if you're running python
>= 2.7, using discover, for instance::
python -m unittest discover -p "unittest*.py"
Platform: UNKNOWN
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Software Development :: Quality Assurance
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 3
logilab-astng-0.24.3/as_string.py 0000644 0000151 0000155 00000043715 12133517265 016225 0 ustar narval narval # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of logilab-astng.
#
# logilab-astng is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 2.1 of the License, or (at your
# option) any later version.
#
# logilab-astng is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see .
"""This module renders ASTNG nodes as string:
* :func:`to_code` function return equivalent (hopefuly valid) python string
* :func:`dump` function return an internal representation of nodes found
in the tree, useful for debugging or understanding the tree structure
"""
import sys
INDENT = ' ' # 4 spaces ; keep indentation variable
def dump(node, ids=False):
"""print a nice astng tree representation.
:param ids: if true, we also print the ids (usefull for debugging)
"""
result = []
_repr_tree(node, result, ids=ids)
return "\n".join(result)
def _repr_tree(node, result, indent='', _done=None, ids=False):
"""built a tree representation of a node as a list of lines"""
if _done is None:
_done = set()
if not hasattr(node, '_astng_fields'): # not a astng node
return
if node in _done:
result.append( indent + 'loop in tree: %s' % node )
return
_done.add(node)
node_str = str(node)
if ids:
node_str += ' . \t%x' % id(node)
result.append( indent + node_str )
indent += INDENT
for field in node._astng_fields:
value = getattr(node, field)
if isinstance(value, (list, tuple) ):
result.append( indent + field + " = [" )
for child in value:
if isinstance(child, (list, tuple) ):
# special case for Dict # FIXME
_repr_tree(child[0], result, indent, _done, ids)
_repr_tree(child[1], result, indent, _done, ids)
result.append(indent + ',')
else:
_repr_tree(child, result, indent, _done, ids)
result.append( indent + "]" )
else:
result.append( indent + field + " = " )
_repr_tree(value, result, indent, _done, ids)
class AsStringVisitor(object):
"""Visitor to render an ASTNG node as a valid python code string"""
def __call__(self, node):
"""Makes this visitor behave as a simple function"""
return node.accept(self)
def _stmt_list(self, stmts):
"""return a list of nodes to string"""
stmts = '\n'.join([nstr for nstr in [n.accept(self) for n in stmts] if nstr])
return INDENT + stmts.replace('\n', '\n'+INDENT)
## visit_ methods ###########################################
def visit_arguments(self, node):
"""return an astng.Function node as string"""
return node.format_args()
def visit_assattr(self, node):
"""return an astng.AssAttr node as string"""
return self.visit_getattr(node)
def visit_assert(self, node):
"""return an astng.Assert node as string"""
if node.fail:
return 'assert %s, %s' % (node.test.accept(self),
node.fail.accept(self))
return 'assert %s' % node.test.accept(self)
def visit_assname(self, node):
"""return an astng.AssName node as string"""
return node.name
def visit_assign(self, node):
"""return an astng.Assign node as string"""
lhs = ' = '.join([n.accept(self) for n in node.targets])
return '%s = %s' % (lhs, node.value.accept(self))
def visit_augassign(self, node):
"""return an astng.AugAssign node as string"""
return '%s %s %s' % (node.target.accept(self), node.op, node.value.accept(self))
def visit_backquote(self, node):
"""return an astng.Backquote node as string"""
return '`%s`' % node.value.accept(self)
def visit_binop(self, node):
"""return an astng.BinOp node as string"""
return '(%s) %s (%s)' % (node.left.accept(self), node.op, node.right.accept(self))
def visit_boolop(self, node):
"""return an astng.BoolOp node as string"""
return (' %s ' % node.op).join(['(%s)' % n.accept(self)
for n in node.values])
def visit_break(self, node):
"""return an astng.Break node as string"""
return 'break'
def visit_callfunc(self, node):
"""return an astng.CallFunc node as string"""
expr_str = node.func.accept(self)
args = [arg.accept(self) for arg in node.args]
if node.starargs:
args.append( '*' + node.starargs.accept(self))
if node.kwargs:
args.append( '**' + node.kwargs.accept(self))
return '%s(%s)' % (expr_str, ', '.join(args))
def visit_class(self, node):
"""return an astng.Class node as string"""
decorate = node.decorators and node.decorators.accept(self) or ''
bases = ', '.join([n.accept(self) for n in node.bases])
bases = bases and '(%s)' % bases or ''
docs = node.doc and '\n%s"""%s"""' % (INDENT, node.doc) or ''
return '\n\n%sclass %s%s:%s\n%s\n' % (decorate, node.name, bases, docs,
self._stmt_list( node.body))
def visit_compare(self, node):
"""return an astng.Compare node as string"""
rhs_str = ' '.join(['%s %s' % (op, expr.accept(self))
for op, expr in node.ops])
return '%s %s' % (node.left.accept(self), rhs_str)
def visit_comprehension(self, node):
"""return an astng.Comprehension node as string"""
ifs = ''.join([ ' if %s' % n.accept(self) for n in node.ifs])
return 'for %s in %s%s' % (node.target.accept(self),
node.iter.accept(self), ifs )
def visit_const(self, node):
"""return an astng.Const node as string"""
return repr(node.value)
def visit_continue(self, node):
"""return an astng.Continue node as string"""
return 'continue'
def visit_delete(self, node): # XXX check if correct
"""return an astng.Delete node as string"""
return 'del %s' % ', '.join([child.accept(self)
for child in node.targets])
def visit_delattr(self, node):
"""return an astng.DelAttr node as string"""
return self.visit_getattr(node)
def visit_delname(self, node):
"""return an astng.DelName node as string"""
return node.name
def visit_decorators(self, node):
"""return an astng.Decorators node as string"""
return '@%s\n' % '\n@'.join([item.accept(self) for item in node.nodes])
def visit_dict(self, node):
"""return an astng.Dict node as string"""
return '{%s}' % ', '.join(['%s: %s' % (key.accept(self),
value.accept(self)) for key, value in node.items])
def visit_dictcomp(self, node):
"""return an astng.DictComp node as string"""
return '{%s: %s %s}' % (node.key.accept(self), node.value.accept(self),
' '.join([n.accept(self) for n in node.generators]))
def visit_discard(self, node):
"""return an astng.Discard node as string"""
return node.value.accept(self)
def visit_emptynode(self, node):
"""dummy method for visiting an Empty node"""
return ''
def visit_excepthandler(self, node):
if node.type:
if node.name:
excs = 'except %s, %s' % (node.type.accept(self),
node.name.accept(self))
else:
excs = 'except %s' % node.type.accept(self)
else:
excs = 'except'
return '%s:\n%s' % (excs, self._stmt_list(node.body))
def visit_ellipsis(self, node):
"""return an astng.Ellipsis node as string"""
return '...'
def visit_empty(self, node):
"""return an Empty node as string"""
return ''
def visit_exec(self, node):
"""return an astng.Exec node as string"""
if node.locals:
return 'exec %s in %s, %s' % (node.expr.accept(self),
node.locals.accept(self),
node.globals.accept(self))
if node.globals:
return 'exec %s in %s' % (node.expr.accept(self),
node.globals.accept(self))
return 'exec %s' % node.expr.accept(self)
def visit_extslice(self, node):
"""return an astng.ExtSlice node as string"""
return ','.join( [dim.accept(self) for dim in node.dims] )
def visit_for(self, node):
"""return an astng.For node as string"""
fors = 'for %s in %s:\n%s' % (node.target.accept(self),
node.iter.accept(self),
self._stmt_list( node.body))
if node.orelse:
fors = '%s\nelse:\n%s' % (fors, self._stmt_list(node.orelse))
return fors
def visit_from(self, node):
"""return an astng.From node as string"""
return 'from %s import %s' % ('.' * (node.level or 0) + node.modname,
_import_string(node.names))
def visit_function(self, node):
"""return an astng.Function node as string"""
decorate = node.decorators and node.decorators.accept(self) or ''
docs = node.doc and '\n%s"""%s"""' % (INDENT, node.doc) or ''
return '\n%sdef %s(%s):%s\n%s' % (decorate, node.name, node.args.accept(self),
docs, self._stmt_list(node.body))
def visit_genexpr(self, node):
"""return an astng.GenExpr node as string"""
return '(%s %s)' % (node.elt.accept(self), ' '.join([n.accept(self)
for n in node.generators]))
def visit_getattr(self, node):
"""return an astng.Getattr node as string"""
return '%s.%s' % (node.expr.accept(self), node.attrname)
def visit_global(self, node):
"""return an astng.Global node as string"""
return 'global %s' % ', '.join(node.names)
def visit_if(self, node):
"""return an astng.If node as string"""
ifs = ['if %s:\n%s' % (node.test.accept(self), self._stmt_list(node.body))]
if node.orelse:# XXX use elif ???
ifs.append('else:\n%s' % self._stmt_list(node.orelse))
return '\n'.join(ifs)
def visit_ifexp(self, node):
"""return an astng.IfExp node as string"""
return '%s if %s else %s' % (node.body.accept(self),
node.test.accept(self), node.orelse.accept(self))
def visit_import(self, node):
"""return an astng.Import node as string"""
return 'import %s' % _import_string(node.names)
def visit_keyword(self, node):
"""return an astng.Keyword node as string"""
return '%s=%s' % (node.arg, node.value.accept(self))
def visit_lambda(self, node):
"""return an astng.Lambda node as string"""
return 'lambda %s: %s' % (node.args.accept(self), node.body.accept(self))
def visit_list(self, node):
"""return an astng.List node as string"""
return '[%s]' % ', '.join([child.accept(self) for child in node.elts])
def visit_listcomp(self, node):
"""return an astng.ListComp node as string"""
return '[%s %s]' % (node.elt.accept(self), ' '.join([n.accept(self)
for n in node.generators]))
def visit_module(self, node):
"""return an astng.Module node as string"""
docs = node.doc and '"""%s"""\n\n' % node.doc or ''
return docs + '\n'.join([n.accept(self) for n in node.body]) + '\n\n'
def visit_name(self, node):
"""return an astng.Name node as string"""
return node.name
def visit_pass(self, node):
"""return an astng.Pass node as string"""
return 'pass'
def visit_print(self, node):
"""return an astng.Print node as string"""
nodes = ', '.join([n.accept(self) for n in node.values])
if not node.nl:
nodes = '%s,' % nodes
if node.dest:
return 'print >> %s, %s' % (node.dest.accept(self), nodes)
return 'print %s' % nodes
def visit_raise(self, node):
"""return an astng.Raise node as string"""
if node.exc:
if node.inst:
if node.tback:
return 'raise %s, %s, %s' % (node.exc.accept(self),
node.inst.accept(self),
node.tback.accept(self))
return 'raise %s, %s' % (node.exc.accept(self),
node.inst.accept(self))
return 'raise %s' % node.exc.accept(self)
return 'raise'
def visit_return(self, node):
"""return an astng.Return node as string"""
if node.value:
return 'return %s' % node.value.accept(self)
else:
return 'return'
def visit_index(self, node):
"""return a astng.Index node as string"""
return node.value.accept(self)
def visit_set(self, node):
"""return an astng.Set node as string"""
return '{%s}' % ', '.join([child.accept(self) for child in node.elts])
def visit_setcomp(self, node):
"""return an astng.SetComp node as string"""
return '{%s %s}' % (node.elt.accept(self), ' '.join([n.accept(self)
for n in node.generators]))
def visit_slice(self, node):
"""return a astng.Slice node as string"""
lower = node.lower and node.lower.accept(self) or ''
upper = node.upper and node.upper.accept(self) or ''
step = node.step and node.step.accept(self) or ''
if step:
return '%s:%s:%s' % (lower, upper, step)
return '%s:%s' % (lower, upper)
def visit_subscript(self, node):
"""return an astng.Subscript node as string"""
return '%s[%s]' % (node.value.accept(self), node.slice.accept(self))
def visit_tryexcept(self, node):
"""return an astng.TryExcept node as string"""
trys = ['try:\n%s' % self._stmt_list( node.body)]
for handler in node.handlers:
trys.append(handler.accept(self))
if node.orelse:
trys.append('else:\n%s' % self._stmt_list(node.orelse))
return '\n'.join(trys)
def visit_tryfinally(self, node):
"""return an astng.TryFinally node as string"""
return 'try:\n%s\nfinally:\n%s' % (self._stmt_list( node.body),
self._stmt_list(node.finalbody))
def visit_tuple(self, node):
"""return an astng.Tuple node as string"""
return '(%s)' % ', '.join([child.accept(self) for child in node.elts])
def visit_unaryop(self, node):
"""return an astng.UnaryOp node as string"""
if node.op == 'not':
operator = 'not '
else:
operator = node.op
return '%s%s' % (operator, node.operand.accept(self))
def visit_while(self, node):
"""return an astng.While node as string"""
whiles = 'while %s:\n%s' % (node.test.accept(self),
self._stmt_list(node.body))
if node.orelse:
whiles = '%s\nelse:\n%s' % (whiles, self._stmt_list(node.orelse))
return whiles
def visit_with(self, node): # 'with' without 'as' is possible
"""return an astng.With node as string"""
as_var = node.vars and " as (%s)" % (node.vars.accept(self)) or ""
withs = 'with (%s)%s:\n%s' % (node.expr.accept(self), as_var,
self._stmt_list( node.body))
return withs
def visit_yield(self, node):
"""yield an ast.Yield node as string"""
yi_val = node.value and (" " + node.value.accept(self)) or ""
expr = 'yield' + yi_val
if node.parent.is_statement:
return expr
else:
return "(%s)" % (expr,)
class AsStringVisitor3k(AsStringVisitor):
"""AsStringVisitor3k overwrites some AsStringVisitor methods"""
def visit_excepthandler(self, node):
if node.type:
if node.name:
excs = 'except %s as %s' % (node.type.accept(self),
node.name.accept(self))
else:
excs = 'except %s' % node.type.accept(self)
else:
excs = 'except'
return '%s:\n%s' % (excs, self._stmt_list(node.body))
def visit_nonlocal(self, node):
"""return an astng.Nonlocal node as string"""
return 'nonlocal %s' % ', '.join(node.names)
def visit_raise(self, node):
"""return an astng.Raise node as string"""
if node.exc:
if node.cause:
return 'raise %s from %s' % (node.exc.accept(self),
node.cause.accept(self))
return 'raise %s' % node.exc.accept(self)
return 'raise'
def visit_starred(self, node):
"""return Starred node as string"""
return "*" + node.value.accept(self)
def _import_string(names):
"""return a list of (name, asname) formatted as a string"""
_names = []
for name, asname in names:
if asname is not None:
_names.append('%s as %s' % (name, asname))
else:
_names.append(name)
return ', '.join(_names)
if sys.version_info >= (3, 0):
AsStringVisitor = AsStringVisitor3k
# this visitor is stateless, thus it can be reused
to_code = AsStringVisitor()
logilab-astng-0.24.3/manager.py 0000644 0000151 0000155 00000026546 12133517265 015651 0 ustar narval narval # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of logilab-astng.
#
# logilab-astng is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 2.1 of the License, or (at your
# option) any later version.
#
# logilab-astng is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see .
"""astng manager: avoid multiple astng build of a same module when
possible by providing a class responsible to get astng representation
from various source and using a cache of built modules)
"""
__docformat__ = "restructuredtext en"
import os
from os.path import dirname, join, isdir, exists
from logilab.common.modutils import NoSourceFile, is_python_source, \
file_from_modpath, load_module_from_name, modpath_from_file, \
get_module_files, get_source_file, zipimport
from logilab.common.configuration import OptionsProviderMixIn
from logilab.astng.exceptions import ASTNGBuildingException
def astng_wrapper(func, modname):
"""wrapper to give to ASTNGManager.project_from_files"""
print 'parsing %s...' % modname
try:
return func(modname)
except ASTNGBuildingException, exc:
print exc
except Exception, exc:
import traceback
traceback.print_exc()
def _silent_no_wrap(func, modname):
"""silent wrapper that doesn't do anything; can be used for tests"""
return func(modname)
def safe_repr(obj):
try:
return repr(obj)
except:
return '???'
class ASTNGManager(OptionsProviderMixIn):
"""the astng manager, responsible to build astng from files
or modules.
Use the Borg pattern.
"""
name = 'astng loader'
options = (("ignore",
{'type' : "csv", 'metavar' : "",
'dest' : "black_list", "default" : ('CVS',),
'help' : "add (may be a directory) to the black list\
. It should be a base name, not a path. You may set this option multiple times\
."}),
("project",
{'default': "No Name", 'type' : 'string', 'short': 'p',
'metavar' : '',
'help' : 'set the project name.'}),
)
brain = {}
def __init__(self):
self.__dict__ = ASTNGManager.brain
if not self.__dict__:
OptionsProviderMixIn.__init__(self)
self.load_defaults()
# NOTE: cache entries are added by the [re]builder
self.astng_cache = {}
self._mod_file_cache = {}
self.transformers = []
def astng_from_file(self, filepath, modname=None, fallback=True, source=False):
"""given a module name, return the astng object"""
try:
filepath = get_source_file(filepath, include_no_ext=True)
source = True
except NoSourceFile:
pass
if modname is None:
try:
modname = '.'.join(modpath_from_file(filepath))
except ImportError:
modname = filepath
if modname in self.astng_cache:
return self.astng_cache[modname]
if source:
from logilab.astng.builder import ASTNGBuilder
return ASTNGBuilder(self).file_build(filepath, modname)
elif fallback and modname:
return self.astng_from_module_name(modname)
raise ASTNGBuildingException('unable to get astng for file %s' %
filepath)
def astng_from_module_name(self, modname, context_file=None):
"""given a module name, return the astng object"""
if modname in self.astng_cache:
return self.astng_cache[modname]
if modname == '__main__':
from logilab.astng.builder import ASTNGBuilder
return ASTNGBuilder(self).string_build('', modname)
old_cwd = os.getcwd()
if context_file:
os.chdir(dirname(context_file))
try:
filepath = self.file_from_module_name(modname, context_file)
if filepath is not None and not is_python_source(filepath):
module = self.zip_import_data(filepath)
if module is not None:
return module
if filepath is None or not is_python_source(filepath):
try:
module = load_module_from_name(modname)
except Exception, ex:
msg = 'Unable to load module %s (%s)' % (modname, ex)
raise ASTNGBuildingException(msg)
return self.astng_from_module(module, modname)
return self.astng_from_file(filepath, modname, fallback=False)
finally:
os.chdir(old_cwd)
def zip_import_data(self, filepath):
if zipimport is None:
return None
from logilab.astng.builder import ASTNGBuilder
builder = ASTNGBuilder(self)
for ext in ('.zip', '.egg'):
try:
eggpath, resource = filepath.rsplit(ext + '/', 1)
except ValueError:
continue
try:
importer = zipimport.zipimporter(eggpath + ext)
zmodname = resource.replace('/', '.')
if importer.is_package(resource):
zmodname = zmodname + '.__init__'
module = builder.string_build(importer.get_source(resource),
zmodname, filepath)
return module
except:
continue
return None
def file_from_module_name(self, modname, contextfile):
try:
value = self._mod_file_cache[(modname, contextfile)]
except KeyError:
try:
value = file_from_modpath(modname.split('.'),
context_file=contextfile)
except ImportError, ex:
msg = 'Unable to load module %s (%s)' % (modname, ex)
value = ASTNGBuildingException(msg)
self._mod_file_cache[(modname, contextfile)] = value
if isinstance(value, ASTNGBuildingException):
raise value
return value
def astng_from_module(self, module, modname=None):
"""given an imported module, return the astng object"""
modname = modname or module.__name__
if modname in self.astng_cache:
return self.astng_cache[modname]
try:
# some builtin modules don't have __file__ attribute
filepath = module.__file__
if is_python_source(filepath):
return self.astng_from_file(filepath, modname)
except AttributeError:
pass
from logilab.astng.builder import ASTNGBuilder
return ASTNGBuilder(self).module_build(module, modname)
def astng_from_class(self, klass, modname=None):
"""get astng for the given class"""
if modname is None:
try:
modname = klass.__module__
except AttributeError:
raise ASTNGBuildingException(
'Unable to get module for class %s' % safe_repr(klass))
modastng = self.astng_from_module_name(modname)
return modastng.getattr(klass.__name__)[0] # XXX
def infer_astng_from_something(self, obj, context=None):
"""infer astng for the given class"""
if hasattr(obj, '__class__') and not isinstance(obj, type):
klass = obj.__class__
else:
klass = obj
try:
modname = klass.__module__
except AttributeError:
raise ASTNGBuildingException(
'Unable to get module for %s' % safe_repr(klass))
except Exception, ex:
raise ASTNGBuildingException(
'Unexpected error while retrieving module for %s: %s'
% (safe_repr(klass), ex))
try:
name = klass.__name__
except AttributeError:
raise ASTNGBuildingException(
'Unable to get name for %s' % safe_repr(klass))
except Exception, ex:
raise ASTNGBuildingException(
'Unexpected error while retrieving name for %s: %s'
% (safe_repr(klass), ex))
# take care, on living object __module__ is regularly wrong :(
modastng = self.astng_from_module_name(modname)
if klass is obj:
for infered in modastng.igetattr(name, context):
yield infered
else:
for infered in modastng.igetattr(name, context):
yield infered.instanciate_class()
def project_from_files(self, files, func_wrapper=astng_wrapper,
project_name=None, black_list=None):
"""return a Project from a list of files or modules"""
# build the project representation
project_name = project_name or self.config.project
black_list = black_list or self.config.black_list
project = Project(project_name)
for something in files:
if not exists(something):
fpath = file_from_modpath(something.split('.'))
elif isdir(something):
fpath = join(something, '__init__.py')
else:
fpath = something
astng = func_wrapper(self.astng_from_file, fpath)
if astng is None:
continue
# XXX why is first file defining the project.path ?
project.path = project.path or astng.file
project.add_module(astng)
base_name = astng.name
# recurse in package except if __init__ was explicitly given
if astng.package and something.find('__init__') == -1:
# recurse on others packages / modules if this is a package
for fpath in get_module_files(dirname(astng.file),
black_list):
astng = func_wrapper(self.astng_from_file, fpath)
if astng is None or astng.name == base_name:
continue
project.add_module(astng)
return project
def register_transformer(self, transformer):
self.transformers.append(transformer)
class Project:
"""a project handle a set of modules / packages"""
def __init__(self, name=''):
self.name = name
self.path = None
self.modules = []
self.locals = {}
self.__getitem__ = self.locals.__getitem__
self.__iter__ = self.locals.__iter__
self.values = self.locals.values
self.keys = self.locals.keys
self.items = self.locals.items
def add_module(self, node):
self.locals[node.name] = node
self.modules.append(node)
def get_module(self, name):
return self.locals[name]
def get_children(self):
return self.modules
def __repr__(self):
return '' % (self.name, id(self),
len(self.modules))
logilab-astng-0.24.3/raw_building.py 0000644 0000151 0000155 00000032404 12133517265 016673 0 ustar narval narval # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of logilab-astng.
#
# logilab-astng is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 2.1 of the License, or (at your
# option) any later version.
#
# logilab-astng is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see .
"""this module contains a set of functions to create astng trees from scratch
(build_* functions) or from living object (object_build_* functions)
"""
__docformat__ = "restructuredtext en"
import sys
from os.path import abspath
from inspect import (getargspec, isdatadescriptor, isfunction, ismethod,
ismethoddescriptor, isclass, isbuiltin)
from logilab.astng.node_classes import CONST_CLS
from logilab.astng.nodes import (Module, Class, Const, const_factory, From,
Function, EmptyNode, Name, Arguments)
from logilab.astng.bases import BUILTINS, Generator
from logilab.astng.manager import ASTNGManager
MANAGER = ASTNGManager()
_CONSTANTS = tuple(CONST_CLS) # the keys of CONST_CLS eg python builtin types
def _attach_local_node(parent, node, name):
node.name = name # needed by add_local_node
parent.add_local_node(node)
_marker = object()
def attach_dummy_node(node, name, object=_marker):
"""create a dummy node and register it in the locals of the given
node with the specified name
"""
enode = EmptyNode()
enode.object = object
_attach_local_node(node, enode, name)
EmptyNode.has_underlying_object = lambda self: self.object is not _marker
def attach_const_node(node, name, value):
"""create a Const node and register it in the locals of the given
node with the specified name
"""
if not name in node.special_attributes:
_attach_local_node(node, const_factory(value), name)
def attach_import_node(node, modname, membername):
"""create a From node and register it in the locals of the given
node with the specified name
"""
from_node = From(modname, [(membername, None)])
_attach_local_node(node, from_node, membername)
def build_module(name, doc=None):
"""create and initialize a astng Module node"""
node = Module(name, doc, pure_python=False)
node.package = False
node.parent = None
return node
def build_class(name, basenames=(), doc=None):
"""create and initialize a astng Class node"""
node = Class(name, doc)
for base in basenames:
basenode = Name()
basenode.name = base
node.bases.append(basenode)
basenode.parent = node
return node
def build_function(name, args=None, defaults=None, flag=0, doc=None):
"""create and initialize a astng Function node"""
args, defaults = args or [], defaults or []
# first argument is now a list of decorators
func = Function(name, doc)
func.args = argsnode = Arguments()
argsnode.args = []
for arg in args:
argsnode.args.append(Name())
argsnode.args[-1].name = arg
argsnode.args[-1].parent = argsnode
argsnode.defaults = []
for default in defaults:
argsnode.defaults.append(const_factory(default))
argsnode.defaults[-1].parent = argsnode
argsnode.kwarg = None
argsnode.vararg = None
argsnode.parent = func
if args:
register_arguments(func)
return func
def build_from_import(fromname, names):
"""create and initialize an astng From import statement"""
return From(fromname, [(name, None) for name in names])
def register_arguments(func, args=None):
"""add given arguments to local
args is a list that may contains nested lists
(i.e. def func(a, (b, c, d)): ...)
"""
if args is None:
args = func.args.args
if func.args.vararg:
func.set_local(func.args.vararg, func.args)
if func.args.kwarg:
func.set_local(func.args.kwarg, func.args)
for arg in args:
if isinstance(arg, Name):
func.set_local(arg.name, arg)
else:
register_arguments(func, arg.elts)
def object_build_class(node, member, localname):
"""create astng for a living class object"""
basenames = [base.__name__ for base in member.__bases__]
return _base_class_object_build(node, member, basenames,
localname=localname)
def object_build_function(node, member, localname):
"""create astng for a living function object"""
args, varargs, varkw, defaults = getargspec(member)
if varargs is not None:
args.append(varargs)
if varkw is not None:
args.append(varkw)
func = build_function(getattr(member, '__name__', None) or localname, args,
defaults, member.func_code.co_flags, member.__doc__)
node.add_local_node(func, localname)
def object_build_datadescriptor(node, member, name):
"""create astng for a living data descriptor object"""
return _base_class_object_build(node, member, [], name)
def object_build_methoddescriptor(node, member, localname):
"""create astng for a living method descriptor object"""
# FIXME get arguments ?
func = build_function(getattr(member, '__name__', None) or localname,
doc=member.__doc__)
# set node's arguments to None to notice that we have no information, not
# and empty argument list
func.args.args = None
node.add_local_node(func, localname)
def _base_class_object_build(node, member, basenames, name=None, localname=None):
"""create astng for a living class object, with a given set of base names
(e.g. ancestors)
"""
klass = build_class(name or getattr(member, '__name__', None) or localname,
basenames, member.__doc__)
klass._newstyle = isinstance(member, type)
node.add_local_node(klass, localname)
try:
# limit the instantiation trick since it's too dangerous
# (such as infinite test execution...)
# this at least resolves common case such as Exception.args,
# OSError.errno
if issubclass(member, Exception):
instdict = member().__dict__
else:
raise TypeError
except:
pass
else:
for name, obj in instdict.items():
valnode = EmptyNode()
valnode.object = obj
valnode.parent = klass
valnode.lineno = 1
klass.instance_attrs[name] = [valnode]
return klass
class InspectBuilder(object):
"""class for building nodes from living object
this is actually a really minimal representation, including only Module,
Function and Class nodes and some others as guessed.
"""
# astng from living objects ###############################################
def __init__(self):
self._done = {}
self._module = None
def inspect_build(self, module, modname=None, path=None):
"""build astng from a living module (i.e. using inspect)
this is used when there is no python source code available (either
because it's a built-in module or because the .py is not available)
"""
self._module = module
if modname is None:
modname = module.__name__
try:
node = build_module(modname, module.__doc__)
except AttributeError:
# in jython, java modules have no __doc__ (see #109562)
node = build_module(modname)
node.file = node.path = path and abspath(path) or path
MANAGER.astng_cache[modname] = node
node.package = hasattr(module, '__path__')
self._done = {}
self.object_build(node, module)
return node
def object_build(self, node, obj):
"""recursive method which create a partial ast from real objects
(only function, class, and method are handled)
"""
if obj in self._done:
return self._done[obj]
self._done[obj] = node
for name in dir(obj):
try:
member = getattr(obj, name)
except AttributeError:
# damned ExtensionClass.Base, I know you're there !
attach_dummy_node(node, name)
continue
if ismethod(member):
member = member.im_func
if isfunction(member):
# verify this is not an imported function
filename = getattr(member.func_code, 'co_filename', None)
if filename is None:
assert isinstance(member, object)
object_build_methoddescriptor(node, member, name)
elif filename != getattr(self._module, '__file__', None):
attach_dummy_node(node, name, member)
else:
object_build_function(node, member, name)
elif isbuiltin(member):
if self.imported_member(node, member, name):
#if obj is object:
# print 'skippp', obj, name, member
continue
object_build_methoddescriptor(node, member, name)
elif isclass(member):
if self.imported_member(node, member, name):
continue
if member in self._done:
class_node = self._done[member]
if not class_node in node.locals.get(name, ()):
node.add_local_node(class_node, name)
else:
class_node = object_build_class(node, member, name)
# recursion
self.object_build(class_node, member)
if name == '__class__' and class_node.parent is None:
class_node.parent = self._done[self._module]
elif ismethoddescriptor(member):
assert isinstance(member, object)
object_build_methoddescriptor(node, member, name)
elif isdatadescriptor(member):
assert isinstance(member, object)
object_build_datadescriptor(node, member, name)
elif type(member) in _CONSTANTS:
attach_const_node(node, name, member)
else:
# create an empty node so that the name is actually defined
attach_dummy_node(node, name, member)
def imported_member(self, node, member, name):
"""verify this is not an imported class or handle it"""
# /!\ some classes like ExtensionClass doesn't have a __module__
# attribute ! Also, this may trigger an exception on badly built module
# (see http://www.logilab.org/ticket/57299 for instance)
try:
modname = getattr(member, '__module__', None)
except:
# XXX use logging
print 'unexpected error while building astng from living object'
import traceback
traceback.print_exc()
modname = None
if modname is None:
if name in ('__new__', '__subclasshook__'):
# Python 2.5.1 (r251:54863, Sep 1 2010, 22:03:14)
# >>> print object.__new__.__module__
# None
modname = BUILTINS
else:
attach_dummy_node(node, name, member)
return True
if {'gtk': 'gtk._gtk'}.get(modname, modname) != self._module.__name__:
# check if it sounds valid and then add an import node, else use a
# dummy node
try:
getattr(sys.modules[modname], name)
except (KeyError, AttributeError):
attach_dummy_node(node, name, member)
else:
attach_import_node(node, modname, name)
return True
return False
### astng boot strapping ################################################### ###
ASTNG_BUILDER = InspectBuilder()
_CONST_PROXY = {}
def astng_boot_strapping():
"""astng boot strapping the builtins module"""
# this boot strapping is necessary since we need the Const nodes to
# inspect_build builtins, and then we can proxy Const
from logilab.common.compat import builtins
astng_builtin = ASTNG_BUILDER.inspect_build(builtins)
for cls, node_cls in CONST_CLS.items():
if cls is type(None):
proxy = build_class('NoneType')
proxy.parent = astng_builtin
else:
proxy = astng_builtin.getattr(cls.__name__)[0]
if cls in (dict, list, set, tuple):
node_cls._proxied = proxy
else:
_CONST_PROXY[cls] = proxy
astng_boot_strapping()
# TODO : find a nicer way to handle this situation;
# However __proxied introduced an
# infinite recursion (see https://bugs.launchpad.net/pylint/+bug/456870)
def _set_proxied(const):
return _CONST_PROXY[const.value.__class__]
Const._proxied = property(_set_proxied)
from types import GeneratorType
Generator._proxied = Class(GeneratorType.__name__, GeneratorType.__doc__)
ASTNG_BUILDER.object_build(Generator._proxied, GeneratorType)
logilab-astng-0.24.3/COPYING.LESSER 0000644 0000151 0000155 00000063637 12133517265 015716 0 ustar narval narval
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations
below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it
becomes a de-facto standard. To achieve this, non-free programs must
be allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control
compilation and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at least
three years, to give the same user the materials specified in
Subsection 6a, above, for a charge no more than the cost of
performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply, and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License
may add an explicit geographical distribution limitation excluding those
countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms
of the ordinary General Public License).
To apply these terms, attach the following notices to the library.
It is safest to attach them to the start of each source file to most
effectively convey the exclusion of warranty; and each file should
have at least the "copyright" line and a pointer to where the full
notice is found.
Copyright (C)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or
your school, if any, to sign a "copyright disclaimer" for the library,
if necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James
Random Hacker.
, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!
logilab-astng-0.24.3/node_classes.py 0000644 0000151 0000155 00000065032 12133517265 016672 0 ustar narval narval # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of logilab-astng.
#
# logilab-astng is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 2.1 of the License, or (at your
# option) any later version.
#
# logilab-astng is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see .
"""Module for some node classes. More nodes in scoped_nodes.py
"""
import sys
from logilab.astng.exceptions import NoDefault
from logilab.astng.bases import (NodeNG, Statement, Instance, InferenceContext,
_infer_stmts, YES, BUILTINS)
from logilab.astng.mixins import BlockRangeMixIn, AssignTypeMixin, \
ParentAssignTypeMixin, FromImportMixIn
def unpack_infer(stmt, context=None):
"""recursively generate nodes inferred by the given statement.
If the inferred value is a list or a tuple, recurse on the elements
"""
if isinstance(stmt, (List, Tuple)):
for elt in stmt.elts:
for infered_elt in unpack_infer(elt, context):
yield infered_elt
return
# if infered is a final node, return it and stop
infered = stmt.infer(context).next()
if infered is stmt:
yield infered
return
# else, infer recursivly, except YES object that should be returned as is
for infered in stmt.infer(context):
if infered is YES:
yield infered
else:
for inf_inf in unpack_infer(infered, context):
yield inf_inf
def are_exclusive(stmt1, stmt2, exceptions=None):
"""return true if the two given statements are mutually exclusive
`exceptions` may be a list of exception names. If specified, discard If
branches and check one of the statement is in an exception handler catching
one of the given exceptions.
algorithm :
1) index stmt1's parents
2) climb among stmt2's parents until we find a common parent
3) if the common parent is a If or TryExcept statement, look if nodes are
in exclusive branches
"""
# index stmt1's parents
stmt1_parents = {}
children = {}
node = stmt1.parent
previous = stmt1
while node:
stmt1_parents[node] = 1
children[node] = previous
previous = node
node = node.parent
# climb among stmt2's parents until we find a common parent
node = stmt2.parent
previous = stmt2
while node:
if node in stmt1_parents:
# if the common parent is a If or TryExcept statement, look if
# nodes are in exclusive branches
if isinstance(node, If) and exceptions is None:
if (node.locate_child(previous)[1]
is not node.locate_child(children[node])[1]):
return True
elif isinstance(node, TryExcept):
c2attr, c2node = node.locate_child(previous)
c1attr, c1node = node.locate_child(children[node])
if c1node is not c2node:
if ((c2attr == 'body' and c1attr == 'handlers' and children[node].catch(exceptions)) or
(c2attr == 'handlers' and c1attr == 'body' and previous.catch(exceptions)) or
(c2attr == 'handlers' and c1attr == 'orelse') or
(c2attr == 'orelse' and c1attr == 'handlers')):
return True
elif c2attr == 'handlers' and c1attr == 'handlers':
return previous is not children[node]
return False
previous = node
node = node.parent
return False
class LookupMixIn(object):
"""Mixin looking up a name in the right scope
"""
def lookup(self, name):
"""lookup a variable name
return the scope node and the list of assignments associated to the given
name according to the scope where it has been found (locals, globals or
builtin)
The lookup is starting from self's scope. If self is not a frame itself and
the name is found in the inner frame locals, statements will be filtered
to remove ignorable statements according to self's location
"""
return self.scope().scope_lookup(self, name)
def ilookup(self, name):
"""infered lookup
return an iterator on infered values of the statements returned by
the lookup method
"""
frame, stmts = self.lookup(name)
context = InferenceContext()
return _infer_stmts(stmts, context, frame)
def _filter_stmts(self, stmts, frame, offset):
"""filter statements to remove ignorable statements.
If self is not a frame itself and the name is found in the inner
frame locals, statements will be filtered to remove ignorable
statements according to self's location
"""
# if offset == -1, my actual frame is not the inner frame but its parent
#
# class A(B): pass
#
# we need this to resolve B correctly
if offset == -1:
myframe = self.frame().parent.frame()
else:
myframe = self.frame()
if not myframe is frame or self is frame:
return stmts
mystmt = self.statement()
# line filtering if we are in the same frame
#
# take care node may be missing lineno information (this is the case for
# nodes inserted for living objects)
if myframe is frame and mystmt.fromlineno is not None:
assert mystmt.fromlineno is not None, mystmt
mylineno = mystmt.fromlineno + offset
else:
# disabling lineno filtering
mylineno = 0
_stmts = []
_stmt_parents = []
for node in stmts:
stmt = node.statement()
# line filtering is on and we have reached our location, break
if mylineno > 0 and stmt.fromlineno > mylineno:
break
assert hasattr(node, 'ass_type'), (node, node.scope(),
node.scope().locals)
ass_type = node.ass_type()
if node.has_base(self):
break
_stmts, done = ass_type._get_filtered_stmts(self, node, _stmts, mystmt)
if done:
break
optional_assign = ass_type.optional_assign
if optional_assign and ass_type.parent_of(self):
# we are inside a loop, loop var assigment is hidding previous
# assigment
_stmts = [node]
_stmt_parents = [stmt.parent]
continue
# XXX comment various branches below!!!
try:
pindex = _stmt_parents.index(stmt.parent)
except ValueError:
pass
else:
# we got a parent index, this means the currently visited node
# is at the same block level as a previously visited node
if _stmts[pindex].ass_type().parent_of(ass_type):
# both statements are not at the same block level
continue
# if currently visited node is following previously considered
# assignement and both are not exclusive, we can drop the
# previous one. For instance in the following code ::
#
# if a:
# x = 1
# else:
# x = 2
# print x
#
# we can't remove neither x = 1 nor x = 2 when looking for 'x'
# of 'print x'; while in the following ::
#
# x = 1
# x = 2
# print x
#
# we can remove x = 1 when we see x = 2
#
# moreover, on loop assignment types, assignment won't
# necessarily be done if the loop has no iteration, so we don't
# want to clear previous assigments if any (hence the test on
# optional_assign)
if not (optional_assign or are_exclusive(_stmts[pindex], node)):
del _stmt_parents[pindex]
del _stmts[pindex]
if isinstance(node, AssName):
if not optional_assign and stmt.parent is mystmt.parent:
_stmts = []
_stmt_parents = []
elif isinstance(node, DelName):
_stmts = []
_stmt_parents = []
continue
if not are_exclusive(self, node):
_stmts.append(node)
_stmt_parents.append(stmt.parent)
return _stmts
# Name classes
class AssName(LookupMixIn, ParentAssignTypeMixin, NodeNG):
"""class representing an AssName node"""
class DelName(LookupMixIn, ParentAssignTypeMixin, NodeNG):
"""class representing a DelName node"""
class Name(LookupMixIn, NodeNG):
"""class representing a Name node"""
##################### node classes ########################################
class Arguments(NodeNG, AssignTypeMixin):
"""class representing an Arguments node"""
_astng_fields = ('args', 'defaults')
args = None
defaults = None
def __init__(self, vararg=None, kwarg=None):
self.vararg = vararg
self.kwarg = kwarg
def _infer_name(self, frame, name):
if self.parent is frame:
return name
return None
def format_args(self):
"""return arguments formatted as string"""
result = [_format_args(self.args, self.defaults)]
if self.vararg:
result.append('*%s' % self.vararg)
if self.kwarg:
result.append('**%s' % self.kwarg)
return ', '.join(result)
def default_value(self, argname):
"""return the default value for an argument
:raise `NoDefault`: if there is no default value defined
"""
i = _find_arg(argname, self.args)[0]
if i is not None:
idx = i - (len(self.args) - len(self.defaults))
if idx >= 0:
return self.defaults[idx]
raise NoDefault()
def is_argument(self, name):
"""return True if the name is defined in arguments"""
if name == self.vararg:
return True
if name == self.kwarg:
return True
return self.find_argname(name, True)[1] is not None
def find_argname(self, argname, rec=False):
"""return index and Name node with given name"""
if self.args: # self.args may be None in some cases (builtin function)
return _find_arg(argname, self.args, rec)
return None, None
def _find_arg(argname, args, rec=False):
for i, arg in enumerate(args):
if isinstance(arg, Tuple):
if rec:
found = _find_arg(argname, arg.elts)
if found[0] is not None:
return found
elif arg.name == argname:
return i, arg
return None, None
def _format_args(args, defaults=None):
values = []
if args is None:
return ''
if defaults is not None:
default_offset = len(args) - len(defaults)
for i, arg in enumerate(args):
if isinstance(arg, Tuple):
values.append('(%s)' % _format_args(arg.elts))
else:
values.append(arg.name)
if defaults is not None and i >= default_offset:
values[-1] += '=' + defaults[i-default_offset].as_string()
return ', '.join(values)
class AssAttr(NodeNG, ParentAssignTypeMixin):
"""class representing an AssAttr node"""
_astng_fields = ('expr',)
expr = None
class Assert(Statement):
"""class representing an Assert node"""
_astng_fields = ('test', 'fail',)
test = None
fail = None
class Assign(Statement, AssignTypeMixin):
"""class representing an Assign node"""
_astng_fields = ('targets', 'value',)
targets = None
value = None
class AugAssign(Statement, AssignTypeMixin):
"""class representing an AugAssign node"""
_astng_fields = ('target', 'value',)
target = None
value = None
class Backquote(NodeNG):
"""class representing a Backquote node"""
_astng_fields = ('value',)
value = None
class BinOp(NodeNG):
"""class representing a BinOp node"""
_astng_fields = ('left', 'right',)
left = None
right = None
class BoolOp(NodeNG):
"""class representing a BoolOp node"""
_astng_fields = ('values',)
values = None
class Break(Statement):
"""class representing a Break node"""
class CallFunc(NodeNG):
"""class representing a CallFunc node"""
_astng_fields = ('func', 'args', 'starargs', 'kwargs')
func = None
args = None
starargs = None
kwargs = None
def __init__(self):
self.starargs = None
self.kwargs = None
class Compare(NodeNG):
"""class representing a Compare node"""
_astng_fields = ('left', 'ops',)
left = None
ops = None
def get_children(self):
"""override get_children for tuple fields"""
yield self.left
for _, comparator in self.ops:
yield comparator # we don't want the 'op'
def last_child(self):
"""override last_child"""
# XXX maybe if self.ops:
return self.ops[-1][1]
#return self.left
class Comprehension(NodeNG):
"""class representing a Comprehension node"""
_astng_fields = ('target', 'iter' ,'ifs')
target = None
iter = None
ifs = None
optional_assign = True
def ass_type(self):
return self
def _get_filtered_stmts(self, lookup_node, node, stmts, mystmt):
"""method used in filter_stmts"""
if self is mystmt:
if isinstance(lookup_node, (Const, Name)):
return [lookup_node], True
elif self.statement() is mystmt:
# original node's statement is the assignment, only keeps
# current node (gen exp, list comp)
return [node], True
return stmts, False
class Const(NodeNG, Instance):
"""represent a constant node like num, str, bool, None, bytes"""
def __init__(self, value=None):
self.value = value
def getitem(self, index, context=None):
if isinstance(self.value, basestring):
return Const(self.value[index])
raise TypeError('%r (value=%s)' % (self, self.value))
def has_dynamic_getattr(self):
return False
def itered(self):
if isinstance(self.value, basestring):
return self.value
raise TypeError()
def pytype(self):
return self._proxied.qname()
class Continue(Statement):
"""class representing a Continue node"""
class Decorators(NodeNG):
"""class representing a Decorators node"""
_astng_fields = ('nodes',)
nodes = None
def __init__(self, nodes=None):
self.nodes = nodes
def scope(self):
# skip the function node to go directly to the upper level scope
return self.parent.parent.scope()
class DelAttr(NodeNG, ParentAssignTypeMixin):
"""class representing a DelAttr node"""
_astng_fields = ('expr',)
expr = None
class Delete(Statement, AssignTypeMixin):
"""class representing a Delete node"""
_astng_fields = ('targets',)
targets = None
class Dict(NodeNG, Instance):
"""class representing a Dict node"""
_astng_fields = ('items',)
def __init__(self, items=None):
if items is None:
self.items = []
else:
self.items = [(const_factory(k), const_factory(v))
for k,v in items.iteritems()]
def pytype(self):
return '%s.dict' % BUILTINS
def get_children(self):
"""get children of a Dict node"""
# overrides get_children
for key, value in self.items:
yield key
yield value
def last_child(self):
"""override last_child"""
if self.items:
return self.items[-1][1]
return None
def itered(self):
return self.items[::2]
def getitem(self, lookup_key, context=None):
for key, value in self.items:
for inferedkey in key.infer(context):
if inferedkey is YES:
continue
if isinstance(inferedkey, Const) and inferedkey.value == lookup_key:
return value
# This should raise KeyError, but all call sites only catch
# IndexError. Let's leave it like that for now.
raise IndexError(lookup_key)
class Discard(Statement):
"""class representing a Discard node"""
_astng_fields = ('value',)
value = None
class Ellipsis(NodeNG):
"""class representing an Ellipsis node"""
class EmptyNode(NodeNG):
"""class representing an EmptyNode node"""
class ExceptHandler(Statement, AssignTypeMixin):
"""class representing an ExceptHandler node"""
_astng_fields = ('type', 'name', 'body',)
type = None
name = None
body = None
def _blockstart_toline(self):
if self.name:
return self.name.tolineno
elif self.type:
return self.type.tolineno
else:
return self.lineno
def set_line_info(self, lastchild):
self.fromlineno = self.lineno
self.tolineno = lastchild.tolineno
self.blockstart_tolineno = self._blockstart_toline()
def catch(self, exceptions):
if self.type is None or exceptions is None:
return True
for node in self.type.nodes_of_class(Name):
if node.name in exceptions:
return True
class Exec(Statement):
"""class representing an Exec node"""
_astng_fields = ('expr', 'globals', 'locals',)
expr = None
globals = None
locals = None
class ExtSlice(NodeNG):
"""class representing an ExtSlice node"""
_astng_fields = ('dims',)
dims = None
class For(BlockRangeMixIn, AssignTypeMixin, Statement):
"""class representing a For node"""
_astng_fields = ('target', 'iter', 'body', 'orelse',)
target = None
iter = None
body = None
orelse = None
optional_assign = True
def _blockstart_toline(self):
return self.iter.tolineno
class From(FromImportMixIn, Statement):
"""class representing a From node"""
def __init__(self, fromname, names, level=0):
self.modname = fromname
self.names = names
self.level = level
class Getattr(NodeNG):
"""class representing a Getattr node"""
_astng_fields = ('expr',)
expr = None
class Global(Statement):
"""class representing a Global node"""
def __init__(self, names):
self.names = names
def _infer_name(self, frame, name):
return name
class If(BlockRangeMixIn, Statement):
"""class representing an If node"""
_astng_fields = ('test', 'body', 'orelse')
test = None
body = None
orelse = None
def _blockstart_toline(self):
return self.test.tolineno
def block_range(self, lineno):
"""handle block line numbers range for if statements"""
if lineno == self.body[0].fromlineno:
return lineno, lineno
if lineno <= self.body[-1].tolineno:
return lineno, self.body[-1].tolineno
return self._elsed_block_range(lineno, self.orelse,
self.body[0].fromlineno - 1)
class IfExp(NodeNG):
"""class representing an IfExp node"""
_astng_fields = ('test', 'body', 'orelse')
test = None
body = None
orelse = None
class Import(FromImportMixIn, Statement):
"""class representing an Import node"""
class Index(NodeNG):
"""class representing an Index node"""
_astng_fields = ('value',)
value = None
class Keyword(NodeNG):
"""class representing a Keyword node"""
_astng_fields = ('value',)
value = None
class List(NodeNG, Instance, ParentAssignTypeMixin):
"""class representing a List node"""
_astng_fields = ('elts',)
def __init__(self, elts=None):
if elts is None:
self.elts = []
else:
self.elts = [const_factory(e) for e in elts]
def pytype(self):
return '%s.list' % BUILTINS
def getitem(self, index, context=None):
return self.elts[index]
def itered(self):
return self.elts
class Nonlocal(Statement):
"""class representing a Nonlocal node"""
def __init__(self, names):
self.names = names
def _infer_name(self, frame, name):
return name
class Pass(Statement):
"""class representing a Pass node"""
class Print(Statement):
"""class representing a Print node"""
_astng_fields = ('dest', 'values',)
dest = None
values = None
class Raise(Statement):
"""class representing a Raise node"""
exc = None
if sys.version_info < (3, 0):
_astng_fields = ('exc', 'inst', 'tback')
inst = None
tback = None
else:
_astng_fields = ('exc', 'cause')
exc = None
cause = None
def raises_not_implemented(self):
if not self.exc:
return
for name in self.exc.nodes_of_class(Name):
if name.name == 'NotImplementedError':
return True
class Return(Statement):
"""class representing a Return node"""
_astng_fields = ('value',)
value = None
class Set(NodeNG, Instance, ParentAssignTypeMixin):
"""class representing a Set node"""
_astng_fields = ('elts',)
def __init__(self, elts=None):
if elts is None:
self.elts = []
else:
self.elts = [const_factory(e) for e in elts]
def pytype(self):
return '%s.set' % BUILTINS
def itered(self):
return self.elts
class Slice(NodeNG):
"""class representing a Slice node"""
_astng_fields = ('lower', 'upper', 'step')
lower = None
upper = None
step = None
class Starred(NodeNG, ParentAssignTypeMixin):
"""class representing a Starred node"""
_astng_fields = ('value',)
value = None
class Subscript(NodeNG):
"""class representing a Subscript node"""
_astng_fields = ('value', 'slice')
value = None
slice = None
class TryExcept(BlockRangeMixIn, Statement):
"""class representing a TryExcept node"""
_astng_fields = ('body', 'handlers', 'orelse',)
body = None
handlers = None
orelse = None
def _infer_name(self, frame, name):
return name
def _blockstart_toline(self):
return self.lineno
def block_range(self, lineno):
"""handle block line numbers range for try/except statements"""
last = None
for exhandler in self.handlers:
if exhandler.type and lineno == exhandler.type.fromlineno:
return lineno, lineno
if exhandler.body[0].fromlineno <= lineno <= exhandler.body[-1].tolineno:
return lineno, exhandler.body[-1].tolineno
if last is None:
last = exhandler.body[0].fromlineno - 1
return self._elsed_block_range(lineno, self.orelse, last)
class TryFinally(BlockRangeMixIn, Statement):
"""class representing a TryFinally node"""
_astng_fields = ('body', 'finalbody',)
body = None
finalbody = None
def _blockstart_toline(self):
return self.lineno
def block_range(self, lineno):
"""handle block line numbers range for try/finally statements"""
child = self.body[0]
# py2.5 try: except: finally:
if (isinstance(child, TryExcept) and child.fromlineno == self.fromlineno
and lineno > self.fromlineno and lineno <= child.tolineno):
return child.block_range(lineno)
return self._elsed_block_range(lineno, self.finalbody)
class Tuple(NodeNG, Instance, ParentAssignTypeMixin):
"""class representing a Tuple node"""
_astng_fields = ('elts',)
def __init__(self, elts=None):
if elts is None:
self.elts = []
else:
self.elts = [const_factory(e) for e in elts]
def pytype(self):
return '%s.tuple' % BUILTINS
def getitem(self, index, context=None):
return self.elts[index]
def itered(self):
return self.elts
class UnaryOp(NodeNG):
"""class representing an UnaryOp node"""
_astng_fields = ('operand',)
operand = None
class While(BlockRangeMixIn, Statement):
"""class representing a While node"""
_astng_fields = ('test', 'body', 'orelse',)
test = None
body = None
orelse = None
def _blockstart_toline(self):
return self.test.tolineno
def block_range(self, lineno):
"""handle block line numbers range for for and while statements"""
return self. _elsed_block_range(lineno, self.orelse)
class With(BlockRangeMixIn, AssignTypeMixin, Statement):
"""class representing a With node"""
_astng_fields = ('expr', 'vars', 'body')
expr = None
vars = None
body = None
def _blockstart_toline(self):
if self.vars:
return self.vars.tolineno
else:
return self.expr.tolineno
class Yield(NodeNG):
"""class representing a Yield node"""
_astng_fields = ('value',)
value = None
# constants ##############################################################
CONST_CLS = {
list: List,
tuple: Tuple,
dict: Dict,
set: Set,
type(None): Const,
}
def _update_const_classes():
"""update constant classes, so the keys of CONST_CLS can be reused"""
klasses = (bool, int, float, complex, str)
if sys.version_info < (3, 0):
klasses += (unicode, long)
if sys.version_info >= (2, 6):
klasses += (bytes,)
for kls in klasses:
CONST_CLS[kls] = Const
_update_const_classes()
def const_factory(value):
"""return an astng node for a python value"""
# XXX we should probably be stricter here and only consider stuff in
# CONST_CLS or do better treatment: in case where value is not in CONST_CLS,
# we should rather recall the builder on this value than returning an empty
# node (another option being that const_factory shouldn't be called with something
# not in CONST_CLS)
assert not isinstance(value, NodeNG)
try:
return CONST_CLS[value.__class__](value)
except (KeyError, AttributeError):
node = EmptyNode()
node.object = value
return node
logilab-astng-0.24.3/utils.py 0000644 0000151 0000155 00000020140 12133517265 015357 0 ustar narval narval # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of logilab-astng.
#
# logilab-astng is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 2.1 of the License, or (at your
# option) any later version.
#
# logilab-astng is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see .
"""this module contains some utilities to navigate in the tree or to
extract information from it
"""
__docformat__ = "restructuredtext en"
from logilab.astng.exceptions import ASTNGBuildingException
from logilab.astng.builder import parse
class ASTWalker:
"""a walker visiting a tree in preorder, calling on the handler:
* visit_ on entering a node, where class name is the class of
the node in lower case
* leave_ on leaving a node, where class name is the class of
the node in lower case
"""
def __init__(self, handler):
self.handler = handler
self._cache = {}
def walk(self, node, _done=None):
"""walk on the tree from , getting callbacks from handler"""
if _done is None:
_done = set()
if node in _done:
raise AssertionError((id(node), node, node.parent))
_done.add(node)
self.visit(node)
for child_node in node.get_children():
self.handler.set_context(node, child_node)
assert child_node is not node
self.walk(child_node, _done)
self.leave(node)
assert node.parent is not node
def get_callbacks(self, node):
"""get callbacks from handler for the visited node"""
klass = node.__class__
methods = self._cache.get(klass)
if methods is None:
handler = self.handler
kid = klass.__name__.lower()
e_method = getattr(handler, 'visit_%s' % kid,
getattr(handler, 'visit_default', None))
l_method = getattr(handler, 'leave_%s' % kid,
getattr(handler, 'leave_default', None))
self._cache[klass] = (e_method, l_method)
else:
e_method, l_method = methods
return e_method, l_method
def visit(self, node):
"""walk on the tree from , getting callbacks from handler"""
method = self.get_callbacks(node)[0]
if method is not None:
method(node)
def leave(self, node):
"""walk on the tree from , getting callbacks from handler"""
method = self.get_callbacks(node)[1]
if method is not None:
method(node)
class LocalsVisitor(ASTWalker):
"""visit a project by traversing the locals dictionary"""
def __init__(self):
ASTWalker.__init__(self, self)
self._visited = {}
def visit(self, node):
"""launch the visit starting from the given node"""
if node in self._visited:
return
self._visited[node] = 1 # FIXME: use set ?
methods = self.get_callbacks(node)
if methods[0] is not None:
methods[0](node)
if 'locals' in node.__dict__: # skip Instance and other proxy
for name, local_node in node.items():
self.visit(local_node)
if methods[1] is not None:
return methods[1](node)
def _check_children(node):
"""a helper function to check children - parent relations"""
for child in node.get_children():
ok = False
if child is None:
print "Hm, child of %s is None" % node
continue
if not hasattr(child, 'parent'):
print " ERROR: %s has child %s %x with no parent" % (node, child, id(child))
elif not child.parent:
print " ERROR: %s has child %s %x with parent %r" % (node, child, id(child), child.parent)
elif child.parent is not node:
print " ERROR: %s %x has child %s %x with wrong parent %s" % (node,
id(node), child, id(child), child.parent)
else:
ok = True
if not ok:
print "lines;", node.lineno, child.lineno
print "of module", node.root(), node.root().name
raise ASTNGBuildingException
_check_children(child)
class TreeTester(object):
'''A helper class to see _ast tree and compare with astng tree
indent: string for tree indent representation
lineno: bool to tell if we should print the line numbers
>>> tester = TreeTester('print')
>>> print tester.native_tree_repr()
. body = [
.
. . nl = True
. ]
>>> print tester.astng_tree_repr()
Module()
body = [
Print()
dest =
values = [
]
]
'''
indent = '. '
lineno = False
def __init__(self, sourcecode):
self._string = ''
self.sourcecode = sourcecode
self._ast_node = None
self.build_ast()
def build_ast(self):
"""build the _ast tree from the source code"""
self._ast_node = parse(self.sourcecode)
def native_tree_repr(self, node=None, indent=''):
"""get a nice representation of the _ast tree"""
self._string = ''
if node is None:
node = self._ast_node
self._native_repr_tree(node, indent)
return self._string
def _native_repr_tree(self, node, indent, _done=None):
"""recursive method for the native tree representation"""
from _ast import Load as _Load, Store as _Store, Del as _Del
from _ast import AST as Node
if _done is None:
_done = set()
if node in _done:
self._string += '\nloop in tree: %r (%s)' % (node,
getattr(node, 'lineno', None))
return
_done.add(node)
self._string += '\n' + indent + '<%s>' % node.__class__.__name__
indent += self.indent
if not hasattr(node, '__dict__'):
self._string += '\n' + self.indent + " ** node has no __dict__ " + str(node)
return
node_dict = node.__dict__
if hasattr(node, '_attributes'):
for a in node._attributes:
attr = node_dict[a]
if attr is None:
continue
if a in ("lineno", "col_offset") and not self.lineno:
continue
self._string +='\n' + indent + a + " = " + repr(attr)
for field in node._fields or ():
attr = node_dict[field]
if attr is None:
continue
if isinstance(attr, list):
if not attr:
continue
self._string += '\n' + indent + field + ' = ['
for elt in attr:
self._native_repr_tree(elt, indent, _done)
self._string += '\n' + indent + ']'
continue
if isinstance(attr, (_Load, _Store, _Del)):
continue
if isinstance(attr, Node):
self._string += '\n' + indent + field + " = "
self._native_repr_tree(attr, indent, _done)
else:
self._string += '\n' + indent + field + " = " + repr(attr)
def build_astng_tree(self):
"""build astng tree from the _ast tree
"""
from logilab.astng.builder import ASTNGBuilder
tree = ASTNGBuilder().string_build(self.sourcecode)
return tree
def astng_tree_repr(self, ids=False):
"""build the astng tree and return a nice tree representation"""
mod = self.build_astng_tree()
return mod.repr_tree(ids)
__all__ = ('LocalsVisitor', 'ASTWalker',)
logilab-astng-0.24.3/README.Python3 0000644 0000151 0000155 00000000746 12133517265 016102 0 ustar narval narval Python3
=======
Approach
--------
We maintain a Python 2 base and use 2to3 to generate Python 3 code.
2to3 is integrated into the distutils installation process and will be run as a
build step when invoked by the python3 interpreter::
python3 setup.py install --no-compile
Debian
------
For the Debian packaging, you can use the debian.py3k/ content against
the debian/ folder::
cp debian.py3k/* debian/
Resources
---------
http://wiki.python.org/moin/PortingPythonToPy3k
logilab-astng-0.24.3/__init__.py 0000644 0000151 0000155 00000005735 12133517265 015773 0 ustar narval narval # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of logilab-astng.
#
# logilab-astng is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 2.1 of the License, or (at your
# option) any later version.
#
# logilab-astng is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see .
"""Python Abstract Syntax Tree New Generation
The aim of this module is to provide a common base representation of
python source code for projects such as pychecker, pyreverse,
pylint... Well, actually the development of this library is essentially
governed by pylint's needs.
It extends class defined in the python's _ast module with some
additional methods and attributes. Instance attributes are added by a
builder object, which can either generate extended ast (let's call
them astng ;) by visiting an existent ast tree or by inspecting living
object. Methods are added by monkey patching ast classes.
Main modules are:
* nodes and scoped_nodes for more information about methods and
attributes added to different node classes
* the manager contains a high level object to get astng trees from
source files and living objects. It maintains a cache of previously
constructed tree for quick access
* builder contains the class responsible to build astng trees
"""
__doctype__ = "restructuredtext en"
import sys
# WARNING: internal imports order matters !
# make all exception classes accessible from astng package
from logilab.astng.exceptions import *
# make all node classes accessible from astng package
from logilab.astng.nodes import *
# trigger extra monkey-patching
from logilab.astng import inference
# more stuff available
from logilab.astng import raw_building
from logilab.astng.bases import YES, Instance, BoundMethod, UnboundMethod
from logilab.astng.node_classes import are_exclusive, unpack_infer
from logilab.astng.scoped_nodes import builtin_lookup
# make a manager instance (borg) as well as Project and Package classes
# accessible from astng package
from logilab.astng.manager import ASTNGManager, Project
MANAGER = ASTNGManager()
del ASTNGManager
# load brain plugins
from os import listdir
from os.path import join, dirname
BRAIN_MODULES_DIR = join(dirname(__file__), 'brain')
if BRAIN_MODULES_DIR not in sys.path:
# add it to the end of the list so user path take precedence
sys.path.append(BRAIN_MODULES_DIR)
# load modules in this directory
for module in listdir(BRAIN_MODULES_DIR):
if module.endswith('.py'):
__import__(module[:-3])
logilab-astng-0.24.3/scoped_nodes.py 0000644 0000151 0000155 00000103140 12133517265 016666 0 ustar narval narval # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of logilab-astng.
#
# logilab-astng is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 2.1 of the License, or (at your
# option) any later version.
#
# logilab-astng is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see .
"""This module contains the classes for "scoped" node, i.e. which are opening a
new local scope in the language definition : Module, Class, Function (and
Lambda, GenExpr, DictComp and SetComp to some extent).
"""
from __future__ import with_statement
__doctype__ = "restructuredtext en"
import sys
from itertools import chain
from logilab.common.compat import builtins
from logilab.common.decorators import cached
from logilab.astng.exceptions import NotFoundError, \
ASTNGBuildingException, InferenceError
from logilab.astng.node_classes import Const, DelName, DelAttr, \
Dict, From, List, Pass, Raise, Return, Tuple, Yield, \
LookupMixIn, const_factory as cf, unpack_infer
from logilab.astng.bases import NodeNG, InferenceContext, Instance,\
YES, Generator, UnboundMethod, BoundMethod, _infer_stmts, copy_context, \
BUILTINS
from logilab.astng.mixins import FilterStmtsMixin
from logilab.astng.bases import Statement
from logilab.astng.manager import ASTNGManager
def remove_nodes(func, cls):
def wrapper(*args, **kwargs):
nodes = [n for n in func(*args, **kwargs) if not isinstance(n, cls)]
if not nodes:
raise NotFoundError()
return nodes
return wrapper
def function_to_method(n, klass):
if isinstance(n, Function):
if n.type == 'classmethod':
return BoundMethod(n, klass)
if n.type != 'staticmethod':
return UnboundMethod(n)
return n
def std_special_attributes(self, name, add_locals=True):
if add_locals:
locals = self.locals
else:
locals = {}
if name == '__name__':
return [cf(self.name)] + locals.get(name, [])
if name == '__doc__':
return [cf(self.doc)] + locals.get(name, [])
if name == '__dict__':
return [Dict()] + locals.get(name, [])
raise NotFoundError(name)
MANAGER = ASTNGManager()
def builtin_lookup(name):
"""lookup a name into the builtin module
return the list of matching statements and the astng for the builtin
module
"""
builtin_astng = MANAGER.astng_from_module(builtins)
if name == '__dict__':
return builtin_astng, ()
try:
stmts = builtin_astng.locals[name]
except KeyError:
stmts = ()
return builtin_astng, stmts
# TODO move this Mixin to mixins.py; problem: 'Function' in _scope_lookup
class LocalsDictNodeNG(LookupMixIn, NodeNG):
""" this class provides locals handling common to Module, Function
and Class nodes, including a dict like interface for direct access
to locals information
"""
# attributes below are set by the builder module or by raw factories
# dictionary of locals with name as key and node defining the local as
# value
def qname(self):
"""return the 'qualified' name of the node, eg module.name,
module.class.name ...
"""
if self.parent is None:
return self.name
return '%s.%s' % (self.parent.frame().qname(), self.name)
def frame(self):
"""return the first parent frame node (i.e. Module, Function or Class)
"""
return self
def scope(self):
"""return the first node defining a new scope (i.e. Module,
Function, Class, Lambda but also GenExpr, DictComp and SetComp)
"""
return self
def _scope_lookup(self, node, name, offset=0):
"""XXX method for interfacing the scope lookup"""
try:
stmts = node._filter_stmts(self.locals[name], self, offset)
except KeyError:
stmts = ()
if stmts:
return self, stmts
if self.parent: # i.e. not Module
# nested scope: if parent scope is a function, that's fine
# else jump to the module
pscope = self.parent.scope()
if not pscope.is_function:
pscope = pscope.root()
return pscope.scope_lookup(node, name)
return builtin_lookup(name) # Module
def set_local(self, name, stmt):
"""define in locals ( is the node defining the name)
if the node is a Module node (i.e. has globals), add the name to
globals
if the name is already defined, ignore it
"""
#assert not stmt in self.locals.get(name, ()), (self, stmt)
self.locals.setdefault(name, []).append(stmt)
__setitem__ = set_local
def _append_node(self, child):
"""append a child, linking it in the tree"""
self.body.append(child)
child.parent = self
def add_local_node(self, child_node, name=None):
"""append a child which should alter locals to the given node"""
if name != '__class__':
# add __class__ node as a child will cause infinite recursion later!
self._append_node(child_node)
self.set_local(name or child_node.name, child_node)
def __getitem__(self, item):
"""method from the `dict` interface returning the first node
associated with the given name in the locals dictionary
:type item: str
:param item: the name of the locally defined object
:raises KeyError: if the name is not defined
"""
return self.locals[item][0]
def __iter__(self):
"""method from the `dict` interface returning an iterator on
`self.keys()`
"""
return iter(self.keys())
def keys(self):
"""method from the `dict` interface returning a tuple containing
locally defined names
"""
return self.locals.keys()
def values(self):
"""method from the `dict` interface returning a tuple containing
locally defined nodes which are instance of `Function` or `Class`
"""
return [self[key] for key in self.keys()]
def items(self):
"""method from the `dict` interface returning a list of tuple
containing each locally defined name with its associated node,
which is an instance of `Function` or `Class`
"""
return zip(self.keys(), self.values())
def __contains__(self, name):
return name in self.locals
has_key = __contains__
# Module #####################################################################
class Module(LocalsDictNodeNG):
_astng_fields = ('body',)
fromlineno = 0
lineno = 0
# attributes below are set by the builder module or by raw factories
# the file from which as been extracted the astng representation. It may
# be None if the representation has been built from a built-in module
file = None
# encoding of python source file, so we can get unicode out of it (python2
# only)
file_encoding = None
# the module name
name = None
# boolean for astng built from source (i.e. ast)
pure_python = None
# boolean for package module
package = None
# dictionary of globals with name as key and node defining the global
# as value
globals = None
# names of python special attributes (handled by getattr impl.)
special_attributes = set(('__name__', '__doc__', '__file__', '__path__',
'__dict__'))
# names of module attributes available through the global scope
scope_attrs = set(('__name__', '__doc__', '__file__', '__path__'))
def __init__(self, name, doc, pure_python=True):
self.name = name
self.doc = doc
self.pure_python = pure_python
self.locals = self.globals = {}
self.body = []
@property
def file_stream(self):
if self.file is not None:
return open(self.file)
return None
def block_range(self, lineno):
"""return block line numbers.
start from the beginning whatever the given lineno
"""
return self.fromlineno, self.tolineno
def scope_lookup(self, node, name, offset=0):
if name in self.scope_attrs and not name in self.locals:
try:
return self, self.getattr(name)
except NotFoundError:
return self, ()
return self._scope_lookup(node, name, offset)
def pytype(self):
return '%s.module' % BUILTINS
def display_type(self):
return 'Module'
def getattr(self, name, context=None, ignore_locals=False):
if name in self.special_attributes:
if name == '__file__':
return [cf(self.file)] + self.locals.get(name, [])
if name == '__path__' and self.package:
return [List()] + self.locals.get(name, [])
return std_special_attributes(self, name)
if not ignore_locals and name in self.locals:
return self.locals[name]
if self.package:
try:
return [self.import_module(name, relative_only=True)]
except ASTNGBuildingException:
raise NotFoundError(name)
except Exception:# XXX pylint tests never pass here; do we need it?
import traceback
traceback.print_exc()
raise NotFoundError(name)
getattr = remove_nodes(getattr, DelName)
def igetattr(self, name, context=None):
"""inferred getattr"""
# set lookup name since this is necessary to infer on import nodes for
# instance
context = copy_context(context)
context.lookupname = name
try:
return _infer_stmts(self.getattr(name, context), context, frame=self)
except NotFoundError:
raise InferenceError(name)
def fully_defined(self):
"""return True if this module has been built from a .py file
and so contains a complete representation including the code
"""
return self.file is not None and self.file.endswith('.py')
def statement(self):
"""return the first parent node marked as statement node
consider a module as a statement...
"""
return self
def previous_sibling(self):
"""module has no sibling"""
return
def next_sibling(self):
"""module has no sibling"""
return
if sys.version_info < (2, 8):
def absolute_import_activated(self):
for stmt in self.locals.get('absolute_import', ()):
if isinstance(stmt, From) and stmt.modname == '__future__':
return True
return False
else:
absolute_import_activated = lambda self: True
def import_module(self, modname, relative_only=False, level=None):
"""import the given module considering self as context"""
if relative_only and level is None:
level = 0
absmodname = self.relative_to_absolute_name(modname, level)
try:
return MANAGER.astng_from_module_name(absmodname)
except ASTNGBuildingException:
# we only want to import a sub module or package of this module,
# skip here
if relative_only:
raise
return MANAGER.astng_from_module_name(modname)
def relative_to_absolute_name(self, modname, level):
"""return the absolute module name for a relative import.
The relative import can be implicit or explicit.
"""
# XXX this returns non sens when called on an absolute import
# like 'pylint.checkers.logilab.astng.utils'
# XXX doesn't return absolute name if self.name isn't absolute name
if self.absolute_import_activated() and level is None:
return modname
if level:
if self.package:
level = level - 1
package_name = self.name.rsplit('.', level)[0]
elif self.package:
package_name = self.name
else:
package_name = self.name.rsplit('.', 1)[0]
if package_name:
if not modname:
return package_name
return '%s.%s' % (package_name, modname)
return modname
def wildcard_import_names(self):
"""return the list of imported names when this module is 'wildcard
imported'
It doesn't include the '__builtins__' name which is added by the
current CPython implementation of wildcard imports.
"""
# take advantage of a living module if it exists
try:
living = sys.modules[self.name]
except KeyError:
pass
else:
try:
return living.__all__
except AttributeError:
return [name for name in living.__dict__.keys()
if not name.startswith('_')]
# else lookup the astng
#
# We separate the different steps of lookup in try/excepts
# to avoid catching too many Exceptions
# However, we can not analyse dynamically constructed __all__
try:
all = self['__all__']
except KeyError:
return [name for name in self.keys() if not name.startswith('_')]
try:
explicit = all.assigned_stmts().next()
except InferenceError:
return [name for name in self.keys() if not name.startswith('_')]
except AttributeError:
# not an assignment node
# XXX infer?
return [name for name in self.keys() if not name.startswith('_')]
try:
# should be a Tuple/List of constant string / 1 string not allowed
return [const.value for const in explicit.elts]
except AttributeError:
return [name for name in self.keys() if not name.startswith('_')]
class ComprehensionScope(LocalsDictNodeNG):
def frame(self):
return self.parent.frame()
scope_lookup = LocalsDictNodeNG._scope_lookup
class GenExpr(ComprehensionScope):
_astng_fields = ('elt', 'generators')
def __init__(self):
self.locals = {}
self.elt = None
self.generators = []
class DictComp(ComprehensionScope):
_astng_fields = ('key', 'value', 'generators')
def __init__(self):
self.locals = {}
self.key = None
self.value = None
self.generators = []
class SetComp(ComprehensionScope):
_astng_fields = ('elt', 'generators')
def __init__(self):
self.locals = {}
self.elt = None
self.generators = []
class _ListComp(NodeNG):
"""class representing a ListComp node"""
_astng_fields = ('elt', 'generators')
elt = None
generators = None
if sys.version_info >= (3, 0):
class ListComp(_ListComp, ComprehensionScope):
"""class representing a ListComp node"""
def __init__(self):
self.locals = {}
else:
class ListComp(_ListComp):
"""class representing a ListComp node"""
# Function ###################################################################
class Lambda(LocalsDictNodeNG, FilterStmtsMixin):
_astng_fields = ('args', 'body',)
name = ''
# function's type, 'function' | 'method' | 'staticmethod' | 'classmethod'
type = 'function'
def __init__(self):
self.locals = {}
self.args = []
self.body = []
def pytype(self):
if 'method' in self.type:
return '%s.instancemethod' % BUILTINS
return '%s.function' % BUILTINS
def display_type(self):
if 'method' in self.type:
return 'Method'
return 'Function'
def callable(self):
return True
def argnames(self):
"""return a list of argument names"""
if self.args.args: # maybe None with builtin functions
names = _rec_get_names(self.args.args)
else:
names = []
if self.args.vararg:
names.append(self.args.vararg)
if self.args.kwarg:
names.append(self.args.kwarg)
return names
def infer_call_result(self, caller, context=None):
"""infer what a function is returning when called"""
return self.body.infer(context)
def scope_lookup(self, node, name, offset=0):
if node in self.args.defaults:
frame = self.parent.frame()
# line offset to avoid that def func(f=func) resolve the default
# value to the defined function
offset = -1
else:
# check this is not used in function decorators
frame = self
return frame._scope_lookup(node, name, offset)
class Function(Statement, Lambda):
_astng_fields = ('decorators', 'args', 'body')
special_attributes = set(('__name__', '__doc__', '__dict__'))
is_function = True
# attributes below are set by the builder module or by raw factories
blockstart_tolineno = None
decorators = None
def __init__(self, name, doc):
self.locals = {}
self.args = []
self.body = []
self.decorators = None
self.name = name
self.doc = doc
self.extra_decorators = []
self.instance_attrs = {}
def set_line_info(self, lastchild):
self.fromlineno = self.lineno
# lineno is the line number of the first decorator, we want the def statement lineno
if self.decorators is not None:
self.fromlineno += sum(node.tolineno - node.lineno + 1
for node in self.decorators.nodes)
self.tolineno = lastchild.tolineno
self.blockstart_tolineno = self.args.tolineno
def block_range(self, lineno):
"""return block line numbers.
start from the "def" position whatever the given lineno
"""
return self.fromlineno, self.tolineno
def getattr(self, name, context=None):
"""this method doesn't look in the instance_attrs dictionary since it's
done by an Instance proxy at inference time.
"""
if name == '__module__':
return [cf(self.root().qname())]
if name in self.instance_attrs:
return self.instance_attrs[name]
return std_special_attributes(self, name, False)
def is_method(self):
"""return true if the function node should be considered as a method"""
# check we are defined in a Class, because this is usually expected
# (e.g. pylint...) when is_method() return True
return self.type != 'function' and isinstance(self.parent.frame(), Class)
def decoratornames(self):
"""return a list of decorator qualified names"""
result = set()
decoratornodes = []
if self.decorators is not None:
decoratornodes += self.decorators.nodes
decoratornodes += self.extra_decorators
for decnode in decoratornodes:
for infnode in decnode.infer():
result.add(infnode.qname())
return result
decoratornames = cached(decoratornames)
def is_bound(self):
"""return true if the function is bound to an Instance or a class"""
return self.type == 'classmethod'
def is_abstract(self, pass_is_abstract=True):
"""return true if the method is abstract
It's considered as abstract if the only statement is a raise of
NotImplementError, or, if pass_is_abstract, a pass statement
"""
for child_node in self.body:
if isinstance(child_node, Raise):
if child_node.raises_not_implemented():
return True
if pass_is_abstract and isinstance(child_node, Pass):
return True
return False
# empty function is the same as function with a single "pass" statement
if pass_is_abstract:
return True
def is_generator(self):
"""return true if this is a generator function"""
# XXX should be flagged, not computed
try:
return self.nodes_of_class(Yield, skip_klass=(Function, Lambda)).next()
except StopIteration:
return False
def infer_call_result(self, caller, context=None):
"""infer what a function is returning when called"""
if self.is_generator():
yield Generator()
return
returns = self.nodes_of_class(Return, skip_klass=Function)
for returnnode in returns:
if returnnode.value is None:
yield Const(None)
else:
try:
for infered in returnnode.value.infer(context):
yield infered
except InferenceError:
yield YES
def _rec_get_names(args, names=None):
"""return a list of all argument names"""
if names is None:
names = []
for arg in args:
if isinstance(arg, Tuple):
_rec_get_names(arg.elts, names)
else:
names.append(arg.name)
return names
# Class ######################################################################
def _class_type(klass, ancestors=None):
"""return a Class node type to differ metaclass, interface and exception
from 'regular' classes
"""
# XXX we have to store ancestors in case we have a ancestor loop
if klass._type is not None:
return klass._type
if klass.name == 'type':
klass._type = 'metaclass'
elif klass.name.endswith('Interface'):
klass._type = 'interface'
elif klass.name.endswith('Exception'):
klass._type = 'exception'
else:
if ancestors is None:
ancestors = set()
if klass in ancestors:
# XXX we are in loop ancestors, and have found no type
klass._type = 'class'
return 'class'
ancestors.add(klass)
# print >> sys.stderr, '_class_type', repr(klass)
for base in klass.ancestors(recurs=False):
if _class_type(base, ancestors) != 'class':
klass._type = base.type
break
if klass._type is None:
klass._type = 'class'
return klass._type
def _iface_hdlr(iface_node):
"""a handler function used by interfaces to handle suspicious
interface nodes
"""
return True
class Class(Statement, LocalsDictNodeNG, FilterStmtsMixin):
# some of the attributes below are set by the builder module or
# by a raw factories
# a dictionary of class instances attributes
_astng_fields = ('decorators', 'bases', 'body') # name
decorators = None
special_attributes = set(('__name__', '__doc__', '__dict__', '__module__',
'__bases__', '__mro__', '__subclasses__'))
blockstart_tolineno = None
_type = None
type = property(_class_type,
doc="class'type, possible values are 'class' | "
"'metaclass' | 'interface' | 'exception'")
def __init__(self, name, doc):
self.instance_attrs = {}
self.locals = {}
self.bases = []
self.body = []
self.name = name
self.doc = doc
def _newstyle_impl(self, context=None):
if context is None:
context = InferenceContext()
if self._newstyle is not None:
return self._newstyle
for base in self.ancestors(recurs=False, context=context):
if base._newstyle_impl(context):
self._newstyle = True
break
if self._newstyle is None:
self._newstyle = False
return self._newstyle
_newstyle = None
newstyle = property(_newstyle_impl,
doc="boolean indicating if it's a new style class"
"or not")
def set_line_info(self, lastchild):
self.fromlineno = self.lineno
self.blockstart_tolineno = self.bases and self.bases[-1].tolineno or self.fromlineno
if lastchild is not None:
self.tolineno = lastchild.tolineno
# else this is a class with only a docstring, then tolineno is (should be) already ok
def block_range(self, lineno):
"""return block line numbers.
start from the "class" position whatever the given lineno
"""
return self.fromlineno, self.tolineno
def pytype(self):
if self.newstyle:
return '%s.type' % BUILTINS
return '%s.classobj' % BUILTINS
def display_type(self):
return 'Class'
def callable(self):
return True
def infer_call_result(self, caller, context=None):
"""infer what a class is returning when called"""
yield Instance(self)
def scope_lookup(self, node, name, offset=0):
if node in self.bases:
frame = self.parent.frame()
# line offset to avoid that class A(A) resolve the ancestor to
# the defined class
offset = -1
else:
frame = self
return frame._scope_lookup(node, name, offset)
# list of parent class as a list of string (i.e. names as they appear
# in the class definition) XXX bw compat
def basenames(self):
return [bnode.as_string() for bnode in self.bases]
basenames = property(basenames)
def ancestors(self, recurs=True, context=None):
"""return an iterator on the node base classes in a prefixed
depth first order
:param recurs:
boolean indicating if it should recurse or return direct
ancestors only
"""
# FIXME: should be possible to choose the resolution order
# XXX inference make infinite loops possible here (see BaseTransformer
# manipulation in the builder module for instance)
yielded = set([self])
if context is None:
context = InferenceContext()
for stmt in self.bases:
with context.restore_path():
try:
for baseobj in stmt.infer(context):
if not isinstance(baseobj, Class):
# duh ?
continue
if baseobj in yielded:
continue # cf xxx above
yielded.add(baseobj)
yield baseobj
if recurs:
for grandpa in baseobj.ancestors(True, context):
if grandpa in yielded:
continue # cf xxx above
yielded.add(grandpa)
yield grandpa
except InferenceError:
# XXX log error ?
continue
def local_attr_ancestors(self, name, context=None):
"""return an iterator on astng representation of parent classes
which have defined in their locals
"""
for astng in self.ancestors(context=context):
if name in astng:
yield astng
def instance_attr_ancestors(self, name, context=None):
"""return an iterator on astng representation of parent classes
which have defined in their instance attribute dictionary
"""
for astng in self.ancestors(context=context):
if name in astng.instance_attrs:
yield astng
def has_base(self, node):
return node in self.bases
def local_attr(self, name, context=None):
"""return the list of assign node associated to name in this class
locals or in its parents
:raises `NotFoundError`:
if no attribute with this name has been find in this class or
its parent classes
"""
try:
return self.locals[name]
except KeyError:
# get if from the first parent implementing it if any
for class_node in self.local_attr_ancestors(name, context):
return class_node.locals[name]
raise NotFoundError(name)
local_attr = remove_nodes(local_attr, DelAttr)
def instance_attr(self, name, context=None):
"""return the astng nodes associated to name in this class instance
attributes dictionary and in its parents
:raises `NotFoundError`:
if no attribute with this name has been find in this class or
its parent classes
"""
values = self.instance_attrs.get(name, [])
# get all values from parents
for class_node in self.instance_attr_ancestors(name, context):
values += class_node.instance_attrs[name]
if not values:
raise NotFoundError(name)
return values
instance_attr = remove_nodes(instance_attr, DelAttr)
def instanciate_class(self):
"""return Instance of Class node, else return self"""
return Instance(self)
def getattr(self, name, context=None):
"""this method doesn't look in the instance_attrs dictionary since it's
done by an Instance proxy at inference time.
It may return a YES object if the attribute has not been actually
found but a __getattr__ or __getattribute__ method is defined
"""
values = self.locals.get(name, [])
if name in self.special_attributes:
if name == '__module__':
return [cf(self.root().qname())] + values
# FIXME: do we really need the actual list of ancestors?
# returning [Tuple()] + values don't break any test
# this is ticket http://www.logilab.org/ticket/52785
# XXX need proper meta class handling + MRO implementation
if name == '__bases__' or (name == '__mro__' and self.newstyle):
node = Tuple()
node.items = self.ancestors(recurs=True, context=context)
return [node] + values
return std_special_attributes(self, name)
# don't modify the list in self.locals!
values = list(values)
for classnode in self.ancestors(recurs=True, context=context):
values += classnode.locals.get(name, [])
if not values:
raise NotFoundError(name)
return values
def igetattr(self, name, context=None):
"""inferred getattr, need special treatment in class to handle
descriptors
"""
# set lookup name since this is necessary to infer on import nodes for
# instance
context = copy_context(context)
context.lookupname = name
try:
for infered in _infer_stmts(self.getattr(name, context), context,
frame=self):
# yield YES object instead of descriptors when necessary
if not isinstance(infered, Const) and isinstance(infered, Instance):
try:
infered._proxied.getattr('__get__', context)
except NotFoundError:
yield infered
else:
yield YES
else:
yield function_to_method(infered, self)
except NotFoundError:
if not name.startswith('__') and self.has_dynamic_getattr(context):
# class handle some dynamic attributes, return a YES object
yield YES
else:
raise InferenceError(name)
def has_dynamic_getattr(self, context=None):
"""return True if the class has a custom __getattr__ or
__getattribute__ method
"""
# need to explicitly handle optparse.Values (setattr is not detected)
if self.name == 'Values' and self.root().name == 'optparse':
return True
try:
self.getattr('__getattr__', context)
return True
except NotFoundError:
#if self.newstyle: XXX cause an infinite recursion error
try:
getattribute = self.getattr('__getattribute__', context)[0]
if getattribute.root().name != BUILTINS:
# class has a custom __getattribute__ defined
return True
except NotFoundError:
pass
return False
def methods(self):
"""return an iterator on all methods defined in the class and
its ancestors
"""
done = {}
for astng in chain(iter((self,)), self.ancestors()):
for meth in astng.mymethods():
if meth.name in done:
continue
done[meth.name] = None
yield meth
def mymethods(self):
"""return an iterator on all methods defined in the class"""
for member in self.values():
if isinstance(member, Function):
yield member
def interfaces(self, herited=True, handler_func=_iface_hdlr):
"""return an iterator on interfaces implemented by the given
class node
"""
# FIXME: what if __implements__ = (MyIFace, MyParent.__implements__)...
try:
implements = Instance(self).getattr('__implements__')[0]
except NotFoundError:
return
if not herited and not implements.frame() is self:
return
found = set()
missing = False
for iface in unpack_infer(implements):
if iface is YES:
missing = True
continue
if not iface in found and handler_func(iface):
found.add(iface)
yield iface
if missing:
raise InferenceError()
logilab-astng-0.24.3/__pkginfo__.py 0000644 0000151 0000155 00000003506 12133517265 016457 0 ustar narval narval # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of logilab-astng.
#
# logilab-astng is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 2.1 of the License, or (at your
# option) any later version.
#
# logilab-astng is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see .
"""logilab.astng packaging information"""
distname = 'logilab-astng'
modname = 'astng'
subpackage_of = 'logilab'
numversion = (0, 24, 3)
version = '.'.join([str(num) for num in numversion])
install_requires = ['logilab-common >= 0.53.0']
license = 'LGPL'
author = 'Logilab'
author_email = 'python-projects@lists.logilab.org'
mailinglist = "mailto://%s" % author_email
web = "http://www.logilab.org/project/%s" % distname
ftp = "ftp://ftp.logilab.org/pub/%s" % modname
description = "rebuild a new abstract syntax tree from Python's ast"
from os.path import join
include_dirs = ['brain',
join('test', 'regrtest_data'),
join('test', 'data'), join('test', 'data2')]
classifiers = ["Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Software Development :: Quality Assurance",
"Programming Language :: Python",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 3",
]
logilab-astng-0.24.3/rebuilder.py 0000644 0000151 0000155 00000107445 12133517265 016212 0 ustar narval narval # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of logilab-astng.
#
# logilab-astng is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 2.1 of the License, or (at your
# option) any later version.
#
# logilab-astng is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see .
"""this module contains utilities for rebuilding a _ast tree in
order to get a single ASTNG representation
"""
import sys
from _ast import (Expr as Discard, Str,
# binary operators
Add, Div, FloorDiv, Mod, Mult, Pow, Sub, BitAnd, BitOr, BitXor,
LShift, RShift,
# logical operators
And, Or,
# unary operators
UAdd, USub, Not, Invert,
# comparison operators
Eq, Gt, GtE, In, Is, IsNot, Lt, LtE, NotEq, NotIn,
)
from logilab.astng import nodes as new
_BIN_OP_CLASSES = {Add: '+',
BitAnd: '&',
BitOr: '|',
BitXor: '^',
Div: '/',
FloorDiv: '//',
Mod: '%',
Mult: '*',
Pow: '**',
Sub: '-',
LShift: '<<',
RShift: '>>'}
_BOOL_OP_CLASSES = {And: 'and',
Or: 'or'}
_UNARY_OP_CLASSES = {UAdd: '+',
USub: '-',
Not: 'not',
Invert: '~'}
_CMP_OP_CLASSES = {Eq: '==',
Gt: '>',
GtE: '>=',
In: 'in',
Is: 'is',
IsNot: 'is not',
Lt: '<',
LtE: '<=',
NotEq: '!=',
NotIn: 'not in'}
CONST_NAME_TRANSFORMS = {'None': None,
'True': True,
'False': False}
REDIRECT = {'arguments': 'Arguments',
'Attribute': 'Getattr',
'comprehension': 'Comprehension',
'Call': 'CallFunc',
'ClassDef': 'Class',
"ListCompFor": 'Comprehension',
"GenExprFor": 'Comprehension',
'excepthandler': 'ExceptHandler',
'Expr': 'Discard',
'FunctionDef': 'Function',
'GeneratorExp': 'GenExpr',
'ImportFrom': 'From',
'keyword': 'Keyword',
'Repr': 'Backquote',
}
def _init_set_doc(node, newnode):
newnode.doc = None
try:
if isinstance(node.body[0], Discard) and isinstance(node.body[0].value, Str):
newnode.tolineno = node.body[0].lineno
newnode.doc = node.body[0].value.s
node.body = node.body[1:]
except IndexError:
pass # ast built from scratch
def _lineno_parent(oldnode, newnode, parent):
newnode.parent = parent
if hasattr(oldnode, 'lineno'):
newnode.lineno = oldnode.lineno
if hasattr(oldnode, 'col_offset'):
newnode.col_offset = oldnode.col_offset
def _set_infos(oldnode, newnode, parent):
newnode.parent = parent
if hasattr(oldnode, 'lineno'):
newnode.lineno = oldnode.lineno
if hasattr(oldnode, 'col_offset'):
newnode.col_offset = oldnode.col_offset
newnode.set_line_info(newnode.last_child()) # set_line_info accepts None
class TreeRebuilder(object):
"""Rebuilds the _ast tree to become an ASTNG tree"""
_visit_meths = {}
def __init__(self):
self.init()
def init(self):
self.asscontext = None
self._metaclass = ['']
self._global_names = []
self._from_nodes = []
self._delayed_assattr = []
def visit(self, node, parent):
cls = node.__class__
if cls in self._visit_meths:
return self._visit_meths[cls](node, parent)
else:
cls_name = cls.__name__
visit_name = 'visit_' + REDIRECT.get(cls_name, cls_name).lower()
visit_method = getattr(self, visit_name)
self._visit_meths[cls] = visit_method
return visit_method(node, parent)
def _save_assignment(self, node, name=None):
"""save assignement situation since node.parent is not available yet"""
if self._global_names and node.name in self._global_names[-1]:
node.root().set_local(node.name, node)
else:
node.parent.set_local(node.name, node)
def visit_arguments(self, node, parent):
"""visit a Arguments node by returning a fresh instance of it"""
newnode = new.Arguments()
_lineno_parent(node, newnode, parent)
self.asscontext = "Ass"
newnode.args = [self.visit(child, newnode) for child in node.args]
self.asscontext = None
newnode.defaults = [self.visit(child, newnode) for child in node.defaults]
newnode.vararg = node.vararg
newnode.kwarg = node.kwarg
# save argument names in locals:
if node.vararg:
newnode.parent.set_local(newnode.vararg, newnode)
if node.kwarg:
newnode.parent.set_local(newnode.kwarg, newnode)
newnode.set_line_info(newnode.last_child())
return newnode
def visit_assattr(self, node, parent):
"""visit a AssAttr node by returning a fresh instance of it"""
assc, self.asscontext = self.asscontext, None
newnode = new.AssAttr()
_lineno_parent(node, newnode, parent)
newnode.expr = self.visit(node.expr, newnode)
self.asscontext = assc
self._delayed_assattr.append(newnode)
newnode.set_line_info(newnode.last_child())
return newnode
def visit_assert(self, node, parent):
"""visit a Assert node by returning a fresh instance of it"""
newnode = new.Assert()
_lineno_parent(node, newnode, parent)
newnode.test = self.visit(node.test, newnode)
if node.msg is not None:
newnode.fail = self.visit(node.msg, newnode)
newnode.set_line_info(newnode.last_child())
return newnode
def visit_assign(self, node, parent):
"""visit a Assign node by returning a fresh instance of it"""
newnode = new.Assign()
_lineno_parent(node, newnode, parent)
self.asscontext = "Ass"
newnode.targets = [self.visit(child, newnode) for child in node.targets]
self.asscontext = None
newnode.value = self.visit(node.value, newnode)
# set some function or metaclass infos XXX explain ?
klass = newnode.parent.frame()
if (isinstance(klass, new.Class)
and isinstance(newnode.value, new.CallFunc)
and isinstance(newnode.value.func, new.Name)):
func_name = newnode.value.func.name
for ass_node in newnode.targets:
try:
meth = klass[ass_node.name]
if isinstance(meth, new.Function):
if func_name in ('classmethod', 'staticmethod'):
meth.type = func_name
elif func_name == 'classproperty': # see lgc.decorators
meth.type = 'classmethod'
meth.extra_decorators.append(newnode.value)
except (AttributeError, KeyError):
continue
elif getattr(newnode.targets[0], 'name', None) == '__metaclass__':
# XXX check more...
self._metaclass[-1] = 'type' # XXX get the actual metaclass
newnode.set_line_info(newnode.last_child())
return newnode
def visit_assname(self, node, parent, node_name=None):
'''visit a node and return a AssName node'''
newnode = new.AssName()
_set_infos(node, newnode, parent)
newnode.name = node_name
self._save_assignment(newnode)
return newnode
def visit_augassign(self, node, parent):
"""visit a AugAssign node by returning a fresh instance of it"""
newnode = new.AugAssign()
_lineno_parent(node, newnode, parent)
newnode.op = _BIN_OP_CLASSES[node.op.__class__] + "="
self.asscontext = "Ass"
newnode.target = self.visit(node.target, newnode)
self.asscontext = None
newnode.value = self.visit(node.value, newnode)
newnode.set_line_info(newnode.last_child())
return newnode
def visit_backquote(self, node, parent):
"""visit a Backquote node by returning a fresh instance of it"""
newnode = new.Backquote()
_lineno_parent(node, newnode, parent)
newnode.value = self.visit(node.value, newnode)
newnode.set_line_info(newnode.last_child())
return newnode
def visit_binop(self, node, parent):
"""visit a BinOp node by returning a fresh instance of it"""
newnode = new.BinOp()
_lineno_parent(node, newnode, parent)
newnode.left = self.visit(node.left, newnode)
newnode.right = self.visit(node.right, newnode)
newnode.op = _BIN_OP_CLASSES[node.op.__class__]
newnode.set_line_info(newnode.last_child())
return newnode
def visit_boolop(self, node, parent):
"""visit a BoolOp node by returning a fresh instance of it"""
newnode = new.BoolOp()
_lineno_parent(node, newnode, parent)
newnode.values = [self.visit(child, newnode) for child in node.values]
newnode.op = _BOOL_OP_CLASSES[node.op.__class__]
newnode.set_line_info(newnode.last_child())
return newnode
def visit_break(self, node, parent):
"""visit a Break node by returning a fresh instance of it"""
newnode = new.Break()
_set_infos(node, newnode, parent)
return newnode
def visit_callfunc(self, node, parent):
"""visit a CallFunc node by returning a fresh instance of it"""
newnode = new.CallFunc()
_lineno_parent(node, newnode, parent)
newnode.func = self.visit(node.func, newnode)
newnode.args = [self.visit(child, newnode) for child in node.args]
if node.starargs is not None:
newnode.starargs = self.visit(node.starargs, newnode)
if node.kwargs is not None:
newnode.kwargs = self.visit(node.kwargs, newnode)
newnode.args.extend(self.visit(child, newnode) for child in node.keywords)
newnode.set_line_info(newnode.last_child())
return newnode
def visit_class(self, node, parent):
"""visit a Class node to become astng"""
self._metaclass.append(self._metaclass[-1])
newnode = new.Class(node.name, None)
_lineno_parent(node, newnode, parent)
_init_set_doc(node, newnode)
newnode.bases = [self.visit(child, newnode) for child in node.bases]
newnode.body = [self.visit(child, newnode) for child in node.body]
if 'decorator_list' in node._fields and node.decorator_list:# py >= 2.6
newnode.decorators = self.visit_decorators(node, newnode)
newnode.set_line_info(newnode.last_child())
metaclass = self._metaclass.pop()
if not newnode.bases:
# no base classes, detect new / style old style according to
# current scope
newnode._newstyle = metaclass == 'type'
newnode.parent.frame().set_local(newnode.name, newnode)
return newnode
def visit_const(self, node, parent):
"""visit a Const node by returning a fresh instance of it"""
newnode = new.Const(node.value)
_set_infos(node, newnode, parent)
return newnode
def visit_continue(self, node, parent):
"""visit a Continue node by returning a fresh instance of it"""
newnode = new.Continue()
_set_infos(node, newnode, parent)
return newnode
def visit_compare(self, node, parent):
"""visit a Compare node by returning a fresh instance of it"""
newnode = new.Compare()
_lineno_parent(node, newnode, parent)
newnode.left = self.visit(node.left, newnode)
newnode.ops = [(_CMP_OP_CLASSES[op.__class__], self.visit(expr, newnode))
for (op, expr) in zip(node.ops, node.comparators)]
newnode.set_line_info(newnode.last_child())
return newnode
def visit_comprehension(self, node, parent):
"""visit a Comprehension node by returning a fresh instance of it"""
newnode = new.Comprehension()
_lineno_parent(node, newnode, parent)
self.asscontext = "Ass"
newnode.target = self.visit(node.target, newnode)
self.asscontext = None
newnode.iter = self.visit(node.iter, newnode)
newnode.ifs = [self.visit(child, newnode) for child in node.ifs]
newnode.set_line_info(newnode.last_child())
return newnode
def visit_decorators(self, node, parent):
"""visit a Decorators node by returning a fresh instance of it"""
# /!\ node is actually a _ast.Function node while
# parent is a astng.nodes.Function node
newnode = new.Decorators()
_lineno_parent(node, newnode, parent)
if 'decorators' in node._fields: # py < 2.6, i.e. 2.5
decorators = node.decorators
else:
decorators= node.decorator_list
newnode.nodes = [self.visit(child, newnode) for child in decorators]
newnode.set_line_info(newnode.last_child())
return newnode
def visit_delete(self, node, parent):
"""visit a Delete node by returning a fresh instance of it"""
newnode = new.Delete()
_lineno_parent(node, newnode, parent)
self.asscontext = "Del"
newnode.targets = [self.visit(child, newnode) for child in node.targets]
self.asscontext = None
newnode.set_line_info(newnode.last_child())
return newnode
def visit_dict(self, node, parent):
"""visit a Dict node by returning a fresh instance of it"""
newnode = new.Dict()
_lineno_parent(node, newnode, parent)
newnode.items = [(self.visit(key, newnode), self.visit(value, newnode))
for key, value in zip(node.keys, node.values)]
newnode.set_line_info(newnode.last_child())
return newnode
def visit_dictcomp(self, node, parent):
"""visit a DictComp node by returning a fresh instance of it"""
newnode = new.DictComp()
_lineno_parent(node, newnode, parent)
newnode.key = self.visit(node.key, newnode)
newnode.value = self.visit(node.value, newnode)
newnode.generators = [self.visit(child, newnode)
for child in node.generators]
newnode.set_line_info(newnode.last_child())
return newnode
def visit_discard(self, node, parent):
"""visit a Discard node by returning a fresh instance of it"""
newnode = new.Discard()
_lineno_parent(node, newnode, parent)
newnode.value = self.visit(node.value, newnode)
newnode.set_line_info(newnode.last_child())
return newnode
def visit_ellipsis(self, node, parent):
"""visit an Ellipsis node by returning a fresh instance of it"""
newnode = new.Ellipsis()
_set_infos(node, newnode, parent)
return newnode
def visit_emptynode(self, node, parent):
"""visit an EmptyNode node by returning a fresh instance of it"""
newnode = new.EmptyNode()
_set_infos(node, newnode, parent)
return newnode
def visit_excepthandler(self, node, parent):
"""visit an ExceptHandler node by returning a fresh instance of it"""
newnode = new.ExceptHandler()
_lineno_parent(node, newnode, parent)
if node.type is not None:
newnode.type = self.visit(node.type, newnode)
if node.name is not None:
# /!\ node.name can be a tuple
self.asscontext = "Ass"
newnode.name = self.visit(node.name, newnode)
self.asscontext = None
newnode.body = [self.visit(child, newnode) for child in node.body]
newnode.set_line_info(newnode.last_child())
return newnode
def visit_exec(self, node, parent):
"""visit an Exec node by returning a fresh instance of it"""
newnode = new.Exec()
_lineno_parent(node, newnode, parent)
newnode.expr = self.visit(node.body, newnode)
if node.globals is not None:
newnode.globals = self.visit(node.globals, newnode)
if node.locals is not None:
newnode.locals = self.visit(node.locals, newnode)
newnode.set_line_info(newnode.last_child())
return newnode
def visit_extslice(self, node, parent):
"""visit an ExtSlice node by returning a fresh instance of it"""
newnode = new.ExtSlice()
_lineno_parent(node, newnode, parent)
newnode.dims = [self.visit(dim, newnode) for dim in node.dims]
newnode.set_line_info(newnode.last_child())
return newnode
def visit_for(self, node, parent):
"""visit a For node by returning a fresh instance of it"""
newnode = new.For()
_lineno_parent(node, newnode, parent)
self.asscontext = "Ass"
newnode.target = self.visit(node.target, newnode)
self.asscontext = None
newnode.iter = self.visit(node.iter, newnode)
newnode.body = [self.visit(child, newnode) for child in node.body]
newnode.orelse = [self.visit(child, newnode) for child in node.orelse]
newnode.set_line_info(newnode.last_child())
return newnode
def visit_from(self, node, parent):
"""visit a From node by returning a fresh instance of it"""
names = [(alias.name, alias.asname) for alias in node.names]
newnode = new.From(node.module or '', names, node.level or None)
_set_infos(node, newnode, parent)
# store From names to add them to locals after building
self._from_nodes.append(newnode)
return newnode
def visit_function(self, node, parent):
"""visit an Function node to become astng"""
self._global_names.append({})
newnode = new.Function(node.name, None)
_lineno_parent(node, newnode, parent)
_init_set_doc(node, newnode)
newnode.args = self.visit(node.args, newnode)
newnode.body = [self.visit(child, newnode) for child in node.body]
if 'decorators' in node._fields: # py < 2.6
attr = 'decorators'
else:
attr = 'decorator_list'
decorators = getattr(node, attr)
if decorators:
newnode.decorators = self.visit_decorators(node, newnode)
newnode.set_line_info(newnode.last_child())
self._global_names.pop()
frame = newnode.parent.frame()
if isinstance(frame, new.Class):
if newnode.name == '__new__':
newnode.type = 'classmethod'
else:
newnode.type = 'method'
if newnode.decorators is not None:
for decorator_expr in newnode.decorators.nodes:
if isinstance(decorator_expr, new.Name):
if decorator_expr.name in ('classmethod', 'staticmethod'):
newnode.type = decorator_expr.name
elif decorator_expr.name == 'classproperty':
newnode.type = 'classmethod'
frame.set_local(newnode.name, newnode)
return newnode
def visit_genexpr(self, node, parent):
"""visit a GenExpr node by returning a fresh instance of it"""
newnode = new.GenExpr()
_lineno_parent(node, newnode, parent)
newnode.elt = self.visit(node.elt, newnode)
newnode.generators = [self.visit(child, newnode) for child in node.generators]
newnode.set_line_info(newnode.last_child())
return newnode
def visit_getattr(self, node, parent):
"""visit a Getattr node by returning a fresh instance of it"""
if self.asscontext == "Del":
# FIXME : maybe we should reintroduce and visit_delattr ?
# for instance, deactivating asscontext
newnode = new.DelAttr()
elif self.asscontext == "Ass":
# FIXME : maybe we should call visit_assattr ?
newnode = new.AssAttr()
self._delayed_assattr.append(newnode)
else:
newnode = new.Getattr()
_lineno_parent(node, newnode, parent)
asscontext, self.asscontext = self.asscontext, None
newnode.expr = self.visit(node.value, newnode)
self.asscontext = asscontext
newnode.attrname = node.attr
newnode.set_line_info(newnode.last_child())
return newnode
def visit_global(self, node, parent):
"""visit an Global node to become astng"""
newnode = new.Global(node.names)
_set_infos(node, newnode, parent)
if self._global_names: # global at the module level, no effect
for name in node.names:
self._global_names[-1].setdefault(name, []).append(newnode)
return newnode
def visit_if(self, node, parent):
"""visit a If node by returning a fresh instance of it"""
newnode = new.If()
_lineno_parent(node, newnode, parent)
newnode.test = self.visit(node.test, newnode)
newnode.body = [self.visit(child, newnode) for child in node.body]
newnode.orelse = [self.visit(child, newnode) for child in node.orelse]
newnode.set_line_info(newnode.last_child())
return newnode
def visit_ifexp(self, node, parent):
"""visit a IfExp node by returning a fresh instance of it"""
newnode = new.IfExp()
_lineno_parent(node, newnode, parent)
newnode.test = self.visit(node.test, newnode)
newnode.body = self.visit(node.body, newnode)
newnode.orelse = self.visit(node.orelse, newnode)
newnode.set_line_info(newnode.last_child())
return newnode
def visit_import(self, node, parent):
"""visit a Import node by returning a fresh instance of it"""
newnode = new.Import()
_set_infos(node, newnode, parent)
newnode.names = [(alias.name, alias.asname) for alias in node.names]
# save import names in parent's locals:
for (name, asname) in newnode.names:
name = asname or name
newnode.parent.set_local(name.split('.')[0], newnode)
return newnode
def visit_index(self, node, parent):
"""visit a Index node by returning a fresh instance of it"""
newnode = new.Index()
_lineno_parent(node, newnode, parent)
newnode.value = self.visit(node.value, newnode)
newnode.set_line_info(newnode.last_child())
return newnode
def visit_keyword(self, node, parent):
"""visit a Keyword node by returning a fresh instance of it"""
newnode = new.Keyword()
_lineno_parent(node, newnode, parent)
newnode.arg = node.arg
newnode.value = self.visit(node.value, newnode)
newnode.set_line_info(newnode.last_child())
return newnode
def visit_lambda(self, node, parent):
"""visit a Lambda node by returning a fresh instance of it"""
newnode = new.Lambda()
_lineno_parent(node, newnode, parent)
newnode.args = self.visit(node.args, newnode)
newnode.body = self.visit(node.body, newnode)
newnode.set_line_info(newnode.last_child())
return newnode
def visit_list(self, node, parent):
"""visit a List node by returning a fresh instance of it"""
newnode = new.List()
_lineno_parent(node, newnode, parent)
newnode.elts = [self.visit(child, newnode) for child in node.elts]
newnode.set_line_info(newnode.last_child())
return newnode
def visit_listcomp(self, node, parent):
"""visit a ListComp node by returning a fresh instance of it"""
newnode = new.ListComp()
_lineno_parent(node, newnode, parent)
newnode.elt = self.visit(node.elt, newnode)
newnode.generators = [self.visit(child, newnode)
for child in node.generators]
newnode.set_line_info(newnode.last_child())
return newnode
def visit_module(self, node, modname, package):
"""visit a Module node by returning a fresh instance of it"""
newnode = new.Module(modname, None)
newnode.package = package
_lineno_parent(node, newnode, parent=None)
_init_set_doc(node, newnode)
newnode.body = [self.visit(child, newnode) for child in node.body]
newnode.set_line_info(newnode.last_child())
return newnode
def visit_name(self, node, parent):
"""visit a Name node by returning a fresh instance of it"""
# True and False can be assigned to something in py2x, so we have to
# check first the asscontext
if self.asscontext == "Del":
newnode = new.DelName()
elif self.asscontext is not None: # Ass
assert self.asscontext == "Ass"
newnode = new.AssName()
elif node.id in CONST_NAME_TRANSFORMS:
newnode = new.Const(CONST_NAME_TRANSFORMS[node.id])
_set_infos(node, newnode, parent)
return newnode
else:
newnode = new.Name()
_lineno_parent(node, newnode, parent)
newnode.name = node.id
# XXX REMOVE me :
if self.asscontext in ('Del', 'Ass'): # 'Aug' ??
self._save_assignment(newnode)
newnode.set_line_info(newnode.last_child())
return newnode
def visit_bytes(self, node, parent):
"""visit a Bytes node by returning a fresh instance of Const"""
newnode = new.Const(node.s)
_set_infos(node, newnode, parent)
return newnode
def visit_num(self, node, parent):
"""visit a Num node by returning a fresh instance of Const"""
newnode = new.Const(node.n)
_set_infos(node, newnode, parent)
return newnode
def visit_pass(self, node, parent):
"""visit a Pass node by returning a fresh instance of it"""
newnode = new.Pass()
_set_infos(node, newnode, parent)
return newnode
def visit_str(self, node, parent):
"""visit a Str node by returning a fresh instance of Const"""
newnode = new.Const(node.s)
_set_infos(node, newnode, parent)
return newnode
def visit_print(self, node, parent):
"""visit a Print node by returning a fresh instance of it"""
newnode = new.Print()
_lineno_parent(node, newnode, parent)
newnode.nl = node.nl
if node.dest is not None:
newnode.dest = self.visit(node.dest, newnode)
newnode.values = [self.visit(child, newnode) for child in node.values]
newnode.set_line_info(newnode.last_child())
return newnode
def visit_raise(self, node, parent):
"""visit a Raise node by returning a fresh instance of it"""
newnode = new.Raise()
_lineno_parent(node, newnode, parent)
if node.type is not None:
newnode.exc = self.visit(node.type, newnode)
if node.inst is not None:
newnode.inst = self.visit(node.inst, newnode)
if node.tback is not None:
newnode.tback = self.visit(node.tback, newnode)
newnode.set_line_info(newnode.last_child())
return newnode
def visit_return(self, node, parent):
"""visit a Return node by returning a fresh instance of it"""
newnode = new.Return()
_lineno_parent(node, newnode, parent)
if node.value is not None:
newnode.value = self.visit(node.value, newnode)
newnode.set_line_info(newnode.last_child())
return newnode
def visit_set(self, node, parent):
"""visit a Set node by returning a fresh instance of it"""
newnode = new.Set()
_lineno_parent(node, newnode, parent)
newnode.elts = [self.visit(child, newnode) for child in node.elts]
newnode.set_line_info(newnode.last_child())
return newnode
def visit_setcomp(self, node, parent):
"""visit a SetComp node by returning a fresh instance of it"""
newnode = new.SetComp()
_lineno_parent(node, newnode, parent)
newnode.elt = self.visit(node.elt, newnode)
newnode.generators = [self.visit(child, newnode)
for child in node.generators]
newnode.set_line_info(newnode.last_child())
return newnode
def visit_slice(self, node, parent):
"""visit a Slice node by returning a fresh instance of it"""
newnode = new.Slice()
_lineno_parent(node, newnode, parent)
if node.lower is not None:
newnode.lower = self.visit(node.lower, newnode)
if node.upper is not None:
newnode.upper = self.visit(node.upper, newnode)
if node.step is not None:
newnode.step = self.visit(node.step, newnode)
newnode.set_line_info(newnode.last_child())
return newnode
def visit_subscript(self, node, parent):
"""visit a Subscript node by returning a fresh instance of it"""
newnode = new.Subscript()
_lineno_parent(node, newnode, parent)
subcontext, self.asscontext = self.asscontext, None
newnode.value = self.visit(node.value, newnode)
newnode.slice = self.visit(node.slice, newnode)
self.asscontext = subcontext
newnode.set_line_info(newnode.last_child())
return newnode
def visit_tryexcept(self, node, parent):
"""visit a TryExcept node by returning a fresh instance of it"""
newnode = new.TryExcept()
_lineno_parent(node, newnode, parent)
newnode.body = [self.visit(child, newnode) for child in node.body]
newnode.handlers = [self.visit(child, newnode) for child in node.handlers]
newnode.orelse = [self.visit(child, newnode) for child in node.orelse]
newnode.set_line_info(newnode.last_child())
return newnode
def visit_tryfinally(self, node, parent):
"""visit a TryFinally node by returning a fresh instance of it"""
newnode = new.TryFinally()
_lineno_parent(node, newnode, parent)
newnode.body = [self.visit(child, newnode) for child in node.body]
newnode.finalbody = [self.visit(n, newnode) for n in node.finalbody]
newnode.set_line_info(newnode.last_child())
return newnode
def visit_tuple(self, node, parent):
"""visit a Tuple node by returning a fresh instance of it"""
newnode = new.Tuple()
_lineno_parent(node, newnode, parent)
newnode.elts = [self.visit(child, newnode) for child in node.elts]
newnode.set_line_info(newnode.last_child())
return newnode
def visit_unaryop(self, node, parent):
"""visit a UnaryOp node by returning a fresh instance of it"""
newnode = new.UnaryOp()
_lineno_parent(node, newnode, parent)
newnode.operand = self.visit(node.operand, newnode)
newnode.op = _UNARY_OP_CLASSES[node.op.__class__]
newnode.set_line_info(newnode.last_child())
return newnode
def visit_while(self, node, parent):
"""visit a While node by returning a fresh instance of it"""
newnode = new.While()
_lineno_parent(node, newnode, parent)
newnode.test = self.visit(node.test, newnode)
newnode.body = [self.visit(child, newnode) for child in node.body]
newnode.orelse = [self.visit(child, newnode) for child in node.orelse]
newnode.set_line_info(newnode.last_child())
return newnode
def visit_with(self, node, parent):
newnode = new.With()
_lineno_parent(node, newnode, parent)
_node = getattr(node, 'items', [node])[0] # python 3.3 XXX
newnode.expr = self.visit(_node.context_expr, newnode)
self.asscontext = "Ass"
if _node.optional_vars is not None:
newnode.vars = self.visit(_node.optional_vars, newnode)
self.asscontext = None
newnode.body = [self.visit(child, newnode) for child in node.body]
newnode.set_line_info(newnode.last_child())
return newnode
def visit_yield(self, node, parent):
"""visit a Yield node by returning a fresh instance of it"""
newnode = new.Yield()
_lineno_parent(node, newnode, parent)
if node.value is not None:
newnode.value = self.visit(node.value, newnode)
newnode.set_line_info(newnode.last_child())
return newnode
class TreeRebuilder3k(TreeRebuilder):
"""extend and overwrite TreeRebuilder for python3k"""
def visit_arg(self, node, parent):
"""visit a arg node by returning a fresh AssName instance"""
# the node is coming from py>=3.0, but we use AssName in py2.x
# XXX or we should instead introduce a Arg node in astng ?
return self.visit_assname(node, parent, node.arg)
def visit_excepthandler(self, node, parent):
"""visit an ExceptHandler node by returning a fresh instance of it"""
newnode = new.ExceptHandler()
_lineno_parent(node, newnode, parent)
if node.type is not None:
newnode.type = self.visit(node.type, newnode)
if node.name is not None:
newnode.name = self.visit_assname(node, newnode, node.name)
newnode.body = [self.visit(child, newnode) for child in node.body]
newnode.set_line_info(newnode.last_child())
return newnode
def visit_nonlocal(self, node, parent):
"""visit a Nonlocal node and return a new instance of it"""
newnode = new.Nonlocal(node.names)
_set_infos(node, newnode, parent)
return newnode
def visit_raise(self, node, parent):
"""visit a Raise node by returning a fresh instance of it"""
newnode = new.Raise()
_lineno_parent(node, newnode, parent)
# no traceback; anyway it is not used in Pylint
if node.exc is not None:
newnode.exc = self.visit(node.exc, newnode)
if node.cause is not None:
newnode.cause = self.visit(node.cause, newnode)
newnode.set_line_info(newnode.last_child())
return newnode
def visit_starred(self, node, parent):
"""visit a Starred node and return a new instance of it"""
newnode = new.Starred()
_lineno_parent(node, newnode, parent)
newnode.value = self.visit(node.value, newnode)
newnode.set_line_info(newnode.last_child())
return newnode
def visit_try(self, node, parent):
# python 3.3 introduce a new Try node replacing TryFinally/TryExcept nodes
if node.finalbody:
newnode = new.TryFinally()
_lineno_parent(node, newnode, parent)
newnode.finalbody = [self.visit(n, newnode) for n in node.finalbody]
if node.handlers:
excnode = new.TryExcept()
_lineno_parent(node, excnode, parent)
excnode.body = [self.visit(child, newnode) for child in node.body]
excnode.handlers = [self.visit(child, newnode) for child in node.handlers]
excnode.orelse = [self.visit(child, newnode) for child in node.orelse]
newnode.body = [excnode]
else:
newnode.body = [self.visit(child, newnode) for child in node.body]
elif node.handlers:
newnode = new.TryExcept()
_lineno_parent(node, newnode, parent)
newnode.body = [self.visit(child, newnode) for child in node.body]
newnode.handlers = [self.visit(child, newnode) for child in node.handlers]
newnode.orelse = [self.visit(child, newnode) for child in node.orelse]
newnode.set_line_info(newnode.last_child())
return newnode
def visit_yieldfrom(self, node, parent):
return self.visit_yield(node, parent)
if sys.version_info >= (3, 0):
TreeRebuilder = TreeRebuilder3k
logilab-astng-0.24.3/inspector.py 0000644 0000151 0000155 00000022361 12133517265 016234 0 ustar narval narval # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of logilab-astng.
#
# logilab-astng is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 2.1 of the License, or (at your
# option) any later version.
#
# logilab-astng is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see .
"""visitor doing some postprocessing on the astng tree.
Try to resolve definitions (namespace) dictionary, relationship...
This module has been imported from pyreverse
"""
__docformat__ = "restructuredtext en"
from os.path import dirname
from logilab.common.modutils import get_module_part, is_relative, \
is_standard_module
from logilab import astng
from logilab.astng.exceptions import InferenceError
from logilab.astng.utils import LocalsVisitor
class IdGeneratorMixIn:
"""
Mixin adding the ability to generate integer uid
"""
def __init__(self, start_value=0):
self.id_count = start_value
def init_counter(self, start_value=0):
"""init the id counter
"""
self.id_count = start_value
def generate_id(self):
"""generate a new identifier
"""
self.id_count += 1
return self.id_count
class Linker(IdGeneratorMixIn, LocalsVisitor):
"""
walk on the project tree and resolve relationships.
According to options the following attributes may be added to visited nodes:
* uid,
a unique identifier for the node (on astng.Project, astng.Module,
astng.Class and astng.locals_type). Only if the linker has been instantiated
with tag=True parameter (False by default).
* Function
a mapping from locals names to their bounded value, which may be a
constant like a string or an integer, or an astng node (on astng.Module,
astng.Class and astng.Function).
* instance_attrs_type
as locals_type but for klass member attributes (only on astng.Class)
* implements,
list of implemented interface _objects_ (only on astng.Class nodes)
"""
def __init__(self, project, inherited_interfaces=0, tag=False):
IdGeneratorMixIn.__init__(self)
LocalsVisitor.__init__(self)
# take inherited interface in consideration or not
self.inherited_interfaces = inherited_interfaces
# tag nodes or not
self.tag = tag
# visited project
self.project = project
def visit_project(self, node):
"""visit an astng.Project node
* optionally tag the node with a unique id
"""
if self.tag:
node.uid = self.generate_id()
for module in node.modules:
self.visit(module)
def visit_package(self, node):
"""visit an astng.Package node
* optionally tag the node with a unique id
"""
if self.tag:
node.uid = self.generate_id()
for subelmt in node.values():
self.visit(subelmt)
def visit_module(self, node):
"""visit an astng.Module node
* set the locals_type mapping
* set the depends mapping
* optionally tag the node with a unique id
"""
if hasattr(node, 'locals_type'):
return
node.locals_type = {}
node.depends = []
if self.tag:
node.uid = self.generate_id()
def visit_class(self, node):
"""visit an astng.Class node
* set the locals_type and instance_attrs_type mappings
* set the implements list and build it
* optionally tag the node with a unique id
"""
if hasattr(node, 'locals_type'):
return
node.locals_type = {}
if self.tag:
node.uid = self.generate_id()
# resolve ancestors
for baseobj in node.ancestors(recurs=False):
specializations = getattr(baseobj, 'specializations', [])
specializations.append(node)
baseobj.specializations = specializations
# resolve instance attributes
node.instance_attrs_type = {}
for assattrs in node.instance_attrs.values():
for assattr in assattrs:
self.handle_assattr_type(assattr, node)
# resolve implemented interface
try:
node.implements = list(node.interfaces(self.inherited_interfaces))
except InferenceError:
node.implements = ()
def visit_function(self, node):
"""visit an astng.Function node
* set the locals_type mapping
* optionally tag the node with a unique id
"""
if hasattr(node, 'locals_type'):
return
node.locals_type = {}
if self.tag:
node.uid = self.generate_id()
link_project = visit_project
link_module = visit_module
link_class = visit_class
link_function = visit_function
def visit_assname(self, node):
"""visit an astng.AssName node
handle locals_type
"""
# avoid double parsing done by different Linkers.visit
# running over the same project:
if hasattr(node, '_handled'):
return
node._handled = True
if node.name in node.frame():
frame = node.frame()
else:
# the name has been defined as 'global' in the frame and belongs
# there. Btw the frame is not yet visited as the name is in the
# root locals; the frame hence has no locals_type attribute
frame = node.root()
try:
values = node.infered()
try:
already_infered = frame.locals_type[node.name]
for valnode in values:
if not valnode in already_infered:
already_infered.append(valnode)
except KeyError:
frame.locals_type[node.name] = values
except astng.InferenceError:
pass
def handle_assattr_type(self, node, parent):
"""handle an astng.AssAttr node
handle instance_attrs_type
"""
try:
values = list(node.infer())
try:
already_infered = parent.instance_attrs_type[node.attrname]
for valnode in values:
if not valnode in already_infered:
already_infered.append(valnode)
except KeyError:
parent.instance_attrs_type[node.attrname] = values
except astng.InferenceError:
pass
def visit_import(self, node):
"""visit an astng.Import node
resolve module dependencies
"""
context_file = node.root().file
for name in node.names:
relative = is_relative(name[0], context_file)
self._imported_module(node, name[0], relative)
def visit_from(self, node):
"""visit an astng.From node
resolve module dependencies
"""
basename = node.modname
context_file = node.root().file
if context_file is not None:
relative = is_relative(basename, context_file)
else:
relative = False
for name in node.names:
if name[0] == '*':
continue
# analyze dependencies
fullname = '%s.%s' % (basename, name[0])
if fullname.find('.') > -1:
try:
# XXX: don't use get_module_part, missing package precedence
fullname = get_module_part(fullname, context_file)
except ImportError:
continue
if fullname != basename:
self._imported_module(node, fullname, relative)
def compute_module(self, context_name, mod_path):
"""return true if the module should be added to dependencies"""
package_dir = dirname(self.project.path)
if context_name == mod_path:
return 0
elif is_standard_module(mod_path, (package_dir,)):
return 1
return 0
# protected methods ########################################################
def _imported_module(self, node, mod_path, relative):
"""notify an imported module, used to analyze dependencies
"""
module = node.root()
context_name = module.name
if relative:
mod_path = '%s.%s' % ('.'.join(context_name.split('.')[:-1]),
mod_path)
if self.compute_module(context_name, mod_path):
# handle dependencies
if not hasattr(module, 'depends'):
module.depends = []
mod_paths = module.depends
if not mod_path in mod_paths:
mod_paths.append(mod_path)
logilab-astng-0.24.3/mixins.py 0000644 0000151 0000155 00000010372 12133517265 015534 0 ustar narval narval # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of logilab-astng.
#
# logilab-astng is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 2.1 of the License, or (at your
# option) any later version.
#
# logilab-astng is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see .
"""This module contains some mixins for the different nodes.
"""
from logilab.astng.exceptions import (ASTNGBuildingException, InferenceError,
NotFoundError)
class BlockRangeMixIn(object):
"""override block range """
def set_line_info(self, lastchild):
self.fromlineno = self.lineno
self.tolineno = lastchild.tolineno
self.blockstart_tolineno = self._blockstart_toline()
def _elsed_block_range(self, lineno, orelse, last=None):
"""handle block line numbers range for try/finally, for, if and while
statements
"""
if lineno == self.fromlineno:
return lineno, lineno
if orelse:
if lineno >= orelse[0].fromlineno:
return lineno, orelse[-1].tolineno
return lineno, orelse[0].fromlineno - 1
return lineno, last or self.tolineno
class FilterStmtsMixin(object):
"""Mixin for statement filtering and assignment type"""
def _get_filtered_stmts(self, _, node, _stmts, mystmt):
"""method used in _filter_stmts to get statemtents and trigger break"""
if self.statement() is mystmt:
# original node's statement is the assignment, only keep
# current node (gen exp, list comp)
return [node], True
return _stmts, False
def ass_type(self):
return self
class AssignTypeMixin(object):
def ass_type(self):
return self
def _get_filtered_stmts(self, lookup_node, node, _stmts, mystmt):
"""method used in filter_stmts"""
if self is mystmt:
return _stmts, True
if self.statement() is mystmt:
# original node's statement is the assignment, only keep
# current node (gen exp, list comp)
return [node], True
return _stmts, False
class ParentAssignTypeMixin(AssignTypeMixin):
def ass_type(self):
return self.parent.ass_type()
class FromImportMixIn(FilterStmtsMixin):
"""MixIn for From and Import Nodes"""
def _infer_name(self, frame, name):
return name
def do_import_module(self, modname):
"""return the ast for a module whose name is imported by
"""
# handle special case where we are on a package node importing a module
# using the same name as the package, which may end in an infinite loop
# on relative imports
# XXX: no more needed ?
mymodule = self.root()
level = getattr(self, 'level', None) # Import as no level
# XXX we should investigate deeper if we really want to check
# importing itself: modname and mymodule.name be relative or absolute
if mymodule.relative_to_absolute_name(modname, level) == mymodule.name:
# FIXME: we used to raise InferenceError here, but why ?
return mymodule
try:
return mymodule.import_module(modname, level=level)
except ASTNGBuildingException:
raise InferenceError(modname)
except SyntaxError, ex:
raise InferenceError(str(ex))
def real_name(self, asname):
"""get name from 'as' name"""
for name, _asname in self.names:
if name == '*':
return asname
if not _asname:
name = name.split('.', 1)[0]
_asname = name
if asname == _asname:
return name
raise NotFoundError(asname)
logilab-astng-0.24.3/COPYING 0000644 0000151 0000155 00000043103 12133517265 014704 0 ustar narval narval GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Copyright (C)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
logilab-astng-0.24.3/setup.py 0000644 0000151 0000155 00000015276 12133517265 015375 0 ustar narval narval #!/usr/bin/env python
# pylint: disable=W0404,W0622,W0704,W0613
# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of logilab-astng.
#
# logilab-astng is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, either version 2.1 of the License, or (at your option) any
# later version.
#
# logilab-astng is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see .
"""Generic Setup script, takes package info from __pkginfo__.py file.
"""
__docformat__ = "restructuredtext en"
import os
import sys
import shutil
from os.path import isdir, exists, join
try:
if os.environ.get('NO_SETUPTOOLS'):
raise ImportError()
from setuptools import setup
from setuptools.command import install_lib
USE_SETUPTOOLS = 1
except ImportError:
from distutils.core import setup
from distutils.command import install_lib
USE_SETUPTOOLS = 0
try:
# python3
from distutils.command.build_py import build_py_2to3 as build_py
except ImportError:
# python2.x
from distutils.command.build_py import build_py
sys.modules.pop('__pkginfo__', None)
# import optional features
__pkginfo__ = __import__("__pkginfo__")
# import required features
from __pkginfo__ import modname, version, license, description, \
web, author, author_email
distname = getattr(__pkginfo__, 'distname', modname)
scripts = getattr(__pkginfo__, 'scripts', [])
data_files = getattr(__pkginfo__, 'data_files', None)
subpackage_of = getattr(__pkginfo__, 'subpackage_of', None)
include_dirs = getattr(__pkginfo__, 'include_dirs', [])
ext_modules = getattr(__pkginfo__, 'ext_modules', None)
install_requires = getattr(__pkginfo__, 'install_requires', None)
dependency_links = getattr(__pkginfo__, 'dependency_links', [])
classifiers = getattr(__pkginfo__, 'classifiers', [])
STD_BLACKLIST = ('CVS', '.svn', '.hg', 'debian', 'dist', 'build')
IGNORED_EXTENSIONS = ('.pyc', '.pyo', '.elc', '~')
if exists('README'):
long_description = open('README').read()
else:
long_description = ''
def ensure_scripts(linux_scripts):
"""Creates the proper script names required for each platform
(taken from 4Suite)
"""
from distutils import util
if util.get_platform()[:3] == 'win':
scripts_ = [script + '.bat' for script in linux_scripts]
else:
scripts_ = linux_scripts
return scripts_
def get_packages(directory, prefix):
"""return a list of subpackages for the given directory"""
result = []
for package in os.listdir(directory):
absfile = join(directory, package)
if isdir(absfile):
if exists(join(absfile, '__init__.py')) or \
package in ('test', 'tests'):
if prefix:
result.append('%s.%s' % (prefix, package))
else:
result.append(package)
result += get_packages(absfile, result[-1])
return result
EMPTY_FILE = '''"""generated file, don't modify or your data will be lost"""
try:
__import__('pkg_resources').declare_namespace(__name__)
except ImportError:
pass
'''
class MyInstallLib(install_lib.install_lib):
"""extend install_lib command to handle package __init__.py if necessary
"""
def run(self):
"""overridden from install_lib class"""
install_lib.install_lib.run(self)
# create Products.__init__.py if needed
if subpackage_of:
product_init = join(self.install_dir, subpackage_of, '__init__.py')
if not exists(product_init):
self.announce('creating %s' % product_init)
stream = open(product_init, 'w')
stream.write(EMPTY_FILE)
stream.close()
class MyBuildPy(build_py):
"""extend build_by command to handle include_dirs variable if necessary
"""
def run(self):
"""overridden from install_lib class"""
build_py.run(self)
# manually install included directories if any
if include_dirs:
if subpackage_of:
base = join(subpackage_of, modname)
else:
base = modname
basedir = os.path.join(self.build_lib, base)
for directory in include_dirs:
dest = join(basedir, directory)
shutil.rmtree(dest, ignore_errors=True)
shutil.copytree(directory, dest)
if sys.version_info >= (3, 0):
# process manually python file in include_dirs (test data)
from subprocess import check_call
print('running 2to3 on', dest) # parens are NOT optional here for py3k compat
check_call(['2to3', '-wn', dest])
def install(**kwargs):
"""setup entry point"""
if USE_SETUPTOOLS:
if '--force-manifest' in sys.argv:
sys.argv.remove('--force-manifest')
# install-layout option was introduced in 2.5.3-1~exp1
elif sys.version_info < (2, 5, 4) and '--install-layout=deb' in sys.argv:
sys.argv.remove('--install-layout=deb')
if subpackage_of:
package = subpackage_of + '.' + modname
kwargs['package_dir'] = {package : '.'}
packages = [package] + get_packages(os.getcwd(), package)
if USE_SETUPTOOLS:
kwargs['namespace_packages'] = [subpackage_of]
else:
kwargs['package_dir'] = {modname : '.'}
packages = [modname] + get_packages(os.getcwd(), modname)
if USE_SETUPTOOLS and install_requires:
kwargs['install_requires'] = install_requires
kwargs['dependency_links'] = dependency_links
kwargs['packages'] = packages
return setup(name = distname,
version = version,
license = license,
description = description,
long_description = long_description,
classifiers = classifiers,
author = author,
author_email = author_email,
url = web,
scripts = ensure_scripts(scripts),
data_files = data_files,
ext_modules = ext_modules,
cmdclass = {'install_lib': MyInstallLib,
'build_py': MyBuildPy},
**kwargs
)
if __name__ == '__main__' :
install()
logilab-astng-0.24.3/exceptions.py 0000644 0000151 0000155 00000003162 12133517265 016405 0 ustar narval narval # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of logilab-astng.
#
# logilab-astng is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 2.1 of the License, or (at your
# option) any later version.
#
# logilab-astng is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
# for more details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-astng. If not, see .
"""this module contains exceptions used in the astng library
"""
__doctype__ = "restructuredtext en"
class ASTNGError(Exception):
"""base exception class for all astng related exceptions"""
class ASTNGBuildingException(ASTNGError):
"""exception class when we are unable to build an astng representation"""
class ResolveError(ASTNGError):
"""base class of astng resolution/inference error"""
class NotFoundError(ResolveError):
"""raised when we are unable to resolve a name"""
class InferenceError(ResolveError):
"""raised when we are unable to infer a node"""
class UnresolvableName(InferenceError):
"""raised when we are unable to resolve a name"""
class NoDefault(ASTNGError):
"""raised by function's `default_value` method when an argument has
no default value
"""