flwr-nightly 1.11.0.dev20240815__py3-none-any.whl → 1.11.0.dev20240817__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.

Potentially problematic release.


This version of flwr-nightly might be problematic. Click here for more details.

Files changed (32) hide show
  1. flwr/cli/config_utils.py +2 -2
  2. flwr/cli/run/run.py +5 -4
  3. flwr/client/app.py +97 -11
  4. flwr/client/{process → clientapp}/__init__.py +7 -0
  5. flwr/client/clientapp/app.py +178 -0
  6. flwr/client/clientapp/clientappio_servicer.py +238 -0
  7. flwr/client/clientapp/utils.py +108 -0
  8. flwr/client/grpc_rere_client/connection.py +8 -1
  9. flwr/client/rest_client/connection.py +14 -2
  10. flwr/client/supernode/__init__.py +0 -2
  11. flwr/client/supernode/app.py +21 -120
  12. flwr/proto/clientappio_pb2.py +17 -13
  13. flwr/proto/clientappio_pb2.pyi +17 -0
  14. flwr/proto/clientappio_pb2_grpc.py +34 -0
  15. flwr/proto/clientappio_pb2_grpc.pyi +13 -0
  16. flwr/server/app.py +3 -0
  17. flwr/server/run_serverapp.py +18 -2
  18. flwr/server/server.py +3 -1
  19. flwr/server/superlink/driver/driver_servicer.py +23 -8
  20. flwr/server/superlink/ffs/disk_ffs.py +6 -3
  21. flwr/server/superlink/ffs/ffs.py +3 -3
  22. flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +9 -3
  23. flwr/server/superlink/fleet/message_handler/message_handler.py +16 -1
  24. flwr/server/superlink/fleet/vce/vce_api.py +2 -2
  25. flwr/server/workflow/default_workflows.py +3 -1
  26. flwr/superexec/deployment.py +8 -9
  27. {flwr_nightly-1.11.0.dev20240815.dist-info → flwr_nightly-1.11.0.dev20240817.dist-info}/METADATA +1 -1
  28. {flwr_nightly-1.11.0.dev20240815.dist-info → flwr_nightly-1.11.0.dev20240817.dist-info}/RECORD +31 -29
  29. {flwr_nightly-1.11.0.dev20240815.dist-info → flwr_nightly-1.11.0.dev20240817.dist-info}/entry_points.txt +1 -1
  30. flwr/client/process/clientappio_servicer.py +0 -145
  31. {flwr_nightly-1.11.0.dev20240815.dist-info → flwr_nightly-1.11.0.dev20240817.dist-info}/LICENSE +0 -0
  32. {flwr_nightly-1.11.0.dev20240815.dist-info → flwr_nightly-1.11.0.dev20240817.dist-info}/WHEEL +0 -0
