flwr-nightly 1.13.0.dev20241113__py3-none-any.whl → 1.13.0.dev20241115__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 (36) hide show
  1. flwr/cli/app.py +2 -0
  2. flwr/cli/ls.py +228 -0
  3. flwr/client/app.py +58 -13
  4. flwr/client/clientapp/app.py +34 -23
  5. flwr/client/grpc_rere_client/connection.py +2 -12
  6. flwr/client/rest_client/connection.py +4 -14
  7. flwr/client/supernode/app.py +56 -18
  8. flwr/common/constant.py +21 -6
  9. flwr/common/serde.py +10 -0
  10. flwr/common/typing.py +31 -10
  11. flwr/proto/exec_pb2.py +22 -13
  12. flwr/proto/exec_pb2.pyi +44 -0
  13. flwr/proto/exec_pb2_grpc.py +34 -0
  14. flwr/proto/exec_pb2_grpc.pyi +13 -0
  15. flwr/proto/run_pb2.py +30 -30
  16. flwr/proto/run_pb2.pyi +18 -1
  17. flwr/server/app.py +37 -12
  18. flwr/server/driver/grpc_driver.py +4 -14
  19. flwr/server/run_serverapp.py +8 -238
  20. flwr/server/serverapp/app.py +33 -22
  21. flwr/server/superlink/fleet/rest_rere/rest_api.py +10 -9
  22. flwr/server/superlink/linkstate/in_memory_linkstate.py +71 -46
  23. flwr/server/superlink/linkstate/linkstate.py +19 -5
  24. flwr/server/superlink/linkstate/sqlite_linkstate.py +79 -111
  25. flwr/server/superlink/linkstate/utils.py +182 -3
  26. flwr/simulation/app.py +3 -3
  27. flwr/simulation/legacy_app.py +21 -1
  28. flwr/simulation/run_simulation.py +7 -18
  29. flwr/simulation/simulationio_connection.py +2 -2
  30. flwr/superexec/deployment.py +9 -5
  31. flwr/superexec/exec_servicer.py +31 -2
  32. {flwr_nightly-1.13.0.dev20241113.dist-info → flwr_nightly-1.13.0.dev20241115.dist-info}/METADATA +2 -1
  33. {flwr_nightly-1.13.0.dev20241113.dist-info → flwr_nightly-1.13.0.dev20241115.dist-info}/RECORD +36 -35
  34. {flwr_nightly-1.13.0.dev20241113.dist-info → flwr_nightly-1.13.0.dev20241115.dist-info}/LICENSE +0 -0
  35. {flwr_nightly-1.13.0.dev20241113.dist-info → flwr_nightly-1.13.0.dev20241115.dist-info}/WHEEL +0 -0
  36. {flwr_nightly-1.13.0.dev20241113.dist-info → flwr_nightly-1.13.0.dev20241115.dist-info}/entry_points.txt +0 -0
flwr/server/app.py CHANGED
@@ -39,21 +39,23 @@ from flwr.common.address import parse_address
39
39
  from flwr.common.args import try_obtain_server_certificates
40
40
  from flwr.common.config import get_flwr_dir, parse_config_args
41
41
  from flwr.common.constant import (
42
- EXEC_API_DEFAULT_ADDRESS,
42
+ CLIENT_OCTET,
43
+ EXEC_API_DEFAULT_SERVER_ADDRESS,
43
44
  FLEET_API_GRPC_BIDI_DEFAULT_ADDRESS,
44
45
  FLEET_API_GRPC_RERE_DEFAULT_ADDRESS,
45
46
  FLEET_API_REST_DEFAULT_ADDRESS,
46
47
  ISOLATION_MODE_PROCESS,
47
48
  ISOLATION_MODE_SUBPROCESS,
48
49
  MISSING_EXTRA_REST,
49
- SERVERAPPIO_API_DEFAULT_ADDRESS,
50
- SIMULATIONIO_API_DEFAULT_ADDRESS,
50
+ SERVER_OCTET,
51
+ SERVERAPPIO_API_DEFAULT_SERVER_ADDRESS,
52
+ SIMULATIONIO_API_DEFAULT_SERVER_ADDRESS,
51
53
  TRANSPORT_TYPE_GRPC_ADAPTER,
52
54
  TRANSPORT_TYPE_GRPC_RERE,
53
55
  TRANSPORT_TYPE_REST,
54
56
  )
