def test_catch_log_exception(): """Test it is still a callback after wrapping including partial.""" async def async_meth(): pass assert asyncio.iscoroutinefunction( logging_util.catch_log_exception(partial(async_meth), lambda: None) ) @callback def callback_meth(): pass assert is_callback( logging_util.catch_log_exception(partial(callback_meth), lambda: None) ) def sync_meth(): pass wrapped = logging_util.catch_log_exception(partial(sync_meth), lambda: None) assert not is_callback(wrapped) assert not asyncio.iscoroutinefunction(wrapped)
def async_subscribe_events( hass: HomeAssistant, subscriptions: list[CALLBACK_TYPE], target: Callable[[Event], None], event_types: tuple[str, ...], entity_ids: list[str] | None, device_ids: list[str] | None, ) -> None: """Subscribe to events for the entities and devices or all. These are the events we need to listen for to do the live logbook stream. """ ent_reg = er.async_get(hass) assert is_callback(target), "target must be a callback" event_forwarder = target if entity_ids or device_ids: entity_ids_set = set(entity_ids) if entity_ids else set() device_ids_set = set(device_ids) if device_ids else set() @callback def _forward_events_filtered(event: Event) -> None: event_data = event.data if (entity_ids_set and event_data.get(ATTR_ENTITY_ID) in entity_ids_set) or ( device_ids_set and event_data.get(ATTR_DEVICE_ID) in device_ids_set): target(event) event_forwarder = _forward_events_filtered for event_type in event_types: subscriptions.append( hass.bus.async_listen(event_type, event_forwarder, run_immediately=True)) @callback def _forward_state_events_filtered(event: Event) -> None: if event.data.get("old_state") is None or event.data.get( "new_state") is None: return state: State = event.data["new_state"] if not _is_state_filtered(ent_reg, state): target(event) if entity_ids: subscriptions.append( async_track_state_change_event(hass, entity_ids, _forward_state_events_filtered)) return # We want the firehose subscriptions.append( hass.bus.async_listen( EVENT_STATE_CHANGED, _forward_state_events_filtered, run_immediately=True, ))
def request_handler_factory( view: HomeAssistantView, handler: Callable ) -> Callable[[web.Request], Awaitable[web.StreamResponse]]: """Wrap the handler classes.""" assert asyncio.iscoroutinefunction(handler) or is_callback( handler), "Handler should be a coroutine or a callback." async def handle(request: web.Request) -> web.StreamResponse: """Handle incoming request.""" if request.app[KEY_HASS].is_stopping: return web.Response(status=HTTP_SERVICE_UNAVAILABLE) authenticated = request.get(KEY_AUTHENTICATED, False) if view.requires_auth and not authenticated: raise HTTPUnauthorized() _LOGGER.debug( "Serving %s to %s (auth: %s)", request.path, request.remote, authenticated, ) try: result = handler(request, **request.match_info) if asyncio.iscoroutine(result): result = await result except vol.Invalid as err: raise HTTPBadRequest() from err except exceptions.ServiceNotFound as err: raise HTTPInternalServerError() from err except exceptions.Unauthorized as err: raise HTTPUnauthorized() from err if isinstance(result, web.StreamResponse): # The method handler returned a ready-made Response, how nice of it return result status_code = HTTP_OK if isinstance(result, tuple): result, status_code = result if isinstance(result, bytes): bresult = result elif isinstance(result, str): bresult = result.encode("utf-8") elif result is None: bresult = b"" else: assert ( False ), f"Result should be None, string, bytes or Response. Got: {result}" return web.Response(body=bresult, status=status_code) return handle
def request_handler_factory(view, handler): """Wrap the handler classes.""" assert asyncio.iscoroutinefunction(handler) or is_callback( handler), "Handler should be a coroutine or a callback." async def handle(request): """Handle incoming request.""" if not request.app[KEY_HASS].is_running: return web.Response(status=503) authenticated = request.get(KEY_AUTHENTICATED, False) if view.requires_auth: if authenticated: if "deprecate_warning_message" in request: _LOGGER.warning(request["deprecate_warning_message"]) await process_success_login(request) else: raise HTTPUnauthorized() _LOGGER.debug( "Serving %s to %s (auth: %s)", request.path, request.get(KEY_REAL_IP), authenticated, ) try: result = handler(request, **request.match_info) if asyncio.iscoroutine(result): result = await result except vol.Invalid: raise HTTPBadRequest() except exceptions.ServiceNotFound: raise HTTPInternalServerError() except exceptions.Unauthorized: raise HTTPUnauthorized() if isinstance(result, web.StreamResponse): # The method handler returned a ready-made Response, how nice of it return result status_code = 200 if isinstance(result, tuple): result, status_code = result if isinstance(result, str): result = result.encode("utf-8") elif result is None: result = b"" elif not isinstance(result, bytes): assert False, ("Result should be None, string, bytes or Response. " "Got: {}").format(result) return web.Response(body=result, status=status_code) return handle
def request_handler_factory(view, handler): """Wrap the handler classes.""" assert asyncio.iscoroutinefunction(handler) or is_callback(handler), \ "Handler should be a coroutine or a callback." async def handle(request): """Handle incoming request.""" if not request.app[KEY_HASS].is_running: return web.Response(status=503) authenticated = request.get(KEY_AUTHENTICATED, False) if view.requires_auth: if authenticated: if 'deprecate_warning_message' in request: _LOGGER.warning(request['deprecate_warning_message']) await process_success_login(request) else: raise HTTPUnauthorized() _LOGGER.debug('Serving %s to %s (auth: %s)', request.path, request.get(KEY_REAL_IP), authenticated) try: result = handler(request, **request.match_info) if asyncio.iscoroutine(result): result = await result except vol.Invalid: raise HTTPBadRequest() except exceptions.ServiceNotFound: raise HTTPInternalServerError() except exceptions.Unauthorized: raise HTTPUnauthorized() if isinstance(result, web.StreamResponse): # The method handler returned a ready-made Response, how nice of it return result status_code = 200 if isinstance(result, tuple): result, status_code = result if isinstance(result, str): result = result.encode('utf-8') elif result is None: result = b'' elif not isinstance(result, bytes): assert False, ('Result should be None, string, bytes or Response. ' 'Got: {}').format(result) return web.Response(body=result, status=status_code) return handle
def async_subscribe_events( hass: HomeAssistant, subscriptions: list[CALLBACK_TYPE], target: Callable[[Event], None], event_types: tuple[str, ...], entities_filter: EntityFilter | None, entity_ids: list[str] | None, device_ids: list[str] | None, ) -> None: """Subscribe to events for the entities and devices or all. These are the events we need to listen for to do the live logbook stream. """ ent_reg = er.async_get(hass) assert is_callback(target), "target must be a callback" event_forwarder = event_forwarder_filtered(target, entities_filter, entity_ids, device_ids) for event_type in event_types: subscriptions.append( hass.bus.async_listen(event_type, event_forwarder, run_immediately=True)) if device_ids and not entity_ids: # No entities to subscribe to but we are filtering # on device ids so we do not want to get any state # changed events return @callback def _forward_state_events_filtered(event: Event) -> None: if event.data.get("old_state") is None or event.data.get( "new_state") is None: return new_state: State = event.data["new_state"] old_state: State = event.data["old_state"] if _is_state_filtered(ent_reg, new_state, old_state) or ( entities_filter and not entities_filter(new_state.entity_id)): return target(event) if entity_ids: subscriptions.append( async_track_state_change_event(hass, entity_ids, _forward_state_events_filtered)) return # We want the firehose subscriptions.append( hass.bus.async_listen( EVENT_STATE_CHANGED, _forward_state_events_filtered, run_immediately=True, ))
def request_handler_factory(view, handler): """Factory to wrap our handler classes.""" assert asyncio.iscoroutinefunction(handler) or is_callback(handler), \ "Handler should be a coroutine or a callback." @asyncio.coroutine def handle(request): """Handle incoming request.""" if not request.app['hass'].is_running: return web.Response(status=503) remote_addr = get_real_ip(request) authenticated = request.get(KEY_AUTHENTICATED, False) if view.requires_auth and not authenticated: yield from process_wrong_login(request) _LOGGER.warning( 'Login attempt or request with an invalid ' 'password from %s', remote_addr) persistent_notification.async_create( request.app['hass'], 'Invalid password used from {}'.format(remote_addr), 'Login attempt failed', NOTIFICATION_ID_LOGIN) raise HTTPUnauthorized() _LOGGER.info('Serving %s to %s (auth: %s)', request.path, remote_addr, authenticated) result = handler(request, **request.match_info) if asyncio.iscoroutine(result): result = yield from result if isinstance(result, web.StreamResponse): # The method handler returned a ready-made Response, how nice of it return result status_code = 200 if isinstance(result, tuple): result, status_code = result if isinstance(result, str): result = result.encode('utf-8') elif result is None: result = b'' elif not isinstance(result, bytes): assert False, ('Result should be None, string, bytes or Response. ' 'Got: {}').format(result) return web.Response(body=result, status=status_code) return handle
def request_handler_factory(view, handler): """Factory to wrap our handler classes.""" assert asyncio.iscoroutinefunction(handler) or is_callback(handler), \ "Handler should be a coroutine or a callback." @asyncio.coroutine def handle(request): """Handle incoming request.""" if not request.app['hass'].is_running: return web.Response(status=503) remote_addr = get_real_ip(request) authenticated = request.get(KEY_AUTHENTICATED, False) if view.requires_auth and not authenticated: yield from process_wrong_login(request) _LOGGER.warning('Login attempt or request with an invalid ' 'password from %s', remote_addr) persistent_notification.async_create( request.app['hass'], 'Invalid password used from {}'.format(remote_addr), 'Login attempt failed', NOTIFICATION_ID_LOGIN) raise HTTPUnauthorized() _LOGGER.info('Serving %s to %s (auth: %s)', request.path, remote_addr, authenticated) result = handler(request, **request.match_info) if asyncio.iscoroutine(result): result = yield from result if isinstance(result, web.StreamResponse): # The method handler returned a ready-made Response, how nice of it return result status_code = 200 if isinstance(result, tuple): result, status_code = result if isinstance(result, str): result = result.encode('utf-8') elif result is None: result = b'' elif not isinstance(result, bytes): assert False, ('Result should be None, string, bytes or Response. ' 'Got: {}').format(result) return web.Response(body=result, status=status_code) return handle
def request_handler_factory(view, handler): """Wrap the handler classes.""" assert asyncio.iscoroutinefunction(handler) or is_callback(handler), \ "Handler should be a coroutine or a callback." async def handle(request): """Handle incoming request.""" if not request.app['hass'].is_running: return web.Response(status=503) authenticated = request.get(KEY_AUTHENTICATED, False) if view.requires_auth: if authenticated: await process_success_login(request) else: raise HTTPUnauthorized() _LOGGER.info('Serving %s to %s (auth: %s)', request.path, request.get(KEY_REAL_IP), authenticated) try: result = handler(request, **request.match_info) if asyncio.iscoroutine(result): result = await result except exceptions.Unauthorized: raise HTTPUnauthorized() if isinstance(result, web.StreamResponse): # The method handler returned a ready-made Response, how nice of it return result status_code = 200 if isinstance(result, tuple): result, status_code = result if isinstance(result, str): result = result.encode('utf-8') elif result is None: result = b'' elif not isinstance(result, bytes): assert False, ('Result should be None, string, bytes or Response. ' 'Got: {}').format(result) return web.Response(body=result, status=status_code) return handle
def request_handler_factory(view, handler): """Wrap the handler classes.""" assert asyncio.iscoroutinefunction(handler) or is_callback(handler), \ "Handler should be a coroutine or a callback." @asyncio.coroutine def handle(request): """Handle incoming request.""" if not request.app['hass'].is_running: return web.Response(status=503) remote_addr = get_real_ip(request) authenticated = request.get(KEY_AUTHENTICATED, False) if view.requires_auth and not authenticated: raise HTTPUnauthorized() _LOGGER.info('Serving %s to %s (auth: %s)', request.path, remote_addr, authenticated) result = handler(request, **request.match_info) if asyncio.iscoroutine(result): result = yield from result if isinstance(result, web.StreamResponse): # The method handler returned a ready-made Response, how nice of it return result status_code = 200 if isinstance(result, tuple): result, status_code = result if isinstance(result, str): result = result.encode('utf-8') elif result is None: result = b'' elif not isinstance(result, bytes): assert False, ('Result should be None, string, bytes or Response. ' 'Got: {}').format(result) return web.Response(body=result, status=status_code) return handle
def catch_log_exception( func: Callable[..., Any], format_err: Callable[..., Any], *args: Any) -> Callable[..., None | Coroutine[Any, Any, None]]: """Decorate a callback to catch and log exceptions.""" # Check for partials to properly determine if coroutine function check_func = func while isinstance(check_func, partial): check_func = check_func.func wrapper_func: Callable[..., None | Coroutine[Any, Any, None]] if asyncio.iscoroutinefunction(check_func): async_func = cast(Callable[..., Coroutine[Any, Any, None]], func) @wraps(async_func) async def async_wrapper(*args: Any) -> None: """Catch and log exception.""" try: await async_func(*args) except Exception: # pylint: disable=broad-except log_exception(format_err, *args) wrapper_func = async_wrapper else: @wraps(func) def wrapper(*args: Any) -> None: """Catch and log exception.""" try: func(*args) except Exception: # pylint: disable=broad-except log_exception(format_err, *args) if is_callback(check_func): wrapper = callback(wrapper) wrapper_func = wrapper return wrapper_func
def handle(request): """Handle incoming request.""" if not view.hass.is_running: return web.Response(status=503) remote_addr = view.hass.http.get_real_ip(request) # Auth code verbose on purpose authenticated = False if view.hass.http.api_password is None: authenticated = True elif view.hass.http.is_trusted_ip(remote_addr): authenticated = True elif hmac.compare_digest(request.headers.get(HTTP_HEADER_HA_AUTH, ''), view.hass.http.api_password): # A valid auth header has been set authenticated = True elif hmac.compare_digest(request.GET.get(DATA_API_PASSWORD, ''), view.hass.http.api_password): authenticated = True if view.requires_auth and not authenticated: _LOGGER.warning('Login attempt or request with an invalid ' 'password from %s', remote_addr) persistent_notification.async_create( view.hass, 'Invalid password used from {}'.format(remote_addr), 'Login attempt failed', NOTIFICATION_ID_LOGIN) raise HTTPUnauthorized() request.authenticated = authenticated _LOGGER.info('Serving %s to %s (auth: %s)', request.path, remote_addr, authenticated) assert asyncio.iscoroutinefunction(handler) or is_callback(handler), \ "Handler should be a coroutine or a callback." result = handler(request, **request.match_info) if asyncio.iscoroutine(result): result = yield from result if isinstance(result, web.StreamResponse): # The method handler returned a ready-made Response, how nice of it return result status_code = 200 if isinstance(result, tuple): result, status_code = result if isinstance(result, str): result = result.encode('utf-8') elif result is None: result = b'' elif not isinstance(result, bytes): assert False, ('Result should be None, string, bytes or Response. ' 'Got: {}').format(result) return web.Response(body=result, status=status_code)
def handle(request): """Handle incoming request.""" remote_addr = view.hass.http.get_real_ip(request) # Auth code verbose on purpose authenticated = False if view.hass.http.api_password is None: authenticated = True elif view.hass.http.is_trusted_ip(remote_addr): authenticated = True elif hmac.compare_digest(request.headers.get(HTTP_HEADER_HA_AUTH, ''), view.hass.http.api_password): # A valid auth header has been set authenticated = True elif hmac.compare_digest(request.GET.get(DATA_API_PASSWORD, ''), view.hass.http.api_password): authenticated = True if view.requires_auth and not authenticated: _LOGGER.warning( 'Login attempt or request with an invalid ' 'password from %s', remote_addr) persistent_notification.async_create( view.hass, 'Invalid password used from {}'.format(remote_addr), 'Login attempt failed', NOTIFICATION_ID_LOGIN) raise HTTPUnauthorized() request.authenticated = authenticated _LOGGER.info('Serving %s to %s (auth: %s)', request.path, remote_addr, authenticated) assert asyncio.iscoroutinefunction(handler) or is_callback(handler), \ "Handler should be a coroutine or a callback." result = handler(request, **request.match_info) if asyncio.iscoroutine(result): result = yield from result if isinstance(result, web.StreamResponse): # The method handler returned a ready-made Response, how nice of it return result status_code = 200 if isinstance(result, tuple): result, status_code = result if isinstance(result, str): result = result.encode('utf-8') elif result is None: result = b'' elif not isinstance(result, bytes): assert False, ('Result should be None, string, bytes or Response. ' 'Got: {}').format(result) return web.Response(body=result, status=status_code)
def _changed(self): if self.change_listener: if is_callback(self.change_listener): self.change_listener() else: self._hass.async_add_job(self.change_listener)