flwr-nightly 1.14.0.dev20241216__py3-none-any.whl → 1.15.0.dev20250112__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. flwr/cli/cli_user_auth_interceptor.py +6 -2
  2. flwr/cli/log.py +8 -6
  3. flwr/cli/login/login.py +11 -4
  4. flwr/cli/ls.py +7 -4
  5. flwr/cli/new/templates/app/.gitignore.tpl +3 -0
  6. flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +1 -1
  7. flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +1 -1
  8. flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +1 -1
  9. flwr/cli/new/templates/app/pyproject.jax.toml.tpl +1 -1
  10. flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +1 -1
  11. flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +1 -1
  12. flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +3 -3
  13. flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +1 -1
  14. flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +1 -1
  15. flwr/cli/run/run.py +7 -2
  16. flwr/cli/stop.py +3 -2
  17. flwr/cli/utils.py +83 -14
  18. flwr/client/app.py +17 -9
  19. flwr/client/client.py +0 -32
  20. flwr/client/grpc_rere_client/client_interceptor.py +6 -0
  21. flwr/client/grpc_rere_client/grpc_adapter.py +16 -0
  22. flwr/client/message_handler/message_handler.py +0 -2
  23. flwr/client/numpy_client.py +0 -44
  24. flwr/client/supernode/app.py +1 -2
  25. flwr/common/auth_plugin/auth_plugin.py +33 -23
  26. flwr/common/constant.py +2 -0
  27. flwr/common/grpc.py +154 -3
  28. flwr/common/record/recordset.py +1 -1
  29. flwr/common/secure_aggregation/crypto/symmetric_encryption.py +45 -0
  30. flwr/common/telemetry.py +13 -3
  31. flwr/common/typing.py +20 -0
  32. flwr/proto/exec_pb2.py +12 -24
  33. flwr/proto/exec_pb2.pyi +27 -54
  34. flwr/proto/fleet_pb2.py +40 -27
  35. flwr/proto/fleet_pb2.pyi +84 -0
  36. flwr/proto/fleet_pb2_grpc.py +66 -0
  37. flwr/proto/fleet_pb2_grpc.pyi +20 -0
  38. flwr/server/app.py +54 -33
  39. flwr/server/run_serverapp.py +8 -9
  40. flwr/server/serverapp/app.py +17 -2
  41. flwr/server/superlink/driver/serverappio_grpc.py +1 -1
  42. flwr/server/superlink/driver/serverappio_servicer.py +29 -6
  43. flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +2 -165
  44. flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +16 -0
  45. flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +2 -1
  46. flwr/server/superlink/fleet/vce/vce_api.py +2 -2
  47. flwr/server/superlink/linkstate/in_memory_linkstate.py +36 -24
  48. flwr/server/superlink/linkstate/linkstate.py +14 -4
  49. flwr/server/superlink/linkstate/sqlite_linkstate.py +56 -31
  50. flwr/server/superlink/simulation/simulationio_grpc.py +1 -1
  51. flwr/server/superlink/simulation/simulationio_servicer.py +13 -0
  52. flwr/simulation/app.py +15 -4
  53. flwr/simulation/run_simulation.py +35 -7
  54. flwr/superexec/exec_grpc.py +1 -1
  55. flwr/superexec/exec_servicer.py +23 -2
  56. {flwr_nightly-1.14.0.dev20241216.dist-info → flwr_nightly-1.15.0.dev20250112.dist-info}/METADATA +5 -5
  57. {flwr_nightly-1.14.0.dev20241216.dist-info → flwr_nightly-1.15.0.dev20250112.dist-info}/RECORD +60 -60
  58. {flwr_nightly-1.14.0.dev20241216.dist-info → flwr_nightly-1.15.0.dev20250112.dist-info}/LICENSE +0 -0
  59. {flwr_nightly-1.14.0.dev20241216.dist-info → flwr_nightly-1.15.0.dev20250112.dist-info}/WHEEL +0 -0
  60. {flwr_nightly-1.14.0.dev20241216.dist-info → flwr_nightly-1.15.0.dev20250112.dist-info}/entry_points.txt +0 -0
flwr/proto/exec_pb2.pyi CHANGED
@@ -143,77 +143,50 @@ global___GetLoginDetailsRequest = GetLoginDetailsRequest
143
143
 
144
144
  class GetLoginDetailsResponse(google.protobuf.message.Message):
145
145
  DESCRIPTOR: google.protobuf.descriptor.Descriptor
146
- class LoginDetailsEntry(google.protobuf.message.Message):
147
- DESCRIPTOR: google.protobuf.descriptor.Descriptor
148
- KEY_FIELD_NUMBER: builtins.int
149
- VALUE_FIELD_NUMBER: builtins.int
150
- key: typing.Text
151
- value: typing.Text
152
- def __init__(self,
153
- *,
154
- key: typing.Text = ...,
155
- value: typing.Text = ...,
156
- ) -> None: ...
157
- def ClearField(self, field_name: typing_extensions.Literal["key",b"key","value",b"value"]) -> None: ...
158
-
159
- LOGIN_DETAILS_FIELD_NUMBER: builtins.int
160
- @property
161
- def login_details(self) -> google.protobuf.internal.containers.ScalarMap[typing.Text, typing.Text]: ...
146
+ AUTH_TYPE_FIELD_NUMBER: builtins.int
147
+ DEVICE_CODE_FIELD_NUMBER: builtins.int
148
+ VERIFICATION_URI_COMPLETE_FIELD_NUMBER: builtins.int
149
+ EXPIRES_IN_FIELD_NUMBER: builtins.int
150
+ INTERVAL_FIELD_NUMBER: builtins.int
151
+ auth_type: typing.Text
152
+ device_code: typing.Text
153
+ verification_uri_complete: typing.Text
154
+ expires_in: builtins.int
155
+ interval: builtins.int
162
156
  def __init__(self,
163
157
  *,
164
- login_details: typing.Optional[typing.Mapping[typing.Text, typing.Text]] = ...,
158
+ auth_type: typing.Text = ...,
159
+ device_code: typing.Text = ...,
160
+ verification_uri_complete: typing.Text = ...,
161
+ expires_in: builtins.int = ...,
162
+ interval: builtins.int = ...,
165
163
  ) -> None: ...
