flwr-nightly 1.8.0.dev20240314__py3-none-any.whl → 1.15.0.dev20250114__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (311) hide show
  1. flwr/cli/app.py +16 -2
  2. flwr/cli/build.py +181 -0
  3. flwr/cli/cli_user_auth_interceptor.py +90 -0
  4. flwr/cli/config_utils.py +343 -0
  5. flwr/cli/example.py +4 -1
  6. flwr/cli/install.py +253 -0
  7. flwr/cli/log.py +182 -0
  8. flwr/{server/superlink/state → cli/login}/__init__.py +4 -10
  9. flwr/cli/login/login.py +88 -0
  10. flwr/cli/ls.py +327 -0
  11. flwr/cli/new/__init__.py +1 -0
  12. flwr/cli/new/new.py +210 -66
  13. flwr/cli/new/templates/app/.gitignore.tpl +163 -0
  14. flwr/cli/new/templates/app/LICENSE.tpl +202 -0
  15. flwr/cli/new/templates/app/README.baseline.md.tpl +127 -0
  16. flwr/cli/new/templates/app/README.flowertune.md.tpl +66 -0
  17. flwr/cli/new/templates/app/README.md.tpl +16 -32
  18. flwr/cli/new/templates/app/code/__init__.baseline.py.tpl +1 -0
  19. flwr/cli/new/templates/app/code/__init__.py.tpl +1 -1
  20. flwr/cli/new/templates/app/code/client.baseline.py.tpl +58 -0
  21. flwr/cli/new/templates/app/code/client.huggingface.py.tpl +55 -0
  22. flwr/cli/new/templates/app/code/client.jax.py.tpl +50 -0
  23. flwr/cli/new/templates/app/code/client.mlx.py.tpl +73 -0
  24. flwr/cli/new/templates/app/code/client.numpy.py.tpl +7 -7
  25. flwr/cli/new/templates/app/code/client.pytorch.py.tpl +30 -21
  26. flwr/cli/new/templates/app/code/client.sklearn.py.tpl +63 -0
  27. flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +57 -1
  28. flwr/cli/new/templates/app/code/dataset.baseline.py.tpl +36 -0
  29. flwr/cli/new/templates/app/code/flwr_tune/__init__.py +15 -0
  30. flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl +126 -0
  31. flwr/cli/new/templates/app/code/flwr_tune/dataset.py.tpl +87 -0
  32. flwr/cli/new/templates/app/code/flwr_tune/models.py.tpl +78 -0
  33. flwr/cli/new/templates/app/code/flwr_tune/server_app.py.tpl +94 -0
  34. flwr/cli/new/templates/app/code/flwr_tune/strategy.py.tpl +83 -0
  35. flwr/cli/new/templates/app/code/model.baseline.py.tpl +80 -0
  36. flwr/cli/new/templates/app/code/server.baseline.py.tpl +46 -0
  37. flwr/cli/new/templates/app/code/server.huggingface.py.tpl +38 -0
  38. flwr/cli/new/templates/app/code/server.jax.py.tpl +26 -0
  39. flwr/cli/new/templates/app/code/server.mlx.py.tpl +31 -0
  40. flwr/cli/new/templates/app/code/server.numpy.py.tpl +22 -9
  41. flwr/cli/new/templates/app/code/server.pytorch.py.tpl +21 -18
  42. flwr/cli/new/templates/app/code/server.sklearn.py.tpl +36 -0
  43. flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +29 -1
  44. flwr/cli/new/templates/app/code/strategy.baseline.py.tpl +1 -0
  45. flwr/cli/new/templates/app/code/task.huggingface.py.tpl +102 -0
  46. flwr/cli/new/templates/app/code/task.jax.py.tpl +57 -0
  47. flwr/cli/new/templates/app/code/task.mlx.py.tpl +102 -0
  48. flwr/cli/new/templates/app/code/task.numpy.py.tpl +7 -0
  49. flwr/cli/new/templates/app/code/task.pytorch.py.tpl +29 -24
  50. flwr/cli/new/templates/app/code/task.sklearn.py.tpl +67 -0
  51. flwr/cli/new/templates/app/code/task.tensorflow.py.tpl +53 -0
  52. flwr/cli/new/templates/app/code/utils.baseline.py.tpl +1 -0
  53. flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +138 -0
  54. flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +68 -0
  55. flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +46 -0
  56. flwr/cli/new/templates/app/pyproject.jax.toml.tpl +35 -0
  57. flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +39 -0
  58. flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +25 -12
  59. flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +29 -14
  60. flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +35 -0
  61. flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +29 -14
  62. flwr/cli/run/__init__.py +1 -0
  63. flwr/cli/run/run.py +212 -34
  64. flwr/cli/stop.py +130 -0
  65. flwr/cli/utils.py +240 -5
  66. flwr/client/__init__.py +3 -2
  67. flwr/client/app.py +432 -255
  68. flwr/client/client.py +1 -11
  69. flwr/client/client_app.py +74 -13
  70. flwr/client/clientapp/__init__.py +22 -0
  71. flwr/client/clientapp/app.py +259 -0
  72. flwr/client/clientapp/clientappio_servicer.py +244 -0
  73. flwr/client/clientapp/utils.py +115 -0
  74. flwr/client/dpfedavg_numpy_client.py +7 -8
  75. flwr/client/grpc_adapter_client/__init__.py +15 -0
  76. flwr/client/grpc_adapter_client/connection.py +98 -0
  77. flwr/client/grpc_client/connection.py +21 -7
  78. flwr/client/grpc_rere_client/__init__.py +1 -1
  79. flwr/client/grpc_rere_client/client_interceptor.py +176 -0
  80. flwr/client/grpc_rere_client/connection.py +163 -56
  81. flwr/client/grpc_rere_client/grpc_adapter.py +167 -0
  82. flwr/client/heartbeat.py +74 -0
  83. flwr/client/message_handler/__init__.py +1 -1
  84. flwr/client/message_handler/message_handler.py +10 -11
  85. flwr/client/mod/__init__.py +5 -5
  86. flwr/client/mod/centraldp_mods.py +4 -2
  87. flwr/client/mod/comms_mods.py +5 -4
  88. flwr/client/mod/localdp_mod.py +10 -5
  89. flwr/client/mod/secure_aggregation/__init__.py +1 -1
  90. flwr/client/mod/secure_aggregation/secaggplus_mod.py +26 -26
  91. flwr/client/mod/utils.py +2 -4
  92. flwr/client/nodestate/__init__.py +26 -0
  93. flwr/client/nodestate/in_memory_nodestate.py +38 -0
  94. flwr/client/nodestate/nodestate.py +31 -0
  95. flwr/client/nodestate/nodestate_factory.py +38 -0
  96. flwr/client/numpy_client.py +8 -31
  97. flwr/client/rest_client/__init__.py +1 -1
  98. flwr/client/rest_client/connection.py +199 -176
  99. flwr/client/run_info_store.py +112 -0
  100. flwr/client/supernode/__init__.py +24 -0
  101. flwr/client/supernode/app.py +321 -0
  102. flwr/client/typing.py +1 -0
  103. flwr/common/__init__.py +17 -11
  104. flwr/common/address.py +47 -3
  105. flwr/common/args.py +153 -0
  106. flwr/common/auth_plugin/__init__.py +24 -0
  107. flwr/common/auth_plugin/auth_plugin.py +121 -0
  108. flwr/common/config.py +243 -0
  109. flwr/common/constant.py +132 -1
  110. flwr/common/context.py +32 -2
  111. flwr/common/date.py +22 -4
  112. flwr/common/differential_privacy.py +2 -2
  113. flwr/common/dp.py +2 -4
  114. flwr/common/exit_handlers.py +3 -3
  115. flwr/common/grpc.py +164 -5
  116. flwr/common/logger.py +230 -12
  117. flwr/common/message.py +191 -106
  118. flwr/common/object_ref.py +179 -44
  119. flwr/common/pyproject.py +1 -0
  120. flwr/common/record/__init__.py +2 -1
  121. flwr/common/record/configsrecord.py +58 -18
  122. flwr/common/record/metricsrecord.py +57 -17
  123. flwr/common/record/parametersrecord.py +88 -20
  124. flwr/common/record/recordset.py +153 -30
  125. flwr/common/record/typeddict.py +30 -55
  126. flwr/common/recordset_compat.py +31 -12
  127. flwr/common/retry_invoker.py +123 -30
  128. flwr/common/secure_aggregation/__init__.py +1 -1
  129. flwr/common/secure_aggregation/crypto/__init__.py +1 -1
  130. flwr/common/secure_aggregation/crypto/shamir.py +11 -11
  131. flwr/common/secure_aggregation/crypto/symmetric_encryption.py +68 -4
  132. flwr/common/secure_aggregation/ndarrays_arithmetic.py +17 -17
  133. flwr/common/secure_aggregation/quantization.py +8 -8
  134. flwr/common/secure_aggregation/secaggplus_constants.py +1 -1
  135. flwr/common/secure_aggregation/secaggplus_utils.py +10 -12
  136. flwr/common/serde.py +298 -19
  137. flwr/common/telemetry.py +65 -29
  138. flwr/common/typing.py +120 -19
  139. flwr/common/version.py +17 -3
  140. flwr/proto/clientappio_pb2.py +45 -0
  141. flwr/proto/clientappio_pb2.pyi +132 -0
  142. flwr/proto/clientappio_pb2_grpc.py +135 -0
  143. flwr/proto/clientappio_pb2_grpc.pyi +53 -0
  144. flwr/proto/exec_pb2.py +62 -0
  145. flwr/proto/exec_pb2.pyi +212 -0
  146. flwr/proto/exec_pb2_grpc.py +237 -0
  147. flwr/proto/exec_pb2_grpc.pyi +93 -0
  148. flwr/proto/fab_pb2.py +31 -0
  149. flwr/proto/fab_pb2.pyi +65 -0
  150. flwr/proto/fab_pb2_grpc.py +4 -0
  151. flwr/proto/fab_pb2_grpc.pyi +4 -0
  152. flwr/proto/fleet_pb2.py +42 -23
  153. flwr/proto/fleet_pb2.pyi +123 -1
  154. flwr/proto/fleet_pb2_grpc.py +170 -0
  155. flwr/proto/fleet_pb2_grpc.pyi +61 -0
  156. flwr/proto/grpcadapter_pb2.py +32 -0
  157. flwr/proto/grpcadapter_pb2.pyi +43 -0
  158. flwr/proto/grpcadapter_pb2_grpc.py +66 -0
  159. flwr/proto/grpcadapter_pb2_grpc.pyi +24 -0
  160. flwr/proto/log_pb2.py +29 -0
  161. flwr/proto/log_pb2.pyi +39 -0
  162. flwr/proto/log_pb2_grpc.py +4 -0
  163. flwr/proto/log_pb2_grpc.pyi +4 -0
  164. flwr/proto/message_pb2.py +41 -0
  165. flwr/proto/message_pb2.pyi +128 -0
  166. flwr/proto/message_pb2_grpc.py +4 -0
  167. flwr/proto/message_pb2_grpc.pyi +4 -0
  168. flwr/proto/node_pb2.py +1 -1
  169. flwr/proto/recordset_pb2.py +35 -33
  170. flwr/proto/recordset_pb2.pyi +40 -14
  171. flwr/proto/run_pb2.py +64 -0
  172. flwr/proto/run_pb2.pyi +268 -0
  173. flwr/proto/run_pb2_grpc.py +4 -0
  174. flwr/proto/run_pb2_grpc.pyi +4 -0
  175. flwr/proto/serverappio_pb2.py +52 -0
  176. flwr/proto/{driver_pb2.pyi → serverappio_pb2.pyi} +62 -20
  177. flwr/proto/serverappio_pb2_grpc.py +410 -0
  178. flwr/proto/serverappio_pb2_grpc.pyi +160 -0
  179. flwr/proto/simulationio_pb2.py +38 -0
  180. flwr/proto/simulationio_pb2.pyi +65 -0
  181. flwr/proto/simulationio_pb2_grpc.py +239 -0
  182. flwr/proto/simulationio_pb2_grpc.pyi +94 -0
  183. flwr/proto/task_pb2.py +7 -8
  184. flwr/proto/task_pb2.pyi +8 -5
  185. flwr/proto/transport_pb2.py +8 -8
  186. flwr/proto/transport_pb2.pyi +9 -6
  187. flwr/server/__init__.py +2 -10
  188. flwr/server/app.py +579 -402
  189. flwr/server/client_manager.py +8 -6
  190. flwr/server/compat/app.py +6 -62
  191. flwr/server/compat/app_utils.py +14 -8
  192. flwr/server/compat/driver_client_proxy.py +25 -58
  193. flwr/server/compat/legacy_context.py +5 -4
  194. flwr/server/driver/__init__.py +2 -0
  195. flwr/server/driver/driver.py +36 -131
  196. flwr/server/driver/grpc_driver.py +217 -81
  197. flwr/server/driver/inmemory_driver.py +182 -0
  198. flwr/server/history.py +28 -29
  199. flwr/server/run_serverapp.py +15 -126
  200. flwr/server/server.py +50 -44
  201. flwr/server/server_app.py +59 -10
  202. flwr/server/serverapp/__init__.py +22 -0
  203. flwr/server/serverapp/app.py +256 -0
  204. flwr/server/serverapp_components.py +52 -0
  205. flwr/server/strategy/__init__.py +2 -2
  206. flwr/server/strategy/aggregate.py +37 -23
  207. flwr/server/strategy/bulyan.py +9 -9
  208. flwr/server/strategy/dp_adaptive_clipping.py +25 -25
  209. flwr/server/strategy/dp_fixed_clipping.py +23 -22
  210. flwr/server/strategy/dpfedavg_adaptive.py +8 -8
  211. flwr/server/strategy/dpfedavg_fixed.py +13 -12
  212. flwr/server/strategy/fault_tolerant_fedavg.py +11 -11
  213. flwr/server/strategy/fedadagrad.py +9 -9
  214. flwr/server/strategy/fedadam.py +20 -10
  215. flwr/server/strategy/fedavg.py +16 -16
  216. flwr/server/strategy/fedavg_android.py +17 -17
  217. flwr/server/strategy/fedavgm.py +9 -9
  218. flwr/server/strategy/fedmedian.py +5 -5
  219. flwr/server/strategy/fedopt.py +6 -6
  220. flwr/server/strategy/fedprox.py +7 -7
  221. flwr/server/strategy/fedtrimmedavg.py +8 -8
  222. flwr/server/strategy/fedxgb_bagging.py +12 -12
  223. flwr/server/strategy/fedxgb_cyclic.py +10 -10
  224. flwr/server/strategy/fedxgb_nn_avg.py +6 -6
  225. flwr/server/strategy/fedyogi.py +9 -9
  226. flwr/server/strategy/krum.py +9 -9
  227. flwr/server/strategy/qfedavg.py +16 -16
  228. flwr/server/strategy/strategy.py +10 -10
  229. flwr/server/superlink/driver/__init__.py +2 -2
  230. flwr/server/superlink/driver/serverappio_grpc.py +61 -0
  231. flwr/server/superlink/driver/serverappio_servicer.py +363 -0
  232. flwr/server/superlink/ffs/__init__.py +24 -0
  233. flwr/server/superlink/ffs/disk_ffs.py +108 -0
  234. flwr/server/superlink/ffs/ffs.py +79 -0
  235. flwr/server/superlink/ffs/ffs_factory.py +47 -0
  236. flwr/server/superlink/fleet/__init__.py +1 -1
  237. flwr/server/superlink/fleet/grpc_adapter/__init__.py +15 -0
  238. flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +162 -0
  239. flwr/server/superlink/fleet/grpc_bidi/__init__.py +1 -1
  240. flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +4 -2
  241. flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py +3 -2
  242. flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py +1 -1
  243. flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +5 -154
  244. flwr/server/superlink/fleet/grpc_rere/__init__.py +1 -1
  245. flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +120 -13
  246. flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +228 -0
  247. flwr/server/superlink/fleet/message_handler/__init__.py +1 -1
  248. flwr/server/superlink/fleet/message_handler/message_handler.py +153 -9
  249. flwr/server/superlink/fleet/rest_rere/__init__.py +1 -1
  250. flwr/server/superlink/fleet/rest_rere/rest_api.py +119 -81
  251. flwr/server/superlink/fleet/vce/__init__.py +1 -0
  252. flwr/server/superlink/fleet/vce/backend/__init__.py +4 -4
  253. flwr/server/superlink/fleet/vce/backend/backend.py +8 -9
  254. flwr/server/superlink/fleet/vce/backend/raybackend.py +87 -68
  255. flwr/server/superlink/fleet/vce/vce_api.py +208 -146
  256. flwr/server/superlink/linkstate/__init__.py +28 -0
  257. flwr/server/superlink/linkstate/in_memory_linkstate.py +581 -0
  258. flwr/server/superlink/linkstate/linkstate.py +389 -0
  259. flwr/server/superlink/{state/state_factory.py → linkstate/linkstate_factory.py} +19 -10
  260. flwr/server/superlink/linkstate/sqlite_linkstate.py +1236 -0
  261. flwr/server/superlink/linkstate/utils.py +389 -0
  262. flwr/server/superlink/simulation/__init__.py +15 -0
  263. flwr/server/superlink/simulation/simulationio_grpc.py +65 -0
  264. flwr/server/superlink/simulation/simulationio_servicer.py +186 -0
  265. flwr/server/superlink/utils.py +65 -0
  266. flwr/server/typing.py +2 -0
  267. flwr/server/utils/__init__.py +1 -1
  268. flwr/server/utils/tensorboard.py +5 -5
  269. flwr/server/utils/validator.py +31 -11
  270. flwr/server/workflow/default_workflows.py +70 -26
  271. flwr/server/workflow/secure_aggregation/secagg_workflow.py +1 -0
  272. flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +40 -27
  273. flwr/simulation/__init__.py +12 -5
  274. flwr/simulation/app.py +247 -315
  275. flwr/simulation/legacy_app.py +402 -0
  276. flwr/simulation/ray_transport/__init__.py +1 -1
  277. flwr/simulation/ray_transport/ray_actor.py +42 -67
  278. flwr/simulation/ray_transport/ray_client_proxy.py +37 -17
  279. flwr/simulation/ray_transport/utils.py +1 -0
  280. flwr/simulation/run_simulation.py +306 -163
  281. flwr/simulation/simulationio_connection.py +89 -0
  282. flwr/superexec/__init__.py +15 -0
  283. flwr/superexec/app.py +59 -0
  284. flwr/superexec/deployment.py +188 -0
  285. flwr/superexec/exec_grpc.py +80 -0
  286. flwr/superexec/exec_servicer.py +231 -0
  287. flwr/superexec/exec_user_auth_interceptor.py +101 -0
  288. flwr/superexec/executor.py +96 -0
  289. flwr/superexec/simulation.py +124 -0
  290. {flwr_nightly-1.8.0.dev20240314.dist-info → flwr_nightly-1.15.0.dev20250114.dist-info}/METADATA +33 -26
  291. flwr_nightly-1.15.0.dev20250114.dist-info/RECORD +328 -0
  292. flwr_nightly-1.15.0.dev20250114.dist-info/entry_points.txt +12 -0
  293. flwr/cli/flower_toml.py +0 -140
  294. flwr/cli/new/templates/app/flower.toml.tpl +0 -13
  295. flwr/cli/new/templates/app/requirements.numpy.txt.tpl +0 -2
  296. flwr/cli/new/templates/app/requirements.pytorch.txt.tpl +0 -4
  297. flwr/cli/new/templates/app/requirements.tensorflow.txt.tpl +0 -4
  298. flwr/client/node_state.py +0 -48
  299. flwr/client/node_state_tests.py +0 -65
  300. flwr/proto/driver_pb2.py +0 -44
  301. flwr/proto/driver_pb2_grpc.py +0 -169
  302. flwr/proto/driver_pb2_grpc.pyi +0 -66
  303. flwr/server/superlink/driver/driver_grpc.py +0 -54
  304. flwr/server/superlink/driver/driver_servicer.py +0 -129
  305. flwr/server/superlink/state/in_memory_state.py +0 -230
  306. flwr/server/superlink/state/sqlite_state.py +0 -630
  307. flwr/server/superlink/state/state.py +0 -154
  308. flwr_nightly-1.8.0.dev20240314.dist-info/RECORD +0 -211
  309. flwr_nightly-1.8.0.dev20240314.dist-info/entry_points.txt +0 -9
  310. {flwr_nightly-1.8.0.dev20240314.dist-info → flwr_nightly-1.15.0.dev20250114.dist-info}/LICENSE +0 -0
  311. {flwr_nightly-1.8.0.dev20240314.dist-info → flwr_nightly-1.15.0.dev20250114.dist-info}/WHEEL +0 -0
