(root)/
Python-3.12.0/
Lib/
test/
test_pyclbr.py
       1  '''
       2     Test cases for pyclbr.py
       3     Nick Mathewson
       4  '''
       5  
       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.test_importlib import util as test_importlib_util
      12  import warnings
      13  
      14  
      15  StaticMethodType = type(staticmethod(lambda: None))
      16  ClassMethodType = type(classmethod(lambda c: None))
      17  
      18  # Here we test the python class browser code.
      19  #
      20  # The main function in this suite, 'testModule', compares the output
      21  # of pyclbr with the introspected members of a module.  Because pyclbr
      22  # is imperfect (as designed), testModule is called with a set of
      23  # members to ignore.
      24  
      25  class ESC[4;38;5;81mPyclbrTest(ESC[4;38;5;149mTestCase):
      26  
      27      def assertListEq(self, l1, l2, ignore):
      28          ''' succeed iff {l1} - {ignore} == {l2} - {ignore} '''
      29          missing = (set(l1) ^ set(l2)) - set(ignore)
      30          if missing:
      31              print("l1=%r\nl2=%r\nignore=%r" % (l1, l2, ignore), file=sys.stderr)
      32              self.fail("%r missing" % missing.pop())
      33  
      34      def assertHasattr(self, obj, attr, ignore):
      35          ''' succeed iff hasattr(obj,attr) or attr in ignore. '''
      36          if attr in ignore: return
      37          if not hasattr(obj, attr): print("???", attr)
      38          self.assertTrue(hasattr(obj, attr),
      39                          'expected hasattr(%r, %r)' % (obj, attr))
      40  
      41  
      42      def assertHaskey(self, obj, key, ignore):
      43          ''' succeed iff key in obj or key in ignore. '''
      44          if key in ignore: return
      45          if key not in obj:
      46              print("***",key, file=sys.stderr)
      47          self.assertIn(key, obj)
      48  
      49      def assertEqualsOrIgnored(self, a, b, ignore):
      50          ''' succeed iff a == b or a in ignore or b in ignore '''
      51          if a not in ignore and b not in ignore:
      52              self.assertEqual(a, b)
      53  
      54      def checkModule(self, moduleName, module=None, ignore=()):
      55          ''' succeed iff pyclbr.readmodule_ex(modulename) corresponds
      56              to the actual module object, module.  Any identifiers in
      57              ignore are ignored.   If no module is provided, the appropriate
      58              module is loaded with __import__.'''
      59  
      60          ignore = set(ignore) | set(['object'])
      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, MethodType):
      72                  # could be a classmethod
      73                  if (not isinstance(classdict[name], ClassMethodType) or
      74                      obj.__self__ is not oclass):
      75                      return False
      76              elif not isinstance(obj, FunctionType):
      77                  return False
      78  
      79              objname = obj.__name__
      80              if objname.startswith("__") and not objname.endswith("__"):
      81                  objname = "_%s%s" % (oclass.__name__, objname)
      82              return objname == name
      83  
      84          # Make sure the toplevel functions and classes are the same.
      85          for name, value in dict.items():
      86              if name in ignore:
      87                  continue
      88              self.assertHasattr(module, name, ignore)
      89              py_item = getattr(module, name)
      90              if isinstance(value, pyclbr.Function):
      91                  self.assertIsInstance(py_item, (FunctionType, BuiltinFunctionType))
      92                  if py_item.__module__ != moduleName:
      93                      continue   # skip functions that came from somewhere else
      94                  self.assertEqual(py_item.__module__, value.module)
      95              else:
      96                  self.assertIsInstance(py_item, type)
      97                  if py_item.__module__ != moduleName:
      98                      continue   # skip classes that came from somewhere else
      99  
     100                  real_bases = [base.__name__ for base in py_item.__bases__]
     101                  pyclbr_bases = [ getattr(base, 'name', base)
     102                                   for base in value.super ]
     103  
     104                  try:
     105                      self.assertListEq(real_bases, pyclbr_bases, ignore)
     106                  except:
     107                      print("class=%s" % py_item, file=sys.stderr)
     108                      raise
     109  
     110                  actualMethods = []
     111                  for m in py_item.__dict__.keys():
     112                      if ismethod(py_item, getattr(py_item, m), m):
     113                          actualMethods.append(m)
     114                  foundMethods = []
     115                  for m in value.methods.keys():
     116                      if m[:2] == '__' and m[-2:] != '__':
     117                          foundMethods.append('_'+name+m)
     118                      else:
     119                          foundMethods.append(m)
     120  
     121                  try:
     122                      self.assertListEq(foundMethods, actualMethods, ignore)
     123                      self.assertEqual(py_item.__module__, value.module)
     124  
     125                      self.assertEqualsOrIgnored(py_item.__name__, value.name,
     126                                                 ignore)
     127                      # can't check file or lineno
     128                  except:
     129                      print("class=%s" % py_item, file=sys.stderr)
     130                      raise
     131  
     132          # Now check for missing stuff.
     133          def defined_in(item, module):
     134              if isinstance(item, type):
     135                  return item.__module__ == module.__name__
     136              if isinstance(item, FunctionType):
     137                  return item.__globals__ is module.__dict__
     138              return False
     139          for name in dir(module):
     140              item = getattr(module, name)
     141              if isinstance(item,  (type, FunctionType)):
     142                  if defined_in(item, module):
     143                      self.assertHaskey(dict, name, ignore)
     144  
     145      def test_easy(self):
     146          self.checkModule('pyclbr')
     147          # XXX: Metaclasses are not supported
     148          # self.checkModule('ast')
     149          self.checkModule('doctest', ignore=("TestResults", "_SpoofOut",
     150                                              "DocTestCase", '_DocTestSuite'))
     151          self.checkModule('difflib', ignore=("Match",))
     152  
     153      def test_decorators(self):
     154          self.checkModule('test.pyclbr_input', ignore=['om'])
     155  
     156      def test_nested(self):
     157          mb = pyclbr
     158          # Set arguments for descriptor creation and _creat_tree call.
     159          m, p, f, t, i = 'test', '', 'test.py', {}, None
     160          source = dedent("""\
     161          def f0():
     162              def f1(a,b,c):
     163                  def f2(a=1, b=2, c=3): pass
     164                  return f1(a,b,d)
     165              class c1: pass
     166          class C0:
     167              "Test class."
     168              def F1():
     169                  "Method."
     170                  return 'return'
     171              class C1():
     172                  class C2:
     173                      "Class nested within nested class."
     174                      def F3(): return 1+1
     175  
     176          """)
     177          actual = mb._create_tree(m, p, f, source, t, i)
     178  
     179          # Create descriptors, linked together, and expected dict.
     180          f0 = mb.Function(m, 'f0', f, 1, end_lineno=5)
     181          f1 = mb._nest_function(f0, 'f1', 2, 4)
     182          f2 = mb._nest_function(f1, 'f2', 3, 3)
     183          c1 = mb._nest_class(f0, 'c1', 5, 5)
     184          C0 = mb.Class(m, 'C0', None, f, 6, end_lineno=14)
     185          F1 = mb._nest_function(C0, 'F1', 8, 10)
     186          C1 = mb._nest_class(C0, 'C1', 11, 14)
     187          C2 = mb._nest_class(C1, 'C2', 12, 14)
     188          F3 = mb._nest_function(C2, 'F3', 14, 14)
     189          expected = {'f0':f0, 'C0':C0}
     190  
     191          def compare(parent1, children1, parent2, children2):
     192              """Return equality of tree pairs.
     193  
     194              Each parent,children pair define a tree.  The parents are
     195              assumed equal.  Comparing the children dictionaries as such
     196              does not work due to comparison by identity and double
     197              linkage.  We separate comparing string and number attributes
     198              from comparing the children of input children.
     199              """
     200              self.assertEqual(children1.keys(), children2.keys())
     201              for ob in children1.values():
     202                  self.assertIs(ob.parent, parent1)
     203              for ob in children2.values():
     204                  self.assertIs(ob.parent, parent2)
     205              for key in children1.keys():
     206                  o1, o2 = children1[key], children2[key]
     207                  t1 = type(o1), o1.name, o1.file, o1.module, o1.lineno, o1.end_lineno
     208                  t2 = type(o2), o2.name, o2.file, o2.module, o2.lineno, o2.end_lineno
     209                  self.assertEqual(t1, t2)
     210                  if type(o1) is mb.Class:
     211                      self.assertEqual(o1.methods, o2.methods)
     212                  # Skip superclasses for now as not part of example
     213                  compare(o1, o1.children, o2, o2.children)
     214  
     215          compare(None, actual, None, expected)
     216  
     217      def test_others(self):
     218          cm = self.checkModule
     219  
     220          # These were once some of the longest modules.
     221          cm('random', ignore=('Random',))  # from _random import Random as CoreGenerator
     222          with warnings.catch_warnings():
     223              warnings.simplefilter('ignore', DeprecationWarning)
     224              cm('cgi', ignore=('log',))      # set with = in module
     225          cm('pickle', ignore=('partial', 'PickleBuffer'))
     226          with warnings.catch_warnings():
     227              warnings.simplefilter('ignore', DeprecationWarning)
     228              cm('sre_parse', ignore=('dump', 'groups', 'pos')) # from sre_constants import *; property
     229          cm(
     230              'pdb',
     231              # pyclbr does not handle elegantly `typing` or properties
     232              ignore=('Union', '_ModuleTarget', '_ScriptTarget'),
     233          )
     234          cm('pydoc', ignore=('input', 'output',)) # properties
     235  
     236          # Tests for modules inside packages
     237          cm('email.parser')
     238          cm('test.test_pyclbr')
     239  
     240  
     241  class ESC[4;38;5;81mReadmoduleTests(ESC[4;38;5;149mTestCase):
     242  
     243      def setUp(self):
     244          self._modules = pyclbr._modules.copy()
     245  
     246      def tearDown(self):
     247          pyclbr._modules = self._modules
     248  
     249  
     250      def test_dotted_name_not_a_package(self):
     251          # test ImportError is raised when the first part of a dotted name is
     252          # not a package.
     253          #
     254          # Issue #14798.
     255          self.assertRaises(ImportError, pyclbr.readmodule_ex, 'asyncio.foo')
     256  
     257      def test_module_has_no_spec(self):
     258          module_name = "doesnotexist"
     259          assert module_name not in pyclbr._modules
     260          with test_importlib_util.uncache(module_name):
     261              with self.assertRaises(ModuleNotFoundError):
     262                  pyclbr.readmodule_ex(module_name)
     263  
     264  
     265  if __name__ == "__main__":
     266      unittest_main()