# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-## Copyright 2021 Canonical Ltd.## This program is free software; you can redistribute it and/or# modify it under the terms of the GNU Lesser General Public# License version 3 as published by the Free Software Foundation.## This program 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 program. If not, see <http://www.gnu.org/licenses/>."""Register and execute callback functions."""importitertoolsimportloggingfromcollections.abcimportCallable,IterablefrompathlibimportPathfromtypingimportNamedTuplefromcraft_partsimporterrorsfromcraft_parts.infosimportProjectInfo,StepInfofromcraft_parts.stepsimportStep
[docs]defregister_stage_packages_filter(func:FilterCallback)->None:"""Register a callback function for stage packages dependency cutoff. Craft Parts includes mechanisms to filter out stage package dependencies in snap bases. This is now deprecated, and a function providing an explicit list of exclusions should be provided by the application. :param func: The callback function returning the filtered packages iterator. """_ensure_not_defined(func,_STAGE_PACKAGE_FILTERS)_STAGE_PACKAGE_FILTERS.append(CallbackHook(func,None))
[docs]defregister_configure_overlay(func:ConfigureOverlayCallback)->None:"""Register a callback function to configure the mounted overlay. This "hook" is called after the overlay's package cache layer is mounted, but *before* the package list is refreshed. It can be used to configure the overlay's system, typically to install extra package repositories for Apt. Note that when the hook is called the overlay is mounted but *not* chrooted into. :param func: The callback function that will be called with the location of the overlay mount and the project info. """_ensure_not_defined(func,_OVERLAY_HOOKS)_OVERLAY_HOOKS.append(CallbackHook(func,None))
[docs]defregister_prologue(func:ExecutionCallback)->None:"""Register an execution prologue callback function. :param func: The callback function to run. """_ensure_not_defined(func,_PROLOGUE_HOOKS)_PROLOGUE_HOOKS.append(CallbackHook(func,None))
[docs]defregister_epilogue(func:ExecutionCallback)->None:"""Register an execution epilogue callback function. :param func: The callback function to run. """_ensure_not_defined(func,_EPILOGUE_HOOKS)_EPILOGUE_HOOKS.append(CallbackHook(func,None))
[docs]defregister_pre_step(func:StepCallback,*,step_list:list[Step]|None=None)->None:"""Register a pre-step callback function. :param func: The callback function to run. :param step_list: The steps before which the callback function should run. If not specified, the callback function will be executed before all steps. """_ensure_not_defined(func,_PRE_STEP_HOOKS)_PRE_STEP_HOOKS.append(CallbackHook(func,step_list))
[docs]defregister_post_step(func:StepCallback,*,step_list:list[Step]|None=None)->None:"""Register a post-step callback function. :param func: The callback function to run. :param step_list: The steps after which the callback function should run. If not specified, the callback function will be executed after all steps. """_ensure_not_defined(func,_POST_STEP_HOOKS)_POST_STEP_HOOKS.append(CallbackHook(func,step_list))
[docs]defunregister_all()->None:"""Clear all existing registered callback functions."""_STAGE_PACKAGE_FILTERS[:]=[]_OVERLAY_HOOKS[:]=[]_PROLOGUE_HOOKS[:]=[]_EPILOGUE_HOOKS[:]=[]_PRE_STEP_HOOKS[:]=[]_POST_STEP_HOOKS[:]=[]
[docs]defget_stage_packages_filters(project_info:ProjectInfo)->set[str]|None:"""Obtain the list of stage packages to be filtered out. :param project_info: The project information to be sent to callback functions. :return: An iterator for the list of packages to be filtered out. """ifnot_STAGE_PACKAGE_FILTERS:returnNonereturnset(itertools.chain(*[f.function(project_info)forfin_STAGE_PACKAGE_FILTERS]))
[docs]defrun_configure_overlay(overlay_dir:Path,project_info:ProjectInfo)->None:"""Run all registered 'configure overlay' callbacks. :param overlay_dir: The location where the overlay is mounted. :param project_info: The project information to be sent to callback functions. """forhookin_OVERLAY_HOOKS:hook.function(overlay_dir,project_info)
[docs]defrun_prologue(project_info:ProjectInfo)->None:"""Run all registered execution prologue callbacks. :param project_info: The project information to be sent to callback functions. """forhookin_PROLOGUE_HOOKS:hook.function(project_info)
[docs]defrun_epilogue(project_info:ProjectInfo)->None:"""Run all registered execution epilogue callbacks. :param project_info: The project information to be sent to callback functions. """forhookin_EPILOGUE_HOOKS:hook.function(project_info)
[docs]defrun_pre_step(step_info:StepInfo)->None:"""Run all registered pre-step callback functions. :param step_info: the step information to be sent to the callback functions. """return_run_step(hook_list=_PRE_STEP_HOOKS,step_info=step_info)
[docs]defrun_post_step(step_info:StepInfo)->None:"""Run all registered post-step callback functions. :param step_info: the step information to be sent to the callback functions. """return_run_step(hook_list=_POST_STEP_HOOKS,step_info=step_info)
def_run_step(*,hook_list:list[CallbackHook],step_info:StepInfo)->None:forhookinhook_list:ifnothook.step_listorstep_info.stepinhook.step_list:hook.function(step_info)def_ensure_not_defined(func:Callback,hook_list:list[CallbackHook])->None:forhookinhook_list:iffunc==hook.function:raiseerrors.CallbackRegistrationError(f"callback function {hook.function.__name__!r} "f"is already registered.")