@@ -0,0 +1,402 @@
1
+ # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+ """Flower simulation app."""
16
+
17
+
18
+ import asyncio
19
+ import logging
20
+ import sys
21
+ import threading
22
+ import traceback
23
+ import warnings
24
+ from logging import ERROR, INFO
25
+ from typing import Any, Optional, Union
26
+
27
+ import ray
28
+ from ray.util.scheduling_strategies import NodeAffinitySchedulingStrategy
29
+
30
+ from flwr.client import ClientFnExt
31
+ from flwr.common import EventType, event
32
+ from flwr.common.constant import NODE_ID_NUM_BYTES
33
+ from flwr.common.logger import (
34
+ log,
35
+ set_logger_propagation,
36
+ warn_deprecated_feature,
37
+ warn_unsupported_feature,
38
+ )
39
+ from flwr.server.client_manager import ClientManager
40
+ from flwr.server.history import History
41
+ from flwr.server.server import Server, init_defaults, run_fl
42
+ from flwr.server.server_config import ServerConfig
43
+ from flwr.server.strategy import Strategy
44
+ from flwr.server.superlink.linkstate.utils import generate_rand_int_from_bytes
45
+ from flwr.simulation.ray_transport.ray_actor import (
46
+ ClientAppActor,
47
+ VirtualClientEngineActor,
48
+ VirtualClientEngineActorPool,
49
+ pool_size_from_resources,
50
+ )
51
+ from flwr.simulation.ray_transport.ray_client_proxy import RayActorClientProxy
52
+
53
+ INVALID_ARGUMENTS_START_SIMULATION = """
54
+ INVALID ARGUMENTS ERROR
55
+
56
+ Invalid Arguments in method:
57
+
58
+ `start_simulation(
59
+ *,
60
+ client_fn: ClientFn,
61
+ num_clients: int,
62
+ clients_ids: Optional[List[str]] = None,
63
+ client_resources: Optional[Dict[str, float]] = None,
64
+ server: Optional[Server] = None,
65
+ config: ServerConfig = None,
66
+ strategy: Optional[Strategy] = None,
67
+ client_manager: Optional[ClientManager] = None,
68
+ ray_init_args: Optional[Dict[str, Any]] = None,
69
+ ) -> None:`
70
+
71
+ REASON:
72
+ Method requires:
73
+ - Either `num_clients`[int] or `clients_ids`[List[str]]
74
+ to be set exclusively.
75
+ OR
76
+ - `len(clients_ids)` == `num_clients`
77
+
78
+ """
79
+
80
+ NodeToPartitionMapping = dict[int, int]
81
+
82
+
83
+ def _create_node_id_to_partition_mapping(
84
+ num_clients: int,
85
+ ) -> NodeToPartitionMapping:
86
+ """Generate a node_id:partition_id mapping."""
87
+ nodes_mapping: NodeToPartitionMapping = {} # {node-id; partition-id}
88
+ for i in range(num_clients):
89
+ while True:
90
+ node_id = generate_rand_int_from_bytes(NODE_ID_NUM_BYTES)
91
+ if node_id not in nodes_mapping:
92
+ break
93
+ nodes_mapping[node_id] = i
94
+ return nodes_mapping
95
+
96
+
97
+ # pylint: disable=too-many-arguments,too-many-statements,too-many-branches
98
+ def start_simulation(
99
+ *,
100
+ client_fn: ClientFnExt,
101
+ num_clients: int,
102
+ clients_ids: Optional[list[str]] = None, # UNSUPPORTED, WILL BE REMOVED
103
+ client_resources: Optional[dict[str, float]] = None,
104
+ server: Optional[Server] = None,
105
+ config: Optional[ServerConfig] = None,
106
+ strategy: Optional[Strategy] = None,
107
+ client_manager: Optional[ClientManager] = None,
108
+ ray_init_args: Optional[dict[str, Any]] = None,
109
+ keep_initialised: Optional[bool] = False,
110
+ actor_type: type[VirtualClientEngineActor] = ClientAppActor,
111
+ actor_kwargs: Optional[dict[str, Any]] = None,
112
+ actor_scheduling: Union[str, NodeAffinitySchedulingStrategy] = "DEFAULT",
113
+ ) -> History:
114
+ """Start a Ray-based Flower simulation server.
115
+
116
+ Warning
117
+ -------
118
+ This function is deprecated since 1.13.0. Use :code: `flwr run` to start a Flower
119
+ simulation.
120
+
121
+ Parameters
122
+ ----------
123
+ client_fn : ClientFnExt
124
+ A function creating `Client` instances. The function must have the signature
125
+ `client_fn(context: Context). It should return
126
+ a single client instance of type `Client`. Note that the created client
127
+ instances are ephemeral and will often be destroyed after a single method
128
+ invocation. Since client instances are not long-lived, they should not attempt
129
+ to carry state over method invocations. Any state required by the instance
130
+ (model, dataset, hyperparameters, ...) should be (re-)created in either the
131
+ call to `client_fn` or the call to any of the client methods (e.g., load
132
+ evaluation data in the `evaluate` method itself).
133
+ num_clients : int
134
+ The total number of clients in this simulation.
135
+ clients_ids : Optional[List[str]]
136
+ UNSUPPORTED, WILL BE REMOVED. USE `num_clients` INSTEAD.
137
+ List `client_id`s for each client. This is only required if
138
+ `num_clients` is not set. Setting both `num_clients` and `clients_ids`
139
+ with `len(clients_ids)` not equal to `num_clients` generates an error.
140
+ Using this argument will raise an error.
141
+ client_resources : Optional[Dict[str, float]] (default: `{"num_cpus": 1, "num_gpus": 0.0}`)
142
+ CPU and GPU resources for a single client. Supported keys
143
+ are `num_cpus` and `num_gpus`. To understand the GPU utilization caused by
144
+ `num_gpus`, as well as using custom resources, please consult the Ray
145
+ documentation.
146
+ server : Optional[flwr.server.Server] (default: None).
147
+ An implementation of the abstract base class `flwr.server.Server`. If no
148
+ instance is provided, then `start_server` will create one.
149
+ config: ServerConfig (default: None).
150
+ Currently supported values are `num_rounds` (int, default: 1) and
151
+ `round_timeout` in seconds (float, default: None).
152
+ strategy : Optional[flwr.server.Strategy] (default: None)
153
+ An implementation of the abstract base class `flwr.server.Strategy`. If
154
+ no strategy is provided, then `start_server` will use
155
+ `flwr.server.strategy.FedAvg`.
156
+ client_manager : Optional[flwr.server.ClientManager] (default: None)
157
+ An implementation of the abstract base class `flwr.server.ClientManager`.
158
+ If no implementation is provided, then `start_simulation` will use
159
+ `flwr.server.client_manager.SimpleClientManager`.
160
+ ray_init_args : Optional[Dict[str, Any]] (default: None)
161
+ Optional dictionary containing arguments for the call to `ray.init`.
162
+ If ray_init_args is None (the default), Ray will be initialized with
163
+ the following default args:
164
+
165
+ { "ignore_reinit_error": True, "include_dashboard": False }
166
+
167
+ An empty dictionary can be used (ray_init_args={}) to prevent any
168
+ arguments from being passed to ray.init.
169
+ keep_initialised: Optional[bool] (default: False)
170
+ Set to True to prevent `ray.shutdown()` in case `ray.is_initialized()=True`.
171
+
172
+ actor_type: VirtualClientEngineActor (default: ClientAppActor)
173
+ Optionally specify the type of actor to use. The actor object, which
174
+ persists throughout the simulation, will be the process in charge of
175
+ executing a ClientApp wrapping input argument `client_fn`.
176
+
177
+ actor_kwargs: Optional[Dict[str, Any]] (default: None)
178
+ If you want to create your own Actor classes, you might need to pass
179
+ some input argument. You can use this dictionary for such purpose.
180
+
181
+ actor_scheduling: Optional[Union[str, NodeAffinitySchedulingStrategy]]
182
+ (default: "DEFAULT")
183
+ Optional string ("DEFAULT" or "SPREAD") for the VCE to choose in which
184
+ node the actor is placed. If you are an advanced user needed more control
185
+ you can use lower-level scheduling strategies to pin actors to specific
186
+ compute nodes (e.g. via NodeAffinitySchedulingStrategy). Please note this
187
+ is an advanced feature. For all details, please refer to the Ray documentation:
188
+ https://docs.ray.io/en/latest/ray-core/scheduling/index.html
189
+
190
+ Returns
191
+ -------
192
+ hist : flwr.server.history.History
193
+ Object containing metrics from training.
194
+ """ # noqa: E501
195
+ # pylint: disable-msg=too-many-locals
196
+ msg = (
197
+ "flwr.simulation.start_simulation() is deprecated."
198
+ "\n\tInstead, use the `flwr run` CLI command to start a local simulation "
199
+ "in your Flower app, as shown for example below:"
200
+ "\n\n\t\t$ flwr new # Create a new Flower app from a template"
201
+ "\n\n\t\t$ flwr run # Run the Flower app in Simulation Mode"
202
+ "\n\n\tUsing `start_simulation()` is deprecated."
203
+ )
204
+ warn_deprecated_feature(name=msg)
205
+
206
+ event(
207
+ EventType.START_SIMULATION_ENTER,
208
+ {"num_clients": len(clients_ids) if clients_ids is not None else num_clients},
209
+ )
210
+
211
+ if clients_ids is not None:
212
+ warn_unsupported_feature(
213
+ "Passing `clients_ids` to `start_simulation` is deprecated and not longer "
214
+ "used by `start_simulation`. Use `num_clients` exclusively instead."
215
+ )
216
+ log(ERROR, "`clients_ids` argument used.")
217
+ sys.exit()
218
+
219
+ # Set logger propagation
220
+ loop: Optional[asyncio.AbstractEventLoop] = None
221
+ try:
222
+ loop = asyncio.get_running_loop()
223
+ except RuntimeError:
224
+ loop = None
225
+ finally:
226
+ if loop and loop.is_running():
227
+ # Set logger propagation to False to prevent duplicated log output in Colab.
228
+ logger = logging.getLogger("flwr")
229
+ _ = set_logger_propagation(logger, False)
230
+
231
+ # Initialize server and server config
232
+ initialized_server, initialized_config = init_defaults(
233
+ server=server,
234
+ config=config,
235
+ strategy=strategy,
236
+ client_manager=client_manager,
237
+ )
238
+
239
+ log(
240
+ INFO,
241
+ "Starting Flower simulation, config: %s",
242
+ initialized_config,
243
+ )
244
+
245
+ # Create node-id to partition-id mapping
246
+ nodes_mapping = _create_node_id_to_partition_mapping(num_clients)
247
+
248
+ # Default arguments for Ray initialization
249
+ if not ray_init_args:
250
+ ray_init_args = {
251
+ "ignore_reinit_error": True,
252
+ "include_dashboard": False,
253
+ }
254
+
255
+ # Shut down Ray if it has already been initialized (unless asked not to)
256
+ if ray.is_initialized() and not keep_initialised:
257
+ ray.shutdown()
258
+
259
+ # Initialize Ray
260
+ ray.init(**ray_init_args)
261
+ cluster_resources = ray.cluster_resources()
262
+ log(
263
+ INFO,
264
+ "Flower VCE: Ray initialized with resources: %s",
265
+ cluster_resources,
266
+ )
267
+
268
+ log(
269
+ INFO,
270
+ "Optimize your simulation with Flower VCE: "
271
+ "https://flower.ai/docs/framework/how-to-run-simulations.html",
272
+ )
273
+
274
+ # Log the resources that a single client will be able to use
275
+ if client_resources is None:
276
+ log(
277
+ INFO,
278
+ "No `client_resources` specified. Using minimal resources for clients.",
279
+ )
280
+ client_resources = {"num_cpus": 1, "num_gpus": 0.0}
281
+
282
+ # Each client needs at the very least one CPU
283
+ if "num_cpus" not in client_resources:
284
+ warnings.warn(
285
+ "No `num_cpus` specified in `client_resources`. "
286
+ "Using `num_cpus=1` for each client.",
287
+ stacklevel=2,
288
+ )
289
+ client_resources["num_cpus"] = 1
290
+
291
+ log(
292
+ INFO,
293
+ "Flower VCE: Resources for each Virtual Client: %s",
294
+ client_resources,
295
+ )
296
+
297
+ actor_args = {} if actor_kwargs is None else actor_kwargs
298
+
299
+ # An actor factory. This is called N times to add N actors
300
+ # to the pool. If at some point the pool can accommodate more actors
301
+ # this will be called again.
302
+ def create_actor_fn() -> type[VirtualClientEngineActor]:
303
+ return actor_type.options( # type: ignore
304
+ **client_resources,
305
+ scheduling_strategy=actor_scheduling,
306
+ ).remote(**actor_args)
307
+
308
+ # Instantiate ActorPool
309
+ pool = VirtualClientEngineActorPool(
310
+ create_actor_fn=create_actor_fn,
311
+ client_resources=client_resources,
312
+ )
313
+
314
+ f_stop = threading.Event()
315
+
316
+ # Periodically, check if the cluster has grown (i.e. a new
317
+ # node has been added). If this happens, we likely want to grow
318
+ # the actor pool by adding more Actors to it.
319
+ def update_resources(f_stop: threading.Event) -> None:
320
+ """Periodically check if more actors can be added to the pool.
321
+
322
+ If so, extend the pool.
323
+ """
324
+ if not f_stop.is_set():
325
+ num_max_actors = pool_size_from_resources(client_resources)
326
+ if num_max_actors > pool.num_actors:
327
+ num_new = num_max_actors - pool.num_actors
328
+ log(
329
+ INFO, "The cluster expanded. Adding %s actors to the pool.", num_new
330
+ )
331
+ pool.add_actors_to_pool(num_actors=num_new)
332
+
333
+ threading.Timer(10, update_resources, [f_stop]).start()
334
+
335
+ update_resources(f_stop)
336
+
337
+ log(
338
+ INFO,
339
+ "Flower VCE: Creating %s with %s actors",
340
+ pool.__class__.__name__,
341
+ pool.num_actors,
342
+ )
343
+
344
+ # Register one RayClientProxy object for each client with the ClientManager
345
+ for node_id, partition_id in nodes_mapping.items():
346
+ client_proxy = RayActorClientProxy(
347
+ client_fn=client_fn,
348
+ node_id=node_id,
349
+ partition_id=partition_id,
350
+ num_partitions=num_clients,
351
+ actor_pool=pool,
352
+ )
353
+ initialized_server.client_manager().register(client=client_proxy)
354
+
355
+ hist = History()
356
+ # pylint: disable=broad-except
357
+ try:
358
+ # Start training
359
+ hist = run_fl(
360
+ server=initialized_server,
361
+ config=initialized_config,
362
+ )
363
+ except Exception as ex:
364
+ log(ERROR, ex)
365
+ log(ERROR, traceback.format_exc())
366
+ log(
367
+ ERROR,
368
+ "Your simulation crashed :(. This could be because of several reasons. "
369
+ "The most common are: "
370
+ "\n\t > Sometimes, issues in the simulation code itself can cause crashes. "
371
+ "It's always a good idea to double-check your code for any potential bugs "
372
+ "or inconsistencies that might be contributing to the problem. "
373
+ "For example: "
374
+ "\n\t\t - You might be using a class attribute in your clients that "
375
+ "hasn't been defined."
376
+ "\n\t\t - There could be an incorrect method call to a 3rd party library "
377
+ "(e.g., PyTorch)."
378
+ "\n\t\t - The return types of methods in your clients/strategies might be "
379
+ "incorrect."
380
+ "\n\t > Your system couldn't fit a single VirtualClient: try lowering "
381
+ "`client_resources`."
382
+ "\n\t > All the actors in your pool crashed. This could be because: "
383
+ "\n\t\t - You clients hit an out-of-memory (OOM) error and actors couldn't "
384
+ "recover from it. Try launching your simulation with more generous "
385
+ "`client_resources` setting (i.e. it seems %s is "
386
+ "not enough for your run). Use fewer concurrent actors. "
387
+ "\n\t\t - You were running a multi-node simulation and all worker nodes "
388
+ "disconnected. The head node might still be alive but cannot accommodate "
389
+ "any actor with resources: %s."
390
+ "\nTake a look at the Flower simulation examples for guidance "
391
+ "<https://flower.ai/docs/framework/how-to-run-simulations.html>.",
392
+ client_resources,
393
+ client_resources,
394
+ )
395
+ raise RuntimeError("Simulation crashed.") from ex
396
+
397
+ finally:
398
+ # Stop time monitoring resources in cluster
399
+ f_stop.set()
400
+ event(EventType.START_SIMULATION_LEAVE)
401
+
402
+ return hist
@@ -1,4 +1,4 @@
1
- # Copyright 2020 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2021 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -14,33 +14,23 @@
14
14
  # ==============================================================================
