Add default value argument to get_*_config_value
authorMarcel van der Veldt <m.vanderveldt@outlook.com>
Mon, 17 Nov 2025 22:25:55 +0000 (23:25 +0100)
committerMarcel van der Veldt <m.vanderveldt@outlook.com>
Mon, 17 Nov 2025 22:25:55 +0000 (23:25 +0100)
music_assistant/controllers/config.py
music_assistant/helpers/api_docs.py
music_assistant/providers/airplay/protocols/airplay2.py

index 765d4f625b83802f4e0b2d3a0bc58ccf97ad5597..95248658d846be9fd5c56e1ffc612bddc5469608 100644 (file)
@@ -234,12 +234,32 @@ class ConfigController:
 
     @overload
     async def get_provider_config_value(
-        self, instance_id: str, key: str, *, return_type: type[_ConfigValueT] = ...
+        self,
+        instance_id: str,
+        key: str,
+        *,
+        default: _ConfigValueT,
+        return_type: type[_ConfigValueT] = ...,
+    ) -> _ConfigValueT: ...
+
+    @overload
+    async def get_provider_config_value(
+        self,
+        instance_id: str,
+        key: str,
+        *,
+        default: ConfigValueType = ...,
+        return_type: type[_ConfigValueT] = ...,
     ) -> _ConfigValueT: ...
 
     @overload
     async def get_provider_config_value(
-        self, instance_id: str, key: str, *, return_type: None = ...
+        self,
+        instance_id: str,
+        key: str,
+        *,
+        default: ConfigValueType = ...,
+        return_type: None = ...,
     ) -> ConfigValueType: ...
 
     @api_command("config/providers/get_value")
@@ -248,6 +268,7 @@ class ConfigController:
         instance_id: str,
         key: str,
         *,
+        default: ConfigValueType = None,
         return_type: type[_ConfigValueT | ConfigValueType] | None = None,
     ) -> _ConfigValueT | ConfigValueType:
         """
@@ -255,6 +276,7 @@ class ConfigController:
 
         :param instance_id: The provider instance ID.
         :param key: The config key to retrieve.
+        :param default: Optional default value to return if key is not found.
         :param return_type: Optional type hint for type inference (e.g., str, int, bool).
             Note: This parameter is used purely for static type checking and does not
             perform runtime type validation. Callers are responsible for ensuring the
@@ -264,6 +286,11 @@ class ConfigController:
         if (cached_value := self._value_cache.get(cache_key)) is not None:
             return cached_value
         conf = await self.get_provider_config(instance_id)
+        if key not in conf.values:
+            if default is not None:
+                return default
+            msg = f"Config key {key} not found for provider {instance_id}"
+            raise KeyError(msg)
         val = (
             conf.values[key].value
             if conf.values[key].value is not None
@@ -516,6 +543,7 @@ class ConfigController:
         key: str,
         unpack_splitted_values: Literal[True],
         *,
+        default: ConfigValueType = ...,
         return_type: type[_ConfigValueT] | None = ...,
     ) -> tuple[str, ...] | list[tuple[str, ...]]: ...
 
@@ -526,6 +554,7 @@ class ConfigController:
         key: str,
         unpack_splitted_values: Literal[False] = False,
         *,
+        default: _ConfigValueT,
         return_type: type[_ConfigValueT] = ...,
     ) -> _ConfigValueT: ...
 
@@ -536,6 +565,18 @@ class ConfigController:
         key: str,
         unpack_splitted_values: Literal[False] = False,
         *,
+        default: ConfigValueType = ...,
+        return_type: type[_ConfigValueT] = ...,
+    ) -> _ConfigValueT: ...
+
+    @overload
+    async def get_player_config_value(
+        self,
+        player_id: str,
+        key: str,
+        unpack_splitted_values: Literal[False] = False,
+        *,
+        default: ConfigValueType = ...,
         return_type: None = ...,
     ) -> ConfigValueType: ...
 
@@ -545,6 +586,8 @@ class ConfigController:
         player_id: str,
         key: str,
         unpack_splitted_values: bool = False,
+        *,
+        default: ConfigValueType = None,
         return_type: type[_ConfigValueT | ConfigValueType] | None = None,
     ) -> _ConfigValueT | ConfigValueType | tuple[str, ...] | list[tuple[str, ...]]:
         """
@@ -553,12 +596,18 @@ class ConfigController:
         :param player_id: The player ID.
         :param key: The config key to retrieve.
         :param unpack_splitted_values: Whether to unpack multi-value config entries.
