blob: fafe17ce5f1b308996120b55fb89b1395ff69fba [file] [log] [blame]
Fred Drake3a28ca82001-08-13 20:26:19 +00001'''
2 Test cases for pyclbr.py
3 Nick Mathewson
4'''
csabella246ff3b2017-07-03 21:31:25 -04005
Christian Heimes05e8be12008-02-23 18:30:17 +00006import sys
csabella246ff3b2017-07-03 21:31:25 -04007from textwrap import dedent
Guido van Rossum13257902007-06-07 23:15:56 +00008from types import FunctionType, MethodType, BuiltinFunctionType
Fred Drake3a28ca82001-08-13 20:26:19 +00009import pyclbr
Zachary Wareac28b792015-12-04 23:32:23 -060010from unittest import TestCase, main as unittest_main
Brett Cannon50865892019-03-22 15:16:50 -070011from test.test_importlib import util as test_importlib_util
Victor Stinner466e18e2019-07-01 19:01:52 +020012
Fred Drake3a28ca82001-08-13 20:26:19 +000013
Anthony Baxterc2a5a632004-08-02 06:10:11 +000014StaticMethodType = type(staticmethod(lambda: None))
15ClassMethodType = type(classmethod(lambda c: None))
16
Tim Peters04601062001-08-13 22:25:24 +000017# Here we test the python class browser code.
Fred Drake3a28ca82001-08-13 20:26:19 +000018#
19# The main function in this suite, 'testModule', compares the output
20# of pyclbr with the introspected members of a module. Because pyclbr
21# is imperfect (as designed), testModule is called with a set of
22# members to ignore.
23
Guido van Rossum0ed7aa12002-12-02 14:54:20 +000024class PyclbrTest(TestCase):
Fred Drake3a28ca82001-08-13 20:26:19 +000025
26 def assertListEq(self, l1, l2, ignore):
27 ''' succeed iff {l1} - {ignore} == {l2} - {ignore} '''
Raymond Hettingera690a992003-11-16 16:17:49 +000028 missing = (set(l1) ^ set(l2)) - set(ignore)
Raymond Hettinger91bbd9a2003-05-02 09:06:28 +000029 if missing:
Guido van Rossumbe19ed72007-02-09 05:37:30 +000030 print("l1=%r\nl2=%r\nignore=%r" % (l1, l2, ignore), file=sys.stderr)
Raymond Hettinger91bbd9a2003-05-02 09:06:28 +000031 self.fail("%r missing" % missing.pop())
Tim Peters04601062001-08-13 22:25:24 +000032
Fred Drake3a28ca82001-08-13 20:26:19 +000033 def assertHasattr(self, obj, attr, ignore):
34 ''' succeed iff hasattr(obj,attr) or attr in ignore. '''
35 if attr in ignore: return
Guido van Rossumbe19ed72007-02-09 05:37:30 +000036 if not hasattr(obj, attr): print("???", attr)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000037 self.assertTrue(hasattr(obj, attr),
Tim Peters5e5ca562002-07-10 02:37:21 +000038 'expected hasattr(%r, %r)' % (obj, attr))
Fred Drake3a28ca82001-08-13 20:26:19 +000039
40
41 def assertHaskey(self, obj, key, ignore):
Guido van Rossume2b70bc2006-08-18 22:13:04 +000042 ''' succeed iff key in obj or key in ignore. '''
Fred Drake3a28ca82001-08-13 20:26:19 +000043 if key in ignore: return
Guido van Rossume2b70bc2006-08-18 22:13:04 +000044 if key not in obj:
Guido van Rossumbe19ed72007-02-09 05:37:30 +000045 print("***",key, file=sys.stderr)
Benjamin Peterson577473f2010-01-19 00:09:57 +000046 self.assertIn(key, obj)
Fred Drake3a28ca82001-08-13 20:26:19 +000047
Anthony Baxterc2a5a632004-08-02 06:10:11 +000048 def assertEqualsOrIgnored(self, a, b, ignore):
Fred Drake3a28ca82001-08-13 20:26:19 +000049 ''' succeed iff a == b or a in ignore or b in ignore '''
Anthony Baxterc2a5a632004-08-02 06:10:11 +000050 if a not in ignore and b not in ignore:
Ezio Melottib3aedd42010-11-20 19:04:17 +000051 self.assertEqual(a, b)
Fred Drake3a28ca82001-08-13 20:26:19 +000052
53 def checkModule(self, moduleName, module=None, ignore=()):
54 ''' succeed iff pyclbr.readmodule_ex(modulename) corresponds
55 to the actual module object, module. Any identifiers in
56 ignore are ignored. If no module is provided, the appropriate
57 module is loaded with __import__.'''
58
Guido van Rossumd858f702006-04-21 09:17:15 +000059 ignore = set(ignore) | set(['object'])
60
Benjamin Peterson2a691a82008-03-31 01:51:45 +000061 if module is None:
Guido van Rossum0ed7aa12002-12-02 14:54:20 +000062 # Import it.
63 # ('<silly>' is to work around an API silliness in __import__)
64 module = __import__(moduleName, globals(), {}, ['<silly>'])
Fred Drake3a28ca82001-08-13 20:26:19 +000065
66 dict = pyclbr.readmodule_ex(moduleName)
67
Anthony Baxterc2a5a632004-08-02 06:10:11 +000068 def ismethod(oclass, obj, name):
69 classdict = oclass.__dict__
Christian Heimes4a22b5d2007-11-25 09:39:14 +000070 if isinstance(obj, MethodType):
71 # could be a classmethod
72 if (not isinstance(classdict[name], ClassMethodType) or
Christian Heimesff737952007-11-27 10:40:20 +000073 obj.__self__ is not oclass):
Anthony Baxterc2a5a632004-08-02 06:10:11 +000074 return False
Christian Heimes4a22b5d2007-11-25 09:39:14 +000075 elif not isinstance(obj, FunctionType):
76 return False
Anthony Baxterc2a5a632004-08-02 06:10:11 +000077
Guido van Rossum7f6a4392002-12-03 08:16:50 +000078 objname = obj.__name__
79 if objname.startswith("__") and not objname.endswith("__"):
Christian Heimes4a22b5d2007-11-25 09:39:14 +000080 objname = "_%s%s" % (oclass.__name__, objname)
Guido van Rossum7f6a4392002-12-03 08:16:50 +000081 return objname == name
82
Fred Drake3a28ca82001-08-13 20:26:19 +000083 # Make sure the toplevel functions and classes are the same.
84 for name, value in dict.items():
Tim Peters04601062001-08-13 22:25:24 +000085 if name in ignore:
Fred Drake3a28ca82001-08-13 20:26:19 +000086 continue
87 self.assertHasattr(module, name, ignore)
88 py_item = getattr(module, name)
89 if isinstance(value, pyclbr.Function):
Ezio Melottie9615932010-01-24 19:26:24 +000090 self.assertIsInstance(py_item, (FunctionType, BuiltinFunctionType))
Thomas Wouters89f507f2006-12-13 04:49:30 +000091 if py_item.__module__ != moduleName:
92 continue # skip functions that came from somewhere else
Ezio Melottib3aedd42010-11-20 19:04:17 +000093 self.assertEqual(py_item.__module__, value.module)
Fred Drake3a28ca82001-08-13 20:26:19 +000094 else:
Ezio Melottie9615932010-01-24 19:26:24 +000095 self.assertIsInstance(py_item, type)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000096 if py_item.__module__ != moduleName:
97 continue # skip classes that came from somewhere else
98
Fred Drake3a28ca82001-08-13 20:26:19 +000099 real_bases = [base.__name__ for base in py_item.__bases__]
Tim Peters04601062001-08-13 22:25:24 +0000100 pyclbr_bases = [ getattr(base, 'name', base)
Fred Drake3a28ca82001-08-13 20:26:19 +0000101 for base in value.super ]
Tim Peters04601062001-08-13 22:25:24 +0000102
Guido van Rossum0ed7aa12002-12-02 14:54:20 +0000103 try:
104 self.assertListEq(real_bases, pyclbr_bases, ignore)
105 except:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000106 print("class=%s" % py_item, file=sys.stderr)
Guido van Rossum0ed7aa12002-12-02 14:54:20 +0000107 raise
Fred Drake3a28ca82001-08-13 20:26:19 +0000108
109 actualMethods = []
Tim Peters37a309d2001-09-04 01:20:04 +0000110 for m in py_item.__dict__.keys():
Anthony Baxterc2a5a632004-08-02 06:10:11 +0000111 if ismethod(py_item, getattr(py_item, m), m):
Fred Drake3a28ca82001-08-13 20:26:19 +0000112 actualMethods.append(m)
113 foundMethods = []
114 for m in value.methods.keys():
115 if m[:2] == '__' and m[-2:] != '__':
116 foundMethods.append('_'+name+m)
117 else:
118 foundMethods.append(m)
119
Guido van Rossum7f6a4392002-12-03 08:16:50 +0000120 try:
121 self.assertListEq(foundMethods, actualMethods, ignore)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000122 self.assertEqual(py_item.__module__, value.module)
Fred Drake3a28ca82001-08-13 20:26:19 +0000123
Anthony Baxterc2a5a632004-08-02 06:10:11 +0000124 self.assertEqualsOrIgnored(py_item.__name__, value.name,
125 ignore)
Guido van Rossum7f6a4392002-12-03 08:16:50 +0000126 # can't check file or lineno
127 except:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000128 print("class=%s" % py_item, file=sys.stderr)
Guido van Rossum7f6a4392002-12-03 08:16:50 +0000129 raise
Fred Drake3a28ca82001-08-13 20:26:19 +0000130
131 # Now check for missing stuff.
Guido van Rossum0ed7aa12002-12-02 14:54:20 +0000132 def defined_in(item, module):
Guido van Rossum13257902007-06-07 23:15:56 +0000133 if isinstance(item, type):
Guido van Rossum0ed7aa12002-12-02 14:54:20 +0000134 return item.__module__ == module.__name__
135 if isinstance(item, FunctionType):
Neal Norwitz221085d2007-02-25 20:55:47 +0000136 return item.__globals__ is module.__dict__
Guido van Rossum0ed7aa12002-12-02 14:54:20 +0000137 return False
Fred Drake3a28ca82001-08-13 20:26:19 +0000138 for name in dir(module):
139 item = getattr(module, name)
Guido van Rossum13257902007-06-07 23:15:56 +0000140 if isinstance(item, (type, FunctionType)):
Guido van Rossum0ed7aa12002-12-02 14:54:20 +0000141 if defined_in(item, module):
142 self.assertHaskey(dict, name, ignore)
Fred Drake3a28ca82001-08-13 20:26:19 +0000143
144 def test_easy(self):
145 self.checkModule('pyclbr')
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300146 # XXX: Metaclasses are not supported
147 # self.checkModule('ast')
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000148 self.checkModule('doctest', ignore=("TestResults", "_SpoofOut",
Andrew Svetlov7c1017b2013-08-29 01:24:39 +0300149 "DocTestCase", '_DocTestSuite'))
Christian Heimes25bb7832008-01-11 16:17:00 +0000150 self.checkModule('difflib', ignore=("Match",))
Fred Drake3a28ca82001-08-13 20:26:19 +0000151
Anthony Baxterc2a5a632004-08-02 06:10:11 +0000152 def test_decorators(self):
153 # XXX: See comment in pyclbr_input.py for a test that would fail
154 # if it were not commented out.
155 #
Christian Heimes4a22b5d2007-11-25 09:39:14 +0000156 self.checkModule('test.pyclbr_input', ignore=['om'])
Tim Peters6db15d72004-08-04 02:36:18 +0000157
csabella246ff3b2017-07-03 21:31:25 -0400158 def test_nested(self):
159 mb = pyclbr
160 # Set arguments for descriptor creation and _creat_tree call.
161 m, p, f, t, i = 'test', '', 'test.py', {}, None
162 source = dedent("""\
163 def f0:
164 def f1(a,b,c):
165 def f2(a=1, b=2, c=3): pass
166 return f1(a,b,d)
167 class c1: pass
168 class C0:
169 "Test class."
170 def F1():
171 "Method."
172 return 'return'
173 class C1():
174 class C2:
175 "Class nested within nested class."
176 def F3(): return 1+1
177
178 """)
179 actual = mb._create_tree(m, p, f, source, t, i)
180
181 # Create descriptors, linked together, and expected dict.
182 f0 = mb.Function(m, 'f0', f, 1)
183 f1 = mb._nest_function(f0, 'f1', 2)
184 f2 = mb._nest_function(f1, 'f2', 3)
185 c1 = mb._nest_class(f0, 'c1', 5)
186 C0 = mb.Class(m, 'C0', None, f, 6)
187 F1 = mb._nest_function(C0, 'F1', 8)
188 C1 = mb._nest_class(C0, 'C1', 11)
189 C2 = mb._nest_class(C1, 'C2', 12)
190 F3 = mb._nest_function(C2, 'F3', 14)
191 expected = {'f0':f0, 'C0':C0}
192
193 def compare(parent1, children1, parent2, children2):
194 """Return equality of tree pairs.
195
196 Each parent,children pair define a tree. The parents are
197 assumed equal. Comparing the children dictionaries as such
198 does not work due to comparison by identity and double
199 linkage. We separate comparing string and number attributes
200 from comparing the children of input children.
201 """
202 self.assertEqual(children1.keys(), children2.keys())
203 for ob in children1.values():
204 self.assertIs(ob.parent, parent1)
205 for ob in children2.values():
206 self.assertIs(ob.parent, parent2)
207 for key in children1.keys():
208 o1, o2 = children1[key], children2[key]
209 t1 = type(o1), o1.name, o1.file, o1.module, o1.lineno
210 t2 = type(o2), o2.name, o2.file, o2.module, o2.lineno
211 self.assertEqual(t1, t2)
212 if type(o1) is mb.Class:
213 self.assertEqual(o1.methods, o2.methods)
214 # Skip superclasses for now as not part of example
215 compare(o1, o1.children, o2, o2.children)
216
217 compare(None, actual, None, expected)
218
Fred Drake3a28ca82001-08-13 20:26:19 +0000219 def test_others(self):
220 cm = self.checkModule
221
Guido van Rossum7f6a4392002-12-03 08:16:50 +0000222 # These were once about the 10 longest modules
Raymond Hettingere401b6f2002-12-30 07:21:32 +0000223 cm('random', ignore=('Random',)) # from _random import Random as CoreGenerator
Guido van Rossum7f6a4392002-12-03 08:16:50 +0000224 cm('cgi', ignore=('log',)) # set with = in module
Antoine Pitrou91f43802019-05-26 17:10:09 +0200225 cm('pickle', ignore=('partial', 'PickleBuffer'))
Brian Curtin9f914a02017-11-10 11:38:25 -0500226 # TODO(briancurtin): openfp is deprecated as of 3.7.
227 # Update this once it has been removed.
R David Murray4d35e752013-07-25 16:12:01 -0400228 cm('aifc', ignore=('openfp', '_aifc_params')) # set with = in module
Serhiy Storchakabd48d272016-09-11 12:50:02 +0300229 cm('sre_parse', ignore=('dump', 'groups', 'pos')) # from sre_constants import *; property
Guido van Rossum7f6a4392002-12-03 08:16:50 +0000230 cm('pdb')
Serhiy Storchakabdf6b912017-03-19 08:40:32 +0200231 cm('pydoc', ignore=('input', 'output',)) # properties
Fred Drake3a28ca82001-08-13 20:26:19 +0000232
Guido van Rossum0ed7aa12002-12-02 14:54:20 +0000233 # Tests for modules inside packages
Neal Norwitz315d8452007-08-30 03:06:59 +0000234 cm('email.parser')
Guido van Rossum7f6a4392002-12-03 08:16:50 +0000235 cm('test.test_pyclbr')
Fred Drake3a28ca82001-08-13 20:26:19 +0000236
Brett Cannon50865892019-03-22 15:16:50 -0700237
238class ReadmoduleTests(TestCase):
239
240 def setUp(self):
241 self._modules = pyclbr._modules.copy()
242
243 def tearDown(self):
244 pyclbr._modules = self._modules
245
246
247 def test_dotted_name_not_a_package(self):
Petri Lehtinen8d886042012-05-18 21:51:11 +0300248 # test ImportError is raised when the first part of a dotted name is
Brett Cannon50865892019-03-22 15:16:50 -0700249 # not a package.
250 #
251 # Issue #14798.
Petri Lehtinen8d886042012-05-18 21:51:11 +0300252 self.assertRaises(ImportError, pyclbr.readmodule_ex, 'asyncore.foo')
253
Brett Cannon50865892019-03-22 15:16:50 -0700254 def test_module_has_no_spec(self):
255 module_name = "doesnotexist"
256 assert module_name not in pyclbr._modules
257 with test_importlib_util.uncache(module_name):
258 with self.assertRaises(ModuleNotFoundError):
259 pyclbr.readmodule_ex(module_name)
260
Fred Drake2e2be372001-09-20 21:33:42 +0000261
Fred Drake2e2be372001-09-20 21:33:42 +0000262if __name__ == "__main__":
Zachary Wareac28b792015-12-04 23:32:23 -0600263 unittest_main()