15
15
  """Ray-based Flower Actor and ActorPool implementation."""
16
16
 
17
- import asyncio
17
+
18
18
  import threading
19
- import traceback
20
19
  from abc import ABC
21
20
  from logging import DEBUG, ERROR, WARNING
22
- from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Type, Union
21
+ from typing import Any, Callable, Optional, Union
23
22
 
24
23
  import ray
25
24
  from ray import ObjectRef
26
25
  from ray.util.actor_pool import ActorPool
27
26
 
28
- from flwr.client.client_app import ClientApp, LoadClientAppError
27
+ from flwr.client.client_app import ClientApp, ClientAppException, LoadClientAppError
29
28
  from flwr.common import Context, Message
30
29
  from flwr.common.logger import log
31
30
 
32
31
  ClientAppFn = Callable[[], ClientApp]
33
32
 
34
33
 
35
- class ClientException(Exception):
36
- """Raised when client side logic crashes with an exception."""
37
-
38
- def __init__(self, message: str):
39
- div = ">" * 7
40
- self.message = "\n" + div + "A ClientException occurred." + message
41
- super().__init__(self.message)
42
-
43
-
44
34
  class VirtualClientEngineActor(ABC):
45
35
  """Abstract base class for VirtualClientEngine Actors."""
46
36
 
