flwr 1.19.0__py3-none-any.whl → 1.21.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- flwr/__init__.py +4 -1
- flwr/app/__init__.py +28 -0
- flwr/app/exception.py +31 -0
- flwr/cli/auth_plugin/oidc_cli_plugin.py +4 -4
- flwr/cli/build.py +15 -5
- flwr/cli/cli_user_auth_interceptor.py +1 -1
- flwr/cli/config_utils.py +3 -3
- flwr/cli/constant.py +25 -8
- flwr/cli/log.py +9 -9
- flwr/cli/login/login.py +3 -3
- flwr/cli/ls.py +5 -5
- flwr/cli/new/new.py +23 -4
- flwr/cli/new/templates/app/README.flowertune.md.tpl +2 -0
- flwr/cli/new/templates/app/README.md.tpl +5 -0
- flwr/cli/new/templates/app/code/__init__.pytorch_msg_api.py.tpl +1 -0
- flwr/cli/new/templates/app/code/client.pytorch_msg_api.py.tpl +80 -0
- flwr/cli/new/templates/app/code/server.pytorch_msg_api.py.tpl +41 -0
- flwr/cli/new/templates/app/code/task.pytorch_msg_api.py.tpl +98 -0
- flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +14 -3
- flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +13 -1
- flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +21 -2
- flwr/cli/new/templates/app/pyproject.jax.toml.tpl +18 -1
- flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +19 -2
- flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +18 -1
- flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +20 -3
- flwr/cli/new/templates/app/pyproject.pytorch_msg_api.toml.tpl +53 -0
- flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +18 -1
- flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +18 -1
- flwr/cli/run/run.py +53 -50
- flwr/cli/stop.py +7 -4
- flwr/cli/utils.py +29 -11
- flwr/client/grpc_adapter_client/connection.py +11 -4
- flwr/client/grpc_rere_client/connection.py +93 -129
- flwr/client/rest_client/connection.py +134 -164
- flwr/clientapp/__init__.py +10 -0
- flwr/clientapp/mod/__init__.py +26 -0
- flwr/clientapp/mod/centraldp_mods.py +132 -0
- flwr/common/args.py +20 -6
- flwr/common/auth_plugin/__init__.py +4 -4
- flwr/common/auth_plugin/auth_plugin.py +7 -7
- flwr/common/constant.py +26 -5
- flwr/common/event_log_plugin/event_log_plugin.py +1 -1
- flwr/common/exit/__init__.py +4 -0
- flwr/common/exit/exit.py +8 -1
- flwr/common/exit/exit_code.py +42 -8
- flwr/common/exit/exit_handler.py +62 -0
- flwr/common/{exit_handlers.py → exit/signal_handler.py} +20 -37
- flwr/common/grpc.py +1 -1
- flwr/common/{inflatable_grpc_utils.py → inflatable_protobuf_utils.py} +52 -10
- flwr/common/inflatable_utils.py +191 -24
- flwr/common/logger.py +1 -1
- flwr/common/record/array.py +101 -22
- flwr/common/record/arraychunk.py +59 -0
- flwr/common/retry_invoker.py +30 -11
- flwr/common/serde.py +0 -28
- flwr/common/telemetry.py +4 -0
- flwr/compat/client/app.py +14 -31
- flwr/compat/server/app.py +2 -2
- flwr/proto/appio_pb2.py +51 -0
- flwr/proto/appio_pb2.pyi +195 -0
- flwr/proto/appio_pb2_grpc.py +4 -0
- flwr/proto/appio_pb2_grpc.pyi +4 -0
- flwr/proto/clientappio_pb2.py +4 -19
- flwr/proto/clientappio_pb2.pyi +0 -125
- flwr/proto/clientappio_pb2_grpc.py +269 -29
- flwr/proto/clientappio_pb2_grpc.pyi +114 -21
- flwr/proto/control_pb2.py +62 -0
- flwr/proto/{exec_pb2_grpc.py → control_pb2_grpc.py} +54 -54
- flwr/proto/{exec_pb2_grpc.pyi → control_pb2_grpc.pyi} +28 -28
- flwr/proto/fleet_pb2.py +12 -20
- flwr/proto/fleet_pb2.pyi +6 -36
- flwr/proto/serverappio_pb2.py +8 -31
- flwr/proto/serverappio_pb2.pyi +0 -152
- flwr/proto/serverappio_pb2_grpc.py +107 -38
- flwr/proto/serverappio_pb2_grpc.pyi +47 -20
- flwr/proto/simulationio_pb2.py +4 -11
- flwr/proto/simulationio_pb2.pyi +0 -58
- flwr/proto/simulationio_pb2_grpc.py +129 -27
- flwr/proto/simulationio_pb2_grpc.pyi +52 -13
- flwr/server/app.py +130 -153
- flwr/server/fleet_event_log_interceptor.py +4 -0
- flwr/server/grid/grpc_grid.py +94 -54
- flwr/server/grid/inmemory_grid.py +1 -0
- flwr/server/serverapp/app.py +165 -144
- flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +8 -0
- flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +1 -1
- flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +2 -5
- flwr/server/superlink/fleet/message_handler/message_handler.py +10 -16
- flwr/server/superlink/fleet/rest_rere/rest_api.py +1 -2
- flwr/server/superlink/fleet/vce/backend/raybackend.py +3 -1
- flwr/server/superlink/fleet/vce/vce_api.py +6 -6
- flwr/server/superlink/linkstate/in_memory_linkstate.py +34 -0
- flwr/server/superlink/linkstate/linkstate.py +2 -1
- flwr/server/superlink/linkstate/sqlite_linkstate.py +45 -0
- flwr/server/superlink/serverappio/serverappio_grpc.py +2 -2
- flwr/server/superlink/serverappio/serverappio_servicer.py +95 -48
- flwr/server/superlink/simulation/simulationio_grpc.py +1 -1
- flwr/server/superlink/simulation/simulationio_servicer.py +98 -22
- flwr/server/superlink/utils.py +0 -35
- flwr/serverapp/__init__.py +12 -0
- flwr/serverapp/dp_fixed_clipping.py +352 -0
- flwr/serverapp/exception.py +38 -0
- flwr/serverapp/strategy/__init__.py +38 -0
- flwr/serverapp/strategy/dp_fixed_clipping.py +352 -0
- flwr/serverapp/strategy/fedadagrad.py +162 -0
- flwr/serverapp/strategy/fedadam.py +181 -0
- flwr/serverapp/strategy/fedavg.py +295 -0
- flwr/serverapp/strategy/fedopt.py +218 -0
- flwr/serverapp/strategy/fedyogi.py +173 -0
- flwr/serverapp/strategy/result.py +105 -0
- flwr/serverapp/strategy/strategy.py +285 -0
- flwr/serverapp/strategy/strategy_utils.py +251 -0
- flwr/serverapp/strategy/strategy_utils_tests.py +304 -0
- flwr/simulation/app.py +159 -154
- flwr/simulation/run_simulation.py +17 -0
- flwr/supercore/app_utils.py +58 -0
- flwr/supercore/cli/__init__.py +22 -0
- flwr/supercore/cli/flower_superexec.py +141 -0
- flwr/supercore/corestate/__init__.py +22 -0
- flwr/supercore/corestate/corestate.py +81 -0
- flwr/{server/superlink → supercore}/ffs/disk_ffs.py +1 -1
- flwr/supercore/grpc_health/__init__.py +25 -0
- flwr/supercore/grpc_health/health_server.py +53 -0
- flwr/supercore/grpc_health/simple_health_servicer.py +38 -0
- flwr/supercore/license_plugin/__init__.py +22 -0
- flwr/supercore/license_plugin/license_plugin.py +26 -0
- flwr/supercore/object_store/in_memory_object_store.py +31 -31
- flwr/supercore/object_store/object_store.py +20 -42
- flwr/supercore/object_store/utils.py +43 -0
- flwr/{superexec → supercore/superexec}/__init__.py +1 -1
- flwr/supercore/superexec/plugin/__init__.py +28 -0
- flwr/supercore/superexec/plugin/base_exec_plugin.py +53 -0
- flwr/supercore/superexec/plugin/clientapp_exec_plugin.py +28 -0
- flwr/supercore/superexec/plugin/exec_plugin.py +71 -0
- flwr/supercore/superexec/plugin/serverapp_exec_plugin.py +28 -0
- flwr/supercore/superexec/plugin/simulation_exec_plugin.py +28 -0
- flwr/supercore/superexec/run_superexec.py +185 -0
- flwr/supercore/utils.py +32 -0
- flwr/superlink/servicer/__init__.py +15 -0
- flwr/superlink/servicer/control/__init__.py +22 -0
- flwr/{superexec/exec_event_log_interceptor.py → superlink/servicer/control/control_event_log_interceptor.py} +9 -5
- flwr/{superexec/exec_grpc.py → superlink/servicer/control/control_grpc.py} +39 -28
- flwr/superlink/servicer/control/control_license_interceptor.py +82 -0
- flwr/{superexec/exec_servicer.py → superlink/servicer/control/control_servicer.py} +79 -31
- flwr/{superexec/exec_user_auth_interceptor.py → superlink/servicer/control/control_user_auth_interceptor.py} +18 -10
- flwr/supernode/cli/flower_supernode.py +3 -7
- flwr/supernode/cli/flwr_clientapp.py +20 -16
- flwr/supernode/nodestate/in_memory_nodestate.py +13 -4
- flwr/supernode/nodestate/nodestate.py +3 -44
- flwr/supernode/runtime/run_clientapp.py +129 -115
- flwr/supernode/servicer/clientappio/__init__.py +1 -3
- flwr/supernode/servicer/clientappio/clientappio_servicer.py +217 -165
- flwr/supernode/start_client_internal.py +205 -148
- {flwr-1.19.0.dist-info → flwr-1.21.0.dist-info}/METADATA +5 -3
- {flwr-1.19.0.dist-info → flwr-1.21.0.dist-info}/RECORD +161 -117
- {flwr-1.19.0.dist-info → flwr-1.21.0.dist-info}/entry_points.txt +1 -0
- flwr/common/inflatable_rest_utils.py +0 -99
- flwr/proto/exec_pb2.py +0 -62
- flwr/superexec/app.py +0 -45
- flwr/superexec/deployment.py +0 -192
- flwr/superexec/executor.py +0 -100
- flwr/superexec/simulation.py +0 -130
- /flwr/proto/{exec_pb2.pyi → control_pb2.pyi} +0 -0
- /flwr/{server/superlink → supercore}/ffs/__init__.py +0 -0
- /flwr/{server/superlink → supercore}/ffs/ffs.py +0 -0
- /flwr/{server/superlink → supercore}/ffs/ffs_factory.py +0 -0
- {flwr-1.19.0.dist-info → flwr-1.21.0.dist-info}/WHEEL +0 -0
|
@@ -15,16 +15,15 @@
|
|
|
15
15
|
"""ClientAppIo API servicer."""
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
from dataclasses import dataclass
|
|
19
18
|
from logging import DEBUG, ERROR
|
|
20
|
-
from typing import
|
|
19
|
+
from typing import cast
|
|
21
20
|
|
|
22
21
|
import grpc
|
|
23
22
|
|
|
24
|
-
from flwr.common import Context
|
|
23
|
+
from flwr.common import Context
|
|
24
|
+
from flwr.common.inflatable import UnexpectedObjectContentError
|
|
25
25
|
from flwr.common.logger import log
|
|
26
26
|
from flwr.common.serde import (
|
|
27
|
-
clientappstatus_to_proto,
|
|
28
27
|
context_from_proto,
|
|
29
28
|
context_to_proto,
|
|
30
29
|
fab_to_proto,
|
|
@@ -36,209 +35,262 @@ from flwr.common.typing import Fab, Run
|
|
|
36
35
|
|
|
37
36
|
# pylint: disable=E0611
|
|
38
37
|
from flwr.proto import clientappio_pb2_grpc
|
|
39
|
-
from flwr.proto.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
38
|
+
from flwr.proto.appio_pb2 import ( # pylint: disable=E0401
|
|
39
|
+
ListAppsToLaunchRequest,
|
|
40
|
+
ListAppsToLaunchResponse,
|
|
41
|
+
PullAppInputsRequest,
|
|
42
|
+
PullAppInputsResponse,
|
|
43
|
+
PullAppMessagesRequest,
|
|
44
|
+
PullAppMessagesResponse,
|
|
45
|
+
PushAppMessagesRequest,
|
|
46
|
+
PushAppMessagesResponse,
|
|
47
|
+
PushAppOutputsRequest,
|
|
48
|
+
PushAppOutputsResponse,
|
|
49
|
+
RequestTokenRequest,
|
|
50
|
+
RequestTokenResponse,
|
|
46
51
|
)
|
|
52
|
+
from flwr.proto.message_pb2 import (
|
|
53
|
+
ConfirmMessageReceivedRequest,
|
|
54
|
+
ConfirmMessageReceivedResponse,
|
|
55
|
+
PullObjectRequest,
|
|
56
|
+
PullObjectResponse,
|
|
57
|
+
PushObjectRequest,
|
|
58
|
+
PushObjectResponse,
|
|
59
|
+
)
|
|
60
|
+
from flwr.proto.run_pb2 import GetRunRequest, GetRunResponse # pylint: disable=E0611
|
|
47
61
|
|
|
62
|
+
# pylint: disable=E0601
|
|
63
|
+
from flwr.supercore.ffs import FfsFactory
|
|
64
|
+
from flwr.supercore.object_store import NoObjectInStoreError, ObjectStoreFactory
|
|
65
|
+
from flwr.supercore.object_store.utils import store_mapping_and_register_objects
|
|
66
|
+
from flwr.supernode.nodestate import NodeStateFactory
|
|
48
67
|
|
|
49
|
-
@dataclass
|
|
50
|
-
class ClientAppInputs:
|
|
51
|
-
"""Specify the inputs to the ClientApp."""
|
|
52
68
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
fab: Optional[Fab]
|
|
57
|
-
token: int
|
|
69
|
+
# pylint: disable=C0103,W0613,W0201
|
|
70
|
+
class ClientAppIoServicer(clientappio_pb2_grpc.ClientAppIoServicer):
|
|
71
|
+
"""ClientAppIo API servicer."""
|
|
58
72
|
|
|
73
|
+
def __init__(
|
|
74
|
+
self,
|
|
75
|
+
state_factory: NodeStateFactory,
|
|
76
|
+
ffs_factory: FfsFactory,
|
|
77
|
+
objectstore_factory: ObjectStoreFactory,
|
|
78
|
+
) -> None:
|
|
79
|
+
self.state_factory = state_factory
|
|
80
|
+
self.ffs_factory = ffs_factory
|
|
81
|
+
self.objectstore_factory = objectstore_factory
|
|
59
82
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
83
|
+
def ListAppsToLaunch(
|
|
84
|
+
self,
|
|
85
|
+
request: ListAppsToLaunchRequest,
|
|
86
|
+
context: grpc.ServicerContext,
|
|
87
|
+
) -> ListAppsToLaunchResponse:
|
|
88
|
+
"""Get run IDs with apps to launch."""
|
|
89
|
+
log(DEBUG, "ClientAppIo.ListAppsToLaunch")
|
|
63
90
|
|
|
64
|
-
|
|
65
|
-
|
|
91
|
+
# Initialize state connection
|
|
92
|
+
state = self.state_factory.state()
|
|
66
93
|
|
|
94
|
+
# Get run IDs with pending messages
|
|
95
|
+
run_ids = state.get_run_ids_with_pending_messages()
|
|
67
96
|
|
|
68
|
-
#
|
|
69
|
-
|
|
70
|
-
"""ClientAppIo API servicer."""
|
|
97
|
+
# Return run IDs
|
|
98
|
+
return ListAppsToLaunchResponse(run_ids=run_ids)
|
|
71
99
|
|
|
72
|
-
def
|
|
73
|
-
self
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
100
|
+
def RequestToken(
|
|
101
|
+
self, request: RequestTokenRequest, context: grpc.ServicerContext
|
|
102
|
+
) -> RequestTokenResponse:
|
|
103
|
+
"""Request token."""
|
|
104
|
+
log(DEBUG, "ClientAppIo.RequestToken")
|
|
77
105
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
) -> GetTokenResponse:
|
|
81
|
-
"""Get token."""
|
|
82
|
-
log(DEBUG, "ClientAppIo.GetToken")
|
|
106
|
+
# Initialize state connection
|
|
107
|
+
state = self.state_factory.state()
|
|
83
108
|
|
|
84
|
-
#
|
|
85
|
-
|
|
86
|
-
context.abort(
|
|
87
|
-
grpc.StatusCode.FAILED_PRECONDITION,
|
|
88
|
-
"No inputs available.",
|
|
89
|
-
)
|
|
90
|
-
clientapp_input = cast(ClientAppInputs, self.clientapp_input)
|
|
109
|
+
# Attempt to create a token for the provided run ID
|
|
110
|
+
token = state.create_token(request.run_id)
|
|
91
111
|
|
|
92
|
-
#
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
112
|
+
# Return the token
|
|
113
|
+
return RequestTokenResponse(token=token or "")
|
|
114
|
+
|
|
115
|
+
def GetRun(
|
|
116
|
+
self, request: GetRunRequest, context: grpc.ServicerContext
|
|
117
|
+
) -> GetRunResponse:
|
|
118
|
+
"""Get run information."""
|
|
119
|
+
log(DEBUG, "ClientAppIo.GetRun")
|
|
120
|
+
|
|
121
|
+
# Initialize state connection
|
|
122
|
+
state = self.state_factory.state()
|
|
123
|
+
|
|
124
|
+
# Retrieve run information
|
|
125
|
+
run = state.get_run(request.run_id)
|
|
98
126
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
self.token_returned = True
|
|
104
|
-
return GetTokenResponse(token=clientapp_input.token)
|
|
127
|
+
if run is None:
|
|
128
|
+
return GetRunResponse()
|
|
129
|
+
|
|
130
|
+
return GetRunResponse(run=run_to_proto(run))
|
|
105
131
|
|
|
106
132
|
def PullClientAppInputs(
|
|
107
|
-
self, request:
|
|
108
|
-
) ->
|
|
133
|
+
self, request: PullAppInputsRequest, context: grpc.ServicerContext
|
|
134
|
+
) -> PullAppInputsResponse:
|
|
109
135
|
"""Pull Message, Context, and Run."""
|
|
110
136
|
log(DEBUG, "ClientAppIo.PullClientAppInputs")
|
|
111
137
|
|
|
112
|
-
#
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
grpc.StatusCode.FAILED_PRECONDITION,
|
|
116
|
-
"No inputs available.",
|
|
117
|
-
)
|
|
118
|
-
clientapp_input = cast(ClientAppInputs, self.clientapp_input)
|
|
138
|
+
# Initialize state and ffs connection
|
|
139
|
+
state = self.state_factory.state()
|
|
140
|
+
ffs = self.ffs_factory.ffs()
|
|
119
141
|
|
|
120
|
-
#
|
|
121
|
-
|
|
142
|
+
# Validate the token
|
|
143
|
+
run_id = state.get_run_id_by_token(request.token)
|
|
144
|
+
if run_id is None or not state.verify_token(run_id, request.token):
|
|
122
145
|
context.abort(
|
|
123
|
-
grpc.StatusCode.
|
|
124
|
-
"
|
|
125
|
-
"Token must be returned before can be returned only once.",
|
|
146
|
+
grpc.StatusCode.PERMISSION_DENIED,
|
|
147
|
+
"Invalid token.",
|
|
126
148
|
)
|
|
149
|
+
raise RuntimeError("This line should never be reached.")
|
|
127
150
|
|
|
128
|
-
#
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
"Mismatch between ClientApp and SuperNode token",
|
|
133
|
-
)
|
|
151
|
+
# Retrieve context, run and fab for this run
|
|
152
|
+
context = cast(Context, state.get_context(run_id))
|
|
153
|
+
run = cast(Run, state.get_run(run_id))
|
|
154
|
+
fab = Fab(run.fab_hash, ffs.get(run.fab_hash)[0]) # type: ignore
|
|
134
155
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
context=context_to_proto(clientapp_input.context),
|
|
140
|
-
run=run_to_proto(clientapp_input.run),
|
|
141
|
-
fab=fab_to_proto(clientapp_input.fab) if clientapp_input.fab else None,
|
|
156
|
+
return PullAppInputsResponse(
|
|
157
|
+
context=context_to_proto(context),
|
|
158
|
+
run=run_to_proto(run),
|
|
159
|
+
fab=fab_to_proto(fab),
|
|
142
160
|
)
|
|
143
161
|
|
|
144
162
|
def PushClientAppOutputs(
|
|
145
|
-
self, request:
|
|
146
|
-
) ->
|
|
163
|
+
self, request: PushAppOutputsRequest, context: grpc.ServicerContext
|
|
164
|
+
) -> PushAppOutputsResponse:
|
|
147
165
|
"""Push Message and Context."""
|
|
148
166
|
log(DEBUG, "ClientAppIo.PushClientAppOutputs")
|
|
149
167
|
|
|
150
|
-
#
|
|
151
|
-
|
|
152
|
-
context.abort(
|
|
153
|
-
grpc.StatusCode.FAILED_PRECONDITION,
|
|
154
|
-
"No inputs available.",
|
|
155
|
-
)
|
|
156
|
-
clientapp_input = cast(ClientAppInputs, self.clientapp_input)
|
|
168
|
+
# Initialize state connection
|
|
169
|
+
state = self.state_factory.state()
|
|
157
170
|
|
|
158
|
-
#
|
|
159
|
-
|
|
171
|
+
# Validate the token
|
|
172
|
+
run_id = state.get_run_id_by_token(request.token)
|
|
173
|
+
if run_id is None or not state.verify_token(run_id, request.token):
|
|
160
174
|
context.abort(
|
|
161
|
-
grpc.StatusCode.
|
|
162
|
-
"
|
|
163
|
-
"Token must be returned before can be returned only once.",
|
|
175
|
+
grpc.StatusCode.PERMISSION_DENIED,
|
|
176
|
+
"Invalid token.",
|
|
164
177
|
)
|
|
178
|
+
raise RuntimeError("This line should never be reached.")
|
|
179
|
+
|
|
180
|
+
# Save the context to the state
|
|
181
|
+
state.store_context(context_from_proto(request.context))
|
|
182
|
+
|
|
183
|
+
# Remove the token to make the run eligible for processing
|
|
184
|
+
# A run associated with a token cannot be handled until its token is cleared
|
|
185
|
+
state.delete_token(run_id)
|
|
165
186
|
|
|
166
|
-
|
|
167
|
-
|
|
187
|
+
return PushAppOutputsResponse()
|
|
188
|
+
|
|
189
|
+
def PullMessage(
|
|
190
|
+
self, request: PullAppMessagesRequest, context: grpc.ServicerContext
|
|
191
|
+
) -> PullAppMessagesResponse:
|
|
192
|
+
"""Pull one Message."""
|
|
193
|
+
# Initialize state and store connection
|
|
194
|
+
state = self.state_factory.state()
|
|
195
|
+
store = self.objectstore_factory.store()
|
|
196
|
+
|
|
197
|
+
# Validate the token
|
|
198
|
+
run_id = state.get_run_id_by_token(request.token)
|
|
199
|
+
if run_id is None or not state.verify_token(run_id, request.token):
|
|
168
200
|
context.abort(
|
|
169
|
-
grpc.StatusCode.
|
|
170
|
-
"
|
|
171
|
-
"Inputs must be delivered before can be returned only once.",
|
|
201
|
+
grpc.StatusCode.PERMISSION_DENIED,
|
|
202
|
+
"Invalid token.",
|
|
172
203
|
)
|
|
204
|
+
raise RuntimeError("This line should never be reached.")
|
|
205
|
+
|
|
206
|
+
# Retrieve message for this run
|
|
207
|
+
message = state.get_messages(run_ids=[run_id], is_reply=False)[0]
|
|
173
208
|
|
|
174
|
-
#
|
|
175
|
-
|
|
209
|
+
# Retrieve the object tree for the message
|
|
210
|
+
object_tree = store.get_object_tree(message.metadata.message_id)
|
|
211
|
+
|
|
212
|
+
return PullAppMessagesResponse(
|
|
213
|
+
messages_list=[message_to_proto(message)],
|
|
214
|
+
message_object_trees=[object_tree],
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
def PushMessage(
|
|
218
|
+
self, request: PushAppMessagesRequest, context: grpc.ServicerContext
|
|
219
|
+
) -> PushAppMessagesResponse:
|
|
220
|
+
"""Push one Message."""
|
|
221
|
+
# Initialize state and store connection
|
|
222
|
+
state = self.state_factory.state()
|
|
223
|
+
store = self.objectstore_factory.store()
|
|
224
|
+
|
|
225
|
+
# Validate the token
|
|
226
|
+
run_id = state.get_run_id_by_token(request.token)
|
|
227
|
+
if run_id is None or not state.verify_token(run_id, request.token):
|
|
176
228
|
context.abort(
|
|
177
|
-
grpc.StatusCode.
|
|
178
|
-
"
|
|
229
|
+
grpc.StatusCode.PERMISSION_DENIED,
|
|
230
|
+
"Invalid token.",
|
|
179
231
|
)
|
|
232
|
+
raise RuntimeError("This line should never be reached.")
|
|
233
|
+
|
|
234
|
+
# Save the message to the state
|
|
235
|
+
state.store_message(message_from_proto(request.messages_list[0]))
|
|
236
|
+
|
|
237
|
+
# Store Message object to descendants mapping and preregister objects
|
|
238
|
+
objects_to_push = store_mapping_and_register_objects(store, request=request)
|
|
239
|
+
|
|
240
|
+
return PushAppMessagesResponse(objects_to_push=objects_to_push)
|
|
241
|
+
|
|
242
|
+
def PushObject(
|
|
243
|
+
self, request: PushObjectRequest, context: grpc.ServicerContext
|
|
244
|
+
) -> PushObjectResponse:
|
|
245
|
+
"""Push an object to the ObjectStore."""
|
|
246
|
+
log(DEBUG, "ServerAppIoServicer.PushObject")
|
|
180
247
|
|
|
181
|
-
#
|
|
248
|
+
# Init state and store
|
|
249
|
+
store = self.objectstore_factory.store()
|
|
250
|
+
|
|
251
|
+
# Insert in store
|
|
252
|
+
stored = False
|
|
182
253
|
try:
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
254
|
+
store.put(request.object_id, request.object_content)
|
|
255
|
+
stored = True
|
|
256
|
+
except (NoObjectInStoreError, ValueError) as e:
|
|
257
|
+
log(ERROR, str(e))
|
|
258
|
+
except UnexpectedObjectContentError as e:
|
|
259
|
+
# Object content is not valid
|
|
260
|
+
context.abort(grpc.StatusCode.FAILED_PRECONDITION, str(e))
|
|
261
|
+
|
|
262
|
+
return PushObjectResponse(stored=stored)
|
|
263
|
+
|
|
264
|
+
def PullObject(
|
|
265
|
+
self, request: PullObjectRequest, context: grpc.ServicerContext
|
|
266
|
+
) -> PullObjectResponse:
|
|
267
|
+
"""Pull an object from the ObjectStore."""
|
|
268
|
+
log(DEBUG, "ServerAppIoServicer.PullObject")
|
|
269
|
+
|
|
270
|
+
# Init state and store
|
|
271
|
+
store = self.objectstore_factory.store()
|
|
272
|
+
|
|
273
|
+
# Fetch from store
|
|
274
|
+
content = store.get(request.object_id)
|
|
275
|
+
if content is not None:
|
|
276
|
+
object_available = content != b""
|
|
277
|
+
return PullObjectResponse(
|
|
278
|
+
object_found=True,
|
|
279
|
+
object_available=object_available,
|
|
280
|
+
object_content=content,
|
|
187
281
|
)
|
|
282
|
+
return PullObjectResponse(object_found=False, object_available=False)
|
|
188
283
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
code = typing.ClientAppOutputCode.UNKNOWN_ERROR
|
|
195
|
-
status = typing.ClientAppOutputStatus(code=code, message="Unkonwn error")
|
|
284
|
+
def ConfirmMessageReceived(
|
|
285
|
+
self, request: ConfirmMessageReceivedRequest, context: grpc.ServicerContext
|
|
286
|
+
) -> ConfirmMessageReceivedResponse:
|
|
287
|
+
"""Confirm message received."""
|
|
288
|
+
log(DEBUG, "ServerAppIoServicer.ConfirmMessageReceived")
|
|
196
289
|
|
|
197
|
-
#
|
|
198
|
-
|
|
199
|
-
return PushClientAppOutputsResponse(status=proto_status)
|
|
290
|
+
# Init state and store
|
|
291
|
+
store = self.objectstore_factory.store()
|
|
200
292
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
Parameters
|
|
207
|
-
----------
|
|
208
|
-
clientapp_input : ClientAppInputs
|
|
209
|
-
The inputs to the ClientApp.
|
|
210
|
-
token_returned : bool
|
|
211
|
-
A boolean indicating if the token has been returned.
|
|
212
|
-
Set to `True` when passing the token to `flwr-clientap`
|
|
213
|
-
and `False` otherwise.
|
|
214
|
-
"""
|
|
215
|
-
if (
|
|
216
|
-
self.clientapp_input is not None
|
|
217
|
-
or self.clientapp_output is not None
|
|
218
|
-
or self.token_returned
|
|
219
|
-
):
|
|
220
|
-
raise ValueError(
|
|
221
|
-
"ClientAppInputs and ClientAppOutputs must not be set before "
|
|
222
|
-
"calling `set_inputs`."
|
|
223
|
-
)
|
|
224
|
-
log(DEBUG, "ClientAppInputs set (token: %s)", clientapp_input.token)
|
|
225
|
-
self.clientapp_input = clientapp_input
|
|
226
|
-
self.token_returned = token_returned
|
|
227
|
-
|
|
228
|
-
def has_outputs(self) -> bool:
|
|
229
|
-
"""Check if ClientAppOutputs are available."""
|
|
230
|
-
return self.clientapp_output is not None
|
|
231
|
-
|
|
232
|
-
def get_outputs(self) -> ClientAppOutputs:
|
|
233
|
-
"""Get ClientApp outputs."""
|
|
234
|
-
if self.clientapp_output is None:
|
|
235
|
-
raise ValueError("ClientAppOutputs not set before calling `get_outputs`.")
|
|
236
|
-
|
|
237
|
-
# Set outputs to a local variable and clear state
|
|
238
|
-
output: ClientAppOutputs = self.clientapp_output
|
|
239
|
-
self.clientapp_input = None
|
|
240
|
-
self.clientapp_output = None
|
|
241
|
-
self.token_returned = False
|
|
242
|
-
self.inputs_returned = False
|
|
243
|
-
|
|
244
|
-
return output
|
|
293
|
+
# Delete the message object
|
|
294
|
+
store.delete(request.message_object_id)
|
|
295
|
+
|
|
296
|
+
return ConfirmMessageReceivedResponse()
|