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