@@ -55,7 +45,7 @@ class VirtualClientEngineActor(ABC):
55
45
  message: Message,
56
46
  cid: str,
57
47
  context: Context,
58
- ) -> Tuple[str, Message, Context]:
48
+ ) -> tuple[str, Message, Context]:
59
49
  """Run a client run."""
60
50
  # Pass message through ClientApp and return a message
61
51
  # return also cid which is needed to ensure results
@@ -71,17 +61,7 @@ class VirtualClientEngineActor(ABC):
71
61
  raise load_ex
72
62
 
73
63
  except Exception as ex:
74
- client_trace = traceback.format_exc()
75
- mssg = (
76
- "\n\tSomething went wrong when running your client run."
77
- "\n\tClient "
78
- + cid
79
- + " crashed when the "
80
- + self.__class__.__name__
81
- + " was running its run."
82
- "\n\tException triggered on the client side: " + client_trace,
83
- )
84
- raise ClientException(str(mssg)) from ex
64
+ raise ClientAppException(str(ex)) from ex
85
65
 
86
66
  return cid, out_message, context
87
67
 
@@ -102,7 +82,7 @@ class ClientAppActor(VirtualClientEngineActor):
102
82
  on_actor_init_fn()
103
83
 
104
84
 
105
- def pool_size_from_resources(client_resources: Dict[str, Union[int, float]]) -> int:
85
+ def pool_size_from_resources(client_resources: dict[str, Union[int, float]]) -> int:
106
86
  """Calculate number of Actors that fit in the cluster.
107
87
 
108
88
  For this we consider the resources available on each node and those required per
@@ -145,14 +125,14 @@ def pool_size_from_resources(client_resources: Dict[str, Union[int, float]]) ->
145
125
  WARNING,
146
126
  "The ActorPool is empty. The system (CPUs=%s, GPUs=%s) "
147
127
  "does not meet the criteria to host at least one client with resources:"
148
- " %s. Lowering the `client_resources` could help.",
128
+ " %s. Lowering these resources could help.",
149
129
  num_cpus,
150
130
  num_gpus,
151
131
  client_resources,
152
132
  )
153
133
  raise ValueError(
154
134
  "ActorPool is empty. Stopping Simulation. "
155
- "Check 'client_resources' passed to `start_simulation`"
135
+ "Check `num_cpus` and/or `num_gpus` passed to the simulation engine"
156
136
  )
157
137
 
158
138
  return total_num_actors
@@ -183,9 +163,9 @@ class VirtualClientEngineActorPool(ActorPool):
183
163
 
184
164
  def __init__(
185
165
  self,
186
- create_actor_fn: Callable[[], Type[VirtualClientEngineActor]],
187
- client_resources: Dict[str, Union[int, float]],
188
- actor_list: Optional[List[Type[VirtualClientEngineActor]]] = None,
166
+ create_actor_fn: Callable[[], type[VirtualClientEngineActor]],
167
+ client_resources: dict[str, Union[int, float]],
168
+ actor_list: Optional[list[type[VirtualClientEngineActor]]] = None,
189
169
  ):
190
170
  self.client_resources = client_resources
191
171
  self.create_actor_fn = create_actor_fn
@@ -204,10 +184,10 @@ class VirtualClientEngineActorPool(ActorPool):
204
184
 
205
185
  # A dict that maps cid to another dict containing: a reference to the remote job
206
186
  # and its status (i.e. whether it is ready or not)
207
- self._cid_to_future: Dict[
208
- str, Dict[str, Union[bool, Optional[ObjectRef[Any]]]]
187
+ self._cid_to_future: dict[
188
+ str, dict[str, Union[bool, Optional[ObjectRef[Any]]]]
209
189
  ] = {}
210
- self.actor_to_remove: Set[str] = set() # a set
190
+ self.actor_to_remove: set[str] = set() # a set
211
191
  self.num_actors = len(actors)
212
192
 
213
193
  self.lock = threading.RLock()
@@ -231,7 +211,7 @@ class VirtualClientEngineActorPool(ActorPool):
231
211
  self._idle_actors.extend(new_actors)
232
212
  self.num_actors += num_actors
233
213
 
234
- def submit(self, fn: Any, value: Tuple[ClientAppFn, Message, str, Context]) -> None:
214
+ def submit(self, fn: Any, value: tuple[ClientAppFn, Message, str, Context]) -> None:
235
215
  """Take an idle actor and assign it to run a client app and Message.
