flwr-nightly 1.21.0.dev20250811__py3-none-any.whl → 1.21.0.dev20250813__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
flwr/common/args.py CHANGED
@@ -28,6 +28,12 @@ from flwr.common.logger import log
28
28
 
29
29
  def add_args_flwr_app_common(parser: argparse.ArgumentParser) -> None:
30
30
  """Add common Flower arguments for flwr-*app to the provided parser."""
31
+ parser.add_argument(
32
+ "--token",
33
+ type=str,
34
+ required=False,
35
+ help="Unique token generated by AppIo API for each app execution",
36
+ )
31
37
  parser.add_argument(
32
38
  "--flwr-dir",
33
39
  default=None,
@@ -47,6 +53,13 @@ def add_args_flwr_app_common(parser: argparse.ArgumentParser) -> None:
47
53
  "is not encrypted. By default, the server runs with HTTPS enabled. "
48
54
  "Use this flag only if you understand the risks.",
49
55
  )
56
+ parser.add_argument(
57
+ "--parent-pid",
58
+ type=int,
59
+ default=None,
60
+ help="The PID of the parent process. When set, the process will terminate "
61
+ "when the parent process exits.",
62
+ )
50
63
 
51
64
 
52
65
  def try_obtain_root_certificates(
@@ -17,23 +17,16 @@ from flwr.proto import log_pb2 as flwr_dot_proto_dot_log__pb2
17
17
  from flwr.proto import message_pb2 as flwr_dot_proto_dot_message__pb2
18
18
  from flwr.proto import run_pb2 as flwr_dot_proto_dot_run__pb2
19
19
  from flwr.proto import fab_pb2 as flwr_dot_proto_dot_fab__pb2
20
+ from flwr.proto import appio_pb2 as flwr_dot_proto_dot_appio__pb2
20
21
 
21
22
 
22
- DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1d\x66lwr/proto/simulationio.proto\x12\nflwr.proto\x1a\x1a\x66lwr/proto/heartbeat.proto\x1a\x14\x66lwr/proto/log.proto\x1a\x18\x66lwr/proto/message.proto\x1a\x14\x66lwr/proto/run.proto\x1a\x14\x66lwr/proto/fab.proto\"\x1d\n\x1bPullSimulationInputsRequest\"\x80\x01\n\x1cPullSimulationInputsResponse\x12$\n\x07\x63ontext\x18\x01 \x01(\x0b\x32\x13.flwr.proto.Context\x12\x1c\n\x03run\x18\x02 \x01(\x0b\x32\x0f.flwr.proto.Run\x12\x1c\n\x03\x66\x61\x62\x18\x03 \x01(\x0b\x32\x0f.flwr.proto.Fab\"T\n\x1cPushSimulationOutputsRequest\x12\x0e\n\x06run_id\x18\x01 \x01(\x04\x12$\n\x07\x63ontext\x18\x02 \x01(\x0b\x32\x13.flwr.proto.Context\"\x1f\n\x1dPushSimulationOutputsResponse2\xb5\x05\n\x0cSimulationIo\x12k\n\x14PullSimulationInputs\x12\'.flwr.proto.PullSimulationInputsRequest\x1a(.flwr.proto.PullSimulationInputsResponse\"\x00\x12n\n\x15PushSimulationOutputs\x12(.flwr.proto.PushSimulationOutputsRequest\x1a).flwr.proto.PushSimulationOutputsResponse\"\x00\x12\\\n\x0fUpdateRunStatus\x12\".flwr.proto.UpdateRunStatusRequest\x1a#.flwr.proto.UpdateRunStatusResponse\"\x00\x12G\n\x08PushLogs\x12\x1b.flwr.proto.PushLogsRequest\x1a\x1c.flwr.proto.PushLogsResponse\"\x00\x12k\n\x14GetFederationOptions\x12\'.flwr.proto.GetFederationOptionsRequest\x1a(.flwr.proto.GetFederationOptionsResponse\"\x00\x12S\n\x0cGetRunStatus\x12\x1f.flwr.proto.GetRunStatusRequest\x1a .flwr.proto.GetRunStatusResponse\"\x00\x12_\n\x10SendAppHeartbeat\x12#.flwr.proto.SendAppHeartbeatRequest\x1a$.flwr.proto.SendAppHeartbeatResponse\"\x00\x62\x06proto3')
23
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1d\x66lwr/proto/simulationio.proto\x12\nflwr.proto\x1a\x1a\x66lwr/proto/heartbeat.proto\x1a\x14\x66lwr/proto/log.proto\x1a\x18\x66lwr/proto/message.proto\x1a\x14\x66lwr/proto/run.proto\x1a\x14\x66lwr/proto/fab.proto\x1a\x16\x66lwr/proto/appio.proto2\x84\x07\n\x0cSimulationIo\x12_\n\x10ListAppsToLaunch\x12#.flwr.proto.ListAppsToLaunchRequest\x1a$.flwr.proto.ListAppsToLaunchResponse\"\x00\x12S\n\x0cRequestToken\x12\x1f.flwr.proto.RequestTokenRequest\x1a .flwr.proto.RequestTokenResponse\"\x00\x12\x41\n\x06GetRun\x12\x19.flwr.proto.GetRunRequest\x1a\x1a.flwr.proto.GetRunResponse\"\x00\x12V\n\rPullAppInputs\x12 .flwr.proto.PullAppInputsRequest\x1a!.flwr.proto.PullAppInputsResponse\"\x00\x12Y\n\x0ePushAppOutputs\x12!.flwr.proto.PushAppOutputsRequest\x1a\".flwr.proto.PushAppOutputsResponse\"\x00\x12\\\n\x0fUpdateRunStatus\x12\".flwr.proto.UpdateRunStatusRequest\x1a#.flwr.proto.UpdateRunStatusResponse\"\x00\x12G\n\x08PushLogs\x12\x1b.flwr.proto.PushLogsRequest\x1a\x1c.flwr.proto.PushLogsResponse\"\x00\x12k\n\x14GetFederationOptions\x12\'.flwr.proto.GetFederationOptionsRequest\x1a(.flwr.proto.GetFederationOptionsResponse\"\x00\x12S\n\x0cGetRunStatus\x12\x1f.flwr.proto.GetRunStatusRequest\x1a .flwr.proto.GetRunStatusResponse\"\x00\x12_\n\x10SendAppHeartbeat\x12#.flwr.proto.SendAppHeartbeatRequest\x1a$.flwr.proto.SendAppHeartbeatResponse\"\x00\x62\x06proto3')
23
24
 
24
25
  _globals = globals()
25
26
  _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
26
27
  _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'flwr.proto.simulationio_pb2', _globals)
27
28
  if _descriptor._USE_C_DESCRIPTORS == False:
28
29
  DESCRIPTOR._options = None
29
- _globals['_PULLSIMULATIONINPUTSREQUEST']._serialized_start=165
30
- _globals['_PULLSIMULATIONINPUTSREQUEST']._serialized_end=194
31
- _globals['_PULLSIMULATIONINPUTSRESPONSE']._serialized_start=197
32
- _globals['_PULLSIMULATIONINPUTSRESPONSE']._serialized_end=325
33
- _globals['_PUSHSIMULATIONOUTPUTSREQUEST']._serialized_start=327
34
- _globals['_PUSHSIMULATIONOUTPUTSREQUEST']._serialized_end=411
35
- _globals['_PUSHSIMULATIONOUTPUTSRESPONSE']._serialized_start=413
36
- _globals['_PUSHSIMULATIONOUTPUTSRESPONSE']._serialized_end=444
37
- _globals['_SIMULATIONIO']._serialized_start=447
38
- _globals['_SIMULATIONIO']._serialized_end=1140
30
+ _globals['_SIMULATIONIO']._serialized_start=190
31
+ _globals['_SIMULATIONIO']._serialized_end=1090
39
32
  # @@protoc_insertion_point(module_scope)
@@ -2,64 +2,6 @@
2
2
  @generated by mypy-protobuf. Do not edit manually!
3
3
  isort:skip_file
