This is an automated email from the ASF dual-hosted git repository. juergbi pushed a commit to branch jbilleter/junction-provenance in repository https://gitbox.apache.org/repos/asf/buildstream.git
commit d07808b2f76234445616132ee798da8a9f45af3f Author: Jürg Billeter <[email protected]> AuthorDate: Fri May 1 11:39:51 2026 +0200 Defer source provenance check until the project is fully loaded Junctions may be loaded before a project is fully loaded, which means that the source provenance attribute project configuration may not be available yet. This adds a callback mechanism to defer the source provenance attribute check if the project is not fully loaded when a junction element with a provenance node is loaded. File "src/buildstream/element.py", line 1075, in _new_from_load_element element.__load_sources(load_element) ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^ File "src/buildstream/element.py", line 2642, in __load_sources provenance_node.validate_keys(project.source_provenance_attributes.keys()) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ AttributeError: 'NoneType' object has no attribute 'keys' Fixes #2116. --- src/buildstream/_project.py | 19 ++++++++++++++++++- src/buildstream/element.py | 22 +++++++++++++++------- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/buildstream/_project.py b/src/buildstream/_project.py index dd46442c2..252616220 100644 --- a/src/buildstream/_project.py +++ b/src/buildstream/_project.py @@ -15,7 +15,7 @@ # Tristan Van Berkom <[email protected]> # Tiago Gomes <[email protected]> -from typing import TYPE_CHECKING, Optional, Dict, Union, List, Sequence +from typing import TYPE_CHECKING, Optional, Dict, Union, List, Sequence, Callable import os import urllib.parse @@ -158,6 +158,8 @@ class Project: self._fully_loaded: bool = False self._project_includes: Optional[Includes] = None + self._fully_loaded_callbacks: List[Callable[[], None]] = [] + # # Initialization body # @@ -692,6 +694,17 @@ class Project: def loaded_projects(self): yield from self.load_context.loaded_projects() + # register_fully_loaded_callback() + # + # Call the specified function once the project is fully loaded. + # + # Args: + # callback (Callable[[], None]): A function to call once fully loaded + # + def register_fully_loaded_callback(self, callback: Callable[[], None]): + assert not self._fully_loaded + self._fully_loaded_callbacks.append(callback) + ######################################################## # Private Methods # ######################################################## @@ -1017,6 +1030,10 @@ class Project: "source-provenance-attributes", None ) or config.get_mapping("source-provenance-attributes") + for callback in self._fully_loaded_callbacks: + callback() + self._fully_loaded_callbacks = None + # _load_pass(): # # Loads parts of the project configuration that are different diff --git a/src/buildstream/element.py b/src/buildstream/element.py index 32666af46..891ee7152 100644 --- a/src/buildstream/element.py +++ b/src/buildstream/element.py @@ -2638,13 +2638,21 @@ class Element(Plugin): provenance_node: MappingNode = source.get_mapping(Symbol.PROVENANCE, default=None) if provenance_node: del source[Symbol.PROVENANCE] - try: - provenance_node.validate_keys(project.source_provenance_attributes.keys()) - except LoadError as E: - raise LoadError( - f"Specified source provenance attribute not defined in project config\n {E}", - LoadErrorReason.UNDEFINED_SOURCE_PROVENANCE_ATTRIBUTE, - ) + + def source_provenance_attribute_check(provenance_node=provenance_node): + try: + provenance_node.validate_keys(project.source_provenance_attributes.keys()) + except LoadError as E: + raise LoadError( + f"Specified source provenance attribute not defined in project config\n {E}", + LoadErrorReason.UNDEFINED_SOURCE_PROVENANCE_ATTRIBUTE, + ) + + # Source provenance attributes are available only after the project is fully loaded + if project.source_provenance_attributes is None: + project.register_fully_loaded_callback(source_provenance_attribute_check) + else: + source_provenance_attribute_check() # make sure everything is a string for key, value in provenance_node.items():
