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