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
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
20import 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
32modfile = mod.__file__
33if modfile.endswith(('c', 'o')):
34    modfile = modfile[:-1]
35
36import __builtin__
37
38try:
39    1 // 0
40except:
41    tb = sys.exc_traceback
42
43git = mod.StupidGit()
44
45class 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
61def generator_function_example(self):
62    for i in xrange(2):
63        yield i
64
65class 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
149class 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
196class 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
214class 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
315class 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
324class 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
366class 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
414class _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
428class _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.
440def attrs_wo_objs(cls):
441    return [t[:3] for t in inspect.classify_class_attrs(cls)]
442
443
444class 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
635class 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
804class 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
818class 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
837def test_main():
838    run_unittest(
839        TestDecorators, TestRetrievingSourceCode, TestOneliners, TestBuggyCases,
840        TestInterpreterStack, TestClassesAndFunctions, TestPredicates,
841        TestGetcallargsFunctions, TestGetcallargsMethods,
842        TestGetcallargsUnboundMethods)
843
844if __name__ == "__main__":
845    test_main()
846