@@ -0,0 +1,108 @@
1
+ # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+ """Flower ClientApp loading utils."""
16
+
17
+ from logging import DEBUG
18
+ from pathlib import Path
19
+ from typing import Callable, Optional
20
+
21
+ from flwr.client.client_app import ClientApp, LoadClientAppError
22
+ from flwr.common.config import (
23
+ get_flwr_dir,
24
+ get_metadata_from_config,
25
+ get_project_config,
26
+ get_project_dir,
27
+ )
28
+ from flwr.common.logger import log
29
+ from flwr.common.object_ref import load_app, validate
30
+
31
+
32
+ def get_load_client_app_fn(
33
+ default_app_ref: str,
34
+ app_path: Optional[str],
35
+ multi_app: bool,
36
+ flwr_dir: Optional[str] = None,
37
+ ) -> Callable[[str, str], ClientApp]:
38
+ """Get the load_client_app_fn function.
39
+
40
+ If `multi_app` is True, this function loads the specified ClientApp
41
+ based on `fab_id` and `fab_version`. If `fab_id` is empty, a default
42
+ ClientApp will be loaded.
43
+
44
+ If `multi_app` is False, it ignores `fab_id` and `fab_version` and
45
+ loads a default ClientApp.
46
+ """
47
+ if not multi_app:
48
+ log(
49
+ DEBUG,
50
+ "Flower SuperNode will load and validate ClientApp `%s`",
51
+ default_app_ref,
52
+ )
53
+
54
+ valid, error_msg = validate(default_app_ref, project_dir=app_path)
55
+ if not valid and error_msg:
56
+ raise LoadClientAppError(error_msg) from None
57
+
58
+ def _load(fab_id: str, fab_version: str) -> ClientApp:
59
+ runtime_app_dir = Path(app_path if app_path else "").absolute()
60
+ # If multi-app feature is disabled
61
+ if not multi_app:
62
+ # Set app reference
63
+ client_app_ref = default_app_ref
64
+ # If multi-app feature is enabled but app directory is provided
65
+ elif app_path is not None:
66
+ config = get_project_config(runtime_app_dir)
67
+ this_fab_version, this_fab_id = get_metadata_from_config(config)
68
+
69
+ if this_fab_version != fab_version or this_fab_id != fab_id:
70
+ raise LoadClientAppError(
71
+ f"FAB ID or version mismatch: Expected FAB ID '{this_fab_id}' and "
72
+ f"FAB version '{this_fab_version}', but received FAB ID '{fab_id}' "
73
+ f"and FAB version '{fab_version}'.",
74
+ ) from None
75
+
76
+ # log(WARN, "FAB ID is not provided; the default ClientApp will be loaded.")
77
+
78
+ # Set app reference
79
+ client_app_ref = config["tool"]["flwr"]["app"]["components"]["clientapp"]
80
+ # If multi-app feature is enabled
81
+ else:
82
+ try:
83
+ runtime_app_dir = get_project_dir(
84
+ fab_id, fab_version, get_flwr_dir(flwr_dir)
85
+ )
86
+ config = get_project_config(runtime_app_dir)
87
+ except Exception as e:
88
+ raise LoadClientAppError("Failed to load ClientApp") from e
89
+
90
+ # Set app reference
91
+ client_app_ref = config["tool"]["flwr"]["app"]["components"]["clientapp"]
92
+
93
+ # Load ClientApp
94
+ log(
95
+ DEBUG,
96
+ "Loading ClientApp `%s`",
97
+ client_app_ref,
98
+ )
99
+ client_app = load_app(client_app_ref, LoadClientAppError, runtime_app_dir)
100
+
101
+ if not isinstance(client_app, ClientApp):
102
+ raise LoadClientAppError(
103
+ f"Attribute {client_app_ref} is not of type {ClientApp}",
104
+ ) from None
105
+
106
+ return client_app
107
+
108
+ return _load
@@ -46,6 +46,7 @@ from flwr.common.serde import (
46
46
  user_config_from_proto,
47
47
  )
48
48
  from flwr.common.typing import Fab, Run
49
+ from flwr.proto.fab_pb2 import GetFabRequest, GetFabResponse # pylint: disable=E0611
49
50
  from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
50
51
  CreateNodeRequest,
51
52
  DeleteNodeRequest,