166
- def ClearField(self, field_name: typing_extensions.Literal["login_details",b"login_details"]) -> None: ...
164
+ def ClearField(self, field_name: typing_extensions.Literal["auth_type",b"auth_type","device_code",b"device_code","expires_in",b"expires_in","interval",b"interval","verification_uri_complete",b"verification_uri_complete"]) -> None: ...
167
165
  global___GetLoginDetailsResponse = GetLoginDetailsResponse
168
166
 
169
167
  class GetAuthTokensRequest(google.protobuf.message.Message):
170
168
  DESCRIPTOR: google.protobuf.descriptor.Descriptor
171
- class AuthDetailsEntry(google.protobuf.message.Message):
172
- DESCRIPTOR: google.protobuf.descriptor.Descriptor
173
- KEY_FIELD_NUMBER: builtins.int
174
- VALUE_FIELD_NUMBER: builtins.int
175
- key: typing.Text
176
- value: typing.Text
177
- def __init__(self,
178
- *,
179
- key: typing.Text = ...,
180
- value: typing.Text = ...,
181
- ) -> None: ...
182
- def ClearField(self, field_name: typing_extensions.Literal["key",b"key","value",b"value"]) -> None: ...
183
-
184
- AUTH_DETAILS_FIELD_NUMBER: builtins.int
185
- @property
186
- def auth_details(self) -> google.protobuf.internal.containers.ScalarMap[typing.Text, typing.Text]: ...
169
+ DEVICE_CODE_FIELD_NUMBER: builtins.int
170
+ device_code: typing.Text
187
171
  def __init__(self,
188
172
  *,
189
- auth_details: typing.Optional[typing.Mapping[typing.Text, typing.Text]] = ...,
173
+ device_code: typing.Text = ...,
190
174
  ) -> None: ...
191
- def ClearField(self, field_name: typing_extensions.Literal["auth_details",b"auth_details"]) -> None: ...
175
+ def ClearField(self, field_name: typing_extensions.Literal["device_code",b"device_code"]) -> None: ...
192
176
  global___GetAuthTokensRequest = GetAuthTokensRequest
193
177
 
194
178
  class GetAuthTokensResponse(google.protobuf.message.Message):
195
179
  DESCRIPTOR: google.protobuf.descriptor.Descriptor
196
- class AuthTokensEntry(google.protobuf.message.Message):
197
- DESCRIPTOR: google.protobuf.descriptor.Descriptor
198
- KEY_FIELD_NUMBER: builtins.int
199
- VALUE_FIELD_NUMBER: builtins.int
200
- key: typing.Text
201
- value: typing.Text
202
- def __init__(self,
203
- *,
204
- key: typing.Text = ...,
205
- value: typing.Text = ...,
206
- ) -> None: ...
207
- def ClearField(self, field_name: typing_extensions.Literal["key",b"key","value",b"value"]) -> None: ...
208
-
209
- AUTH_TOKENS_FIELD_NUMBER: builtins.int
210
- @property
211
- def auth_tokens(self) -> google.protobuf.internal.containers.ScalarMap[typing.Text, typing.Text]: ...
180
+ ACCESS_TOKEN_FIELD_NUMBER: builtins.int
181
+ REFRESH_TOKEN_FIELD_NUMBER: builtins.int
182
+ access_token: typing.Text
183
+ refresh_token: typing.Text
212
184
  def __init__(self,
213
185
  *,
214
- auth_tokens: typing.Optional[typing.Mapping[typing.Text, typing.Text]] = ...,
186
+ access_token: typing.Text = ...,
187
+ refresh_token: typing.Text = ...,
215
188
  ) -> None: ...
216
- def ClearField(self, field_name: typing_extensions.Literal["auth_tokens",b"auth_tokens"]) -> None: ...
189
+ def ClearField(self, field_name: typing_extensions.Literal["access_token",b"access_token","refresh_token",b"refresh_token"]) -> None: ...
217
190
  global___GetAuthTokensResponse = GetAuthTokensResponse
218
191
 
219
192
  class StopRunRequest(google.protobuf.message.Message):
flwr/proto/fleet_pb2.py CHANGED
@@ -16,9 +16,10 @@ from flwr.proto import node_pb2 as flwr_dot_proto_dot_node__pb2
16
16
  from flwr.proto import task_pb2 as flwr_dot_proto_dot_task__pb2
17
17
  from flwr.proto import run_pb2 as flwr_dot_proto_dot_run__pb2
18
18
  from flwr.proto import fab_pb2 as flwr_dot_proto_dot_fab__pb2
19
+ from flwr.proto import message_pb2 as flwr_dot_proto_dot_message__pb2
19
20
 
20
21
 
