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
298class TestDecorators(GetSourceBase):
299    fodderFile = mod2
300
301    def test_wrapped_decorator(self):
302        self.assertSourceEqual(mod2.wrapped, 14, 17)
303
304    def test_replacing_decorator(self):
305        self.assertSourceEqual(mod2.gone, 9, 10)
306
307class TestOneliners(GetSourceBase):
308    fodderFile = mod2
309    def test_oneline_lambda(self):
310        # Test inspect.getsource with a one-line lambda function.
311        self.assertSourceEqual(mod2.oll, 25, 25)
312
313    def test_threeline_lambda(self):
314        # Test inspect.getsource with a three-line lambda function,
315        # where the second and third lines are _not_ indented.
316        self.assertSourceEqual(mod2.tll, 28, 30)
317
318    def test_twoline_indented_lambda(self):
319        # Test inspect.getsource with a two-line lambda function,
320        # where the second line _is_ indented.
321        self.assertSourceEqual(mod2.tlli, 33, 34)
322
323    def test_onelinefunc(self):
324        # Test inspect.getsource with a regular one-line function.
325        self.assertSourceEqual(mod2.onelinefunc, 37, 37)
326
327    def test_manyargs(self):
328        # Test inspect.getsource with a regular function where
329        # the arguments are on two lines and _not_ indented and
330        # the body on the second line with the last arguments.
331        self.assertSourceEqual(mod2.manyargs, 40, 41)
332
333    def test_twolinefunc(self):
334        # Test inspect.getsource with a regular function where
335        # the body is on two lines, following the argument list and
336        # continued on the next line by a \\.
337        self.assertSourceEqual(mod2.twolinefunc, 44, 45)
338
339    def test_lambda_in_list(self):
340        # Test inspect.getsource with a one-line lambda function
341        # defined in a list, indented.
342        self.assertSourceEqual(mod2.a[1], 49, 49)
343
344    def test_anonymous(self):
345        # Test inspect.getsource with a lambda function defined
346        # as argument to another function.
347        self.assertSourceEqual(mod2.anonymous, 55, 55)
348
349class TestBuggyCases(GetSourceBase):
350    fodderFile = mod2
351
352    def test_with_comment(self):
353        self.assertSourceEqual(mod2.with_comment, 58, 59)
354
355    def test_multiline_sig(self):
356        self.assertSourceEqual(mod2.multiline_sig[0], 63, 64)
357
358    def test_nested_class(self):
359        self.assertSourceEqual(mod2.func69().func71, 71, 72)
360
361    def test_one_liner_followed_by_non_name(self):
362        self.assertSourceEqual(mod2.func77, 77, 77)
363
364    def test_one_liner_dedent_non_name(self):
365        self.assertSourceEqual(mod2.cls82.func83, 83, 83)
366
367    def test_with_comment_instead_of_docstring(self):
368        self.assertSourceEqual(mod2.func88, 88, 90)
369
370    def test_method_in_dynamic_class(self):
371        self.assertSourceEqual(mod2.method_in_dynamic_class, 95, 97)
372
373    @unittest.skipIf(
374        not hasattr(unicodedata, '__file__') or
375            unicodedata.__file__[-4:] in (".pyc", ".pyo"),
376        "unicodedata is not an external binary module")
377    def test_findsource_binary(self):
378        self.assertRaises(IOError, inspect.getsource, unicodedata)
379        self.assertRaises(IOError, inspect.findsource, unicodedata)
380
381    def test_findsource_code_in_linecache(self):
382        lines = ["x=1"]
383        co = compile(lines[0], "_dynamically_created_file", "exec")
384        self.assertRaises(IOError, inspect.findsource, co)
385        self.assertRaises(IOError, inspect.getsource, co)
386        linecache.cache[co.co_filename] = (1, None, lines, co.co_filename)
387        self.assertEqual(inspect.findsource(co), (lines,0))
388        self.assertEqual(inspect.getsource(co), lines[0])
389
390# Helper for testing classify_class_attrs.
391def attrs_wo_objs(cls):
392    return [t[:3] for t in inspect.classify_class_attrs(cls)]
393
394class TestClassesAndFunctions(unittest.TestCase):
395    def test_classic_mro(self):
396        # Test classic-class method resolution order.
397        class A:    pass
398        class B(A): pass
399        class C(A): pass
400        class D(B, C): pass
401
402        expected = (D, B, A, C)
403        got = inspect.getmro(D)
404        self.assertEqual(expected, got)
405
406    def test_newstyle_mro(self):
407        # The same w/ new-class MRO.
408        class A(object):    pass
409        class B(A): pass
410        class C(A): pass
411        class D(B, C): pass
412
413        expected = (D, B, C, A, object)
414        got = inspect.getmro(D)
415        self.assertEqual(expected, got)
416
417    def assertArgSpecEquals(self, routine, args_e, varargs_e = None,
418                            varkw_e = None, defaults_e = None,
419                            formatted = None):
420        args, varargs, varkw, defaults = inspect.getargspec(routine)
421        self.assertEqual(args, args_e)
422        self.assertEqual(varargs, varargs_e)
423        self.assertEqual(varkw, varkw_e)
424        self.assertEqual(defaults, defaults_e)
425        if formatted is not None:
426            self.assertEqual(inspect.formatargspec(args, varargs, varkw, defaults),
427                             formatted)
428
429    def test_getargspec(self):
430        self.assertArgSpecEquals(mod.eggs, ['x', 'y'], formatted = '(x, y)')
431
432        self.assertArgSpecEquals(mod.spam,
433                                 ['a', 'b', 'c', 'd', ['e', ['f']]],
434                                 'g', 'h', (3, (4, (5,))),
435                                 '(a, b, c, d=3, (e, (f,))=(4, (5,)), *g, **h)')
436
437    def test_getargspec_method(self):
438        class A(object):
439            def m(self):
440                pass
441        self.assertArgSpecEquals(A.m, ['self'])
442
443    def test_getargspec_sublistofone(self):
444        with check_py3k_warnings(
445                ("tuple parameter unpacking has been removed", SyntaxWarning),
446                ("parenthesized argument names are invalid", SyntaxWarning)):
447            exec 'def sublistOfOne((foo,)): return 1'
448            self.assertArgSpecEquals(sublistOfOne, [['foo']])
449
450            exec 'def fakeSublistOfOne((foo)): return 1'
451            self.assertArgSpecEquals(fakeSublistOfOne, ['foo'])
452
453
454    def _classify_test(self, newstyle):
455        """Helper for testing that classify_class_attrs finds a bunch of
456        different kinds of attributes on a given class.
457        """
458        if newstyle:
459            base = object
460        else:
461            class base:
462                pass
463
464        class A(base):
465            def s(): pass
466            s = staticmethod(s)
467
468            def c(cls): pass
469            c = classmethod(c)
470
471            def getp(self): pass
472            p = property(getp)
473
474            def m(self): pass
475
476            def m1(self): pass
477
478            datablob = '1'
479
480        attrs = attrs_wo_objs(A)
481        self.assertIn(('s', 'static method', A), attrs, 'missing static method')
482        self.assertIn(('c', 'class method', A), attrs, 'missing class method')
483        self.assertIn(('p', 'property', A), attrs, 'missing property')
484        self.assertIn(('m', 'method', A), attrs, 'missing plain method')
485        self.assertIn(('m1', 'method', A), attrs, 'missing plain method')
486        self.assertIn(('datablob', 'data', A), attrs, 'missing data')
487
488        class B(A):
489            def m(self): pass
490
491        attrs = attrs_wo_objs(B)
492        self.assertIn(('s', 'static method', A), attrs, 'missing static method')
493        self.assertIn(('c', 'class method', A), attrs, 'missing class method')
494        self.assertIn(('p', 'property', A), attrs, 'missing property')
495        self.assertIn(('m', 'method', B), attrs, 'missing plain method')
496        self.assertIn(('m1', 'method', A), attrs, 'missing plain method')
497        self.assertIn(('datablob', 'data', A), attrs, 'missing data')
498
499
500        class C(A):
501            def m(self): pass
502            def c(self): pass
503
504        attrs = attrs_wo_objs(C)
505        self.assertIn(('s', 'static method', A), attrs, 'missing static method')
506        self.assertIn(('c', 'method', C), attrs, 'missing plain method')
507        self.assertIn(('p', 'property', A), attrs, 'missing property')
508        self.assertIn(('m', 'method', C), attrs, 'missing plain method')
509        self.assertIn(('m1', 'method', A), attrs, 'missing plain method')
510        self.assertIn(('datablob', 'data', A), attrs, 'missing data')
511
512        class D(B, C):
513            def m1(self): pass
514
515        attrs = attrs_wo_objs(D)
516        self.assertIn(('s', 'static method', A), attrs, 'missing static method')
517        if newstyle:
518            self.assertIn(('c', 'method', C), attrs, 'missing plain method')
519        else:
520            self.assertIn(('c', 'class method', A), attrs, 'missing class method')
521        self.assertIn(('p', 'property', A), attrs, 'missing property')
522        self.assertIn(('m', 'method', B), attrs, 'missing plain method')
523        self.assertIn(('m1', 'method', D), attrs, 'missing plain method')
524        self.assertIn(('datablob', 'data', A), attrs, 'missing data')
525
526
527    def test_classify_oldstyle(self):
528        """classify_class_attrs finds static methods, class methods,
529        properties, normal methods, and data attributes on an old-style
530        class.
531        """
532        self._classify_test(False)
533
534
535    def test_classify_newstyle(self):
536        """Just like test_classify_oldstyle, but for a new-style class.
537        """
538        self._classify_test(True)
539
540
541
542class TestGetcallargsFunctions(unittest.TestCase):
543
544    # tuple parameters are named '.1', '.2', etc.
545    is_tuplename = re.compile(r'^\.\d+$').match
546
547    def assertEqualCallArgs(self, func, call_params_string, locs=None):
548        locs = dict(locs or {}, func=func)
549        r1 = eval('func(%s)' % call_params_string, None, locs)
550        r2 = eval('inspect.getcallargs(func, %s)' % call_params_string, None,
551                  locs)
552        self.assertEqual(r1, r2)
553
554    def assertEqualException(self, func, call_param_string, locs=None):
555        locs = dict(locs or {}, func=func)
556        try:
557            eval('func(%s)' % call_param_string, None, locs)
558        except Exception, ex1:
559            pass
560        else:
561            self.fail('Exception not raised')
562        try:
563            eval('inspect.getcallargs(func, %s)' % call_param_string, None,
564                 locs)
565        except Exception, ex2:
566            pass
567        else:
568            self.fail('Exception not raised')
569        self.assertIs(type(ex1), type(ex2))
570        self.assertEqual(str(ex1), str(ex2))
571
572    def makeCallable(self, signature):
573        """Create a function that returns its locals(), excluding the
574        autogenerated '.1', '.2', etc. tuple param names (if any)."""
575        with check_py3k_warnings(
576            ("tuple parameter unpacking has been removed", SyntaxWarning),
577            quiet=True):
578            code = ("lambda %s: dict(i for i in locals().items() "
579                    "if not is_tuplename(i[0]))")
580            return eval(code % signature, {'is_tuplename' : self.is_tuplename})
581
582    def test_plain(self):
583        f = self.makeCallable('a, b=1')
584        self.assertEqualCallArgs(f, '2')
585        self.assertEqualCallArgs(f, '2, 3')
586        self.assertEqualCallArgs(f, 'a=2')
587        self.assertEqualCallArgs(f, 'b=3, a=2')
588        self.assertEqualCallArgs(f, '2, b=3')
589        # expand *iterable / **mapping
590        self.assertEqualCallArgs(f, '*(2,)')
591        self.assertEqualCallArgs(f, '*[2]')
592        self.assertEqualCallArgs(f, '*(2, 3)')
593        self.assertEqualCallArgs(f, '*[2, 3]')
594        self.assertEqualCallArgs(f, '**{"a":2}')
595        self.assertEqualCallArgs(f, 'b=3, **{"a":2}')
596        self.assertEqualCallArgs(f, '2, **{"b":3}')
597        self.assertEqualCallArgs(f, '**{"b":3, "a":2}')
598        # expand UserList / UserDict
599        self.assertEqualCallArgs(f, '*UserList([2])')
600        self.assertEqualCallArgs(f, '*UserList([2, 3])')
601        self.assertEqualCallArgs(f, '**UserDict(a=2)')
602        self.assertEqualCallArgs(f, '2, **UserDict(b=3)')
603        self.assertEqualCallArgs(f, 'b=2, **UserDict(a=3)')
604        # unicode keyword args
605        self.assertEqualCallArgs(f, '**{u"a":2}')
606        self.assertEqualCallArgs(f, 'b=3, **{u"a":2}')
607        self.assertEqualCallArgs(f, '2, **{u"b":3}')
608        self.assertEqualCallArgs(f, '**{u"b":3, u"a":2}')
609
610    def test_varargs(self):
611        f = self.makeCallable('a, b=1, *c')
612        self.assertEqualCallArgs(f, '2')
613        self.assertEqualCallArgs(f, '2, 3')
614        self.assertEqualCallArgs(f, '2, 3, 4')
615        self.assertEqualCallArgs(f, '*(2,3,4)')
616        self.assertEqualCallArgs(f, '2, *[3,4]')
617        self.assertEqualCallArgs(f, '2, 3, *UserList([4])')
618
619    def test_varkw(self):
620        f = self.makeCallable('a, b=1, **c')
621        self.assertEqualCallArgs(f, 'a=2')
622        self.assertEqualCallArgs(f, '2, b=3, c=4')
623        self.assertEqualCallArgs(f, 'b=3, a=2, c=4')
624        self.assertEqualCallArgs(f, 'c=4, **{"a":2, "b":3}')
625        self.assertEqualCallArgs(f, '2, c=4, **{"b":3}')
626        self.assertEqualCallArgs(f, 'b=2, **{"a":3, "c":4}')
627        self.assertEqualCallArgs(f, '**UserDict(a=2, b=3, c=4)')
628        self.assertEqualCallArgs(f, '2, c=4, **UserDict(b=3)')
629        self.assertEqualCallArgs(f, 'b=2, **UserDict(a=3, c=4)')
630        # unicode keyword args
631        self.assertEqualCallArgs(f, 'c=4, **{u"a":2, u"b":3}')
632        self.assertEqualCallArgs(f, '2, c=4, **{u"b":3}')
633        self.assertEqualCallArgs(f, 'b=2, **{u"a":3, u"c":4}')
634
635    def test_varkw_only(self):
636        # issue11256:
637        f = self.makeCallable('**c')
638        self.assertEqualCallArgs(f, '')
639        self.assertEqualCallArgs(f, 'a=1')
640        self.assertEqualCallArgs(f, 'a=1, b=2')
641        self.assertEqualCallArgs(f, 'c=3, **{"a": 1, "b": 2}')
642        self.assertEqualCallArgs(f, '**UserDict(a=1, b=2)')
643        self.assertEqualCallArgs(f, 'c=3, **UserDict(a=1, b=2)')
644
645    def test_tupleargs(self):
646        f = self.makeCallable('(b,c), (d,(e,f))=(0,[1,2])')
647        self.assertEqualCallArgs(f, '(2,3)')
648        self.assertEqualCallArgs(f, '[2,3]')
649        self.assertEqualCallArgs(f, 'UserList([2,3])')
650        self.assertEqualCallArgs(f, '(2,3), (4,(5,6))')
651        self.assertEqualCallArgs(f, '(2,3), (4,[5,6])')
652        self.assertEqualCallArgs(f, '(2,3), [4,UserList([5,6])]')
653
654    def test_multiple_features(self):
655        f = self.makeCallable('a, b=2, (c,(d,e))=(3,[4,5]), *f, **g')
656        self.assertEqualCallArgs(f, '2, 3, (4,[5,6]), 7')
657        self.assertEqualCallArgs(f, '2, 3, *[(4,[5,6]), 7], x=8')
658        self.assertEqualCallArgs(f, '2, 3, x=8, *[(4,[5,6]), 7]')
659        self.assertEqualCallArgs(f, '2, x=8, *[3, (4,[5,6]), 7], y=9')
660        self.assertEqualCallArgs(f, 'x=8, *[2, 3, (4,[5,6])], y=9')
661        self.assertEqualCallArgs(f, 'x=8, *UserList([2, 3, (4,[5,6])]), '
662                                 '**{"y":9, "z":10}')
663        self.assertEqualCallArgs(f, '2, x=8, *UserList([3, (4,[5,6])]), '
664                                 '**UserDict(y=9, z=10)')
665
666    def test_errors(self):
667        f0 = self.makeCallable('')
668        f1 = self.makeCallable('a, b')
669        f2 = self.makeCallable('a, b=1')
670        # f0 takes no arguments
671        self.assertEqualException(f0, '1')
672        self.assertEqualException(f0, 'x=1')
673        self.assertEqualException(f0, '1,x=1')
674        # f1 takes exactly 2 arguments
675        self.assertEqualException(f1, '')
676        self.assertEqualException(f1, '1')
677        self.assertEqualException(f1, 'a=2')
678        self.assertEqualException(f1, 'b=3')
679        # f2 takes at least 1 argument
680        self.assertEqualException(f2, '')
681        self.assertEqualException(f2, 'b=3')
682        for f in f1, f2:
683            # f1/f2 takes exactly/at most 2 arguments
684            self.assertEqualException(f, '2, 3, 4')
685            self.assertEqualException(f, '1, 2, 3, a=1')
686            self.assertEqualException(f, '2, 3, 4, c=5')
687            self.assertEqualException(f, '2, 3, 4, a=1, c=5')
688            # f got an unexpected keyword argument
689            self.assertEqualException(f, 'c=2')
690            self.assertEqualException(f, '2, c=3')
691            self.assertEqualException(f, '2, 3, c=4')
692            self.assertEqualException(f, '2, c=4, b=3')
693            self.assertEqualException(f, '**{u"\u03c0\u03b9": 4}')
694            # f got multiple values for keyword argument
695            self.assertEqualException(f, '1, a=2')
696            self.assertEqualException(f, '1, **{"a":2}')
697            self.assertEqualException(f, '1, 2, b=3')
698            # XXX: Python inconsistency
699            # - for functions and bound methods: unexpected keyword 'c'
700            # - for unbound methods: multiple values for keyword 'a'
701            #self.assertEqualException(f, '1, c=3, a=2')
702        f = self.makeCallable('(a,b)=(0,1)')
703        self.assertEqualException(f, '1')
704        self.assertEqualException(f, '[1]')
705        self.assertEqualException(f, '(1,2,3)')
706        # issue11256:
707        f3 = self.makeCallable('**c')
708        self.assertEqualException(f3, '1, 2')
709        self.assertEqualException(f3, '1, 2, a=1, b=2')
710
711class TestGetcallargsMethods(TestGetcallargsFunctions):
712
713    def setUp(self):
714        class Foo(object):
715            pass
716        self.cls = Foo
717        self.inst = Foo()
718
719    def makeCallable(self, signature):
720        assert 'self' not in signature
721        mk = super(TestGetcallargsMethods, self).makeCallable
722        self.cls.method = mk('self, ' + signature)
723        return self.inst.method
724
725class TestGetcallargsUnboundMethods(TestGetcallargsMethods):
726
727    def makeCallable(self, signature):
728        super(TestGetcallargsUnboundMethods, self).makeCallable(signature)
729        return self.cls.method
730
731    def assertEqualCallArgs(self, func, call_params_string, locs=None):
732        return super(TestGetcallargsUnboundMethods, self).assertEqualCallArgs(
733            *self._getAssertEqualParams(func, call_params_string, locs))
734
735    def assertEqualException(self, func, call_params_string, locs=None):
736        return super(TestGetcallargsUnboundMethods, self).assertEqualException(
737            *self._getAssertEqualParams(func, call_params_string, locs))
738
739    def _getAssertEqualParams(self, func, call_params_string, locs=None):
740        assert 'inst' not in call_params_string
741        locs = dict(locs or {}, inst=self.inst)
742        return (func, 'inst,' + call_params_string, locs)
743
744def test_main():
745    run_unittest(
746        TestDecorators, TestRetrievingSourceCode, TestOneliners, TestBuggyCases,
747        TestInterpreterStack, TestClassesAndFunctions, TestPredicates,
748        TestGetcallargsFunctions, TestGetcallargsMethods,
749        TestGetcallargsUnboundMethods)
750
751if __name__ == "__main__":
752    test_main()
753