236
216
 
237
217
  Submit a job to an actor by first removing it from the list of idle actors, then
@@ -241,7 +221,7 @@ class VirtualClientEngineActorPool(ActorPool):
241
221
  actor = self._idle_actors.pop()
242
222
  if self._check_and_remove_actor_from_pool(actor):
243
223
  future = fn(actor, app_fn, mssg, cid, context)
244
- future_key = tuple(future) if isinstance(future, List) else future
224
+ future_key = tuple(future) if isinstance(future, list) else future
245
225
  self._future_to_actor[future_key] = (self._next_task_index, actor, cid)
246
226
  self._next_task_index += 1
247
227
 
@@ -249,7 +229,7 @@ class VirtualClientEngineActorPool(ActorPool):
249
229
  self._cid_to_future[cid]["future"] = future_key
250
230
 
251
231
  def submit_client_job(
252
- self, actor_fn: Any, job: Tuple[ClientAppFn, Message, str, Context]
232
+ self, actor_fn: Any, job: tuple[ClientAppFn, Message, str, Context]
253
233
  ) -> None:
254
234
  """Submit a job while tracking client ids."""
255
235
  _, _, cid, _ = job
@@ -289,7 +269,7 @@ class VirtualClientEngineActorPool(ActorPool):
289
269
 
