test_extension.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  2. # not use this file except in compliance with the License. You may obtain
  3. # a copy of the License at
  4. #
  5. # http://www.apache.org/licenses/LICENSE-2.0
  6. #
  7. # Unless required by applicable law or agreed to in writing, software
  8. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  9. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  10. # License for the specific language governing permissions and limitations
  11. # under the License.
  12. """Tests for stevedore.extension
  13. """
  14. import importlib.metadata as importlib_metadata
  15. import operator
  16. from unittest import mock
  17. from stevedore import exception
  18. from stevedore import extension
  19. from stevedore.tests import utils
  20. ALL_NAMES = ['e1', 't1', 't2']
  21. WORKING_NAMES = ['t1', 't2']
  22. class FauxExtension(object):
  23. def __init__(self, *args, **kwds):
  24. self.args = args
  25. self.kwds = kwds
  26. def get_args_and_data(self, data):
  27. return self.args, self.kwds, data
  28. class BrokenExtension(object):
  29. def __init__(self, *args, **kwds):
  30. raise IOError("Did not create")
  31. class TestCallback(utils.TestCase):
  32. def test_detect_plugins(self):
  33. em = extension.ExtensionManager('stevedore.test.extension')
  34. names = sorted(em.names())
  35. self.assertEqual(names, ALL_NAMES)
  36. def test_get_by_name(self):
  37. em = extension.ExtensionManager('stevedore.test.extension')
  38. e = em['t1']
  39. self.assertEqual(e.name, 't1')
  40. def test_list_entry_points(self):
  41. em = extension.ExtensionManager('stevedore.test.extension')
  42. n = em.list_entry_points()
  43. self.assertEqual(set(['e1', 'e2', 't1', 't2']),
  44. set(map(operator.attrgetter("name"), n)))
  45. self.assertEqual(4, len(n))
  46. def test_list_entry_points_names(self):
  47. em = extension.ExtensionManager('stevedore.test.extension')
  48. names = em.entry_points_names()
  49. self.assertEqual(set(['e1', 'e2', 't1', 't2']), set(names))
  50. self.assertEqual(4, len(names))
  51. def test_contains_by_name(self):
  52. em = extension.ExtensionManager('stevedore.test.extension')
  53. self.assertEqual('t1' in em, True)
  54. def test_get_by_name_missing(self):
  55. em = extension.ExtensionManager('stevedore.test.extension')
  56. try:
  57. em['t3']
  58. except KeyError:
  59. pass
  60. else:
  61. assert False, 'Failed to raise KeyError'
  62. def test_load_multiple_times_entry_points(self):
  63. # We expect to get the same EntryPoint object because we save them
  64. # in the cache.
  65. em1 = extension.ExtensionManager('stevedore.test.extension')
  66. eps1 = [ext.entry_point for ext in em1]
  67. em2 = extension.ExtensionManager('stevedore.test.extension')
  68. eps2 = [ext.entry_point for ext in em2]
  69. self.assertIs(eps1[0], eps2[0])
  70. def test_load_multiple_times_plugins(self):
  71. # We expect to get the same plugin object (module or class)
  72. # because the underlying import machinery will cache the values.
  73. em1 = extension.ExtensionManager('stevedore.test.extension')
  74. plugins1 = [ext.plugin for ext in em1]
  75. em2 = extension.ExtensionManager('stevedore.test.extension')
  76. plugins2 = [ext.plugin for ext in em2]
  77. self.assertIs(plugins1[0], plugins2[0])
  78. def test_use_cache(self):
  79. # If we insert something into the cache of entry points,
  80. # the manager should not have to call into entrypoints
  81. # to find the plugins.
  82. cache = extension.ExtensionManager.ENTRY_POINT_CACHE
  83. cache['stevedore.test.faux'] = []
  84. with mock.patch('stevedore._cache.get_group_all',
  85. side_effect=
  86. AssertionError('called get_group_all')):
  87. em = extension.ExtensionManager('stevedore.test.faux')
  88. names = em.names()
  89. self.assertEqual(names, [])
  90. def test_iterable(self):
  91. em = extension.ExtensionManager('stevedore.test.extension')
  92. names = sorted(e.name for e in em)
  93. self.assertEqual(names, ALL_NAMES)
  94. def test_invoke_on_load(self):
  95. em = extension.ExtensionManager('stevedore.test.extension',
  96. invoke_on_load=True,
  97. invoke_args=('a',),
  98. invoke_kwds={'b': 'B'},
  99. )
  100. self.assertEqual(len(em.extensions), 2)
  101. for e in em.extensions:
  102. self.assertEqual(e.obj.args, ('a',))
  103. self.assertEqual(e.obj.kwds, {'b': 'B'})
  104. def test_map_return_values(self):
  105. def mapped(ext, *args, **kwds):
  106. return ext.name
  107. em = extension.ExtensionManager('stevedore.test.extension',
  108. invoke_on_load=True,
  109. )
  110. results = em.map(mapped)
  111. self.assertEqual(sorted(results), WORKING_NAMES)
  112. def test_map_arguments(self):
  113. objs = []
  114. def mapped(ext, *args, **kwds):
  115. objs.append((ext, args, kwds))
  116. em = extension.ExtensionManager('stevedore.test.extension',
  117. invoke_on_load=True,
  118. )
  119. em.map(mapped, 1, 2, a='A', b='B')
  120. self.assertEqual(len(objs), 2)
  121. names = sorted([o[0].name for o in objs])
  122. self.assertEqual(names, WORKING_NAMES)
  123. for o in objs:
  124. self.assertEqual(o[1], (1, 2))
  125. self.assertEqual(o[2], {'a': 'A', 'b': 'B'})
  126. def test_map_eats_errors(self):
  127. def mapped(ext, *args, **kwds):
  128. raise RuntimeError('hard coded error')
  129. em = extension.ExtensionManager('stevedore.test.extension',
  130. invoke_on_load=True,
  131. )
  132. results = em.map(mapped, 1, 2, a='A', b='B')
  133. self.assertEqual(results, [])
  134. def test_map_propagate_exceptions(self):
  135. def mapped(ext, *args, **kwds):
  136. raise RuntimeError('hard coded error')
  137. em = extension.ExtensionManager('stevedore.test.extension',
  138. invoke_on_load=True,
  139. propagate_map_exceptions=True
  140. )
  141. try:
  142. em.map(mapped, 1, 2, a='A', b='B')
  143. assert False
  144. except RuntimeError:
  145. pass
  146. def test_map_errors_when_no_plugins(self):
  147. expected_str = 'No stevedore.test.extension.none extensions found'
  148. def mapped(ext, *args, **kwds):
  149. pass
  150. em = extension.ExtensionManager('stevedore.test.extension.none',
  151. invoke_on_load=True,
  152. )
  153. try:
  154. em.map(mapped, 1, 2, a='A', b='B')
  155. except exception.NoMatches as err:
  156. self.assertEqual(expected_str, str(err))
  157. def test_map_method(self):
  158. em = extension.ExtensionManager('stevedore.test.extension',
  159. invoke_on_load=True,
  160. )
  161. result = em.map_method('get_args_and_data', 42)
  162. self.assertEqual(set(r[2] for r in result), set([42]))
  163. def test_items(self):
  164. em = extension.ExtensionManager('stevedore.test.extension')
  165. expected_output = set([(name, em[name]) for name in ALL_NAMES])
  166. self.assertEqual(expected_output, set(em.items()))
  167. class TestLoadRequirementsNewSetuptools(utils.TestCase):
  168. # setuptools 11.3 and later
  169. def setUp(self):
  170. super(TestLoadRequirementsNewSetuptools, self).setUp()
  171. self.mock_ep = mock.Mock(spec=['require', 'resolve', 'load', 'name'])
  172. self.em = extension.ExtensionManager.make_test_instance([])
  173. def test_verify_requirements(self):
  174. self.em._load_one_plugin(self.mock_ep, False, (), {},
  175. verify_requirements=True)
  176. self.mock_ep.require.assert_called_once_with()
  177. self.mock_ep.resolve.assert_called_once_with()
  178. def test_no_verify_requirements(self):
  179. self.em._load_one_plugin(self.mock_ep, False, (), {},
  180. verify_requirements=False)
  181. self.assertEqual(0, self.mock_ep.require.call_count)
  182. self.mock_ep.resolve.assert_called_once_with()
  183. class TestLoadRequirementsOldSetuptools(utils.TestCase):
  184. # Before setuptools 11.3
  185. def setUp(self):
  186. super(TestLoadRequirementsOldSetuptools, self).setUp()
  187. self.mock_ep = mock.Mock(spec=['load', 'name'])
  188. self.em = extension.ExtensionManager.make_test_instance([])
  189. def test_verify_requirements(self):
  190. self.em._load_one_plugin(self.mock_ep, False, (), {},
  191. verify_requirements=True)
  192. self.mock_ep.load.assert_called_once_with()
  193. def test_no_verify_requirements(self):
  194. self.em._load_one_plugin(self.mock_ep, False, (), {},
  195. verify_requirements=False)
  196. self.mock_ep.load.assert_called_once_with()
  197. class TestExtensionProperties(utils.TestCase):
  198. def setUp(self):
  199. self.ext1 = extension.Extension(
  200. 'name',
  201. importlib_metadata.EntryPoint(
  202. 'name', 'module.name:attribute.name [extra]', 'group_name',
  203. ),
  204. mock.Mock(),
  205. None,
  206. )
  207. self.ext2 = extension.Extension(
  208. 'name',
  209. importlib_metadata.EntryPoint(
  210. 'name', 'module:attribute', 'group_name',
  211. ),
  212. mock.Mock(),
  213. None,
  214. )
  215. def test_module_name(self):
  216. self.assertEqual('module.name', self.ext1.module_name)
  217. self.assertEqual('module', self.ext2.module_name)
  218. def test_attr(self):
  219. self.assertEqual('attribute.name', self.ext1.attr)
  220. self.assertEqual('attribute', self.ext2.attr)
  221. def test_entry_point_target(self):
  222. self.assertEqual('module.name:attribute.name [extra]',
  223. self.ext1.entry_point_target)
  224. self.assertEqual('module:attribute',
  225. self.ext2.entry_point_target)