@@ -292,7 +293,13 @@ def grpc_request_response( # pylint: disable=R0913, R0914, R0915
292
293
 
293
294
  def get_fab(fab_hash: str) -> Fab:
294
295
  # Call FleetAPI
295
- raise NotImplementedError
296
+ get_fab_request = GetFabRequest(hash_str=fab_hash)
297
+ get_fab_response: GetFabResponse = retry_invoker.invoke(
298
+ stub.GetFab,
299
+ request=get_fab_request,
300
+ )
301
+
302
+ return Fab(get_fab_response.fab.hash_str, get_fab_response.fab.content)
296
303
 
297
304
  try:
298
305
  # Yield methods
@@ -46,6 +46,7 @@ from flwr.common.serde import (
46
46
  user_config_from_proto,
47
47
  )
48
48
  from flwr.common.typing import Fab, Run
49
+ from flwr.proto.fab_pb2 import GetFabRequest, GetFabResponse # pylint: disable=E0611
49
50
  from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
50
51
  CreateNodeRequest,
51
52
  CreateNodeResponse,
@@ -74,6 +75,7 @@ PATH_PULL_TASK_INS: str = "api/v0/fleet/pull-task-ins"
74
75
  PATH_PUSH_TASK_RES: str = "api/v0/fleet/push-task-res"
75
76
  PATH_PING: str = "api/v0/fleet/ping"
76
77
  PATH_GET_RUN: str = "/api/v0/fleet/get-run"
78
+ PATH_GET_FAB: str = "/api/v0/fleet/get-fab"
77
79
 
78
80
  T = TypeVar("T", bound=GrpcMessage)
79
81
 
@@ -369,8 +371,18 @@ def http_request_response( # pylint: disable=,R0913, R0914, R0915
369
371
  )
370
372
 
371
373
  def get_fab(fab_hash: str) -> Fab:
372
- # Call FleetAPI
373
- raise NotImplementedError
374
+ # Construct the request
375
+ req = GetFabRequest(hash_str=fab_hash)
376
+
377
+ # Send the request
378
+ res = _request(req, GetFabResponse, PATH_GET_FAB)
379
+ if res is None:
380
+ return Fab("", b"")
381
+
382
+ return Fab(
383
+ res.fab.hash_str,
384
+ res.fab.content,
385
+ )
374
386
 
375
387
  try:
376
388
  # Yield methods
@@ -15,12 +15,10 @@
15
15
  """Flower SuperNode."""
16
16
 
17
17
 
18
- from .app import flwr_clientapp as flwr_clientapp
19
18
  from .app import run_client_app as run_client_app
20
19
  from .app import run_supernode as run_supernode
21
20
 
22
21
  __all__ = [
23
- "flwr_clientapp",
24
22
  "run_client_app",
25
23
  "run_supernode",
26
24
  ]
@@ -18,7 +18,7 @@ import argparse
18
18
  import sys
19
19
  from logging import DEBUG, INFO, WARN
20
20
  from pathlib import Path
21
- from typing import Callable, Optional, Tuple
21
+ from typing import Optional, Tuple
22
22
 
23
23
  from cryptography.exceptions import UnsupportedAlgorithm
24
24
  from cryptography.hazmat.primitives.asymmetric import ec
@@ -27,15 +27,8 @@ from cryptography.hazmat.primitives.serialization import (
27
27
  load_ssh_public_key,
28
28
  )
29
29
 
30
- from flwr.client.client_app import ClientApp, LoadClientAppError
31
30
  from flwr.common import EventType, event
32
- from flwr.common.config import (
33
- get_flwr_dir,
34
- get_metadata_from_config,
35
- get_project_config,
36
- get_project_dir,
37
- parse_config_args,
38
- )
31
+ from flwr.common.config import parse_config_args
39
32
  from flwr.common.constant import (
40
33
  TRANSPORT_TYPE_GRPC_ADAPTER,
41
34
  TRANSPORT_TYPE_GRPC_RERE,
@@ -43,9 +36,9 @@ from flwr.common.constant import (
43
36
  )
44
37
  from flwr.common.exit_handlers import register_exit_handlers
45
38
  from flwr.common.logger import log, warn_deprecated_feature
46
- from flwr.common.object_ref import load_app, validate
47
39
 
48
- from ..app import _start_client_internal
40
+ from ..app import start_client_internal
41
+ from ..clientapp.utils import get_load_client_app_fn
49
42
 
50
43
  ADDRESS_FLEET_API_GRPC_RERE = "0.0.0.0:9092"
51
44
 
@@ -61,7 +54,7 @@ def run_supernode() -> None:
61
54
  _warn_deprecated_server_arg(args)
62
55
 
63
56
  root_certificates = _get_certificates(args)
64
- load_fn = _get_load_client_app_fn(
57
+ load_fn = get_load_client_app_fn(
65
58
  default_app_ref="",
66
59
  app_path=args.app,
67
60
  flwr_dir=args.flwr_dir,
@@ -69,7 +62,7 @@ def run_supernode() -> None:
69
62
  )
70
63
  authentication_keys = _try_setup_client_authentication(args)
71
64
 
72
- _start_client_internal(
65
+ start_client_internal(
73
66
  server_address=args.superlink,
74
67
  load_client_app_fn=load_fn,
75
68
  transport=args.transport,
@@ -79,7 +72,8 @@ def run_supernode() -> None:
79
72
  max_retries=args.max_retries,
80
73
  max_wait_time=args.max_wait_time,
81
74
  node_config=parse_config_args([args.node_config]),
82
- flwr_path=get_flwr_dir(args.flwr_dir),
75
+ isolate=args.isolate,
76
+ supernode_address=args.supernode_address,
83
77
  )
84
78
 
85
79
  # Graceful shutdown
@@ -99,14 +93,14 @@ def run_client_app() -> None:
99
93
  _warn_deprecated_server_arg(args)
100
94
 
101
95
  root_certificates = _get_certificates(args)
102
- load_fn = _get_load_client_app_fn(
96
+ load_fn = get_load_client_app_fn(
103
97
  default_app_ref=getattr(args, "client-app"),
104
98
  app_path=args.dir,
105
99
  multi_app=False,
106
100
  )
107
101
  authentication_keys = _try_setup_client_authentication(args)
108
102
 
109
- _start_client_internal(
103
+ start_client_internal(
110
104
  server_address=args.superlink,
111
105
  node_config=parse_config_args([args.node_config]),
112
106
  load_client_app_fn=load_fn,
@@ -120,31 +114,6 @@ def run_client_app() -> None:
120
114
  register_exit_handlers(event_type=EventType.RUN_CLIENT_APP_LEAVE)
121
115
 
122
116
 
123
- def flwr_clientapp() -> None:
124
- """Run process-isolated Flower ClientApp."""
125
- log(INFO, "Starting Flower ClientApp")
126
-
127
- parser = argparse.ArgumentParser(
128
- description="Run a Flower ClientApp",
129
- )
130
- parser.add_argument(
131
- "--address",
132
- help="Address of SuperNode ClientAppIo gRPC servicer",
133
- )
134
- parser.add_argument(
135
- "--token",
136
- help="Unique token generated by SuperNode for each ClientApp execution",
137
- )
138
- args = parser.parse_args()
139
- log(
140
- DEBUG,
141
- "Staring isolated `ClientApp` connected to SuperNode ClientAppIo at %s "
142
- "with the token %s",
143
- args.address,
144
- args.token,
145
- )
146
-
147
-
148
117
  def _warn_deprecated_server_arg(args: argparse.Namespace) -> None:
149
118
  """Warn about the deprecated argument `--server`."""
150
119
  if args.server != ADDRESS_FLEET_API_GRPC_RERE:
@@ -200,85 +169,6 @@ def _get_certificates(args: argparse.Namespace) -> Optional[bytes]:
200
169
  return root_certificates
201
170
 
202
171
 
203
- def _get_load_client_app_fn(
204
- default_app_ref: str,
205
- app_path: Optional[str],
206
- multi_app: bool,
207
- flwr_dir: Optional[str] = None,
208
- ) -> Callable[[str, str], ClientApp]:
209
- """Get the load_client_app_fn function.
210
-
211
- If `multi_app` is True, this function loads the specified ClientApp
212
- based on `fab_id` and `fab_version`. If `fab_id` is empty, a default
213
- ClientApp will be loaded.
214
-
215
- If `multi_app` is False, it ignores `fab_id` and `fab_version` and
216
- loads a default ClientApp.
217
- """
218
- if not multi_app:
219
- log(
220
- DEBUG,
221
- "Flower SuperNode will load and validate ClientApp `%s`",
222
- default_app_ref,
223
- )
224
-
225
- valid, error_msg = validate(default_app_ref, project_dir=app_path)
226
- if not valid and error_msg:
227
- raise LoadClientAppError(error_msg) from None
228
-
229
- def _load(fab_id: str, fab_version: str) -> ClientApp:
230
- runtime_app_dir = Path(app_path if app_path else "").absolute()
231
- # If multi-app feature is disabled
232
- if not multi_app:
233
- # Set app reference
234
- client_app_ref = default_app_ref
235
- # If multi-app feature is enabled but app directory is provided
236
- elif app_path is not None:
237
- config = get_project_config(runtime_app_dir)
238
- this_fab_version, this_fab_id = get_metadata_from_config(config)
239
-
240
- if this_fab_version != fab_version or this_fab_id != fab_id:
241
- raise LoadClientAppError(
242
- f"FAB ID or version mismatch: Expected FAB ID '{this_fab_id}' and "
243
- f"FAB version '{this_fab_version}', but received FAB ID '{fab_id}' "
244
- f"and FAB version '{fab_version}'.",
245
- ) from None
246
-
247
- # log(WARN, "FAB ID is not provided; the default ClientApp will be loaded.")
248
-
249
- # Set app reference
250
- client_app_ref = config["tool"]["flwr"]["app"]["components"]["clientapp"]
251
- # If multi-app feature is enabled
252
- else:
253
- try:
254
- runtime_app_dir = get_project_dir(
255
- fab_id, fab_version, get_flwr_dir(flwr_dir)
256
- )
257
- config = get_project_config(runtime_app_dir)
258
- except Exception as e:
259
- raise LoadClientAppError("Failed to load ClientApp") from e
260
-
261
- # Set app reference
262
- client_app_ref = config["tool"]["flwr"]["app"]["components"]["clientapp"]
263
-
264
- # Load ClientApp
265
- log(
266
- DEBUG,
267
- "Loading ClientApp `%s`",
268
- client_app_ref,
269
- )
270
- client_app = load_app(client_app_ref, LoadClientAppError, runtime_app_dir)
271
-
272
- if not isinstance(client_app, ClientApp):
273
- raise LoadClientAppError(
274
- f"Attribute {client_app_ref} is not of type {ClientApp}",
275
- ) from None
276
-
277
- return client_app
278
-
279
- return _load
280
-
281
-
282
172
  def _parse_args_run_supernode() -> argparse.ArgumentParser:
283
173
  """Parse flower-supernode command line arguments."""
284
174
  parser = argparse.ArgumentParser(
@@ -308,6 +198,17 @@ def _parse_args_run_supernode() -> argparse.ArgumentParser:
308
198
  - `$HOME/.flwr/` in all other cases
309
199
  """,
310
200
  )
201
+ parser.add_argument(
202
+ "--isolate",
203
+ action="store_true",
204
+ help="Run `ClientApp` in an isolated subprocess. By default, `ClientApp` "
205
+ "runs in the same process that executes the SuperNode.",
206
+ )
207
+ parser.add_argument(
208
+ "--supernode-address",
209
+ default="0.0.0.0:9094",
210
+ help="Set the SuperNode gRPC server address. Defaults to `0.0.0.0:9094`.",
211
+ )
311
212
 
312
213
  return parser
313
214
 
@@ -17,25 +17,29 @@ from flwr.proto import run_pb2 as flwr_dot_proto_dot_run__pb2
17
17
  from flwr.proto import message_pb2 as flwr_dot_proto_dot_message__pb2
18
18
 
19
19
 
20
- DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1c\x66lwr/proto/clientappio.proto\x12\nflwr.proto\x1a\x14\x66lwr/proto/fab.proto\x1a\x14\x66lwr/proto/run.proto\x1a\x18\x66lwr/proto/message.proto\"W\n\x15\x43lientAppOutputStatus\x12-\n\x04\x63ode\x18\x01 \x01(\x0e\x32\x1f.flwr.proto.ClientAppOutputCode\x12\x0f\n\x07message\x18\x02 \x01(\t\"+\n\x1aPullClientAppInputsRequest\x12\r\n\x05token\x18\x01 \x01(\x12\"\x87\x01\n\x1bPullClientAppInputsResponse\x12$\n\x07message\x18\x01 \x01(\x0b\x32\x13.flwr.proto.Message\x12$\n\x07\x63ontext\x18\x02 \x01(\x0b\x32\x13.flwr.proto.Context\x12\x1c\n\x03run\x18\x03 \x01(\x0b\x32\x0f.flwr.proto.Run\"x\n\x1bPushClientAppOutputsRequest\x12\r\n\x05token\x18\x01 \x01(\x12\x12$\n\x07message\x18\x02 \x01(\x0b\x32\x13.flwr.proto.Message\x12$\n\x07\x63ontext\x18\x03 \x01(\x0b\x32\x13.flwr.proto.Context\"Q\n\x1cPushClientAppOutputsResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.flwr.proto.ClientAppOutputStatus*L\n\x13\x43lientAppOutputCode\x12\x0b\n\x07SUCCESS\x10\x00\x12\x15\n\x11\x44\x45\x41\x44LINE_EXCEEDED\x10\x01\x12\x11\n\rUNKNOWN_ERROR\x10\x02\x32\xe4\x01\n\x0b\x43lientAppIo\x12h\n\x13PullClientAppInputs\x12&.flwr.proto.PullClientAppInputsRequest\x1a\'.flwr.proto.PullClientAppInputsResponse\"\x00\x12k\n\x14PushClientAppOutputs\x12\'.flwr.proto.PushClientAppOutputsRequest\x1a(.flwr.proto.PushClientAppOutputsResponse\"\x00\x62\x06proto3')
20
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1c\x66lwr/proto/clientappio.proto\x12\nflwr.proto\x1a\x14\x66lwr/proto/fab.proto\x1a\x14\x66lwr/proto/run.proto\x1a\x18\x66lwr/proto/message.proto\"W\n\x15\x43lientAppOutputStatus\x12-\n\x04\x63ode\x18\x01 \x01(\x0e\x32\x1f.flwr.proto.ClientAppOutputCode\x12\x0f\n\x07message\x18\x02 \x01(\t\"\x11\n\x0fGetTokenRequest\"!\n\x10GetTokenResponse\x12\r\n\x05token\x18\x01 \x01(\x12\"+\n\x1aPullClientAppInputsRequest\x12\r\n\x05token\x18\x01 \x01(\x12\"\x87\x01\n\x1bPullClientAppInputsResponse\x12$\n\x07message\x18\x01 \x01(\x0b\x32\x13.flwr.proto.Message\x12$\n\x07\x63ontext\x18\x02 \x01(\x0b\x32\x13.flwr.proto.Context\x12\x1c\n\x03run\x18\x03 \x01(\x0b\x32\x0f.flwr.proto.Run\"x\n\x1bPushClientAppOutputsRequest\x12\r\n\x05token\x18\x01 \x01(\x12\x12$\n\x07message\x18\x02 \x01(\x0b\x32\x13.flwr.proto.Message\x12$\n\x07\x63ontext\x18\x03 \x01(\x0b\x32\x13.flwr.proto.Context\"Q\n\x1cPushClientAppOutputsResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.flwr.proto.ClientAppOutputStatus*L\n\x13\x43lientAppOutputCode\x12\x0b\n\x07SUCCESS\x10\x00\x12\x15\n\x11\x44\x45\x41\x44LINE_EXCEEDED\x10\x01\x12\x11\n\rUNKNOWN_ERROR\x10\x02\x32\xad\x02\n\x0b\x43lientAppIo\x12G\n\x08GetToken\x12\x1b.flwr.proto.GetTokenRequest\x1a\x1c.flwr.proto.GetTokenResponse\"\x00\x12h\n\x13PullClientAppInputs\x12&.flwr.proto.PullClientAppInputsRequest\x1a\'.flwr.proto.PullClientAppInputsResponse\"\x00\x12k\n\x14PushClientAppOutputs\x12\'.flwr.proto.PushClientAppOutputsRequest\x1a(.flwr.proto.PushClientAppOutputsResponse\"\x00\x62\x06proto3')
21
21
 
22
22
  _globals = globals()
23
23
  _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
24
24
  _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'flwr.proto.clientappio_pb2', _globals)
25
25
  if _descriptor._USE_C_DESCRIPTORS == False:
26
26
  DESCRIPTOR._options = None
27
- _globals['_CLIENTAPPOUTPUTCODE']._serialized_start=591
28
- _globals['_CLIENTAPPOUTPUTCODE']._serialized_end=667
27
+ _globals['_CLIENTAPPOUTPUTCODE']._serialized_start=645
28
+ _globals['_CLIENTAPPOUTPUTCODE']._serialized_end=721
29
29
  _globals['_CLIENTAPPOUTPUTSTATUS']._serialized_start=114
30
30
  _globals['_CLIENTAPPOUTPUTSTATUS']._serialized_end=201
31
- _globals['_PULLCLIENTAPPINPUTSREQUEST']._serialized_start=203
32
- _globals['_PULLCLIENTAPPINPUTSREQUEST']._serialized_end=246
33
- _globals['_PULLCLIENTAPPINPUTSRESPONSE']._serialized_start=249
34
- _globals['_PULLCLIENTAPPINPUTSRESPONSE']._serialized_end=384
35
- _globals['_PUSHCLIENTAPPOUTPUTSREQUEST']._serialized_start=386
36
- _globals['_PUSHCLIENTAPPOUTPUTSREQUEST']._serialized_end=506
37
- _globals['_PUSHCLIENTAPPOUTPUTSRESPONSE']._serialized_start=508
38
- _globals['_PUSHCLIENTAPPOUTPUTSRESPONSE']._serialized_end=589
39
- _globals['_CLIENTAPPIO']._serialized_start=670
40
- _globals['_CLIENTAPPIO']._serialized_end=898
31
+ _globals['_GETTOKENREQUEST']._serialized_start=203
32
+ _globals['_GETTOKENREQUEST']._serialized_end=220
33
+ _globals['_GETTOKENRESPONSE']._serialized_start=222
34
+ _globals['_GETTOKENRESPONSE']._serialized_end=255
35
+ _globals['_PULLCLIENTAPPINPUTSREQUEST']._serialized_start=257
36
+ _globals['_PULLCLIENTAPPINPUTSREQUEST']._serialized_end=300
37
+ _globals['_PULLCLIENTAPPINPUTSRESPONSE']._serialized_start=303
38
+ _globals['_PULLCLIENTAPPINPUTSRESPONSE']._serialized_end=438
39
+ _globals['_PUSHCLIENTAPPOUTPUTSREQUEST']._serialized_start=440
40
+ _globals['_PUSHCLIENTAPPOUTPUTSREQUEST']._serialized_end=560
41
+ _globals['_PUSHCLIENTAPPOUTPUTSRESPONSE']._serialized_start=562
42
+ _globals['_PUSHCLIENTAPPOUTPUTSRESPONSE']._serialized_end=643
43
+ _globals['_CLIENTAPPIO']._serialized_start=724
44
+ _globals['_CLIENTAPPIO']._serialized_end=1025
41
45
  # @@protoc_insertion_point(module_scope)
@@ -44,6 +44,23 @@ class ClientAppOutputStatus(google.protobuf.message.Message):
44
44
  def ClearField(self, field_name: typing_extensions.Literal["code",b"code","message",b"message"]) -> None: ...
45
45
  global___ClientAppOutputStatus = ClientAppOutputStatus
46
46
 
47
+ class GetTokenRequest(google.protobuf.message.Message):
48
+ DESCRIPTOR: google.protobuf.descriptor.Descriptor
49
+ def __init__(self,
50
+ ) -> None: ...
51
+ global___GetTokenRequest = GetTokenRequest
52
+
53
+ class GetTokenResponse(google.protobuf.message.Message):
54
+ DESCRIPTOR: google.protobuf.descriptor.Descriptor
55
+ TOKEN_FIELD_NUMBER: builtins.int
56
+ token: builtins.int
57
+ def __init__(self,
58
+ *,
59
+ token: builtins.int = ...,
60
+ ) -> None: ...
61
+ def ClearField(self, field_name: typing_extensions.Literal["token",b"token"]) -> None: ...
62
+ global___GetTokenResponse = GetTokenResponse
63
+
47
64
  class PullClientAppInputsRequest(google.protobuf.message.Message):
48
65
  DESCRIPTOR: google.protobuf.descriptor.Descriptor
49
66
  TOKEN_FIELD_NUMBER: builtins.int
@@ -14,6 +14,11 @@ class ClientAppIoStub(object):
14
14
  Args:
15
15
  channel: A grpc.Channel.
16
16
  """
17
+ self.GetToken = channel.unary_unary(
18
+ '/flwr.proto.ClientAppIo/GetToken',
19
+ request_serializer=flwr_dot_proto_dot_clientappio__pb2.GetTokenRequest.SerializeToString,
20
+ response_deserializer=flwr_dot_proto_dot_clientappio__pb2.GetTokenResponse.FromString,
21
+ )
17
22
  self.PullClientAppInputs = channel.unary_unary(
18
23
  '/flwr.proto.ClientAppIo/PullClientAppInputs',
19
24
  request_serializer=flwr_dot_proto_dot_clientappio__pb2.PullClientAppInputsRequest.SerializeToString,
@@ -29,6 +34,13 @@ class ClientAppIoStub(object):
29
34
  class ClientAppIoServicer(object):
30
35
  """Missing associated documentation comment in .proto file."""
31
36
 
37
+ def GetToken(self, request, context):
38
+ """Get token
39
+ """
40
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
41
+ context.set_details('Method not implemented!')
42
+ raise NotImplementedError('Method not implemented!')
43
+
32
44
  def PullClientAppInputs(self, request, context):
33
45
  """Get Message, Context, and Run
34
46
  """
@@ -46,6 +58,11 @@ class ClientAppIoServicer(object):
46
58
 
47
59
  def add_ClientAppIoServicer_to_server(servicer, server):
48
60
  rpc_method_handlers = {
61
+ 'GetToken': grpc.unary_unary_rpc_method_handler(
62
+ servicer.GetToken,
63
+ request_deserializer=flwr_dot_proto_dot_clientappio__pb2.GetTokenRequest.FromString,
64
+ response_serializer=flwr_dot_proto_dot_clientappio__pb2.GetTokenResponse.SerializeToString,
65
+ ),
49
66
  'PullClientAppInputs': grpc.unary_unary_rpc_method_handler(
50
67
  servicer.PullClientAppInputs,
51
68
  request_deserializer=flwr_dot_proto_dot_clientappio__pb2.PullClientAppInputsRequest.FromString,
@@ -66,6 +83,23 @@ def add_ClientAppIoServicer_to_server(servicer, server):
66
83
  class ClientAppIo(object):
67
84
  """Missing associated documentation comment in .proto file."""
68
85
 
86
+ @staticmethod
87
+ def GetToken(request,
88
+ target,
89
+ options=(),
90
+ channel_credentials=None,
91
+ call_credentials=None,
92
+ insecure=False,
93
+ compression=None,
94
+ wait_for_ready=None,
95
+ timeout=None,
96
+ metadata=None):
97
+ return grpc.experimental.unary_unary(request, target, '/flwr.proto.ClientAppIo/GetToken',
98
+ flwr_dot_proto_dot_clientappio__pb2.GetTokenRequest.SerializeToString,
99
+ flwr_dot_proto_dot_clientappio__pb2.GetTokenResponse.FromString,
100
+ options, channel_credentials,
101
+ insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
102
+
69
103
  @staticmethod
70
104
  def PullClientAppInputs(request,
71
105
  target,
@@ -8,6 +8,11 @@ import grpc
8
8
 
9
9
  class ClientAppIoStub:
10
10
  def __init__(self, channel: grpc.Channel) -> None: ...
11
+ GetToken: grpc.UnaryUnaryMultiCallable[
12
+ flwr.proto.clientappio_pb2.GetTokenRequest,
13
+ flwr.proto.clientappio_pb2.GetTokenResponse]
14
+ """Get token"""
15
+
11
16
  PullClientAppInputs: grpc.UnaryUnaryMultiCallable[
12
17
  flwr.proto.clientappio_pb2.PullClientAppInputsRequest,
13
18
  flwr.proto.clientappio_pb2.PullClientAppInputsResponse]
@@ -20,6 +25,14 @@ class ClientAppIoStub:
20
25
 
21
26
 
22
27
  class ClientAppIoServicer(metaclass=abc.ABCMeta):
28
+ @abc.abstractmethod
29
+ def GetToken(self,
30
+ request: flwr.proto.clientappio_pb2.GetTokenRequest,
31
+ context: grpc.ServicerContext,
32
+ ) -> flwr.proto.clientappio_pb2.GetTokenResponse:
33
+ """Get token"""
34
+ pass
35
+
23
36
  @abc.abstractmethod
24
37
  def PullClientAppInputs(self,
25
38
  request: flwr.proto.clientappio_pb2.PullClientAppInputsRequest,
flwr/server/app.py CHANGED
@@ -301,6 +301,7 @@ def run_superlink() -> None:
301
301
  fleet_server = _run_fleet_api_grpc_rere(
302
302
  address=fleet_address,
303
303
  state_factory=state_factory,
304
+ ffs_factory=ffs_factory,
304
305
  certificates=certificates,
305
306
  interceptors=interceptors,
306
307
  )
@@ -487,6 +488,7 @@ def _try_obtain_certificates(
487
488
  def _run_fleet_api_grpc_rere(
488
489
  address: str,
489
490
  state_factory: StateFactory,
491
+ ffs_factory: FfsFactory,
490
492
  certificates: Optional[Tuple[bytes, bytes, bytes]],
491
493
  interceptors: Optional[Sequence[grpc.ServerInterceptor]] = None,
492
494
  ) -> grpc.Server:
@@ -494,6 +496,7 @@ def _run_fleet_api_grpc_rere(
494
496
  # Create Fleet API gRPC server
495
497
  fleet_servicer = FleetServicer(
496
498
  state_factory=state_factory,
499
+ ffs_factory=ffs_factory,
497
500
  )
498
501
  fleet_add_servicer_to_server_fn = add_FleetServicer_to_server
499
502
  fleet_grpc_server = generic_create_grpc_server(
@@ -21,6 +21,8 @@ from logging import DEBUG, INFO, WARN
21
21
  from pathlib import Path
22
22
  from typing import Optional
23
23
 
24
+ from flwr.cli.config_utils import get_fab_metadata
25
+ from flwr.cli.install import install_from_fab
24
26
  from flwr.common import Context, EventType, RecordSet, event
25
27
  from flwr.common.config import (
26
28
  get_flwr_dir,
@@ -36,6 +38,7 @@ from flwr.proto.driver_pb2 import ( # pylint: disable=E0611
36
38
  CreateRunRequest,
37
39
  CreateRunResponse,
38
40
  )
41
+ from flwr.proto.fab_pb2 import GetFabRequest, GetFabResponse # pylint: disable=E0611
39
42
 
40
43
  from .driver import Driver
41
44
  from .driver.grpc_driver import GrpcDriver
@@ -87,7 +90,8 @@ def run(
87
90
  log(DEBUG, "ServerApp finished running.")
88
91
 
89
92
 
90
- def run_server_app() -> None: # pylint: disable=too-many-branches
93
+ # pylint: disable-next=too-many-branches,too-many-statements,too-many-locals
94
+ def run_server_app() -> None:
91
95
  """Run Flower server app."""
92
96
  event(EventType.RUN_SERVER_APP_ENTER)
93
97
 
@@ -164,7 +168,19 @@ def run_server_app() -> None: # pylint: disable=too-many-branches
164
168
  )
165
169
  flwr_dir = get_flwr_dir(args.flwr_dir)
166
170
  run_ = driver.run
167
- app_path = str(get_project_dir(run_.fab_id, run_.fab_version, flwr_dir))
171
+ if run_.fab_hash:
172
+ fab_req = GetFabRequest(hash_str=run_.fab_hash)
173
+ # pylint: disable-next=W0212
174
+ fab_res: GetFabResponse = driver._stub.GetFab(fab_req)
175
+ if fab_res.fab.hash_str != run_.fab_hash:
176
+ raise ValueError("FAB hashes don't match.")
177
+
178
+ install_from_fab(fab_res.fab.content, flwr_dir, True)
179
+ fab_id, fab_version = get_fab_metadata(fab_res.fab.content)
180
+ else:
181
+ fab_id, fab_version = run_.fab_id, run_.fab_version
182
+
183
+ app_path = str(get_project_dir(fab_id, fab_version, flwr_dir))
168
184
  config = get_project_config(app_path)
169
185
  else:
170
186
  # User provided `app_dir`, but not `--run-id`
flwr/server/server.py CHANGED
@@ -91,7 +91,7 @@ class Server:
91
91
  # Initialize parameters
92
92
  log(INFO, "[INIT]")
93
93
  self.parameters = self._get_initial_parameters(server_round=0, timeout=timeout)
94
- log(INFO, "Evaluating initial global parameters")
94
+ log(INFO, "Starting evaluation of initial global parameters")
95
95
  res = self.strategy.evaluate(0, parameters=self.parameters)
96
96
  if res is not None:
97
97
  log(
@@ -102,6 +102,8 @@ class Server:
102
102
  )
103
103
  history.add_loss_centralized(server_round=0, loss=res[0])
104
104
  history.add_metrics_centralized(server_round=0, metrics=res[1])
105
+ else:
106
+ log(INFO, "Evaluation returned no results (`None`)")
105
107
 
106
108
  # Run federated learning for num_rounds
107
109
  start_time = timeit.default_timer()