named.py 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  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. import logging
  13. from .extension import ExtensionManager
  14. LOG = logging.getLogger(__name__)
  15. class NamedExtensionManager(ExtensionManager):
  16. """Loads only the named extensions.
  17. This is useful for explicitly enabling extensions in a
  18. configuration file, for example.
  19. :param namespace: The namespace for the entry points.
  20. :type namespace: str
  21. :param names: The names of the extensions to load.
  22. :type names: list(str)
  23. :param invoke_on_load: Boolean controlling whether to invoke the
  24. object returned by the entry point after the driver is loaded.
  25. :type invoke_on_load: bool
  26. :param invoke_args: Positional arguments to pass when invoking
  27. the object returned by the entry point. Only used if invoke_on_load
  28. is True.
  29. :type invoke_args: tuple
  30. :param invoke_kwds: Named arguments to pass when invoking
  31. the object returned by the entry point. Only used if invoke_on_load
  32. is True.
  33. :type invoke_kwds: dict
  34. :param name_order: If true, sort the loaded extensions to match the
  35. order used in ``names``.
  36. :type name_order: bool
  37. :param propagate_map_exceptions: Boolean controlling whether exceptions
  38. are propagated up through the map call or whether they are logged and
  39. then ignored
  40. :type propagate_map_exceptions: bool
  41. :param on_load_failure_callback: Callback function that will be called when
  42. an entrypoint can not be loaded. The arguments that will be provided
  43. when this is called (when an entrypoint fails to load) are
  44. (manager, entrypoint, exception)
  45. :type on_load_failure_callback: function
  46. :param on_missing_entrypoints_callback: Callback function that will be
  47. called when one or more names cannot be found. The provided argument
  48. will be a subset of the 'names' parameter.
  49. :type on_missing_entrypoints_callback: function
  50. :param verify_requirements: Use setuptools to enforce the
  51. dependencies of the plugin(s) being loaded. Defaults to False.
  52. :type verify_requirements: bool
  53. :param warn_on_missing_entrypoint: Flag to control whether failing
  54. to load a plugin is reported via a log mess. Only applies if
  55. on_missing_entrypoints_callback is None.
  56. :type warn_on_missing_entrypoint: bool
  57. """
  58. def __init__(self, namespace, names,
  59. invoke_on_load=False, invoke_args=(), invoke_kwds={},
  60. name_order=False, propagate_map_exceptions=False,
  61. on_load_failure_callback=None,
  62. on_missing_entrypoints_callback=None,
  63. verify_requirements=False,
  64. warn_on_missing_entrypoint=True):
  65. self._init_attributes(
  66. namespace, names, name_order=name_order,
  67. propagate_map_exceptions=propagate_map_exceptions,
  68. on_load_failure_callback=on_load_failure_callback)
  69. extensions = self._load_plugins(invoke_on_load,
  70. invoke_args,
  71. invoke_kwds,
  72. verify_requirements)
  73. self._missing_names = set(names) - set([e.name for e in extensions])
  74. if self._missing_names:
  75. if on_missing_entrypoints_callback:
  76. on_missing_entrypoints_callback(self._missing_names)
  77. elif warn_on_missing_entrypoint:
  78. LOG.warning('Could not load %s' %
  79. ', '.join(self._missing_names))
  80. self._init_plugins(extensions)
  81. @classmethod
  82. def make_test_instance(cls, extensions, namespace='TESTING',
  83. propagate_map_exceptions=False,
  84. on_load_failure_callback=None,
  85. verify_requirements=False):
  86. """Construct a test NamedExtensionManager
  87. Test instances are passed a list of extensions to use rather than
  88. loading them from entry points.
  89. :param extensions: Pre-configured Extension instances
  90. :type extensions: list of :class:`~stevedore.extension.Extension`
  91. :param namespace: The namespace for the manager; used only for
  92. identification since the extensions are passed in.
  93. :type namespace: str
  94. :param propagate_map_exceptions: Boolean controlling whether exceptions
  95. are propagated up through the map call or whether they are logged
  96. and then ignored
  97. :type propagate_map_exceptions: bool
  98. :param on_load_failure_callback: Callback function that will
  99. be called when an entrypoint can not be loaded. The
  100. arguments that will be provided when this is called (when
  101. an entrypoint fails to load) are (manager, entrypoint,
  102. exception)
  103. :type on_load_failure_callback: function
  104. :param verify_requirements: Use setuptools to enforce the
  105. dependencies of the plugin(s) being loaded. Defaults to False.
  106. :type verify_requirements: bool
  107. :return: The manager instance, initialized for testing
  108. """
  109. o = cls.__new__(cls)
  110. names = [e.name for e in extensions]
  111. o._init_attributes(namespace, names,
  112. propagate_map_exceptions=propagate_map_exceptions,
  113. on_load_failure_callback=on_load_failure_callback)
  114. o._init_plugins(extensions)
  115. return o
  116. def _init_attributes(self, namespace, names, name_order=False,
  117. propagate_map_exceptions=False,
  118. on_load_failure_callback=None):
  119. super(NamedExtensionManager, self)._init_attributes(
  120. namespace, propagate_map_exceptions=propagate_map_exceptions,
  121. on_load_failure_callback=on_load_failure_callback)
  122. self._names = names
  123. self._missing_names = set()
  124. self._name_order = name_order
  125. def _init_plugins(self, extensions):
  126. super(NamedExtensionManager, self)._init_plugins(extensions)
  127. if self._name_order:
  128. self.extensions = [self[n] for n in self._names
  129. if n not in self._missing_names]
  130. def _load_one_plugin(self, ep, invoke_on_load, invoke_args, invoke_kwds,
  131. verify_requirements):
  132. # Check the name before going any further to prevent
  133. # undesirable code from being loaded at all if we are not
  134. # going to use it.
  135. if ep.name not in self._names:
  136. return None
  137. return super(NamedExtensionManager, self)._load_one_plugin(
  138. ep, invoke_on_load, invoke_args, invoke_kwds,
  139. verify_requirements,
  140. )