# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*-## Copyright 2018-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/>."""Provide a report on why a step is outdated."""importloggingfromdataclassesimportdataclassfromcraft_parts.stepsimportStepfromcraft_parts.utilsimportformatting_utilslogger=logging.getLogger(__name__)
[docs]@dataclass(frozen=True)classDependency:"""The part and step that are a prerequisite to another step."""part_name:strstep:Step
[docs]classOutdatedReport:"""The OutdatedReport class explains why a given step is outdated. An outdated step is defined to be a step that has run, but since doing so one of the following things have happened: - A step earlier in the lifecycle has run again. - The source on disk has been updated. """def__init__(self,*,previous_step_modified:Step|None=None,source_modified:bool=False,outdated_files:list[str]|None=None,outdated_dirs:list[str]|None=None,)->None:"""Create a new OutdatedReport. :param previous_step_modified: Step earlier in the lifecycle that has changed. :param source_modified: Whether the source changed on disk. """self.previous_step_modified=previous_step_modifiedself.source_modified=source_modifiedself.outdated_files=outdated_filesself.outdated_dirs=outdated_dirs
[docs]defreason(self)->str:"""Get summarized report. :return: Short summary of why the step is outdated. """reasons=[]ifself.previous_step_modified:reasons.append(f"{self.previous_step_modified.name!r} step")ifself.source_modified:reasons.append("source")ifnotreasons:return""returnf'{formatting_utils.humanize_list(reasons,"and","{}")} changed'
[docs]classDirtyReport:"""The DirtyReport class explains why a given step is dirty. A dirty step is defined to be a step that has run, but since doing so one of the following things have happened: - One or more properties used by the step have changed. - One of more project options have changed. - One of more of its dependencies have been re-staged. """def__init__(self,*,dirty_properties:list[str]|None=None,dirty_project_options:list[str]|None=None,changed_dependencies:list[Dependency]|None=None,)->None:"""Create a new DirtyReport. :param dirty_properties: Properties that have changed. :param dirty_project_options: Project options that have changed. :param changed_dependencies: Dependencies that have changed. """self.dirty_properties=dirty_propertiesself.dirty_project_options=dirty_project_optionsself.changed_dependencies=changed_dependencies# pylint: disable=too-many-branches
[docs]defreason(self)->str:"""Get summarized report. :return: Short summary of why the part is dirty. """reasons=[]reasons_count=0ifself.dirty_properties:reasons_count+=1ifself.dirty_project_options:reasons_count+=1ifself.changed_dependencies:reasons_count+=1ifself.dirty_properties:logger.debug("dirty properties: %r",self.dirty_properties)# Be specific only if this is the only reasonifreasons_count>1orlen(self.dirty_properties)>1:reasons.append("properties")else:reasons.append(f"{self.dirty_properties[0]!r} property")ifself.dirty_project_options:logger.debug("dirty project options: %r",self.dirty_project_options)# Be specific only if this is the only reasonifreasons_count>1orlen(self.dirty_project_options)>1:reasons.append("options")else:reasons.append(f"{self.dirty_project_options[0]!r} option")ifself.changed_dependencies:logger.debug("changed dependencies: %r",self.changed_dependencies)# Be specific only if this is the only reasonifreasons_count>1orlen(self.changed_dependencies)>1:reasons.append("dependencies")else:part_name=self.changed_dependencies[0].part_namestep_name=self.changed_dependencies[0].step.name.lower()reasons.append(f"{step_name} for part {part_name!r}")ifnotreasons:return""returnf'{formatting_utils.humanize_list(reasons,"and","{}")} changed'