''' Test cases for pyclbr.py Nick Mathewson ''' from test.test_support import run_unittest, import_module import sys from types import ClassType, FunctionType, MethodType, BuiltinFunctionType import pyclbr from unittest import TestCase StaticMethodType = type(staticmethod(lambda: None)) ClassMethodType = type(classmethod(lambda c: None)) # Silence Py3k warning import_module('commands', deprecated=True) # This next line triggers an error on old versions of pyclbr. from commands import getstatus # Here we test the python class browser code. # # The main function in this suite, 'testModule', compares the output # of pyclbr with the introspected members of a module. Because pyclbr # is imperfect (as designed), testModule is called with a set of # members to ignore. class PyclbrTest(TestCase): def assertListEq(self, l1, l2, ignore): ''' succeed iff {l1} - {ignore} == {l2} - {ignore} ''' missing = (set(l1) ^ set(l2)) - set(ignore) if missing: print >>sys.stderr, "l1=%r\nl2=%r\nignore=%r" % (l1, l2, ignore) self.fail("%r missing" % missing.pop()) def assertHasattr(self, obj, attr, ignore): ''' succeed iff hasattr(obj,attr) or attr in ignore. ''' if attr in ignore: return if not hasattr(obj, attr): print "???", attr self.assertTrue(hasattr(obj, attr), 'expected hasattr(%r, %r)' % (obj, attr)) def assertHaskey(self, obj, key, ignore): ''' succeed iff key in obj or key in ignore. ''' if key in ignore: return if key not in obj: print >>sys.stderr, "***", key self.assertIn(key, obj) def assertEqualsOrIgnored(self, a, b, ignore): ''' succeed iff a == b or a in ignore or b in ignore ''' if a not in ignore and b not in ignore: self.assertEqual(a, b) def checkModule(self, moduleName, module=None, ignore=()): ''' succeed iff pyclbr.readmodule_ex(modulename) corresponds to the actual module object, module. Any identifiers in ignore are ignored. If no module is provided, the appropriate module is loaded with __import__.''' if module is None: # Import it. # ('' is to work around an API silliness in __import__) module = __import__(moduleName, globals(), {}, ['']) dict = pyclbr.readmodule_ex(moduleName) def ismethod(oclass, obj, name): classdict = oclass.__dict__ if isinstance(obj, FunctionType): if not isinstance(classdict[name], StaticMethodType): return False else: if not isinstance(obj, MethodType): return False if obj.im_self is not None: if (not isinstance(classdict[name], ClassMethodType) or obj.im_self is not oclass): return False else: if not isinstance(classdict[name], FunctionType): return False objname = obj.__name__ if objname.startswith("__") and not objname.endswith("__"): objname = "_%s%s" % (obj.im_class.__name__, objname) return objname == name # Make sure the toplevel functions and classes are the same. for name, value in dict.items(): if name in ignore: continue self.assertHasattr(module, name, ignore) py_item = getattr(module, name) if isinstance(value, pyclbr.Function): self.assertIsInstance(py_item, (FunctionType, BuiltinFunctionType)) if py_item.__module__ != moduleName: continue # skip functions that came from somewhere else self.assertEqual(py_item.__module__, value.module) else: self.assertIsInstance(py_item, (ClassType, type)) if py_item.__module__ != moduleName: continue # skip classes that came from somewhere else real_bases = [base.__name__ for base in py_item.__bases__] pyclbr_bases = [ getattr(base, 'name', base) for base in value.super ] try: self.assertListEq(real_bases, pyclbr_bases, ignore) except: print >>sys.stderr, "class=%s" % py_item raise actualMethods = [] for m in py_item.__dict__.keys(): if ismethod(py_item, getattr(py_item, m), m): actualMethods.append(m) foundMethods = [] for m in value.methods.keys(): if m[:2] == '__' and m[-2:] != '__': foundMethods.append('_'+name+m) else: foundMethods.append(m) try: self.assertListEq(foundMethods, actualMethods, ignore) self.assertEqual(py_item.__module__, value.module) self.assertEqualsOrIgnored(py_item.__name__, value.name, ignore) # can't check file or lineno except: print >>sys.stderr, "class=%s" % py_item raise # Now check for missing stuff. def defined_in(item, module): if isinstance(item, ClassType): return item.__module__ == module.__name__ if isinstance(item, FunctionType): return item.func_globals is module.__dict__ return False for name in dir(module): item = getattr(module, name) if isinstance(item, (ClassType, FunctionType)): if defined_in(item, module): self.assertHaskey(dict, name, ignore) def test_easy(self): self.checkModule('pyclbr') self.checkModule('doctest', ignore=("DocTestCase",)) # Silence Py3k warning rfc822 = import_module('rfc822', deprecated=True) self.checkModule('rfc822', rfc822) self.checkModule('difflib') def test_decorators(self): # XXX: See comment in pyclbr_input.py for a test that would fail # if it were not commented out. # self.checkModule('test.pyclbr_input') def test_others(self): cm = self.checkModule # These were once about the 10 longest modules cm('random', ignore=('Random',)) # from _random import Random as CoreGenerator cm('cgi', ignore=('log',)) # set with = in module cm('urllib', ignore=('_CFNumberToInt32', '_CStringFromCFString', '_CFSetup', 'getproxies_registry', 'proxy_bypass_registry', 'proxy_bypass_macosx_sysconf', 'open_https', 'getproxies_macosx_sysconf', 'getproxies_internetconfig',)) # not on all platforms cm('pickle') cm('aifc', ignore=('openfp',)) # set with = in module cm('Cookie') cm('sre_parse', ignore=('dump',)) # from sre_constants import * cm('pdb') cm('pydoc') # Tests for modules inside packages cm('email.parser') cm('test.test_pyclbr') def test_issue_14798(self): # test ImportError is raised when the first part of a dotted name is # not a package self.assertRaises(ImportError, pyclbr.readmodule_ex, 'asyncore.foo') def test_main(): run_unittest(PyclbrTest) if __name__ == "__main__": test_main()