21
- DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x66lwr/proto/fleet.proto\x12\nflwr.proto\x1a\x15\x66lwr/proto/node.proto\x1a\x15\x66lwr/proto/task.proto\x1a\x14\x66lwr/proto/run.proto\x1a\x14\x66lwr/proto/fab.proto\"*\n\x11\x43reateNodeRequest\x12\x15\n\rping_interval\x18\x01 \x01(\x01\"4\n\x12\x43reateNodeResponse\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\"3\n\x11\x44\x65leteNodeRequest\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\"\x14\n\x12\x44\x65leteNodeResponse\"D\n\x0bPingRequest\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\x12\x15\n\rping_interval\x18\x02 \x01(\x01\"\x1f\n\x0cPingResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\"F\n\x12PullTaskInsRequest\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\x12\x10\n\x08task_ids\x18\x02 \x03(\t\"k\n\x13PullTaskInsResponse\x12(\n\treconnect\x18\x01 \x01(\x0b\x32\x15.flwr.proto.Reconnect\x12*\n\rtask_ins_list\x18\x02 \x03(\x0b\x32\x13.flwr.proto.TaskIns\"`\n\x12PushTaskResRequest\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\x12*\n\rtask_res_list\x18\x02 \x03(\x0b\x32\x13.flwr.proto.TaskRes\"\xae\x01\n\x13PushTaskResResponse\x12(\n\treconnect\x18\x01 \x01(\x0b\x32\x15.flwr.proto.Reconnect\x12=\n\x07results\x18\x02 \x03(\x0b\x32,.flwr.proto.PushTaskResResponse.ResultsEntry\x1a.\n\x0cResultsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\r:\x02\x38\x01\"\x1e\n\tReconnect\x12\x11\n\treconnect\x18\x01 \x01(\x04\x32\x8c\x04\n\x05\x46leet\x12M\n\nCreateNode\x12\x1d.flwr.proto.CreateNodeRequest\x1a\x1e.flwr.proto.CreateNodeResponse\"\x00\x12M\n\nDeleteNode\x12\x1d.flwr.proto.DeleteNodeRequest\x1a\x1e.flwr.proto.DeleteNodeResponse\"\x00\x12;\n\x04Ping\x12\x17.flwr.proto.PingRequest\x1a\x18.flwr.proto.PingResponse\"\x00\x12P\n\x0bPullTaskIns\x12\x1e.flwr.proto.PullTaskInsRequest\x1a\x1f.flwr.proto.PullTaskInsResponse\"\x00\x12P\n\x0bPushTaskRes\x12\x1e.flwr.proto.PushTaskResRequest\x1a\x1f.flwr.proto.PushTaskResResponse\"\x00\x12\x41\n\x06GetRun\x12\x19.flwr.proto.GetRunRequest\x1a\x1a.flwr.proto.GetRunResponse\"\x00\x12\x41\n\x06GetFab\x12\x19.flwr.proto.GetFabRequest\x1a\x1a.flwr.proto.GetFabResponse\"\x00\x62\x06proto3')
22
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x66lwr/proto/fleet.proto\x12\nflwr.proto\x1a\x15\x66lwr/proto/node.proto\x1a\x15\x66lwr/proto/task.proto\x1a\x14\x66lwr/proto/run.proto\x1a\x14\x66lwr/proto/fab.proto\x1a\x18\x66lwr/proto/message.proto\"*\n\x11\x43reateNodeRequest\x12\x15\n\rping_interval\x18\x01 \x01(\x01\"4\n\x12\x43reateNodeResponse\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\"3\n\x11\x44\x65leteNodeRequest\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\"\x14\n\x12\x44\x65leteNodeResponse\"D\n\x0bPingRequest\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\x12\x15\n\rping_interval\x18\x02 \x01(\x01\"\x1f\n\x0cPingResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\"F\n\x12PullTaskInsRequest\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\x12\x10\n\x08task_ids\x18\x02 \x03(\t\"k\n\x13PullTaskInsResponse\x12(\n\treconnect\x18\x01 \x01(\x0b\x32\x15.flwr.proto.Reconnect\x12*\n\rtask_ins_list\x18\x02 \x03(\x0b\x32\x13.flwr.proto.TaskIns\"`\n\x12PushTaskResRequest\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\x12*\n\rtask_res_list\x18\x02 \x03(\x0b\x32\x13.flwr.proto.TaskRes\"\xae\x01\n\x13PushTaskResResponse\x12(\n\treconnect\x18\x01 \x01(\x0b\x32\x15.flwr.proto.Reconnect\x12=\n\x07results\x18\x02 \x03(\x0b\x32,.flwr.proto.PushTaskResResponse.ResultsEntry\x1a.\n\x0cResultsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\r:\x02\x38\x01\"J\n\x13PullMessagesRequest\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\x12\x13\n\x0bmessage_ids\x18\x02 \x03(\t\"l\n\x14PullMessagesResponse\x12(\n\treconnect\x18\x01 \x01(\x0b\x32\x15.flwr.proto.Reconnect\x12*\n\rmessages_list\x18\x02 \x03(\x0b\x32\x13.flwr.proto.Message\"a\n\x13PushMessagesRequest\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\x12*\n\rmessages_list\x18\x02 \x03(\x0b\x32\x13.flwr.proto.Message\"\xb0\x01\n\x14PushMessagesResponse\x12(\n\treconnect\x18\x01 \x01(\x0b\x32\x15.flwr.proto.Reconnect\x12>\n\x07results\x18\x02 \x03(\x0b\x32-.flwr.proto.PushMessagesResponse.ResultsEntry\x1a.\n\x0cResultsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\r:\x02\x38\x01\"\x1e\n\tReconnect\x12\x11\n\treconnect\x18\x01 \x01(\x04\x32\xb6\x05\n\x05\x46leet\x12M\n\nCreateNode\x12\x1d.flwr.proto.CreateNodeRequest\x1a\x1e.flwr.proto.CreateNodeResponse\"\x00\x12M\n\nDeleteNode\x12\x1d.flwr.proto.DeleteNodeRequest\x1a\x1e.flwr.proto.DeleteNodeResponse\"\x00\x12;\n\x04Ping\x12\x17.flwr.proto.PingRequest\x1a\x18.flwr.proto.PingResponse\"\x00\x12P\n\x0bPullTaskIns\x12\x1e.flwr.proto.PullTaskInsRequest\x1a\x1f.flwr.proto.PullTaskInsResponse\"\x00\x12S\n\x0cPullMessages\x12\x1f.flwr.proto.PullMessagesRequest\x1a .flwr.proto.PullMessagesResponse\"\x00\x12P\n\x0bPushTaskRes\x12\x1e.flwr.proto.PushTaskResRequest\x1a\x1f.flwr.proto.PushTaskResResponse\"\x00\x12S\n\x0cPushMessages\x12\x1f.flwr.proto.PushMessagesRequest\x1a .flwr.proto.PushMessagesResponse\"\x00\x12\x41\n\x06GetRun\x12\x19.flwr.proto.GetRunRequest\x1a\x1a.flwr.proto.GetRunResponse\"\x00\x12\x41\n\x06GetFab\x12\x19.flwr.proto.GetFabRequest\x1a\x1a.flwr.proto.GetFabResponse\"\x00\x62\x06proto3')
22
23
 
