1import re 2import sys 3import types 4import unittest 5import inspect 6import linecache 7import datetime 8import textwrap 9from UserList import UserList 10from UserDict import UserDict 11 12from test.support import run_unittest, check_py3k_warnings, have_unicode 13 14with check_py3k_warnings( 15 ("tuple parameter unpacking has been removed", SyntaxWarning), 16 quiet=True): 17 from test import inspect_fodder as mod 18 from test import inspect_fodder2 as mod2 19 20# C module for test_findsource_binary 21try: 22 import unicodedata 23except ImportError: 24 unicodedata = None 25 26# Functions tested in this suite: 27# ismodule, isclass, ismethod, isfunction, istraceback, isframe, iscode, 28# isbuiltin, isroutine, isgenerator, isgeneratorfunction, getmembers, 29# getdoc, getfile, getmodule, getsourcefile, getcomments, getsource, 30# getclasstree, getargspec, getargvalues, formatargspec, formatargvalues, 31# currentframe, stack, trace, isdatadescriptor 32 33# NOTE: There are some additional tests relating to interaction with 34# zipimport in the test_zipimport_support test module. 35 36modfile = mod.__file__ 37if modfile.endswith(('c', 'o')): 38 modfile = modfile[:-1] 39 40import __builtin__ 41 42try: 43 1 // 0 44except: 45 tb = sys.exc_traceback 46 47git = mod.StupidGit() 48 49class IsTestBase(unittest.TestCase): 50 predicates = set([inspect.isbuiltin, inspect.isclass, inspect.iscode, 51 inspect.isframe, inspect.isfunction, inspect.ismethod, 52 inspect.ismodule, inspect.istraceback, 53 inspect.isgenerator, inspect.isgeneratorfunction]) 54 55 def istest(self, predicate, exp): 56 obj = eval(exp) 57 self.assertTrue(predicate(obj), '%s(%s)' % (predicate.__name__, exp)) 58 59 for other in self.predicates - set([predicate]): 60 if predicate == inspect.isgeneratorfunction and\ 61 other == inspect.isfunction: 62 continue 63 self.assertFalse(other(obj), 'not %s(%s)' % (other.__name__, exp)) 64 65def generator_function_example(self): 66 for i in xrange(2): 67 yield i 68 69class TestPredicates(IsTestBase): 70 def test_sixteen(self): 71 count = len(filter(lambda x:x.startswith('is'), dir(inspect))) 72 # This test is here for remember you to update Doc/library/inspect.rst 73 # which claims there are 16 such functions 74 expected = 16 75 err_msg = "There are %d (not %d) is* functions" % (count, expected) 76 self.assertEqual(count, expected, err_msg) 77 78 79 def test_excluding_predicates(self): 80 self.istest(inspect.isbuiltin, 'sys.exit') 81 self.istest(inspect.isbuiltin, '[].append') 82 self.istest(inspect.iscode, 'mod.spam.func_code') 83 self.istest(inspect.isframe, 'tb.tb_frame') 84 self.istest(inspect.isfunction, 'mod.spam') 85 self.istest(inspect.ismethod, 'mod.StupidGit.abuse') 86 self.istest(inspect.ismethod, 'git.argue') 87 self.istest(inspect.ismodule, 'mod') 88 self.istest(inspect.istraceback, 'tb') 89 self.istest(inspect.isdatadescriptor, '__builtin__.file.closed') 90 self.istest(inspect.isdatadescriptor, '__builtin__.file.softspace') 91 self.istest(inspect.isgenerator, '(x for x in xrange(2))') 92 self.istest(inspect.isgeneratorfunction, 'generator_function_example') 93 if hasattr(types, 'GetSetDescriptorType'): 94 self.istest(inspect.isgetsetdescriptor, 95 'type(tb.tb_frame).f_locals') 96 else: 97 self.assertFalse(inspect.isgetsetdescriptor(type(tb.tb_frame).f_locals)) 98 if hasattr(types, 'MemberDescriptorType'): 99 self.istest(inspect.ismemberdescriptor, 'datetime.timedelta.days') 100 else: 101 self.assertFalse(inspect.ismemberdescriptor(datetime.timedelta.days)) 102 103 def test_isroutine(self): 104 self.assertTrue(inspect.isroutine(mod.spam)) 105 self.assertTrue(inspect.isroutine([].count)) 106 107 def test_isclass(self): 108 self.istest(inspect.isclass, 'mod.StupidGit') 109 self.assertTrue(inspect.isclass(list)) 110 111 class newstyle(object): pass 112 self.assertTrue(inspect.isclass(newstyle)) 113 114 class CustomGetattr(object): 115 def __getattr__(self, attr): 116 return None 117 self.assertFalse(inspect.isclass(CustomGetattr())) 118 119 def test_get_slot_members(self): 120 class C(object): 121 __slots__ = ("a", "b") 122 123 x = C() 124 x.a = 42 125 members = dict(inspect.getmembers(x)) 126 self.assertIn('a', members) 127 self.assertNotIn('b', members) 128 129 def test_isabstract(self): 130 from abc import ABCMeta, abstractmethod 131 132 class AbstractClassExample(object): 133 __metaclass__ = ABCMeta 134 135 @abstractmethod 136 def foo(self): 137 pass 138 139 class ClassExample(AbstractClassExample): 140 def foo(self): 141 pass 142 143 a = ClassExample() 144 145 # Test general behaviour. 146 self.assertTrue(inspect.isabstract(AbstractClassExample)) 147 self.assertFalse(inspect.isabstract(ClassExample)) 148 self.assertFalse(inspect.isabstract(a)) 149 self.assertFalse(inspect.isabstract(int)) 150 self.assertFalse(inspect.isabstract(5)) 151 152 153class TestInterpreterStack(IsTestBase): 154 def __init__(self, *args, **kwargs): 155 unittest.TestCase.__init__(self, *args, **kwargs) 156 157 git.abuse(7, 8, 9) 158 159 def test_abuse_done(self): 160 self.istest(inspect.istraceback, 'git.ex[2]') 161 self.istest(inspect.isframe, 'mod.fr') 162 163 def test_stack(self): 164 self.assertTrue(len(mod.st) >= 5) 165 self.assertEqual(mod.st[0][1:], 166 (modfile, 16, 'eggs', [' st = inspect.stack()\n'], 0)) 167 self.assertEqual(mod.st[1][1:], 168 (modfile, 9, 'spam', [' eggs(b + d, c + f)\n'], 0)) 169 self.assertEqual(mod.st[2][1:], 170 (modfile, 43, 'argue', [' spam(a, b, c)\n'], 0)) 171 self.assertEqual(mod.st[3][1:], 172 (modfile, 39, 'abuse', [' self.argue(a, b, c)\n'], 0)) 173 174 def test_trace(self): 175 self.assertEqual(len(git.tr), 3) 176 self.assertEqual(git.tr[0][1:], (modfile, 43, 'argue', 177 [' spam(a, b, c)\n'], 0)) 178 self.assertEqual(git.tr[1][1:], (modfile, 9, 'spam', 179 [' eggs(b + d, c + f)\n'], 0)) 180 self.assertEqual(git.tr[2][1:], (modfile, 18, 'eggs', 181 [' q = y // 0\n'], 0)) 182 183 def test_frame(self): 184 args, varargs, varkw, locals = inspect.getargvalues(mod.fr) 185 self.assertEqual(args, ['x', 'y']) 186 self.assertEqual(varargs, None) 187 self.assertEqual(varkw, None) 188 self.assertEqual(locals, {'x': 11, 'p': 11, 'y': 14}) 189 self.assertEqual(inspect.formatargvalues(args, varargs, varkw, locals), 190 '(x=11, y=14)') 191 192 def test_previous_frame(self): 193 args, varargs, varkw, locals = inspect.getargvalues(mod.fr.f_back) 194 self.assertEqual(args, ['a', 'b', 'c', 'd', ['e', ['f']]]) 195 self.assertEqual(varargs, 'g') 196 self.assertEqual(varkw, 'h') 197 self.assertEqual(inspect.formatargvalues(args, varargs, varkw, locals), 198 '(a=7, b=8, c=9, d=3, (e=4, (f=5,)), *g=(), **h={})') 199 200class GetSourceBase(unittest.TestCase): 201 # Subclasses must override. 202 fodderFile = None 203 204 def __init__(self, *args, **kwargs): 205 unittest.TestCase.__init__(self, *args, **kwargs) 206 207 with open(inspect.getsourcefile(self.fodderFile)) as fp: 208 self.source = fp.read() 209 210 def sourcerange(self, top, bottom): 211 lines = self.source.split("\n") 212 return "\n".join(lines[top-1:bottom]) + ("\n" if bottom else "") 213 214 def assertSourceEqual(self, obj, top, bottom): 215 self.assertEqual(inspect.getsource(obj), 216 self.sourcerange(top, bottom)) 217 218class TestRetrievingSourceCode(GetSourceBase): 219 fodderFile = mod 220 221 def test_getclasses(self): 222 classes = inspect.getmembers(mod, inspect.isclass) 223 self.assertEqual(classes, 224 [('FesteringGob', mod.FesteringGob), 225 ('MalodorousPervert', mod.MalodorousPervert), 226 ('ParrotDroppings', mod.ParrotDroppings), 227 ('StupidGit', mod.StupidGit), 228 ('Tit', mod.MalodorousPervert), 229 ]) 230 tree = inspect.getclasstree([cls[1] for cls in classes]) 231 self.assertEqual(tree, 232 [(mod.ParrotDroppings, ()), 233 [(mod.FesteringGob, (mod.MalodorousPervert, 234 mod.ParrotDroppings)) 235 ], 236 (mod.StupidGit, ()), 237 [(mod.MalodorousPervert, (mod.StupidGit,)), 238 [(mod.FesteringGob, (mod.MalodorousPervert, 239 mod.ParrotDroppings)) 240 ] 241 ] 242 ]) 243 tree = inspect.getclasstree([cls[1] for cls in classes], True) 244 self.assertEqual(tree, 245 [(mod.ParrotDroppings, ()), 246 (mod.StupidGit, ()), 247 [(mod.MalodorousPervert, (mod.StupidGit,)), 248 [(mod.FesteringGob, (mod.MalodorousPervert, 249 mod.ParrotDroppings)) 250 ] 251 ] 252 ]) 253 254 def test_getfunctions(self): 255 functions = inspect.getmembers(mod, inspect.isfunction) 256 self.assertEqual(functions, [('eggs', mod.eggs), 257 ('spam', mod.spam)]) 258 259 @unittest.skipIf(sys.flags.optimize >= 2, 260 "Docstrings are omitted with -O2 and above") 261 def test_getdoc(self): 262 self.assertEqual(inspect.getdoc(mod), 'A module docstring.') 263 self.assertEqual(inspect.getdoc(mod.StupidGit), 264 'A longer,\n\nindented\n\ndocstring.') 265 self.assertEqual(inspect.getdoc(git.abuse), 266 'Another\n\ndocstring\n\ncontaining\n\ntabs') 267 268 def test_cleandoc(self): 269 self.assertEqual(inspect.cleandoc('An\n indented\n docstring.'), 270 'An\nindented\ndocstring.') 271 272 def test_getcomments(self): 273 self.assertEqual(inspect.getcomments(mod), '# line 1\n') 274 self.assertEqual(inspect.getcomments(mod.StupidGit), '# line 20\n') 275 276 def test_getmodule(self): 277 # Check actual module 278 self.assertEqual(inspect.getmodule(mod), mod) 279 # Check class (uses __module__ attribute) 280 self.assertEqual(inspect.getmodule(mod.StupidGit), mod) 281 # Check a method (no __module__ attribute, falls back to filename) 282 self.assertEqual(inspect.getmodule(mod.StupidGit.abuse), mod) 283 # Do it again (check the caching isn't broken) 284 self.assertEqual(inspect.getmodule(mod.StupidGit.abuse), mod) 285 # Check a builtin 286 self.assertEqual(inspect.getmodule(str), sys.modules["__builtin__"]) 287 # Check filename override 288 self.assertEqual(inspect.getmodule(None, modfile), mod) 289 290 def test_getsource(self): 291 self.assertSourceEqual(git.abuse, 29, 39) 292 self.assertSourceEqual(mod.StupidGit, 21, 46) 293 294 def test_getsourcefile(self): 295 self.assertEqual(inspect.getsourcefile(mod.spam), modfile) 296 self.assertEqual(inspect.getsourcefile(git.abuse), modfile) 297 fn = "_non_existing_filename_used_for_sourcefile_test.py" 298 co = compile("None", fn, "exec") 299 self.assertEqual(inspect.getsourcefile(co), None) 300 linecache.cache[co.co_filename] = (1, None, "None", co.co_filename) 301 self.assertEqual(inspect.getsourcefile(co), fn) 302 303 def test_getfile(self): 304 self.assertEqual(inspect.getfile(mod.StupidGit), mod.__file__) 305 306 def test_getmodule_recursion(self): 307 from types import ModuleType 308 name = '__inspect_dummy' 309 m = sys.modules[name] = ModuleType(name) 310 m.__file__ = "<string>" # hopefully not a real filename... 311 m.__loader__ = "dummy" # pretend the filename is understood by a loader 312 exec "def x(): pass" in m.__dict__ 313 self.assertEqual(inspect.getsourcefile(m.x.func_code), '<string>') 314 del sys.modules[name] 315 inspect.getmodule(compile('a=10','','single')) 316 317 def test_proceed_with_fake_filename(self): 318 '''doctest monkeypatches linecache to enable inspection''' 319 fn, source = '<test>', 'def x(): pass\n' 320 getlines = linecache.getlines 321 def monkey(filename, module_globals=None): 322 if filename == fn: 323 return source.splitlines(True) 324 else: 325 return getlines(filename, module_globals) 326 linecache.getlines = monkey 327 try: 328 ns = {} 329 exec compile(source, fn, 'single') in ns 330 inspect.getsource(ns["x"]) 331 finally: 332 linecache.getlines = getlines 333 334class TestGettingSourceOfToplevelFrames(GetSourceBase): 335 fodderFile = mod 336 337 def test_range_toplevel_frame(self): 338 self.maxDiff = None 339 self.assertSourceEqual(mod.currentframe, 1, None) 340 341 def test_range_traceback_toplevel_frame(self): 342 self.assertSourceEqual(mod.tb, 1, None) 343 344class TestDecorators(GetSourceBase): 345 fodderFile = mod2 346 347 def test_wrapped_decorator(self): 348 self.assertSourceEqual(mod2.wrapped, 14, 17) 349 350 def test_replacing_decorator(self): 351 self.assertSourceEqual(mod2.gone, 9, 10) 352 353class TestOneliners(GetSourceBase): 354 fodderFile = mod2 355 def test_oneline_lambda(self): 356 # Test inspect.getsource with a one-line lambda function. 357 self.assertSourceEqual(mod2.oll, 25, 25) 358 359 def test_threeline_lambda(self): 360 # Test inspect.getsource with a three-line lambda function, 361 # where the second and third lines are _not_ indented. 362 self.assertSourceEqual(mod2.tll, 28, 30) 363 364 def test_twoline_indented_lambda(self): 365 # Test inspect.getsource with a two-line lambda function, 366 # where the second line _is_ indented. 367 self.assertSourceEqual(mod2.tlli, 33, 34) 368 369 def test_onelinefunc(self): 370 # Test inspect.getsource with a regular one-line function. 371 self.assertSourceEqual(mod2.onelinefunc, 37, 37) 372 373 def test_manyargs(self): 374 # Test inspect.getsource with a regular function where 375 # the arguments are on two lines and _not_ indented and 376 # the body on the second line with the last arguments. 377 self.assertSourceEqual(mod2.manyargs, 40, 41) 378 379 def test_twolinefunc(self): 380 # Test inspect.getsource with a regular function where 381 # the body is on two lines, following the argument list and 382 # continued on the next line by a \\. 383 self.assertSourceEqual(mod2.twolinefunc, 44, 45) 384 385 def test_lambda_in_list(self): 386 # Test inspect.getsource with a one-line lambda function 387 # defined in a list, indented. 388 self.assertSourceEqual(mod2.a[1], 49, 49) 389 390 def test_anonymous(self): 391 # Test inspect.getsource with a lambda function defined 392 # as argument to another function. 393 self.assertSourceEqual(mod2.anonymous, 55, 55) 394 395class TestBuggyCases(GetSourceBase): 396 fodderFile = mod2 397 398 def test_with_comment(self): 399 self.assertSourceEqual(mod2.with_comment, 58, 59) 400 401 def test_multiline_sig(self): 402 self.assertSourceEqual(mod2.multiline_sig[0], 63, 64) 403 404 def test_nested_class(self): 405 self.assertSourceEqual(mod2.func69().func71, 71, 72) 406 407 def test_one_liner_followed_by_non_name(self): 408 self.assertSourceEqual(mod2.func77, 77, 77) 409 410 def test_one_liner_dedent_non_name(self): 411 self.assertSourceEqual(mod2.cls82.func83, 83, 83) 412 413 def test_with_comment_instead_of_docstring(self): 414 self.assertSourceEqual(mod2.func88, 88, 90) 415 416 def test_method_in_dynamic_class(self): 417 self.assertSourceEqual(mod2.method_in_dynamic_class, 95, 97) 418 419 @unittest.skipIf( 420 not hasattr(unicodedata, '__file__') or 421 unicodedata.__file__[-4:] in (".pyc", ".pyo"), 422 "unicodedata is not an external binary module") 423 def test_findsource_binary(self): 424 self.assertRaises(IOError, inspect.getsource, unicodedata) 425 self.assertRaises(IOError, inspect.findsource, unicodedata) 426 427 def test_findsource_code_in_linecache(self): 428 lines = ["x=1"] 429 co = compile(lines[0], "_dynamically_created_file", "exec") 430 self.assertRaises(IOError, inspect.findsource, co) 431 self.assertRaises(IOError, inspect.getsource, co) 432 linecache.cache[co.co_filename] = (1, None, lines, co.co_filename) 433 self.assertEqual(inspect.findsource(co), (lines,0)) 434 self.assertEqual(inspect.getsource(co), lines[0]) 435 436 def test_findsource_without_filename(self): 437 for fname in ['', '<string>']: 438 co = compile('x=1', fname, "exec") 439 self.assertRaises(IOError, inspect.findsource, co) 440 self.assertRaises(IOError, inspect.getsource, co) 441 442 443class _BrokenDataDescriptor(object): 444 """ 445 A broken data descriptor. See bug #1785. 446 """ 447 def __get__(*args): 448 raise AssertionError("should not __get__ data descriptors") 449 450 def __set__(*args): 451 raise RuntimeError 452 453 def __getattr__(*args): 454 raise AssertionError("should not __getattr__ data descriptors") 455 456 457class _BrokenMethodDescriptor(object): 458 """ 459 A broken method descriptor. See bug #1785. 460 """ 461 def __get__(*args): 462 raise AssertionError("should not __get__ method descriptors") 463 464 def __getattr__(*args): 465 raise AssertionError("should not __getattr__ method descriptors") 466 467 468# Helper for testing classify_class_attrs. 469def attrs_wo_objs(cls): 470 return [t[:3] for t in inspect.classify_class_attrs(cls)] 471 472 473class TestClassesAndFunctions(unittest.TestCase): 474 def test_classic_mro(self): 475 # Test classic-class method resolution order. 476 class A: pass 477 class B(A): pass 478 class C(A): pass 479 class D(B, C): pass 480 481 expected = (D, B, A, C) 482 got = inspect.getmro(D) 483 self.assertEqual(expected, got) 484 485 def test_newstyle_mro(self): 486 # The same w/ new-class MRO. 487 class A(object): pass 488 class B(A): pass 489 class C(A): pass 490 class D(B, C): pass 491 492 expected = (D, B, C, A, object) 493 got = inspect.getmro(D) 494 self.assertEqual(expected, got) 495 496 def assertArgSpecEquals(self, routine, args_e, varargs_e = None, 497 varkw_e = None, defaults_e = None, 498 formatted = None): 499 args, varargs, varkw, defaults = inspect.getargspec(routine) 500 self.assertEqual(args, args_e) 501 self.assertEqual(varargs, varargs_e) 502 self.assertEqual(varkw, varkw_e) 503 self.assertEqual(defaults, defaults_e) 504 if formatted is not None: 505 self.assertEqual(inspect.formatargspec(args, varargs, varkw, defaults), 506 formatted) 507 508 def test_getargspec(self): 509 self.assertArgSpecEquals(mod.eggs, ['x', 'y'], formatted = '(x, y)') 510 511 self.assertArgSpecEquals(mod.spam, 512 ['a', 'b', 'c', 'd', ['e', ['f']]], 513 'g', 'h', (3, (4, (5,))), 514 '(a, b, c, d=3, (e, (f,))=(4, (5,)), *g, **h)') 515 516 with check_py3k_warnings(("tuple parameter unpacking has been removed", 517 SyntaxWarning), 518 quiet=True): 519 exec(textwrap.dedent(''' 520 def spam_deref(a, b, c, d=3, (e, (f,))=(4, (5,)), *g, **h): 521 def eggs(): 522 return a + b + c + d + e + f + g + h 523 return eggs 524 ''')) 525 self.assertArgSpecEquals(spam_deref, 526 ['a', 'b', 'c', 'd', ['e', ['f']]], 527 'g', 'h', (3, (4, (5,))), 528 '(a, b, c, d=3, (e, (f,))=(4, (5,)), *g, **h)') 529 530 def test_getargspec_method(self): 531 class A(object): 532 def m(self): 533 pass 534 self.assertArgSpecEquals(A.m, ['self']) 535 536 def test_getargspec_sublistofone(self): 537 with check_py3k_warnings( 538 ("tuple parameter unpacking has been removed", SyntaxWarning), 539 ("parenthesized argument names are invalid", SyntaxWarning)): 540 exec 'def sublistOfOne((foo,)): return 1' 541 self.assertArgSpecEquals(sublistOfOne, [['foo']]) 542 543 exec 'def sublistOfOne((foo,)): return (lambda: foo)' 544 self.assertArgSpecEquals(sublistOfOne, [['foo']]) 545 546 exec 'def fakeSublistOfOne((foo)): return 1' 547 self.assertArgSpecEquals(fakeSublistOfOne, ['foo']) 548 549 exec 'def sublistOfOne((foo)): return (lambda: foo)' 550 self.assertArgSpecEquals(sublistOfOne, ['foo']) 551 552 553 def _classify_test(self, newstyle): 554 """Helper for testing that classify_class_attrs finds a bunch of 555 different kinds of attributes on a given class. 556 """ 557 if newstyle: 558 base = object 559 else: 560 class base: 561 pass 562 563 class A(base): 564 def s(): pass 565 s = staticmethod(s) 566 567 def c(cls): pass 568 c = classmethod(c) 569 570 def getp(self): pass 571 p = property(getp) 572 573 def m(self): pass 574 575 def m1(self): pass 576 577 datablob = '1' 578 579 dd = _BrokenDataDescriptor() 580 md = _BrokenMethodDescriptor() 581 582 attrs = attrs_wo_objs(A) 583 self.assertIn(('s', 'static method', A), attrs, 'missing static method') 584 self.assertIn(('c', 'class method', A), attrs, 'missing class method') 585 self.assertIn(('p', 'property', A), attrs, 'missing property') 586 self.assertIn(('m', 'method', A), attrs, 'missing plain method') 587 self.assertIn(('m1', 'method', A), attrs, 'missing plain method') 588 self.assertIn(('datablob', 'data', A), attrs, 'missing data') 589 self.assertIn(('md', 'method', A), attrs, 'missing method descriptor') 590 self.assertIn(('dd', 'data', A), attrs, 'missing data descriptor') 591 592 class B(A): 593 def m(self): pass 594 595 attrs = attrs_wo_objs(B) 596 self.assertIn(('s', 'static method', A), attrs, 'missing static method') 597 self.assertIn(('c', 'class method', A), attrs, 'missing class method') 598 self.assertIn(('p', 'property', A), attrs, 'missing property') 599 self.assertIn(('m', 'method', B), attrs, 'missing plain method') 600 self.assertIn(('m1', 'method', A), attrs, 'missing plain method') 601 self.assertIn(('datablob', 'data', A), attrs, 'missing data') 602 self.assertIn(('md', 'method', A), attrs, 'missing method descriptor') 603 self.assertIn(('dd', 'data', A), attrs, 'missing data descriptor') 604 605 606 class C(A): 607 def m(self): pass 608 def c(self): pass 609 610 attrs = attrs_wo_objs(C) 611 self.assertIn(('s', 'static method', A), attrs, 'missing static method') 612 self.assertIn(('c', 'method', C), attrs, 'missing plain method') 613 self.assertIn(('p', 'property', A), attrs, 'missing property') 614 self.assertIn(('m', 'method', C), attrs, 'missing plain method') 615 self.assertIn(('m1', 'method', A), attrs, 'missing plain method') 616 self.assertIn(('datablob', 'data', A), attrs, 'missing data') 617 self.assertIn(('md', 'method', A), attrs, 'missing method descriptor') 618 self.assertIn(('dd', 'data', A), attrs, 'missing data descriptor') 619 620 class D(B, C): 621 def m1(self): pass 622 623 attrs = attrs_wo_objs(D) 624 self.assertIn(('s', 'static method', A), attrs, 'missing static method') 625 if newstyle: 626 self.assertIn(('c', 'method', C), attrs, 'missing plain method') 627 else: 628 self.assertIn(('c', 'class method', A), attrs, 'missing class method') 629 self.assertIn(('p', 'property', A), attrs, 'missing property') 630 self.assertIn(('m', 'method', B), attrs, 'missing plain method') 631 self.assertIn(('m1', 'method', D), attrs, 'missing plain method') 632 self.assertIn(('datablob', 'data', A), attrs, 'missing data') 633 self.assertIn(('md', 'method', A), attrs, 'missing method descriptor') 634 self.assertIn(('dd', 'data', A), attrs, 'missing data descriptor') 635 636 637 def test_classify_oldstyle(self): 638 """classify_class_attrs finds static methods, class methods, 639 properties, normal methods, and data attributes on an old-style 640 class. 641 """ 642 self._classify_test(False) 643 644 645 def test_classify_newstyle(self): 646 """Just like test_classify_oldstyle, but for a new-style class. 647 """ 648 self._classify_test(True) 649 650 def test_classify_builtin_types(self): 651 # Simple sanity check that all built-in types can have their 652 # attributes classified. 653 for name in dir(__builtin__): 654 builtin = getattr(__builtin__, name) 655 if isinstance(builtin, type): 656 inspect.classify_class_attrs(builtin) 657 658 def test_getmembers_method(self): 659 # Old-style classes 660 class B: 661 def f(self): 662 pass 663 664 self.assertIn(('f', B.f), inspect.getmembers(B)) 665 # contrary to spec, ismethod() is also True for unbound methods 666 # (see #1785) 667 self.assertIn(('f', B.f), inspect.getmembers(B, inspect.ismethod)) 668 b = B() 669 self.assertIn(('f', b.f), inspect.getmembers(b)) 670 self.assertIn(('f', b.f), inspect.getmembers(b, inspect.ismethod)) 671 672 # New-style classes 673 class B(object): 674 def f(self): 675 pass 676 677 self.assertIn(('f', B.f), inspect.getmembers(B)) 678 self.assertIn(('f', B.f), inspect.getmembers(B, inspect.ismethod)) 679 b = B() 680 self.assertIn(('f', b.f), inspect.getmembers(b)) 681 self.assertIn(('f', b.f), inspect.getmembers(b, inspect.ismethod)) 682 683 684class TestGetcallargsFunctions(unittest.TestCase): 685 686 # tuple parameters are named '.1', '.2', etc. 687 is_tuplename = re.compile(r'^\.\d+$').match 688 689 def assertEqualCallArgs(self, func, call_params_string, locs=None): 690 locs = dict(locs or {}, func=func) 691 r1 = eval('func(%s)' % call_params_string, None, locs) 692 r2 = eval('inspect.getcallargs(func, %s)' % call_params_string, None, 693 locs) 694 self.assertEqual(r1, r2) 695 696 def assertEqualException(self, func, call_param_string, locs=None): 697 locs = dict(locs or {}, func=func) 698 try: 699 eval('func(%s)' % call_param_string, None, locs) 700 except Exception, ex1: 701 pass 702 else: 703 self.fail('Exception not raised') 704 try: 705 eval('inspect.getcallargs(func, %s)' % call_param_string, None, 706 locs) 707 except Exception, ex2: 708 pass 709 else: 710 self.fail('Exception not raised') 711 self.assertIs(type(ex1), type(ex2)) 712 self.assertEqual(str(ex1), str(ex2)) 713 714 def makeCallable(self, signature): 715 """Create a function that returns its locals(), excluding the 716 autogenerated '.1', '.2', etc. tuple param names (if any).""" 717 with check_py3k_warnings( 718 ("tuple parameter unpacking has been removed", SyntaxWarning), 719 quiet=True): 720 code = ("lambda %s: dict(i for i in locals().items() " 721 "if not is_tuplename(i[0]))") 722 return eval(code % signature, {'is_tuplename' : self.is_tuplename}) 723 724 def test_plain(self): 725 f = self.makeCallable('a, b=1') 726 self.assertEqualCallArgs(f, '2') 727 self.assertEqualCallArgs(f, '2, 3') 728 self.assertEqualCallArgs(f, 'a=2') 729 self.assertEqualCallArgs(f, 'b=3, a=2') 730 self.assertEqualCallArgs(f, '2, b=3') 731 # expand *iterable / **mapping 732 self.assertEqualCallArgs(f, '*(2,)') 733 self.assertEqualCallArgs(f, '*[2]') 734 self.assertEqualCallArgs(f, '*(2, 3)') 735 self.assertEqualCallArgs(f, '*[2, 3]') 736 self.assertEqualCallArgs(f, '**{"a":2}') 737 self.assertEqualCallArgs(f, 'b=3, **{"a":2}') 738 self.assertEqualCallArgs(f, '2, **{"b":3}') 739 self.assertEqualCallArgs(f, '**{"b":3, "a":2}') 740 # expand UserList / UserDict 741 self.assertEqualCallArgs(f, '*UserList([2])') 742 self.assertEqualCallArgs(f, '*UserList([2, 3])') 743 self.assertEqualCallArgs(f, '**UserDict(a=2)') 744 self.assertEqualCallArgs(f, '2, **UserDict(b=3)') 745 self.assertEqualCallArgs(f, 'b=2, **UserDict(a=3)') 746 # unicode keyword args 747 self.assertEqualCallArgs(f, '**{u"a":2}') 748 self.assertEqualCallArgs(f, 'b=3, **{u"a":2}') 749 self.assertEqualCallArgs(f, '2, **{u"b":3}') 750 self.assertEqualCallArgs(f, '**{u"b":3, u"a":2}') 751 752 def test_varargs(self): 753 f = self.makeCallable('a, b=1, *c') 754 self.assertEqualCallArgs(f, '2') 755 self.assertEqualCallArgs(f, '2, 3') 756 self.assertEqualCallArgs(f, '2, 3, 4') 757 self.assertEqualCallArgs(f, '*(2,3,4)') 758 self.assertEqualCallArgs(f, '2, *[3,4]') 759 self.assertEqualCallArgs(f, '2, 3, *UserList([4])') 760 761 def test_varkw(self): 762 f = self.makeCallable('a, b=1, **c') 763 self.assertEqualCallArgs(f, 'a=2') 764 self.assertEqualCallArgs(f, '2, b=3, c=4') 765 self.assertEqualCallArgs(f, 'b=3, a=2, c=4') 766 self.assertEqualCallArgs(f, 'c=4, **{"a":2, "b":3}') 767 self.assertEqualCallArgs(f, '2, c=4, **{"b":3}') 768 self.assertEqualCallArgs(f, 'b=2, **{"a":3, "c":4}') 769 self.assertEqualCallArgs(f, '**UserDict(a=2, b=3, c=4)') 770 self.assertEqualCallArgs(f, '2, c=4, **UserDict(b=3)') 771 self.assertEqualCallArgs(f, 'b=2, **UserDict(a=3, c=4)') 772 # unicode keyword args 773 self.assertEqualCallArgs(f, 'c=4, **{u"a":2, u"b":3}') 774 self.assertEqualCallArgs(f, '2, c=4, **{u"b":3}') 775 self.assertEqualCallArgs(f, 'b=2, **{u"a":3, u"c":4}') 776 777 def test_varkw_only(self): 778 # issue11256: 779 f = self.makeCallable('**c') 780 self.assertEqualCallArgs(f, '') 781 self.assertEqualCallArgs(f, 'a=1') 782 self.assertEqualCallArgs(f, 'a=1, b=2') 783 self.assertEqualCallArgs(f, 'c=3, **{"a": 1, "b": 2}') 784 self.assertEqualCallArgs(f, '**UserDict(a=1, b=2)') 785 self.assertEqualCallArgs(f, 'c=3, **UserDict(a=1, b=2)') 786 787 def test_tupleargs(self): 788 f = self.makeCallable('(b,c), (d,(e,f))=(0,[1,2])') 789 self.assertEqualCallArgs(f, '(2,3)') 790 self.assertEqualCallArgs(f, '[2,3]') 791 self.assertEqualCallArgs(f, 'UserList([2,3])') 792 self.assertEqualCallArgs(f, '(2,3), (4,(5,6))') 793 self.assertEqualCallArgs(f, '(2,3), (4,[5,6])') 794 self.assertEqualCallArgs(f, '(2,3), [4,UserList([5,6])]') 795 796 def test_multiple_features(self): 797 f = self.makeCallable('a, b=2, (c,(d,e))=(3,[4,5]), *f, **g') 798 self.assertEqualCallArgs(f, '2, 3, (4,[5,6]), 7') 799 self.assertEqualCallArgs(f, '2, 3, *[(4,[5,6]), 7], x=8') 800 self.assertEqualCallArgs(f, '2, 3, x=8, *[(4,[5,6]), 7]') 801 self.assertEqualCallArgs(f, '2, x=8, *[3, (4,[5,6]), 7], y=9') 802 self.assertEqualCallArgs(f, 'x=8, *[2, 3, (4,[5,6])], y=9') 803 self.assertEqualCallArgs(f, 'x=8, *UserList([2, 3, (4,[5,6])]), ' 804 '**{"y":9, "z":10}') 805 self.assertEqualCallArgs(f, '2, x=8, *UserList([3, (4,[5,6])]), ' 806 '**UserDict(y=9, z=10)') 807 808 def test_errors(self): 809 f0 = self.makeCallable('') 810 f1 = self.makeCallable('a, b') 811 f2 = self.makeCallable('a, b=1') 812 # f0 takes no arguments 813 self.assertEqualException(f0, '1') 814 self.assertEqualException(f0, 'x=1') 815 self.assertEqualException(f0, '1,x=1') 816 # f1 takes exactly 2 arguments 817 self.assertEqualException(f1, '') 818 self.assertEqualException(f1, '1') 819 self.assertEqualException(f1, 'a=2') 820 self.assertEqualException(f1, 'b=3') 821 # f2 takes at least 1 argument 822 self.assertEqualException(f2, '') 823 self.assertEqualException(f2, 'b=3') 824 for f in f1, f2: 825 # f1/f2 takes exactly/at most 2 arguments 826 self.assertEqualException(f, '2, 3, 4') 827 self.assertEqualException(f, '1, 2, 3, a=1') 828 self.assertEqualException(f, '2, 3, 4, c=5') 829 self.assertEqualException(f, '2, 3, 4, a=1, c=5') 830 # f got an unexpected keyword argument 831 self.assertEqualException(f, 'c=2') 832 self.assertEqualException(f, '2, c=3') 833 self.assertEqualException(f, '2, 3, c=4') 834 self.assertEqualException(f, '2, c=4, b=3') 835 if have_unicode: 836 self.assertEqualException(f, '**{u"\u03c0\u03b9": 4}') 837 # f got multiple values for keyword argument 838 self.assertEqualException(f, '1, a=2') 839 self.assertEqualException(f, '1, **{"a":2}') 840 self.assertEqualException(f, '1, 2, b=3') 841 # XXX: Python inconsistency 842 # - for functions and bound methods: unexpected keyword 'c' 843 # - for unbound methods: multiple values for keyword 'a' 844 #self.assertEqualException(f, '1, c=3, a=2') 845 f = self.makeCallable('(a,b)=(0,1)') 846 self.assertEqualException(f, '1') 847 self.assertEqualException(f, '[1]') 848 self.assertEqualException(f, '(1,2,3)') 849 # issue11256: 850 f3 = self.makeCallable('**c') 851 self.assertEqualException(f3, '1, 2') 852 self.assertEqualException(f3, '1, 2, a=1, b=2') 853 854 855class TestGetcallargsFunctionsCellVars(TestGetcallargsFunctions): 856 857 def makeCallable(self, signature): 858 """Create a function that returns its locals(), excluding the 859 autogenerated '.1', '.2', etc. tuple param names (if any).""" 860 with check_py3k_warnings( 861 ("tuple parameter unpacking has been removed", SyntaxWarning), 862 quiet=True): 863 code = """lambda %s: ( 864 (lambda: a+b+c+d+d+e+f+g+h), # make parameters cell vars 865 dict(i for i in locals().items() 866 if not is_tuplename(i[0])) 867 )[1]""" 868 return eval(code % signature, {'is_tuplename' : self.is_tuplename}) 869 870 871class TestGetcallargsMethods(TestGetcallargsFunctions): 872 873 def setUp(self): 874 class Foo(object): 875 pass 876 self.cls = Foo 877 self.inst = Foo() 878 879 def makeCallable(self, signature): 880 assert 'self' not in signature 881 mk = super(TestGetcallargsMethods, self).makeCallable 882 self.cls.method = mk('self, ' + signature) 883 return self.inst.method 884 885class TestGetcallargsUnboundMethods(TestGetcallargsMethods): 886 887 def makeCallable(self, signature): 888 super(TestGetcallargsUnboundMethods, self).makeCallable(signature) 889 return self.cls.method 890 891 def assertEqualCallArgs(self, func, call_params_string, locs=None): 892 return super(TestGetcallargsUnboundMethods, self).assertEqualCallArgs( 893 *self._getAssertEqualParams(func, call_params_string, locs)) 894 895 def assertEqualException(self, func, call_params_string, locs=None): 896 return super(TestGetcallargsUnboundMethods, self).assertEqualException( 897 *self._getAssertEqualParams(func, call_params_string, locs)) 898 899 def _getAssertEqualParams(self, func, call_params_string, locs=None): 900 assert 'inst' not in call_params_string 901 locs = dict(locs or {}, inst=self.inst) 902 return (func, 'inst,' + call_params_string, locs) 903 904def test_main(): 905 run_unittest( 906 TestDecorators, TestRetrievingSourceCode, TestOneliners, TestBuggyCases, 907 TestInterpreterStack, TestClassesAndFunctions, TestPredicates, 908 TestGetcallargsFunctions, TestGetcallargsFunctionsCellVars, 909 TestGetcallargsMethods, TestGetcallargsUnboundMethods, 910 TestGettingSourceOfToplevelFrames) 911 912if __name__ == "__main__": 913 test_main() 914