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