23
24
  _globals = globals()
24
25
  _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
@@ -27,30 +28,42 @@ if _descriptor._USE_C_DESCRIPTORS == False:
27
28
  DESCRIPTOR._options = None
28
29
  _globals['_PUSHTASKRESRESPONSE_RESULTSENTRY']._options = None
29
30
  _globals['_PUSHTASKRESRESPONSE_RESULTSENTRY']._serialized_options = b'8\001'
30
- _globals['_CREATENODEREQUEST']._serialized_start=128
31
- _globals['_CREATENODEREQUEST']._serialized_end=170
32
- _globals['_CREATENODERESPONSE']._serialized_start=172
33
- _globals['_CREATENODERESPONSE']._serialized_end=224
34
- _globals['_DELETENODEREQUEST']._serialized_start=226
35
- _globals['_DELETENODEREQUEST']._serialized_end=277
36
- _globals['_DELETENODERESPONSE']._serialized_start=279
37
- _globals['_DELETENODERESPONSE']._serialized_end=299
38
- _globals['_PINGREQUEST']._serialized_start=301
39
- _globals['_PINGREQUEST']._serialized_end=369
40
- _globals['_PINGRESPONSE']._serialized_start=371
41
- _globals['_PINGRESPONSE']._serialized_end=402
42
- _globals['_PULLTASKINSREQUEST']._serialized_start=404
43
- _globals['_PULLTASKINSREQUEST']._serialized_end=474
44
- _globals['_PULLTASKINSRESPONSE']._serialized_start=476
45
- _globals['_PULLTASKINSRESPONSE']._serialized_end=583
46
- _globals['_PUSHTASKRESREQUEST']._serialized_start=585
47
- _globals['_PUSHTASKRESREQUEST']._serialized_end=681
48
- _globals['_PUSHTASKRESRESPONSE']._serialized_start=684
49
- _globals['_PUSHTASKRESRESPONSE']._serialized_end=858
50
- _globals['_PUSHTASKRESRESPONSE_RESULTSENTRY']._serialized_start=812
51
- _globals['_PUSHTASKRESRESPONSE_RESULTSENTRY']._serialized_end=858
52
- _globals['_RECONNECT']._serialized_start=860
53
- _globals['_RECONNECT']._serialized_end=890
54
- _globals['_FLEET']._serialized_start=893
55
- _globals['_FLEET']._serialized_end=1417
31
+ _globals['_PUSHMESSAGESRESPONSE_RESULTSENTRY']._options = None
32
+ _globals['_PUSHMESSAGESRESPONSE_RESULTSENTRY']._serialized_options = b'8\001'
33
+ _globals['_CREATENODEREQUEST']._serialized_start=154
34
+ _globals['_CREATENODEREQUEST']._serialized_end=196
35
+ _globals['_CREATENODERESPONSE']._serialized_start=198
36
+ _globals['_CREATENODERESPONSE']._serialized_end=250
37
+ _globals['_DELETENODEREQUEST']._serialized_start=252
38
+ _globals['_DELETENODEREQUEST']._serialized_end=303
39
+ _globals['_DELETENODERESPONSE']._serialized_start=305
40
+ _globals['_DELETENODERESPONSE']._serialized_end=325
41
+ _globals['_PINGREQUEST']._serialized_start=327
42
+ _globals['_PINGREQUEST']._serialized_end=395
43
+ _globals['_PINGRESPONSE']._serialized_start=397
44
+ _globals['_PINGRESPONSE']._serialized_end=428
45
+ _globals['_PULLTASKINSREQUEST']._serialized_start=430
46
+ _globals['_PULLTASKINSREQUEST']._serialized_end=500
47
+ _globals['_PULLTASKINSRESPONSE']._serialized_start=502
48
+ _globals['_PULLTASKINSRESPONSE']._serialized_end=609
49
+ _globals['_PUSHTASKRESREQUEST']._serialized_start=611
50
+ _globals['_PUSHTASKRESREQUEST']._serialized_end=707
51
+ _globals['_PUSHTASKRESRESPONSE']._serialized_start=710
52
+ _globals['_PUSHTASKRESRESPONSE']._serialized_end=884
53
+ _globals['_PUSHTASKRESRESPONSE_RESULTSENTRY']._serialized_start=838
54
+ _globals['_PUSHTASKRESRESPONSE_RESULTSENTRY']._serialized_end=884
55
+ _globals['_PULLMESSAGESREQUEST']._serialized_start=886
56
+ _globals['_PULLMESSAGESREQUEST']._serialized_end=960
57
+ _globals['_PULLMESSAGESRESPONSE']._serialized_start=962
58
+ _globals['_PULLMESSAGESRESPONSE']._serialized_end=1070
59
+ _globals['_PUSHMESSAGESREQUEST']._serialized_start=1072
60
+ _globals['_PUSHMESSAGESREQUEST']._serialized_end=1169
61
+ _globals['_PUSHMESSAGESRESPONSE']._serialized_start=1172
62
+ _globals['_PUSHMESSAGESRESPONSE']._serialized_end=1348
63
+ _globals['_PUSHMESSAGESRESPONSE_RESULTSENTRY']._serialized_start=838
64
+ _globals['_PUSHMESSAGESRESPONSE_RESULTSENTRY']._serialized_end=884
65
+ _globals['_RECONNECT']._serialized_start=1350
66
+ _globals['_RECONNECT']._serialized_end=1380
67
+ _globals['_FLEET']._serialized_start=1383
68
+ _globals['_FLEET']._serialized_end=2077
56
69
  # @@protoc_insertion_point(module_scope)
