test_advice.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. ##############################################################################
  2. #
  3. # Copyright (c) 2003 Zope Foundation and Contributors.
  4. # All Rights Reserved.
  5. #
  6. # This software is subject to the provisions of the Zope Public License,
  7. # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
  8. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
  9. # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  10. # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
  11. # FOR A PARTICULAR PURPOSE.
  12. #
  13. ##############################################################################
  14. """Tests for advice
  15. This module was adapted from 'protocols.tests.advice', part of the Python
  16. Enterprise Application Kit (PEAK). Please notify the PEAK authors
  17. (pje@telecommunity.com and tsarna@sarna.org) if bugs are found or
  18. Zope-specific changes are required, so that the PEAK version of this module
  19. can be kept in sync.
  20. PEAK is a Python application framework that interoperates with (but does
  21. not require) Zope 3 and Twisted. It provides tools for manipulating UML
  22. models, object-relational persistence, aspect-oriented programming, and more.
  23. Visit the PEAK home page at http://peak.telecommunity.com for more information.
  24. """
  25. import unittest
  26. import sys
  27. from zope.interface._compat import _skip_under_py2
  28. from zope.interface._compat import _skip_under_py3k
  29. class FrameInfoTest(unittest.TestCase):
  30. def test_w_module(self):
  31. from . import advisory_testing
  32. (kind, module,
  33. f_locals, f_globals) = advisory_testing.moduleLevelFrameInfo
  34. self.assertEqual(kind, "module")
  35. for d in module.__dict__, f_locals, f_globals:
  36. self.assertTrue(d is advisory_testing.my_globals)
  37. @_skip_under_py3k
  38. def test_w_ClassicClass(self):
  39. from . import advisory_testing
  40. (kind,
  41. module,
  42. f_locals,
  43. f_globals) = advisory_testing.ClassicClass.classLevelFrameInfo
  44. self.assertEqual(kind, "class")
  45. self.assertTrue(
  46. f_locals is advisory_testing.ClassicClass.__dict__) # ???
  47. for d in module.__dict__, f_globals:
  48. self.assertTrue(d is advisory_testing.my_globals)
  49. def test_w_NewStyleClass(self):
  50. from . import advisory_testing
  51. (kind,
  52. module,
  53. f_locals,
  54. f_globals) = advisory_testing.NewStyleClass.classLevelFrameInfo
  55. self.assertEqual(kind, "class")
  56. for d in module.__dict__, f_globals:
  57. self.assertTrue(d is advisory_testing.my_globals)
  58. def test_inside_function_call(self):
  59. from zope.interface.advice import getFrameInfo
  60. kind, module, f_locals, f_globals = getFrameInfo(sys._getframe())
  61. self.assertEqual(kind, "function call")
  62. self.assertTrue(f_locals is locals()) # ???
  63. for d in module.__dict__, f_globals:
  64. self.assertTrue(d is globals())
  65. def test_inside_exec(self):
  66. from zope.interface.advice import getFrameInfo
  67. _globals = {'getFrameInfo': getFrameInfo}
  68. _locals = {}
  69. exec(_FUNKY_EXEC, _globals, _locals)
  70. self.assertEqual(_locals['kind'], "exec")
  71. self.assertTrue(_locals['f_locals'] is _locals)
  72. self.assertTrue(_locals['module'] is None)
  73. self.assertTrue(_locals['f_globals'] is _globals)
  74. _FUNKY_EXEC = """\
  75. import sys
  76. kind, module, f_locals, f_globals = getFrameInfo(sys._getframe())
  77. """
  78. class AdviceTests(unittest.TestCase):
  79. @_skip_under_py3k
  80. def test_order(self):
  81. from .advisory_testing import ping
  82. log = []
  83. class Foo(object):
  84. ping(log, 1)
  85. ping(log, 2)
  86. ping(log, 3)
  87. # Strip the list nesting
  88. for i in 1, 2, 3:
  89. self.assertTrue(isinstance(Foo, list))
  90. Foo, = Foo
  91. self.assertEqual(log, [(1, Foo), (2, [Foo]), (3, [[Foo]])])
  92. @_skip_under_py3k
  93. def test_single_explicit_meta(self):
  94. from .advisory_testing import ping
  95. class Metaclass(type):
  96. pass
  97. class Concrete(Metaclass):
  98. __metaclass__ = Metaclass
  99. ping([],1)
  100. Concrete, = Concrete
  101. self.assertTrue(Concrete.__class__ is Metaclass)
  102. @_skip_under_py3k
  103. def test_mixed_metas(self):
  104. from .advisory_testing import ping
  105. class Metaclass1(type):
  106. pass
  107. class Metaclass2(type):
  108. pass
  109. class Base1:
  110. __metaclass__ = Metaclass1
  111. class Base2:
  112. __metaclass__ = Metaclass2
  113. try:
  114. class Derived(Base1, Base2):
  115. ping([], 1)
  116. self.fail("Should have gotten incompatibility error")
  117. except TypeError:
  118. pass
  119. class Metaclass3(Metaclass1, Metaclass2):
  120. pass
  121. class Derived(Base1, Base2):
  122. __metaclass__ = Metaclass3
  123. ping([], 1)
  124. self.assertTrue(isinstance(Derived, list))
  125. Derived, = Derived
  126. self.assertTrue(isinstance(Derived, Metaclass3))
  127. @_skip_under_py3k
  128. def test_meta_no_bases(self):
  129. from .advisory_testing import ping
  130. from types import ClassType
  131. class Thing:
  132. ping([], 1)
  133. klass, = Thing # unpack list created by pong
  134. self.assertEqual(type(klass), ClassType)
  135. class Test_isClassAdvisor(unittest.TestCase):
  136. def _callFUT(self, *args, **kw):
  137. from zope.interface.advice import isClassAdvisor
  138. return isClassAdvisor(*args, **kw)
  139. def test_w_non_function(self):
  140. self.assertEqual(self._callFUT(self), False)
  141. def test_w_normal_function(self):
  142. def foo():
  143. raise NotImplementedError()
  144. self.assertEqual(self._callFUT(foo), False)
  145. def test_w_advisor_function(self):
  146. def bar():
  147. raise NotImplementedError()
  148. bar.previousMetaclass = object()
  149. self.assertEqual(self._callFUT(bar), True)
  150. class Test_determineMetaclass(unittest.TestCase):
  151. def _callFUT(self, *args, **kw):
  152. from zope.interface.advice import determineMetaclass
  153. return determineMetaclass(*args, **kw)
  154. @_skip_under_py3k
  155. def test_empty(self):
  156. from types import ClassType
  157. self.assertEqual(self._callFUT(()), ClassType)
  158. def test_empty_w_explicit_metatype(self):
  159. class Meta(type):
  160. pass
  161. self.assertEqual(self._callFUT((), Meta), Meta)
  162. def test_single(self):
  163. class Meta(type):
  164. pass
  165. self.assertEqual(self._callFUT((Meta,)), type)
  166. @_skip_under_py3k
  167. def test_meta_of_class(self):
  168. class Metameta(type):
  169. pass
  170. class Meta(type):
  171. __metaclass__ = Metameta
  172. self.assertEqual(self._callFUT((Meta, type)), Metameta)
  173. @_skip_under_py2
  174. def test_meta_of_class_py3k(self):
  175. # Work around SyntaxError under Python2.
  176. EXEC = '\n'.join([
  177. 'class Metameta(type):',
  178. ' pass',
  179. 'class Meta(type, metaclass=Metameta):',
  180. ' pass',
  181. ])
  182. globs = {}
  183. exec(EXEC, globs)
  184. Meta = globs['Meta']
  185. Metameta = globs['Metameta']
  186. self.assertEqual(self._callFUT((Meta, type)), Metameta)
  187. @_skip_under_py3k
  188. def test_multiple_in_hierarchy(self):
  189. class Meta_A(type):
  190. pass
  191. class Meta_B(Meta_A):
  192. pass
  193. class A(type):
  194. __metaclass__ = Meta_A
  195. class B(type):
  196. __metaclass__ = Meta_B
  197. self.assertEqual(self._callFUT((A, B,)), Meta_B)
  198. @_skip_under_py2
  199. def test_multiple_in_hierarchy_py3k(self):
  200. # Work around SyntaxError under Python2.
  201. EXEC = '\n'.join([
  202. 'class Meta_A(type):',
  203. ' pass',
  204. 'class Meta_B(Meta_A):',
  205. ' pass',
  206. 'class A(type, metaclass=Meta_A):',
  207. ' pass',
  208. 'class B(type, metaclass=Meta_B):',
  209. ' pass',
  210. ])
  211. globs = {}
  212. exec(EXEC, globs)
  213. Meta_A = globs['Meta_A']
  214. Meta_B = globs['Meta_B']
  215. A = globs['A']
  216. B = globs['B']
  217. self.assertEqual(self._callFUT((A, B)), Meta_B)
  218. @_skip_under_py3k
  219. def test_multiple_not_in_hierarchy(self):
  220. class Meta_A(type):
  221. pass
  222. class Meta_B(type):
  223. pass
  224. class A(type):
  225. __metaclass__ = Meta_A
  226. class B(type):
  227. __metaclass__ = Meta_B
  228. self.assertRaises(TypeError, self._callFUT, (A, B,))
  229. @_skip_under_py2
  230. def test_multiple_not_in_hierarchy_py3k(self):
  231. # Work around SyntaxError under Python2.
  232. EXEC = '\n'.join([
  233. 'class Meta_A(type):',
  234. ' pass',
  235. 'class Meta_B(type):',
  236. ' pass',
  237. 'class A(type, metaclass=Meta_A):',
  238. ' pass',
  239. 'class B(type, metaclass=Meta_B):',
  240. ' pass',
  241. ])
  242. globs = {}
  243. exec(EXEC, globs)
  244. Meta_A = globs['Meta_A']
  245. Meta_B = globs['Meta_B']
  246. A = globs['A']
  247. B = globs['B']
  248. self.assertRaises(TypeError, self._callFUT, (A, B))
  249. class Test_minimalBases(unittest.TestCase):
  250. def _callFUT(self, klasses):
  251. from zope.interface.advice import minimalBases
  252. return minimalBases(klasses)
  253. def test_empty(self):
  254. self.assertEqual(self._callFUT([]), [])
  255. @_skip_under_py3k
  256. def test_w_oldstyle_meta(self):
  257. class C:
  258. pass
  259. self.assertEqual(self._callFUT([type(C)]), [])
  260. @_skip_under_py3k
  261. def test_w_oldstyle_class(self):
  262. class C:
  263. pass
  264. self.assertEqual(self._callFUT([C]), [C])
  265. def test_w_newstyle_meta(self):
  266. self.assertEqual(self._callFUT([type]), [type])
  267. def test_w_newstyle_class(self):
  268. class C(object):
  269. pass
  270. self.assertEqual(self._callFUT([C]), [C])
  271. def test_simple_hierarchy_skips_implied(self):
  272. class A(object):
  273. pass
  274. class B(A):
  275. pass
  276. class C(B):
  277. pass
  278. class D(object):
  279. pass
  280. self.assertEqual(self._callFUT([A, B, C]), [C])
  281. self.assertEqual(self._callFUT([A, C]), [C])
  282. self.assertEqual(self._callFUT([B, C]), [C])
  283. self.assertEqual(self._callFUT([A, B]), [B])
  284. self.assertEqual(self._callFUT([D, B, D]), [B, D])
  285. def test_repeats_kicked_to_end_of_queue(self):
  286. class A(object):
  287. pass
  288. class B(object):
  289. pass
  290. self.assertEqual(self._callFUT([A, B, A]), [B, A])