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