flwr/proto/fleet_pb2.pyi CHANGED
@@ -3,6 +3,7 @@
3
3
  isort:skip_file
4
4
  """
5
5
  import builtins
6
+ import flwr.proto.message_pb2
6
7
  import flwr.proto.node_pb2
7
8
  import flwr.proto.task_pb2
8
9
  import google.protobuf.descriptor
@@ -169,6 +170,89 @@ class PushTaskResResponse(google.protobuf.message.Message):
169
170
  def ClearField(self, field_name: typing_extensions.Literal["reconnect",b"reconnect","results",b"results"]) -> None: ...
170
171
  global___PushTaskResResponse = PushTaskResResponse
171
172
 
173
+ class PullMessagesRequest(google.protobuf.message.Message):
174
+ """PullMessages messages"""
175
+ DESCRIPTOR: google.protobuf.descriptor.Descriptor
176
+ NODE_FIELD_NUMBER: builtins.int
177
+ MESSAGE_IDS_FIELD_NUMBER: builtins.int
178
+ @property
179
+ def node(self) -> flwr.proto.node_pb2.Node: ...
180
+ @property
181
+ def message_ids(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[typing.Text]: ...
182
+ def __init__(self,
183
+ *,
184
+ node: typing.Optional[flwr.proto.node_pb2.Node] = ...,
185
+ message_ids: typing.Optional[typing.Iterable[typing.Text]] = ...,
186
+ ) -> None: ...
187
+ def HasField(self, field_name: typing_extensions.Literal["node",b"node"]) -> builtins.bool: ...
188
+ def ClearField(self, field_name: typing_extensions.Literal["message_ids",b"message_ids","node",b"node"]) -> None: ...
189
+ global___PullMessagesRequest = PullMessagesRequest
190
+
191
+ class PullMessagesResponse(google.protobuf.message.Message):
192
+ DESCRIPTOR: google.protobuf.descriptor.Descriptor
193
+ RECONNECT_FIELD_NUMBER: builtins.int
194
+ MESSAGES_LIST_FIELD_NUMBER: builtins.int
195
+ @property
196
+ def reconnect(self) -> global___Reconnect: ...
197
+ @property
198
+ def messages_list(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[flwr.proto.message_pb2.Message]: ...
199
+ def __init__(self,
200
+ *,
201
+ reconnect: typing.Optional[global___Reconnect] = ...,
202
+ messages_list: typing.Optional[typing.Iterable[flwr.proto.message_pb2.Message]] = ...,
203
+ ) -> None: ...
204
+ def HasField(self, field_name: typing_extensions.Literal["reconnect",b"reconnect"]) -> builtins.bool: ...
205
+ def ClearField(self, field_name: typing_extensions.Literal["messages_list",b"messages_list","reconnect",b"reconnect"]) -> None: ...
206
+ global___PullMessagesResponse = PullMessagesResponse
207
+
208
+ class PushMessagesRequest(google.protobuf.message.Message):
209
+ """PushMessages messages"""
210
+ DESCRIPTOR: google.protobuf.descriptor.Descriptor
211
+ NODE_FIELD_NUMBER: builtins.int
212
+ MESSAGES_LIST_FIELD_NUMBER: builtins.int
213
+ @property
214
+ def node(self) -> flwr.proto.node_pb2.Node: ...
215
+ @property
216
+ def messages_list(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[flwr.proto.message_pb2.Message]: ...
217
+ def __init__(self,
218
+ *,
219
+ node: typing.Optional[flwr.proto.node_pb2.Node] = ...,
220
+ messages_list: typing.Optional[typing.Iterable[flwr.proto.message_pb2.Message]] = ...,
221
+ ) -> None: ...
222
+ def HasField(self, field_name: typing_extensions.Literal["node",b"node"]) -> builtins.bool: ...
223
+ def ClearField(self, field_name: typing_extensions.Literal["messages_list",b"messages_list","node",b"node"]) -> None: ...
224
+ global___PushMessagesRequest = PushMessagesRequest
225
+
226
+ class PushMessagesResponse(google.protobuf.message.Message):
227
+ DESCRIPTOR: google.protobuf.descriptor.Descriptor
228
+ class ResultsEntry(google.protobuf.message.Message):
229
+ DESCRIPTOR: google.protobuf.descriptor.Descriptor
230
+ KEY_FIELD_NUMBER: builtins.int
231
+ VALUE_FIELD_NUMBER: builtins.int
232
+ key: typing.Text
233
+ value: builtins.int
234
+ def __init__(self,
235
+ *,
236
+ key: typing.Text = ...,
237
+ value: builtins.int = ...,
238
+ ) -> None: ...
239
+ def ClearField(self, field_name: typing_extensions.Literal["key",b"key","value",b"value"]) -> None: ...
240
+
241
+ RECONNECT_FIELD_NUMBER: builtins.int
242
+ RESULTS_FIELD_NUMBER: builtins.int
243
+ @property
244
+ def reconnect(self) -> global___Reconnect: ...
245
+ @property
246
+ def results(self) -> google.protobuf.internal.containers.ScalarMap[typing.Text, builtins.int]: ...
247
+ def __init__(self,
248
+ *,
249
+ reconnect: typing.Optional[global___Reconnect] = ...,
250
+ results: typing.Optional[typing.Mapping[typing.Text, builtins.int]] = ...,
251
+ ) -> None: ...
252
+ def HasField(self, field_name: typing_extensions.Literal["reconnect",b"reconnect"]) -> builtins.bool: ...
253
+ def ClearField(self, field_name: typing_extensions.Literal["reconnect",b"reconnect","results",b"results"]) -> None: ...
254
+ global___PushMessagesResponse = PushMessagesResponse
255
+
172
256
  class Reconnect(google.protobuf.message.Message):
173
257
  DESCRIPTOR: google.protobuf.descriptor.Descriptor
174
258
  RECONNECT_FIELD_NUMBER: builtins.int
@@ -36,11 +36,21 @@ class FleetStub(object):
36
36
  request_serializer=flwr_dot_proto_dot_fleet__pb2.PullTaskInsRequest.SerializeToString,
37
37
  response_deserializer=flwr_dot_proto_dot_fleet__pb2.PullTaskInsResponse.FromString,
38
38
  )
39
+ self.PullMessages = channel.unary_unary(
40
+ '/flwr.proto.Fleet/PullMessages',
41
+ request_serializer=flwr_dot_proto_dot_fleet__pb2.PullMessagesRequest.SerializeToString,
42
+ response_deserializer=flwr_dot_proto_dot_fleet__pb2.PullMessagesResponse.FromString,
43
+ )
39
44
  self.PushTaskRes = channel.unary_unary(
40
45
  '/flwr.proto.Fleet/PushTaskRes',
41
46
  request_serializer=flwr_dot_proto_dot_fleet__pb2.PushTaskResRequest.SerializeToString,
42
47
  response_deserializer=flwr_dot_proto_dot_fleet__pb2.PushTaskResResponse.FromString,
43
48
  )
49
+ self.PushMessages = channel.unary_unary(
50
+ '/flwr.proto.Fleet/PushMessages',
51
+ request_serializer=flwr_dot_proto_dot_fleet__pb2.PushMessagesRequest.SerializeToString,
52
+ response_deserializer=flwr_dot_proto_dot_fleet__pb2.PushMessagesResponse.FromString,
53
+ )
44
54
  self.GetRun = channel.unary_unary(
45
55
  '/flwr.proto.Fleet/GetRun',
46
56
  request_serializer=flwr_dot_proto_dot_run__pb2.GetRunRequest.SerializeToString,
@@ -83,6 +93,12 @@ class FleetServicer(object):
83
93
  context.set_details('Method not implemented!')
84
94
  raise NotImplementedError('Method not implemented!')
85
95
 
96
+ def PullMessages(self, request, context):
97
+ """Missing associated documentation comment in .proto file."""
98
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
99
+ context.set_details('Method not implemented!')
100
+ raise NotImplementedError('Method not implemented!')
101
+
86
102
  def PushTaskRes(self, request, context):
87
103
  """Complete one or more tasks, if possible
