]>
Commit | Line | Data |
---|---|---|
4710c53d | 1 | '''\r |
2 | Test cases for pyclbr.py\r | |
3 | Nick Mathewson\r | |
4 | '''\r | |
5 | from test.test_support import run_unittest, import_module\r | |
6 | import sys\r | |
7 | from types import ClassType, FunctionType, MethodType, BuiltinFunctionType\r | |
8 | import pyclbr\r | |
9 | from unittest import TestCase\r | |
10 | \r | |
11 | StaticMethodType = type(staticmethod(lambda: None))\r | |
12 | ClassMethodType = type(classmethod(lambda c: None))\r | |
13 | \r | |
14 | # Silence Py3k warning\r | |
15 | import_module('commands', deprecated=True)\r | |
16 | \r | |
17 | # This next line triggers an error on old versions of pyclbr.\r | |
18 | from commands import getstatus\r | |
19 | \r | |
20 | # Here we test the python class browser code.\r | |
21 | #\r | |
22 | # The main function in this suite, 'testModule', compares the output\r | |
23 | # of pyclbr with the introspected members of a module. Because pyclbr\r | |
24 | # is imperfect (as designed), testModule is called with a set of\r | |
25 | # members to ignore.\r | |
26 | \r | |
27 | class PyclbrTest(TestCase):\r | |
28 | \r | |
29 | def assertListEq(self, l1, l2, ignore):\r | |
30 | ''' succeed iff {l1} - {ignore} == {l2} - {ignore} '''\r | |
31 | missing = (set(l1) ^ set(l2)) - set(ignore)\r | |
32 | if missing:\r | |
33 | print >>sys.stderr, "l1=%r\nl2=%r\nignore=%r" % (l1, l2, ignore)\r | |
34 | self.fail("%r missing" % missing.pop())\r | |
35 | \r | |
36 | def assertHasattr(self, obj, attr, ignore):\r | |
37 | ''' succeed iff hasattr(obj,attr) or attr in ignore. '''\r | |
38 | if attr in ignore: return\r | |
39 | if not hasattr(obj, attr): print "???", attr\r | |
40 | self.assertTrue(hasattr(obj, attr),\r | |
41 | 'expected hasattr(%r, %r)' % (obj, attr))\r | |
42 | \r | |
43 | \r | |
44 | def assertHaskey(self, obj, key, ignore):\r | |
45 | ''' succeed iff key in obj or key in ignore. '''\r | |
46 | if key in ignore: return\r | |
47 | if key not in obj:\r | |
48 | print >>sys.stderr, "***", key\r | |
49 | self.assertIn(key, obj)\r | |
50 | \r | |
51 | def assertEqualsOrIgnored(self, a, b, ignore):\r | |
52 | ''' succeed iff a == b or a in ignore or b in ignore '''\r | |
53 | if a not in ignore and b not in ignore:\r | |
54 | self.assertEqual(a, b)\r | |
55 | \r | |
56 | def checkModule(self, moduleName, module=None, ignore=()):\r | |
57 | ''' succeed iff pyclbr.readmodule_ex(modulename) corresponds\r | |
58 | to the actual module object, module. Any identifiers in\r | |
59 | ignore are ignored. If no module is provided, the appropriate\r | |
60 | module is loaded with __import__.'''\r | |
61 | \r | |
62 | if module is None:\r | |
63 | # Import it.\r | |
64 | # ('<silly>' is to work around an API silliness in __import__)\r | |
65 | module = __import__(moduleName, globals(), {}, ['<silly>'])\r | |
66 | \r | |
67 | dict = pyclbr.readmodule_ex(moduleName)\r | |
68 | \r | |
69 | def ismethod(oclass, obj, name):\r | |
70 | classdict = oclass.__dict__\r | |
71 | if isinstance(obj, FunctionType):\r | |
72 | if not isinstance(classdict[name], StaticMethodType):\r | |
73 | return False\r | |
74 | else:\r | |
75 | if not isinstance(obj, MethodType):\r | |
76 | return False\r | |
77 | if obj.im_self is not None:\r | |
78 | if (not isinstance(classdict[name], ClassMethodType) or\r | |
79 | obj.im_self is not oclass):\r | |
80 | return False\r | |
81 | else:\r | |
82 | if not isinstance(classdict[name], FunctionType):\r | |
83 | return False\r | |
84 | \r | |
85 | objname = obj.__name__\r | |
86 | if objname.startswith("__") and not objname.endswith("__"):\r | |
87 | objname = "_%s%s" % (obj.im_class.__name__, objname)\r | |
88 | return objname == name\r | |
89 | \r | |
90 | # Make sure the toplevel functions and classes are the same.\r | |
91 | for name, value in dict.items():\r | |
92 | if name in ignore:\r | |
93 | continue\r | |
94 | self.assertHasattr(module, name, ignore)\r | |
95 | py_item = getattr(module, name)\r | |
96 | if isinstance(value, pyclbr.Function):\r | |
97 | self.assertIsInstance(py_item, (FunctionType, BuiltinFunctionType))\r | |
98 | if py_item.__module__ != moduleName:\r | |
99 | continue # skip functions that came from somewhere else\r | |
100 | self.assertEqual(py_item.__module__, value.module)\r | |
101 | else:\r | |
102 | self.assertIsInstance(py_item, (ClassType, type))\r | |
103 | if py_item.__module__ != moduleName:\r | |
104 | continue # skip classes that came from somewhere else\r | |
105 | \r | |
106 | real_bases = [base.__name__ for base in py_item.__bases__]\r | |
107 | pyclbr_bases = [ getattr(base, 'name', base)\r | |
108 | for base in value.super ]\r | |
109 | \r | |
110 | try:\r | |
111 | self.assertListEq(real_bases, pyclbr_bases, ignore)\r | |
112 | except:\r | |
113 | print >>sys.stderr, "class=%s" % py_item\r | |
114 | raise\r | |
115 | \r | |
116 | actualMethods = []\r | |
117 | for m in py_item.__dict__.keys():\r | |
118 | if ismethod(py_item, getattr(py_item, m), m):\r | |
119 | actualMethods.append(m)\r | |
120 | foundMethods = []\r | |
121 | for m in value.methods.keys():\r | |
122 | if m[:2] == '__' and m[-2:] != '__':\r | |
123 | foundMethods.append('_'+name+m)\r | |
124 | else:\r | |
125 | foundMethods.append(m)\r | |
126 | \r | |
127 | try:\r | |
128 | self.assertListEq(foundMethods, actualMethods, ignore)\r | |
129 | self.assertEqual(py_item.__module__, value.module)\r | |
130 | \r | |
131 | self.assertEqualsOrIgnored(py_item.__name__, value.name,\r | |
132 | ignore)\r | |
133 | # can't check file or lineno\r | |
134 | except:\r | |
135 | print >>sys.stderr, "class=%s" % py_item\r | |
136 | raise\r | |
137 | \r | |
138 | # Now check for missing stuff.\r | |
139 | def defined_in(item, module):\r | |
140 | if isinstance(item, ClassType):\r | |
141 | return item.__module__ == module.__name__\r | |
142 | if isinstance(item, FunctionType):\r | |
143 | return item.func_globals is module.__dict__\r | |
144 | return False\r | |
145 | for name in dir(module):\r | |
146 | item = getattr(module, name)\r | |
147 | if isinstance(item, (ClassType, FunctionType)):\r | |
148 | if defined_in(item, module):\r | |
149 | self.assertHaskey(dict, name, ignore)\r | |
150 | \r | |
151 | def test_easy(self):\r | |
152 | self.checkModule('pyclbr')\r | |
153 | self.checkModule('doctest', ignore=("DocTestCase",))\r | |
154 | # Silence Py3k warning\r | |
155 | rfc822 = import_module('rfc822', deprecated=True)\r | |
156 | self.checkModule('rfc822', rfc822)\r | |
157 | self.checkModule('difflib')\r | |
158 | \r | |
159 | def test_decorators(self):\r | |
160 | # XXX: See comment in pyclbr_input.py for a test that would fail\r | |
161 | # if it were not commented out.\r | |
162 | #\r | |
163 | self.checkModule('test.pyclbr_input')\r | |
164 | \r | |
165 | def test_others(self):\r | |
166 | cm = self.checkModule\r | |
167 | \r | |
168 | # These were once about the 10 longest modules\r | |
169 | cm('random', ignore=('Random',)) # from _random import Random as CoreGenerator\r | |
170 | cm('cgi', ignore=('log',)) # set with = in module\r | |
171 | cm('urllib', ignore=('_CFNumberToInt32',\r | |
172 | '_CStringFromCFString',\r | |
173 | '_CFSetup',\r | |
174 | 'getproxies_registry',\r | |
175 | 'proxy_bypass_registry',\r | |
176 | 'proxy_bypass_macosx_sysconf',\r | |
177 | 'open_https',\r | |
178 | 'getproxies_macosx_sysconf',\r | |
179 | 'getproxies_internetconfig',)) # not on all platforms\r | |
180 | cm('pickle')\r | |
181 | cm('aifc', ignore=('openfp',)) # set with = in module\r | |
182 | cm('Cookie')\r | |
183 | cm('sre_parse', ignore=('dump',)) # from sre_constants import *\r | |
184 | cm('pdb')\r | |
185 | cm('pydoc')\r | |
186 | \r | |
187 | # Tests for modules inside packages\r | |
188 | cm('email.parser')\r | |
189 | cm('test.test_pyclbr')\r | |
190 | \r | |
191 | \r | |
192 | def test_main():\r | |
193 | run_unittest(PyclbrTest)\r | |
194 | \r | |
195 | \r | |
196 | if __name__ == "__main__":\r | |
197 | test_main()\r |