290
270
  return self._cid_to_future[cid]["ready"] # type: ignore
291
271
 
292
- def _fetch_future_result(self, cid: str) -> Tuple[Message, Context]:
272
+ def _fetch_future_result(self, cid: str) -> tuple[Message, Context]:
293
273
  """Fetch result and updated context for a VirtualClient from Object Store.
294
274
 
295
275
  The job submitted by the ClientProxy interfacing with client with cid=cid is
@@ -403,7 +383,7 @@ class VirtualClientEngineActorPool(ActorPool):
403
383
 
404
384
  def get_client_result(
405
385
  self, cid: str, timeout: Optional[float]
406
- ) -> Tuple[Message, Context]:
386
+ ) -> tuple[Message, Context]:
407
387
  """Get result from VirtualClient with specific cid."""
408
388
  # Loop until all jobs submitted to the pool are completed. Break early
409
389
  # if the result for the ClientProxy calling this method is ready
@@ -419,27 +399,19 @@ class VirtualClientEngineActorPool(ActorPool):
419
399
  return self._fetch_future_result(cid)
420
400
 
421
401
 
422
- def init_ray(*args: Any, **kwargs: Any) -> None:
423
- """Intialises Ray if not already initialised."""
424
- if not ray.is_initialized():
425
- ray.init(*args, **kwargs)
426
-
427
-
428
402
  class BasicActorPool:
429
403
  """A basic actor pool."""
430
404
 
431
405
  def __init__(
432
406
  self,
433
- actor_type: Type[VirtualClientEngineActor],
434
- client_resources: Dict[str, Union[int, float]],
435
- actor_kwargs: Dict[str, Any],
407
+ actor_type: type[VirtualClientEngineActor],
408
+ client_resources: dict[str, Union[int, float]],
409
+ actor_kwargs: dict[str, Any],
436
410
  ):
437
411
  self.client_resources = client_resources
438
412
 
439
413
  # Queue of idle actors
440
- self.pool: "asyncio.Queue[Type[VirtualClientEngineActor]]" = asyncio.Queue(
441
- maxsize=1024
442
- )
414
+ self.pool: list[VirtualClientEngineActor] = []
443
415
  self.num_actors = 0
444
416
 
445
417
  # Resolve arguments to pass during actor init
@@ -453,38 +425,37 @@ class BasicActorPool:
453
425
  # Figure out how many actors can be created given the cluster resources
454
426
  # and the resources the user indicates each VirtualClient will need
455
427
  self.actors_capacity = pool_size_from_resources(client_resources)
456
- self._future_to_actor: Dict[Any, Type[VirtualClientEngineActor]] = {}
428
+ self._future_to_actor: dict[Any, VirtualClientEngineActor] = {}
457
429
 
458
430
  def is_actor_available(self) -> bool:
459
431
  """Return true if there is an idle actor."""
460
- return self.pool.qsize() > 0
432
+ return len(self.pool) > 0
461
433
 
462
- async def add_actors_to_pool(self, num_actors: int) -> None:
434
+ def add_actors_to_pool(self, num_actors: int) -> None:
463
435
  """Add actors to the pool.
