blob: e865006ac4a401ad525114d12f24cbdc86a1f3ea [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 ..types import FastEnum
from ..node import ScalarNode, MappingNode
from .._exceptions import LoadError
from ..exceptions import LoadErrorReason
# PluginOriginType:
#
# An enumeration depicting the type of plugin origin
#
class PluginOriginType(FastEnum):
LOCAL = "local"
PIP = "pip"
# PluginConfiguration:
#
# An object representing the configuration of a single
# plugin in the origin.
#
class PluginConfiguration:
def __init__(self, kind, allow_deprecated):
self.kind = kind
self.allow_deprecated = allow_deprecated
# PluginOrigin
#
# Base class holding common properties of all origins.
#
class PluginOrigin:
# Common fields valid for all plugin origins
_COMMON_CONFIG_KEYS = ["origin", "sources", "elements", "allow-deprecated"]
def __init__(self, origin_type):
# Public
self.origin_type = origin_type # The PluginOriginType
self.elements = {} # A dictionary of PluginConfiguration
self.sources = {} # A dictionary of PluginConfiguration objects
self.provenance = None
# Private
self._project = None
self._kinds = {}
self._allow_deprecated = False
# new_from_node()
#
# Load a PluginOrigin from the YAML in project.conf
#
# Args:
# project (Project): The project from whence this origin is loaded
# origin_node (MappingNode): The node defining this origin
#
# Returns:
# (PluginOrigin): The newly created PluginOrigin
#
@classmethod
def new_from_node(cls, project, origin_node):
origin_type = origin_node.get_enum("origin", PluginOriginType)
if origin_type == PluginOriginType.LOCAL:
origin = PluginOriginLocal()
elif origin_type == PluginOriginType.PIP:
origin = PluginOriginPip()
origin.provenance = origin_node.get_provenance()
origin._project = project
origin._load(origin_node)
# Parse commonly defined aspects of PluginOrigins
origin._allow_deprecated = origin_node.get_bool("allow-deprecated", False)
element_sequence = origin_node.get_sequence("elements", [])
origin._load_plugin_configurations(element_sequence, origin.elements)
source_sequence = origin_node.get_sequence("sources", [])
origin._load_plugin_configurations(source_sequence, origin.sources)
return origin
# _load()
#
# Abstract method for loading data from the origin node, this
# method should not load the source and element lists.
#
# Args:
# origin_node (MappingNode): The node defining this origin
#
def _load(self, origin_node):
pass
# _load_plugin_configurations()
#
# Helper function to load the list of source or element
# PluginConfigurations
#
# Args:
# sequence_node (SequenceNode): The list of configurations
# dictionary (dict): The location to store the results
#
def _load_plugin_configurations(self, sequence_node, dictionary):
for node in sequence_node:
# Parse as a simple string
if type(node) is ScalarNode: # pylint: disable=unidiomatic-typecheck
kind = node.as_str()
conf = PluginConfiguration(kind, self._allow_deprecated)
# Parse as a dictionary
elif type(node) is MappingNode: # pylint: disable=unidiomatic-typecheck
node.validate_keys(["kind", "allow-deprecated"])
kind = node.get_str("kind")
allow_deprecated = node.get_bool("allow-deprecated", self._allow_deprecated)
conf = PluginConfiguration(kind, allow_deprecated)
else:
p = node.get_provenance()
raise LoadError(
"{}: Plugin is not specified as a string or a dictionary".format(p), LoadErrorReason.INVALID_DATA
)
dictionary[kind] = conf
# PluginOriginLocal
#
# PluginOrigin for local plugins
#
class PluginOriginLocal(PluginOrigin):
def __init__(self):
super().__init__(PluginOriginType.LOCAL)
# An absolute path to where the plugin can be found
#
self.path = None
def _load(self, origin_node):
origin_node.validate_keys(["path", *PluginOrigin._COMMON_CONFIG_KEYS])
path_node = origin_node.get_scalar("path")
path = self._project.get_path_from_node(path_node, check_is_dir=True)
self.path = os.path.join(self._project.directory, path)
# 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 _load(self, origin_node):
origin_node.validate_keys(["package-name", *PluginOrigin._COMMON_CONFIG_KEYS])
self.package_name = origin_node.get_str("package-name")