test_inspect.py revision aa98058cc44ba20f35c106d20918c6196b737561
1import sys 2import types 3import unittest 4import inspect 5import datetime 6 7from test.test_support import TESTFN, run_unittest 8 9from test import inspect_fodder as mod 10from test import inspect_fodder2 as mod2 11 12# C module for test_findsource_binary 13import unicodedata 14 15# Functions tested in this suite: 16# ismodule, isclass, ismethod, isfunction, istraceback, isframe, iscode, 17# isbuiltin, isroutine, isgenerator, isgeneratorfunction, getmembers, 18# getdoc, getfile, getmodule, getsourcefile, getcomments, getsource, 19# getclasstree, getargspec, getargvalues, formatargspec, formatargvalues, 20# currentframe, stack, trace, isdatadescriptor 21 22# NOTE: There are some additional tests relating to interaction with 23# zipimport in the test_zipimport_support test module. 24 25modfile = mod.__file__ 26if modfile.endswith(('c', 'o')): 27 modfile = modfile[:-1] 28 29import __builtin__ 30 31try: 32 1/0 33except: 34 tb = sys.exc_traceback 35 36git = mod.StupidGit() 37 38class IsTestBase(unittest.TestCase): 39 predicates = set([inspect.isbuiltin, inspect.isclass, inspect.iscode, 40 inspect.isframe, inspect.isfunction, inspect.ismethod, 41 inspect.ismodule, inspect.istraceback, 42 inspect.isgenerator, inspect.isgeneratorfunction]) 43 44 def istest(self, predicate, exp): 45 obj = eval(exp) 46 self.assertTrue(predicate(obj), '%s(%s)' % (predicate.__name__, exp)) 47 48 for other in self.predicates - set([predicate]): 49 if predicate == inspect.isgeneratorfunction and\ 50 other == inspect.isfunction: 51 continue 52 self.assertFalse(other(obj), 'not %s(%s)' % (other.__name__, exp)) 53 54def generator_function_example(self): 55 for i in xrange(2): 56 yield i 57 58class TestPredicates(IsTestBase): 59 def test_sixteen(self): 60 count = len(filter(lambda x:x.startswith('is'), dir(inspect))) 61 # This test is here for remember you to update Doc/library/inspect.rst 62 # which claims there are 16 such functions 63 expected = 16 64 err_msg = "There are %d (not %d) is* functions" % (count, expected) 65 self.assertEqual(count, expected, err_msg) 66 67 68 def test_excluding_predicates(self): 69 self.istest(inspect.isbuiltin, 'sys.exit') 70 self.istest(inspect.isbuiltin, '[].append') 71 self.istest(inspect.iscode, 'mod.spam.func_code') 72 self.istest(inspect.isframe, 'tb.tb_frame') 73 self.istest(inspect.isfunction, 'mod.spam') 74 self.istest(inspect.ismethod, 'mod.StupidGit.abuse') 75 self.istest(inspect.ismethod, 'git.argue') 76 self.istest(inspect.ismodule, 'mod') 77 self.istest(inspect.istraceback, 'tb') 78 self.istest(inspect.isdatadescriptor, '__builtin__.file.closed') 79 self.istest(inspect.isdatadescriptor, '__builtin__.file.softspace') 80 self.istest(inspect.isgenerator, '(x for x in xrange(2))') 81 self.istest(inspect.isgeneratorfunction, 'generator_function_example') 82 if hasattr(types, 'GetSetDescriptorType'): 83 self.istest(inspect.isgetsetdescriptor, 84 'type(tb.tb_frame).f_locals') 85 else: 86 self.assertFalse(inspect.isgetsetdescriptor(type(tb.tb_frame).f_locals)) 87 if hasattr(types, 'MemberDescriptorType'): 88 self.istest(inspect.ismemberdescriptor, 'datetime.timedelta.days') 89 else: 90 self.assertFalse(inspect.ismemberdescriptor(datetime.timedelta.days)) 91 92 def test_isroutine(self): 93 self.assertTrue(inspect.isroutine(mod.spam)) 94 self.assertTrue(inspect.isroutine([].count)) 95 96 def test_isclass(self): 97 self.istest(inspect.isclass, 'mod.StupidGit') 98 self.assertTrue(inspect.isclass(list)) 99 100 class newstyle(object): pass 101 self.assertTrue(inspect.isclass(newstyle)) 102 103 class CustomGetattr(object): 104 def __getattr__(self, attr): 105 return None 106 self.assertFalse(inspect.isclass(CustomGetattr())) 107 108 def test_get_slot_members(self): 109 class C(object): 110 __slots__ = ("a", "b") 111 112 x = C() 113 x.a = 42 114 members = dict(inspect.getmembers(x)) 115 self.assertIn('a', members) 116 self.assertNotIn('b', members) 117 118 def test_isabstract(self): 119 from abc import ABCMeta, abstractmethod 120 121 class AbstractClassExample(object): 122 __metaclass__ = ABCMeta 123 124 @abstractmethod 125 def foo(self): 126 pass 127 128 class ClassExample(AbstractClassExample): 129 def foo(self): 130 pass 131 132 a = ClassExample() 133 134 # Test general behaviour. 135 self.assertTrue(inspect.isabstract(AbstractClassExample)) 136 self.assertFalse(inspect.isabstract(ClassExample)) 137 self.assertFalse(inspect.isabstract(a)) 138 self.assertFalse(inspect.isabstract(int)) 139 self.assertFalse(inspect.isabstract(5)) 140 141 142class TestInterpreterStack(IsTestBase): 143 def __init__(self, *args, **kwargs): 144 unittest.TestCase.__init__(self, *args, **kwargs) 145 146 git.abuse(7, 8, 9) 147 148 def test_abuse_done(self): 149 self.istest(inspect.istraceback, 'git.ex[2]') 150 self.istest(inspect.isframe, 'mod.fr') 151 152 def test_stack(self): 153 self.assertTrue(len(mod.st) >= 5) 154 self.assertEqual(mod.st[0][1:], 155 (modfile, 16, 'eggs', [' st = inspect.stack()\n'], 0)) 156 self.assertEqual(mod.st[1][1:], 157 (modfile, 9, 'spam', [' eggs(b + d, c + f)\n'], 0)) 158 self.assertEqual(mod.st[2][1:], 159 (modfile, 43, 'argue', [' spam(a, b, c)\n'], 0)) 160 self.assertEqual(mod.st[3][1:], 161 (modfile, 39, 'abuse', [' self.argue(a, b, c)\n'], 0)) 162 163 def test_trace(self): 164 self.assertEqual(len(git.tr), 3) 165 self.assertEqual(git.tr[0][1:], (modfile, 43, 'argue', 166 [' spam(a, b, c)\n'], 0)) 167 self.assertEqual(git.tr[1][1:], (modfile, 9, 'spam', 168 [' eggs(b + d, c + f)\n'], 0)) 169 self.assertEqual(git.tr[2][1:], (modfile, 18, 'eggs', 170 [' q = y / 0\n'], 0)) 171 172 def test_frame(self): 173 args, varargs, varkw, locals = inspect.getargvalues(mod.fr) 174 self.assertEqual(args, ['x', 'y']) 175 self.assertEqual(varargs, None) 176 self.assertEqual(varkw, None) 177 self.assertEqual(locals, {'x': 11, 'p': 11, 'y': 14}) 178 self.assertEqual(inspect.formatargvalues(args, varargs, varkw, locals), 179 '(x=11, y=14)') 180 181 def test_previous_frame(self): 182 args, varargs, varkw, locals = inspect.getargvalues(mod.fr.f_back) 183 self.assertEqual(args, ['a', 'b', 'c', 'd', ['e', ['f']]]) 184 self.assertEqual(varargs, 'g') 185 self.assertEqual(varkw, 'h') 186 self.assertEqual(inspect.formatargvalues(args, varargs, varkw, locals), 187 '(a=7, b=8, c=9, d=3, (e=4, (f=5,)), *g=(), **h={})') 188 189class GetSourceBase(unittest.TestCase): 190 # Subclasses must override. 191 fodderFile = None 192 193 def __init__(self, *args, **kwargs): 194 unittest.TestCase.__init__(self, *args, **kwargs) 195 196 with open(inspect.getsourcefile(self.fodderFile)) as fp: 197 self.source = fp.read() 198 199 def sourcerange(self, top, bottom): 200 lines = self.source.split("\n") 201 return "\n".join(lines[top-1:bottom]) + "\n" 202 203 def assertSourceEqual(self, obj, top, bottom): 204 self.assertEqual(inspect.getsource(obj), 205 self.sourcerange(top, bottom)) 206 207class TestRetrievingSourceCode(GetSourceBase): 208 fodderFile = mod 209 210 def test_getclasses(self): 211 classes = inspect.getmembers(mod, inspect.isclass) 212 self.assertEqual(classes, 213 [('FesteringGob', mod.FesteringGob), 214 ('MalodorousPervert', mod.MalodorousPervert), 215 ('ParrotDroppings', mod.ParrotDroppings), 216 ('StupidGit', mod.StupidGit)]) 217 tree = inspect.getclasstree([cls[1] for cls in classes], 1) 218 self.assertEqual(tree, 219 [(mod.ParrotDroppings, ()), 220 (mod.StupidGit, ()), 221 [(mod.MalodorousPervert, (mod.StupidGit,)), 222 [(mod.FesteringGob, (mod.MalodorousPervert, 223 mod.ParrotDroppings)) 224 ] 225 ] 226 ]) 227 228 def test_getfunctions(self): 229 functions = inspect.getmembers(mod, inspect.isfunction) 230 self.assertEqual(functions, [('eggs', mod.eggs), 231 ('spam', mod.spam)]) 232 233 def test_getdoc(self): 234 self.assertEqual(inspect.getdoc(mod), 'A module docstring.') 235 self.assertEqual(inspect.getdoc(mod.StupidGit), 236 'A longer,\n\nindented\n\ndocstring.') 237 self.assertEqual(inspect.getdoc(git.abuse), 238 'Another\n\ndocstring\n\ncontaining\n\ntabs') 239 240 def test_cleandoc(self): 241 self.assertEqual(inspect.cleandoc('An\n indented\n docstring.'), 242 'An\nindented\ndocstring.') 243 244 def test_getcomments(self): 245 self.assertEqual(inspect.getcomments(mod), '# line 1\n') 246 self.assertEqual(inspect.getcomments(mod.StupidGit), '# line 20\n') 247 248 def test_getmodule(self): 249 # Check actual module 250 self.assertEqual(inspect.getmodule(mod), mod) 251 # Check class (uses __module__ attribute) 252 self.assertEqual(inspect.getmodule(mod.StupidGit), mod) 253 # Check a method (no __module__ attribute, falls back to filename) 254 self.assertEqual(inspect.getmodule(mod.StupidGit.abuse), mod) 255 # Do it again (check the caching isn't broken) 256 self.assertEqual(inspect.getmodule(mod.StupidGit.abuse), mod) 257 # Check a builtin 258 self.assertEqual(inspect.getmodule(str), sys.modules["__builtin__"]) 259 # Check filename override 260 self.assertEqual(inspect.getmodule(None, modfile), mod) 261 262 def test_getsource(self): 263 self.assertSourceEqual(git.abuse, 29, 39) 264 self.assertSourceEqual(mod.StupidGit, 21, 46) 265 266 def test_getsourcefile(self): 267 self.assertEqual(inspect.getsourcefile(mod.spam), modfile) 268 self.assertEqual(inspect.getsourcefile(git.abuse), modfile) 269 270 def test_getfile(self): 271 self.assertEqual(inspect.getfile(mod.StupidGit), mod.__file__) 272 273 def test_getmodule_recursion(self): 274 from types import ModuleType 275 name = '__inspect_dummy' 276 m = sys.modules[name] = ModuleType(name) 277 m.__file__ = "<string>" # hopefully not a real filename... 278 m.__loader__ = "dummy" # pretend the filename is understood by a loader 279 exec "def x(): pass" in m.__dict__ 280 self.assertEqual(inspect.getsourcefile(m.x.func_code), '<string>') 281 del sys.modules[name] 282 inspect.getmodule(compile('a=10','','single')) 283 284class TestDecorators(GetSourceBase): 285 fodderFile = mod2 286 287 def test_wrapped_decorator(self): 288 self.assertSourceEqual(mod2.wrapped, 14, 17) 289 290 def test_replacing_decorator(self): 291 self.assertSourceEqual(mod2.gone, 9, 10) 292 293class TestOneliners(GetSourceBase): 294 fodderFile = mod2 295 def test_oneline_lambda(self): 296 # Test inspect.getsource with a one-line lambda function. 297 self.assertSourceEqual(mod2.oll, 25, 25) 298 299 def test_threeline_lambda(self): 300 # Test inspect.getsource with a three-line lambda function, 301 # where the second and third lines are _not_ indented. 302 self.assertSourceEqual(mod2.tll, 28, 30) 303 304 def test_twoline_indented_lambda(self): 305 # Test inspect.getsource with a two-line lambda function, 306 # where the second line _is_ indented. 307 self.assertSourceEqual(mod2.tlli, 33, 34) 308 309 def test_onelinefunc(self): 310 # Test inspect.getsource with a regular one-line function. 311 self.assertSourceEqual(mod2.onelinefunc, 37, 37) 312 313 def test_manyargs(self): 314 # Test inspect.getsource with a regular function where 315 # the arguments are on two lines and _not_ indented and 316 # the body on the second line with the last arguments. 317 self.assertSourceEqual(mod2.manyargs, 40, 41) 318 319 def test_twolinefunc(self): 320 # Test inspect.getsource with a regular function where 321 # the body is on two lines, following the argument list and 322 # continued on the next line by a \\. 323 self.assertSourceEqual(mod2.twolinefunc, 44, 45) 324 325 def test_lambda_in_list(self): 326 # Test inspect.getsource with a one-line lambda function 327 # defined in a list, indented. 328 self.assertSourceEqual(mod2.a[1], 49, 49) 329 330 def test_anonymous(self): 331 # Test inspect.getsource with a lambda function defined 332 # as argument to another function. 333 self.assertSourceEqual(mod2.anonymous, 55, 55) 334 335class TestBuggyCases(GetSourceBase): 336 fodderFile = mod2 337 338 def test_with_comment(self): 339 self.assertSourceEqual(mod2.with_comment, 58, 59) 340 341 def test_multiline_sig(self): 342 self.assertSourceEqual(mod2.multiline_sig[0], 63, 64) 343 344 def test_nested_class(self): 345 self.assertSourceEqual(mod2.func69().func71, 71, 72) 346 347 def test_one_liner_followed_by_non_name(self): 348 self.assertSourceEqual(mod2.func77, 77, 77) 349 350 def test_one_liner_dedent_non_name(self): 351 self.assertSourceEqual(mod2.cls82.func83, 83, 83) 352 353 def test_with_comment_instead_of_docstring(self): 354 self.assertSourceEqual(mod2.func88, 88, 90) 355 356 def test_method_in_dynamic_class(self): 357 self.assertSourceEqual(mod2.method_in_dynamic_class, 95, 97) 358 359 @unittest.skipIf( 360 not hasattr(unicodedata, '__file__') or 361 unicodedata.__file__[-4:] in (".pyc", ".pyo"), 362 "unicodedata is not an external binary module") 363 def test_findsource_binary(self): 364 self.assertRaises(IOError, inspect.getsource, unicodedata) 365 self.assertRaises(IOError, inspect.findsource, unicodedata) 366 367# Helper for testing classify_class_attrs. 368def attrs_wo_objs(cls): 369 return [t[:3] for t in inspect.classify_class_attrs(cls)] 370 371class TestClassesAndFunctions(unittest.TestCase): 372 def test_classic_mro(self): 373 # Test classic-class method resolution order. 374 class A: pass 375 class B(A): pass 376 class C(A): pass 377 class D(B, C): pass 378 379 expected = (D, B, A, C) 380 got = inspect.getmro(D) 381 self.assertEqual(expected, got) 382 383 def test_newstyle_mro(self): 384 # The same w/ new-class MRO. 385 class A(object): pass 386 class B(A): pass 387 class C(A): pass 388 class D(B, C): pass 389 390 expected = (D, B, C, A, object) 391 got = inspect.getmro(D) 392 self.assertEqual(expected, got) 393 394 def assertArgSpecEquals(self, routine, args_e, varargs_e = None, 395 varkw_e = None, defaults_e = None, 396 formatted = None): 397 args, varargs, varkw, defaults = inspect.getargspec(routine) 398 self.assertEqual(args, args_e) 399 self.assertEqual(varargs, varargs_e) 400 self.assertEqual(varkw, varkw_e) 401 self.assertEqual(defaults, defaults_e) 402 if formatted is not None: 403 self.assertEqual(inspect.formatargspec(args, varargs, varkw, defaults), 404 formatted) 405 406 def test_getargspec(self): 407 self.assertArgSpecEquals(mod.eggs, ['x', 'y'], formatted = '(x, y)') 408 409 self.assertArgSpecEquals(mod.spam, 410 ['a', 'b', 'c', 'd', ['e', ['f']]], 411 'g', 'h', (3, (4, (5,))), 412 '(a, b, c, d=3, (e, (f,))=(4, (5,)), *g, **h)') 413 414 def test_getargspec_method(self): 415 class A(object): 416 def m(self): 417 pass 418 self.assertArgSpecEquals(A.m, ['self']) 419 420 def test_getargspec_sublistofone(self): 421 def sublistOfOne((foo,)): return 1 422 self.assertArgSpecEquals(sublistOfOne, [['foo']]) 423 424 def fakeSublistOfOne((foo)): return 1 425 self.assertArgSpecEquals(fakeSublistOfOne, ['foo']) 426 427 def test_classify_oldstyle(self): 428 class A: 429 def s(): pass 430 s = staticmethod(s) 431 432 def c(cls): pass 433 c = classmethod(c) 434 435 def getp(self): pass 436 p = property(getp) 437 438 def m(self): pass 439 440 def m1(self): pass 441 442 datablob = '1' 443 444 attrs = attrs_wo_objs(A) 445 self.assertIn(('s', 'static method', A), attrs, 'missing static method') 446 self.assertIn(('c', 'class method', A), attrs, 'missing class method') 447 self.assertIn(('p', 'property', A), attrs, 'missing property') 448 self.assertIn(('m', 'method', A), attrs, 'missing plain method') 449 self.assertIn(('m1', 'method', A), attrs, 'missing plain method') 450 self.assertIn(('datablob', 'data', A), attrs, 'missing data') 451 452 class B(A): 453 def m(self): pass 454 455 attrs = attrs_wo_objs(B) 456 self.assertIn(('s', 'static method', A), attrs, 'missing static method') 457 self.assertIn(('c', 'class method', A), attrs, 'missing class method') 458 self.assertIn(('p', 'property', A), attrs, 'missing property') 459 self.assertIn(('m', 'method', B), attrs, 'missing plain method') 460 self.assertIn(('m1', 'method', A), attrs, 'missing plain method') 461 self.assertIn(('datablob', 'data', A), attrs, 'missing data') 462 463 464 class C(A): 465 def m(self): pass 466 def c(self): pass 467 468 attrs = attrs_wo_objs(C) 469 self.assertIn(('s', 'static method', A), attrs, 'missing static method') 470 self.assertIn(('c', 'method', C), attrs, 'missing plain method') 471 self.assertIn(('p', 'property', A), attrs, 'missing property') 472 self.assertIn(('m', 'method', C), attrs, 'missing plain method') 473 self.assertIn(('m1', 'method', A), attrs, 'missing plain method') 474 self.assertIn(('datablob', 'data', A), attrs, 'missing data') 475 476 class D(B, C): 477 def m1(self): pass 478 479 attrs = attrs_wo_objs(D) 480 self.assertIn(('s', 'static method', A), attrs, 'missing static method') 481 self.assertIn(('c', 'class method', A), attrs, 'missing class method') 482 self.assertIn(('p', 'property', A), attrs, 'missing property') 483 self.assertIn(('m', 'method', B), attrs, 'missing plain method') 484 self.assertIn(('m1', 'method', D), attrs, 'missing plain method') 485 self.assertIn(('datablob', 'data', A), attrs, 'missing data') 486 487 # Repeat all that, but w/ new-style classes. 488 def test_classify_newstyle(self): 489 class A(object): 490 491 def s(): pass 492 s = staticmethod(s) 493 494 def c(cls): pass 495 c = classmethod(c) 496 497 def getp(self): pass 498 p = property(getp) 499 500 def m(self): pass 501 502 def m1(self): pass 503 504 datablob = '1' 505 506 attrs = attrs_wo_objs(A) 507 self.assertIn(('s', 'static method', A), attrs, 'missing static method') 508 self.assertIn(('c', 'class method', A), attrs, 'missing class method') 509 self.assertIn(('p', 'property', A), attrs, 'missing property') 510 self.assertIn(('m', 'method', A), attrs, 'missing plain method') 511 self.assertIn(('m1', 'method', A), attrs, 'missing plain method') 512 self.assertIn(('datablob', 'data', A), attrs, 'missing data') 513 514 class B(A): 515 516 def m(self): pass 517 518 attrs = attrs_wo_objs(B) 519 self.assertIn(('s', 'static method', A), attrs, 'missing static method') 520 self.assertIn(('c', 'class method', A), attrs, 'missing class method') 521 self.assertIn(('p', 'property', A), attrs, 'missing property') 522 self.assertIn(('m', 'method', B), attrs, 'missing plain method') 523 self.assertIn(('m1', 'method', A), attrs, 'missing plain method') 524 self.assertIn(('datablob', 'data', A), attrs, 'missing data') 525 526 527 class C(A): 528 529 def m(self): pass 530 def c(self): pass 531 532 attrs = attrs_wo_objs(C) 533 self.assertIn(('s', 'static method', A), attrs, 'missing static method') 534 self.assertIn(('c', 'method', C), attrs, 'missing plain method') 535 self.assertIn(('p', 'property', A), attrs, 'missing property') 536 self.assertIn(('m', 'method', C), attrs, 'missing plain method') 537 self.assertIn(('m1', 'method', A), attrs, 'missing plain method') 538 self.assertIn(('datablob', 'data', A), attrs, 'missing data') 539 540 class D(B, C): 541 542 def m1(self): pass 543 544 attrs = attrs_wo_objs(D) 545 self.assertIn(('s', 'static method', A), attrs, 'missing static method') 546 self.assertIn(('c', 'method', C), attrs, 'missing plain method') 547 self.assertIn(('p', 'property', A), attrs, 'missing property') 548 self.assertIn(('m', 'method', B), attrs, 'missing plain method') 549 self.assertIn(('m1', 'method', D), attrs, 'missing plain method') 550 self.assertIn(('datablob', 'data', A), attrs, 'missing data') 551 552def test_main(): 553 run_unittest(TestDecorators, TestRetrievingSourceCode, TestOneliners, 554 TestBuggyCases, 555 TestInterpreterStack, TestClassesAndFunctions, TestPredicates) 556 557if __name__ == "__main__": 558 test_main() 559