88
104
 
@@ -92,6 +108,12 @@ class FleetServicer(object):
92
108
  context.set_details('Method not implemented!')
93
109
  raise NotImplementedError('Method not implemented!')
94
110
 
111
+ def PushMessages(self, request, context):
112
+ """Missing associated documentation comment in .proto file."""
113
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
114
+ context.set_details('Method not implemented!')
115
+ raise NotImplementedError('Method not implemented!')
116
+
95
117
  def GetRun(self, request, context):
96
118
  """Missing associated documentation comment in .proto file."""
97
119
  context.set_code(grpc.StatusCode.UNIMPLEMENTED)
@@ -128,11 +150,21 @@ def add_FleetServicer_to_server(servicer, server):
128
150
  request_deserializer=flwr_dot_proto_dot_fleet__pb2.PullTaskInsRequest.FromString,
129
151
  response_serializer=flwr_dot_proto_dot_fleet__pb2.PullTaskInsResponse.SerializeToString,
130
152
  ),
153
+ 'PullMessages': grpc.unary_unary_rpc_method_handler(
154
+ servicer.PullMessages,
155
+ request_deserializer=flwr_dot_proto_dot_fleet__pb2.PullMessagesRequest.FromString,
156
+ response_serializer=flwr_dot_proto_dot_fleet__pb2.PullMessagesResponse.SerializeToString,
157
+ ),
131
158
  'PushTaskRes': grpc.unary_unary_rpc_method_handler(
132
159
  servicer.PushTaskRes,
133
160
  request_deserializer=flwr_dot_proto_dot_fleet__pb2.PushTaskResRequest.FromString,
134
161
  response_serializer=flwr_dot_proto_dot_fleet__pb2.PushTaskResResponse.SerializeToString,
135
162
  ),
