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