blob: 0db091fd92dab3a26311884345f1c0fecacca6c6 [file] [log] [blame]
#
# Copyright (C) 2020 Codethink Limited
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
#
import os
from .._exceptions import PluginError
from .pluginorigin import PluginType, PluginOrigin, PluginOriginType
# PluginOriginPip
#
# PluginOrigin for pip plugins
#
class PluginOriginPip(PluginOrigin):
def __init__(self):
super().__init__(PluginOriginType.PIP)
# The pip package name to extract plugins from
#
self._package_name = None
def get_plugin_paths(self, kind, plugin_type):
import pkg_resources
# Sources and elements are looked up in separate
# entrypoint groups from the same package.
#
if plugin_type == PluginType.SOURCE:
entrypoint_group = "buildstream.plugins.sources"
elif plugin_type == PluginType.ELEMENT:
entrypoint_group = "buildstream.plugins.elements"
# key by a tuple to avoid collision
try:
package = pkg_resources.get_entry_info(self._package_name, entrypoint_group, kind)
except pkg_resources.DistributionNotFound as e:
raise PluginError(
"{}: Failed to load {} plugin '{}': {}".format(
self.provenance_node.get_provenance(), plugin_type, kind, e
),
reason="package-not-found",
) from e
except pkg_resources.VersionConflict as e:
raise PluginError(
"{}: Version conflict encountered while loading {} plugin '{}'".format(
self.provenance_node.get_provenance(), plugin_type, kind
),
detail=e.report(),
reason="package-version-conflict",
) from e
except (
# For setuptools < 49.0.0
pkg_resources.RequirementParseError,
# For setuptools >= 49.0.0
pkg_resources.extern.packaging.requirements.InvalidRequirement,
) as e:
raise PluginError(
"{}: Malformed package-name '{}' encountered: {}".format(
self.provenance_node.get_provenance(), self._package_name, e
),
reason="package-malformed-requirement",
) from e
if package is None:
raise PluginError(
"{}: Pip package {} does not contain a plugin named '{}'".format(
self.provenance_node.get_provenance(), self._package_name, kind
),
reason="plugin-not-found",
)
location = package.dist.get_resource_filename(
pkg_resources._manager, package.module_name.replace(".", os.sep) + ".py"
)
# Also load the defaults - required since setuptools
# may need to extract the file.
try:
defaults = package.dist.get_resource_filename(
pkg_resources._manager, package.module_name.replace(".", os.sep) + ".yaml"
)
except KeyError:
# The plugin didn't have an accompanying YAML file
defaults = None
return (
os.path.dirname(location),
defaults,
"python package '{}' at: {}".format(package.dist, package.dist.location),
)
def load_config(self, origin_node):
origin_node.validate_keys(["package-name", *PluginOrigin._COMMON_CONFIG_KEYS])
self._package_name = origin_node.get_str("package-name")