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