blob: eaab591f74efe49676851b41cd827cc6b68a32c7 [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
13from functools import partial
Fred Drake3a28ca82001-08-13 20:26:19 +000014
Anthony Baxterc2a5a632004-08-02 06:10:11 +000015StaticMethodType = type(staticmethod(lambda: None))
16ClassMethodType = type(classmethod(lambda c: None))
17
Tim Peters04601062001-08-13 22:25:24 +000018# Here we test the python class browser code.
Fred Drake3a28ca82001-08-13 20:26:19 +000019#
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
Guido van Rossum0ed7aa12002-12-02 14:54:20 +000025class PyclbrTest(TestCase):
Fred Drake3a28ca82001-08-13 20:26:19 +000026
27 def assertListEq(self, l1, l2, ignore):
28 ''' succeed iff {l1} - {ignore} == {l2} - {ignore} '''
Raymond Hettingera690a992003-11-16 16:17:49 +000029 missing = (set(l1) ^ set(l2)) - set(ignore)
Raymond Hettinger91bbd9a2003-05-02 09:06:28 +000030 if missing:
Guido van Rossumbe19ed72007-02-09 05:37:30 +000031 print("l1=%r\nl2=%r\nignore=%r" % (l1, l2, ignore), file=sys.stderr)
Raymond Hettinger91bbd9a2003-05-02 09:06:28 +000032 self.fail("%r missing" % missing.pop())
Tim Peters04601062001-08-13 22:25:24 +000033
Fred Drake3a28ca82001-08-13 20:26:19 +000034 def assertHasattr(self, obj, attr, ignore):
35 ''' succeed iff hasattr(obj,attr) or attr in ignore. '''
36 if attr in ignore: return
Guido van Rossumbe19ed72007-02-09 05:37:30 +000037 if not hasattr(obj, attr): print("???", attr)
Benjamin Petersonc9c0f202009-06-30 23:06:06 +000038 self.assertTrue(hasattr(obj, attr),
Tim Peters5e5ca562002-07-10 02:37:21 +000039 'expected hasattr(%r, %r)' % (obj, attr))
Fred Drake3a28ca82001-08-13 20:26:19 +000040
41
42 def assertHaskey(self, obj, key, ignore):
Guido van Rossume2b70bc2006-08-18 22:13:04 +000043 ''' succeed iff key in obj or key in ignore. '''
Fred Drake3a28ca82001-08-13 20:26:19 +000044 if key in ignore: return
Guido van Rossume2b70bc2006-08-18 22:13:04 +000045 if key not in obj:
Guido van Rossumbe19ed72007-02-09 05:37:30 +000046 print("***",key, file=sys.stderr)
Benjamin Peterson577473f2010-01-19 00:09:57 +000047 self.assertIn(key, obj)
Fred Drake3a28ca82001-08-13 20:26:19 +000048
Anthony Baxterc2a5a632004-08-02 06:10:11 +000049 def assertEqualsOrIgnored(self, a, b, ignore):
Fred Drake3a28ca82001-08-13 20:26:19 +000050 ''' succeed iff a == b or a in ignore or b in ignore '''
Anthony Baxterc2a5a632004-08-02 06:10:11 +000051 if a not in ignore and b not in ignore:
Ezio Melottib3aedd42010-11-20 19:04:17 +000052 self.assertEqual(a, b)
Fred Drake3a28ca82001-08-13 20:26:19 +000053
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
Guido van Rossumd858f702006-04-21 09:17:15 +000060 ignore = set(ignore) | set(['object'])
61
Benjamin Peterson2a691a82008-03-31 01:51:45 +000062 if module is None:
Guido van Rossum0ed7aa12002-12-02 14:54:20 +000063 # Import it.
64 # ('<silly>' is to work around an API silliness in __import__)
65 module = __import__(moduleName, globals(), {}, ['<silly>'])
Fred Drake3a28ca82001-08-13 20:26:19 +000066
67 dict = pyclbr.readmodule_ex(moduleName)
68
Anthony Baxterc2a5a632004-08-02 06:10:11 +000069 def ismethod(oclass, obj, name):
70 classdict = oclass.__dict__
Christian Heimes4a22b5d2007-11-25 09:39:14 +000071 if isinstance(obj, MethodType):
72 # could be a classmethod
73 if (not isinstance(classdict[name], ClassMethodType) or
Christian Heimesff737952007-11-27 10:40:20 +000074 obj.__self__ is not oclass):
Anthony Baxterc2a5a632004-08-02 06:10:11 +000075 return False
Christian Heimes4a22b5d2007-11-25 09:39:14 +000076 elif not isinstance(obj, FunctionType):
77 return False
Anthony Baxterc2a5a632004-08-02 06:10:11 +000078
Guido van Rossum7f6a4392002-12-03 08:16:50 +000079 objname = obj.__name__
80 if objname.startswith("__") and not objname.endswith("__"):
Christian Heimes4a22b5d2007-11-25 09:39:14 +000081 objname = "_%s%s" % (oclass.__name__, objname)
Guido van Rossum7f6a4392002-12-03 08:16:50 +000082 return objname == name
83
Fred Drake3a28ca82001-08-13 20:26:19 +000084 # Make sure the toplevel functions and classes are the same.
85 for name, value in dict.items():
Tim Peters04601062001-08-13 22:25:24 +000086 if name in ignore:
Fred Drake3a28ca82001-08-13 20:26:19 +000087 continue
88 self.assertHasattr(module, name, ignore)
89 py_item = getattr(module, name)
90 if isinstance(value, pyclbr.Function):
Ezio Melottie9615932010-01-24 19:26:24 +000091 self.assertIsInstance(py_item, (FunctionType, BuiltinFunctionType))
Thomas Wouters89f507f2006-12-13 04:49:30 +000092 if py_item.__module__ != moduleName:
93 continue # skip functions that came from somewhere else
Ezio Melottib3aedd42010-11-20 19:04:17 +000094 self.assertEqual(py_item.__module__, value.module)
Fred Drake3a28ca82001-08-13 20:26:19 +000095 else:
Ezio Melottie9615932010-01-24 19:26:24 +000096 self.assertIsInstance(py_item, type)
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000097 if py_item.__module__ != moduleName:
98 continue # skip classes that came from somewhere else
99
Fred Drake3a28ca82001-08-13 20:26:19 +0000100 real_bases = [base.__name__ for base in py_item.__bases__]
Tim Peters04601062001-08-13 22:25:24 +0000101 pyclbr_bases = [ getattr(base, 'name', base)
Fred Drake3a28ca82001-08-13 20:26:19 +0000102 for base in value.super ]
Tim Peters04601062001-08-13 22:25:24 +0000103
Guido van Rossum0ed7aa12002-12-02 14:54:20 +0000104 try:
105 self.assertListEq(real_bases, pyclbr_bases, ignore)
106 except:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000107 print("class=%s" % py_item, file=sys.stderr)
Guido van Rossum0ed7aa12002-12-02 14:54:20 +0000108 raise
Fred Drake3a28ca82001-08-13 20:26:19 +0000109
110 actualMethods = []
Tim Peters37a309d2001-09-04 01:20:04 +0000111 for m in py_item.__dict__.keys():
Anthony Baxterc2a5a632004-08-02 06:10:11 +0000112 if ismethod(py_item, getattr(py_item, m), m):
Fred Drake3a28ca82001-08-13 20:26:19 +0000113 actualMethods.append(m)
114 foundMethods = []
115 for m in value.methods.keys():
116 if m[:2] == '__' and m[-2:] != '__':
117 foundMethods.append('_'+name+m)
118 else:
119 foundMethods.append(m)
120
Guido van Rossum7f6a4392002-12-03 08:16:50 +0000121 try:
122 self.assertListEq(foundMethods, actualMethods, ignore)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000123 self.assertEqual(py_item.__module__, value.module)
Fred Drake3a28ca82001-08-13 20:26:19 +0000124
Anthony Baxterc2a5a632004-08-02 06:10:11 +0000125 self.assertEqualsOrIgnored(py_item.__name__, value.name,
126 ignore)
Guido van Rossum7f6a4392002-12-03 08:16:50 +0000127 # can't check file or lineno
128 except:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000129 print("class=%s" % py_item, file=sys.stderr)
Guido van Rossum7f6a4392002-12-03 08:16:50 +0000130 raise
Fred Drake3a28ca82001-08-13 20:26:19 +0000131
132 # Now check for missing stuff.
Guido van Rossum0ed7aa12002-12-02 14:54:20 +0000133 def defined_in(item, module):
Guido van Rossum13257902007-06-07 23:15:56 +0000134 if isinstance(item, type):
Guido van Rossum0ed7aa12002-12-02 14:54:20 +0000135 return item.__module__ == module.__name__
136 if isinstance(item, FunctionType):
Neal Norwitz221085d2007-02-25 20:55:47 +0000137 return item.__globals__ is module.__dict__
Guido van Rossum0ed7aa12002-12-02 14:54:20 +0000138 return False
Fred Drake3a28ca82001-08-13 20:26:19 +0000139 for name in dir(module):
140 item = getattr(module, name)
Guido van Rossum13257902007-06-07 23:15:56 +0000141 if isinstance(item, (type, FunctionType)):
Guido van Rossum0ed7aa12002-12-02 14:54:20 +0000142 if defined_in(item, module):
143 self.assertHaskey(dict, name, ignore)
Fred Drake3a28ca82001-08-13 20:26:19 +0000144
145 def test_easy(self):
146 self.checkModule('pyclbr')
Georg Brandl9f0f9602008-06-12 22:23:59 +0000147 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
Victor Stinnerd65e4f42015-10-12 14:38:24 +0200225 cm('pickle', ignore=('partial',))
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
Petri Lehtinen8d886042012-05-18 21:51:11 +0300237 def test_issue_14798(self):
238 # test ImportError is raised when the first part of a dotted name is
239 # not a package
240 self.assertRaises(ImportError, pyclbr.readmodule_ex, 'asyncore.foo')
241
Fred Drake2e2be372001-09-20 21:33:42 +0000242
Fred Drake2e2be372001-09-20 21:33:42 +0000243if __name__ == "__main__":
Zachary Wareac28b792015-12-04 23:32:23 -0600244 unittest_main()