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