flwr-nightly 1.11.0.dev20240822__py3-none-any.whl → 1.11.1.dev20240912__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 (75) hide show
  1. flwr/cli/app.py +0 -2
  2. flwr/cli/build.py +1 -1
  3. flwr/cli/new/new.py +41 -40
  4. flwr/cli/new/templates/app/LICENSE.tpl +202 -0
  5. flwr/cli/new/templates/app/README.baseline.md.tpl +127 -0
  6. flwr/cli/new/templates/app/README.flowertune.md.tpl +16 -6
  7. flwr/cli/new/templates/app/README.md.tpl +7 -30
  8. flwr/cli/new/templates/app/code/__init__.baseline.py.tpl +1 -0
  9. flwr/cli/new/templates/app/code/client.baseline.py.tpl +58 -0
  10. flwr/cli/new/templates/app/code/client.huggingface.py.tpl +19 -29
  11. flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +0 -3
  12. flwr/cli/new/templates/app/code/dataset.baseline.py.tpl +36 -0
  13. flwr/cli/new/templates/app/code/flwr_tune/{client.py.tpl → client_app.py.tpl} +50 -40
  14. flwr/cli/new/templates/app/code/flwr_tune/dataset.py.tpl +32 -2
  15. flwr/cli/new/templates/app/code/flwr_tune/models.py.tpl +0 -3
  16. flwr/cli/new/templates/app/code/flwr_tune/server_app.py.tpl +95 -0
  17. flwr/cli/new/templates/app/code/flwr_tune/strategy.py.tpl +83 -0
  18. flwr/cli/new/templates/app/code/model.baseline.py.tpl +80 -0
  19. flwr/cli/new/templates/app/code/server.baseline.py.tpl +46 -0
  20. flwr/cli/new/templates/app/code/server.huggingface.py.tpl +18 -3
  21. flwr/cli/new/templates/app/code/strategy.baseline.py.tpl +1 -0
  22. flwr/cli/new/templates/app/code/task.huggingface.py.tpl +16 -13
  23. flwr/cli/new/templates/app/code/utils.baseline.py.tpl +1 -0
  24. flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +138 -0
  25. flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +34 -7
  26. flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +9 -1
  27. flwr/cli/run/run.py +12 -2
  28. flwr/client/__init__.py +0 -4
  29. flwr/client/app.py +3 -4
  30. flwr/client/client.py +22 -1
  31. flwr/client/client_app.py +2 -2
  32. flwr/client/grpc_rere_client/client_interceptor.py +15 -7
  33. flwr/client/numpy_client.py +22 -1
  34. flwr/client/rest_client/connection.py +1 -1
  35. flwr/client/supernode/app.py +8 -7
  36. flwr/common/address.py +43 -0
  37. flwr/common/config.py +14 -11
  38. flwr/common/constant.py +12 -1
  39. flwr/common/record/recordset.py +1 -1
  40. flwr/common/record/typeddict.py +24 -1
  41. flwr/common/telemetry.py +36 -30
  42. flwr/server/__init__.py +0 -4
  43. flwr/server/app.py +27 -22
  44. flwr/server/compat/app.py +0 -5
  45. flwr/server/driver/grpc_driver.py +3 -6
  46. flwr/server/run_serverapp.py +20 -7
  47. flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +15 -2
  48. flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +5 -0
  49. flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +19 -8
  50. flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +13 -12
  51. flwr/server/superlink/fleet/rest_rere/rest_api.py +71 -122
  52. flwr/server/superlink/fleet/vce/backend/backend.py +1 -2
  53. flwr/server/superlink/fleet/vce/backend/raybackend.py +33 -15
  54. flwr/server/superlink/fleet/vce/vce_api.py +2 -6
  55. flwr/server/superlink/state/in_memory_state.py +15 -15
  56. flwr/server/superlink/state/sqlite_state.py +10 -10
  57. flwr/server/superlink/state/state.py +8 -8
  58. flwr/server/workflow/secure_aggregation/secagg_workflow.py +1 -0
  59. flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +1 -0
  60. flwr/simulation/ray_transport/ray_actor.py +2 -2
  61. flwr/simulation/run_simulation.py +85 -25
  62. flwr/superexec/__init__.py +0 -6
  63. flwr/superexec/app.py +5 -3
  64. flwr/superexec/deployment.py +2 -2
  65. flwr/superexec/simulation.py +20 -1
  66. {flwr_nightly-1.11.0.dev20240822.dist-info → flwr_nightly-1.11.1.dev20240912.dist-info}/METADATA +3 -3
  67. {flwr_nightly-1.11.0.dev20240822.dist-info → flwr_nightly-1.11.1.dev20240912.dist-info}/RECORD +70 -62
  68. flwr_nightly-1.11.1.dev20240912.dist-info/entry_points.txt +10 -0
  69. flwr/cli/new/templates/app/code/flwr_tune/app.py.tpl +0 -89
  70. flwr/cli/new/templates/app/code/flwr_tune/config.yaml.tpl +0 -34
  71. flwr/cli/new/templates/app/code/flwr_tune/server.py.tpl +0 -48
  72. flwr/cli/new/templates/app/code/flwr_tune/static_config.yaml.tpl +0 -11
  73. flwr_nightly-1.11.0.dev20240822.dist-info/entry_points.txt +0 -10
  74. {flwr_nightly-1.11.0.dev20240822.dist-info → flwr_nightly-1.11.1.dev20240912.dist-info}/LICENSE +0 -0
  75. {flwr_nightly-1.11.0.dev20240822.dist-info → flwr_nightly-1.11.1.dev20240912.dist-info}/WHEEL +0 -0
