| ''' | |
| 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. | |
| # ('<silly>' is to work around an API silliness in __import__) | |
| module = __import__(moduleName, globals(), {}, ['<silly>']) | |
| 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_main(): | |
| run_unittest(PyclbrTest) | |
| if __name__ == "__main__": | |
| test_main() |