Skip to content

Commit

Permalink
Introduce ClientAppException (#3191)
Browse files Browse the repository at this point in the history
  • Loading branch information
jafermarq authored Apr 2, 2024
1 parent 987b198 commit f95d641
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 36 deletions.
33 changes: 24 additions & 9 deletions src/py/flwr/client/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
TRANSPORT_TYPE_GRPC_RERE,
TRANSPORT_TYPE_REST,
TRANSPORT_TYPES,
ErrorCode,
)
from flwr.common.exit_handlers import register_exit_handlers
from flwr.common.logger import log, warn_deprecated_feature
Expand Down Expand Up @@ -483,35 +484,49 @@ def _load_client_app() -> ClientApp:
# Create an error reply message that will never be used to prevent
# the used-before-assignment linting error
reply_message = message.create_error_reply(
error=Error(code=0, reason="Unknown")
error=Error(code=ErrorCode.UNKNOWN, reason="Unknown")
)

# Handle app loading and task message
try:
# Load ClientApp instance
client_app: ClientApp = load_client_app_fn()

# Execute ClientApp
reply_message = client_app(message=message, context=context)
# Update node state
node_state.update_context(
run_id=message.metadata.run_id,
context=context,
)
except Exception as ex: # pylint: disable=broad-exception-caught
log(ERROR, "ClientApp raised an exception", exc_info=ex)

# Legacy grpc-bidi
if transport in ["grpc-bidi", None]:
log(ERROR, "Client raised an exception.", exc_info=ex)
# Raise exception, crash process
raise ex

# Don't update/change NodeState

# Create error message
e_code = ErrorCode.CLIENT_APP_RAISED_EXCEPTION
# Reason example: "<class 'ZeroDivisionError'>:<'division by zero'>"
reason = str(type(ex)) + ":<'" + str(ex) + "'>"
exc_entity = "ClientApp"
if isinstance(ex, LoadClientAppError):
reason = (
"An exception was raised when attempting to load "
"`ClientApp`"
)
e_code = ErrorCode.LOAD_CLIENT_APP_EXCEPTION
exc_entity = "SuperNode"

log(ERROR, "%s raised an exception", exc_entity, exc_info=ex)

# Create error message
reply_message = message.create_error_reply(
error=Error(code=0, reason=reason)
error=Error(code=e_code, reason=reason)
)
else:
# No exception, update node state
node_state.update_context(
run_id=message.metadata.run_id,
context=context,
)

# Send
Expand Down
9 changes: 9 additions & 0 deletions src/py/flwr/client/client_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@
from .typing import ClientAppCallable


class ClientAppException(Exception):
"""Exception raised when an exception is raised while executing a ClientApp."""

def __init__(self, message: str):
ex_name = self.__class__.__name__
self.message = f"\nException {ex_name} occurred. Message: " + message
super().__init__(self.message)


class ClientApp:
"""Flower ClientApp.
Expand Down
3 changes: 2 additions & 1 deletion src/py/flwr/common/constant.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ class ErrorCode:
"""Error codes for Message's Error."""

UNKNOWN = 0
CLIENT_APP_RAISED_EXCEPTION = 1
LOAD_CLIENT_APP_EXCEPTION = 1
CLIENT_APP_RAISED_EXCEPTION = 2

def __new__(cls) -> ErrorCode:
"""Prevent instantiation."""
Expand Down
17 changes: 13 additions & 4 deletions src/py/flwr/server/superlink/fleet/vce/vce_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@
from logging import DEBUG, ERROR, INFO, WARN
from typing import Callable, Dict, List, Optional

from flwr.client.client_app import ClientApp, LoadClientAppError
from flwr.client.client_app import ClientApp, ClientAppException, LoadClientAppError
from flwr.client.node_state import NodeState
from flwr.common.constant import PING_MAX_INTERVAL
from flwr.common.constant import PING_MAX_INTERVAL, ErrorCode
from flwr.common.logger import log
from flwr.common.message import Error
from flwr.common.object_ref import load_app
Expand Down Expand Up @@ -94,9 +94,18 @@ async def worker(
except Exception as ex: # pylint: disable=broad-exception-caught
log(ERROR, ex)
log(ERROR, traceback.format_exc())

if isinstance(ex, ClientAppException):
e_code = ErrorCode.CLIENT_APP_RAISED_EXCEPTION
elif isinstance(ex, LoadClientAppError):
e_code = ErrorCode.LOAD_CLIENT_APP_EXCEPTION
else:
e_code = ErrorCode.UNKNOWN

reason = str(type(ex)) + ":<'" + str(ex) + "'>"
error = Error(code=0, reason=reason)
out_mssg = message.create_error_reply(error=error)
out_mssg = message.create_error_reply(
error=Error(code=e_code, reason=reason)
)

finally:
if out_mssg:
Expand Down
24 changes: 2 additions & 22 deletions src/py/flwr/simulation/ray_transport/ray_actor.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

import asyncio
import threading
import traceback
from abc import ABC
from logging import DEBUG, ERROR, WARNING
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Type, Union
Expand All @@ -25,22 +24,13 @@
from ray import ObjectRef
from ray.util.actor_pool import ActorPool

from flwr.client.client_app import ClientApp, LoadClientAppError
from flwr.client.client_app import ClientApp, ClientAppException, LoadClientAppError
from flwr.common import Context, Message
from flwr.common.logger import log

ClientAppFn = Callable[[], ClientApp]


class ClientException(Exception):
"""Raised when client side logic crashes with an exception."""

def __init__(self, message: str):
div = ">" * 7
self.message = "\n" + div + "A ClientException occurred." + message
super().__init__(self.message)


class VirtualClientEngineActor(ABC):
"""Abstract base class for VirtualClientEngine Actors."""

Expand Down Expand Up @@ -71,17 +61,7 @@ def run(
raise load_ex

except Exception as ex:
client_trace = traceback.format_exc()
mssg = (
"\n\tSomething went wrong when running your client run."
"\n\tClient "
+ cid
+ " crashed when the "
+ self.__class__.__name__
+ " was running its run."
"\n\tException triggered on the client side: " + client_trace,
)
raise ClientException(str(mssg)) from ex
raise ClientAppException(str(ex)) from ex

return cid, out_message, context

Expand Down

0 comments on commit f95d641

Please sign in to comment.