test_metaclass.py 6.2 KB

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