@@ -30,6 +30,7 @@ from cryptography.hazmat.primitives.serialization import (
30
30
  from flwr.common import EventType, event
31
31
  from flwr.common.config import parse_config_args
32
32
  from flwr.common.constant import (
33
+ FLEET_API_GRPC_RERE_DEFAULT_ADDRESS,
33
34
  TRANSPORT_TYPE_GRPC_ADAPTER,
34
35
  TRANSPORT_TYPE_GRPC_RERE,
35
36
  TRANSPORT_TYPE_REST,
@@ -44,8 +45,6 @@ from ..app import (
44
45
  )
45
46
  from ..clientapp.utils import get_load_client_app_fn
46
47
 
47
- ADDRESS_FLEET_API_GRPC_RERE = "0.0.0.0:9092"
48
-
49
48
 
50
49
  def run_supernode() -> None:
51
50
  """Run Flower SuperNode."""
@@ -77,7 +76,9 @@ def run_supernode() -> None:
77
76
  authentication_keys=authentication_keys,
78
77
  max_retries=args.max_retries,
79
78
  max_wait_time=args.max_wait_time,
80
- node_config=parse_config_args([args.node_config]),
79
+ node_config=parse_config_args(
80
+ [args.node_config] if args.node_config else args.node_config
81
+ ),
81
82
  isolation=args.isolation,
82
83
  supernode_address=args.supernode_address,
83
84
  )
@@ -101,11 +102,11 @@ def run_client_app() -> None:
101
102
 
102
103
  def _warn_deprecated_server_arg(args: argparse.Namespace) -> None:
103
104
  """Warn about the deprecated argument `--server`."""
104
- if args.server != ADDRESS_FLEET_API_GRPC_RERE:
105
+ if args.server != FLEET_API_GRPC_RERE_DEFAULT_ADDRESS:
105
106
  warn = "Passing flag --server is deprecated. Use --superlink instead."
106
107
  warn_deprecated_feature(warn)
107
108
 
108
- if args.superlink != ADDRESS_FLEET_API_GRPC_RERE:
109
+ if args.superlink != FLEET_API_GRPC_RERE_DEFAULT_ADDRESS:
109
110
  # if `--superlink` also passed, then
110
111
  # warn user that this argument overrides what was passed with `--server`
111
112
  log(
@@ -245,12 +246,12 @@ def _parse_args_common(parser: argparse.ArgumentParser) -> None:
245
246
  )
246
247
  parser.add_argument(
247
248
  "--server",
248
- default=ADDRESS_FLEET_API_GRPC_RERE,
249
+ default=FLEET_API_GRPC_RERE_DEFAULT_ADDRESS,
249
250
  help="Server address",
250
251
  )
251
252
  parser.add_argument(
252
253
  "--superlink",
253
- default=ADDRESS_FLEET_API_GRPC_RERE,
254
+ default=FLEET_API_GRPC_RERE_DEFAULT_ADDRESS,
254
255
  help="SuperLink Fleet API (gRPC-rere) address (IPv4, IPv6, or a domain name)",
255
256
  )
256
257
  parser.add_argument(
flwr/common/address.py CHANGED
@@ -14,6 +14,7 @@
14
14
  # ==============================================================================
15
15
  """Flower IP address utils."""
16
16
 
17
+ import socket
17
18
  from ipaddress import ip_address
18
19
  from typing import Optional, Tuple
19
20
 
@@ -57,3 +58,45 @@ def parse_address(address: str) -> Optional[Tuple[str, int, Optional[bool]]]:
57
58
 
58
59
  except ValueError:
59
60
  return None
61
+
62
+
63
+ def is_port_in_use(address: str) -> bool:
64
+ """Check if the port specified in address is in use.
65
+
66
+ Parameters
67
+ ----------
68
+ address : str
69
+ The string representation of a domain, an IPv4, or an IPV6 address
70
+ with the port number.
71
+
72
+ For example, '127.0.0.1:8080', or `[::1]:8080`.
73
+
74
+ Returns
75
+ -------
76
+ bool
77
+ If the port provided is in use or can't be parsed,
78
+ the function will return True, otherwise it will return False.
79
+ """
80
+ parsed_address = parse_address(address)
81
+ if not parsed_address:
82
+ return True
83
+ host, port, is_v6 = parsed_address
84
+
85
+ if is_v6:
86
+ protocol = socket.AF_INET6
87
+ else:
88
+ protocol = socket.AF_INET
89
+
90
+ with socket.socket(protocol, socket.SOCK_STREAM) as s:
91
+ s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
92
+ try:
93
+ if is_v6:
94
+ # For IPv6, provide `flowinfo` and `scopeid` as 0
95
+ s.bind((host, port, 0, 0))
96
+ else:
97
+ # For IPv4
98
+ s.bind((host, port))
99
+ except OSError:
100
+ return True
101
+
102
+ return False
flwr/common/config.py CHANGED
@@ -185,23 +185,26 @@ def parse_config_args(
185
185
  if config is None:
186
186
  return overrides
187
187
 
188
+ # Handle if .toml file is passed
189
+ if len(config) == 1 and config[0].endswith(".toml"):
190
+ with Path(config[0]).open("rb") as config_file:
191
+ overrides = flatten_dict(tomli.load(config_file))
192
+ return overrides
193
+
188
194
  # Regular expression to capture key-value pairs with possible quoted values
189
195
  pattern = re.compile(r"(\S+?)=(\'[^\']*\'|\"[^\"]*\"|\S+)")
190
196
 
191
197
  for config_line in config:
192
198
  if config_line:
193
- matches = pattern.findall(config_line)
199
+ # .toml files aren't allowed alongside other configs
200
+ if config_line.endswith(".toml"):
201
+ raise ValueError(
202
+ "TOML files cannot be passed alongside key-value pairs."
203
+ )
194
204
 
195
- if (
196
- len(matches) == 1
197
- and "=" not in matches[0][0]
198
- and matches[0][0].endswith(".toml")
199
- ):
200
- with Path(matches[0][0]).open("rb") as config_file:
201
- overrides = flatten_dict(tomli.load(config_file))
202
- else:
203
- toml_str = "\n".join(f"{k} = {v}" for k, v in matches)
204
- overrides.update(tomli.loads(toml_str))
205
+ matches = pattern.findall(config_line)
206
+ toml_str = "\n".join(f"{k} = {v}" for k, v in matches)
207
+ overrides.update(tomli.loads(toml_str))
205
208
 
206
209
  return overrides
207
210
 
flwr/common/constant.py CHANGED
@@ -37,7 +37,18 @@ TRANSPORT_TYPES = [
37
37
  TRANSPORT_TYPE_VCE,
38
38
  ]
39
39
 
40
- SUPEREXEC_DEFAULT_ADDRESS = "0.0.0.0:9093"
40
+ # Addresses
41
+ # SuperNode
42
+ CLIENTAPPIO_API_DEFAULT_ADDRESS = "0.0.0.0:9094"
43
+ # SuperExec
44
+ EXEC_API_DEFAULT_ADDRESS = "0.0.0.0:9093"
45
+ # SuperLink
46
+ DRIVER_API_DEFAULT_ADDRESS = "0.0.0.0:9091"
47
+ FLEET_API_GRPC_RERE_DEFAULT_ADDRESS = "0.0.0.0:9092"
48
+ FLEET_API_GRPC_BIDI_DEFAULT_ADDRESS = (
49
+ "[::]:8080" # IPv6 to keep start_server compatible
50
+ )
51
+ FLEET_API_REST_DEFAULT_ADDRESS = "0.0.0.0:9093"
41
52
 
42
53
  # Constants for ping
43
54
  PING_DEFAULT_INTERVAL = 30
@@ -119,7 +119,7 @@ class RecordSet:
119
119
  Let's see an example.
120
120
 
121
121
  >>> from flwr.common import RecordSet
122
- >>> from flwr.common import ConfigsRecords, MetricsRecords, ParametersRecord
122
+ >>> from flwr.common import ConfigsRecord, MetricsRecord, ParametersRecord
123
123
  >>>
124
124
  >>> # Let's begin with an empty record
125
125
  >>> my_recordset = RecordSet()
@@ -15,7 +15,18 @@
15
15
  """Typed dict base class for *Records."""
16
16
 
17
17
 
18
- from typing import Callable, Dict, Generic, Iterator, MutableMapping, TypeVar, cast
18
+ from typing import (
19
+ Callable,
20
+ Dict,
21
+ Generic,
22
+ ItemsView,
23
+ Iterator,
24
+ KeysView,
25
+ MutableMapping,
26
+ TypeVar,
27
+ ValuesView,
28
+ cast,
29
+ )
19
30
 
20
31
  K = TypeVar("K") # Key type
21
32
  V = TypeVar("V") # Value type
@@ -73,3 +84,15 @@ class TypedDict(MutableMapping[K, V], Generic[K, V]):
73
84
  if isinstance(other, dict):
74
85
  return data == other
75
86
  return NotImplemented
87
+
88
+ def keys(self) -> KeysView[K]:
89
+ """D.keys() -> a set-like object providing a view on D's keys."""
90
+ return cast(Dict[K, V], self.__dict__["_data"]).keys()
91
+
92
+ def values(self) -> ValuesView[V]:
93
+ """D.values() -> an object providing a view on D's values."""
94
+ return cast(Dict[K, V], self.__dict__["_data"]).values()
95
+
96
+ def items(self) -> ItemsView[K, V]:
97
+ """D.items() -> a set-like object providing a view on D's items."""
98
+ return cast(Dict[K, V], self.__dict__["_data"]).items()
flwr/common/telemetry.py CHANGED
@@ -132,53 +132,59 @@ class EventType(str, Enum):
132
132
  # Ping
133
133
  PING = auto()
134
134
 
135
- # Client: start_client
135
+ # --- LEGACY FUNCTIONS -------------------------------------------------------------
136
+
137
+ # Legacy: `start_client` function
136
138
  START_CLIENT_ENTER = auto()
137
139
  START_CLIENT_LEAVE = auto()
138
140
 
139
- # Server: start_server
141
+ # Legacy: `start_server` function
140
142
  START_SERVER_ENTER = auto()
141
143
  START_SERVER_LEAVE = auto()
142
144
 
143
- # Driver API
144
- RUN_DRIVER_API_ENTER = auto()
145
- RUN_DRIVER_API_LEAVE = auto()
145
+ # Legacy: `start_simulation` function
146
+ START_SIMULATION_ENTER = auto()
147
+ START_SIMULATION_LEAVE = auto()
146
148
 
147
- # Fleet API
148
- RUN_FLEET_API_ENTER = auto()
149
- RUN_FLEET_API_LEAVE = auto()
149
+ # --- `flwr` CLI -------------------------------------------------------------------
150
150
 
151
- # Driver API and Fleet API
152
- RUN_SUPERLINK_ENTER = auto()
153
- RUN_SUPERLINK_LEAVE = auto()
151
+ # Not yet implemented
154
152
 
155
- # Simulation
156
- START_SIMULATION_ENTER = auto()
157
- START_SIMULATION_LEAVE = auto()
153
+ # --- SuperExec --------------------------------------------------------------------
158
154
 
159
- # Driver: Driver
160
- DRIVER_CONNECT = auto()
161
- DRIVER_DISCONNECT = auto()
155
+ # SuperExec
156
+ RUN_SUPEREXEC_ENTER = auto()
157
+ RUN_SUPEREXEC_LEAVE = auto()
162
158
 
163
- # Driver: start_driver
164
- START_DRIVER_ENTER = auto()
165
- START_DRIVER_LEAVE = auto()
159
+ # --- Simulation Engine ------------------------------------------------------------
166
160
 
167
- # flower-client-app
168
- RUN_CLIENT_APP_ENTER = auto()
169
- RUN_CLIENT_APP_LEAVE = auto()
161
+ # CLI: flower-simulation
162
+ CLI_FLOWER_SIMULATION_ENTER = auto()
163
+ CLI_FLOWER_SIMULATION_LEAVE = auto()
170
164
 
171
- # flower-server-app
172
- RUN_SERVER_APP_ENTER = auto()
173
- RUN_SERVER_APP_LEAVE = auto()
165
+ # Python API: `run_simulation`
166
+ PYTHON_API_RUN_SIMULATION_ENTER = auto()
167
+ PYTHON_API_RUN_SIMULATION_LEAVE = auto()
174
168
 
175
- # SuperNode
169
+ # --- Deployment Engine ------------------------------------------------------------
170
+
171
+ # CLI: `flower-superlink`
172
+ RUN_SUPERLINK_ENTER = auto()
173
+ RUN_SUPERLINK_LEAVE = auto()
174
+
175
+ # CLI: `flower-supernode`
176
176
  RUN_SUPERNODE_ENTER = auto()
177
177
  RUN_SUPERNODE_LEAVE = auto()
178
178
 
179
- # SuperExec
180
- RUN_SUPEREXEC_ENTER = auto()
181
- RUN_SUPEREXEC_LEAVE = auto()
179
+ # CLI: `flower-server-app`
180
+ RUN_SERVER_APP_ENTER = auto()
181
+ RUN_SERVER_APP_LEAVE = auto()
182
+
183
+ # --- DEPRECATED -------------------------------------------------------------------
184
+
185
+ # [DEPRECATED] CLI: `flower-client-app`
186
+ RUN_CLIENT_APP_ENTER = auto()
187
+ RUN_CLIENT_APP_LEAVE = auto()
182
188
 
183
189
 
184
190
  # Use the ThreadPoolExecutor with max_workers=1 to have a queue
flwr/server/__init__.py CHANGED
@@ -17,14 +17,12 @@
17
17
 
18
18
  from . import strategy
19
19
  from . import workflow as workflow
20
- from .app import run_superlink as run_superlink
21
20
  from .app import start_server as start_server
22
21
  from .client_manager import ClientManager as ClientManager
23
22
  from .client_manager import SimpleClientManager as SimpleClientManager
24
23
  from .compat import LegacyContext as LegacyContext
25
24
  from .driver import Driver as Driver
26
25
  from .history import History as History
27
- from .run_serverapp import run_server_app as run_server_app
28
26
  from .server import Server as Server
29
27
  from .server_app import ServerApp as ServerApp
30
28
  from .server_config import ServerConfig as ServerConfig
@@ -40,8 +38,6 @@ __all__ = [
40
38
  "ServerAppComponents",
41
39
  "ServerConfig",
42
40
  "SimpleClientManager",
43
- "run_server_app",
44
- "run_superlink",
45
41
  "start_server",
46
42
  "strategy",
47
43
  "workflow",
flwr/server/app.py CHANGED
@@ -36,6 +36,10 @@ from flwr.common import GRPC_MAX_MESSAGE_LENGTH, EventType, event
36
36
  from flwr.common.address import parse_address
37
37
  from flwr.common.config import get_flwr_dir
38
38
  from flwr.common.constant import (
39
+ DRIVER_API_DEFAULT_ADDRESS,
40
+ FLEET_API_GRPC_BIDI_DEFAULT_ADDRESS,
41
+ FLEET_API_GRPC_RERE_DEFAULT_ADDRESS,
42
+ FLEET_API_REST_DEFAULT_ADDRESS,
39
43
  MISSING_EXTRA_REST,
40
44
  TRANSPORT_TYPE_GRPC_ADAPTER,
41
45
  TRANSPORT_TYPE_GRPC_RERE,
@@ -68,18 +72,13 @@ from .superlink.fleet.grpc_rere.fleet_servicer import FleetServicer
68
72
  from .superlink.fleet.grpc_rere.server_interceptor import AuthenticateServerInterceptor
69
73
  from .superlink.state import StateFactory
70
74
 
71
- ADDRESS_DRIVER_API = "0.0.0.0:9091"
72
- ADDRESS_FLEET_API_GRPC_RERE = "0.0.0.0:9092"
73
- ADDRESS_FLEET_API_GRPC_BIDI = "[::]:8080" # IPv6 to keep start_server compatible
74
- ADDRESS_FLEET_API_REST = "0.0.0.0:9093"
75
-
76
75
  DATABASE = ":flwr-in-memory-state:"
77
76
  BASE_DIR = get_flwr_dir() / "superlink" / "ffs"
78
77
 
79
78
 
80
79
  def start_server( # pylint: disable=too-many-arguments,too-many-locals
81
80
  *,
82
- server_address: str = ADDRESS_FLEET_API_GRPC_BIDI,
81
+ server_address: str = FLEET_API_GRPC_BIDI_DEFAULT_ADDRESS,
83
82
  server: Optional[Server] = None,
84
83
  config: Optional[ServerConfig] = None,
85
84
  strategy: Optional[Strategy] = None,
@@ -232,9 +231,9 @@ def run_superlink() -> None:
232
231
  TRANSPORT_TYPE_GRPC_RERE,
233
232
  TRANSPORT_TYPE_GRPC_ADAPTER,
234
233
  ]:
235
- args.fleet_api_address = ADDRESS_FLEET_API_GRPC_RERE
234
+ args.fleet_api_address = FLEET_API_GRPC_RERE_DEFAULT_ADDRESS
236
235
  elif args.fleet_api_type == TRANSPORT_TYPE_REST:
237
- args.fleet_api_address = ADDRESS_FLEET_API_REST
236
+ args.fleet_api_address = FLEET_API_REST_DEFAULT_ADDRESS
238
237
 
239
238
  fleet_address, host, port = _format_address(args.fleet_api_address)
240
239
 
@@ -271,30 +270,31 @@ def run_superlink() -> None:
271
270
  ssl_keyfile,
272
271
  ssl_certfile,
273
272
  state_factory,
273
+ ffs_factory,
274
274
  num_workers,
275
275
  ),
276
276
  )
277
277
  fleet_thread.start()
278
278
  bckg_threads.append(fleet_thread)
279
279
  elif args.fleet_api_type == TRANSPORT_TYPE_GRPC_RERE:
280
- maybe_keys = _try_setup_client_authentication(args, certificates)
280
+ maybe_keys = _try_setup_node_authentication(args, certificates)
281
281
  interceptors: Optional[Sequence[grpc.ServerInterceptor]] = None
282
282
  if maybe_keys is not None:
283
283
  (
284
- client_public_keys,
284
+ node_public_keys,
285
285
  server_private_key,
286
286
  server_public_key,
287
287
  ) = maybe_keys
288
288
  state = state_factory.state()
289
- state.store_client_public_keys(client_public_keys)
289
+ state.store_node_public_keys(node_public_keys)
290
290
  state.store_server_private_public_key(
291
291
  private_key_to_bytes(server_private_key),
292
292
  public_key_to_bytes(server_public_key),
293
293
  )
294
294
  log(
295
295
  INFO,
296
- "Client authentication enabled with %d known public keys",
297
- len(client_public_keys),
296
+ "Node authentication enabled with %d known public keys",
297
+ len(node_public_keys),
298
298
  )
299
299
  interceptors = [AuthenticateServerInterceptor(state)]
300
300
 
@@ -310,6 +310,7 @@ def run_superlink() -> None:
310
310
  fleet_server = _run_fleet_api_grpc_adapter(
311
311
  address=fleet_address,
312
312
  state_factory=state_factory,
313
+ ffs_factory=ffs_factory,
313
314
  certificates=certificates,
314
315
  )
315
316
  grpc_servers.append(fleet_server)
@@ -342,7 +343,7 @@ def _format_address(address: str) -> Tuple[str, str, int]:
342
343
  return (f"[{host}]:{port}" if is_v6 else f"{host}:{port}", host, port)
343
344
 
344
345
 
345
- def _try_setup_client_authentication(
346
+ def _try_setup_node_authentication(
346
347
  args: argparse.Namespace,
347
348
  certificates: Optional[Tuple[bytes, bytes, bytes]],
348
349
  ) -> Optional[Tuple[Set[bytes], ec.EllipticCurvePrivateKey, ec.EllipticCurvePublicKey]]:
@@ -371,16 +372,16 @@ def _try_setup_client_authentication(
371
372
  "`--ssl-keyfile`, and `—-ssl-ca-certfile` and try again."
372
373
  )
373
374
 
374
- client_keys_file_path = Path(args.auth_list_public_keys)
375
- if not client_keys_file_path.exists():
375
+ node_keys_file_path = Path(args.auth_list_public_keys)
376
+ if not node_keys_file_path.exists():
376
377
  sys.exit(
377
378
  "The provided path to the known public keys CSV file does not exist: "
378
- f"{client_keys_file_path}. "
379
+ f"{node_keys_file_path}. "
379
380
  "Please provide the CSV file path containing known public keys "
380
381
  "to '--auth-list-public-keys'."
381
382
  )
382
383
 
383
- client_public_keys: Set[bytes] = set()
384
+ node_public_keys: Set[bytes] = set()
384
385
 
385
386
  try:
386
387
  ssh_private_key = load_ssh_private_key(
@@ -411,13 +412,13 @@ def _try_setup_client_authentication(
411
412
  "path points to a valid public key file and try again."
412
413
  )
413
414
 
414
- with open(client_keys_file_path, newline="", encoding="utf-8") as csvfile:
415
+ with open(node_keys_file_path, newline="", encoding="utf-8") as csvfile:
415
416
  reader = csv.reader(csvfile)
416
417
  for row in reader:
417
418
  for element in row:
418
419
  public_key = load_ssh_public_key(element.encode())
419
420
  if isinstance(public_key, ec.EllipticCurvePublicKey):
420
- client_public_keys.add(public_key_to_bytes(public_key))
421
+ node_public_keys.add(public_key_to_bytes(public_key))
421
422
  else:
422
423
  sys.exit(
423
424
  "Error: Unable to parse the public keys in the CSV "
@@ -425,7 +426,7 @@ def _try_setup_client_authentication(
425
426
  "known SSH public keys files and try again."
426
427
  )
427
428
  return (
428
- client_public_keys,
429
+ node_public_keys,
429
430
  ssh_private_key,
430
431
  ssh_public_key,
431
432
  )
@@ -516,12 +517,14 @@ def _run_fleet_api_grpc_rere(
516
517
  def _run_fleet_api_grpc_adapter(
517
518
  address: str,
518
519
  state_factory: StateFactory,
520
+ ffs_factory: FfsFactory,
519
521
  certificates: Optional[Tuple[bytes, bytes, bytes]],
520
522
  ) -> grpc.Server:
521
523
  """Run Fleet API (GrpcAdapter)."""
522
524
  # Create Fleet API gRPC server
523
525
  fleet_servicer = GrpcAdapterServicer(
524
526
  state_factory=state_factory,
527
+ ffs_factory=ffs_factory,
525
528
  )
526
529
  fleet_add_servicer_to_server_fn = add_GrpcAdapterServicer_to_server
527
530
  fleet_grpc_server = generic_create_grpc_server(
@@ -544,6 +547,7 @@ def _run_fleet_api_rest(
544
547
  ssl_keyfile: Optional[str],
545
548
  ssl_certfile: Optional[str],
546
549
  state_factory: StateFactory,
550
+ ffs_factory: FfsFactory,
547
551
  num_workers: int,
548
552
  ) -> None:
549
553
  """Run Driver API (REST-based)."""
@@ -558,6 +562,7 @@ def _run_fleet_api_rest(
558
562
 
559
563
  # See: https://www.starlette.io/applications/#accessing-the-app-instance
560
564
  fast_api_app.state.STATE_FACTORY = state_factory
565
+ fast_api_app.state.FFS_FACTORY = ffs_factory
561
566
 
562
567
  uvicorn.run(
563
568
  app="flwr.server.superlink.fleet.rest_rere.rest_api:app",
@@ -647,7 +652,7 @@ def _add_args_driver_api(parser: argparse.ArgumentParser) -> None:
647
652
  parser.add_argument(
648
653
  "--driver-api-address",
649
654
  help="Driver API (gRPC) server address (IPv4, IPv6, or a domain name).",
650
- default=ADDRESS_DRIVER_API,
655
+ default=DRIVER_API_DEFAULT_ADDRESS,
651
656
  )
652
657
 
653
658
 
flwr/server/compat/app.py CHANGED
@@ -18,7 +18,6 @@
18
18
  from logging import INFO
19
19
  from typing import Optional
20
20
 
21
- from flwr.common import EventType, event
22
21
  from flwr.common.logger import log
23
22
  from flwr.server.client_manager import ClientManager
24
23
  from flwr.server.history import History
@@ -65,8 +64,6 @@ def start_driver( # pylint: disable=too-many-arguments, too-many-locals
65
64
  hist : flwr.server.history.History
66
65
  Object containing training and evaluation metrics.
67
66
  """
68
- event(EventType.START_DRIVER_ENTER)
69
-
70
67
  # Initialize the Driver API server and config
71
68
  initialized_server, initialized_config = init_defaults(
72
69
  server=server,
@@ -96,6 +93,4 @@ def start_driver( # pylint: disable=too-many-arguments, too-many-locals
96
93
  f_stop.set()
97
94
  thread.join()
98
95
 
99
- event(EventType.START_SERVER_LEAVE)
100
-
101
96
  return hist
@@ -21,7 +21,8 @@ from typing import Iterable, List, Optional, cast
21
21
 
22
22
  import grpc
23
23
 
24
- from flwr.common import DEFAULT_TTL, EventType, Message, Metadata, RecordSet, event
24
+ from flwr.common import DEFAULT_TTL, Message, Metadata, RecordSet
25
+ from flwr.common.constant import DRIVER_API_DEFAULT_ADDRESS
25
26
  from flwr.common.grpc import create_channel
26
27
  from flwr.common.logger import log
27
28
  from flwr.common.serde import (
@@ -45,8 +46,6 @@ from flwr.proto.task_pb2 import TaskIns # pylint: disable=E0611
45
46
 
46
47
  from .driver import Driver
47
48
 
48
- DEFAULT_SERVER_ADDRESS_DRIVER = "[::]:9091"
49
-
50
49
  ERROR_MESSAGE_DRIVER_NOT_CONNECTED = """
51
50
  [Driver] Error: Not connected.
52
51
 
@@ -73,7 +72,7 @@ class GrpcDriver(Driver):
73
72
  def __init__( # pylint: disable=too-many-arguments
74
73
  self,
75
74
  run_id: int,
76
- driver_service_address: str = DEFAULT_SERVER_ADDRESS_DRIVER,
75
+ driver_service_address: str = DRIVER_API_DEFAULT_ADDRESS,
77
76
  root_certificates: Optional[bytes] = None,
78
77
  ) -> None:
79
78
  self._run_id = run_id
@@ -94,7 +93,6 @@ class GrpcDriver(Driver):
94
93
 
95
94
  This will not call GetRun.
96
95
  """
97
- event(EventType.DRIVER_CONNECT)
98
96
  if self._is_connected:
99
97
  log(WARNING, "Already connected")
100
98
  return
@@ -108,7 +106,6 @@ class GrpcDriver(Driver):
108
106
 
109
107
  def _disconnect(self) -> None:
110
108
  """Disconnect from the Driver API."""
111
- event(EventType.DRIVER_DISCONNECT)
112
109
  if not self._is_connected:
113
110
  log(DEBUG, "Already disconnected")
114
111
  return
@@ -31,6 +31,7 @@ from flwr.common.config import (
31
31
  get_project_config,
32
32
  get_project_dir,
33
33
  )
34
+ from flwr.common.constant import DRIVER_API_DEFAULT_ADDRESS
34
35
  from flwr.common.logger import log, update_console_handler, warn_deprecated_feature
35
36
  from flwr.common.object_ref import load_app
36
37
  from flwr.common.typing import UserConfig
@@ -44,8 +45,6 @@ from .driver import Driver
44
45
  from .driver.grpc_driver import GrpcDriver
45
46
  from .server_app import LoadServerAppError, ServerApp
46
47
 
47
- ADDRESS_DRIVER_API = "0.0.0.0:9091"
48
-
49
48
 
50
49
  def run(
51
50
  driver: Driver,
@@ -97,11 +96,26 @@ def run_server_app() -> None:
97
96
 
98
97
  args = _parse_args_run_server_app().parse_args()
99
98
 
100
- if args.server != ADDRESS_DRIVER_API:
99
+ # Check if the server app reference is passed.
100
+ # Since Flower 1.11, passing a reference is not allowed.
101
+ app_path: Optional[str] = args.app
102
+ # If the provided app_path doesn't exist, and contains a ":",
103
+ # it is likely to be a server app reference instead of a path.
104
+ if app_path is not None and not Path(app_path).exists() and ":" in app_path:
105
+ sys.exit(
106
+ "It appears you've passed a reference like `server:app`.\n\n"
107
+ "Note that since version `1.11.0`, `flower-server-app` no longer supports "
108
+ "passing a reference to a `ServerApp` attribute. Instead, you need to pass "
109
+ "the path to Flower app via the argument `--app`. This is the path to a "
110
+ "directory containing a `pyproject.toml`. You can create a valid Flower "
111
+ "app by executing `flwr new` and following the prompt."
112
+ )
113
+
114
+ if args.server != DRIVER_API_DEFAULT_ADDRESS:
101
115
  warn = "Passing flag --server is deprecated. Use --superlink instead."
102
116
  warn_deprecated_feature(warn)
103
117
 
104
- if args.superlink != ADDRESS_DRIVER_API:
118
+ if args.superlink != DRIVER_API_DEFAULT_ADDRESS:
105
119
  # if `--superlink` also passed, then
106
120
  # warn user that this argument overrides what was passed with `--server`
107
121
  log(
@@ -151,7 +165,6 @@ def run_server_app() -> None:
151
165
  cert_path,
152
166
  )
153
167
 
154
- app_path: Optional[str] = args.app
155
168
  if not (app_path is None) ^ (args.run_id is None):
156
169
  raise sys.exit(
157
170
  "Please provide either a Flower App path or a Run ID, but not both. "
@@ -261,12 +274,12 @@ def _parse_args_run_server_app() -> argparse.ArgumentParser:
261
274
  )
262
275
  parser.add_argument(
263
276
  "--server",
264
- default=ADDRESS_DRIVER_API,
277
+ default=DRIVER_API_DEFAULT_ADDRESS,
265
278
  help="Server address",
266
279
  )
267
280
  parser.add_argument(
268
281
  "--superlink",
269
- default=ADDRESS_DRIVER_API,
282
+ default=DRIVER_API_DEFAULT_ADDRESS,
270
283
  help="SuperLink Driver API (gRPC-rere) address (IPv4, IPv6, or a domain name)",
271
284
  )
272
285
  parser.add_argument(