(root)/
Python-3.12.0/
Lib/
test/
test_pickle.py
       1  from _compat_pickle import (IMPORT_MAPPING, REVERSE_IMPORT_MAPPING,
       2                              NAME_MAPPING, REVERSE_NAME_MAPPING)
       3  import builtins
       4  import pickle
       5  import io
       6  import collections
       7  import struct
       8  import sys
       9  import warnings
      10  import weakref
      11  
      12  import doctest
      13  import unittest
      14  from test import support
      15  from test.support import import_helper
      16  
      17  from test.pickletester import AbstractHookTests
      18  from test.pickletester import AbstractUnpickleTests
      19  from test.pickletester import AbstractPickleTests
      20  from test.pickletester import AbstractPickleModuleTests
      21  from test.pickletester import AbstractPersistentPicklerTests
      22  from test.pickletester import AbstractIdentityPersistentPicklerTests
      23  from test.pickletester import AbstractPicklerUnpicklerObjectTests
      24  from test.pickletester import AbstractDispatchTableTests
      25  from test.pickletester import AbstractCustomPicklerClass
      26  from test.pickletester import BigmemPickleTests
      27  
      28  try:
      29      import _pickle
      30      has_c_implementation = True
      31  except ImportError:
      32      has_c_implementation = False
      33  
      34  
      35  class ESC[4;38;5;81mPyPickleTests(ESC[4;38;5;149mAbstractPickleModuleTests, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
      36      dump = staticmethod(pickle._dump)
      37      dumps = staticmethod(pickle._dumps)
      38      load = staticmethod(pickle._load)
      39      loads = staticmethod(pickle._loads)
      40      Pickler = pickle._Pickler
      41      Unpickler = pickle._Unpickler
      42  
      43  
      44  class ESC[4;38;5;81mPyUnpicklerTests(ESC[4;38;5;149mAbstractUnpickleTests, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
      45  
      46      unpickler = pickle._Unpickler
      47      bad_stack_errors = (IndexError,)
      48      truncated_errors = (pickle.UnpicklingError, EOFError,
      49                          AttributeError, ValueError,
      50                          struct.error, IndexError, ImportError)
      51  
      52      def loads(self, buf, **kwds):
      53          f = io.BytesIO(buf)
      54          u = self.unpickler(f, **kwds)
      55          return u.load()
      56  
      57  
      58  class ESC[4;38;5;81mPyPicklerTests(ESC[4;38;5;149mAbstractPickleTests, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
      59  
      60      pickler = pickle._Pickler
      61      unpickler = pickle._Unpickler
      62  
      63      def dumps(self, arg, proto=None, **kwargs):
      64          f = io.BytesIO()
      65          p = self.pickler(f, proto, **kwargs)
      66          p.dump(arg)
      67          f.seek(0)
      68          return bytes(f.read())
      69  
      70      def loads(self, buf, **kwds):
      71          f = io.BytesIO(buf)
      72          u = self.unpickler(f, **kwds)
      73          return u.load()
      74  
      75  
      76  class ESC[4;38;5;81mInMemoryPickleTests(ESC[4;38;5;149mAbstractPickleTests, ESC[4;38;5;149mAbstractUnpickleTests,
      77                            ESC[4;38;5;149mBigmemPickleTests, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
      78  
      79      bad_stack_errors = (pickle.UnpicklingError, IndexError)
      80      truncated_errors = (pickle.UnpicklingError, EOFError,
      81                          AttributeError, ValueError,
      82                          struct.error, IndexError, ImportError)
      83  
      84      def dumps(self, arg, protocol=None, **kwargs):
      85          return pickle.dumps(arg, protocol, **kwargs)
      86  
      87      def loads(self, buf, **kwds):
      88          return pickle.loads(buf, **kwds)
      89  
      90      test_framed_write_sizes_with_delayed_writer = None
      91  
      92  
      93  class ESC[4;38;5;81mPersistentPicklerUnpicklerMixin(ESC[4;38;5;149mobject):
      94  
      95      def dumps(self, arg, proto=None):
      96          class ESC[4;38;5;81mPersPickler(ESC[4;38;5;149mselfESC[4;38;5;149m.ESC[4;38;5;149mpickler):
      97              def persistent_id(subself, obj):
      98                  return self.persistent_id(obj)
      99          f = io.BytesIO()
     100          p = PersPickler(f, proto)
     101          p.dump(arg)
     102          return f.getvalue()
     103  
     104      def loads(self, buf, **kwds):
     105          class ESC[4;38;5;81mPersUnpickler(ESC[4;38;5;149mselfESC[4;38;5;149m.ESC[4;38;5;149munpickler):
     106              def persistent_load(subself, obj):
     107                  return self.persistent_load(obj)
     108          f = io.BytesIO(buf)
     109          u = PersUnpickler(f, **kwds)
     110          return u.load()
     111  
     112  
     113  class ESC[4;38;5;81mPyPersPicklerTests(ESC[4;38;5;149mAbstractPersistentPicklerTests,
     114                           ESC[4;38;5;149mPersistentPicklerUnpicklerMixin, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     115  
     116      pickler = pickle._Pickler
     117      unpickler = pickle._Unpickler
     118  
     119  
     120  class ESC[4;38;5;81mPyIdPersPicklerTests(ESC[4;38;5;149mAbstractIdentityPersistentPicklerTests,
     121                             ESC[4;38;5;149mPersistentPicklerUnpicklerMixin, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     122  
     123      pickler = pickle._Pickler
     124      unpickler = pickle._Unpickler
     125  
     126      @support.cpython_only
     127      def test_pickler_reference_cycle(self):
     128          def check(Pickler):
     129              for proto in range(pickle.HIGHEST_PROTOCOL + 1):
     130                  f = io.BytesIO()
     131                  pickler = Pickler(f, proto)
     132                  pickler.dump('abc')
     133                  self.assertEqual(self.loads(f.getvalue()), 'abc')
     134              pickler = Pickler(io.BytesIO())
     135              self.assertEqual(pickler.persistent_id('def'), 'def')
     136              r = weakref.ref(pickler)
     137              del pickler
     138              self.assertIsNone(r())
     139  
     140          class ESC[4;38;5;81mPersPickler(ESC[4;38;5;149mselfESC[4;38;5;149m.ESC[4;38;5;149mpickler):
     141              def persistent_id(subself, obj):
     142                  return obj
     143          check(PersPickler)
     144  
     145          class ESC[4;38;5;81mPersPickler(ESC[4;38;5;149mselfESC[4;38;5;149m.ESC[4;38;5;149mpickler):
     146              @classmethod
     147              def persistent_id(cls, obj):
     148                  return obj
     149          check(PersPickler)
     150  
     151          class ESC[4;38;5;81mPersPickler(ESC[4;38;5;149mselfESC[4;38;5;149m.ESC[4;38;5;149mpickler):
     152              @staticmethod
     153              def persistent_id(obj):
     154                  return obj
     155          check(PersPickler)
     156  
     157      @support.cpython_only
     158      def test_custom_pickler_dispatch_table_memleak(self):
     159          # See https://github.com/python/cpython/issues/89988
     160  
     161          class ESC[4;38;5;81mPickler(ESC[4;38;5;149mselfESC[4;38;5;149m.ESC[4;38;5;149mpickler):
     162              def __init__(self, *args, **kwargs):
     163                  self.dispatch_table = table
     164                  super().__init__(*args, **kwargs)
     165  
     166          class ESC[4;38;5;81mDispatchTable:
     167              pass
     168  
     169          table = DispatchTable()
     170          pickler = Pickler(io.BytesIO())
     171          self.assertIs(pickler.dispatch_table, table)
     172          table_ref = weakref.ref(table)
     173          self.assertIsNotNone(table_ref())
     174          del pickler
     175          del table
     176          support.gc_collect()
     177          self.assertIsNone(table_ref())
     178  
     179  
     180      @support.cpython_only
     181      def test_unpickler_reference_cycle(self):
     182          def check(Unpickler):
     183              for proto in range(pickle.HIGHEST_PROTOCOL + 1):
     184                  unpickler = Unpickler(io.BytesIO(self.dumps('abc', proto)))
     185                  self.assertEqual(unpickler.load(), 'abc')
     186              unpickler = Unpickler(io.BytesIO())
     187              self.assertEqual(unpickler.persistent_load('def'), 'def')
     188              r = weakref.ref(unpickler)
     189              del unpickler
     190              self.assertIsNone(r())
     191  
     192          class ESC[4;38;5;81mPersUnpickler(ESC[4;38;5;149mselfESC[4;38;5;149m.ESC[4;38;5;149munpickler):
     193              def persistent_load(subself, pid):
     194                  return pid
     195          check(PersUnpickler)
     196  
     197          class ESC[4;38;5;81mPersUnpickler(ESC[4;38;5;149mselfESC[4;38;5;149m.ESC[4;38;5;149munpickler):
     198              @classmethod
     199              def persistent_load(cls, pid):
     200                  return pid
     201          check(PersUnpickler)
     202  
     203          class ESC[4;38;5;81mPersUnpickler(ESC[4;38;5;149mselfESC[4;38;5;149m.ESC[4;38;5;149munpickler):
     204              @staticmethod
     205              def persistent_load(pid):
     206                  return pid
     207          check(PersUnpickler)
     208  
     209  
     210  class ESC[4;38;5;81mPyPicklerUnpicklerObjectTests(ESC[4;38;5;149mAbstractPicklerUnpicklerObjectTests, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     211  
     212      pickler_class = pickle._Pickler
     213      unpickler_class = pickle._Unpickler
     214  
     215  
     216  class ESC[4;38;5;81mPyDispatchTableTests(ESC[4;38;5;149mAbstractDispatchTableTests, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     217  
     218      pickler_class = pickle._Pickler
     219  
     220      def get_dispatch_table(self):
     221          return pickle.dispatch_table.copy()
     222  
     223  
     224  class ESC[4;38;5;81mPyChainDispatchTableTests(ESC[4;38;5;149mAbstractDispatchTableTests, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     225  
     226      pickler_class = pickle._Pickler
     227  
     228      def get_dispatch_table(self):
     229          return collections.ChainMap({}, pickle.dispatch_table)
     230  
     231  
     232  class ESC[4;38;5;81mPyPicklerHookTests(ESC[4;38;5;149mAbstractHookTests, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     233      class ESC[4;38;5;81mCustomPyPicklerClass(ESC[4;38;5;149mpickleESC[4;38;5;149m.ESC[4;38;5;149m_Pickler,
     234                                 ESC[4;38;5;149mAbstractCustomPicklerClass):
     235          pass
     236      pickler_class = CustomPyPicklerClass
     237  
     238  
     239  if has_c_implementation:
     240      class ESC[4;38;5;81mCPickleTests(ESC[4;38;5;149mAbstractPickleModuleTests, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     241          from _pickle import dump, dumps, load, loads, Pickler, Unpickler
     242  
     243      class ESC[4;38;5;81mCUnpicklerTests(ESC[4;38;5;149mPyUnpicklerTests):
     244          unpickler = _pickle.Unpickler
     245          bad_stack_errors = (pickle.UnpicklingError,)
     246          truncated_errors = (pickle.UnpicklingError,)
     247  
     248      class ESC[4;38;5;81mCPicklerTests(ESC[4;38;5;149mPyPicklerTests):
     249          pickler = _pickle.Pickler
     250          unpickler = _pickle.Unpickler
     251  
     252      class ESC[4;38;5;81mCPersPicklerTests(ESC[4;38;5;149mPyPersPicklerTests):
     253          pickler = _pickle.Pickler
     254          unpickler = _pickle.Unpickler
     255  
     256      class ESC[4;38;5;81mCIdPersPicklerTests(ESC[4;38;5;149mPyIdPersPicklerTests):
     257          pickler = _pickle.Pickler
     258          unpickler = _pickle.Unpickler
     259  
     260      class ESC[4;38;5;81mCDumpPickle_LoadPickle(ESC[4;38;5;149mPyPicklerTests):
     261          pickler = _pickle.Pickler
     262          unpickler = pickle._Unpickler
     263  
     264      class ESC[4;38;5;81mDumpPickle_CLoadPickle(ESC[4;38;5;149mPyPicklerTests):
     265          pickler = pickle._Pickler
     266          unpickler = _pickle.Unpickler
     267  
     268      class ESC[4;38;5;81mCPicklerUnpicklerObjectTests(ESC[4;38;5;149mAbstractPicklerUnpicklerObjectTests, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     269          pickler_class = _pickle.Pickler
     270          unpickler_class = _pickle.Unpickler
     271  
     272          def test_issue18339(self):
     273              unpickler = self.unpickler_class(io.BytesIO())
     274              with self.assertRaises(TypeError):
     275                  unpickler.memo = object
     276              # used to cause a segfault
     277              with self.assertRaises(ValueError):
     278                  unpickler.memo = {-1: None}
     279              unpickler.memo = {1: None}
     280  
     281      class ESC[4;38;5;81mCDispatchTableTests(ESC[4;38;5;149mAbstractDispatchTableTests, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     282          pickler_class = pickle.Pickler
     283          def get_dispatch_table(self):
     284              return pickle.dispatch_table.copy()
     285  
     286      class ESC[4;38;5;81mCChainDispatchTableTests(ESC[4;38;5;149mAbstractDispatchTableTests, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     287          pickler_class = pickle.Pickler
     288          def get_dispatch_table(self):
     289              return collections.ChainMap({}, pickle.dispatch_table)
     290  
     291      class ESC[4;38;5;81mCPicklerHookTests(ESC[4;38;5;149mAbstractHookTests, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     292          class ESC[4;38;5;81mCustomCPicklerClass(ESC[4;38;5;149m_pickleESC[4;38;5;149m.ESC[4;38;5;149mPickler, ESC[4;38;5;149mAbstractCustomPicklerClass):
     293              pass
     294          pickler_class = CustomCPicklerClass
     295  
     296      @support.cpython_only
     297      class ESC[4;38;5;81mHeapTypesTests(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     298          def setUp(self):
     299              pickler = _pickle.Pickler(io.BytesIO())
     300              unpickler = _pickle.Unpickler(io.BytesIO())
     301  
     302              self._types = (
     303                  _pickle.Pickler,
     304                  _pickle.Unpickler,
     305                  type(pickler.memo),
     306                  type(unpickler.memo),
     307  
     308                  # We cannot test the _pickle.Pdata;
     309                  # there's no way to get to it.
     310              )
     311  
     312          def test_have_gc(self):
     313              import gc
     314              for tp in self._types:
     315                  with self.subTest(tp=tp):
     316                      self.assertTrue(gc.is_tracked(tp))
     317  
     318          def test_immutable(self):
     319              for tp in self._types:
     320                  with self.subTest(tp=tp):
     321                      with self.assertRaisesRegex(TypeError, "immutable"):
     322                          tp.foo = "bar"
     323  
     324      @support.cpython_only
     325      class ESC[4;38;5;81mSizeofTests(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     326          check_sizeof = support.check_sizeof
     327  
     328          def test_pickler(self):
     329              basesize = support.calcobjsize('7P2n3i2n3i2P')
     330              p = _pickle.Pickler(io.BytesIO())
     331              self.assertEqual(object.__sizeof__(p), basesize)
     332              MT_size = struct.calcsize('3nP0n')
     333              ME_size = struct.calcsize('Pn0P')
     334              check = self.check_sizeof
     335              check(p, basesize +
     336                  MT_size + 8 * ME_size +  # Minimal memo table size.
     337                  sys.getsizeof(b'x'*4096))  # Minimal write buffer size.
     338              for i in range(6):
     339                  p.dump(chr(i))
     340              check(p, basesize +
     341                  MT_size + 32 * ME_size +  # Size of memo table required to
     342                                            # save references to 6 objects.
     343                  0)  # Write buffer is cleared after every dump().
     344  
     345          def test_unpickler(self):
     346              basesize = support.calcobjsize('2P2n2P 2P2n2i5P 2P3n8P2n2i')
     347              unpickler = _pickle.Unpickler
     348              P = struct.calcsize('P')  # Size of memo table entry.
     349              n = struct.calcsize('n')  # Size of mark table entry.
     350              check = self.check_sizeof
     351              for encoding in 'ASCII', 'UTF-16', 'latin-1':
     352                  for errors in 'strict', 'replace':
     353                      u = unpickler(io.BytesIO(),
     354                                    encoding=encoding, errors=errors)
     355                      self.assertEqual(object.__sizeof__(u), basesize)
     356                      check(u, basesize +
     357                               32 * P +  # Minimal memo table size.
     358                               len(encoding) + 1 + len(errors) + 1)
     359  
     360              stdsize = basesize + len('ASCII') + 1 + len('strict') + 1
     361              def check_unpickler(data, memo_size, marks_size):
     362                  dump = pickle.dumps(data)
     363                  u = unpickler(io.BytesIO(dump),
     364                                encoding='ASCII', errors='strict')
     365                  u.load()
     366                  check(u, stdsize + memo_size * P + marks_size * n)
     367  
     368              check_unpickler(0, 32, 0)
     369              # 20 is minimal non-empty mark stack size.
     370              check_unpickler([0] * 100, 32, 20)
     371              # 128 is memo table size required to save references to 100 objects.
     372              check_unpickler([chr(i) for i in range(100)], 128, 20)
     373              def recurse(deep):
     374                  data = 0
     375                  for i in range(deep):
     376                      data = [data, data]
     377                  return data
     378              check_unpickler(recurse(0), 32, 0)
     379              check_unpickler(recurse(1), 32, 20)
     380              check_unpickler(recurse(20), 32, 20)
     381              check_unpickler(recurse(50), 64, 60)
     382              check_unpickler(recurse(100), 128, 140)
     383  
     384              u = unpickler(io.BytesIO(pickle.dumps('a', 0)),
     385                            encoding='ASCII', errors='strict')
     386              u.load()
     387              check(u, stdsize + 32 * P + 2 + 1)
     388  
     389  
     390  ALT_IMPORT_MAPPING = {
     391      ('_elementtree', 'xml.etree.ElementTree'),
     392      ('cPickle', 'pickle'),
     393      ('StringIO', 'io'),
     394      ('cStringIO', 'io'),
     395  }
     396  
     397  ALT_NAME_MAPPING = {
     398      ('__builtin__', 'basestring', 'builtins', 'str'),
     399      ('exceptions', 'StandardError', 'builtins', 'Exception'),
     400      ('UserDict', 'UserDict', 'collections', 'UserDict'),
     401      ('socket', '_socketobject', 'socket', 'SocketType'),
     402  }
     403  
     404  def mapping(module, name):
     405      if (module, name) in NAME_MAPPING:
     406          module, name = NAME_MAPPING[(module, name)]
     407      elif module in IMPORT_MAPPING:
     408          module = IMPORT_MAPPING[module]
     409      return module, name
     410  
     411  def reverse_mapping(module, name):
     412      if (module, name) in REVERSE_NAME_MAPPING:
     413          module, name = REVERSE_NAME_MAPPING[(module, name)]
     414      elif module in REVERSE_IMPORT_MAPPING:
     415          module = REVERSE_IMPORT_MAPPING[module]
     416      return module, name
     417  
     418  def getmodule(module):
     419      try:
     420          return sys.modules[module]
     421      except KeyError:
     422          try:
     423              with warnings.catch_warnings():
     424                  action = 'always' if support.verbose else 'ignore'
     425                  warnings.simplefilter(action, DeprecationWarning)
     426                  __import__(module)
     427          except AttributeError as exc:
     428              if support.verbose:
     429                  print("Can't import module %r: %s" % (module, exc))
     430              raise ImportError
     431          except ImportError as exc:
     432              if support.verbose:
     433                  print(exc)
     434              raise
     435          return sys.modules[module]
     436  
     437  def getattribute(module, name):
     438      obj = getmodule(module)
     439      for n in name.split('.'):
     440          obj = getattr(obj, n)
     441      return obj
     442  
     443  def get_exceptions(mod):
     444      for name in dir(mod):
     445          attr = getattr(mod, name)
     446          if isinstance(attr, type) and issubclass(attr, BaseException):
     447              yield name, attr
     448  
     449  class ESC[4;38;5;81mCompatPickleTests(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     450      def test_import(self):
     451          modules = set(IMPORT_MAPPING.values())
     452          modules |= set(REVERSE_IMPORT_MAPPING)
     453          modules |= {module for module, name in REVERSE_NAME_MAPPING}
     454          modules |= {module for module, name in NAME_MAPPING.values()}
     455          for module in modules:
     456              try:
     457                  getmodule(module)
     458              except ImportError:
     459                  pass
     460  
     461      def test_import_mapping(self):
     462          for module3, module2 in REVERSE_IMPORT_MAPPING.items():
     463              with self.subTest((module3, module2)):
     464                  try:
     465                      getmodule(module3)
     466                  except ImportError:
     467                      pass
     468                  if module3[:1] != '_':
     469                      self.assertIn(module2, IMPORT_MAPPING)
     470                      self.assertEqual(IMPORT_MAPPING[module2], module3)
     471  
     472      def test_name_mapping(self):
     473          for (module3, name3), (module2, name2) in REVERSE_NAME_MAPPING.items():
     474              with self.subTest(((module3, name3), (module2, name2))):
     475                  if (module2, name2) == ('exceptions', 'OSError'):
     476                      attr = getattribute(module3, name3)
     477                      self.assertTrue(issubclass(attr, OSError))
     478                  elif (module2, name2) == ('exceptions', 'ImportError'):
     479                      attr = getattribute(module3, name3)
     480                      self.assertTrue(issubclass(attr, ImportError))
     481                  else:
     482                      module, name = mapping(module2, name2)
     483                      if module3[:1] != '_':
     484                          self.assertEqual((module, name), (module3, name3))
     485                      try:
     486                          attr = getattribute(module3, name3)
     487                      except ImportError:
     488                          pass
     489                      else:
     490                          self.assertEqual(getattribute(module, name), attr)
     491  
     492      def test_reverse_import_mapping(self):
     493          for module2, module3 in IMPORT_MAPPING.items():
     494              with self.subTest((module2, module3)):
     495                  try:
     496                      getmodule(module3)
     497                  except ImportError as exc:
     498                      if support.verbose:
     499                          print(exc)
     500                  if ((module2, module3) not in ALT_IMPORT_MAPPING and
     501                      REVERSE_IMPORT_MAPPING.get(module3, None) != module2):
     502                      for (m3, n3), (m2, n2) in REVERSE_NAME_MAPPING.items():
     503                          if (module3, module2) == (m3, m2):
     504                              break
     505                      else:
     506                          self.fail('No reverse mapping from %r to %r' %
     507                                    (module3, module2))
     508                  module = REVERSE_IMPORT_MAPPING.get(module3, module3)
     509                  module = IMPORT_MAPPING.get(module, module)
     510                  self.assertEqual(module, module3)
     511  
     512      def test_reverse_name_mapping(self):
     513          for (module2, name2), (module3, name3) in NAME_MAPPING.items():
     514              with self.subTest(((module2, name2), (module3, name3))):
     515                  try:
     516                      attr = getattribute(module3, name3)
     517                  except ImportError:
     518                      pass
     519                  module, name = reverse_mapping(module3, name3)
     520                  if (module2, name2, module3, name3) not in ALT_NAME_MAPPING:
     521                      self.assertEqual((module, name), (module2, name2))
     522                  module, name = mapping(module, name)
     523                  self.assertEqual((module, name), (module3, name3))
     524  
     525      def test_exceptions(self):
     526          self.assertEqual(mapping('exceptions', 'StandardError'),
     527                           ('builtins', 'Exception'))
     528          self.assertEqual(mapping('exceptions', 'Exception'),
     529                           ('builtins', 'Exception'))
     530          self.assertEqual(reverse_mapping('builtins', 'Exception'),
     531                           ('exceptions', 'Exception'))
     532          self.assertEqual(mapping('exceptions', 'OSError'),
     533                           ('builtins', 'OSError'))
     534          self.assertEqual(reverse_mapping('builtins', 'OSError'),
     535                           ('exceptions', 'OSError'))
     536  
     537          for name, exc in get_exceptions(builtins):
     538              with self.subTest(name):
     539                  if exc in (BlockingIOError,
     540                             ResourceWarning,
     541                             StopAsyncIteration,
     542                             RecursionError,
     543                             EncodingWarning,
     544                             BaseExceptionGroup,
     545                             ExceptionGroup):
     546                      continue
     547                  if exc is not OSError and issubclass(exc, OSError):
     548                      self.assertEqual(reverse_mapping('builtins', name),
     549                                       ('exceptions', 'OSError'))
     550                  elif exc is not ImportError and issubclass(exc, ImportError):
     551                      self.assertEqual(reverse_mapping('builtins', name),
     552                                       ('exceptions', 'ImportError'))
     553                      self.assertEqual(mapping('exceptions', name),
     554                                       ('exceptions', name))
     555                  else:
     556                      self.assertEqual(reverse_mapping('builtins', name),
     557                                       ('exceptions', name))
     558                      self.assertEqual(mapping('exceptions', name),
     559                                       ('builtins', name))
     560  
     561      def test_multiprocessing_exceptions(self):
     562          module = import_helper.import_module('multiprocessing.context')
     563          for name, exc in get_exceptions(module):
     564              if issubclass(exc, Warning):
     565                  continue
     566              with self.subTest(name):
     567                  self.assertEqual(reverse_mapping('multiprocessing.context', name),
     568                                   ('multiprocessing', name))
     569                  self.assertEqual(mapping('multiprocessing', name),
     570                                   ('multiprocessing.context', name))
     571  
     572  
     573  def load_tests(loader, tests, pattern):
     574      tests.addTest(doctest.DocTestSuite())
     575      return tests
     576  
     577  
     578  if __name__ == "__main__":
     579      unittest.main()