163
+ 'PushMessages': grpc.unary_unary_rpc_method_handler(
164
+ servicer.PushMessages,
165
+ request_deserializer=flwr_dot_proto_dot_fleet__pb2.PushMessagesRequest.FromString,
166
+ response_serializer=flwr_dot_proto_dot_fleet__pb2.PushMessagesResponse.SerializeToString,
167
+ ),
136
168
  'GetRun': grpc.unary_unary_rpc_method_handler(
137
169
  servicer.GetRun,
138
170
  request_deserializer=flwr_dot_proto_dot_run__pb2.GetRunRequest.FromString,
@@ -221,6 +253,23 @@ class Fleet(object):
221
253
  options, channel_credentials,
222
254
  insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
223
255
 
256
+ @staticmethod
257
+ def PullMessages(request,
258
+ target,
259
+ options=(),
260
+ channel_credentials=None,
261
+ call_credentials=None,
262
+ insecure=False,
263
+ compression=None,
264
+ wait_for_ready=None,
265
+ timeout=None,
266
+ metadata=None):
267
+ return grpc.experimental.unary_unary(request, target, '/flwr.proto.Fleet/PullMessages',
268
+ flwr_dot_proto_dot_fleet__pb2.PullMessagesRequest.SerializeToString,
269
+ flwr_dot_proto_dot_fleet__pb2.PullMessagesResponse.FromString,
270
+ options, channel_credentials,
271
+ insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
272
+
224
273
  @staticmethod
225
274
  def PushTaskRes(request,
226
275
  target,
@@ -238,6 +287,23 @@ class Fleet(object):
238
287
  options, channel_credentials,
239
288
  insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
240
289
 
290
+ @staticmethod
291
+ def PushMessages(request,
292
+ target,
293
+ options=(),
294
+ channel_credentials=None,
295
+ call_credentials=None,
296
+ insecure=False,
297
+ compression=None,
298
+ wait_for_ready=None,
299
+ timeout=None,
300
+ metadata=None):
301
+ return grpc.experimental.unary_unary(request, target, '/flwr.proto.Fleet/PushMessages',
302
+ flwr_dot_proto_dot_fleet__pb2.PushMessagesRequest.SerializeToString,
303
+ flwr_dot_proto_dot_fleet__pb2.PushMessagesResponse.FromString,
304
+ options, channel_credentials,
305
+ insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
306
+
241
307
  @staticmethod
242
308
  def GetRun(request,
243
309
  target,
@@ -30,6 +30,10 @@ class FleetStub:
30
30
  HTTP API path: /api/v1/fleet/pull-task-ins
31
31
  """
32
32
 
33
+ PullMessages: grpc.UnaryUnaryMultiCallable[
34
+ flwr.proto.fleet_pb2.PullMessagesRequest,
35
+ flwr.proto.fleet_pb2.PullMessagesResponse]
36
+
33
37
  PushTaskRes: grpc.UnaryUnaryMultiCallable[
34
38
  flwr.proto.fleet_pb2.PushTaskResRequest,
35
39
  flwr.proto.fleet_pb2.PushTaskResResponse]
@@ -38,6 +42,10 @@ class FleetStub:
38
42
  HTTP API path: /api/v1/fleet/push-task-res
39
43
  """
40
44
 
45
+ PushMessages: grpc.UnaryUnaryMultiCallable[
46
+ flwr.proto.fleet_pb2.PushMessagesRequest,
47
+ flwr.proto.fleet_pb2.PushMessagesResponse]
48
+
41
49
  GetRun: grpc.UnaryUnaryMultiCallable[
42
50
  flwr.proto.run_pb2.GetRunRequest,
43
51
  flwr.proto.run_pb2.GetRunResponse]
@@ -78,6 +86,12 @@ class FleetServicer(metaclass=abc.ABCMeta):
78
86
  """
79
87
  pass
80
88
 
89
+ @abc.abstractmethod
90
+ def PullMessages(self,
91
+ request: flwr.proto.fleet_pb2.PullMessagesRequest,
92
+ context: grpc.ServicerContext,
93
+ ) -> flwr.proto.fleet_pb2.PullMessagesResponse: ...
94
+
81
95
  @abc.abstractmethod
82
96
  def PushTaskRes(self,
83
97
  request: flwr.proto.fleet_pb2.PushTaskResRequest,
@@ -89,6 +103,12 @@ class FleetServicer(metaclass=abc.ABCMeta):
89
103
  """
90
104
  pass
91
105
 
106
+ @abc.abstractmethod
107
+ def PushMessages(self,
108
+ request: flwr.proto.fleet_pb2.PushMessagesRequest,
109
+ context: grpc.ServicerContext,
110
+ ) -> flwr.proto.fleet_pb2.PushMessagesResponse: ...
111
+
92
112
  @abc.abstractmethod
93
113
  def GetRun(self,
94
114
  request: flwr.proto.run_pb2.GetRunRequest,
flwr/server/app.py CHANGED
@@ -18,7 +18,8 @@
18
18
  import argparse
19
19
  import csv
20
20
  import importlib.util
21
- import subprocess
21
+ import multiprocessing
22
+ import multiprocessing.context
22
23
  import sys
23
24
  import threading
24
25
  from collections.abc import Sequence
@@ -59,6 +60,7 @@ from flwr.common.constant import (
59
60
  TRANSPORT_TYPE_REST,
60
61
  )
61
62
  from flwr.common.exit_handlers import register_exit_handlers
63
+ from flwr.common.grpc import generic_create_grpc_server
62
64
  from flwr.common.logger import log, warn_deprecated_feature
63
65
  from flwr.common.secure_aggregation.crypto.symmetric_encryption import (
64
66
  private_key_to_bytes,
@@ -68,6 +70,8 @@ from flwr.proto.fleet_pb2_grpc import ( # pylint: disable=E0611
68
70
  add_FleetServicer_to_server,
69
71
  )
70
72
  from flwr.proto.grpcadapter_pb2_grpc import add_GrpcAdapterServicer_to_server
73
+ from flwr.server.serverapp.app import flwr_serverapp
74
+ from flwr.simulation.app import flwr_simulation
71
75
  from flwr.superexec.app import load_executor
72
76
  from flwr.superexec.exec_grpc import run_exec_api_grpc
73
77
 
@@ -79,10 +83,7 @@ from .strategy import Strategy
79
83
  from .superlink.driver.serverappio_grpc import run_serverappio_api_grpc
80
84
  from .superlink.ffs.ffs_factory import FfsFactory
81
85
  from .superlink.fleet.grpc_adapter.grpc_adapter_servicer import GrpcAdapterServicer
82
- from .superlink.fleet.grpc_bidi.grpc_server import (
83
- generic_create_grpc_server,
84
- start_grpc_server,
85
- )
86
+ from .superlink.fleet.grpc_bidi.grpc_server import start_grpc_server
86
87
  from .superlink.fleet.grpc_rere.fleet_servicer import FleetServicer
87
88
  from .superlink.fleet.grpc_rere.server_interceptor import AuthenticateServerInterceptor
88
89
  from .superlink.linkstate import LinkStateFactory
@@ -263,11 +264,10 @@ def run_superlink() -> None:
263
264
  # Obtain certificates
264
265
  certificates = try_obtain_server_certificates(args, args.fleet_api_type)
265
266
 
266
- user_auth_config = _try_obtain_user_auth_config(args)
267
267
  auth_plugin: Optional[ExecAuthPlugin] = None
268
- # user_auth_config is None only if the args.user_auth_config is not provided
269
- if user_auth_config is not None:
270
- auth_plugin = _try_obtain_exec_auth_plugin(user_auth_config)
268
+ # Load the auth plugin if the args.user_auth_config is provided
269
+ if cfg_path := getattr(args, "user_auth_config", None):
270
+ auth_plugin = _try_obtain_exec_auth_plugin(Path(cfg_path))
271
271
 
272
272
  # Initialize StateFactory
273
273
  state_factory = LinkStateFactory(args.database)
@@ -293,7 +293,7 @@ def run_superlink() -> None:
293
293
  # Determine Exec plugin
294
294
  # If simulation is used, don't start ServerAppIo and Fleet APIs
295
295
  sim_exec = executor.__class__.__qualname__ == "SimulationEngine"
296
- bckg_threads = []
296
+ bckg_threads: list[threading.Thread] = []
297
297
 
298
298
  if sim_exec:
299
299
  simulationio_server: grpc.Server = run_simulationio_api_grpc(
@@ -361,6 +361,7 @@ def run_superlink() -> None:
361
361
  ffs_factory,
362
362
  num_workers,
363
363
  ),
364
+ daemon=True,
364
365
  )
365
366
  fleet_thread.start()
366
367
  bckg_threads.append(fleet_thread)
@@ -374,6 +375,7 @@ def run_superlink() -> None:
374
375
  server_public_key,
375
376
  ) = maybe_keys
376
377
  state = state_factory.state()
378
+ state.clear_supernode_auth_keys_and_credentials()
377
379
  state.store_node_public_keys(node_public_keys)
378
380
  state.store_server_private_public_key(
379
381
  private_key_to_bytes(server_private_key),
@@ -426,6 +428,7 @@ def run_superlink() -> None:
426
428
  address,
427
429
  cmd,
428
430
  ),
431
+ daemon=True,
429
432
  )
430
433
  scheduler_th.start()
431
434
  bckg_threads.append(scheduler_th)
@@ -434,16 +437,24 @@ def run_superlink() -> None:
434
437
  register_exit_handlers(
435
438
  event_type=EventType.RUN_SUPERLINK_LEAVE,
436
439
  grpc_servers=grpc_servers,
437
- bckg_threads=bckg_threads,
438
440
  )
439
441
 
440
- # Block
441
- while True:
442
- if bckg_threads:
443
- for thread in bckg_threads:
444
- if not thread.is_alive():
445
- sys.exit(1)
446
- exec_server.wait_for_termination(timeout=1)
442
+ # Block until a thread exits prematurely
443
+ while all(thread.is_alive() for thread in bckg_threads):
444
+ sleep(0.1)
445
+
446
+ # Exit if any thread has exited prematurely
447
+ sys.exit(1)
448
+
449
+
450
+ def _run_flwr_command(args: list[str]) -> None:
451
+ sys.argv = args
452
+ if args[0] == "flwr-serverapp":
453
+ flwr_serverapp()
454
+ elif args[0] == "flwr-simulation":
455
+ flwr_simulation()
456
+ else:
457
+ raise ValueError(f"Unknown command: {args[0]}")
447
458
 
448
459
 
449
460
  def _flwr_scheduler(
@@ -453,15 +464,18 @@ def _flwr_scheduler(
453
464
  cmd: str,
454
465
  ) -> None:
455
466
  log(DEBUG, "Started %s scheduler thread.", cmd)
456
-
457
467
  state = state_factory.state()
468
+ run_id_to_proc: dict[int, multiprocessing.context.SpawnProcess] = {}
469
+
470
+ # Use the "spawn" start method for multiprocessing.
471
+ mp_spawn_context = multiprocessing.get_context("spawn")
458
472
 
459
473
  # Periodically check for a pending run in the LinkState
460
474
  while True:
461
- sleep(3)
475
+ sleep(0.1)
462
476
  pending_run_id = state.get_pending_run_id()
463
477
 
464
- if pending_run_id:
478
+ if pending_run_id and pending_run_id not in run_id_to_proc:
465
479
 
466
480
  log(
467
481
  INFO,
@@ -478,10 +492,18 @@ def _flwr_scheduler(
478
492
  "--insecure",
479
493
  ]
480
494
 
481
- subprocess.Popen( # pylint: disable=consider-using-with
482
- command,
483
- text=True,
495
+ proc = mp_spawn_context.Process(
496
+ target=_run_flwr_command, args=(command,), daemon=True
484
497
  )
498
+ proc.start()
499
+
500
+ # Store the process
501
+ run_id_to_proc[pending_run_id] = proc
502
+
503
+ # Clean up finished processes
504
+ for run_id, proc in list(run_id_to_proc.items()):
505
+ if not proc.is_alive():
506
+ del run_id_to_proc[run_id]
485
507
 
486
508
 
487
509
  def _format_address(address: str) -> tuple[str, str, int]:
@@ -583,21 +605,20 @@ def _try_setup_node_authentication(
583
605
  )
584
606
 
585
607
 
586
- def _try_obtain_user_auth_config(args: argparse.Namespace) -> Optional[dict[str, Any]]:
587
- if getattr(args, "user_auth_config", None) is not None:
588
- with open(args.user_auth_config, encoding="utf-8") as file:
589
- config: dict[str, Any] = yaml.safe_load(file)
590
- return config
591
- return None
592
-
608
+ def _try_obtain_exec_auth_plugin(config_path: Path) -> Optional[ExecAuthPlugin]:
609
+ # Load YAML file
610
+ with config_path.open("r", encoding="utf-8") as file:
611
+ config: dict[str, Any] = yaml.safe_load(file)
593
612
 
594
- def _try_obtain_exec_auth_plugin(config: dict[str, Any]) -> Optional[ExecAuthPlugin]:
613
+ # Load authentication configuration
595
614
  auth_config: dict[str, Any] = config.get("authentication", {})
596
615
  auth_type: str = auth_config.get(AUTH_TYPE, "")
616
+
617
+ # Load authentication plugin
597
618
  try:
598
619
  all_plugins: dict[str, type[ExecAuthPlugin]] = get_exec_auth_plugins()
599
620
  auth_plugin_class = all_plugins[auth_type]
600
- return auth_plugin_class(config=auth_config)
621
+ return auth_plugin_class(user_auth_config_path=config_path)
601
622
  except KeyError:
602
623
  if auth_type != "":
603
624
  sys.exit(