464
436
 
465
437
  This method may be executed also if new resources are added to your Ray cluster
466
438
  (e.g. you add a new node).
467
439
  """
468
440
  for _ in range(num_actors):
469
- await self.pool.put(self.create_actor_fn()) # type: ignore
441
+ self.pool.append(self.create_actor_fn()) # type: ignore
470
442
  self.num_actors += num_actors
471
443
 
472
- async def terminate_all_actors(self) -> None:
444
+ def terminate_all_actors(self) -> None:
473
445
  """Terminate actors in pool."""
474
446
  num_terminated = 0
475
- while self.pool.qsize():
476
- actor = await self.pool.get()
447
+ for actor in self.pool:
477
448
  actor.terminate.remote() # type: ignore
478
449
  num_terminated += 1
479
450
 
480
451
  log(DEBUG, "Terminated %i actors", num_terminated)
481
452
 
482
- async def submit(
483
- self, actor_fn: Any, job: Tuple[ClientAppFn, Message, str, Context]
453
+ def submit(
454
+ self, actor_fn: Any, job: tuple[ClientAppFn, Message, str, Context]
484
455
  ) -> Any:
485
456
  """On idle actor, submit job and return future."""
486
457
  # Remove idle actor from pool
487
- actor = await self.pool.get()
458
+ actor = self.pool.pop()
488
459
  # Submit job to actor
489
460
  app_fn, mssg, cid, context = job
490
461
  future = actor_fn(actor, app_fn, mssg, cid, context)
@@ -493,14 +464,18 @@ class BasicActorPool:
493
464
  self._future_to_actor[future] = actor
494
465
  return future
495
466
 
496
- async def fetch_result_and_return_actor_to_pool(
467
+ def add_actor_back_to_pool(self, future: Any) -> None:
468
+ """Ad actor assigned to run future back into the pool."""
469
+ actor = self._future_to_actor.pop(future)
470
+ self.pool.append(actor)
471
+
472
+ def fetch_result_and_return_actor_to_pool(
497
473
  self, future: Any
498
- ) -> Tuple[Message, Context]:
474
+ ) -> tuple[Message, Context]:
499
475
  """Pull result given a future and add actor back to pool."""
500
- # Get actor that ran job
501
- actor = self._future_to_actor.pop(future)
502
- await self.pool.put(actor)
503
476
  # Retrieve result for object store
504
477
  # Instead of doing ray.get(future) we await it
505
- _, out_mssg, updated_context = await future
478
+ _, out_mssg, updated_context = ray.get(future)
479
+ # Get actor that ran job
480
+ self.add_actor_back_to_pool(future)
506
481
  return out_mssg, updated_context