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