Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | source = '''# line 1 |
2 | 'A module docstring.' | |
3 | ||
4 | import sys, inspect | |
5 | # line 5 | |
6 | ||
7 | # line 7 | |
8 | def spam(a, b, c, d=3, (e, (f,))=(4, (5,)), *g, **h): | |
9 | eggs(b + d, c + f) | |
10 | ||
11 | # line 11 | |
12 | def eggs(x, y): | |
13 | "A docstring." | |
14 | global fr, st | |
15 | fr = inspect.currentframe() | |
16 | st = inspect.stack() | |
17 | p = x | |
18 | q = y / 0 | |
19 | ||
20 | # line 20 | |
21 | class StupidGit: | |
22 | """A longer, | |
23 | ||
24 | indented | |
25 | ||
26 | docstring.""" | |
27 | # line 27 | |
28 | ||
29 | def abuse(self, a, b, c): # a comment | |
30 | """Another | |
31 | ||
32 | \tdocstring | |
33 | ||
34 | containing | |
35 | ||
36 | \ttabs | |
37 | \t | |
38 | """ | |
39 | self.argue(a, b, c) | |
40 | # line 40 | |
41 | def argue(self, a, b, c): | |
42 | try: | |
43 | spam(a, b, c) | |
44 | except: | |
45 | self.ex = sys.exc_info() | |
46 | self.tr = inspect.trace() | |
47 | ||
48 | # line 48 | |
49 | class MalodorousPervert(StupidGit): | |
50 | pass | |
51 | ||
52 | class ParrotDroppings: | |
53 | pass | |
54 | ||
55 | class FesteringGob(MalodorousPervert, ParrotDroppings): | |
56 | pass | |
57 | ''' | |
58 | ||
59 | # Functions tested in this suite: | |
60 | # ismodule, isclass, ismethod, isfunction, istraceback, isframe, iscode, | |
61 | # isbuiltin, isroutine, getmembers, getdoc, getfile, getmodule, | |
62 | # getsourcefile, getcomments, getsource, getclasstree, getargspec, | |
63 | # getargvalues, formatargspec, formatargvalues, currentframe, stack, trace | |
64 | # isdatadescriptor | |
65 | ||
66 | from test.test_support import TestFailed, TESTFN | |
67 | import sys, imp, os, string | |
68 | ||
69 | def test(assertion, message, *args): | |
70 | if not assertion: | |
71 | raise TestFailed, message % args | |
72 | ||
73 | import inspect | |
74 | ||
75 | file = open(TESTFN, 'w') | |
76 | file.write(source) | |
77 | file.close() | |
78 | ||
79 | # Note that load_source creates file TESTFN+'c' or TESTFN+'o'. | |
80 | mod = imp.load_source('testmod', TESTFN) | |
81 | files_to_clean_up = [TESTFN, TESTFN + 'c', TESTFN + 'o'] | |
82 | ||
83 | def istest(func, exp): | |
84 | obj = eval(exp) | |
85 | test(func(obj), '%s(%s)' % (func.__name__, exp)) | |
86 | for other in [inspect.isbuiltin, inspect.isclass, inspect.iscode, | |
87 | inspect.isframe, inspect.isfunction, inspect.ismethod, | |
88 | inspect.ismodule, inspect.istraceback]: | |
89 | if other is not func: | |
90 | test(not other(obj), 'not %s(%s)' % (other.__name__, exp)) | |
91 | ||
92 | git = mod.StupidGit() | |
93 | try: | |
94 | 1/0 | |
95 | except: | |
96 | tb = sys.exc_traceback | |
97 | ||
98 | istest(inspect.isbuiltin, 'sys.exit') | |
99 | istest(inspect.isbuiltin, '[].append') | |
100 | istest(inspect.isclass, 'mod.StupidGit') | |
101 | istest(inspect.iscode, 'mod.spam.func_code') | |
102 | istest(inspect.isframe, 'tb.tb_frame') | |
103 | istest(inspect.isfunction, 'mod.spam') | |
104 | istest(inspect.ismethod, 'mod.StupidGit.abuse') | |
105 | istest(inspect.ismethod, 'git.argue') | |
106 | istest(inspect.ismodule, 'mod') | |
107 | istest(inspect.istraceback, 'tb') | |
108 | import __builtin__ | |
109 | istest(inspect.isdatadescriptor, '__builtin__.file.closed') | |
110 | istest(inspect.isdatadescriptor, '__builtin__.file.softspace') | |
111 | test(inspect.isroutine(mod.spam), 'isroutine(mod.spam)') | |
112 | test(inspect.isroutine([].count), 'isroutine([].count)') | |
113 | ||
114 | classes = inspect.getmembers(mod, inspect.isclass) | |
115 | test(classes == | |
116 | [('FesteringGob', mod.FesteringGob), | |
117 | ('MalodorousPervert', mod.MalodorousPervert), | |
118 | ('ParrotDroppings', mod.ParrotDroppings), | |
119 | ('StupidGit', mod.StupidGit)], 'class list') | |
120 | tree = inspect.getclasstree(map(lambda x: x[1], classes), 1) | |
121 | test(tree == | |
122 | [(mod.ParrotDroppings, ()), | |
123 | (mod.StupidGit, ()), | |
124 | [(mod.MalodorousPervert, (mod.StupidGit,)), | |
125 | [(mod.FesteringGob, (mod.MalodorousPervert, mod.ParrotDroppings)) | |
126 | ] | |
127 | ] | |
128 | ], 'class tree') | |
129 | ||
130 | functions = inspect.getmembers(mod, inspect.isfunction) | |
131 | test(functions == [('eggs', mod.eggs), ('spam', mod.spam)], 'function list') | |
132 | ||
133 | test(inspect.getdoc(mod) == 'A module docstring.', 'getdoc(mod)') | |
134 | test(inspect.getcomments(mod) == '# line 1\n', 'getcomments(mod)') | |
135 | test(inspect.getmodule(mod.StupidGit) == mod, 'getmodule(mod.StupidGit)') | |
136 | test(inspect.getfile(mod.StupidGit) == TESTFN, 'getfile(mod.StupidGit)') | |
137 | test(inspect.getsourcefile(mod.spam) == TESTFN, 'getsourcefile(mod.spam)') | |
138 | test(inspect.getsourcefile(git.abuse) == TESTFN, 'getsourcefile(git.abuse)') | |
139 | ||
140 | def sourcerange(top, bottom): | |
141 | lines = string.split(source, '\n') | |
142 | return string.join(lines[top-1:bottom], '\n') + '\n' | |
143 | ||
144 | test(inspect.getsource(git.abuse) == sourcerange(29, 39), | |
145 | 'getsource(git.abuse)') | |
146 | test(inspect.getsource(mod.StupidGit) == sourcerange(21, 46), | |
147 | 'getsource(mod.StupidGit)') | |
148 | test(inspect.getdoc(mod.StupidGit) == | |
149 | 'A longer,\n\nindented\n\ndocstring.', 'getdoc(mod.StupidGit)') | |
150 | test(inspect.getdoc(git.abuse) == | |
151 | 'Another\n\ndocstring\n\ncontaining\n\ntabs', 'getdoc(git.abuse)') | |
152 | test(inspect.getcomments(mod.StupidGit) == '# line 20\n', | |
153 | 'getcomments(mod.StupidGit)') | |
154 | ||
155 | git.abuse(7, 8, 9) | |
156 | ||
157 | istest(inspect.istraceback, 'git.ex[2]') | |
158 | istest(inspect.isframe, 'mod.fr') | |
159 | ||
160 | test(len(git.tr) == 3, 'trace() length') | |
161 | test(git.tr[0][1:] == (TESTFN, 43, 'argue', | |
162 | [' spam(a, b, c)\n'], 0), | |
163 | 'trace() row 2') | |
164 | test(git.tr[1][1:] == (TESTFN, 9, 'spam', [' eggs(b + d, c + f)\n'], 0), | |
165 | 'trace() row 2') | |
166 | test(git.tr[2][1:] == (TESTFN, 18, 'eggs', [' q = y / 0\n'], 0), | |
167 | 'trace() row 3') | |
168 | ||
169 | test(len(mod.st) >= 5, 'stack() length') | |
170 | test(mod.st[0][1:] == | |
171 | (TESTFN, 16, 'eggs', [' st = inspect.stack()\n'], 0), | |
172 | 'stack() row 1') | |
173 | test(mod.st[1][1:] == | |
174 | (TESTFN, 9, 'spam', [' eggs(b + d, c + f)\n'], 0), | |
175 | 'stack() row 2') | |
176 | test(mod.st[2][1:] == | |
177 | (TESTFN, 43, 'argue', [' spam(a, b, c)\n'], 0), | |
178 | 'stack() row 3') | |
179 | test(mod.st[3][1:] == | |
180 | (TESTFN, 39, 'abuse', [' self.argue(a, b, c)\n'], 0), | |
181 | 'stack() row 4') | |
182 | ||
183 | args, varargs, varkw, locals = inspect.getargvalues(mod.fr) | |
184 | test(args == ['x', 'y'], 'mod.fr args') | |
185 | test(varargs == None, 'mod.fr varargs') | |
186 | test(varkw == None, 'mod.fr varkw') | |
187 | test(locals == {'x': 11, 'p': 11, 'y': 14}, 'mod.fr locals') | |
188 | test(inspect.formatargvalues(args, varargs, varkw, locals) == | |
189 | '(x=11, y=14)', 'mod.fr formatted argvalues') | |
190 | ||
191 | args, varargs, varkw, locals = inspect.getargvalues(mod.fr.f_back) | |
192 | test(args == ['a', 'b', 'c', 'd', ['e', ['f']]], 'mod.fr.f_back args') | |
193 | test(varargs == 'g', 'mod.fr.f_back varargs') | |
194 | test(varkw == 'h', 'mod.fr.f_back varkw') | |
195 | test(inspect.formatargvalues(args, varargs, varkw, locals) == | |
196 | '(a=7, b=8, c=9, d=3, (e=4, (f=5,)), *g=(), **h={})', | |
197 | 'mod.fr.f_back formatted argvalues') | |
198 | ||
199 | for fname in files_to_clean_up: | |
200 | try: | |
201 | os.unlink(fname) | |
202 | except: | |
203 | pass | |
204 | ||
205 | # Test for decorators as well. | |
206 | ||
207 | source = r""" | |
208 | def wrap(foo=None): | |
209 | def wrapper(func): | |
210 | return func | |
211 | return wrapper | |
212 | ||
213 | def replace(func): | |
214 | def insteadfunc(): | |
215 | print 'hello' | |
216 | return insteadfunc | |
217 | ||
218 | # two decorators, one with argument | |
219 | @wrap() | |
220 | @wrap(wrap) | |
221 | def wrapped(): | |
222 | pass | |
223 | ||
224 | @replace | |
225 | def gone(): | |
226 | pass""" | |
227 | ||
228 | file = open(TESTFN + "2", "w") | |
229 | file.write(source) | |
230 | file.close() | |
231 | files_to_clean_up = [TESTFN + "2", TESTFN + '2c', TESTFN + '2o'] | |
232 | ||
233 | mod2 = imp.load_source("testmod3", TESTFN + "2") | |
234 | ||
235 | test(inspect.getsource(mod2.wrapped) == sourcerange(13, 16), | |
236 | "inspect.getsource(mod.wrapped)") | |
237 | test(inspect.getsource(mod2.gone) == sourcerange(8, 9), | |
238 | "inspect.getsource(mod.gone)") | |
239 | ||
240 | for fname in files_to_clean_up: | |
241 | try: | |
242 | os.unlink(fname) | |
243 | except: | |
244 | pass | |
245 | ||
246 | # Test classic-class method resolution order. | |
247 | class A: pass | |
248 | class B(A): pass | |
249 | class C(A): pass | |
250 | class D(B, C): pass | |
251 | ||
252 | expected = (D, B, A, C) | |
253 | got = inspect.getmro(D) | |
254 | test(expected == got, "expected %r mro, got %r", expected, got) | |
255 | ||
256 | # The same w/ new-class MRO. | |
257 | class A(object): pass | |
258 | class B(A): pass | |
259 | class C(A): pass | |
260 | class D(B, C): pass | |
261 | ||
262 | expected = (D, B, C, A, object) | |
263 | got = inspect.getmro(D) | |
264 | test(expected == got, "expected %r mro, got %r", expected, got) | |
265 | ||
266 | # Test classify_class_attrs. | |
267 | def attrs_wo_objs(cls): | |
268 | return [t[:3] for t in inspect.classify_class_attrs(cls)] | |
269 | ||
270 | class A: | |
271 | def s(): pass | |
272 | s = staticmethod(s) | |
273 | ||
274 | def c(cls): pass | |
275 | c = classmethod(c) | |
276 | ||
277 | def getp(self): pass | |
278 | p = property(getp) | |
279 | ||
280 | def m(self): pass | |
281 | ||
282 | def m1(self): pass | |
283 | ||
284 | datablob = '1' | |
285 | ||
286 | attrs = attrs_wo_objs(A) | |
287 | test(('s', 'static method', A) in attrs, 'missing static method') | |
288 | test(('c', 'class method', A) in attrs, 'missing class method') | |
289 | test(('p', 'property', A) in attrs, 'missing property') | |
290 | test(('m', 'method', A) in attrs, 'missing plain method') | |
291 | test(('m1', 'method', A) in attrs, 'missing plain method') | |
292 | test(('datablob', 'data', A) in attrs, 'missing data') | |
293 | ||
294 | class B(A): | |
295 | def m(self): pass | |
296 | ||
297 | attrs = attrs_wo_objs(B) | |
298 | test(('s', 'static method', A) in attrs, 'missing static method') | |
299 | test(('c', 'class method', A) in attrs, 'missing class method') | |
300 | test(('p', 'property', A) in attrs, 'missing property') | |
301 | test(('m', 'method', B) in attrs, 'missing plain method') | |
302 | test(('m1', 'method', A) in attrs, 'missing plain method') | |
303 | test(('datablob', 'data', A) in attrs, 'missing data') | |
304 | ||
305 | ||
306 | class C(A): | |
307 | def m(self): pass | |
308 | def c(self): pass | |
309 | ||
310 | attrs = attrs_wo_objs(C) | |
311 | test(('s', 'static method', A) in attrs, 'missing static method') | |
312 | test(('c', 'method', C) in attrs, 'missing plain method') | |
313 | test(('p', 'property', A) in attrs, 'missing property') | |
314 | test(('m', 'method', C) in attrs, 'missing plain method') | |
315 | test(('m1', 'method', A) in attrs, 'missing plain method') | |
316 | test(('datablob', 'data', A) in attrs, 'missing data') | |
317 | ||
318 | class D(B, C): | |
319 | def m1(self): pass | |
320 | ||
321 | attrs = attrs_wo_objs(D) | |
322 | test(('s', 'static method', A) in attrs, 'missing static method') | |
323 | test(('c', 'class method', A) in attrs, 'missing class method') | |
324 | test(('p', 'property', A) in attrs, 'missing property') | |
325 | test(('m', 'method', B) in attrs, 'missing plain method') | |
326 | test(('m1', 'method', D) in attrs, 'missing plain method') | |
327 | test(('datablob', 'data', A) in attrs, 'missing data') | |
328 | ||
329 | # Repeat all that, but w/ new-style classes. | |
330 | ||
331 | class A(object): | |
332 | ||
333 | def s(): pass | |
334 | s = staticmethod(s) | |
335 | ||
336 | def c(cls): pass | |
337 | c = classmethod(c) | |
338 | ||
339 | def getp(self): pass | |
340 | p = property(getp) | |
341 | ||
342 | def m(self): pass | |
343 | ||
344 | def m1(self): pass | |
345 | ||
346 | datablob = '1' | |
347 | ||
348 | attrs = attrs_wo_objs(A) | |
349 | test(('s', 'static method', A) in attrs, 'missing static method') | |
350 | test(('c', 'class method', A) in attrs, 'missing class method') | |
351 | test(('p', 'property', A) in attrs, 'missing property') | |
352 | test(('m', 'method', A) in attrs, 'missing plain method') | |
353 | test(('m1', 'method', A) in attrs, 'missing plain method') | |
354 | test(('datablob', 'data', A) in attrs, 'missing data') | |
355 | ||
356 | class B(A): | |
357 | ||
358 | def m(self): pass | |
359 | ||
360 | attrs = attrs_wo_objs(B) | |
361 | test(('s', 'static method', A) in attrs, 'missing static method') | |
362 | test(('c', 'class method', A) in attrs, 'missing class method') | |
363 | test(('p', 'property', A) in attrs, 'missing property') | |
364 | test(('m', 'method', B) in attrs, 'missing plain method') | |
365 | test(('m1', 'method', A) in attrs, 'missing plain method') | |
366 | test(('datablob', 'data', A) in attrs, 'missing data') | |
367 | ||
368 | ||
369 | class C(A): | |
370 | ||
371 | def m(self): pass | |
372 | def c(self): pass | |
373 | ||
374 | attrs = attrs_wo_objs(C) | |
375 | test(('s', 'static method', A) in attrs, 'missing static method') | |
376 | test(('c', 'method', C) in attrs, 'missing plain method') | |
377 | test(('p', 'property', A) in attrs, 'missing property') | |
378 | test(('m', 'method', C) in attrs, 'missing plain method') | |
379 | test(('m1', 'method', A) in attrs, 'missing plain method') | |
380 | test(('datablob', 'data', A) in attrs, 'missing data') | |
381 | ||
382 | class D(B, C): | |
383 | ||
384 | def m1(self): pass | |
385 | ||
386 | attrs = attrs_wo_objs(D) | |
387 | test(('s', 'static method', A) in attrs, 'missing static method') | |
388 | test(('c', 'method', C) in attrs, 'missing plain method') | |
389 | test(('p', 'property', A) in attrs, 'missing property') | |
390 | test(('m', 'method', B) in attrs, 'missing plain method') | |
391 | test(('m1', 'method', D) in attrs, 'missing plain method') | |
392 | test(('datablob', 'data', A) in attrs, 'missing data') | |
393 | ||
394 | args, varargs, varkw, defaults = inspect.getargspec(mod.eggs) | |
395 | test(args == ['x', 'y'], 'mod.eggs args') | |
396 | test(varargs == None, 'mod.eggs varargs') | |
397 | test(varkw == None, 'mod.eggs varkw') | |
398 | test(defaults == None, 'mod.eggs defaults') | |
399 | test(inspect.formatargspec(args, varargs, varkw, defaults) == | |
400 | '(x, y)', 'mod.eggs formatted argspec') | |
401 | args, varargs, varkw, defaults = inspect.getargspec(mod.spam) | |
402 | test(args == ['a', 'b', 'c', 'd', ['e', ['f']]], 'mod.spam args') | |
403 | test(varargs == 'g', 'mod.spam varargs') | |
404 | test(varkw == 'h', 'mod.spam varkw') | |
405 | test(defaults == (3, (4, (5,))), 'mod.spam defaults') | |
406 | test(inspect.formatargspec(args, varargs, varkw, defaults) == | |
407 | '(a, b, c, d=3, (e, (f,))=(4, (5,)), *g, **h)', | |
408 | 'mod.spam formatted argspec') | |
409 | args, varargs, varkw, defaults = inspect.getargspec(A.m) | |
410 | test(args == ['self'], 'A.m args') | |
411 | test(varargs is None, 'A.m varargs') | |
412 | test(varkw is None, 'A.m varkw') | |
413 | test(defaults is None, 'A.m defaults') | |
414 | ||
415 | # Doc/lib/libinspect.tex claims there are 11 such functions | |
416 | count = len(filter(lambda x:x.startswith('is'), dir(inspect))) | |
417 | test(count == 11, "There are %d (not 11) is* functions", count) | |
418 | ||
419 | def sublistOfOne((foo)): return 1 | |
420 | ||
421 | args, varargs, varkw, defaults = inspect.getargspec(sublistOfOne) | |
422 | test(args == [['foo']], 'sublistOfOne args') | |
423 | test(varargs is None, 'sublistOfOne varargs') | |
424 | test(varkw is None, 'sublistOfOne varkw') | |
425 | test(defaults is None, 'sublistOfOn defaults') |