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