| 1 | # This program is free software; you can redistribute it and/or modify it under |
|---|
| 2 | # the terms of the GNU General Public License as published by the Free Software |
|---|
| 3 | # Foundation; either version 2 of the License, or (at your option) any later |
|---|
| 4 | # version. |
|---|
| 5 | |
|---|
| 6 | # This program is distributed in the hope that it will be useful, but WITHOUT |
|---|
| 7 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
|---|
| 8 | # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
|---|
| 9 | |
|---|
| 10 | # You should have received a copy of the GNU General Public License along with |
|---|
| 11 | # this program; if not, write to the Free Software Foundation, Inc., |
|---|
| 12 | # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
|---|
| 13 | """tests for specific behaviour of astng scoped nodes (ie module, class and |
|---|
| 14 | function) |
|---|
| 15 | """ |
|---|
| 16 | |
|---|
| 17 | import sys |
|---|
| 18 | |
|---|
| 19 | from logilab.common.testlib import TestCase, unittest_main |
|---|
| 20 | from logilab.common.compat import sorted |
|---|
| 21 | from logilab.astng import builder, nodes, scoped_nodes, \ |
|---|
| 22 | InferenceError, NotFoundError |
|---|
| 23 | |
|---|
| 24 | abuilder = builder.ASTNGBuilder() |
|---|
| 25 | MODULE = abuilder.file_build('data/module.py', 'data.module') |
|---|
| 26 | MODULE2 = abuilder.file_build('data/module2.py', 'data.module2') |
|---|
| 27 | NONREGR = abuilder.file_build('data/nonregr.py', 'data.nonregr') |
|---|
| 28 | |
|---|
| 29 | def _test_dict_interface(self, node, test_attr): |
|---|
| 30 | self.assert_(node[test_attr] is node[test_attr]) |
|---|
| 31 | self.assert_(test_attr in node) |
|---|
| 32 | node.keys() |
|---|
| 33 | node.values() |
|---|
| 34 | node.items() |
|---|
| 35 | iter(node) |
|---|
| 36 | |
|---|
| 37 | |
|---|
| 38 | class ModuleNodeTC(TestCase): |
|---|
| 39 | |
|---|
| 40 | def test_dict_interface(self): |
|---|
| 41 | _test_dict_interface(self, MODULE, 'YO') |
|---|
| 42 | |
|---|
| 43 | def test_getattr(self): |
|---|
| 44 | yo = MODULE.getattr('YO')[0] |
|---|
| 45 | self.assertIsInstance(yo, nodes.Class) |
|---|
| 46 | self.assertEquals(yo.name, 'YO') |
|---|
| 47 | red = MODULE.igetattr('redirect').next() |
|---|
| 48 | self.assertIsInstance(red, nodes.Function) |
|---|
| 49 | self.assertEquals(red.name, 'nested_args') |
|---|
| 50 | spawn = MODULE.igetattr('spawn').next() |
|---|
| 51 | self.assertIsInstance(spawn, nodes.Class) |
|---|
| 52 | self.assertEquals(spawn.name, 'Execute') |
|---|
| 53 | # resolve packageredirection |
|---|
| 54 | sys.path.insert(1, 'data') |
|---|
| 55 | try: |
|---|
| 56 | m = abuilder.file_build('data/appl/myConnection.py', 'appl.myConnection') |
|---|
| 57 | cnx = m.igetattr('SSL1').next().igetattr('Connection').next() |
|---|
| 58 | self.assertEquals(cnx.__class__, nodes.Class) |
|---|
| 59 | self.assertEquals(cnx.name, 'Connection') |
|---|
| 60 | self.assertEquals(cnx.root().name, 'SSL1.Connection1') |
|---|
| 61 | finally: |
|---|
| 62 | del sys.path[1] |
|---|
| 63 | self.assertEquals(len(NONREGR.getattr('enumerate')), 2) |
|---|
| 64 | # raise ResolveError |
|---|
| 65 | self.assertRaises(InferenceError, MODULE.igetattr, 'YOAA') |
|---|
| 66 | |
|---|
| 67 | def test_wildard_import_names(self): |
|---|
| 68 | m = abuilder.file_build('data/all.py', 'all') |
|---|
| 69 | self.assertEquals(m.wildcard_import_names(), ['Aaa', '_bla', 'name']) |
|---|
| 70 | m = abuilder.file_build('data/notall.py', 'notall') |
|---|
| 71 | res = m.wildcard_import_names() |
|---|
| 72 | res.sort() |
|---|
| 73 | self.assertEquals(res, ['Aaa', 'func', 'name', 'other']) |
|---|
| 74 | |
|---|
| 75 | def test_as_string(self): |
|---|
| 76 | """just check as_string on a whole module doesn't raise an exception |
|---|
| 77 | """ |
|---|
| 78 | self.assert_(MODULE.as_string()) |
|---|
| 79 | self.assert_(MODULE2.as_string()) |
|---|
| 80 | |
|---|
| 81 | |
|---|
| 82 | class FunctionNodeTC(TestCase): |
|---|
| 83 | |
|---|
| 84 | def test_dict_interface(self): |
|---|
| 85 | _test_dict_interface(self, MODULE['global_access'], 'local') |
|---|
| 86 | |
|---|
| 87 | def test_default_value(self): |
|---|
| 88 | func = MODULE2['make_class'] |
|---|
| 89 | self.assertIsInstance(func.default_value('base'), nodes.Getattr) |
|---|
| 90 | self.assertRaises(scoped_nodes.NoDefault, func.default_value, 'args') |
|---|
| 91 | self.assertRaises(scoped_nodes.NoDefault, func.default_value, 'kwargs') |
|---|
| 92 | self.assertRaises(scoped_nodes.NoDefault, func.default_value, 'any') |
|---|
| 93 | self.assertIsInstance(func.mularg_class('args'), nodes.Tuple) |
|---|
| 94 | self.assertIsInstance(func.mularg_class('kwargs'), nodes.Dict) |
|---|
| 95 | self.assertEquals(func.mularg_class('base'), None) |
|---|
| 96 | |
|---|
| 97 | def test_navigation(self): |
|---|
| 98 | function = MODULE['global_access'] |
|---|
| 99 | self.assertEquals(function.statement(), function) |
|---|
| 100 | l_sibling = function.previous_sibling() |
|---|
| 101 | self.assertIsInstance(l_sibling, nodes.Assign) |
|---|
| 102 | self.assert_(l_sibling is function.getChildNodes()[0].previous_sibling()) |
|---|
| 103 | r_sibling = function.next_sibling() |
|---|
| 104 | self.assertIsInstance(r_sibling, nodes.Class) |
|---|
| 105 | self.assertEquals(r_sibling.name, 'YO') |
|---|
| 106 | self.assert_(r_sibling is function.getChildNodes()[0].next_sibling()) |
|---|
| 107 | last = r_sibling.next_sibling().next_sibling().next_sibling() |
|---|
| 108 | self.assertIsInstance(last, nodes.Assign) |
|---|
| 109 | self.assertEquals(last.next_sibling(), None) |
|---|
| 110 | first = l_sibling.previous_sibling().previous_sibling().previous_sibling().previous_sibling().previous_sibling() |
|---|
| 111 | self.assertEquals(first.previous_sibling(), None) |
|---|
| 112 | |
|---|
| 113 | def test_nested_args(self): |
|---|
| 114 | func = MODULE['nested_args'] |
|---|
| 115 | self.assertEquals(func.argnames, ['a', ('b', 'c', 'd')]) |
|---|
| 116 | local = func.keys() |
|---|
| 117 | local.sort() |
|---|
| 118 | self.assertEquals(local, ['a', 'b', 'c', 'd']) |
|---|
| 119 | self.assertEquals(func.type, 'function') |
|---|
| 120 | |
|---|
| 121 | def test_format_args(self): |
|---|
| 122 | func = MODULE2['make_class'] |
|---|
| 123 | self.assertEquals(func.format_args(), 'any, base=data.module.YO, *args, **kwargs') |
|---|
| 124 | func = MODULE['nested_args'] |
|---|
| 125 | self.assertEquals(func.format_args(), 'a, (b,c,d)') |
|---|
| 126 | |
|---|
| 127 | def test_is_abstract(self): |
|---|
| 128 | method = MODULE2['AbstractClass']['to_override'] |
|---|
| 129 | self.assert_(method.is_abstract(pass_is_abstract=False)) |
|---|
| 130 | self.failUnlessEqual(method.qname(), 'data.module2.AbstractClass.to_override') |
|---|
| 131 | self.failUnlessEqual(method.pytype(), '__builtin__.instancemethod') |
|---|
| 132 | method = MODULE2['AbstractClass']['return_something'] |
|---|
| 133 | self.assert_(not method.is_abstract(pass_is_abstract=False)) |
|---|
| 134 | # non regression : test raise "string" doesn't cause an exception in is_abstract |
|---|
| 135 | func = MODULE2['raise_string'] |
|---|
| 136 | self.assert_(not func.is_abstract(pass_is_abstract=False)) |
|---|
| 137 | |
|---|
| 138 | ## def test_raises(self): |
|---|
| 139 | ## method = MODULE2['AbstractClass']['to_override'] |
|---|
| 140 | ## self.assertEquals([str(term) for term in method.raises()], |
|---|
| 141 | ## ["CallFunc(Name('NotImplementedError'), [], None, None)"] ) |
|---|
| 142 | |
|---|
| 143 | ## def test_returns(self): |
|---|
| 144 | ## method = MODULE2['AbstractClass']['return_something'] |
|---|
| 145 | ## # use string comp since Node doesn't handle __cmp__ |
|---|
| 146 | ## self.assertEquals([str(term) for term in method.returns()], |
|---|
| 147 | ## ["Const('toto')", "Const(None)"]) |
|---|
| 148 | |
|---|
| 149 | def test_lambda_pytype(self): |
|---|
| 150 | data = ''' |
|---|
| 151 | def f(): |
|---|
| 152 | g = lambda: None |
|---|
| 153 | ''' |
|---|
| 154 | astng = abuilder.string_build(data, __name__, __file__) |
|---|
| 155 | g = list(astng['f'].ilookup('g'))[0] |
|---|
| 156 | self.failUnlessEqual(g.pytype(), '__builtin__.function') |
|---|
| 157 | |
|---|
| 158 | class ClassNodeTC(TestCase): |
|---|
| 159 | |
|---|
| 160 | def test_dict_interface(self): |
|---|
| 161 | _test_dict_interface(self, MODULE['YOUPI'], 'method') |
|---|
| 162 | |
|---|
| 163 | def test_navigation(self): |
|---|
| 164 | klass = MODULE['YO'] |
|---|
| 165 | self.assertEquals(klass.statement(), klass) |
|---|
| 166 | l_sibling = klass.previous_sibling() |
|---|
| 167 | self.assert_(isinstance(l_sibling, nodes.Function), l_sibling) |
|---|
| 168 | self.assertEquals(l_sibling.name, 'global_access') |
|---|
| 169 | r_sibling = klass.next_sibling() |
|---|
| 170 | self.assertIsInstance(r_sibling, nodes.Class) |
|---|
| 171 | self.assertEquals(r_sibling.name, 'YOUPI') |
|---|
| 172 | |
|---|
| 173 | def test_local_attr_ancestors(self): |
|---|
| 174 | klass2 = MODULE['YOUPI'] |
|---|
| 175 | it = klass2.local_attr_ancestors('__init__') |
|---|
| 176 | anc_klass = it.next() |
|---|
| 177 | self.assertIsInstance(anc_klass, nodes.Class) |
|---|
| 178 | self.assertEquals(anc_klass.name, 'YO') |
|---|
| 179 | self.assertRaises(StopIteration, it.next) |
|---|
| 180 | it = klass2.local_attr_ancestors('method') |
|---|
| 181 | self.assertRaises(StopIteration, it.next) |
|---|
| 182 | |
|---|
| 183 | def test_instance_attr_ancestors(self): |
|---|
| 184 | klass2 = MODULE['YOUPI'] |
|---|
| 185 | it = klass2.instance_attr_ancestors('yo') |
|---|
| 186 | anc_klass = it.next() |
|---|
| 187 | self.assertIsInstance(anc_klass, nodes.Class) |
|---|
| 188 | self.assertEquals(anc_klass.name, 'YO') |
|---|
| 189 | self.assertRaises(StopIteration, it.next) |
|---|
| 190 | klass2 = MODULE['YOUPI'] |
|---|
| 191 | it = klass2.instance_attr_ancestors('member') |
|---|
| 192 | self.assertRaises(StopIteration, it.next) |
|---|
| 193 | |
|---|
| 194 | def test_methods(self): |
|---|
| 195 | klass2 = MODULE['YOUPI'] |
|---|
| 196 | methods = [m.name for m in klass2.methods()] |
|---|
| 197 | methods.sort() |
|---|
| 198 | self.assertEquals(methods, ['__init__', 'class_method', |
|---|
| 199 | 'method', 'static_method']) |
|---|
| 200 | methods = [m.name for m in klass2.mymethods()] |
|---|
| 201 | methods.sort() |
|---|
| 202 | self.assertEquals(methods, ['__init__', 'class_method', |
|---|
| 203 | 'method', 'static_method']) |
|---|
| 204 | klass2 = MODULE2['Specialization'] |
|---|
| 205 | methods = [m.name for m in klass2.mymethods()] |
|---|
| 206 | methods.sort() |
|---|
| 207 | self.assertEquals(methods, []) |
|---|
| 208 | self.assertEquals(klass2.local_attr('method').name, 'method') |
|---|
| 209 | self.assertRaises(NotFoundError, klass2.local_attr, 'nonexistant') |
|---|
| 210 | methods = [m.name for m in klass2.methods()] |
|---|
| 211 | methods.sort() |
|---|
| 212 | self.assertEquals(methods, ['__init__', 'class_method', |
|---|
| 213 | 'method', 'static_method']) |
|---|
| 214 | |
|---|
| 215 | #def test_rhs(self): |
|---|
| 216 | # my_dict = MODULE['MY_DICT'] |
|---|
| 217 | # self.assertIsInstance(my_dict.rhs(), nodes.Dict) |
|---|
| 218 | # a = MODULE['YO']['a'] |
|---|
| 219 | # value = a.rhs() |
|---|
| 220 | # self.assertIsInstance(value, nodes.Const) |
|---|
| 221 | # self.assertEquals(value.value, 1) |
|---|
| 222 | |
|---|
| 223 | def test_ancestors(self): |
|---|
| 224 | klass = MODULE['YOUPI'] |
|---|
| 225 | ancs = [a.name for a in klass.ancestors()] |
|---|
| 226 | self.assertEquals(ancs, ['YO']) |
|---|
| 227 | klass = MODULE2['Specialization'] |
|---|
| 228 | ancs = [a.name for a in klass.ancestors()] |
|---|
| 229 | self.assertEquals(ancs, ['YOUPI', 'YO', 'YO']) |
|---|
| 230 | |
|---|
| 231 | def test_type(self): |
|---|
| 232 | klass = MODULE['YOUPI'] |
|---|
| 233 | self.assertEquals(klass.type, 'class') |
|---|
| 234 | klass = MODULE2['Metaclass'] |
|---|
| 235 | self.assertEquals(klass.type, 'metaclass') |
|---|
| 236 | klass = MODULE2['MyException'] |
|---|
| 237 | self.assertEquals(klass.type, 'exception') |
|---|
| 238 | klass = MODULE2['MyIFace'] |
|---|
| 239 | self.assertEquals(klass.type, 'interface') |
|---|
| 240 | klass = MODULE2['MyError'] |
|---|
| 241 | self.assertEquals(klass.type, 'exception') |
|---|
| 242 | |
|---|
| 243 | def test_interfaces(self): |
|---|
| 244 | for klass, interfaces in (('Concrete0', ['MyIFace']), |
|---|
| 245 | ('Concrete1', ['MyIFace', 'AnotherIFace']), |
|---|
| 246 | ('Concrete2', ['MyIFace', 'AnotherIFace']), |
|---|
| 247 | ('Concrete23', ['MyIFace', 'AnotherIFace'])): |
|---|
| 248 | klass = MODULE2[klass] |
|---|
| 249 | self.assertEquals([i.name for i in klass.interfaces()], |
|---|
| 250 | interfaces) |
|---|
| 251 | |
|---|
| 252 | def test_inner_classes(self): |
|---|
| 253 | eee = NONREGR['Ccc']['Eee'] |
|---|
| 254 | self.assertEquals([n.name for n in eee.ancestors()], ['Ddd', 'Aaa', 'object']) |
|---|
| 255 | |
|---|
| 256 | |
|---|
| 257 | def test_classmethod_attributes(self): |
|---|
| 258 | data = ''' |
|---|
| 259 | class WebAppObject(object): |
|---|
| 260 | def registered(cls, application): |
|---|
| 261 | cls.appli = application |
|---|
| 262 | cls.schema = application.schema |
|---|
| 263 | cls.config = application.config |
|---|
| 264 | return cls |
|---|
| 265 | registered = classmethod(registered) |
|---|
| 266 | ''' |
|---|
| 267 | astng = abuilder.string_build(data, __name__, __file__) |
|---|
| 268 | cls = astng['WebAppObject'] |
|---|
| 269 | self.assertEquals(sorted(cls.locals.keys()), |
|---|
| 270 | ['__dict__', '__doc__', '__module__', '__name__', |
|---|
| 271 | 'appli', 'config', 'registered', 'schema']) |
|---|
| 272 | |
|---|
| 273 | __all__ = ('ModuleNodeTC', 'ImportNodeTC', 'FunctionNodeTC', 'ClassNodeTC') |
|---|
| 274 | |
|---|
| 275 | if __name__ == '__main__': |
|---|
| 276 | unittest_main() |
|---|