(root)/
Python-3.11.7/
Lib/
test/
test_metaclass.py
       1  import doctest
       2  import unittest
       3  
       4  
       5  doctests = """
       6  
       7  Basic class construction.
       8  
       9      >>> class C:
      10      ...     def meth(self): print("Hello")
      11      ...
      12      >>> C.__class__ is type
      13      True
      14      >>> a = C()
      15      >>> a.__class__ is C
      16      True
      17      >>> a.meth()
      18      Hello
      19      >>>
      20  
      21  Use *args notation for the bases.
      22  
      23      >>> class A: pass
      24      >>> class B: pass
      25      >>> bases = (A, B)
      26      >>> class C(*bases): pass
      27      >>> C.__bases__ == bases
      28      True
      29      >>>
      30  
      31  Use a trivial metaclass.
      32  
      33      >>> class M(type):
      34      ...     pass
      35      ...
      36      >>> class C(metaclass=M):
      37      ...    def meth(self): print("Hello")
      38      ...
      39      >>> C.__class__ is M
      40      True
      41      >>> a = C()
      42      >>> a.__class__ is C
      43      True
      44      >>> a.meth()
      45      Hello
      46      >>>
      47  
      48  Use **kwds notation for the metaclass keyword.
      49  
      50      >>> kwds = {'metaclass': M}
      51      >>> class C(**kwds): pass
      52      ...
      53      >>> C.__class__ is M
      54      True
      55      >>> a = C()
      56      >>> a.__class__ is C
      57      True
      58      >>>
      59  
      60  Use a metaclass with a __prepare__ static method.
      61  
      62      >>> class M(type):
      63      ...    @staticmethod
      64      ...    def __prepare__(*args, **kwds):
      65      ...        print("Prepare called:", args, kwds)
      66      ...        return dict()
      67      ...    def __new__(cls, name, bases, namespace, **kwds):
      68      ...        print("New called:", kwds)
      69      ...        return type.__new__(cls, name, bases, namespace)
      70      ...    def __init__(cls, *args, **kwds):
      71      ...        pass
      72      ...
      73      >>> class C(metaclass=M):
      74      ...     def meth(self): print("Hello")
      75      ...
      76      Prepare called: ('C', ()) {}
      77      New called: {}
      78      >>>
      79  
      80  Also pass another keyword.
      81  
      82      >>> class C(object, metaclass=M, other="haha"):
      83      ...     pass
      84      ...
      85      Prepare called: ('C', (<class 'object'>,)) {'other': 'haha'}
      86      New called: {'other': 'haha'}
      87      >>> C.__class__ is M
      88      True
      89      >>> C.__bases__ == (object,)
      90      True
      91      >>> a = C()
      92      >>> a.__class__ is C
      93      True
      94      >>>
      95  
      96  Check that build_class doesn't mutate the kwds dict.
      97  
      98      >>> kwds = {'metaclass': type}
      99      >>> class C(**kwds): pass
     100      ...
     101      >>> kwds == {'metaclass': type}
     102      True
     103      >>>
     104  
     105  Use various combinations of explicit keywords and **kwds.
     106  
     107      >>> bases = (object,)
     108      >>> kwds = {'metaclass': M, 'other': 'haha'}
     109      >>> class C(*bases, **kwds): pass
     110      ...
     111      Prepare called: ('C', (<class 'object'>,)) {'other': 'haha'}
     112      New called: {'other': 'haha'}
     113      >>> C.__class__ is M
     114      True
     115      >>> C.__bases__ == (object,)
     116      True
     117      >>> class B: pass
     118      >>> kwds = {'other': 'haha'}
     119      >>> class C(B, metaclass=M, *bases, **kwds): pass
     120      ...
     121      Prepare called: ('C', (<class 'test.test_metaclass.B'>, <class 'object'>)) {'other': 'haha'}
     122      New called: {'other': 'haha'}
     123      >>> C.__class__ is M
     124      True
     125      >>> C.__bases__ == (B, object)
     126      True
     127      >>>
     128  
     129  Check for duplicate keywords.
     130  
     131      >>> class C(metaclass=type, metaclass=type): pass
     132      ...
     133      Traceback (most recent call last):
     134      [...]
     135      SyntaxError: keyword argument repeated: metaclass
     136      >>>
     137  
     138  Another way.
     139  
     140      >>> kwds = {'metaclass': type}
     141      >>> class C(metaclass=type, **kwds): pass
     142      ...
     143      Traceback (most recent call last):
     144      [...]
     145      TypeError: __build_class__() got multiple values for keyword argument 'metaclass'
     146      >>>
     147  
     148  Use a __prepare__ method that returns an instrumented dict.
     149  
     150      >>> class LoggingDict(dict):
     151      ...     def __setitem__(self, key, value):
     152      ...         print("d[%r] = %r" % (key, value))
     153      ...         dict.__setitem__(self, key, value)
     154      ...
     155      >>> class Meta(type):
     156      ...    @staticmethod
     157      ...    def __prepare__(name, bases):
     158      ...        return LoggingDict()
     159      ...
     160      >>> class C(metaclass=Meta):
     161      ...     foo = 2+2
     162      ...     foo = 42
     163      ...     bar = 123
     164      ...
     165      d['__module__'] = 'test.test_metaclass'
     166      d['__qualname__'] = 'C'
     167      d['foo'] = 4
     168      d['foo'] = 42
     169      d['bar'] = 123
     170      >>>
     171  
     172  Use a metaclass that doesn't derive from type.
     173  
     174      >>> def meta(name, bases, namespace, **kwds):
     175      ...     print("meta:", name, bases)
     176      ...     print("ns:", sorted(namespace.items()))
     177      ...     print("kw:", sorted(kwds.items()))
     178      ...     return namespace
     179      ...
     180      >>> class C(metaclass=meta):
     181      ...     a = 42
     182      ...     b = 24
     183      ...
     184      meta: C ()
     185      ns: [('__module__', 'test.test_metaclass'), ('__qualname__', 'C'), ('a', 42), ('b', 24)]
     186      kw: []
     187      >>> type(C) is dict
     188      True
     189      >>> print(sorted(C.items()))
     190      [('__module__', 'test.test_metaclass'), ('__qualname__', 'C'), ('a', 42), ('b', 24)]
     191      >>>
     192  
     193  And again, with a __prepare__ attribute.
     194  
     195      >>> def prepare(name, bases, **kwds):
     196      ...     print("prepare:", name, bases, sorted(kwds.items()))
     197      ...     return LoggingDict()
     198      ...
     199      >>> meta.__prepare__ = prepare
     200      >>> class C(metaclass=meta, other="booh"):
     201      ...    a = 1
     202      ...    a = 2
     203      ...    b = 3
     204      ...
     205      prepare: C () [('other', 'booh')]
     206      d['__module__'] = 'test.test_metaclass'
     207      d['__qualname__'] = 'C'
     208      d['a'] = 1
     209      d['a'] = 2
     210      d['b'] = 3
     211      meta: C ()
     212      ns: [('__module__', 'test.test_metaclass'), ('__qualname__', 'C'), ('a', 2), ('b', 3)]
     213      kw: [('other', 'booh')]
     214      >>>
     215  
     216  The default metaclass must define a __prepare__() method.
     217  
     218      >>> type.__prepare__()
     219      {}
     220      >>>
     221  
     222  Make sure it works with subclassing.
     223  
     224      >>> class M(type):
     225      ...     @classmethod
     226      ...     def __prepare__(cls, *args, **kwds):
     227      ...         d = super().__prepare__(*args, **kwds)
     228      ...         d["hello"] = 42
     229      ...         return d
     230      ...
     231      >>> class C(metaclass=M):
     232      ...     print(hello)
     233      ...
     234      42
     235      >>> print(C.hello)
     236      42
     237      >>>
     238  
     239  Test failures in looking up the __prepare__ method work.
     240      >>> class ObscureException(Exception):
     241      ...     pass
     242      >>> class FailDescr:
     243      ...     def __get__(self, instance, owner):
     244      ...        raise ObscureException
     245      >>> class Meta(type):
     246      ...     __prepare__ = FailDescr()
     247      >>> class X(metaclass=Meta):
     248      ...     pass
     249      Traceback (most recent call last):
     250      [...]
     251      test.test_metaclass.ObscureException
     252  
     253  """
     254  
     255  import sys
     256  
     257  # Trace function introduces __locals__ which causes various tests to fail.
     258  if hasattr(sys, 'gettrace') and sys.gettrace():
     259      __test__ = {}
     260  else:
     261      __test__ = {'doctests' : doctests}
     262  
     263  def load_tests(loader, tests, pattern):
     264      tests.addTest(doctest.DocTestSuite())
     265      return tests
     266  
     267  
     268  if __name__ == "__main__":
     269      unittest.main()