inspect: a module for getting information out of live Python objects
diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py
new file mode 100644
index 0000000..00b3d7d
--- /dev/null
+++ b/Lib/test/test_inspect.py
@@ -0,0 +1,207 @@
+source = '''# line 1
+'A module docstring.'
+
+import sys, inspect
+# line 5
+
+# line 7
+def spam(a, b, c, d=3, (e, (f,))=(4, (5,)), *g, **h):
+    eggs(b + d, c + f)
+
+# line 11
+def eggs(x, y):
+    "A docstring."
+    global fr, st
+    fr = inspect.currentframe()
+    st = inspect.stack()
+    p = x
+    q = y / 0
+
+# line 20
+class StupidGit:
+    """A longer,
+
+    indented
+
+    docstring."""
+# line 27
+
+    def abuse(self, a, b, c):
+        """Another
+
+\tdocstring
+
+        containing
+
+\ttabs
+\t
+        """
+        self.argue(a, b, c)
+# line 40
+    def argue(self, a, b, c):
+        try:
+            spam(a, b, c)
+        except:
+            self.ex = sys.exc_info()
+            self.tr = inspect.trace()
+
+# line 48
+class MalodorousPervert(StupidGit):
+    pass
+
+class ParrotDroppings:
+    pass
+
+class FesteringGob(MalodorousPervert, ParrotDroppings):
+    pass
+'''
+
+# Functions tested in this suite:
+# ismodule, isclass, ismethod, isfunction, istraceback, isframe, iscode,
+# isbuiltin, isroutine, getmembers, getdoc, getfile, getmodule,
+# getsourcefile, getcomments, getsource, getclasstree, getargspec,
+# getargvalues, formatargspec, formatargvalues, currentframe, stack, trace
+
+from test_support import TestFailed, TESTFN
+import sys, imp, os, string
+
+def test(assertion, message, *args):
+    if not assertion:
+        raise TestFailed, message % args
+
+import inspect
+
+file = open(TESTFN, 'w')
+file.write(source)
+file.close()
+
+mod = imp.load_source('testmod', TESTFN)
+
+def istest(func, exp):
+    obj = eval(exp)
+    test(func(obj), '%s(%s)' % (func.__name__, exp))
+    for other in [inspect.isbuiltin, inspect.isclass, inspect.iscode,
+                  inspect.isframe, inspect.isfunction, inspect.ismethod,
+                  inspect.ismodule, inspect.istraceback]:
+        if other is not func:
+            test(not other(obj), 'not %s(%s)' % (other.__name__, exp))
+
+git = mod.StupidGit()
+try:
+    1/0
+except:
+    tb = sys.exc_traceback
+
+istest(inspect.isbuiltin, 'sys.exit')
+istest(inspect.isbuiltin, '[].append')
+istest(inspect.isclass, 'mod.StupidGit')
+istest(inspect.iscode, 'mod.spam.func_code')
+istest(inspect.isframe, 'tb.tb_frame')
+istest(inspect.isfunction, 'mod.spam')
+istest(inspect.ismethod, 'mod.StupidGit.abuse')
+istest(inspect.ismethod, 'git.argue')
+istest(inspect.ismodule, 'mod')
+istest(inspect.istraceback, 'tb')
+test(inspect.isroutine(mod.spam), 'isroutine(mod.spam)')
+test(inspect.isroutine([].count), 'isroutine([].count)')
+
+classes = inspect.getmembers(mod, inspect.isclass)
+test(classes ==
+     [('FesteringGob', mod.FesteringGob),
+      ('MalodorousPervert', mod.MalodorousPervert),
+      ('ParrotDroppings', mod.ParrotDroppings),
+      ('StupidGit', mod.StupidGit)], 'class list')
+tree = inspect.getclasstree(map(lambda x: x[1], classes), 1)
+test(tree ==
+     [(mod.ParrotDroppings, ()),
+      (mod.StupidGit, ()),
+      [(mod.MalodorousPervert, (mod.StupidGit,)),
+       [(mod.FesteringGob, (mod.MalodorousPervert, mod.ParrotDroppings))
+       ]
+      ]
+     ], 'class tree')
+
+functions = inspect.getmembers(mod, inspect.isfunction)
+test(functions == [('eggs', mod.eggs), ('spam', mod.spam)], 'function list')
+
+test(inspect.getdoc(mod) == 'A module docstring.', 'getdoc(mod)')
+test(inspect.getcomments(mod) == '# line 1\n', 'getcomments(mod)')
+test(inspect.getmodule(mod.StupidGit) == mod, 'getmodule(mod.StupidGit)')
+test(inspect.getfile(mod.StupidGit) == TESTFN, 'getfile(mod.StupidGit)')
+test(inspect.getsourcefile(mod.spam) == TESTFN, 'getsourcefile(mod.spam)')
+test(inspect.getsourcefile(git.abuse) == TESTFN, 'getsourcefile(git.abuse)')
+
+def sourcerange(top, bottom):
+    lines = string.split(source, '\n')
+    return string.join(lines[top-1:bottom], '\n') + '\n'
+
+test(inspect.getsource(git.abuse) == sourcerange(29, 39),
+     'getsource(git.abuse)')
+test(inspect.getsource(mod.StupidGit) == sourcerange(21, 46),
+     'getsource(mod.StupidGit)')
+test(inspect.getdoc(mod.StupidGit) ==
+     'A longer,\n\nindented\n\ndocstring.', 'getdoc(mod.StupidGit)')
+test(inspect.getdoc(git.abuse) ==
+     'Another\n\ndocstring\n\ncontaining\n\ntabs\n\n', 'getdoc(git.abuse)')
+test(inspect.getcomments(mod.StupidGit) == '# line 20\n',
+     'getcomments(mod.StupidGit)')
+
+args, varargs, varkw, defaults = inspect.getargspec(mod.eggs)
+test(args == ['x', 'y'], 'mod.eggs args')
+test(varargs == None, 'mod.eggs varargs')
+test(varkw == None, 'mod.eggs varkw')
+test(defaults == None, 'mod.eggs defaults')
+test(inspect.formatargspec(args, varargs, varkw, defaults) ==
+     '(x, y)', 'mod.eggs formatted argspec')
+args, varargs, varkw, defaults = inspect.getargspec(mod.spam)
+test(args == ['a', 'b', 'c', 'd', ['e', ['f']]], 'mod.spam args')
+test(varargs == 'g', 'mod.spam varargs')
+test(varkw == 'h', 'mod.spam varkw')
+test(defaults == (3, (4, (5,))), 'mod.spam defaults')
+test(inspect.formatargspec(args, varargs, varkw, defaults) ==
+     '(a, b, c, d=3, (e, (f,))=(4, (5,)), *g, **h)',
+     'mod.spam formatted argspec')
+
+git.abuse(7, 8, 9)
+
+istest(inspect.istraceback, 'git.ex[2]')
+istest(inspect.isframe, 'mod.fr')
+
+test(len(git.tr) == 2, 'trace() length')
+test(git.tr[0][1:] == ('@test', 9, 'spam', ['    eggs(b + d, c + f)\n'], 0),
+     'trace() row 1')
+test(git.tr[1][1:] == ('@test', 18, 'eggs', ['    q = y / 0\n'], 0),
+     'trace() row 2')
+
+test(len(mod.st) >= 5, 'stack() length')
+test(mod.st[0][1:] ==
+     ('@test', 16, 'eggs', ['    st = inspect.stack()\n'], 0),
+     'stack() row 1')
+test(mod.st[1][1:] ==
+     ('@test', 9, 'spam', ['    eggs(b + d, c + f)\n'], 0),
+     'stack() row 2')
+test(mod.st[2][1:] ==
+     ('@test', 43, 'argue', ['            spam(a, b, c)\n'], 0),
+     'stack() row 3')
+test(mod.st[3][1:] ==
+     ('@test', 39, 'abuse', ['        self.argue(a, b, c)\n'], 0),
+     'stack() row 4')
+# row 4 is in test_inspect.py
+
+args, varargs, varkw, locals = inspect.getargvalues(mod.fr)
+test(args == ['x', 'y'], 'mod.fr args')
+test(varargs == None, 'mod.fr varargs')
+test(varkw == None, 'mod.fr varkw')
+test(locals == {'x': 11, 'p': 11, 'y': 14}, 'mod.fr locals')
+test(inspect.formatargvalues(args, varargs, varkw, locals) ==
+     '(x=11, y=14)', 'mod.fr formatted argvalues')
+
+args, varargs, varkw, locals = inspect.getargvalues(mod.fr.f_back)
+test(args == ['a', 'b', 'c', 'd', ['e', ['f']]], 'mod.fr.f_back args')
+test(varargs == 'g', 'mod.fr.f_back varargs')
+test(varkw == 'h', 'mod.fr.f_back varkw')
+test(inspect.formatargvalues(args, varargs, varkw, locals) ==
+     '(a=7, b=8, c=9, d=3, (e=4, (f=5,)), *g=(), **h={})',
+     'mod.fr.f_back formatted argvalues')
+
+os.unlink(TESTFN)