From 96ff174dac255ddfdeb25e5d874d006113ae29f4 Mon Sep 17 00:00:00 2001 From: Marek Fiala Date: Mon, 22 Sep 2025 14:20:34 +0200 Subject: [PATCH] feat(tools): idf.py extension - added interface version --- docs/en/api-guides/tools/idf-py.rst | 4 +++- tools/idf_py_actions/tools.py | 6 ++++++ tools/test_build_system/test_idf_extension.py | 21 +++++++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/docs/en/api-guides/tools/idf-py.rst b/docs/en/api-guides/tools/idf-py.rst index 9f55cabd79..b7fe102118 100644 --- a/docs/en/api-guides/tools/idf-py.rst +++ b/docs/en/api-guides/tools/idf-py.rst @@ -333,6 +333,7 @@ An extension file defines an ``action_extensions`` function which returns additi print(f"About to execute {len(tasks)} task(s): {[t.name for t in tasks]}") return { + "version": "1", "global_options": [ { "names": ["--detail", "-d"], @@ -362,8 +363,9 @@ An extension file defines an ``action_extensions`` function which returns additi Extension API Reference ----------------------- -The ``action_extensions`` function takes arguments ``base_actions`` (all currently registered commands) and ``project_path`` (absolute project directory) and returns a dictionary with up to three keys: +The ``action_extensions`` function takes arguments ``base_actions`` (all currently registered commands) and ``project_path`` (absolute project directory) and returns a dictionary with up to four keys: +- ``version``: A string representing the interface version of the extension. Currently, the API version is ``1``. **This key is mandatory** and must be provided. - ``global_options``: A list of options available globally for all commands. Each option is a dictionary with fields such as ``names``, ``help``, ``type``, ``is_flag``, ``scope``, etc. - ``global_action_callbacks``: A list of functions called once before any task execution. Each global action callback function accepts three arguments: diff --git a/tools/idf_py_actions/tools.py b/tools/idf_py_actions/tools.py index 3a5b30a346..d9694cb317 100644 --- a/tools/idf_py_actions/tools.py +++ b/tools/idf_py_actions/tools.py @@ -768,6 +768,12 @@ def merge_action_lists(*action_lists: dict, custom_actions: dict[str, Any] | Non if not custom_actions: return merged_actions + if not custom_actions.get('version'): + raise AttributeError( + 'Attribute "version" is required in custom extension. ' + 'Please update your extension dictionary to contain the "version" attribute.' + ) + existing_identifiers = _get_all_action_identifiers(merged_actions['actions']) for name, action in custom_actions.get('actions', {}).items(): try: diff --git a/tools/test_build_system/test_idf_extension.py b/tools/test_build_system/test_idf_extension.py index 19681a7ed8..123c996507 100644 --- a/tools/test_build_system/test_idf_extension.py +++ b/tools/test_build_system/test_idf_extension.py @@ -23,6 +23,7 @@ def action_extensions(base_actions, project_path): return 0 return {{ + 'version': '1', 'global_options': [{global_options}], 'actions': {{ {actions} @@ -211,6 +212,26 @@ def test_extension_from_component_invalid_syntax(idf_py: IdfPyFunc, test_app_cop ret = idf_py('--help') assert "has no attribute 'action_extensions'" in ret.stderr + idf_ext_py.write_text( + textwrap.dedent( + TEST_EXT_TEMPLATE.format( + suffix='component extension', + global_options='', + actions="""'test-component-action': { + 'callback': test_extension_action, + 'help': 'Test action from component extension' + }""", + ) + ) + ) + replace_in_file( + idf_ext_py, + "'version': '1',", + '\n', + ) + ret = idf_py('--help') + assert 'Attribute "version" is required in custom extension.' in ret.stderr + # ----------- Test cases for entry point extension -----------