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.
Files changed (167) hide show
  1. flwr/__init__.py +4 -1
  2. flwr/app/__init__.py +28 -0
  3. flwr/app/exception.py +31 -0
  4. flwr/cli/auth_plugin/oidc_cli_plugin.py +4 -4
  5. flwr/cli/build.py +15 -5
  6. flwr/cli/cli_user_auth_interceptor.py +1 -1
  7. flwr/cli/config_utils.py +3 -3
  8. flwr/cli/constant.py +25 -8
  9. flwr/cli/log.py +9 -9
  10. flwr/cli/login/login.py +3 -3
  11. flwr/cli/ls.py +5 -5
  12. flwr/cli/new/new.py +23 -4
  13. flwr/cli/new/templates/app/README.flowertune.md.tpl +2 -0
  14. flwr/cli/new/templates/app/README.md.tpl +5 -0
  15. flwr/cli/new/templates/app/code/__init__.pytorch_msg_api.py.tpl +1 -0
  16. flwr/cli/new/templates/app/code/client.pytorch_msg_api.py.tpl +80 -0
  17. flwr/cli/new/templates/app/code/server.pytorch_msg_api.py.tpl +41 -0
  18. flwr/cli/new/templates/app/code/task.pytorch_msg_api.py.tpl +98 -0
  19. flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +14 -3
  20. flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +13 -1
  21. flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +21 -2
  22. flwr/cli/new/templates/app/pyproject.jax.toml.tpl +18 -1
  23. flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +19 -2
  24. flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +18 -1
  25. flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +20 -3
  26. flwr/cli/new/templates/app/pyproject.pytorch_msg_api.toml.tpl +53 -0
  27. flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +18 -1
  28. flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +18 -1
  29. flwr/cli/run/run.py +53 -50
  30. flwr/cli/stop.py +7 -4
  31. flwr/cli/utils.py +29 -11
  32. flwr/client/grpc_adapter_client/connection.py +11 -4
  33. flwr/client/grpc_rere_client/connection.py +93 -129
  34. flwr/client/rest_client/connection.py +134 -164
  35. flwr/clientapp/__init__.py +10 -0
  36. flwr/clientapp/mod/__init__.py +26 -0
  37. flwr/clientapp/mod/centraldp_mods.py +132 -0
  38. flwr/common/args.py +20 -6
  39. flwr/common/auth_plugin/__init__.py +4 -4
  40. flwr/common/auth_plugin/auth_plugin.py +7 -7
  41. flwr/common/constant.py +26 -5
  42. flwr/common/event_log_plugin/event_log_plugin.py +1 -1
  43. flwr/common/exit/__init__.py +4 -0
  44. flwr/common/exit/exit.py +8 -1
  45. flwr/common/exit/exit_code.py +42 -8
  46. flwr/common/exit/exit_handler.py +62 -0
  47. flwr/common/{exit_handlers.py → exit/signal_handler.py} +20 -37
  48. flwr/common/grpc.py +1 -1
  49. flwr/common/{inflatable_grpc_utils.py → inflatable_protobuf_utils.py} +52 -10
  50. flwr/common/inflatable_utils.py +191 -24
  51. flwr/common/logger.py +1 -1
  52. flwr/common/record/array.py +101 -22
  53. flwr/common/record/arraychunk.py +59 -0
  54. flwr/common/retry_invoker.py +30 -11
  55. flwr/common/serde.py +0 -28
  56. flwr/common/telemetry.py +4 -0
  57. flwr/compat/client/app.py +14 -31
  58. flwr/compat/server/app.py +2 -2
  59. flwr/proto/appio_pb2.py +51 -0
  60. flwr/proto/appio_pb2.pyi +195 -0
  61. flwr/proto/appio_pb2_grpc.py +4 -0
  62. flwr/proto/appio_pb2_grpc.pyi +4 -0
  63. flwr/proto/clientappio_pb2.py +4 -19
  64. flwr/proto/clientappio_pb2.pyi +0 -125
  65. flwr/proto/clientappio_pb2_grpc.py +269 -29
  66. flwr/proto/clientappio_pb2_grpc.pyi +114 -21
  67. flwr/proto/control_pb2.py +62 -0
  68. flwr/proto/{exec_pb2_grpc.py → control_pb2_grpc.py} +54 -54
  69. flwr/proto/{exec_pb2_grpc.pyi → control_pb2_grpc.pyi} +28 -28
  70. flwr/proto/fleet_pb2.py +12 -20
  71. flwr/proto/fleet_pb2.pyi +6 -36
  72. flwr/proto/serverappio_pb2.py +8 -31
  73. flwr/proto/serverappio_pb2.pyi +0 -152
  74. flwr/proto/serverappio_pb2_grpc.py +107 -38
  75. flwr/proto/serverappio_pb2_grpc.pyi +47 -20
  76. flwr/proto/simulationio_pb2.py +4 -11
  77. flwr/proto/simulationio_pb2.pyi +0 -58
  78. flwr/proto/simulationio_pb2_grpc.py +129 -27
  79. flwr/proto/simulationio_pb2_grpc.pyi +52 -13
  80. flwr/server/app.py +130 -153
  81. flwr/server/fleet_event_log_interceptor.py +4 -0
  82. flwr/server/grid/grpc_grid.py +94 -54
  83. flwr/server/grid/inmemory_grid.py +1 -0
  84. flwr/server/serverapp/app.py +165 -144
  85. flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +8 -0
  86. flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +1 -1
  87. flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +2 -5
  88. flwr/server/superlink/fleet/message_handler/message_handler.py +10 -16
  89. flwr/server/superlink/fleet/rest_rere/rest_api.py +1 -2
  90. flwr/server/superlink/fleet/vce/backend/raybackend.py +3 -1
  91. flwr/server/superlink/fleet/vce/vce_api.py +6 -6
  92. flwr/server/superlink/linkstate/in_memory_linkstate.py +34 -0
  93. flwr/server/superlink/linkstate/linkstate.py +2 -1
  94. flwr/server/superlink/linkstate/sqlite_linkstate.py +45 -0
  95. flwr/server/superlink/serverappio/serverappio_grpc.py +2 -2
  96. flwr/server/superlink/serverappio/serverappio_servicer.py +95 -48
  97. flwr/server/superlink/simulation/simulationio_grpc.py +1 -1
  98. flwr/server/superlink/simulation/simulationio_servicer.py +98 -22
  99. flwr/server/superlink/utils.py +0 -35
  100. flwr/serverapp/__init__.py +12 -0
  101. flwr/serverapp/dp_fixed_clipping.py +352 -0
  102. flwr/serverapp/exception.py +38 -0
  103. flwr/serverapp/strategy/__init__.py +38 -0
  104. flwr/serverapp/strategy/dp_fixed_clipping.py +352 -0
  105. flwr/serverapp/strategy/fedadagrad.py +162 -0
  106. flwr/serverapp/strategy/fedadam.py +181 -0
  107. flwr/serverapp/strategy/fedavg.py +295 -0
  108. flwr/serverapp/strategy/fedopt.py +218 -0
  109. flwr/serverapp/strategy/fedyogi.py +173 -0
  110. flwr/serverapp/strategy/result.py +105 -0
  111. flwr/serverapp/strategy/strategy.py +285 -0
  112. flwr/serverapp/strategy/strategy_utils.py +251 -0
  113. flwr/serverapp/strategy/strategy_utils_tests.py +304 -0
  114. flwr/simulation/app.py +159 -154
  115. flwr/simulation/run_simulation.py +17 -0
  116. flwr/supercore/app_utils.py +58 -0
  117. flwr/supercore/cli/__init__.py +22 -0
  118. flwr/supercore/cli/flower_superexec.py +141 -0
  119. flwr/supercore/corestate/__init__.py +22 -0
  120. flwr/supercore/corestate/corestate.py +81 -0
  121. flwr/{server/superlink → supercore}/ffs/disk_ffs.py +1 -1
  122. flwr/supercore/grpc_health/__init__.py +25 -0
  123. flwr/supercore/grpc_health/health_server.py +53 -0
  124. flwr/supercore/grpc_health/simple_health_servicer.py +38 -0
  125. flwr/supercore/license_plugin/__init__.py +22 -0
  126. flwr/supercore/license_plugin/license_plugin.py +26 -0
  127. flwr/supercore/object_store/in_memory_object_store.py +31 -31
  128. flwr/supercore/object_store/object_store.py +20 -42
  129. flwr/supercore/object_store/utils.py +43 -0
  130. flwr/{superexec → supercore/superexec}/__init__.py +1 -1
  131. flwr/supercore/superexec/plugin/__init__.py +28 -0
  132. flwr/supercore/superexec/plugin/base_exec_plugin.py +53 -0
  133. flwr/supercore/superexec/plugin/clientapp_exec_plugin.py +28 -0
  134. flwr/supercore/superexec/plugin/exec_plugin.py +71 -0
  135. flwr/supercore/superexec/plugin/serverapp_exec_plugin.py +28 -0
  136. flwr/supercore/superexec/plugin/simulation_exec_plugin.py +28 -0
  137. flwr/supercore/superexec/run_superexec.py +185 -0
  138. flwr/supercore/utils.py +32 -0
  139. flwr/superlink/servicer/__init__.py +15 -0
  140. flwr/superlink/servicer/control/__init__.py +22 -0
  141. flwr/{superexec/exec_event_log_interceptor.py → superlink/servicer/control/control_event_log_interceptor.py} +9 -5
  142. flwr/{superexec/exec_grpc.py → superlink/servicer/control/control_grpc.py} +39 -28
  143. flwr/superlink/servicer/control/control_license_interceptor.py +82 -0
  144. flwr/{superexec/exec_servicer.py → superlink/servicer/control/control_servicer.py} +79 -31
  145. flwr/{superexec/exec_user_auth_interceptor.py → superlink/servicer/control/control_user_auth_interceptor.py} +18 -10
  146. flwr/supernode/cli/flower_supernode.py +3 -7
  147. flwr/supernode/cli/flwr_clientapp.py +20 -16
  148. flwr/supernode/nodestate/in_memory_nodestate.py +13 -4
  149. flwr/supernode/nodestate/nodestate.py +3 -44
  150. flwr/supernode/runtime/run_clientapp.py +129 -115
  151. flwr/supernode/servicer/clientappio/__init__.py +1 -3
  152. flwr/supernode/servicer/clientappio/clientappio_servicer.py +217 -165
  153. flwr/supernode/start_client_internal.py +205 -148
  154. {flwr-1.19.0.dist-info → flwr-1.21.0.dist-info}/METADATA +5 -3
  155. {flwr-1.19.0.dist-info → flwr-1.21.0.dist-info}/RECORD +161 -117
  156. {flwr-1.19.0.dist-info → flwr-1.21.0.dist-info}/entry_points.txt +1 -0
  157. flwr/common/inflatable_rest_utils.py +0 -99
  158. flwr/proto/exec_pb2.py +0 -62
  159. flwr/superexec/app.py +0 -45
  160. flwr/superexec/deployment.py +0 -192
  161. flwr/superexec/executor.py +0 -100
  162. flwr/superexec/simulation.py +0 -130
  163. /flwr/proto/{exec_pb2.pyi → control_pb2.pyi} +0 -0
  164. /flwr/{server/superlink → supercore}/ffs/__init__.py +0 -0
  165. /flwr/{server/superlink → supercore}/ffs/ffs.py +0 -0
  166. /flwr/{server/superlink → supercore}/ffs/ffs_factory.py +0 -0
  167. {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 Optional, cast
19
+ from typing import cast
21
20
 
22
21
  import grpc
23
22
 
24
- from flwr.common import Context, Message, typing
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.clientappio_pb2 import ( # pylint: disable=E0401
40
- GetTokenRequest,
41
- GetTokenResponse,
42
- PullClientAppInputsRequest,
43
- PullClientAppInputsResponse,
44
- PushClientAppOutputsRequest,
45
- PushClientAppOutputsResponse,
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
- message: Message
54
- context: Context
55
- run: Run
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
- @dataclass
61
- class ClientAppOutputs:
62
- """Specify the outputs from the ClientApp."""
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
- message: Message
65
- context: Context
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
- # pylint: disable=C0103,W0613,W0201
69
- class ClientAppIoServicer(clientappio_pb2_grpc.ClientAppIoServicer):
70
- """ClientAppIo API servicer."""
97
+ # Return run IDs
98
+ return ListAppsToLaunchResponse(run_ids=run_ids)
71
99
 
72
- def __init__(self) -> None:
73
- self.clientapp_input: Optional[ClientAppInputs] = None
74
- self.clientapp_output: Optional[ClientAppOutputs] = None
75
- self.token_returned: bool = False
76
- self.inputs_returned: bool = False
100
+ def RequestToken(
101
+ self, request: RequestTokenRequest, context: grpc.ServicerContext
102
+ ) -> RequestTokenResponse:
103
+ """Request token."""
104
+ log(DEBUG, "ClientAppIo.RequestToken")
77
105
 
78
- def GetToken(
79
- self, request: GetTokenRequest, context: grpc.ServicerContext
80
- ) -> GetTokenResponse:
81
- """Get token."""
82
- log(DEBUG, "ClientAppIo.GetToken")
106
+ # Initialize state connection
107
+ state = self.state_factory.state()
83
108
 
84
- # Fail if no ClientAppInputs are available
85
- if self.clientapp_input is None:
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
- # Fail if token was already returned in a previous call
93
- if self.token_returned:
94
- context.abort(
95
- grpc.StatusCode.FAILED_PRECONDITION,
96
- "Token already returned. A token can be returned only once.",
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
- # If
100
- # - ClientAppInputs is set, and
101
- # - token hasn't been returned before,
102
- # return token
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: PullClientAppInputsRequest, context: grpc.ServicerContext
108
- ) -> PullClientAppInputsResponse:
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
- # Fail if no ClientAppInputs are available
113
- if self.clientapp_input is None:
114
- context.abort(
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
- # Fail if token wasn't returned in a previous call
121
- if not self.token_returned:
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.FAILED_PRECONDITION,
124
- "Token hasn't been returned."
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
- # Fail if token isn't matching
129
- if request.token != clientapp_input.token:
130
- context.abort(
131
- grpc.StatusCode.INVALID_ARGUMENT,
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
- # Success
136
- self.inputs_returned = True
137
- return PullClientAppInputsResponse(
138
- message=message_to_proto(clientapp_input.message),
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: PushClientAppOutputsRequest, context: grpc.ServicerContext
146
- ) -> PushClientAppOutputsResponse:
163
+ self, request: PushAppOutputsRequest, context: grpc.ServicerContext
164
+ ) -> PushAppOutputsResponse:
147
165
  """Push Message and Context."""
148
166
  log(DEBUG, "ClientAppIo.PushClientAppOutputs")
149
167
 
150
- # Fail if no ClientAppInputs are available
151
- if not self.clientapp_input:
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
- # Fail if token wasn't returned in a previous call
159
- if not self.token_returned:
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.FAILED_PRECONDITION,
162
- "Token hasn't been returned."
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
- # Fail if inputs weren't delivered in a previous call
167
- if not self.inputs_returned:
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.FAILED_PRECONDITION,
170
- "Inputs haven't been delivered."
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
- # Fail if token isn't matching
175
- if request.token != clientapp_input.token:
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.INVALID_ARGUMENT,
178
- "Mismatch between ClientApp and SuperNode token",
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
- # Preconditions met
248
+ # Init state and store
249
+ store = self.objectstore_factory.store()
250
+
251
+ # Insert in store
252
+ stored = False
182
253
  try:
183
- # Update Message and Context
184
- self.clientapp_output = ClientAppOutputs(
185
- message=message_from_proto(request.message),
186
- context=context_from_proto(request.context),
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
- # Set status
190
- code = typing.ClientAppOutputCode.SUCCESS
191
- status = typing.ClientAppOutputStatus(code=code, message="Success")
192
- except Exception as e: # pylint: disable=broad-exception-caught
193
- log(ERROR, "ClientApp failed to push message to SuperNode, %s", e)
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
- # Return status to ClientApp process
198
- proto_status = clientappstatus_to_proto(status=status)
199
- return PushClientAppOutputsResponse(status=proto_status)
290
+ # Init state and store
291
+ store = self.objectstore_factory.store()
200
292
 
201
- def set_inputs(
202
- self, clientapp_input: ClientAppInputs, token_returned: bool
203
- ) -> None:
204
- """Set ClientApp inputs.
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()