55
57
  from flwr.common.exit_handlers import register_exit_handlers
56
- from flwr.common.logger import log
58
+ from flwr.common.logger import log, warn_deprecated_feature
57
59
  from flwr.common.secure_aggregation.crypto.symmetric_encryption import (
58
60
  private_key_to_bytes,
59
61
  public_key_to_bytes,
@@ -99,6 +101,11 @@ def start_server( # pylint: disable=too-many-arguments,too-many-locals
99
101
  ) -> History:
100
102
  """Start a Flower server using the gRPC transport layer.
101
103
 
104
+ Warning
105
+ -------
106
+ This function is deprecated since 1.13.0. Use the :code:`flower-superlink` command
107
+ instead to start a SuperLink.
108
+
102
109
  Parameters
103
110
  ----------
104
111
  server_address : Optional[str]
@@ -156,6 +163,17 @@ def start_server( # pylint: disable=too-many-arguments,too-many-locals
156
163
  >>> )
157
164
  >>> )
158
165
  """
166
+ msg = (
167
+ "flwr.server.start_server() is deprecated."
168
+ "\n\tInstead, use the `flower-superlink` CLI command to start a SuperLink "
169
+ "as shown below:"
170
+ "\n\n\t\t$ flower-superlink --insecure"
171
+ "\n\n\tTo view usage and all available options, run:"
172
+ "\n\n\t\t$ flower-superlink --help"
173
+ "\n\n\tUsing `start_server()` is deprecated."
174
+ )
175
+ warn_deprecated_feature(name=msg)
176
+
159
177
  event(EventType.START_SERVER_ENTER)
160
178
 
161
179
  # Parse IP address
@@ -367,7 +385,11 @@ def run_superlink() -> None:
367
385
 
368
386
  if args.isolation == ISOLATION_MODE_SUBPROCESS:
369
387
 
370
- address = simulationio_address if sim_exec else serverappio_address
388
+ _octet, _colon, _port = serverappio_address.rpartition(":")
389
+ io_address = (
390
+ f"{CLIENT_OCTET}:{_port}" if _octet == SERVER_OCTET else serverappio_address
391
+ )
392
+ address = simulationio_address if sim_exec else io_address
371
393
  cmd = "flwr-simulation" if sim_exec else "flwr-serverapp"
372
394
 
373
395
  # Scheduler thread
@@ -426,7 +448,7 @@ def _flwr_scheduler(
426
448
  command = [
427
449
  cmd,
428
450
  "--run-once",
429
- "--superlink",
451
+ "--serverappio-api-address",
430
452
  io_api_address,
431
453
  ]
432
454
  if ssl_ca_certfile:
@@ -732,8 +754,9 @@ def _add_args_common(parser: argparse.ArgumentParser) -> None:
732
754
  def _add_args_serverappio_api(parser: argparse.ArgumentParser) -> None:
733
755
  parser.add_argument(
734
756
  "--serverappio-api-address",
735
- help="ServerAppIo API (gRPC) server address (IPv4, IPv6, or a domain name).",
736
- default=SERVERAPPIO_API_DEFAULT_ADDRESS,
757
+ default=SERVERAPPIO_API_DEFAULT_SERVER_ADDRESS,
758
+ help="ServerAppIo API (gRPC) server address (IPv4, IPv6, or a domain name). "
759
+ f"By default, it is set to {SERVERAPPIO_API_DEFAULT_SERVER_ADDRESS}.",
737
760
  )
738
761
 
739
762
 
@@ -766,8 +789,9 @@ def _add_args_exec_api(parser: argparse.ArgumentParser) -> None:
766
789
  """Add command line arguments for Exec API."""
767
790
  parser.add_argument(
768
791
  "--exec-api-address",
769
- help="Exec API server address (IPv4, IPv6, or a domain name)",
770
- default=EXEC_API_DEFAULT_ADDRESS,
792
+ help="Exec API server address (IPv4, IPv6, or a domain name) "
793
+ f"By default, it is set to {EXEC_API_DEFAULT_SERVER_ADDRESS}.",
794
+ default=EXEC_API_DEFAULT_SERVER_ADDRESS,
771
795
  )
772
796
  parser.add_argument(
773
797
  "--executor",
@@ -791,6 +815,7 @@ def _add_args_exec_api(parser: argparse.ArgumentParser) -> None:
791
815
  def _add_args_simulationio_api(parser: argparse.ArgumentParser) -> None:
792
816
  parser.add_argument(
793
817
  "--simulationio-api-address",
794
- help="SimulationIo API (gRPC) server address (IPv4, IPv6, or a domain name).",
795
- default=SIMULATIONIO_API_DEFAULT_ADDRESS,
818
+ default=SIMULATIONIO_API_DEFAULT_SERVER_ADDRESS,
819
+ help="SimulationIo API (gRPC) server address (IPv4, IPv6, or a domain name)."
820
+ f"By default, it is set to {SIMULATIONIO_API_DEFAULT_SERVER_ADDRESS}.",
796
821
  )
@@ -23,14 +23,10 @@ from typing import Optional, cast
23
23
  import grpc
24
24
 
25
25
  from flwr.common import DEFAULT_TTL, Message, Metadata, RecordSet
26
- from flwr.common.constant import SERVERAPPIO_API_DEFAULT_ADDRESS
26
+ from flwr.common.constant import SERVERAPPIO_API_DEFAULT_CLIENT_ADDRESS
27
27
  from flwr.common.grpc import create_channel
28
28
  from flwr.common.logger import log
29
- from flwr.common.serde import (
30
- message_from_taskres,
31
- message_to_taskins,
32
- user_config_from_proto,
33
- )
29
+ from flwr.common.serde import message_from_taskres, message_to_taskins, run_from_proto
34
30
  from flwr.common.typing import Run
35
31
  from flwr.proto.node_pb2 import Node # pylint: disable=E0611
36
32
  from flwr.proto.run_pb2 import GetRunRequest, GetRunResponse # pylint: disable=E0611
@@ -70,7 +66,7 @@ class GrpcDriver(Driver):
70
66
 
71
67
  def __init__( # pylint: disable=too-many-arguments
72
68
  self,
73
- serverappio_service_address: str = SERVERAPPIO_API_DEFAULT_ADDRESS,
69
+ serverappio_service_address: str = SERVERAPPIO_API_DEFAULT_CLIENT_ADDRESS,
74
70
  root_certificates: Optional[bytes] = None,
75
71
  ) -> None:
76
72
  self._addr = serverappio_service_address
@@ -119,13 +115,7 @@ class GrpcDriver(Driver):
119
115
  res: GetRunResponse = self._stub.GetRun(req)
120
116
  if not res.HasField("run"):
121
117
  raise RuntimeError(f"Cannot find the run with ID: {run_id}")
122
- self._run = Run(
123
- run_id=res.run.run_id,
124
- fab_id=res.run.fab_id,
125
- fab_version=res.run.fab_version,
126
- fab_hash=res.run.fab_hash,
127
- override_config=user_config_from_proto(res.run.override_config),
128
- )
118
+ self._run = run_from_proto(res.run)
129
119
 
130
120
  @property
131
121
  def run(self) -> Run:
@@ -15,33 +15,15 @@
15
15
  """Run ServerApp."""
16
16
 
17
17
 
18
- import argparse
19
18
  import sys
20
- from logging import DEBUG, INFO, WARN
21
- from pathlib import Path
19
+ from logging import DEBUG, ERROR
22
20
  from typing import Optional
23
21
 
24
- from flwr.cli.config_utils import get_fab_metadata
25
- from flwr.cli.install import install_from_fab
26
- from flwr.common import Context, EventType, RecordSet, event
27
- from flwr.common.config import (
28
- get_flwr_dir,
29
- get_fused_config_from_dir,
30
- get_metadata_from_config,
31
- get_project_config,
32
- get_project_dir,
33
- )
34
- from flwr.common.constant import SERVERAPPIO_API_DEFAULT_ADDRESS
35
- from flwr.common.logger import log, update_console_handler, warn_deprecated_feature
22
+ from flwr.common import Context
23
+ from flwr.common.logger import log, warn_unsupported_feature
36
24
  from flwr.common.object_ref import load_app
37
- from flwr.proto.fab_pb2 import GetFabRequest, GetFabResponse # pylint: disable=E0611
38
- from flwr.proto.run_pb2 import ( # pylint: disable=E0611
39
- CreateRunRequest,
40
- CreateRunResponse,
41
- )
42
25
 
43
26
  from .driver import Driver
44
- from .driver.grpc_driver import GrpcDriver
45
27
  from .server_app import LoadServerAppError, ServerApp
46
28
 
47
29
 
@@ -87,221 +69,9 @@ def run(
87
69
  # pylint: disable-next=too-many-branches,too-many-statements,too-many-locals
88
70
  def run_server_app() -> None:
89
71
  """Run Flower server app."""
90
- event(EventType.RUN_SERVER_APP_ENTER)
91
-
92
- args = _parse_args_run_server_app().parse_args()
93
-
94
- # Check if the server app reference is passed.
95
- # Since Flower 1.11, passing a reference is not allowed.
96
- app_path: Optional[str] = args.app
97
- # If the provided app_path doesn't exist, and contains a ":",
98
- # it is likely to be a server app reference instead of a path.
99
- if app_path is not None and not Path(app_path).exists() and ":" in app_path:
100
- sys.exit(
101
- "It appears you've passed a reference like `server:app`.\n\n"
102
- "Note that since version `1.11.0`, `flower-server-app` no longer supports "
103
- "passing a reference to a `ServerApp` attribute. Instead, you need to pass "
104
- "the path to Flower app via the argument `--app`. This is the path to a "
105
- "directory containing a `pyproject.toml`. You can create a valid Flower "
106
- "app by executing `flwr new` and following the prompt."
107
- )
108
-
109
- if args.server != SERVERAPPIO_API_DEFAULT_ADDRESS:
110
- warn = "Passing flag --server is deprecated. Use --superlink instead."
111
- warn_deprecated_feature(warn)
112
-
113
- if args.superlink != SERVERAPPIO_API_DEFAULT_ADDRESS:
114
- # if `--superlink` also passed, then
115
- # warn user that this argument overrides what was passed with `--server`
116
- log(
117
- WARN,
118
- "Both `--server` and `--superlink` were passed. "
119
- "`--server` will be ignored. Connecting to the "
120
- "SuperLink ServerAppIo API at %s.",
121
- args.superlink,
122
- )
123
- else:
124
- args.superlink = args.server
125
-
126
- update_console_handler(
127
- level=DEBUG if args.verbose else INFO,
128
- timestamps=args.verbose,
129
- colored=True,
130
- )
131
-
132
- # Obtain certificates
133
- if args.insecure:
134
- if args.root_certificates is not None:
135
- sys.exit(
136
- "Conflicting options: The '--insecure' flag disables HTTPS, "
137
- "but '--root-certificates' was also specified. Please remove "
138
- "the '--root-certificates' option when running in insecure mode, "
139
- "or omit '--insecure' to use HTTPS."
140
- )
141
- log(
142
- WARN,
143
- "Option `--insecure` was set. "
144
- "Starting insecure HTTP client connected to %s.",
145
- args.superlink,
146
- )
147
- root_certificates = None
148
- else:
149
- # Load the certificates if provided, or load the system certificates
150
- cert_path = args.root_certificates
151
- if cert_path is None:
152
- root_certificates = None
153
- else:
154
- root_certificates = Path(cert_path).read_bytes()
155
- log(
156
- DEBUG,
157
- "Starting secure HTTPS client connected to %s "
158
- "with the following certificates: %s.",
159
- args.superlink,
160
- cert_path,
161
- )
162
-
163
- if not (app_path is None) ^ (args.run_id is None):
164
- raise sys.exit(
165
- "Please provide either a Flower App path or a Run ID, but not both. "
166
- "For more details, use: ``flower-server-app -h``"
167
- )
168
-
169
- # Initialize GrpcDriver
170
- if app_path is None:
171
- # User provided `--run-id`, but not `app_dir`
172
- driver = GrpcDriver(
173
- serverappio_service_address=args.superlink,
174
- root_certificates=root_certificates,
175
- )
176
- flwr_dir = get_flwr_dir(args.flwr_dir)
177
- driver.set_run(args.run_id)
178
- run_ = driver.run
179
- if not run_.fab_hash:
180
- raise ValueError("FAB hash not provided.")
181
- fab_req = GetFabRequest(hash_str=run_.fab_hash)
182
- # pylint: disable-next=W0212
183
- fab_res: GetFabResponse = driver._stub.GetFab(fab_req)
184
- if fab_res.fab.hash_str != run_.fab_hash:
185
- raise ValueError("FAB hashes don't match.")
186
- install_from_fab(fab_res.fab.content, flwr_dir, True)
187
- fab_id, fab_version = get_fab_metadata(fab_res.fab.content)
188
-
189
- app_path = str(get_project_dir(fab_id, fab_version, run_.fab_hash, flwr_dir))
190
- config = get_project_config(app_path)
191
- run_id = run_.run_id
192
- else:
193
- # User provided `app_dir`, but not `--run-id`
194
- # Create run if run_id is not provided
195
- driver = GrpcDriver(
196
- serverappio_service_address=args.superlink,
197
- root_certificates=root_certificates,
198
- )
199
- # Load config from the project directory
200
- config = get_project_config(app_path)
201
- fab_version, fab_id = get_metadata_from_config(config)
202
-
203
- # Create run
204
- req = CreateRunRequest(fab_id=fab_id, fab_version=fab_version)
205
- res: CreateRunResponse = driver._stub.CreateRun(req) # pylint: disable=W0212
206
- # Fetch full `Run` using `run_id`
207
- driver.set_run(res.run_id) # pylint: disable=W0212
208
- run_id = res.run_id
209
-
210
- # Obtain server app reference and the run config
211
- server_app_attr = config["tool"]["flwr"]["app"]["components"]["serverapp"]
212
- server_app_run_config = get_fused_config_from_dir(
213
- Path(app_path), driver.run.override_config
72
+ warn_unsupported_feature(
73
+ "The command `flower-server-app` is deprecated and no longer in use. "
74
+ "Use the `flwr-serverapp` exclusively instead."
214
75
  )
215
-
216
- log(DEBUG, "Flower will load ServerApp `%s` in %s", server_app_attr, app_path)
217
-
218
- log(
219
- DEBUG,
220
- "root_certificates: `%s`",
221
- root_certificates,
222
- )
223
-
224
- # Initialize Context
225
- context = Context(
226
- run_id=run_id,
227
- node_id=0,
228
- node_config={},
229
- state=RecordSet(),
230
- run_config=server_app_run_config,
231
- )
232
-
233
- # Run the ServerApp with the Driver
234
- run(
235
- driver=driver,
236
- context=context,
237
- server_app_dir=app_path,
238
- server_app_attr=server_app_attr,
239
- )
240
-
241
- # Clean up
242
- driver.close()
243
-
244
- event(EventType.RUN_SERVER_APP_LEAVE)
245
-
246
-
247
- def _parse_args_run_server_app() -> argparse.ArgumentParser:
248
- """Parse flower-server-app command line arguments."""
249
- parser = argparse.ArgumentParser(
250
- description="Start a Flower server app",
251
- )
252
-
253
- parser.add_argument(
254
- "app",
255
- nargs="?",
256
- default=None,
257
- help="Load and run the `ServerApp` from the specified Flower App path. "
258
- "The `pyproject.toml` file must be located in the root of this path.",
259
- )
260
- parser.add_argument(
261
- "--insecure",
262
- action="store_true",
263
- help="Run the `ServerApp` without HTTPS. By default, the app runs with "
264
- "HTTPS enabled. Use this flag only if you understand the risks.",
265
- )
266
- parser.add_argument(
267
- "--verbose",
268
- action="store_true",
269
- help="Set the logging to `DEBUG`.",
270
- )
271
- parser.add_argument(
272
- "--root-certificates",
273
- metavar="ROOT_CERT",
274
- type=str,
275
- help="Specifies the path to the PEM-encoded root certificate file for "
276
- "establishing secure HTTPS connections.",
277
- )
278
- parser.add_argument(
279
- "--server",
280
- default=SERVERAPPIO_API_DEFAULT_ADDRESS,
281
- help="Server address",
282
- )
283
- parser.add_argument(
284
- "--superlink",
285
- default=SERVERAPPIO_API_DEFAULT_ADDRESS,
286
- help="SuperLink ServerAppIo API (gRPC-rere) address "
287
- "(IPv4, IPv6, or a domain name)",
288
- )
289
- parser.add_argument(
290
- "--run-id",
291
- default=None,
292
- type=int,
293
- help="The identifier of the run.",
294
- )
295
- parser.add_argument(
296
- "--flwr-dir",
297
- default=None,
298
- help="""The path containing installed Flower Apps.
299
- By default, this value is equal to:
300
-
301
- - `$FLWR_HOME/` if `$FLWR_HOME` is defined
302
- - `$XDG_DATA_HOME/.flwr/` if `$XDG_DATA_HOME` is defined
303
- - `$HOME/.flwr/` in all other cases
304
- """,
305
- )
306
-
307
- return parser
76
+ log(ERROR, "`flower-server-app` used.")
77
+ sys.exit()
@@ -30,7 +30,11 @@ from flwr.common.config import (
30
30
  get_project_config,
31
31
  get_project_dir,
32
32
  )
33
- from flwr.common.constant import Status, SubStatus
33
+ from flwr.common.constant import (
34
+ SERVERAPPIO_API_DEFAULT_CLIENT_ADDRESS,
35
+ Status,
36
+ SubStatus,
37
+ )
34
38
  from flwr.common.logger import (
35
39
  log,
36
40
  mirror_output_to_queue,
@@ -62,33 +66,18 @@ def flwr_serverapp() -> None:
62
66
  log_queue: Queue[Optional[str]] = Queue()
63
67
  mirror_output_to_queue(log_queue)
64
68
 
65
- parser = argparse.ArgumentParser(
66
- description="Run a Flower ServerApp",
67
- )
68
- parser.add_argument(
69
- "--superlink",
70
- type=str,
71
- help="Address of SuperLink's ServerAppIo API",
72
- )
73
- parser.add_argument(
74
- "--run-once",
75
- action="store_true",
76
- help="When set, this process will start a single ServerApp for a pending Run. "
77
- "If there is no pending Run, the process will exit.",
78
- )
79
- add_args_flwr_app_common(parser=parser)
80
- args = parser.parse_args()
69
+ args = _parse_args_run_flwr_serverapp().parse_args()
81
70
 
82
71
  log(INFO, "Starting Flower ServerApp")
83
- certificates = try_obtain_root_certificates(args, args.superlink)
72
+ certificates = try_obtain_root_certificates(args, args.serverappio_api_address)
84
73
 
85
74
  log(
86
75
  DEBUG,
87
76
  "Starting isolated `ServerApp` connected to SuperLink's ServerAppIo API at %s",
88
- args.superlink,
77
+ args.serverappio_api_address,
89
78
  )
90
79
  run_serverapp(
91
- superlink=args.superlink,
80
+ serverappio_api_address=args.serverappio_api_address,
92
81
  log_queue=log_queue,
93
82
  run_once=args.run_once,
94
83
  flwr_dir=args.flwr_dir,
@@ -100,7 +89,7 @@ def flwr_serverapp() -> None:
100
89
 
101
90
 
102
91
  def run_serverapp( # pylint: disable=R0914, disable=W0212
103
- superlink: str,
92
+ serverappio_api_address: str,
104
93
  log_queue: Queue[Optional[str]],
105
94
  run_once: bool,
106
95
  flwr_dir: Optional[str] = None,
@@ -108,7 +97,7 @@ def run_serverapp( # pylint: disable=R0914, disable=W0212
108
97
  ) -> None:
109
98
  """Run Flower ServerApp process."""
110
99
  driver = GrpcDriver(
111
- serverappio_service_address=superlink,
100
+ serverappio_service_address=serverappio_api_address,
112
101
  root_certificates=certificates,
113
102
  )
114
103
 
@@ -212,3 +201,25 @@ def run_serverapp( # pylint: disable=R0914, disable=W0212
212
201
  # Stop the loop if `flwr-serverapp` is expected to process a single run
213
202
  if run_once:
214
203
  break
204
+
205
+
206
+ def _parse_args_run_flwr_serverapp() -> argparse.ArgumentParser:
207
+ """Parse flwr-serverapp command line arguments."""
208
+ parser = argparse.ArgumentParser(
209
+ description="Run a Flower ServerApp",
210
+ )
211
+ parser.add_argument(
212
+ "--serverappio-api-address",
213
+ default=SERVERAPPIO_API_DEFAULT_CLIENT_ADDRESS,
214
+ type=str,
215
+ help="Address of SuperLink's ServerAppIo API (IPv4, IPv6, or a domain name)."
216
+ f"By default, it is set to {SERVERAPPIO_API_DEFAULT_CLIENT_ADDRESS}.",
217
+ )
218
+ parser.add_argument(
219
+ "--run-once",
220
+ action="store_true",
221
+ help="When set, this process will start a single ServerApp for a pending Run. "
222
+ "If there is no pending Run, the process will exit.",
223
+ )
224
+ add_args_flwr_app_common(parser=parser)
225
+ return parser
@@ -19,7 +19,7 @@ from __future__ import annotations
19
19
 
20
20
  import sys
21
21
  from collections.abc import Awaitable
22
- from typing import Callable, TypeVar
22
+ from typing import Callable, TypeVar, cast
23
23
 
24
24
  from google.protobuf.message import Message as GrpcMessage
25
25
 
@@ -39,8 +39,9 @@ from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
39
39
  )
40
40
  from flwr.proto.run_pb2 import GetRunRequest, GetRunResponse # pylint: disable=E0611
41
41
  from flwr.server.superlink.ffs.ffs import Ffs
42
+ from flwr.server.superlink.ffs.ffs_factory import FfsFactory
42
43
  from flwr.server.superlink.fleet.message_handler import message_handler
43
- from flwr.server.superlink.linkstate import LinkState
44
+ from flwr.server.superlink.linkstate import LinkState, LinkStateFactory
44
45
 
45
46
  try:
46
47
  from starlette.applications import Starlette
@@ -90,7 +91,7 @@ def rest_request_response(
90
91
  async def create_node(request: CreateNodeRequest) -> CreateNodeResponse:
91
92
  """Create Node."""
92
93
  # Get state from app
93
- state: LinkState = app.state.STATE_FACTORY.state()
94
+ state: LinkState = cast(LinkStateFactory, app.state.STATE_FACTORY).state()
94
95
 
95
96
  # Handle message
96
97
  return message_handler.create_node(request=request, state=state)
@@ -100,7 +101,7 @@ async def create_node(request: CreateNodeRequest) -> CreateNodeResponse:
100
101
  async def delete_node(request: DeleteNodeRequest) -> DeleteNodeResponse:
101
102
  """Delete Node Id."""
102
103
  # Get state from app
103
- state: LinkState = app.state.STATE_FACTORY.state()
104
+ state: LinkState = cast(LinkStateFactory, app.state.STATE_FACTORY).state()
104
105
 
105
106
  # Handle message
106
107
  return message_handler.delete_node(request=request, state=state)
@@ -110,7 +111,7 @@ async def delete_node(request: DeleteNodeRequest) -> DeleteNodeResponse:
110
111
  async def pull_task_ins(request: PullTaskInsRequest) -> PullTaskInsResponse:
111
112
  """Pull TaskIns."""
112
113
  # Get state from app
113
- state: LinkState = app.state.STATE_FACTORY.state()
114
+ state: LinkState = cast(LinkStateFactory, app.state.STATE_FACTORY).state()
114
115
 
115
116
  # Handle message
116
117
  return message_handler.pull_task_ins(request=request, state=state)
@@ -121,7 +122,7 @@ async def pull_task_ins(request: PullTaskInsRequest) -> PullTaskInsResponse:
121
122
  async def push_task_res(request: PushTaskResRequest) -> PushTaskResResponse:
122
123
  """Push TaskRes."""
123
124
  # Get state from app
124
- state: LinkState = app.state.STATE_FACTORY.state()
125
+ state: LinkState = cast(LinkStateFactory, app.state.STATE_FACTORY).state()
125
126
 
126
127
  # Handle message
127
128
  return message_handler.push_task_res(request=request, state=state)
@@ -131,7 +132,7 @@ async def push_task_res(request: PushTaskResRequest) -> PushTaskResResponse:
131
132
  async def ping(request: PingRequest) -> PingResponse:
132
133
  """Ping."""
133
134
  # Get state from app
134
- state: LinkState = app.state.STATE_FACTORY.state()
135
+ state: LinkState = cast(LinkStateFactory, app.state.STATE_FACTORY).state()
135
136
 
136
137
  # Handle message
137
138
  return message_handler.ping(request=request, state=state)
@@ -141,7 +142,7 @@ async def ping(request: PingRequest) -> PingResponse:
141
142
  async def get_run(request: GetRunRequest) -> GetRunResponse:
142
143
  """GetRun."""
143
144
  # Get state from app
144
- state: LinkState = app.state.STATE_FACTORY.state()
145
+ state: LinkState = cast(LinkStateFactory, app.state.STATE_FACTORY).state()
145
146
 
146
147
  # Handle message
147
148
  return message_handler.get_run(request=request, state=state)
@@ -151,7 +152,7 @@ async def get_run(request: GetRunRequest) -> GetRunResponse:
151
152
  async def get_fab(request: GetFabRequest) -> GetFabResponse:
152
153
  """GetRun."""
153
154
  # Get ffs from app
154
- ffs: Ffs = app.state.FFS_FACTORY.state()
155
+ ffs: Ffs = cast(FfsFactory, app.state.FFS_FACTORY).ffs()
155
156
 
156
157
  # Handle message
157
158
  return message_handler.get_fab(request=request, ffs=ffs)