4
4
  """
5
- import builtins
6
- import flwr.proto.fab_pb2
7
- import flwr.proto.message_pb2
8
- import flwr.proto.run_pb2
9
5
  import google.protobuf.descriptor
10
- import google.protobuf.message
11
- import typing
12
- import typing_extensions
13
6
 
14
7
  DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
15
-
16
- class PullSimulationInputsRequest(google.protobuf.message.Message):
17
- """PullSimulationInputs messages"""
18
- DESCRIPTOR: google.protobuf.descriptor.Descriptor
19
- def __init__(self,
20
- ) -> None: ...
21
- global___PullSimulationInputsRequest = PullSimulationInputsRequest
22
-
23
- class PullSimulationInputsResponse(google.protobuf.message.Message):
24
- DESCRIPTOR: google.protobuf.descriptor.Descriptor
25
- CONTEXT_FIELD_NUMBER: builtins.int
26
- RUN_FIELD_NUMBER: builtins.int
27
- FAB_FIELD_NUMBER: builtins.int
28
- @property
29
- def context(self) -> flwr.proto.message_pb2.Context: ...
30
- @property
31
- def run(self) -> flwr.proto.run_pb2.Run: ...
32
- @property
33
- def fab(self) -> flwr.proto.fab_pb2.Fab: ...
34
- def __init__(self,
35
- *,
36
- context: typing.Optional[flwr.proto.message_pb2.Context] = ...,
37
- run: typing.Optional[flwr.proto.run_pb2.Run] = ...,
38
- fab: typing.Optional[flwr.proto.fab_pb2.Fab] = ...,
39
- ) -> None: ...
40
- def HasField(self, field_name: typing_extensions.Literal["context",b"context","fab",b"fab","run",b"run"]) -> builtins.bool: ...
41
- def ClearField(self, field_name: typing_extensions.Literal["context",b"context","fab",b"fab","run",b"run"]) -> None: ...
42
- global___PullSimulationInputsResponse = PullSimulationInputsResponse
43
-
44
- class PushSimulationOutputsRequest(google.protobuf.message.Message):
45
- """PushSimulationOutputs messages"""
46
- DESCRIPTOR: google.protobuf.descriptor.Descriptor
47
- RUN_ID_FIELD_NUMBER: builtins.int
48
- CONTEXT_FIELD_NUMBER: builtins.int
49
- run_id: builtins.int
50
- @property
51
- def context(self) -> flwr.proto.message_pb2.Context: ...
52
- def __init__(self,
53
- *,
54
- run_id: builtins.int = ...,
55
- context: typing.Optional[flwr.proto.message_pb2.Context] = ...,
56
- ) -> None: ...
57
- def HasField(self, field_name: typing_extensions.Literal["context",b"context"]) -> builtins.bool: ...
58
- def ClearField(self, field_name: typing_extensions.Literal["context",b"context","run_id",b"run_id"]) -> None: ...
59
- global___PushSimulationOutputsRequest = PushSimulationOutputsRequest
60
-
61
- class PushSimulationOutputsResponse(google.protobuf.message.Message):
62
- DESCRIPTOR: google.protobuf.descriptor.Descriptor
63
- def __init__(self,
64
- ) -> None: ...
65
- global___PushSimulationOutputsResponse = PushSimulationOutputsResponse
@@ -2,10 +2,10 @@
2
2
  """Client and server classes corresponding to protobuf-defined services."""
3
3
  import grpc
4
4
 
5
+ from flwr.proto import appio_pb2 as flwr_dot_proto_dot_appio__pb2
5
6
  from flwr.proto import heartbeat_pb2 as flwr_dot_proto_dot_heartbeat__pb2
6
7
  from flwr.proto import log_pb2 as flwr_dot_proto_dot_log__pb2
7
8
  from flwr.proto import run_pb2 as flwr_dot_proto_dot_run__pb2
8
- from flwr.proto import simulationio_pb2 as flwr_dot_proto_dot_simulationio__pb2
9
9
 
10
10
 
11
11
  class SimulationIoStub(object):
@@ -17,15 +17,30 @@ class SimulationIoStub(object):
17
17
  Args:
18
18
  channel: A grpc.Channel.
19
19
  """