+        :param default: Optional default value to return if key is not found.
         :param return_type: Optional type hint for type inference (e.g., str, int, bool).
             Note: This parameter is used purely for static type checking and does not
             perform runtime type validation. Callers are responsible for ensuring the
             specified type matches the actual config value type.
         """
         conf = await self.get_player_config(player_id)
+        if key not in conf.values:
+            if default is not None:
+                return default
+            msg = f"Config key {key} not found for player {player_id}"
+            raise KeyError(msg)
         if unpack_splitted_values:
             return conf.values[key].get_splitted_values()
         return (
@@ -890,12 +939,32 @@ class ConfigController:
 
     @overload
     async def get_core_config_value(
-        self, domain: str, key: str, *, return_type: type[_ConfigValueT] = ...
+        self,
+        domain: str,
+        key: str,
+        *,
+        default: _ConfigValueT,
+        return_type: type[_ConfigValueT] = ...,
     ) -> _ConfigValueT: ...
 
     @overload
     async def get_core_config_value(
-        self, domain: str, key: str, *, return_type: None = ...
+        self,
+        domain: str,
+        key: str,
+        *,
+        default: ConfigValueType = ...,
+        return_type: type[_ConfigValueT] = ...,
+    ) -> _ConfigValueT: ...
+
+    @overload
+    async def get_core_config_value(
+        self,
+        domain: str,
+        key: str,
+        *,
+        default: ConfigValueType = ...,
+        return_type: None = ...,
     ) -> ConfigValueType: ...
 
     @api_command("config/core/get_value")
@@ -904,6 +973,7 @@ class ConfigController:
         domain: str,
         key: str,
         *,
+        default: ConfigValueType = None,
         return_type: type[_ConfigValueT | ConfigValueType] | None = None,
     ) -> _ConfigValueT | ConfigValueType:
         """
@@ -911,12 +981,18 @@ class ConfigController:
 
         :param domain: The core controller domain.
         :param key: The config key to retrieve.
+        :param default: Optional default value to return if key is not found.
         :param return_type: Optional type hint for type inference (e.g., str, int, bool).
             Note: This parameter is used purely for static type checking and does not
             perform runtime type validation. Callers are responsible for ensuring the
             specified type matches the actual config value type.
         """
         conf = await self.get_core_config(domain)
+        if key not in conf.values:
+            if default is not None:
+                return default
+            msg = f"Config key {key} not found for core controller {domain}"
+            raise KeyError(msg)
         return (
             conf.values[key].value
             if conf.values[key].value is not None
index 1e561b1a5570570ea52c83c55103326aac42f3fa..ba5dc6b23f451f90100170737464831d5c865bc3 100644 (file)
@@ -428,6 +428,9 @@ def generate_openapi_spec(
         for param_name in handler.signature.parameters:
             if param_name == "self":
                 continue
+            # Skip return_type parameter (used only for type hints)
+            if param_name == "return_type":
+                continue
             param_type = handler.type_hints.get(param_name, Any)
             # Skip Any types as they don't provide useful schema information
             if param_type is not Any and str(param_type) != "typing.Any":
@@ -1186,6 +1189,9 @@ def generate_commands_reference(  # noqa: PLR0915
             for param_name, param in handler.signature.parameters.items():
                 if param_name == "self":
                     continue
+                # Skip return_type parameter (used only for type hints)
+                if param_name == "return_type":
+                    continue
                 is_required = param.default is inspect.Parameter.empty
                 param_type = handler.type_hints.get(param_name, Any)
                 type_str = str(param_type)
@@ -1495,6 +1501,9 @@ def generate_schemas_reference(  # noqa: PLR0915
         for param_name in handler.signature.parameters:
             if param_name == "self":
                 continue
+            # Skip return_type parameter (used only for type hints)
+            if param_name == "return_type":
+                continue
             param_type = handler.type_hints.get(param_name, Any)
             if param_type is not Any and str(param_type) != "typing.Any":
                 _get_type_schema(param_type, schemas)
@@ -2317,6 +2326,9 @@ def generate_html_docs(  # noqa: PLR0915
             for param_name, param in handler.signature.parameters.items():
                 if param_name == "self":
                     continue
+                # Skip return_type parameter (used only for type hints)
+                if param_name == "return_type":
+                    continue
 
                 param_type = handler.type_hints.get(param_name, Any)
                 is_required = param.default is inspect.Parameter.empty
index a55a9e4924c69f42ef4efc9bc49f5500f3a91383..fe3f5df2907a74f3bc827af4e498ef13273810c3 100644 (file)
@@ -60,7 +60,7 @@ class AirPlay2Stream(AirPlayProtocol):
         sync_adjust = self.mass.config.get_raw_player_config_value(player_id, CONF_SYNC_ADJUST, 0)
         assert isinstance(sync_adjust, int)
         read_ahead = await self.mass.config.get_player_config_value(
-            player_id, CONF_READ_AHEAD_BUFFER
+            player_id, CONF_READ_AHEAD_BUFFER, return_type=int
         )
 
         txt_kv: str = ""