blob: 839c58f0fde5be1424bd47c6e0ed836ec91f9fe5 [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
6import os
Christian Heimes05e8be12008-02-23 18:30:17 +00007import sys
csabella246ff3b2017-07-03 21:31:25 -04008from textwrap import dedent
Guido van Rossum13257902007-06-07 23:15:56 +00009from types import FunctionType, MethodType, BuiltinFunctionType
Fred Drake3a28ca82001-08-13 20:26:19 +000010import pyclbr
Zachary Wareac28b792015-12-04 23:32:23 -060011from unittest import TestCase, main as unittest_main
csabella246ff3b2017-07-03 21:31:25 -040012from test import support
Brett Cannon50865892019-03-22 15:16:50 -070013from test.test_importlib import util as test_importlib_util
csabella246ff3b2017-07-03 21:31:25 -040014from functools import partial
Fred Drake3a28ca82001-08-13 20:26:19 +000015
Anthony Baxterc2a5a632004-08-02 06:10:11 +000016StaticMethodType = type(staticmethod(lambda: None))
17ClassMethodType = type(classmethod(lambda c: None))
18
Tim Peters04601062001-08-13 22:25:24 +000019# Here we test the python class browser code.
Fred Drake3a28ca82001-08-13 20:26:19 +000020#
21# The main function in this suite, 'testModule', compares the output
22# of pyclbr with the introspected members of a module. Because pyclbr
23# is imperfect (as designed), testModule is called with a set of
24# members to ignore.
25
Guido van Rossum0ed7aa12002-12-02 14:54:20 +000026class PyclbrTest(TestCase):
Fred Drake3a28ca82001-08-13 20:26:19 +000027
28 def assertListEq(self, l1, l2, ignore):
29 ''' succeed iff {l1} - {ignore} == {l2} - {ignore} '''
Raymond Hettingera690a992003-11-16 16:17:49 +000030 missing = (set(l1) ^ set(l2)) - set(ignore)
Raymond Hettinger91bbd9a2003-05-02 09:06:28 +000031 if missing:
Guido van Rossumbe19ed72007-02-09 05:37:30 +000032 print("l1=%r\nl2=%r\nignore=%r" % (l1, l2, ignore), file=sys.stderr)
Raymond Hettinger91bbd9a2003-05-02 09:06:28 +000033 self.fail("%r missing" % missing.pop())
Tim Peters04601062001-08-13 22:25:24 +000034
Fred Drake3a28ca82001-08-13 20:26:19 +000035 def assertHasattr(self, obj, attr, ignore):
36 ''' succeed iff hasattr(obj,attr) or attr in ignore. '''
37 if attr in ignore: return
Guido van Rossumbe19ed72007-02-09 05:37:30 +000038 if not hasattr(obj, attr): print("???", attr)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000039 self.assertTrue(hasattr(obj, attr),
Tim Peters5e5ca562002-07-10 02:37:21 +000040 'expected hasattr(%r, %r)' % (obj, attr))
Fred Drake3a28ca82001-08-13 20:26:19 +000041
42
43 def assertHaskey(self, obj, key, ignore):
Guido van Rossume2b70bc2006-08-18 22:13:04 +000044 ''' succeed iff key in obj or key in ignore. '''
Fred Drake3a28ca82001-08-13 20:26:19 +000045 if key in ignore: return
Guido van Rossume2b70bc2006-08-18 22:13:04 +000046 if key not in obj:
Guido van Rossumbe19ed72007-02-09 05:37:30 +000047 print("***",key, file=sys.stderr)
Benjamin Peterson577473f2010-01-19 00:09:57 +000048 self.assertIn(key, obj)
Fred Drake3a28ca82001-08-13 20:26:19 +000049
Anthony Baxterc2a5a632004-08-02 06:10:11 +000050 def assertEqualsOrIgnored(self, a, b, ignore):
Fred Drake3a28ca82001-08-13 20:26:19 +000051 ''' succeed iff a == b or a in ignore or b in ignore '''
Anthony Baxterc2a5a632004-08-02 06:10:11 +000052 if a not in ignore and b not in ignore:
Ezio Melottib3aedd42010-11-20 19:04:17 +000053 self.assertEqual(a, b)
Fred Drake3a28ca82001-08-13 20:26:19 +000054
55 def checkModule(self, moduleName, module=None, ignore=()):
56 ''' succeed iff pyclbr.readmodule_ex(modulename) corresponds
57 to the actual module object, module. Any identifiers in
58 ignore are ignored. If no module is provided, the appropriate
59 module is loaded with __import__.'''
60
Guido van Rossumd858f702006-04-21 09:17:15 +000061 ignore = set(ignore) | set(['object'])
62
Benjamin Peterson2a691a82008-03-31 01:51:45 +000063 if module is None:
Guido van Rossum0ed7aa12002-12-02 14:54:20 +000064 # Import it.
65 # ('<silly>' is to work around an API silliness in __import__)
66 module = __import__(moduleName, globals(), {}, ['<silly>'])
Fred Drake3a28ca82001-08-13 20:26:19 +000067
68 dict = pyclbr.readmodule_ex(moduleName)
69
Anthony Baxterc2a5a632004-08-02 06:10:11 +000070 def ismethod(oclass, obj, name):
71 classdict = oclass.__dict__
Christian Heimes4a22b5d2007-11-25 09:39:14 +000072 if isinstance(obj, MethodType):
73 # could be a classmethod
74 if (not isinstance(classdict[name], ClassMethodType) or
Christian Heimesff737952007-11-27 10:40:20 +000075 obj.__self__ is not oclass):
Anthony Baxterc2a5a632004-08-02 06:10:11 +000076 return False
Christian Heimes4a22b5d2007-11-25 09:39:14 +000077 elif not isinstance(obj, FunctionType):
78 return False
Anthony Baxterc2a5a632004-08-02 06:10:11 +000079
Guido van Rossum7f6a4392002-12-03 08:16:50 +000080 objname = obj.__name__
81 if objname.startswith("__") and not objname.endswith("__"):
Christian Heimes4a22b5d2007-11-25 09:39:14 +000082 objname = "_%s%s" % (oclass.__name__, objname)
Guido van Rossum7f6a4392002-12-03 08:16:50 +000083 return objname == name
84
Fred Drake3a28ca82001-08-13 20:26:19 +000085 # Make sure the toplevel functions and classes are the same.
86 for name, value in dict.items():
Tim Peters04601062001-08-13 22:25:24 +000087 if name in ignore:
Fred Drake3a28ca82001-08-13 20:26:19 +000088 continue
89 self.assertHasattr(module, name, ignore)
90 py_item = getattr(module, name)
91 if isinstance(value, pyclbr.Function):
Ezio Melottie9615932010-01-24 19:26:24 +000092 self.assertIsInstance(py_item, (FunctionType, BuiltinFunctionType))
Thomas Wouters89f507f2006-12-13 04:49:30 +000093 if py_item.__module__ != moduleName:
94 continue # skip functions that came from somewhere else
Ezio Melottib3aedd42010-11-20 19:04:17 +000095 self.assertEqual(py_item.__module__, value.module)
Fred Drake3a28ca82001-08-13 20:26:19 +000096 else:
Ezio Melottie9615932010-01-24 19:26:24 +000097 self.assertIsInstance(py_item, type)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000098 if py_item.__module__ != moduleName:
99 continue # skip classes that came from somewhere else
100
Fred Drake3a28ca82001-08-13 20:26:19 +0000101 real_bases = [base.__name__ for base in py_item.__bases__]
Tim Peters04601062001-08-13 22:25:24 +0000102 pyclbr_bases = [ getattr(base, 'name', base)
Fred Drake3a28ca82001-08-13 20:26:19 +0000103 for base in value.super ]
Tim Peters04601062001-08-13 22:25:24 +0000104
Guido van Rossum0ed7aa12002-12-02 14:54:20 +0000105 try:
106 self.assertListEq(real_bases, pyclbr_bases, ignore)
107 except:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000108 print("class=%s" % py_item, file=sys.stderr)
Guido van Rossum0ed7aa12002-12-02 14:54:20 +0000109 raise
Fred Drake3a28ca82001-08-13 20:26:19 +0000110
111 actualMethods = []
Tim Peters37a309d2001-09-04 01:20:04 +0000112 for m in py_item.__dict__.keys():
Anthony Baxterc2a5a632004-08-02 06:10:11 +0000113 if ismethod(py_item, getattr(py_item, m), m):
Fred Drake3a28ca82001-08-13 20:26:19 +0000114 actualMethods.append(m)
115 foundMethods = []
116 for m in value.methods.keys():
117 if m[:2] == '__' and m[-2:] != '__':
118 foundMethods.append('_'+name+m)
119 else:
120 foundMethods.append(m)
121
Guido van Rossum7f6a4392002-12-03 08:16:50 +0000122 try:
123 self.assertListEq(foundMethods, actualMethods, ignore)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000124 self.assertEqual(py_item.__module__, value.module)
Fred Drake3a28ca82001-08-13 20:26:19 +0000125
Anthony Baxterc2a5a632004-08-02 06:10:11 +0000126 self.assertEqualsOrIgnored(py_item.__name__, value.name,
127 ignore)
Guido van Rossum7f6a4392002-12-03 08:16:50 +0000128 # can't check file or lineno
129 except:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000130 print("class=%s" % py_item, file=sys.stderr)
Guido van Rossum7f6a4392002-12-03 08:16:50 +0000131 raise
Fred Drake3a28ca82001-08-13 20:26:19 +0000132
133 # Now check for missing stuff.
Guido van Rossum0ed7aa12002-12-02 14:54:20 +0000134 def defined_in(item, module):
Guido van Rossum13257902007-06-07 23:15:56 +0000135 if isinstance(item, type):
Guido van Rossum0ed7aa12002-12-02 14:54:20 +0000136 return item.__module__ == module.__name__
137 if isinstance(item, FunctionType):
Neal Norwitz221085d2007-02-25 20:55:47 +0000138 return item.__globals__ is module.__dict__
Guido van Rossum0ed7aa12002-12-02 14:54:20 +0000139 return False
Fred Drake3a28ca82001-08-13 20:26:19 +0000140 for name in dir(module):
141 item = getattr(module, name)
Guido van Rossum13257902007-06-07 23:15:56 +0000142 if isinstance(item, (type, FunctionType)):
Guido van Rossum0ed7aa12002-12-02 14:54:20 +0000143 if defined_in(item, module):
144 self.assertHaskey(dict, name, ignore)
Fred Drake3a28ca82001-08-13 20:26:19 +0000145
146 def test_easy(self):
147 self.checkModule('pyclbr')
Serhiy Storchaka3f228112018-09-27 17:42:37 +0300148 # XXX: Metaclasses are not supported
149 # self.checkModule('ast')
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000150 self.checkModule('doctest', ignore=("TestResults", "_SpoofOut",
Andrew Svetlov7c1017b2013-08-29 01:24:39 +0300151 "DocTestCase", '_DocTestSuite'))
Christian Heimes25bb7832008-01-11 16:17:00 +0000152 self.checkModule('difflib', ignore=("Match",))
Fred Drake3a28ca82001-08-13 20:26:19 +0000153
Anthony Baxterc2a5a632004-08-02 06:10:11 +0000154 def test_decorators(self):
155 # XXX: See comment in pyclbr_input.py for a test that would fail
156 # if it were not commented out.
157 #
Christian Heimes4a22b5d2007-11-25 09:39:14 +0000158 self.checkModule('test.pyclbr_input', ignore=['om'])
Tim Peters6db15d72004-08-04 02:36:18 +0000159
csabella246ff3b2017-07-03 21:31:25 -0400160 def test_nested(self):
161 mb = pyclbr
162 # Set arguments for descriptor creation and _creat_tree call.
163 m, p, f, t, i = 'test', '', 'test.py', {}, None
164 source = dedent("""\
165 def f0:
166 def f1(a,b,c):
167 def f2(a=1, b=2, c=3): pass
168 return f1(a,b,d)
169 class c1: pass
170 class C0:
171 "Test class."
172 def F1():
173 "Method."
174 return 'return'
175 class C1():
176 class C2:
177 "Class nested within nested class."
178 def F3(): return 1+1
179
180 """)
181 actual = mb._create_tree(m, p, f, source, t, i)
182
183 # Create descriptors, linked together, and expected dict.
184 f0 = mb.Function(m, 'f0', f, 1)
185 f1 = mb._nest_function(f0, 'f1', 2)
186 f2 = mb._nest_function(f1, 'f2', 3)
187 c1 = mb._nest_class(f0, 'c1', 5)
188 C0 = mb.Class(m, 'C0', None, f, 6)
189 F1 = mb._nest_function(C0, 'F1', 8)
190 C1 = mb._nest_class(C0, 'C1', 11)
191 C2 = mb._nest_class(C1, 'C2', 12)
192 F3 = mb._nest_function(C2, 'F3', 14)
193 expected = {'f0':f0, 'C0':C0}
194
195 def compare(parent1, children1, parent2, children2):
196 """Return equality of tree pairs.
197
198 Each parent,children pair define a tree. The parents are
199 assumed equal. Comparing the children dictionaries as such
200 does not work due to comparison by identity and double
201 linkage. We separate comparing string and number attributes
202 from comparing the children of input children.
203 """
204 self.assertEqual(children1.keys(), children2.keys())
205 for ob in children1.values():
206 self.assertIs(ob.parent, parent1)
207 for ob in children2.values():
208 self.assertIs(ob.parent, parent2)
209 for key in children1.keys():
210 o1, o2 = children1[key], children2[key]
211 t1 = type(o1), o1.name, o1.file, o1.module, o1.lineno
212 t2 = type(o2), o2.name, o2.file, o2.module, o2.lineno
213 self.assertEqual(t1, t2)
214 if type(o1) is mb.Class:
215 self.assertEqual(o1.methods, o2.methods)
216 # Skip superclasses for now as not part of example
217 compare(o1, o1.children, o2, o2.children)
218
219 compare(None, actual, None, expected)
220
Fred Drake3a28ca82001-08-13 20:26:19 +0000221 def test_others(self):
222 cm = self.checkModule
223
Guido van Rossum7f6a4392002-12-03 08:16:50 +0000224 # These were once about the 10 longest modules
Raymond Hettingere401b6f2002-12-30 07:21:32 +0000225 cm('random', ignore=('Random',)) # from _random import Random as CoreGenerator
Guido van Rossum7f6a4392002-12-03 08:16:50 +0000226 cm('cgi', ignore=('log',)) # set with = in module
Victor Stinnerd65e4f42015-10-12 14:38:24 +0200227 cm('pickle', ignore=('partial',))
Brian Curtin9f914a02017-11-10 11:38:25 -0500228 # TODO(briancurtin): openfp is deprecated as of 3.7.
229 # Update this once it has been removed.
R David Murray4d35e752013-07-25 16:12:01 -0400230 cm('aifc', ignore=('openfp', '_aifc_params')) # set with = in module
Serhiy Storchakabd48d272016-09-11 12:50:02 +0300231 cm('sre_parse', ignore=('dump', 'groups', 'pos')) # from sre_constants import *; property
Guido van Rossum7f6a4392002-12-03 08:16:50 +0000232 cm('pdb')
Serhiy Storchakabdf6b912017-03-19 08:40:32 +0200233 cm('pydoc', ignore=('input', 'output',)) # properties
Fred Drake3a28ca82001-08-13 20:26:19 +0000234
Guido van Rossum0ed7aa12002-12-02 14:54:20 +0000235 # Tests for modules inside packages
Neal Norwitz315d8452007-08-30 03:06:59 +0000236 cm('email.parser')
Guido van Rossum7f6a4392002-12-03 08:16:50 +0000237 cm('test.test_pyclbr')
Fred Drake3a28ca82001-08-13 20:26:19 +0000238
Brett Cannon50865892019-03-22 15:16:50 -0700239
240class ReadmoduleTests(TestCase):
241
242 def setUp(self):
243 self._modules = pyclbr._modules.copy()
244
245 def tearDown(self):
246 pyclbr._modules = self._modules
247
248
249 def test_dotted_name_not_a_package(self):
Petri Lehtinen8d886042012-05-18 21:51:11 +0300250 # test ImportError is raised when the first part of a dotted name is
Brett Cannon50865892019-03-22 15:16:50 -0700251 # not a package.
252 #
253 # Issue #14798.
Petri Lehtinen8d886042012-05-18 21:51:11 +0300254 self.assertRaises(ImportError, pyclbr.readmodule_ex, 'asyncore.foo')
255
Brett Cannon50865892019-03-22 15:16:50 -0700256 def test_module_has_no_spec(self):
257 module_name = "doesnotexist"
258 assert module_name not in pyclbr._modules
259 with test_importlib_util.uncache(module_name):
260 with self.assertRaises(ModuleNotFoundError):
261 pyclbr.readmodule_ex(module_name)
262
Fred Drake2e2be372001-09-20 21:33:42 +0000263
Fred Drake2e2be372001-09-20 21:33:42 +0000264if __name__ == "__main__":
Zachary Wareac28b792015-12-04 23:32:23 -0600265 unittest_main()