PageRenderTime 44ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/Lib/test/test_pyclbr.py

https://github.com/albertz/CPython
Python | 244 lines | 191 code | 25 blank | 28 comment | 34 complexity | 46c25e5e8280df8f86e21a0ceaa07c1f MD5 | raw file
  1. '''
  2. Test cases for pyclbr.py
  3. Nick Mathewson
  4. '''
  5. import os
  6. import sys
  7. from textwrap import dedent
  8. from types import FunctionType, MethodType, BuiltinFunctionType
  9. import pyclbr
  10. from unittest import TestCase, main as unittest_main
  11. from test import support
  12. from functools import partial
  13. StaticMethodType = type(staticmethod(lambda: None))
  14. ClassMethodType = type(classmethod(lambda c: None))
  15. # Here we test the python class browser code.
  16. #
  17. # The main function in this suite, 'testModule', compares the output
  18. # of pyclbr with the introspected members of a module. Because pyclbr
  19. # is imperfect (as designed), testModule is called with a set of
  20. # members to ignore.
  21. class PyclbrTest(TestCase):
  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. def assertHasattr(self, obj, attr, ignore):
  29. ''' succeed iff hasattr(obj,attr) or attr in ignore. '''
  30. if attr in ignore: return
  31. if not hasattr(obj, attr): print("???", attr)
  32. self.assertTrue(hasattr(obj, attr),
  33. 'expected hasattr(%r, %r)' % (obj, attr))
  34. def assertHaskey(self, obj, key, ignore):
  35. ''' succeed iff key in obj or key in ignore. '''
  36. if key in ignore: return
  37. if key not in obj:
  38. print("***",key, file=sys.stderr)
  39. self.assertIn(key, obj)
  40. def assertEqualsOrIgnored(self, a, b, ignore):
  41. ''' succeed iff a == b or a in ignore or b in ignore '''
  42. if a not in ignore and b not in ignore:
  43. self.assertEqual(a, b)
  44. def checkModule(self, moduleName, module=None, ignore=()):
  45. ''' succeed iff pyclbr.readmodule_ex(modulename) corresponds
  46. to the actual module object, module. Any identifiers in
  47. ignore are ignored. If no module is provided, the appropriate
  48. module is loaded with __import__.'''
  49. ignore = set(ignore) | set(['object'])
  50. if module is None:
  51. # Import it.
  52. # ('<silly>' is to work around an API silliness in __import__)
  53. module = __import__(moduleName, globals(), {}, ['<silly>'])
  54. dict = pyclbr.readmodule_ex(moduleName)
  55. def ismethod(oclass, obj, name):
  56. classdict = oclass.__dict__
  57. if isinstance(obj, MethodType):
  58. # could be a classmethod
  59. if (not isinstance(classdict[name], ClassMethodType) or
  60. obj.__self__ is not oclass):
  61. return False
  62. elif not isinstance(obj, FunctionType):
  63. return False
  64. objname = obj.__name__
  65. if objname.startswith("__") and not objname.endswith("__"):
  66. objname = "_%s%s" % (oclass.__name__, objname)
  67. return objname == name
  68. # Make sure the toplevel functions and classes are the same.
  69. for name, value in dict.items():
  70. if name in ignore:
  71. continue
  72. self.assertHasattr(module, name, ignore)
  73. py_item = getattr(module, name)
  74. if isinstance(value, pyclbr.Function):
  75. self.assertIsInstance(py_item, (FunctionType, BuiltinFunctionType))
  76. if py_item.__module__ != moduleName:
  77. continue # skip functions that came from somewhere else
  78. self.assertEqual(py_item.__module__, value.module)
  79. else:
  80. self.assertIsInstance(py_item, type)
  81. if py_item.__module__ != moduleName:
  82. continue # skip classes that came from somewhere else
  83. real_bases = [base.__name__ for base in py_item.__bases__]
  84. pyclbr_bases = [ getattr(base, 'name', base)
  85. for base in value.super ]
  86. try:
  87. self.assertListEq(real_bases, pyclbr_bases, ignore)
  88. except:
  89. print("class=%s" % py_item, file=sys.stderr)
  90. raise
  91. actualMethods = []
  92. for m in py_item.__dict__.keys():
  93. if ismethod(py_item, getattr(py_item, m), m):
  94. actualMethods.append(m)
  95. foundMethods = []
  96. for m in value.methods.keys():
  97. if m[:2] == '__' and m[-2:] != '__':
  98. foundMethods.append('_'+name+m)
  99. else:
  100. foundMethods.append(m)
  101. try:
  102. self.assertListEq(foundMethods, actualMethods, ignore)
  103. self.assertEqual(py_item.__module__, value.module)
  104. self.assertEqualsOrIgnored(py_item.__name__, value.name,
  105. ignore)
  106. # can't check file or lineno
  107. except:
  108. print("class=%s" % py_item, file=sys.stderr)
  109. raise
  110. # Now check for missing stuff.
  111. def defined_in(item, module):
  112. if isinstance(item, type):
  113. return item.__module__ == module.__name__
  114. if isinstance(item, FunctionType):
  115. return item.__globals__ is module.__dict__
  116. return False
  117. for name in dir(module):
  118. item = getattr(module, name)
  119. if isinstance(item, (type, FunctionType)):
  120. if defined_in(item, module):
  121. self.assertHaskey(dict, name, ignore)
  122. def test_easy(self):
  123. self.checkModule('pyclbr')
  124. self.checkModule('ast')
  125. self.checkModule('doctest', ignore=("TestResults", "_SpoofOut",
  126. "DocTestCase", '_DocTestSuite'))
  127. self.checkModule('difflib', ignore=("Match",))
  128. def test_decorators(self):
  129. # XXX: See comment in pyclbr_input.py for a test that would fail
  130. # if it were not commented out.
  131. #
  132. self.checkModule('test.pyclbr_input', ignore=['om'])
  133. def test_nested(self):
  134. mb = pyclbr
  135. # Set arguments for descriptor creation and _creat_tree call.
  136. m, p, f, t, i = 'test', '', 'test.py', {}, None
  137. source = dedent("""\
  138. def f0:
  139. def f1(a,b,c):
  140. def f2(a=1, b=2, c=3): pass
  141. return f1(a,b,d)
  142. class c1: pass
  143. class C0:
  144. "Test class."
  145. def F1():
  146. "Method."
  147. return 'return'
  148. class C1():
  149. class C2:
  150. "Class nested within nested class."
  151. def F3(): return 1+1
  152. """)
  153. actual = mb._create_tree(m, p, f, source, t, i)
  154. # Create descriptors, linked together, and expected dict.
  155. f0 = mb.Function(m, 'f0', f, 1)
  156. f1 = mb._nest_function(f0, 'f1', 2)
  157. f2 = mb._nest_function(f1, 'f2', 3)
  158. c1 = mb._nest_class(f0, 'c1', 5)
  159. C0 = mb.Class(m, 'C0', None, f, 6)
  160. F1 = mb._nest_function(C0, 'F1', 8)
  161. C1 = mb._nest_class(C0, 'C1', 11)
  162. C2 = mb._nest_class(C1, 'C2', 12)
  163. F3 = mb._nest_function(C2, 'F3', 14)
  164. expected = {'f0':f0, 'C0':C0}
  165. def compare(parent1, children1, parent2, children2):
  166. """Return equality of tree pairs.
  167. Each parent,children pair define a tree. The parents are
  168. assumed equal. Comparing the children dictionaries as such
  169. does not work due to comparison by identity and double
  170. linkage. We separate comparing string and number attributes
  171. from comparing the children of input children.
  172. """
  173. self.assertEqual(children1.keys(), children2.keys())
  174. for ob in children1.values():
  175. self.assertIs(ob.parent, parent1)
  176. for ob in children2.values():
  177. self.assertIs(ob.parent, parent2)
  178. for key in children1.keys():
  179. o1, o2 = children1[key], children2[key]
  180. t1 = type(o1), o1.name, o1.file, o1.module, o1.lineno
  181. t2 = type(o2), o2.name, o2.file, o2.module, o2.lineno
  182. self.assertEqual(t1, t2)
  183. if type(o1) is mb.Class:
  184. self.assertEqual(o1.methods, o2.methods)
  185. # Skip superclasses for now as not part of example
  186. compare(o1, o1.children, o2, o2.children)
  187. compare(None, actual, None, expected)
  188. def test_others(self):
  189. cm = self.checkModule
  190. # These were once about the 10 longest modules
  191. cm('random', ignore=('Random',)) # from _random import Random as CoreGenerator
  192. cm('cgi', ignore=('log',)) # set with = in module
  193. cm('pickle', ignore=('partial',))
  194. # TODO(briancurtin): openfp is deprecated as of 3.7.
  195. # Update this once it has been removed.
  196. cm('aifc', ignore=('openfp', '_aifc_params')) # set with = in module
  197. cm('sre_parse', ignore=('dump', 'groups', 'pos')) # from sre_constants import *; property
  198. cm('pdb')
  199. cm('pydoc', ignore=('input', 'output',)) # properties
  200. # Tests for modules inside packages
  201. cm('email.parser')
  202. cm('test.test_pyclbr')
  203. def test_issue_14798(self):
  204. # test ImportError is raised when the first part of a dotted name is
  205. # not a package
  206. self.assertRaises(ImportError, pyclbr.readmodule_ex, 'asyncore.foo')
  207. if __name__ == "__main__":
  208. unittest_main()