20
- self.PullSimulationInputs = channel.unary_unary(
21
- '/flwr.proto.SimulationIo/PullSimulationInputs',
22
- request_serializer=flwr_dot_proto_dot_simulationio__pb2.PullSimulationInputsRequest.SerializeToString,
23
- response_deserializer=flwr_dot_proto_dot_simulationio__pb2.PullSimulationInputsResponse.FromString,
20
+ self.ListAppsToLaunch = channel.unary_unary(
21
+ '/flwr.proto.SimulationIo/ListAppsToLaunch',
22
+ request_serializer=flwr_dot_proto_dot_appio__pb2.ListAppsToLaunchRequest.SerializeToString,
23
+ response_deserializer=flwr_dot_proto_dot_appio__pb2.ListAppsToLaunchResponse.FromString,
24
24
  )
25
- self.PushSimulationOutputs = channel.unary_unary(
26
- '/flwr.proto.SimulationIo/PushSimulationOutputs',
27
- request_serializer=flwr_dot_proto_dot_simulationio__pb2.PushSimulationOutputsRequest.SerializeToString,
28
- response_deserializer=flwr_dot_proto_dot_simulationio__pb2.PushSimulationOutputsResponse.FromString,
25
+ self.RequestToken = channel.unary_unary(
26
+ '/flwr.proto.SimulationIo/RequestToken',
27
+ request_serializer=flwr_dot_proto_dot_appio__pb2.RequestTokenRequest.SerializeToString,
28
+ response_deserializer=flwr_dot_proto_dot_appio__pb2.RequestTokenResponse.FromString,
29
+ )
30
+ self.GetRun = channel.unary_unary(
31
+ '/flwr.proto.SimulationIo/GetRun',
32
+ request_serializer=flwr_dot_proto_dot_run__pb2.GetRunRequest.SerializeToString,
33
+ response_deserializer=flwr_dot_proto_dot_run__pb2.GetRunResponse.FromString,
34
+ )
35
+ self.PullAppInputs = channel.unary_unary(
36
+ '/flwr.proto.SimulationIo/PullAppInputs',
37
+ request_serializer=flwr_dot_proto_dot_appio__pb2.PullAppInputsRequest.SerializeToString,
38
+ response_deserializer=flwr_dot_proto_dot_appio__pb2.PullAppInputsResponse.FromString,
39
+ )
40
+ self.PushAppOutputs = channel.unary_unary(
41
+ '/flwr.proto.SimulationIo/PushAppOutputs',
42
+ request_serializer=flwr_dot_proto_dot_appio__pb2.PushAppOutputsRequest.SerializeToString,
43
+ response_deserializer=flwr_dot_proto_dot_appio__pb2.PushAppOutputsResponse.FromString,
29
44
  )
30
45
  self.UpdateRunStatus = channel.unary_unary(
31
46
  '/flwr.proto.SimulationIo/UpdateRunStatus',
@@ -57,14 +72,35 @@ class SimulationIoStub(object):
57
72
  class SimulationIoServicer(object):
58
73
  """Missing associated documentation comment in .proto file."""
59
74
 
60
- def PullSimulationInputs(self, request, context):
75
+ def ListAppsToLaunch(self, request, context):
76
+ """List runs to launch
77
+ """
78
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
79
+ context.set_details('Method not implemented!')
80
+ raise NotImplementedError('Method not implemented!')
81
+
82
+ def RequestToken(self, request, context):
83
+ """Request token for a run
84
+ """
85
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
86
+ context.set_details('Method not implemented!')
87
+ raise NotImplementedError('Method not implemented!')
88
+
89
+ def GetRun(self, request, context):
90
+ """Get run details
91
+ """
92
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
93
+ context.set_details('Method not implemented!')
94
+ raise NotImplementedError('Method not implemented!')
95
+
96
+ def PullAppInputs(self, request, context):
61
97
  """Pull Simulation inputs
62
98
  """
63
99
  context.set_code(grpc.StatusCode.UNIMPLEMENTED)
64
100
  context.set_details('Method not implemented!')
65
101
  raise NotImplementedError('Method not implemented!')
66
102
 
67
- def PushSimulationOutputs(self, request, context):
103
+ def PushAppOutputs(self, request, context):
68
104
  """Push Simulation outputs
69
105
  """
70
106
  context.set_code(grpc.StatusCode.UNIMPLEMENTED)
@@ -109,15 +145,30 @@ class SimulationIoServicer(object):
109
145
 
110
146
  def add_SimulationIoServicer_to_server(servicer, server):
111
147
  rpc_method_handlers = {
112
- 'PullSimulationInputs': grpc.unary_unary_rpc_method_handler(
113
- servicer.PullSimulationInputs,
114
- request_deserializer=flwr_dot_proto_dot_simulationio__pb2.PullSimulationInputsRequest.FromString,
115
- response_serializer=flwr_dot_proto_dot_simulationio__pb2.PullSimulationInputsResponse.SerializeToString,
148
+ 'ListAppsToLaunch': grpc.unary_unary_rpc_method_handler(
149
+ servicer.ListAppsToLaunch,
150
+ request_deserializer=flwr_dot_proto_dot_appio__pb2.ListAppsToLaunchRequest.FromString,
151
+ response_serializer=flwr_dot_proto_dot_appio__pb2.ListAppsToLaunchResponse.SerializeToString,
152
+ ),
153
+ 'RequestToken': grpc.unary_unary_rpc_method_handler(
154
+ servicer.RequestToken,
155
+ request_deserializer=flwr_dot_proto_dot_appio__pb2.RequestTokenRequest.FromString,
156
+ response_serializer=flwr_dot_proto_dot_appio__pb2.RequestTokenResponse.SerializeToString,
116
157
  ),
117
- 'PushSimulationOutputs': grpc.unary_unary_rpc_method_handler(
118
- servicer.PushSimulationOutputs,
119
- request_deserializer=flwr_dot_proto_dot_simulationio__pb2.PushSimulationOutputsRequest.FromString,
120
- response_serializer=flwr_dot_proto_dot_simulationio__pb2.PushSimulationOutputsResponse.SerializeToString,
158
+ 'GetRun': grpc.unary_unary_rpc_method_handler(
159
+ servicer.GetRun,
160
+ request_deserializer=flwr_dot_proto_dot_run__pb2.GetRunRequest.FromString,
161
+ response_serializer=flwr_dot_proto_dot_run__pb2.GetRunResponse.SerializeToString,
162
+ ),
163
+ 'PullAppInputs': grpc.unary_unary_rpc_method_handler(
164
+ servicer.PullAppInputs,
165
+ request_deserializer=flwr_dot_proto_dot_appio__pb2.PullAppInputsRequest.FromString,
166
+ response_serializer=flwr_dot_proto_dot_appio__pb2.PullAppInputsResponse.SerializeToString,
167
+ ),
168
+ 'PushAppOutputs': grpc.unary_unary_rpc_method_handler(
169
+ servicer.PushAppOutputs,
170
+ request_deserializer=flwr_dot_proto_dot_appio__pb2.PushAppOutputsRequest.FromString,
171
+ response_serializer=flwr_dot_proto_dot_appio__pb2.PushAppOutputsResponse.SerializeToString,
121
172
  ),
122
173
  'UpdateRunStatus': grpc.unary_unary_rpc_method_handler(
123
174
  servicer.UpdateRunStatus,
@@ -155,7 +206,58 @@ class SimulationIo(object):
155
206
  """Missing associated documentation comment in .proto file."""
156
207
 
157
208
  @staticmethod
158
- def PullSimulationInputs(request,
209
+ def ListAppsToLaunch(request,
210
+ target,
211
+ options=(),
212
+ channel_credentials=None,
213
+ call_credentials=None,
214
+ insecure=False,
215
+ compression=None,
216
+ wait_for_ready=None,
217
+ timeout=None,
218
+ metadata=None):
219
+ return grpc.experimental.unary_unary(request, target, '/flwr.proto.SimulationIo/ListAppsToLaunch',
220
+ flwr_dot_proto_dot_appio__pb2.ListAppsToLaunchRequest.SerializeToString,
221
+ flwr_dot_proto_dot_appio__pb2.ListAppsToLaunchResponse.FromString,
222
+ options, channel_credentials,
223
+ insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
224
+
225
+ @staticmethod
226
+ def RequestToken(request,
227
+ target,
228
+ options=(),
229
+ channel_credentials=None,
230
+ call_credentials=None,
231
+ insecure=False,
232
+ compression=None,
233
+ wait_for_ready=None,
234
+ timeout=None,
235
+ metadata=None):
236
+ return grpc.experimental.unary_unary(request, target, '/flwr.proto.SimulationIo/RequestToken',
237
+ flwr_dot_proto_dot_appio__pb2.RequestTokenRequest.SerializeToString,
238
+ flwr_dot_proto_dot_appio__pb2.RequestTokenResponse.FromString,
239
+ options, channel_credentials,
240
+ insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
241
+
242
+ @staticmethod
243
+ def GetRun(request,
244
+ target,
245
+ options=(),
246
+ channel_credentials=None,
247
+ call_credentials=None,
248
+ insecure=False,
249
+ compression=None,
250
+ wait_for_ready=None,
251
+ timeout=None,
252
+ metadata=None):
253
+ return grpc.experimental.unary_unary(request, target, '/flwr.proto.SimulationIo/GetRun',
254
+ flwr_dot_proto_dot_run__pb2.GetRunRequest.SerializeToString,
255
+ flwr_dot_proto_dot_run__pb2.GetRunResponse.FromString,
256
+ options, channel_credentials,
257
+ insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
258
+
259
+ @staticmethod
260
+ def PullAppInputs(request,
159
261
  target,
160
262
  options=(),
161
263
  channel_credentials=None,
@@ -165,14 +267,14 @@ class SimulationIo(object):
165
267
  wait_for_ready=None,
166
268
  timeout=None,
167
269
  metadata=None):
168
- return grpc.experimental.unary_unary(request, target, '/flwr.proto.SimulationIo/PullSimulationInputs',
169
- flwr_dot_proto_dot_simulationio__pb2.PullSimulationInputsRequest.SerializeToString,
170
- flwr_dot_proto_dot_simulationio__pb2.PullSimulationInputsResponse.FromString,
270
+ return grpc.experimental.unary_unary(request, target, '/flwr.proto.SimulationIo/PullAppInputs',
271
+ flwr_dot_proto_dot_appio__pb2.PullAppInputsRequest.SerializeToString,
272
+ flwr_dot_proto_dot_appio__pb2.PullAppInputsResponse.FromString,
171
273
  options, channel_credentials,
172
274
  insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
173
275
 
174
276
  @staticmethod
175
- def PushSimulationOutputs(request,
277
+ def PushAppOutputs(request,
176
278
  target,
177
279
  options=(),
178
280
  channel_credentials=None,
@@ -182,9 +284,9 @@ class SimulationIo(object):
182
284
  wait_for_ready=None,
183
285
  timeout=None,
184
286
  metadata=None):
185
- return grpc.experimental.unary_unary(request, target, '/flwr.proto.SimulationIo/PushSimulationOutputs',
186
- flwr_dot_proto_dot_simulationio__pb2.PushSimulationOutputsRequest.SerializeToString,
187
- flwr_dot_proto_dot_simulationio__pb2.PushSimulationOutputsResponse.FromString,
287
+ return grpc.experimental.unary_unary(request, target, '/flwr.proto.SimulationIo/PushAppOutputs',
288
+ flwr_dot_proto_dot_appio__pb2.PushAppOutputsRequest.SerializeToString,
289
+ flwr_dot_proto_dot_appio__pb2.PushAppOutputsResponse.FromString,
188
290
  options, channel_credentials,
189
291
  insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
190
292
 
@@ -3,22 +3,37 @@
3
3
  isort:skip_file
4
4
  """
5
5
  import abc
6
+ import flwr.proto.appio_pb2
6
7
  import flwr.proto.heartbeat_pb2
7
8
  import flwr.proto.log_pb2
8
9
  import flwr.proto.run_pb2
9
- import flwr.proto.simulationio_pb2
10
10
  import grpc
11
11
 
12
12
  class SimulationIoStub:
13
13
  def __init__(self, channel: grpc.Channel) -> None: ...
14
- PullSimulationInputs: grpc.UnaryUnaryMultiCallable[
15
- flwr.proto.simulationio_pb2.PullSimulationInputsRequest,
16
- flwr.proto.simulationio_pb2.PullSimulationInputsResponse]
14
+ ListAppsToLaunch: grpc.UnaryUnaryMultiCallable[
15
+ flwr.proto.appio_pb2.ListAppsToLaunchRequest,
16
+ flwr.proto.appio_pb2.ListAppsToLaunchResponse]
17
+ """List runs to launch"""
18
+
19
+ RequestToken: grpc.UnaryUnaryMultiCallable[
20
+ flwr.proto.appio_pb2.RequestTokenRequest,
21
+ flwr.proto.appio_pb2.RequestTokenResponse]
22
+ """Request token for a run"""
23
+
24
+ GetRun: grpc.UnaryUnaryMultiCallable[
25
+ flwr.proto.run_pb2.GetRunRequest,
26
+ flwr.proto.run_pb2.GetRunResponse]
27
+ """Get run details"""
28
+
29
+ PullAppInputs: grpc.UnaryUnaryMultiCallable[
30
+ flwr.proto.appio_pb2.PullAppInputsRequest,
31
+ flwr.proto.appio_pb2.PullAppInputsResponse]
17
32
  """Pull Simulation inputs"""
18
33
 
19
- PushSimulationOutputs: grpc.UnaryUnaryMultiCallable[
20
- flwr.proto.simulationio_pb2.PushSimulationOutputsRequest,
21
- flwr.proto.simulationio_pb2.PushSimulationOutputsResponse]
34
+ PushAppOutputs: grpc.UnaryUnaryMultiCallable[
35
+ flwr.proto.appio_pb2.PushAppOutputsRequest,
36
+ flwr.proto.appio_pb2.PushAppOutputsResponse]
22
37
  """Push Simulation outputs"""
23
38
 
24
39
  UpdateRunStatus: grpc.UnaryUnaryMultiCallable[
@@ -49,18 +64,42 @@ class SimulationIoStub:
49
64
 
50
65
  class SimulationIoServicer(metaclass=abc.ABCMeta):
51
66
  @abc.abstractmethod
52
- def PullSimulationInputs(self,
53
- request: flwr.proto.simulationio_pb2.PullSimulationInputsRequest,
67
+ def ListAppsToLaunch(self,
68
+ request: flwr.proto.appio_pb2.ListAppsToLaunchRequest,
54
69
  context: grpc.ServicerContext,
55
- ) -> flwr.proto.simulationio_pb2.PullSimulationInputsResponse:
70
+ ) -> flwr.proto.appio_pb2.ListAppsToLaunchResponse:
71
+ """List runs to launch"""
72
+ pass
73
+
74
+ @abc.abstractmethod
75
+ def RequestToken(self,
76
+ request: flwr.proto.appio_pb2.RequestTokenRequest,
77
+ context: grpc.ServicerContext,
78
+ ) -> flwr.proto.appio_pb2.RequestTokenResponse:
79
+ """Request token for a run"""
80
+ pass
81
+
82
+ @abc.abstractmethod
83
+ def GetRun(self,
84
+ request: flwr.proto.run_pb2.GetRunRequest,
85
+ context: grpc.ServicerContext,
86
+ ) -> flwr.proto.run_pb2.GetRunResponse:
87
+ """Get run details"""
88
+ pass
89
+
90
+ @abc.abstractmethod
91
+ def PullAppInputs(self,
92
+ request: flwr.proto.appio_pb2.PullAppInputsRequest,
93
+ context: grpc.ServicerContext,
94
+ ) -> flwr.proto.appio_pb2.PullAppInputsResponse:
56
95
  """Pull Simulation inputs"""
57
96
  pass
58
97
 
59
98
  @abc.abstractmethod
60
- def PushSimulationOutputs(self,
61
- request: flwr.proto.simulationio_pb2.PushSimulationOutputsRequest,
99
+ def PushAppOutputs(self,
100
+ request: flwr.proto.appio_pb2.PushAppOutputsRequest,
62
101
  context: grpc.ServicerContext,
63
- ) -> flwr.proto.simulationio_pb2.PushSimulationOutputsResponse:
102
+ ) -> flwr.proto.appio_pb2.PushAppOutputsResponse:
64
103
  """Push Simulation outputs"""
65
104
  pass
66
105
 
flwr/server/app.py CHANGED
@@ -18,9 +18,8 @@
18
18
  import argparse
19
19
  import csv
20
20
  import importlib.util
21
- import multiprocessing
22
- import multiprocessing.context
23
21
  import os
22
+ import subprocess
24
23
  import sys
25
24
  import threading
26
25
  from collections.abc import Sequence
@@ -55,6 +54,7 @@ from flwr.common.constant import (
55
54
  TRANSPORT_TYPE_GRPC_RERE,
56
55
  TRANSPORT_TYPE_REST,
57
56
  EventLogWriterType,
57
+ ExecPluginType,
58
58
  )
59
59
  from flwr.common.event_log_plugin import EventLogWriterPlugin
60
60
  from flwr.common.exit import ExitCode, flwr_exit
@@ -69,8 +69,6 @@ from flwr.proto.fleet_pb2_grpc import ( # pylint: disable=E0611
69
69
  )
70
70
  from flwr.proto.grpcadapter_pb2_grpc import add_GrpcAdapterServicer_to_server
71
71
  from flwr.server.fleet_event_log_interceptor import FleetEventLogInterceptor
72
- from flwr.server.serverapp.app import flwr_serverapp
73
- from flwr.simulation.app import flwr_simulation
74
72
  from flwr.supercore.ffs import FfsFactory
75
73
  from flwr.supercore.object_store import ObjectStoreFactory
76
74
  from flwr.superlink.executor import load_executor
@@ -219,10 +217,10 @@ def run_superlink() -> None:
219
217
 
220
218
  # Determine Exec plugin
221
219
  # If simulation is used, don't start ServerAppIo and Fleet APIs
222
- sim_exec = executor.__class__.__qualname__ == "SimulationEngine"
220
+ is_simulation = executor.__class__.__qualname__ == "SimulationEngine"
223
221
  bckg_threads: list[threading.Thread] = []
224
222
 
225
- if sim_exec:
223
+ if is_simulation:
226
224
  simulationio_server: grpc.Server = run_simulationio_api_grpc(
227
225
  address=simulationio_address,
228
226
  state_factory=state_factory,
@@ -340,25 +338,18 @@ def run_superlink() -> None:
340
338
  io_address = (
341
339
  f"{CLIENT_OCTET}:{_port}" if _octet == SERVER_OCTET else serverappio_address
342
340
  )
343
- address_arg = (
344
- "--simulationio-api-address" if sim_exec else "--serverappio-api-address"
345
- )
346
- address = simulationio_address if sim_exec else io_address
347
- cmd = "flwr-simulation" if sim_exec else "flwr-serverapp"
348
-
349
- # Scheduler thread
350
- scheduler_th = threading.Thread(
351
- target=_flwr_scheduler,
352
- args=(
353
- state_factory,
354
- address_arg,
355
- address,
356
- cmd,
357
- ),
358
- daemon=True,
359
- )
360
- scheduler_th.start()
361
- bckg_threads.append(scheduler_th)
341
+ command = ["flower-superexec", "--insecure"]
342
+ command += [
343
+ "--appio-api-address",
344
+ simulationio_address if is_simulation else io_address,
345
+ ]
346
+ command += [
347
+ "--plugin-type",
348
+ ExecPluginType.SIMULATION if is_simulation else ExecPluginType.SERVER_APP,
349
+ ]
350
+ command += ["--parent-pid", str(os.getpid())]
351
+ # pylint: disable-next=consider-using-with
352
+ subprocess.Popen(command)
362
353
 
363
354
  # Graceful shutdown
364
355
  register_exit_handlers(
@@ -376,75 +367,6 @@ def run_superlink() -> None:
376
367
  flwr_exit(ExitCode.SUPERLINK_THREAD_CRASH)
377
368
 
378
369
 
379
- def _run_flwr_command(args: list[str], main_pid: int) -> None:
380
- # Monitor the main process in case of SIGKILL
381
- def main_process_monitor() -> None:
382
- while True:
383
- sleep(1)
384
- if os.getppid() != main_pid:
385
- os.kill(os.getpid(), 9)
386
-
387
- threading.Thread(target=main_process_monitor, daemon=True).start()
388
-
389
- # Run the command
390
- sys.argv = args
391
- if args[0] == "flwr-serverapp":
392
- flwr_serverapp()
393
- elif args[0] == "flwr-simulation":
394
- flwr_simulation()
395
- else:
396
- raise ValueError(f"Unknown command: {args[0]}")
397
-
398
-
399
- def _flwr_scheduler(
400
- state_factory: LinkStateFactory,
401
- io_api_arg: str,
402
- io_api_address: str,
403
- cmd: str,
404
- ) -> None:
405
- log(DEBUG, "Started %s scheduler thread.", cmd)
406
- state = state_factory.state()
407
- run_id_to_proc: dict[int, multiprocessing.context.SpawnProcess] = {}
408
-
409
- # Use the "spawn" start method for multiprocessing.
410
- mp_spawn_context = multiprocessing.get_context("spawn")
411
-
412
- # Periodically check for a pending run in the LinkState
413
- while True:
414
- sleep(0.1)
415
- pending_run_id = state.get_pending_run_id()
416
-
417
- if pending_run_id and pending_run_id not in run_id_to_proc:
418
-
419
- log(
420
- INFO,
421
- "Launching %s subprocess. Connects to SuperLink on %s",
422
- cmd,
423
- io_api_address,
424
- )
425
- # Start subprocess
426
- command = [
427
- cmd,
428
- "--run-once",
429
- io_api_arg,
430
- io_api_address,
431
- "--insecure",
432
- ]
433
-
434
- proc = mp_spawn_context.Process(
435
- target=_run_flwr_command, args=(command, os.getpid()), daemon=True
436
- )
437
- proc.start()
438
-
439
- # Store the process
440
- run_id_to_proc[pending_run_id] = proc
441
-
442
- # Clean up finished processes
443
- for run_id, proc in list(run_id_to_proc.items()):
444
- if not proc.is_alive():
445
- del run_id_to_proc[run_id]
446
-
447
-
448
370
  def _format_address(address: str) -> tuple[str, str, int]:
449
371
  parsed_address = parse_address(address)
450
372
  if not parsed_address:
@@ -92,7 +92,7 @@ def flwr_serverapp() -> None:
92
92
  serverappio_api_address=args.serverappio_api_address,
93
93
  log_queue=log_queue,
94
94
  token=args.token,
95
- run_once=args.run_once,
95
+ run_once=(args.token is not None) or args.run_once,
96
96
  flwr_dir=args.flwr_dir,
97
97
  certificates=None,
98
98
  parent_pid=args.parent_pid,
@@ -287,19 +287,6 @@ def _parse_args_run_flwr_serverapp() -> argparse.ArgumentParser:
287
287
  help="Address of SuperLink's ServerAppIo API (IPv4, IPv6, or a domain name)."
288
288
  f"By default, it is set to {SERVERAPPIO_API_DEFAULT_CLIENT_ADDRESS}.",
289
289
  )
290
- parser.add_argument(
291
- "--token",
292
- type=str,
293
- required=False,
294
- help="Unique token generated by SuperNode for each ServerApp execution",
295
- )
296
- parser.add_argument(
297
- "--parent-pid",
298
- type=int,
299
- default=None,
300
- help="The PID of the parent process. When set, the process will terminate "
301
- "when the parent process exits.",
302
- )
303
290
  parser.add_argument(
304
291
  "--run-once",
305
292
  action="store_true",
@@ -34,6 +34,16 @@ from flwr.common.serde import (
34
34
  )
35
35
  from flwr.common.typing import Fab, RunStatus
36
36
  from flwr.proto import simulationio_pb2_grpc
37
+ from flwr.proto.appio_pb2 import ( # pylint: disable=E0611
38
+ ListAppsToLaunchRequest,
39
+ ListAppsToLaunchResponse,
40
+ PullAppInputsRequest,
41
+ PullAppInputsResponse,
42
+ PushAppOutputsRequest,
43
+ PushAppOutputsResponse,
44
+ RequestTokenRequest,
45
+ RequestTokenResponse,
46
+ )
37
47
  from flwr.proto.heartbeat_pb2 import ( # pylint: disable=E0611
38
48
  SendAppHeartbeatRequest,
39
49
  SendAppHeartbeatResponse,
@@ -45,17 +55,13 @@ from flwr.proto.log_pb2 import ( # pylint: disable=E0611
45
55
  from flwr.proto.run_pb2 import ( # pylint: disable=E0611
46
56
  GetFederationOptionsRequest,
47
57
  GetFederationOptionsResponse,
58
+ GetRunRequest,
59
+ GetRunResponse,
48
60
  GetRunStatusRequest,
49
61
  GetRunStatusResponse,
50
62
  UpdateRunStatusRequest,
51
63
  UpdateRunStatusResponse,
52
64
  )
53
- from flwr.proto.simulationio_pb2 import ( # pylint: disable=E0611
54
- PullSimulationInputsRequest,
55
- PullSimulationInputsResponse,
56
- PushSimulationOutputsRequest,
57
- PushSimulationOutputsResponse,
58
- )
59
65
  from flwr.server.superlink.linkstate import LinkStateFactory
60
66
  from flwr.server.superlink.utils import abort_if
61
67
  from flwr.supercore.ffs import FfsFactory
@@ -71,23 +77,73 @@ class SimulationIoServicer(simulationio_pb2_grpc.SimulationIoServicer):
71
77
  self.ffs_factory = ffs_factory
72
78
  self.lock = threading.RLock()
73
79
 
74
- def PullSimulationInputs(
75
- self, request: PullSimulationInputsRequest, context: ServicerContext
76
- ) -> PullSimulationInputsResponse:
80
+ def ListAppsToLaunch(
81
+ self,
82
+ request: ListAppsToLaunchRequest,
83
+ context: grpc.ServicerContext,
84
+ ) -> ListAppsToLaunchResponse:
85
+ """Get run IDs with pending messages."""
86
+ log(DEBUG, "SimulationIoServicer.ListAppsToLaunch")
87
+
88
+ # Initialize state connection
89
+ state = self.state_factory.state()
90
+
91
+ # Get IDs of runs in pending status
92
+ run_ids = state.get_run_ids(flwr_aid=None)
93
+ pending_run_ids = []
94
+ for run_id, status in state.get_run_status(run_ids).items():
95
+ if status.status == Status.PENDING:
96
+ pending_run_ids.append(run_id)
97
+
98
+ # Return run IDs
99
+ return ListAppsToLaunchResponse(run_ids=pending_run_ids)
100
+
101
+ def RequestToken(
102
+ self, request: RequestTokenRequest, context: grpc.ServicerContext
103
+ ) -> RequestTokenResponse:
104
+ """Request token."""
105
+ log(DEBUG, "SimulationIoServicer.RequestToken")
106
+
107
+ # Initialize state connection
108
+ state = self.state_factory.state()
109
+
110
+ # Attempt to create a token for the provided run ID
111
+ token = state.create_token(request.run_id)
112
+
113
+ # Return the token
114
+ return RequestTokenResponse(token=token or "")
115
+
116
+ def GetRun(
117
+ self, request: GetRunRequest, context: grpc.ServicerContext
118
+ ) -> GetRunResponse:
119
+ """Get run information."""
120
+ log(DEBUG, "SimulationIoServicer.GetRun")
121
+
122
+ # Init state
123
+ state = self.state_factory.state()
124
+
125
+ # Retrieve run information
126
+ run = state.get_run(request.run_id)
127
+
128
+ if run is None:
129
+ return GetRunResponse()
130
+
131
+ return GetRunResponse(run=run_to_proto(run))
132
+
133
+ def PullAppInputs(
134
+ self, request: PullAppInputsRequest, context: ServicerContext
135
+ ) -> PullAppInputsResponse:
77
136
  """Pull SimultionIo process inputs."""
78
137
  log(DEBUG, "SimultionIoServicer.SimultionIoInputs")
79
138
  # Init access to LinkState and Ffs
80
139
  state = self.state_factory.state()
81
140
  ffs = self.ffs_factory.ffs()
82
141
 
142
+ # Validate the token
143
+ run_id = self._verify_token(request.token, context)
144
+
83
145
  # Lock access to LinkState, preventing obtaining the same pending run_id
84
146
  with self.lock:
85
- # Attempt getting the run_id of a pending run
86
- run_id = state.get_pending_run_id()
87
- # If there's no pending run, return an empty response
88
- if run_id is None:
89
- return PullSimulationInputsResponse()
90
-
91
147
  # Retrieve Context, Run and Fab for the run_id
92
148
  serverapp_ctxt = state.get_serverapp_context(run_id)
93
149
  run = state.get_run(run_id)
@@ -99,7 +155,7 @@ class SimulationIoServicer(simulationio_pb2_grpc.SimulationIoServicer):
99
155
  # Update run status to STARTING
100
156
  if state.update_run_status(run_id, RunStatus(Status.STARTING, "", "")):
101
157
  log(INFO, "Starting run %d", run_id)
102
- return PullSimulationInputsResponse(
158
+ return PullAppInputsResponse(
103
159
  context=context_to_proto(serverapp_ctxt),
104
160
  run=run_to_proto(run),
105
161
  fab=fab_to_proto(fab),
@@ -109,11 +165,16 @@ class SimulationIoServicer(simulationio_pb2_grpc.SimulationIoServicer):
109
165
  # or if the status cannot be updated to STARTING
110
166
  raise RuntimeError(f"Failed to start run {run_id}")
111
167
 
112
- def PushSimulationOutputs(
113
- self, request: PushSimulationOutputsRequest, context: ServicerContext
114
- ) -> PushSimulationOutputsResponse:
168
+ def PushAppOutputs(
169
+ self, request: PushAppOutputsRequest, context: ServicerContext
170
+ ) -> PushAppOutputsResponse:
115
171
  """Push Simulation process outputs."""
116
- log(DEBUG, "SimultionIoServicer.PushSimulationOutputs")
172
+ log(DEBUG, "SimultionIoServicer.PushAppOutputs")
173
+
174
+ # Validate the token
175
+ run_id = self._verify_token(request.token, context)
176
+
177
+ # Init access to LinkState
117
178
  state = self.state_factory.state()
118
179
 
119
180
  # Abort if the run is not running
@@ -126,7 +187,10 @@ class SimulationIoServicer(simulationio_pb2_grpc.SimulationIoServicer):
126
187
  )
127
188
 
128
189
  state.set_serverapp_context(request.run_id, context_from_proto(request.context))
129
- return PushSimulationOutputsResponse()
190
+
191
+ # Remove the token
192
+ state.delete_token(run_id)
193
+ return PushAppOutputsResponse()
130
194
 
131
195
  def UpdateRunStatus(
132
196
  self, request: UpdateRunStatusRequest, context: grpc.ServicerContext
@@ -208,3 +272,15 @@ class SimulationIoServicer(simulationio_pb2_grpc.SimulationIoServicer):
208
272
  )
209
273
 
210
274
  return SendAppHeartbeatResponse(success=success)
275
+
276
+ def _verify_token(self, token: str, context: grpc.ServicerContext) -> int:
277
+ """Verify the token and return the associated run ID."""
278
+ state = self.state_factory.state()
279
+ run_id = state.get_run_id_by_token(token)
280
+ if run_id is None or not state.verify_token(run_id, token):
281
+ context.abort(
282
+ grpc.StatusCode.PERMISSION_DENIED,
283
+ "Invalid token.",
284
+ )
285
+ raise RuntimeError("This line should never be reached.")
286
+ return run_id
flwr/simulation/app.py CHANGED
@@ -19,7 +19,6 @@ import argparse
19
19
  import gc
20
20
  from logging import DEBUG, ERROR, INFO
21
21
  from queue import Queue
22
- from time import sleep
23
22
  from typing import Optional
24
23
 
25
24
  from flwr.cli.config_utils import get_fab_metadata
@@ -57,19 +56,20 @@ from flwr.common.serde import (
57
56
  run_status_to_proto,
58
57
  )
59
58
  from flwr.common.typing import RunStatus
59
+ from flwr.proto.appio_pb2 import ( # pylint: disable=E0611
60
+ PullAppInputsRequest,
61
+ PullAppInputsResponse,
62
+ PushAppOutputsRequest,
63
+ )
60
64
  from flwr.proto.run_pb2 import ( # pylint: disable=E0611
61
65
  GetFederationOptionsRequest,
62
66
  GetFederationOptionsResponse,
63
67
  UpdateRunStatusRequest,
64
68
  )
65
- from flwr.proto.simulationio_pb2 import ( # pylint: disable=E0611
66
- PullSimulationInputsRequest,
67
- PullSimulationInputsResponse,
68
- PushSimulationOutputsRequest,
69
- )
70
69
  from flwr.server.superlink.fleet.vce.backend.backend import BackendConfig
71
70
  from flwr.simulation.run_simulation import _run_simulation
72
71
  from flwr.simulation.simulationio_connection import SimulationIoConnection
72
+ from flwr.supercore.app_utils import simple_get_token, start_parent_process_monitor
73
73
 
74
74
 
75
75
  def flwr_simulation() -> None:
@@ -97,23 +97,31 @@ def flwr_simulation() -> None:
97
97
  run_simulation_process(
98
98
  simulationio_api_address=args.simulationio_api_address,
99
99
  log_queue=log_queue,
100
- run_once=args.run_once,
100
+ run_once=(args.token is not None) or args.run_once,
101
+ token=args.token,
101
102
  flwr_dir_=args.flwr_dir,
102
103
  certificates=None,
104
+ parent_pid=args.parent_pid,
103
105
  )
104
106
 
105
107
  # Restore stdout/stderr
106
108
  restore_output()
107
109
 
108
110
 
109
- def run_simulation_process( # pylint: disable=R0914, disable=W0212, disable=R0915
111
+ def run_simulation_process( # pylint: disable=R0913, R0914, R0915, R0917, W0212
110
112
  simulationio_api_address: str,
111
113
  log_queue: Queue[Optional[str]],
112
114
  run_once: bool,
115
+ token: Optional[str] = None,
113
116
  flwr_dir_: Optional[str] = None,
114
117
  certificates: Optional[bytes] = None,
118
+ parent_pid: Optional[int] = None,
115
119
  ) -> None:
116
120
  """Run Flower Simulation process."""
121
+ # Start monitoring the parent process if a PID is provided
122
+ if parent_pid is not None:
123
+ start_parent_process_monitor(parent_pid)
124
+
117
125
  conn = SimulationIoConnection(
118
126
  simulationio_service_address=simulationio_api_address,
119
127
  root_certificates=certificates,
@@ -127,14 +135,14 @@ def run_simulation_process( # pylint: disable=R0914, disable=W0212, disable=R09
127
135
  while True:
128
136
 
129
137
  try:
130
- # Pull SimulationInputs from LinkState
131
- req = PullSimulationInputsRequest()
132
- res: PullSimulationInputsResponse = conn._stub.PullSimulationInputs(req)
133
- if not res.HasField("run"):
134
- sleep(3)
135
- run_status = None
136
- continue
138
+ # If token is not set, loop until token is received from SuperNode
139
+ if token is None:
140
+ log(DEBUG, "[flwr-simulation] Request token")
141
+ token = simple_get_token(conn._stub)
137
142
 
143
+ # Pull SimulationInputs from LinkState
144
+ req = PullAppInputsRequest(token=token)
145
+ res: PullAppInputsResponse = conn._stub.PullAppInputs(req)
138
146
  context = context_from_proto(res.context)
139
147
  run = run_from_proto(res.run)
140
148
  fab = fab_from_proto(res.fab)
@@ -240,10 +248,10 @@ def run_simulation_process( # pylint: disable=R0914, disable=W0212, disable=R09
240
248
 
241
249
  # Send resulting context
242
250
  context_proto = context_to_proto(updated_context)
243
- out_req = PushSimulationOutputsRequest(
244
- run_id=run.run_id, context=context_proto
251
+ out_req = PushAppOutputsRequest(
252
+ token=token, run_id=run.run_id, context=context_proto
245
253
  )
246
- _ = conn._stub.PushSimulationOutputs(out_req)
254
+ _ = conn._stub.PushAppOutputs(out_req)
247
255
 
248
256
  run_status = RunStatus(Status.FINISHED, SubStatus.COMPLETED, "")
249
257
 
@@ -277,6 +285,9 @@ def run_simulation_process( # pylint: disable=R0914, disable=W0212, disable=R09
277
285
  del updated_context
278
286
  except NameError:
279
287
  pass
288
+
289
+ # Remove the token
290
+ token = None
280
291
  gc.collect()
281
292
 
282
293
  # Stop the loop if `flwr-simulation` is expected to process a single run
@@ -29,6 +29,7 @@ from flwr.proto.appio_pb2 import ( # pylint: disable=E0611
29
29
  )
30
30
  from flwr.proto.clientappio_pb2_grpc import ClientAppIoStub
31
31
  from flwr.proto.serverappio_pb2_grpc import ServerAppIoStub
32
+ from flwr.proto.simulationio_pb2_grpc import SimulationIoStub
32
33
 
33
34
  if os.name == "nt":
34
35
  from ctypes import windll # type: ignore
@@ -70,7 +71,9 @@ def start_parent_process_monitor(
70
71
  threading.Thread(target=monitor, daemon=True).start()
71
72
 
72
73
 
73
- def simple_get_token(stub: Union[ClientAppIoStub, ServerAppIoStub]) -> str:
74
+ def simple_get_token(
75
+ stub: Union[ClientAppIoStub, ServerAppIoStub, SimulationIoStub]
76
+ ) -> str:
74
77
  """Get a token from SuperLink/SuperNode.
75
78
 
76
79
  This shall be removed once the SuperExec is fully implemented.
@@ -65,19 +65,6 @@ def _parse_args_run_flwr_clientapp() -> argparse.ArgumentParser:
65
65
  help="Address of SuperNode's ClientAppIo API (IPv4, IPv6, or a domain name)."
66
66
  f"By default, it is set to {CLIENTAPPIO_API_DEFAULT_CLIENT_ADDRESS}.",
67
67
  )
68
- parser.add_argument(
69
- "--token",
70
- type=str,
71
- required=False,
72
- help="Unique token generated by SuperNode for each ClientApp execution",
73
- )
74
- parser.add_argument(
75
- "--parent-pid",
76
- type=int,
77
- default=None,
78
- help="The PID of the parent process. When set, the process will terminate "
79
- "when the parent process exits.",
80
- )
81
68
  parser.add_argument(
82
69
  "--run-once",
83
70
  action="store_true",
@@ -35,14 +35,13 @@ from flwr.common import GRPC_MAX_MESSAGE_LENGTH, Context, Message, RecordDict
35
35
  from flwr.common.address import parse_address
36
36
  from flwr.common.config import get_flwr_dir, get_fused_config_from_fab
37
37
  from flwr.common.constant import (
38
- CLIENT_OCTET,
39
38
  CLIENTAPPIO_API_DEFAULT_SERVER_ADDRESS,
40
39
  ISOLATION_MODE_SUBPROCESS,
41
- SERVER_OCTET,
42
40
  TRANSPORT_TYPE_GRPC_ADAPTER,
43
41
  TRANSPORT_TYPE_GRPC_RERE,
44
42
  TRANSPORT_TYPE_REST,
45
43
  TRANSPORT_TYPES,
44
+ ExecPluginType,
46
45
  )
47
46
  from flwr.common.exit import ExitCode, flwr_exit
48
47
  from flwr.common.exit_handlers import register_exit_handlers
@@ -164,6 +163,15 @@ def start_client_internal(
164
163
  ffs = ffs_factory.ffs()
165
164
  store = object_store_factory.store()
166
165
 
166
+ # Launch the SuperExec if the isolation mode is `subprocess`
167
+ if isolation == ISOLATION_MODE_SUBPROCESS:
168
+ command = ["flower-superexec", "--insecure"]
169
+ command += ["--appio-api-address", clientappio_api_address]
170
+ command += ["--plugin-type", ExecPluginType.CLIENT_APP]
171
+ command += ["--parent-pid", str(os.getpid())]
172
+ # pylint: disable-next=consider-using-with
173
+ subprocess.Popen(command)
174
+
167
175
  with _init_connection(
168
176
  transport=transport,
169
177
  server_address=server_address,
@@ -207,35 +215,6 @@ def start_client_internal(
207
215
  confirm_message_received=confirm_message_received,
208
216
  )
209
217
 
210
- # Two isolation modes:
211
- # 1. `subprocess`: SuperNode is starting the ClientApp
212
- # process as a subprocess.
213
- # 2. `process`: ClientApp process gets started separately
214
- # (via `flwr-clientapp`), for example, in a separate
215
- # Docker container.
216
-
217
- # Mode 1: SuperNode starts ClientApp as subprocess
218
- start_subprocess = isolation == ISOLATION_MODE_SUBPROCESS
219
-
220
- if start_subprocess and run_id is not None:
221
- _octet, _colon, _port = clientappio_api_address.rpartition(":")
222
- io_address = (
223
- f"{CLIENT_OCTET}:{_port}"
224
- if _octet == SERVER_OCTET
225
- else clientappio_api_address
226
- )
227
- # Start ClientApp subprocess
228
- command = [
229
- "flwr-clientapp",
230
- "--clientappio-api-address",
231
- io_address,
232
- "--parent-pid",
233
- str(os.getpid()),
234
- "--insecure",
235
- "--run-once",
236
- ]
237
- subprocess.run(command, check=False)
238
-
239
218
  # No message has been pulled therefore we can skip the push stage.
240
219
  if run_id is None:
241
220
  # If no message was received, wait for a while
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: flwr-nightly
3
- Version: 1.21.0.dev20250811
3
+ Version: 1.21.0.dev20250813
4
4
  Summary: Flower: A Friendly Federated AI Framework
5
5
  License: Apache-2.0
6
6
  Keywords: Artificial Intelligence,Federated AI,Federated Analytics,Federated Evaluation,Federated Learning,Flower,Machine Learning
@@ -172,8 +172,9 @@ Flower Baselines is a collection of community-contributed projects that reproduc
172
172
  - [FedOpt](https://github.com/adap/flower/tree/main/baselines/flwr_baselines/flwr_baselines/publications/adaptive_federated_optimization)
173
173
 
174
174
  Please refer to the [Flower Baselines Documentation](https://flower.ai/docs/baselines/) for a detailed categorization of baselines and for additional info including:
175
- * [How to use Flower Baselines](https://flower.ai/docs/baselines/how-to-use-baselines.html)
176
- * [How to contribute a new Flower Baseline](https://flower.ai/docs/baselines/how-to-contribute-baselines.html)
175
+
176
+ - [How to use Flower Baselines](https://flower.ai/docs/baselines/how-to-use-baselines.html)
177
+ - [How to contribute a new Flower Baseline](https://flower.ai/docs/baselines/how-to-contribute-baselines.html)
177
178
 
178
179
  ## Flower Usage Examples
179
180
 
@@ -104,7 +104,7 @@ flwr/client/typing.py,sha256=Jw3rawDzI_-ZDcRmEQcs5gZModY7oeQlEeltYsdOhlU,1048
104
104
  flwr/clientapp/__init__.py,sha256=zGW4z49Ojzoi1hDiRC7kyhLjijUilc6fqHhtM_ATRVA,719
105
105
  flwr/common/__init__.py,sha256=5GCLVk399Az_rTJHNticRlL0Sl_oPw_j5_LuFKfX7-M,4171
106
106
  flwr/common/address.py,sha256=9JucdTwlc-jpeJkRKeUboZoacUtErwSVtnDR9kAtLqE,4119
107
- flwr/common/args.py,sha256=XFQ5PU0lU7NS1QCiKhhESHVeL8KSjcD3x8h4P3e5qlM,5298
107
+ flwr/common/args.py,sha256=WVx-NI3d9kc1vCxfQ_JAHqoAulJHbvMXDqoGjz21hF0,5686
108
108
  flwr/common/auth_plugin/__init__.py,sha256=DktrRcGZrRarLf7Jb_UlHtOyLp9_-kEplyq6PS5-vOA,988
109
109
  flwr/common/auth_plugin/auth_plugin.py,sha256=mM7SuphO4OsVAVJR1GErYVgYT83ZjxDzS_gha12bT9E,4855
110
110
  flwr/common/config.py,sha256=glcZDjco-amw1YfQcYTFJ4S1pt9APoexT-mf1QscuHs,13960
@@ -220,17 +220,17 @@ flwr/proto/serverappio_pb2.py,sha256=CRbRIJk9r4RvLng1a_2M32LdNM3PoZmBRlNLO7fKVFs
220
220
  flwr/proto/serverappio_pb2.pyi,sha256=MDY9CEUnev_oi7Y-gZIXx8divcc0BufLAE2d7nmbo_Y,1319
221
221
  flwr/proto/serverappio_pb2_grpc.py,sha256=hfRrdawakgu1uV6vf7lHSqB7IZNlxmsYmep3KJQXjjs,27446
222
222
  flwr/proto/serverappio_pb2_grpc.pyi,sha256=65o7dZaEbqaYJKnJG84umeHGKGdJJQKK1FYMIUnvYwQ,7461
223
- flwr/proto/simulationio_pb2.py,sha256=sAJX72z-IttVGxyU3PFnG8AFuA-pV7itvBoxz-hOudE,3342
224
- flwr/proto/simulationio_pb2.pyi,sha256=oXx8_FLBe5B54wduZj-f89kub73XxNtQbThuW8YfPAs,2660
225
- flwr/proto/simulationio_pb2_grpc.py,sha256=HuGbhOwV_A5GTbvmd5XTm6lSm9fWUgKcxq9OKhgmBT0,12999
226
- flwr/proto/simulationio_pb2_grpc.pyi,sha256=KaGjpR6nH9XLM6oAPjgX-HrGbJU0dAnk9GpNjMkiR54,3687
223
+ flwr/proto/simulationio_pb2.py,sha256=9XIMVuYUP5GsRh2ppp6mWw-IF50TY1xt6MWGR0xveMs,2733
224
+ flwr/proto/simulationio_pb2.pyi,sha256=XbFvpZvvrS7QcH5AFXfpRGl4hQvhd3QdKO6x0oTlCCU,165
225
+ flwr/proto/simulationio_pb2_grpc.py,sha256=QJqP8njZpWbcG_vpbDI9wYEuULHPRcaEMiOxmshzgXU,17562
226
+ flwr/proto/simulationio_pb2_grpc.pyi,sha256=l9GDFkqrrHIEt3I5Eg1ZeMvEP3faN8Qcyh1pQe91DJA,4807
227
227
  flwr/proto/transport_pb2.py,sha256=P-jX_tUyk_8xFe-vIUUSfZlHGtk2Ou3A8eXdBKkp5AY,9824
228
228
  flwr/proto/transport_pb2.pyi,sha256=ipHQ03eFBqsxtAuAVefZ2lVr04BZ4YifJCS2eauNmy8,21627
229
229
  flwr/proto/transport_pb2_grpc.py,sha256=vLN3EHtx2aEEMCO4f1Upu-l27BPzd3-5pV-u8wPcosk,2598
230
230
  flwr/proto/transport_pb2_grpc.pyi,sha256=AGXf8RiIiW2J5IKMlm_3qT3AzcDa4F3P5IqUjve_esA,766
231
231
  flwr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
232
232
  flwr/server/__init__.py,sha256=LQQHiuL2jy7TpNaKastRdGsexlxSt5ZWAQNVqitDnrY,1598
233
- flwr/server/app.py,sha256=_STQz3zw9EBb5oMBuGUQ7bK-euoHRt2NJ6FwYUb8-Kk,30902
233
+ flwr/server/app.py,sha256=E7HFs_IB0y1haNFcuGy6dOqQ00zmW63O7pDbl8hfyLU,28639
234
234
  flwr/server/client_manager.py,sha256=5jCGavVli7XdupvWWo7ru3PdFTlRU8IGvHFSSoUVLRs,6227
235
235
  flwr/server/client_proxy.py,sha256=sv0E9AldBYOvc3pusqFh-GnyreeMfsXQ1cuTtxTq_wY,2399
236
236
  flwr/server/compat/__init__.py,sha256=0IsttWvY15qO98_1GyzVC-vR1e_ZPXOdu2qUlOkYMPE,886
@@ -250,7 +250,7 @@ flwr/server/server.py,sha256=39m4FSN2T-uVA-no9nstN0eWW0co-IUUAIMmpd3V7Jc,17893
250
250
  flwr/server/server_app.py,sha256=8uagoZX-3CY3tazPqkIV9jY-cN0YrRRrDmVe23o0AV0,9515
251
251
  flwr/server/server_config.py,sha256=e_6ddh0riwOJsdNn2BFev344uMWfDk9n7dyjNpPgm1w,1349
252
252
  flwr/server/serverapp/__init__.py,sha256=xcC0T_MQSMS9cicUzUUpMNCOsF2d8Oh_8jvnoBLuZvo,800
253
- flwr/server/serverapp/app.py,sha256=-IZY02Ym6AREMPaLld6WCD9mjDz7wsqfYgLLJxpy2pg,10433
253
+ flwr/server/serverapp/app.py,sha256=9S0B4yEuL1QFbPR7RQvn1N7BVu9t7jFhgNpIfXuRvGg,10067
254
254
  flwr/server/serverapp_components.py,sha256=dfSqmrsVy3arKXpl3ZIBQWdV8rehfIms8aJooyzdmEM,2118
255
255
  flwr/server/strategy/__init__.py,sha256=HhsSWMWaC7oCb2g7Kqn1MBKdrfvgi8VxACy9ZL706Q0,2836
256
256
  flwr/server/strategy/aggregate.py,sha256=smlKKy-uFUuuFR12vlclucnwSQWRz78R79-Km4RWqbw,13978
@@ -308,7 +308,7 @@ flwr/server/superlink/serverappio/serverappio_grpc.py,sha256=zcvzDhCAnlFxAwCiJUH
308
308
  flwr/server/superlink/serverappio/serverappio_servicer.py,sha256=3C_0boRbYuY1Vlf0DRGzBvTUX-D5UUzxYkFihSMZf-A,20094
309
309
  flwr/server/superlink/simulation/__init__.py,sha256=Ry8DrNaZCMcQXvUc4FoCN2m3dvUQgWjasfp015o3Ec4,718
310
310
  flwr/server/superlink/simulation/simulationio_grpc.py,sha256=VqWKxjpd4bCgPFKsgtIZPk9YcG0kc1EEmr5k20EKty4,2205
311
- flwr/server/superlink/simulation/simulationio_servicer.py,sha256=m1T1zvEn81jlfx9hVTqmeWxAu6APCS2YW8l5O0OQvhU,7724
311
+ flwr/server/superlink/simulation/simulationio_servicer.py,sha256=aZp67AeNCGs1zI4mvj_WUOL8noxNcsYu_QIpYKhnHXg,9992
312
312
  flwr/server/superlink/utils.py,sha256=zXmyU2o535b9dgz-TvFklzfuQk4irNnMtiK8vT4Dm1c,2454
313
313
  flwr/server/typing.py,sha256=LvO6gq7H6TAWhA9JFx0WyqHxU7FycyvhSsLjBLPgpts,1011
314
314
  flwr/server/utils/__init__.py,sha256=U4gM84-uUFddarODDQkO6SjNUuGhFcsHJZMjSEbezkU,884
@@ -322,7 +322,7 @@ flwr/server/workflow/secure_aggregation/secagg_workflow.py,sha256=b_pKk7gmbahwyj
322
322
  flwr/server/workflow/secure_aggregation/secaggplus_workflow.py,sha256=DkayCsnlAya6Y2PZsueLgoUCMRtV-GbnW08RfWx_SXM,29460
323
323
  flwr/serverapp/__init__.py,sha256=HPvC_ZvMS7GCM7ALVrG_Wwm4bSDr4DZETeC561v3T9w,719
324
324
  flwr/simulation/__init__.py,sha256=Gg6OsP1Z-ixc3-xxzvl7j7rz2Fijy9rzyEPpxgAQCeM,1556
325
- flwr/simulation/app.py,sha256=hP7vP29qZOA_3dipzUdmkRXP25eS8RlF1MT-S09t3oc,10625
325
+ flwr/simulation/app.py,sha256=et3bEZT4dstgZY9N4xKpftoDaKTbc39JjN3AKvaLn-s,11107
326
326
  flwr/simulation/legacy_app.py,sha256=nMISQqW0otJL1-2Kfd94O6BLlGS2IEmEPKTM2WGKrIs,15861
327
327
  flwr/simulation/ray_transport/__init__.py,sha256=ogd-0AMv2U-wBZ1r3sHWaDIOIrVqr88Xi6C8o4Dviy0,734
328
328
  flwr/simulation/ray_transport/ray_actor.py,sha256=JN3xTqFIr5Z750k92CcA_uavzOHhSWDwE2WCaecvpks,19147
@@ -331,7 +331,7 @@ flwr/simulation/ray_transport/utils.py,sha256=KrexpWYCF-dAF3UHc9yDbPQWO-ahMT-BbD
331
331
  flwr/simulation/run_simulation.py,sha256=-sp3dNZcp7MCAH0BlmZpVcFAGvozRdYXRdDYcH_2Zxk,20838
332
332
  flwr/simulation/simulationio_connection.py,sha256=mzS1C6EEREwQDPceDo30anAasmTDLb9qqV2tXlBhOUA,3494
333
333
  flwr/supercore/__init__.py,sha256=pqkFoow_E6UhbBlhmoD1gmTH-33yJRhBsIZqxRPFZ7U,755
334
- flwr/supercore/app_utils.py,sha256=WS3tly_QIWE-NRogbtFVC5l6arxP3Md1XItI9idmt0M,2771
334
+ flwr/supercore/app_utils.py,sha256=ogT14HdSIxToJoOgVngdKJ3nf7eY4b2wSjzkPT--kX0,2857
335
335
  flwr/supercore/cli/__init__.py,sha256=EDl2aO-fuQfxSbL-T1W9RAfA2N0hpWHmqX_GSwblJbQ,845
336
336
  flwr/supercore/cli/flower_superexec.py,sha256=J_rf7SCVW9L9wsBScOYa-oJOpyb_e1WOtwTGSyUFu1k,3882
337
337
  flwr/supercore/corestate/__init__.py,sha256=Vau6-L_JG5QzNqtCTa9xCKGGljc09wY8avZmIjSJemg,774
@@ -377,7 +377,7 @@ flwr/superlink/servicer/control/control_user_auth_interceptor.py,sha256=9Aqhrt_U
377
377
  flwr/supernode/__init__.py,sha256=KgeCaVvXWrU3rptNR1y0oBp4YtXbAcrnCcJAiOoWkI4,707
378
378
  flwr/supernode/cli/__init__.py,sha256=JuEMr0-s9zv-PEWKuLB9tj1ocNfroSyNJ-oyv7ati9A,887
379
379
  flwr/supernode/cli/flower_supernode.py,sha256=fAkk9zGhoP8Sv05EkdXRiCtirTAzWkSZBqRoaDdgflk,8529
380
- flwr/supernode/cli/flwr_clientapp.py,sha256=zaro6BoUEmfKIPQYuyJ9oR4rrHSS3bufhEqxcTo5VZU,3153
380
+ flwr/supernode/cli/flwr_clientapp.py,sha256=jxjR6etQRCHzG3zL04kyTZzicMMYdZ9dMiKdrW1uXs4,2759
381
381
  flwr/supernode/nodestate/__init__.py,sha256=CyLLObbmmVgfRO88UCM0VMait1dL57mUauUDfuSHsbU,976
382
382
  flwr/supernode/nodestate/in_memory_nodestate.py,sha256=rr_tg7YXhf_seYFipSB59TAfheKPratx3rrvHUOJ80g,7343
383
383
  flwr/supernode/nodestate/nodestate.py,sha256=jCOewZyctecMxsM0-_-pQwef9P3O5QjnKCgCGyx2PK4,5047
@@ -387,8 +387,8 @@ flwr/supernode/runtime/run_clientapp.py,sha256=vAeBTgIi4SmV4IRq1dSjXaxrFUPEeHg-n
387
387
  flwr/supernode/servicer/__init__.py,sha256=lucTzre5WPK7G1YLCfaqg3rbFWdNSb7ZTt-ca8gxdEo,717
388
388
  flwr/supernode/servicer/clientappio/__init__.py,sha256=7Oy62Y_oijqF7Dxi6tpcUQyOpLc_QpIRZ83NvwmB0Yg,813
389
389
  flwr/supernode/servicer/clientappio/clientappio_servicer.py,sha256=nIHRu38EWK-rpNOkcgBRAAKwYQQWFeCwu0lkO7OPZGQ,10239
390
- flwr/supernode/start_client_internal.py,sha256=iqJR8WbCW-8RQIRNwARZYoxhnlaAo5KnluCOEfRoLWM,21020
391
- flwr_nightly-1.21.0.dev20250811.dist-info/METADATA,sha256=oiloqTG58PFA094Vp2CmxyqFyCdrQTN74woSpLvclHA,15966
392
- flwr_nightly-1.21.0.dev20250811.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
393
- flwr_nightly-1.21.0.dev20250811.dist-info/entry_points.txt,sha256=hxHD2ixb_vJFDOlZV-zB4Ao32_BQlL34ftsDh1GXv14,420
394
- flwr_nightly-1.21.0.dev20250811.dist-info/RECORD,,
390
+ flwr/supernode/start_client_internal.py,sha256=z2o92MQKzTRB-AZTELROueZ2ZQYouu947hiU-WJ_oq4,20257
391
+ flwr_nightly-1.21.0.dev20250813.dist-info/METADATA,sha256=0aRgIClEyMHvfwiaW40CrEwvVvE7hnUcjZ0XZ5zet7k,15967
392
+ flwr_nightly-1.21.0.dev20250813.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
393
+ flwr_nightly-1.21.0.dev20250813.dist-info/entry_points.txt,sha256=hxHD2ixb_vJFDOlZV-zB4Ao32_BQlL34ftsDh1GXv14,420
394
+ flwr_nightly-1.21.0.dev20250813.dist-info/RECORD,,