diff --git a/homeassistant/auth/__init__.py b/homeassistant/auth/__init__.py index b3f57656dd7..23fdb775a8a 100644 --- a/homeassistant/auth/__init__.py +++ b/homeassistant/auth/__init__.py @@ -103,7 +103,7 @@ class AuthManagerFlowManager(data_entry_flow.FlowManager): """Return a user as result of login flow.""" flow = cast(LoginFlow, flow) - if result["type"] != data_entry_flow.RESULT_TYPE_CREATE_ENTRY: + if result["type"] != data_entry_flow.FlowResultType.CREATE_ENTRY: return result # we got final result diff --git a/homeassistant/components/almond/config_flow.py b/homeassistant/components/almond/config_flow.py index dfbdae219ca..11c883f4e0a 100644 --- a/homeassistant/components/almond/config_flow.py +++ b/homeassistant/components/almond/config_flow.py @@ -64,7 +64,7 @@ class AlmondFlowHandler( """Handle authorize step.""" result = await super().async_step_auth(user_input) - if result["type"] == data_entry_flow.RESULT_TYPE_EXTERNAL_STEP: + if result["type"] == data_entry_flow.FlowResultType.EXTERNAL_STEP: self.host = str(URL(result["url"]).with_path("me")) return result diff --git a/homeassistant/components/auth/login_flow.py b/homeassistant/components/auth/login_flow.py index cd6a405d42c..b24da92afdd 100644 --- a/homeassistant/components/auth/login_flow.py +++ b/homeassistant/components/auth/login_flow.py @@ -52,7 +52,7 @@ flow for details. Progress the flow. Most flows will be 1 page, but could optionally add extra login challenges, like TFA. Once the flow has finished, the returned step will -have type RESULT_TYPE_CREATE_ENTRY and "result" key will contain an authorization code. +have type FlowResultType.CREATE_ENTRY and "result" key will contain an authorization code. The authorization code associated with an authorized user by default, it will associate with an credential if "type" set to "link_user" in "/auth/login_flow" @@ -123,13 +123,13 @@ class AuthProvidersView(HomeAssistantView): def _prepare_result_json(result): """Convert result to JSON.""" - if result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY: + if result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY: data = result.copy() data.pop("result") data.pop("data") return data - if result["type"] != data_entry_flow.RESULT_TYPE_FORM: + if result["type"] != data_entry_flow.FlowResultType.FORM: return result data = result.copy() @@ -154,11 +154,11 @@ class LoginFlowBaseView(HomeAssistantView): async def _async_flow_result_to_response(self, request, client_id, result): """Convert the flow result to a response.""" - if result["type"] != data_entry_flow.RESULT_TYPE_CREATE_ENTRY: + if result["type"] != data_entry_flow.FlowResultType.CREATE_ENTRY: # @log_invalid_auth does not work here since it returns HTTP 200. # We need to manually log failed login attempts. if ( - result["type"] == data_entry_flow.RESULT_TYPE_FORM + result["type"] == data_entry_flow.FlowResultType.FORM and (errors := result.get("errors")) and errors.get("base") in ( diff --git a/homeassistant/components/auth/mfa_setup_flow.py b/homeassistant/components/auth/mfa_setup_flow.py index aa45cc1b028..e288fe33df7 100644 --- a/homeassistant/components/auth/mfa_setup_flow.py +++ b/homeassistant/components/auth/mfa_setup_flow.py @@ -129,11 +129,11 @@ def websocket_depose_mfa( def _prepare_result_json(result): """Convert result to JSON.""" - if result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY: + if result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY: data = result.copy() return data - if result["type"] != data_entry_flow.RESULT_TYPE_FORM: + if result["type"] != data_entry_flow.FlowResultType.FORM: return result data = result.copy() diff --git a/homeassistant/components/config/config_entries.py b/homeassistant/components/config/config_entries.py index 0a093ee4574..b1756b58c3e 100644 --- a/homeassistant/components/config/config_entries.py +++ b/homeassistant/components/config/config_entries.py @@ -143,7 +143,7 @@ class ConfigManagerEntryResourceReloadView(HomeAssistantView): def _prepare_config_flow_result_json(result, prepare_result_json): """Convert result to JSON.""" - if result["type"] != data_entry_flow.RESULT_TYPE_CREATE_ENTRY: + if result["type"] != data_entry_flow.FlowResultType.CREATE_ENTRY: return prepare_result_json(result) data = result.copy() diff --git a/homeassistant/components/mqtt/discovery.py b/homeassistant/components/mqtt/discovery.py index 8685c790fd2..3bbf8ef61ad 100644 --- a/homeassistant/components/mqtt/discovery.py +++ b/homeassistant/components/mqtt/discovery.py @@ -11,7 +11,7 @@ import time from homeassistant.const import CONF_DEVICE, CONF_PLATFORM from homeassistant.core import HomeAssistant -from homeassistant.data_entry_flow import RESULT_TYPE_ABORT +from homeassistant.data_entry_flow import FlowResultType import homeassistant.helpers.config_validation as cv from homeassistant.helpers.dispatcher import ( async_dispatcher_connect, @@ -305,7 +305,7 @@ async def async_start( # noqa: C901 ) if ( result - and result["type"] == RESULT_TYPE_ABORT + and result["type"] == FlowResultType.ABORT and result["reason"] in ("already_configured", "single_instance_allowed") ): diff --git a/homeassistant/config_entries.py b/homeassistant/config_entries.py index bb24b24a7fb..14900153ae4 100644 --- a/homeassistant/config_entries.py +++ b/homeassistant/config_entries.py @@ -682,7 +682,7 @@ class ConfigEntriesFlowManager(data_entry_flow.FlowManager): if not self._async_has_other_discovery_flows(flow.flow_id): persistent_notification.async_dismiss(self.hass, DISCOVERY_NOTIFICATION_ID) - if result["type"] != data_entry_flow.RESULT_TYPE_CREATE_ENTRY: + if result["type"] != data_entry_flow.FlowResultType.CREATE_ENTRY: return result # Check if config entry exists with unique ID. Unload it. @@ -1534,7 +1534,7 @@ class OptionsFlowManager(data_entry_flow.FlowManager): """ flow = cast(OptionsFlow, flow) - if result["type"] != data_entry_flow.RESULT_TYPE_CREATE_ENTRY: + if result["type"] != data_entry_flow.FlowResultType.CREATE_ENTRY: return result entry = self.hass.config_entries.async_get_entry(flow.handler) diff --git a/homeassistant/data_entry_flow.py b/homeassistant/data_entry_flow.py index 714cea07044..abc8061c0d5 100644 --- a/homeassistant/data_entry_flow.py +++ b/homeassistant/data_entry_flow.py @@ -10,10 +10,27 @@ from typing import Any, TypedDict import voluptuous as vol +from .backports.enum import StrEnum from .core import HomeAssistant, callback from .exceptions import HomeAssistantError +from .helpers.frame import report from .util import uuid as uuid_util + +class FlowResultType(StrEnum): + """Result type for a data entry flow.""" + + FORM = "form" + CREATE_ENTRY = "create_entry" + ABORT = "abort" + EXTERNAL_STEP = "external" + EXTERNAL_STEP_DONE = "external_done" + SHOW_PROGRESS = "progress" + SHOW_PROGRESS_DONE = "progress_done" + MENU = "menu" + + +# RESULT_TYPE_* is deprecated, to be removed in 2022.9 RESULT_TYPE_FORM = "form" RESULT_TYPE_CREATE_ENTRY = "create_entry" RESULT_TYPE_ABORT = "abort" @@ -64,7 +81,7 @@ class FlowResult(TypedDict, total=False): """Typed result dict.""" version: int - type: str + type: FlowResultType flow_id: str handler: str title: str @@ -207,7 +224,7 @@ class FlowManager(abc.ABC): self._initialize_tasks[handler].remove(task) self._initializing[handler].remove(init_done) - if result["type"] != RESULT_TYPE_ABORT: + if result["type"] != FlowResultType.ABORT: await self.async_post_init(flow, result) return result @@ -252,7 +269,7 @@ class FlowManager(abc.ABC): user_input = cur_step["data_schema"](user_input) # Handle a menu navigation choice - if cur_step["type"] == RESULT_TYPE_MENU and user_input: + if cur_step["type"] == FlowResultType.MENU and user_input: result = await self._async_handle_step( flow, user_input["next_step_id"], None ) @@ -261,18 +278,25 @@ class FlowManager(abc.ABC): flow, cur_step["step_id"], user_input ) - if cur_step["type"] in (RESULT_TYPE_EXTERNAL_STEP, RESULT_TYPE_SHOW_PROGRESS): - if cur_step["type"] == RESULT_TYPE_EXTERNAL_STEP and result["type"] not in ( - RESULT_TYPE_EXTERNAL_STEP, - RESULT_TYPE_EXTERNAL_STEP_DONE, + if cur_step["type"] in ( + FlowResultType.EXTERNAL_STEP, + FlowResultType.SHOW_PROGRESS, + ): + if cur_step["type"] == FlowResultType.EXTERNAL_STEP and result[ + "type" + ] not in ( + FlowResultType.EXTERNAL_STEP, + FlowResultType.EXTERNAL_STEP_DONE, ): raise ValueError( "External step can only transition to " "external step or external step done." ) - if cur_step["type"] == RESULT_TYPE_SHOW_PROGRESS and result["type"] not in ( - RESULT_TYPE_SHOW_PROGRESS, - RESULT_TYPE_SHOW_PROGRESS_DONE, + if cur_step["type"] == FlowResultType.SHOW_PROGRESS and result[ + "type" + ] not in ( + FlowResultType.SHOW_PROGRESS, + FlowResultType.SHOW_PROGRESS_DONE, ): raise ValueError( "Show progress can only transition to show progress or show progress done." @@ -282,7 +306,7 @@ class FlowManager(abc.ABC): # the frontend. if ( cur_step["step_id"] != result.get("step_id") - or result["type"] == RESULT_TYPE_SHOW_PROGRESS + or result["type"] == FlowResultType.SHOW_PROGRESS ): # Tell frontend to reload the flow state. self.hass.bus.async_fire( @@ -345,25 +369,21 @@ class FlowManager(abc.ABC): if step_done: step_done.set_result(None) - if result["type"] not in ( - RESULT_TYPE_FORM, - RESULT_TYPE_EXTERNAL_STEP, - RESULT_TYPE_CREATE_ENTRY, - RESULT_TYPE_ABORT, - RESULT_TYPE_EXTERNAL_STEP_DONE, - RESULT_TYPE_SHOW_PROGRESS, - RESULT_TYPE_SHOW_PROGRESS_DONE, - RESULT_TYPE_MENU, - ): - raise ValueError(f"Handler returned incorrect type: {result['type']}") + if not isinstance(result["type"], FlowResultType): + result["type"] = FlowResultType(result["type"]) # type: ignore[unreachable] + report( + "does not use FlowResultType enum for data entry flow result type. " + "This is deprecated and will stop working in Home Assistant 2022.9", + error_if_core=False, + ) if result["type"] in ( - RESULT_TYPE_FORM, - RESULT_TYPE_EXTERNAL_STEP, - RESULT_TYPE_EXTERNAL_STEP_DONE, - RESULT_TYPE_SHOW_PROGRESS, - RESULT_TYPE_SHOW_PROGRESS_DONE, - RESULT_TYPE_MENU, + FlowResultType.FORM, + FlowResultType.EXTERNAL_STEP, + FlowResultType.EXTERNAL_STEP_DONE, + FlowResultType.SHOW_PROGRESS, + FlowResultType.SHOW_PROGRESS_DONE, + FlowResultType.MENU, ): flow.cur_step = result return result @@ -372,7 +392,7 @@ class FlowManager(abc.ABC): result = await self.async_finish_flow(flow, result.copy()) # _async_finish_flow may change result type, check it again - if result["type"] == RESULT_TYPE_FORM: + if result["type"] == FlowResultType.FORM: flow.cur_step = result return result @@ -427,7 +447,7 @@ class FlowHandler: ) -> FlowResult: """Return the definition of a form to gather user input.""" return { - "type": RESULT_TYPE_FORM, + "type": FlowResultType.FORM, "flow_id": self.flow_id, "handler": self.handler, "step_id": step_id, @@ -449,7 +469,7 @@ class FlowHandler: """Finish config flow and create a config entry.""" return { "version": self.VERSION, - "type": RESULT_TYPE_CREATE_ENTRY, + "type": FlowResultType.CREATE_ENTRY, "flow_id": self.flow_id, "handler": self.handler, "title": title, @@ -480,7 +500,7 @@ class FlowHandler: ) -> FlowResult: """Return the definition of an external step for the user to take.""" return { - "type": RESULT_TYPE_EXTERNAL_STEP, + "type": FlowResultType.EXTERNAL_STEP, "flow_id": self.flow_id, "handler": self.handler, "step_id": step_id, @@ -492,7 +512,7 @@ class FlowHandler: def async_external_step_done(self, *, next_step_id: str) -> FlowResult: """Return the definition of an external step for the user to take.""" return { - "type": RESULT_TYPE_EXTERNAL_STEP_DONE, + "type": FlowResultType.EXTERNAL_STEP_DONE, "flow_id": self.flow_id, "handler": self.handler, "step_id": next_step_id, @@ -508,7 +528,7 @@ class FlowHandler: ) -> FlowResult: """Show a progress message to the user, without user input allowed.""" return { - "type": RESULT_TYPE_SHOW_PROGRESS, + "type": FlowResultType.SHOW_PROGRESS, "flow_id": self.flow_id, "handler": self.handler, "step_id": step_id, @@ -520,7 +540,7 @@ class FlowHandler: def async_show_progress_done(self, *, next_step_id: str) -> FlowResult: """Mark the progress done.""" return { - "type": RESULT_TYPE_SHOW_PROGRESS_DONE, + "type": FlowResultType.SHOW_PROGRESS_DONE, "flow_id": self.flow_id, "handler": self.handler, "step_id": next_step_id, @@ -539,7 +559,7 @@ class FlowHandler: Options dict maps step_id => i18n label """ return { - "type": RESULT_TYPE_MENU, + "type": FlowResultType.MENU, "flow_id": self.flow_id, "handler": self.handler, "step_id": step_id, @@ -558,7 +578,7 @@ def _create_abort_data( ) -> FlowResult: """Return the definition of an external step for the user to take.""" return { - "type": RESULT_TYPE_ABORT, + "type": FlowResultType.ABORT, "flow_id": flow_id, "handler": handler, "reason": reason, diff --git a/homeassistant/helpers/data_entry_flow.py b/homeassistant/helpers/data_entry_flow.py index 2126e048fc5..444876a7674 100644 --- a/homeassistant/helpers/data_entry_flow.py +++ b/homeassistant/helpers/data_entry_flow.py @@ -26,7 +26,7 @@ class _BaseFlowManagerView(HomeAssistantView): self, result: data_entry_flow.FlowResult ) -> data_entry_flow.FlowResult: """Convert result to JSON.""" - if result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY: + if result["type"] == data_entry_flow.FlowResultType.CREATE_ENTRY: data = result.copy() data.pop("result") data.pop("data") diff --git a/homeassistant/helpers/schema_config_entry_flow.py b/homeassistant/helpers/schema_config_entry_flow.py index 4073421bc2c..12ffa7cc101 100644 --- a/homeassistant/helpers/schema_config_entry_flow.py +++ b/homeassistant/helpers/schema_config_entry_flow.py @@ -42,7 +42,7 @@ class SchemaFlowFormStep: # The next_step function is called if the schema validates successfully or if no # schema is defined. The next_step function is passed the union of config entry # options and user input from previous steps. - # If next_step returns None, the flow is ended with RESULT_TYPE_CREATE_ENTRY. + # If next_step returns None, the flow is ended with FlowResultType.CREATE_ENTRY. next_step: Callable[[dict[str, Any]], str | None] = lambda _: None # Optional function to allow amending a form schema. diff --git a/pylint/plugins/hass_imports.py b/pylint/plugins/hass_imports.py index cc160c1cfbd..31fbe8f498e 100644 --- a/pylint/plugins/hass_imports.py +++ b/pylint/plugins/hass_imports.py @@ -220,6 +220,12 @@ _OBSOLETE_IMPORT: dict[str, list[ObsoleteImportMatch]] = { constant=re.compile(r"^SOURCE_(\w*)$"), ), ], + "homeassistant.data_entry_flow": [ + ObsoleteImportMatch( + reason="replaced by FlowResultType enum", + constant=re.compile(r"^RESULT_TYPE_(\w*)$"), + ), + ], "homeassistant.helpers.device_registry": [ ObsoleteImportMatch( reason="replaced by DeviceEntryDisabler enum",