blob: 53780ea02bd0085d85bc336cceee7e34365b8815 [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/>.
#
# Authors:
# Tristan Van Berkom <tristan.vanberkom@codethink.co.uk>
from typing import TYPE_CHECKING, cast, Optional, Iterator, Dict, List, Sequence
from .types import _Scope, OverlapAction
from .utils import FileListResult
from ._pluginproxy import PluginProxy
if TYPE_CHECKING:
from typing import Any
from .node import MappingNode, ScalarNode, SequenceNode
from .sandbox import Sandbox
from .source import Source
from .element import Element # pylint: disable=cyclic-import
# ElementProxy()
#
# A PluginProxy for Element instances.
#
# Refer to the Element class for the documentation for these APIs.
#
class ElementProxy(PluginProxy):
def __lt__(self, other):
return self.name < other.name
##############################################################
# Exposed proxied APIs #
##############################################################
@property
def project_name(self):
return cast("Element", self._plugin).project_name
@property
def normal_name(self):
return cast("Element", self._plugin).normal_name
def sources(self) -> Iterator["Source"]:
return cast("Element", self._plugin).sources()
def dependencies(self, selection: Sequence["Element"] = None, *, recurse: bool = True) -> Iterator["Element"]:
#
# When dependencies() is called on a dependency of the main plugin Element,
# we simply reroute the call to the original owning element, while specifying
# this element as the selection.
#
# This ensures we only allow returning dependencies in the _Scope.RUN scope
# of this element.
#
if selection is None:
selection = [cast("Element", self._plugin)]
# Return the iterable from the called generator, this is more performant than yielding from it
return cast("Element", self._owner).dependencies(selection, recurse=recurse)
def search(self, name: str) -> Optional["Element"]:
#
# Similarly to dependencies() above, we only search in the _Scope.RUN
# of dependencies of the active element plugin.
#
return cast("Element", self._plugin)._search(_Scope.RUN, name)
def node_subst_vars(self, node: "ScalarNode") -> str:
return cast("Element", self._plugin).node_subst_vars(node)
def node_subst_sequence_vars(self, node: "SequenceNode[ScalarNode]") -> List[str]:
return cast("Element", self._plugin).node_subst_sequence_vars(node)
def compute_manifest(
self, *, include: Optional[List[str]] = None, exclude: Optional[List[str]] = None, orphans: bool = True
) -> str:
return cast("Element", self._plugin).compute_manifest(include=include, exclude=exclude, orphans=orphans)
def get_artifact_name(self, key: Optional[str] = None) -> str:
return cast("Element", self._plugin).get_artifact_name(key=key)
def stage_artifact(
self,
sandbox: "Sandbox",
*,
path: str = None,
action: str = OverlapAction.WARNING,
include: Optional[List[str]] = None,
exclude: Optional[List[str]] = None,
orphans: bool = True
) -> FileListResult:
owner = cast("Element", self._owner)
element = cast("Element", self._plugin)
assert owner._overlap_collector is not None, "Attempted to stage artifacts outside of Element.stage()"
with owner._overlap_collector.session(action, path):
result = element._stage_artifact(
sandbox, path=path, action=action, include=include, exclude=exclude, orphans=orphans, owner=owner
)
return result
def stage_dependency_artifacts(
self,
sandbox: "Sandbox",
selection: Sequence["Element"] = None,
*,
path: str = None,
action: str = OverlapAction.WARNING,
include: Optional[List[str]] = None,
exclude: Optional[List[str]] = None,
orphans: bool = True
) -> None:
#
# Same approach used here as in Element.dependencies()
#
if selection is None:
selection = [cast("Element", self._plugin)]
cast("Element", self._owner).stage_dependency_artifacts(
sandbox, selection, path=path, action=action, include=include, exclude=exclude, orphans=orphans
)
def integrate(self, sandbox: "Sandbox") -> None:
cast("Element", self._plugin).integrate(sandbox)
def get_public_data(self, domain: str) -> "MappingNode[Any]":
return cast("Element", self._plugin).get_public_data(domain)
def get_environment(self) -> Dict[str, str]:
return cast("Element", self._plugin).get_environment()
def get_variable(self, varname: str) -> Optional[str]:
return cast("Element", self._plugin).get_variable(varname)
##############################################################
# Element Internal APIs #
##############################################################
#
# Some functions the Element expects to call directly on the
# proxy.
#
def _dependencies(self, scope, *, recurse=True, visited=None):
#
# We use a return statement even though this is a generator, simply
# to avoid the generator overhead of yielding each element.
#
return cast("Element", self._plugin)._dependencies(scope, recurse=recurse, visited=visited)
def _file_is_whitelisted(self, path):
return cast("Element", self._plugin)._file_is_whitelisted(path)
def _stage_artifact(
self,
sandbox: "Sandbox",
*,
path: str = None,
action: str = OverlapAction.WARNING,
include: Optional[List[str]] = None,
exclude: Optional[List[str]] = None,
orphans: bool = True,
owner: Optional["Element"] = None
) -> FileListResult:
owner = cast("Element", self._owner)
element = cast("Element", self._plugin)
return element._stage_artifact(
sandbox, path=path, action=action, include=include, exclude=exclude, orphans=orphans, owner=owner
)