flwr-nightly 1.8.0.dev20240315__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.
- flwr/cli/app.py +16 -2
- flwr/cli/build.py +181 -0
- flwr/cli/cli_user_auth_interceptor.py +90 -0
- flwr/cli/config_utils.py +343 -0
- flwr/cli/example.py +4 -1
- flwr/cli/install.py +253 -0
- flwr/cli/log.py +182 -0
- flwr/{server/superlink/state → cli/login}/__init__.py +4 -10
- flwr/cli/login/login.py +88 -0
- flwr/cli/ls.py +327 -0
- flwr/cli/new/__init__.py +1 -0
- flwr/cli/new/new.py +210 -66
- flwr/cli/new/templates/app/.gitignore.tpl +163 -0
- flwr/cli/new/templates/app/LICENSE.tpl +202 -0
- flwr/cli/new/templates/app/README.baseline.md.tpl +127 -0
- flwr/cli/new/templates/app/README.flowertune.md.tpl +66 -0
- flwr/cli/new/templates/app/README.md.tpl +16 -32
- flwr/cli/new/templates/app/code/__init__.baseline.py.tpl +1 -0
- flwr/cli/new/templates/app/code/__init__.py.tpl +1 -1
- flwr/cli/new/templates/app/code/client.baseline.py.tpl +58 -0
- flwr/cli/new/templates/app/code/client.huggingface.py.tpl +55 -0
- flwr/cli/new/templates/app/code/client.jax.py.tpl +50 -0
- flwr/cli/new/templates/app/code/client.mlx.py.tpl +73 -0
- flwr/cli/new/templates/app/code/client.numpy.py.tpl +7 -7
- flwr/cli/new/templates/app/code/client.pytorch.py.tpl +30 -21
- flwr/cli/new/templates/app/code/client.sklearn.py.tpl +63 -0
- flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +57 -1
- flwr/cli/new/templates/app/code/dataset.baseline.py.tpl +36 -0
- flwr/cli/new/templates/app/code/flwr_tune/__init__.py +15 -0
- flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl +126 -0
- flwr/cli/new/templates/app/code/flwr_tune/dataset.py.tpl +87 -0
- flwr/cli/new/templates/app/code/flwr_tune/models.py.tpl +78 -0
- flwr/cli/new/templates/app/code/flwr_tune/server_app.py.tpl +94 -0
- flwr/cli/new/templates/app/code/flwr_tune/strategy.py.tpl +83 -0
- flwr/cli/new/templates/app/code/model.baseline.py.tpl +80 -0
- flwr/cli/new/templates/app/code/server.baseline.py.tpl +46 -0
- flwr/cli/new/templates/app/code/server.huggingface.py.tpl +38 -0
- flwr/cli/new/templates/app/code/server.jax.py.tpl +26 -0
- flwr/cli/new/templates/app/code/server.mlx.py.tpl +31 -0
- flwr/cli/new/templates/app/code/server.numpy.py.tpl +22 -9
- flwr/cli/new/templates/app/code/server.pytorch.py.tpl +21 -18
- flwr/cli/new/templates/app/code/server.sklearn.py.tpl +36 -0
- flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +29 -1
- flwr/cli/new/templates/app/code/strategy.baseline.py.tpl +1 -0
- flwr/cli/new/templates/app/code/task.huggingface.py.tpl +102 -0
- flwr/cli/new/templates/app/code/task.jax.py.tpl +57 -0
- flwr/cli/new/templates/app/code/task.mlx.py.tpl +102 -0
- flwr/cli/new/templates/app/code/task.numpy.py.tpl +7 -0
- flwr/cli/new/templates/app/code/task.pytorch.py.tpl +29 -24
- flwr/cli/new/templates/app/code/task.sklearn.py.tpl +67 -0
- flwr/cli/new/templates/app/code/task.tensorflow.py.tpl +53 -0
- flwr/cli/new/templates/app/code/utils.baseline.py.tpl +1 -0
- flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +138 -0
- flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +68 -0
- flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +46 -0
- flwr/cli/new/templates/app/pyproject.jax.toml.tpl +35 -0
- flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +39 -0
- flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +25 -12
- flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +29 -14
- flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +35 -0
- flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +29 -14
- flwr/cli/run/__init__.py +1 -0
- flwr/cli/run/run.py +212 -34
- flwr/cli/stop.py +130 -0
- flwr/cli/utils.py +240 -5
- flwr/client/__init__.py +3 -2
- flwr/client/app.py +432 -255
- flwr/client/client.py +1 -11
- flwr/client/client_app.py +74 -13
- flwr/client/clientapp/__init__.py +22 -0
- flwr/client/clientapp/app.py +259 -0
- flwr/client/clientapp/clientappio_servicer.py +244 -0
- flwr/client/clientapp/utils.py +115 -0
- flwr/client/dpfedavg_numpy_client.py +7 -8
- flwr/client/grpc_adapter_client/__init__.py +15 -0
- flwr/client/grpc_adapter_client/connection.py +98 -0
- flwr/client/grpc_client/connection.py +21 -7
- flwr/client/grpc_rere_client/__init__.py +1 -1
- flwr/client/grpc_rere_client/client_interceptor.py +176 -0
- flwr/client/grpc_rere_client/connection.py +163 -56
- flwr/client/grpc_rere_client/grpc_adapter.py +167 -0
- flwr/client/heartbeat.py +74 -0
- flwr/client/message_handler/__init__.py +1 -1
- flwr/client/message_handler/message_handler.py +10 -11
- flwr/client/mod/__init__.py +5 -5
- flwr/client/mod/centraldp_mods.py +4 -2
- flwr/client/mod/comms_mods.py +5 -4
- flwr/client/mod/localdp_mod.py +10 -5
- flwr/client/mod/secure_aggregation/__init__.py +1 -1
- flwr/client/mod/secure_aggregation/secaggplus_mod.py +26 -26
- flwr/client/mod/utils.py +2 -4
- flwr/client/nodestate/__init__.py +26 -0
- flwr/client/nodestate/in_memory_nodestate.py +38 -0
- flwr/client/nodestate/nodestate.py +31 -0
- flwr/client/nodestate/nodestate_factory.py +38 -0
- flwr/client/numpy_client.py +8 -31
- flwr/client/rest_client/__init__.py +1 -1
- flwr/client/rest_client/connection.py +199 -176
- flwr/client/run_info_store.py +112 -0
- flwr/client/supernode/__init__.py +24 -0
- flwr/client/supernode/app.py +321 -0
- flwr/client/typing.py +1 -0
- flwr/common/__init__.py +17 -11
- flwr/common/address.py +47 -3
- flwr/common/args.py +153 -0
- flwr/common/auth_plugin/__init__.py +24 -0
- flwr/common/auth_plugin/auth_plugin.py +121 -0
- flwr/common/config.py +243 -0
- flwr/common/constant.py +132 -1
- flwr/common/context.py +32 -2
- flwr/common/date.py +22 -4
- flwr/common/differential_privacy.py +2 -2
- flwr/common/dp.py +2 -4
- flwr/common/exit_handlers.py +3 -3
- flwr/common/grpc.py +164 -5
- flwr/common/logger.py +230 -12
- flwr/common/message.py +191 -106
- flwr/common/object_ref.py +179 -44
- flwr/common/pyproject.py +1 -0
- flwr/common/record/__init__.py +2 -1
- flwr/common/record/configsrecord.py +58 -18
- flwr/common/record/metricsrecord.py +57 -17
- flwr/common/record/parametersrecord.py +88 -20
- flwr/common/record/recordset.py +153 -30
- flwr/common/record/typeddict.py +30 -55
- flwr/common/recordset_compat.py +31 -12
- flwr/common/retry_invoker.py +123 -30
- flwr/common/secure_aggregation/__init__.py +1 -1
- flwr/common/secure_aggregation/crypto/__init__.py +1 -1
- flwr/common/secure_aggregation/crypto/shamir.py +11 -11
- flwr/common/secure_aggregation/crypto/symmetric_encryption.py +68 -4
- flwr/common/secure_aggregation/ndarrays_arithmetic.py +17 -17
- flwr/common/secure_aggregation/quantization.py +8 -8
- flwr/common/secure_aggregation/secaggplus_constants.py +1 -1
- flwr/common/secure_aggregation/secaggplus_utils.py +10 -12
- flwr/common/serde.py +298 -19
- flwr/common/telemetry.py +65 -29
- flwr/common/typing.py +120 -19
- flwr/common/version.py +17 -3
- flwr/proto/clientappio_pb2.py +45 -0
- flwr/proto/clientappio_pb2.pyi +132 -0
- flwr/proto/clientappio_pb2_grpc.py +135 -0
- flwr/proto/clientappio_pb2_grpc.pyi +53 -0
- flwr/proto/exec_pb2.py +62 -0
- flwr/proto/exec_pb2.pyi +212 -0
- flwr/proto/exec_pb2_grpc.py +237 -0
- flwr/proto/exec_pb2_grpc.pyi +93 -0
- flwr/proto/fab_pb2.py +31 -0
- flwr/proto/fab_pb2.pyi +65 -0
- flwr/proto/fab_pb2_grpc.py +4 -0
- flwr/proto/fab_pb2_grpc.pyi +4 -0
- flwr/proto/fleet_pb2.py +42 -23
- flwr/proto/fleet_pb2.pyi +123 -1
- flwr/proto/fleet_pb2_grpc.py +170 -0
- flwr/proto/fleet_pb2_grpc.pyi +61 -0
- flwr/proto/grpcadapter_pb2.py +32 -0
- flwr/proto/grpcadapter_pb2.pyi +43 -0
- flwr/proto/grpcadapter_pb2_grpc.py +66 -0
- flwr/proto/grpcadapter_pb2_grpc.pyi +24 -0
- flwr/proto/log_pb2.py +29 -0
- flwr/proto/log_pb2.pyi +39 -0
- flwr/proto/log_pb2_grpc.py +4 -0
- flwr/proto/log_pb2_grpc.pyi +4 -0
- flwr/proto/message_pb2.py +41 -0
- flwr/proto/message_pb2.pyi +128 -0
- flwr/proto/message_pb2_grpc.py +4 -0
- flwr/proto/message_pb2_grpc.pyi +4 -0
- flwr/proto/node_pb2.py +1 -1
- flwr/proto/recordset_pb2.py +35 -33
- flwr/proto/recordset_pb2.pyi +40 -14
- flwr/proto/run_pb2.py +64 -0
- flwr/proto/run_pb2.pyi +268 -0
- flwr/proto/run_pb2_grpc.py +4 -0
- flwr/proto/run_pb2_grpc.pyi +4 -0
- flwr/proto/serverappio_pb2.py +52 -0
- flwr/proto/{driver_pb2.pyi → serverappio_pb2.pyi} +62 -20
- flwr/proto/serverappio_pb2_grpc.py +410 -0
- flwr/proto/serverappio_pb2_grpc.pyi +160 -0
- flwr/proto/simulationio_pb2.py +38 -0
- flwr/proto/simulationio_pb2.pyi +65 -0
- flwr/proto/simulationio_pb2_grpc.py +239 -0
- flwr/proto/simulationio_pb2_grpc.pyi +94 -0
- flwr/proto/task_pb2.py +7 -8
- flwr/proto/task_pb2.pyi +8 -5
- flwr/proto/transport_pb2.py +8 -8
- flwr/proto/transport_pb2.pyi +9 -6
- flwr/server/__init__.py +2 -10
- flwr/server/app.py +579 -402
- flwr/server/client_manager.py +8 -6
- flwr/server/compat/app.py +6 -62
- flwr/server/compat/app_utils.py +14 -8
- flwr/server/compat/driver_client_proxy.py +25 -58
- flwr/server/compat/legacy_context.py +5 -4
- flwr/server/driver/__init__.py +2 -0
- flwr/server/driver/driver.py +36 -131
- flwr/server/driver/grpc_driver.py +217 -81
- flwr/server/driver/inmemory_driver.py +182 -0
- flwr/server/history.py +28 -29
- flwr/server/run_serverapp.py +15 -126
- flwr/server/server.py +50 -44
- flwr/server/server_app.py +59 -10
- flwr/server/serverapp/__init__.py +22 -0
- flwr/server/serverapp/app.py +256 -0
- flwr/server/serverapp_components.py +52 -0
- flwr/server/strategy/__init__.py +2 -2
- flwr/server/strategy/aggregate.py +37 -23
- flwr/server/strategy/bulyan.py +9 -9
- flwr/server/strategy/dp_adaptive_clipping.py +25 -25
- flwr/server/strategy/dp_fixed_clipping.py +23 -22
- flwr/server/strategy/dpfedavg_adaptive.py +8 -8
- flwr/server/strategy/dpfedavg_fixed.py +13 -12
- flwr/server/strategy/fault_tolerant_fedavg.py +11 -11
- flwr/server/strategy/fedadagrad.py +9 -9
- flwr/server/strategy/fedadam.py +20 -10
- flwr/server/strategy/fedavg.py +16 -16
- flwr/server/strategy/fedavg_android.py +17 -17
- flwr/server/strategy/fedavgm.py +9 -9
- flwr/server/strategy/fedmedian.py +5 -5
- flwr/server/strategy/fedopt.py +6 -6
- flwr/server/strategy/fedprox.py +7 -7
- flwr/server/strategy/fedtrimmedavg.py +8 -8
- flwr/server/strategy/fedxgb_bagging.py +12 -12
- flwr/server/strategy/fedxgb_cyclic.py +10 -10
- flwr/server/strategy/fedxgb_nn_avg.py +6 -6
- flwr/server/strategy/fedyogi.py +9 -9
- flwr/server/strategy/krum.py +9 -9
- flwr/server/strategy/qfedavg.py +16 -16
- flwr/server/strategy/strategy.py +10 -10
- flwr/server/superlink/driver/__init__.py +2 -2
- flwr/server/superlink/driver/serverappio_grpc.py +61 -0
- flwr/server/superlink/driver/serverappio_servicer.py +363 -0
- flwr/server/superlink/ffs/__init__.py +24 -0
- flwr/server/superlink/ffs/disk_ffs.py +108 -0
- flwr/server/superlink/ffs/ffs.py +79 -0
- flwr/server/superlink/ffs/ffs_factory.py +47 -0
- flwr/server/superlink/fleet/__init__.py +1 -1
- flwr/server/superlink/fleet/grpc_adapter/__init__.py +15 -0
- flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +162 -0
- flwr/server/superlink/fleet/grpc_bidi/__init__.py +1 -1
- flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +4 -2
- flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py +3 -2
- flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py +1 -1
- flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +5 -154
- flwr/server/superlink/fleet/grpc_rere/__init__.py +1 -1
- flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +120 -13
- flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +228 -0
- flwr/server/superlink/fleet/message_handler/__init__.py +1 -1
- flwr/server/superlink/fleet/message_handler/message_handler.py +153 -9
- flwr/server/superlink/fleet/rest_rere/__init__.py +1 -1
- flwr/server/superlink/fleet/rest_rere/rest_api.py +119 -81
- flwr/server/superlink/fleet/vce/__init__.py +1 -0
- flwr/server/superlink/fleet/vce/backend/__init__.py +4 -4
- flwr/server/superlink/fleet/vce/backend/backend.py +8 -9
- flwr/server/superlink/fleet/vce/backend/raybackend.py +87 -68
- flwr/server/superlink/fleet/vce/vce_api.py +208 -146
- flwr/server/superlink/linkstate/__init__.py +28 -0
- flwr/server/superlink/linkstate/in_memory_linkstate.py +581 -0
- flwr/server/superlink/linkstate/linkstate.py +389 -0
- flwr/server/superlink/{state/state_factory.py → linkstate/linkstate_factory.py} +19 -10
- flwr/server/superlink/linkstate/sqlite_linkstate.py +1236 -0
- flwr/server/superlink/linkstate/utils.py +389 -0
- flwr/server/superlink/simulation/__init__.py +15 -0
- flwr/server/superlink/simulation/simulationio_grpc.py +65 -0
- flwr/server/superlink/simulation/simulationio_servicer.py +186 -0
- flwr/server/superlink/utils.py +65 -0
- flwr/server/typing.py +2 -0
- flwr/server/utils/__init__.py +1 -1
- flwr/server/utils/tensorboard.py +5 -5
- flwr/server/utils/validator.py +31 -11
- flwr/server/workflow/default_workflows.py +70 -26
- flwr/server/workflow/secure_aggregation/secagg_workflow.py +1 -0
- flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +40 -27
- flwr/simulation/__init__.py +12 -5
- flwr/simulation/app.py +247 -315
- flwr/simulation/legacy_app.py +402 -0
- flwr/simulation/ray_transport/__init__.py +1 -1
- flwr/simulation/ray_transport/ray_actor.py +42 -67
- flwr/simulation/ray_transport/ray_client_proxy.py +37 -17
- flwr/simulation/ray_transport/utils.py +1 -0
- flwr/simulation/run_simulation.py +306 -163
- flwr/simulation/simulationio_connection.py +89 -0
- flwr/superexec/__init__.py +15 -0
- flwr/superexec/app.py +59 -0
- flwr/superexec/deployment.py +188 -0
- flwr/superexec/exec_grpc.py +80 -0
- flwr/superexec/exec_servicer.py +231 -0
- flwr/superexec/exec_user_auth_interceptor.py +101 -0
- flwr/superexec/executor.py +96 -0
- flwr/superexec/simulation.py +124 -0
- {flwr_nightly-1.8.0.dev20240315.dist-info → flwr_nightly-1.15.0.dev20250114.dist-info}/METADATA +33 -26
- flwr_nightly-1.15.0.dev20250114.dist-info/RECORD +328 -0
- flwr_nightly-1.15.0.dev20250114.dist-info/entry_points.txt +12 -0
- flwr/cli/flower_toml.py +0 -140
- flwr/cli/new/templates/app/flower.toml.tpl +0 -13
- flwr/cli/new/templates/app/requirements.numpy.txt.tpl +0 -2
- flwr/cli/new/templates/app/requirements.pytorch.txt.tpl +0 -4
- flwr/cli/new/templates/app/requirements.tensorflow.txt.tpl +0 -4
- flwr/client/node_state.py +0 -48
- flwr/client/node_state_tests.py +0 -65
- flwr/proto/driver_pb2.py +0 -44
- flwr/proto/driver_pb2_grpc.py +0 -169
- flwr/proto/driver_pb2_grpc.pyi +0 -66
- flwr/server/superlink/driver/driver_grpc.py +0 -54
- flwr/server/superlink/driver/driver_servicer.py +0 -129
- flwr/server/superlink/state/in_memory_state.py +0 -230
- flwr/server/superlink/state/sqlite_state.py +0 -630
- flwr/server/superlink/state/state.py +0 -154
- flwr_nightly-1.8.0.dev20240315.dist-info/RECORD +0 -211
- flwr_nightly-1.8.0.dev20240315.dist-info/entry_points.txt +0 -9
- {flwr_nightly-1.8.0.dev20240315.dist-info → flwr_nightly-1.15.0.dev20250114.dist-info}/LICENSE +0 -0
- {flwr_nightly-1.8.0.dev20240315.dist-info → flwr_nightly-1.15.0.dev20250114.dist-info}/WHEEL +0 -0
@@ -14,61 +14,144 @@
|
|
14
14
|
# ==============================================================================
|
15
15
|
"""Flower Simulation."""
|
16
16
|
|
17
|
+
|
17
18
|
import argparse
|
18
19
|
import asyncio
|
19
20
|
import json
|
20
21
|
import logging
|
22
|
+
import sys
|
21
23
|
import threading
|
22
24
|
import traceback
|
23
25
|
from logging import DEBUG, ERROR, INFO, WARNING
|
24
|
-
from
|
25
|
-
from
|
26
|
-
|
27
|
-
import grpc
|
26
|
+
from pathlib import Path
|
27
|
+
from queue import Empty, Queue
|
28
|
+
from typing import Any, Optional
|
28
29
|
|
30
|
+
from flwr.cli.config_utils import load_and_validate
|
31
|
+
from flwr.cli.utils import get_sha256_hash
|
29
32
|
from flwr.client import ClientApp
|
30
|
-
from flwr.common import EventType, event, log
|
31
|
-
from flwr.common.
|
32
|
-
from flwr.
|
33
|
-
from flwr.
|
33
|
+
from flwr.common import Context, EventType, RecordSet, event, log, now
|
34
|
+
from flwr.common.config import get_fused_config_from_dir, parse_config_args
|
35
|
+
from flwr.common.constant import RUN_ID_NUM_BYTES, Status
|
36
|
+
from flwr.common.logger import (
|
37
|
+
set_logger_propagation,
|
38
|
+
update_console_handler,
|
39
|
+
warn_deprecated_feature_with_example,
|
40
|
+
)
|
41
|
+
from flwr.common.typing import Run, RunStatus, UserConfig
|
42
|
+
from flwr.server.driver import Driver, InMemoryDriver
|
43
|
+
from flwr.server.run_serverapp import run as _run
|
34
44
|
from flwr.server.server_app import ServerApp
|
35
|
-
from flwr.server.superlink.driver.driver_grpc import run_driver_api_grpc
|
36
45
|
from flwr.server.superlink.fleet import vce
|
37
|
-
from flwr.server.superlink.
|
46
|
+
from flwr.server.superlink.fleet.vce.backend.backend import BackendConfig
|
47
|
+
from flwr.server.superlink.linkstate import LinkStateFactory
|
48
|
+
from flwr.server.superlink.linkstate.in_memory_linkstate import RunRecord
|
49
|
+
from flwr.server.superlink.linkstate.utils import generate_rand_int_from_bytes
|
38
50
|
from flwr.simulation.ray_transport.utils import (
|
39
51
|
enable_tf_gpu_growth as enable_gpu_growth,
|
40
52
|
)
|
41
53
|
|
42
54
|
|
55
|
+
def _replace_keys(d: Any, match: str, target: str) -> Any:
|
56
|
+
if isinstance(d, dict):
|
57
|
+
return {
|
58
|
+
k.replace(match, target): _replace_keys(v, match, target)
|
59
|
+
for k, v in d.items()
|
60
|
+
}
|
61
|
+
if isinstance(d, list):
|
62
|
+
return [_replace_keys(i, match, target) for i in d]
|
63
|
+
return d
|
64
|
+
|
65
|
+
|
43
66
|
# Entry point from CLI
|
67
|
+
# pylint: disable=too-many-locals
|
44
68
|
def run_simulation_from_cli() -> None:
|
45
69
|
"""Run Simulation Engine from the CLI."""
|
46
70
|
args = _parse_args_run_simulation().parse_args()
|
47
71
|
|
72
|
+
event(
|
73
|
+
EventType.CLI_FLOWER_SIMULATION_ENTER,
|
74
|
+
event_details={"backend": args.backend, "num-supernodes": args.num_supernodes},
|
75
|
+
)
|
76
|
+
|
77
|
+
if args.enable_tf_gpu_growth:
|
78
|
+
warn_deprecated_feature_with_example(
|
79
|
+
"Passing `--enable-tf-gpu-growth` is deprecated.",
|
80
|
+
example_message="Instead, set the `TF_FORCE_GPU_ALLOW_GROWTH` environmnet "
|
81
|
+
"variable to true.",
|
82
|
+
code_example='TF_FORCE_GPU_ALLOW_GROWTH="true" flower-simulation <...>',
|
83
|
+
)
|
84
|
+
|
48
85
|
# Load JSON config
|
49
86
|
backend_config_dict = json.loads(args.backend_config)
|
50
87
|
|
51
|
-
|
52
|
-
|
53
|
-
|
88
|
+
if backend_config_dict:
|
89
|
+
# Backend config internally operates with `_` not with `-`
|
90
|
+
backend_config_dict = _replace_keys(backend_config_dict, match="-", target="_")
|
91
|
+
log(DEBUG, "backend_config_dict: %s", backend_config_dict)
|
92
|
+
|
93
|
+
run_id = (
|
94
|
+
generate_rand_int_from_bytes(RUN_ID_NUM_BYTES)
|
95
|
+
if args.run_id is None
|
96
|
+
else args.run_id
|
97
|
+
)
|
98
|
+
|
99
|
+
app_path = Path(args.app)
|
100
|
+
if not app_path.is_dir():
|
101
|
+
log(ERROR, "--app is not a directory")
|
102
|
+
sys.exit("Simulation Engine cannot start.")
|
103
|
+
|
104
|
+
# Load pyproject.toml
|
105
|
+
config, errors, warnings = load_and_validate(
|
106
|
+
app_path / "pyproject.toml", check_module=False
|
107
|
+
)
|
108
|
+
if errors:
|
109
|
+
raise ValueError(errors)
|
110
|
+
|
111
|
+
if warnings:
|
112
|
+
log(WARNING, warnings)
|
113
|
+
|
114
|
+
if config is None:
|
115
|
+
raise ValueError("Config extracted from FAB's pyproject.toml is not valid")
|
116
|
+
|
117
|
+
# Get ClientApp and SeverApp components
|
118
|
+
app_components = config["tool"]["flwr"]["app"]["components"]
|
119
|
+
client_app_attr = app_components["clientapp"]
|
120
|
+
server_app_attr = app_components["serverapp"]
|
121
|
+
|
122
|
+
override_config = parse_config_args(
|
123
|
+
[args.run_config] if args.run_config else args.run_config
|
124
|
+
)
|
125
|
+
fused_config = get_fused_config_from_dir(app_path, override_config)
|
126
|
+
|
127
|
+
# Create run
|
128
|
+
run = Run.create_empty(run_id)
|
129
|
+
run.override_config = override_config
|
130
|
+
|
131
|
+
_ = _run_simulation(
|
132
|
+
server_app_attr=server_app_attr,
|
133
|
+
client_app_attr=client_app_attr,
|
54
134
|
num_supernodes=args.num_supernodes,
|
55
135
|
backend_name=args.backend,
|
56
136
|
backend_config=backend_config_dict,
|
57
|
-
app_dir=args.
|
58
|
-
|
137
|
+
app_dir=args.app,
|
138
|
+
run=run,
|
59
139
|
enable_tf_gpu_growth=args.enable_tf_gpu_growth,
|
60
140
|
verbose_logging=args.verbose,
|
141
|
+
server_app_run_config=fused_config,
|
142
|
+
is_app=True,
|
143
|
+
exit_event=EventType.CLI_FLOWER_SIMULATION_LEAVE,
|
61
144
|
)
|
62
145
|
|
63
146
|
|
64
147
|
# Entry point from Python session (script or notebook)
|
65
|
-
# pylint: disable=too-many-arguments
|
148
|
+
# pylint: disable=too-many-arguments,too-many-positional-arguments
|
66
149
|
def run_simulation(
|
67
150
|
server_app: ServerApp,
|
68
151
|
client_app: ClientApp,
|
69
152
|
num_supernodes: int,
|
70
153
|
backend_name: str = "ray",
|
71
|
-
backend_config: Optional[
|
154
|
+
backend_config: Optional[BackendConfig] = None,
|
72
155
|
enable_tf_gpu_growth: bool = False,
|
73
156
|
verbose_logging: bool = False,
|
74
157
|
) -> None:
|
@@ -85,16 +168,18 @@ def run_simulation(
|
|
85
168
|
messages sent by the `ServerApp`.
|
86
169
|
|
87
170
|
num_supernodes : int
|
88
|
-
Number of nodes that run a ClientApp. They can be sampled by a
|
89
|
-
|
90
|
-
should perform.
|
171
|
+
Number of nodes that run a ClientApp. They can be sampled by a Driver in the
|
172
|
+
ServerApp and receive a Message describing what the ClientApp should perform.
|
91
173
|
|
92
174
|
backend_name : str (default: ray)
|
93
175
|
A simulation backend that runs `ClientApp`s.
|
94
176
|
|
95
|
-
backend_config : Optional[
|
96
|
-
'A dictionary
|
97
|
-
backend.
|
177
|
+
backend_config : Optional[BackendConfig]
|
178
|
+
'A dictionary to configure a backend. Separate dictionaries to configure
|
179
|
+
different elements of backend. Supported top-level keys are `init_args`
|
180
|
+
for values parsed to initialisation of backend, `client_resources`
|
181
|
+
to define the resources for clients, and `actor` to define the actor
|
182
|
+
parameters. Values supported in <value> are those included by
|
98
183
|
`flwr.common.typing.ConfigsRecordValues`.
|
99
184
|
|
100
185
|
enable_tf_gpu_growth : bool (default: False)
|
@@ -106,10 +191,24 @@ def run_simulation(
|
|
106
191
|
works in the TensorFlow documentation: https://www.tensorflow.org/api/stable.
|
107
192
|
|
108
193
|
verbose_logging : bool (default: False)
|
109
|
-
When
|
194
|
+
When disabled, only INFO, WARNING and ERROR log messages will be shown. If
|
110
195
|
enabled, DEBUG-level logs will be displayed.
|
111
196
|
"""
|
112
|
-
|
197
|
+
event(
|
198
|
+
EventType.PYTHON_API_RUN_SIMULATION_ENTER,
|
199
|
+
event_details={"backend": backend_name, "num-supernodes": num_supernodes},
|
200
|
+
)
|
201
|
+
|
202
|
+
if enable_tf_gpu_growth:
|
203
|
+
warn_deprecated_feature_with_example(
|
204
|
+
"Passing `enable_tf_gpu_growth=True` is deprecated.",
|
205
|
+
example_message="Instead, set the `TF_FORCE_GPU_ALLOW_GROWTH` environment "
|
206
|
+
"variable to true.",
|
207
|
+
code_example='import os;os.environ["TF_FORCE_GPU_ALLOW_GROWTH"]="true"'
|
208
|
+
"\n\tflwr.simulation.run_simulationt(...)",
|
209
|
+
)
|
210
|
+
|
211
|
+
_ = _run_simulation(
|
113
212
|
num_supernodes=num_supernodes,
|
114
213
|
client_app=client_app,
|
115
214
|
server_app=server_app,
|
@@ -117,110 +216,157 @@ def run_simulation(
|
|
117
216
|
backend_config=backend_config,
|
118
217
|
enable_tf_gpu_growth=enable_tf_gpu_growth,
|
119
218
|
verbose_logging=verbose_logging,
|
219
|
+
exit_event=EventType.PYTHON_API_RUN_SIMULATION_LEAVE,
|
120
220
|
)
|
121
221
|
|
122
222
|
|
123
|
-
# pylint: disable=too-many-arguments
|
223
|
+
# pylint: disable=too-many-arguments,too-many-positional-arguments
|
124
224
|
def run_serverapp_th(
|
125
225
|
server_app_attr: Optional[str],
|
126
226
|
server_app: Optional[ServerApp],
|
227
|
+
server_app_run_config: UserConfig,
|
127
228
|
driver: Driver,
|
128
229
|
app_dir: str,
|
129
|
-
f_stop:
|
230
|
+
f_stop: threading.Event,
|
231
|
+
has_exception: threading.Event,
|
130
232
|
enable_tf_gpu_growth: bool,
|
131
|
-
|
233
|
+
run_id: int,
|
234
|
+
ctx_queue: "Queue[Context]",
|
132
235
|
) -> threading.Thread:
|
133
236
|
"""Run SeverApp in a thread."""
|
134
237
|
|
135
|
-
def server_th_with_start_checks(
|
136
|
-
tf_gpu_growth: bool,
|
238
|
+
def server_th_with_start_checks(
|
239
|
+
tf_gpu_growth: bool,
|
240
|
+
stop_event: threading.Event,
|
241
|
+
exception_event: threading.Event,
|
242
|
+
_driver: Driver,
|
243
|
+
_server_app_dir: str,
|
244
|
+
_server_app_run_config: UserConfig,
|
245
|
+
_server_app_attr: Optional[str],
|
246
|
+
_server_app: Optional[ServerApp],
|
247
|
+
_ctx_queue: "Queue[Context]",
|
137
248
|
) -> None:
|
138
|
-
"""Run SeverApp, after check if GPU memory
|
249
|
+
"""Run SeverApp, after check if GPU memory growth has to be set.
|
139
250
|
|
140
251
|
Upon exception, trigger stop event for Simulation Engine.
|
141
252
|
"""
|
142
253
|
try:
|
143
254
|
if tf_gpu_growth:
|
144
|
-
log(INFO, "Enabling GPU growth for Tensorflow on the
|
255
|
+
log(INFO, "Enabling GPU growth for Tensorflow on the server thread.")
|
145
256
|
enable_gpu_growth()
|
146
257
|
|
258
|
+
# Initialize Context
|
259
|
+
context = Context(
|
260
|
+
run_id=run_id,
|
261
|
+
node_id=0,
|
262
|
+
node_config={},
|
263
|
+
state=RecordSet(),
|
264
|
+
run_config=_server_app_run_config,
|
265
|
+
)
|
266
|
+
|
147
267
|
# Run ServerApp
|
148
|
-
|
268
|
+
updated_context = _run(
|
269
|
+
driver=_driver,
|
270
|
+
context=context,
|
271
|
+
server_app_dir=_server_app_dir,
|
272
|
+
server_app_attr=_server_app_attr,
|
273
|
+
loaded_server_app=_server_app,
|
274
|
+
)
|
275
|
+
_ctx_queue.put(updated_context)
|
149
276
|
except Exception as ex: # pylint: disable=broad-exception-caught
|
150
277
|
log(ERROR, "ServerApp thread raised an exception: %s", ex)
|
151
278
|
log(ERROR, traceback.format_exc())
|
279
|
+
exception_event.set()
|
280
|
+
raise
|
152
281
|
finally:
|
153
282
|
log(DEBUG, "ServerApp finished running.")
|
154
283
|
# Upon completion, trigger stop event if one was passed
|
155
284
|
if stop_event is not None:
|
156
285
|
stop_event.set()
|
157
|
-
log(
|
286
|
+
log(DEBUG, "Triggered stop event for Simulation Engine.")
|
158
287
|
|
159
288
|
serverapp_th = threading.Thread(
|
160
289
|
target=server_th_with_start_checks,
|
161
|
-
args=(
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
290
|
+
args=(
|
291
|
+
enable_tf_gpu_growth,
|
292
|
+
f_stop,
|
293
|
+
has_exception,
|
294
|
+
driver,
|
295
|
+
app_dir,
|
296
|
+
server_app_run_config,
|
297
|
+
server_app_attr,
|
298
|
+
server_app,
|
299
|
+
ctx_queue,
|
300
|
+
),
|
168
301
|
)
|
169
|
-
sleep(delay_launch)
|
170
302
|
serverapp_th.start()
|
171
303
|
return serverapp_th
|
172
304
|
|
173
305
|
|
174
|
-
# pylint: disable=too-many-locals
|
306
|
+
# pylint: disable=too-many-locals,too-many-positional-arguments
|
175
307
|
def _main_loop(
|
176
308
|
num_supernodes: int,
|
177
309
|
backend_name: str,
|
178
310
|
backend_config_stream: str,
|
179
|
-
driver_api_address: str,
|
180
311
|
app_dir: str,
|
312
|
+
is_app: bool,
|
181
313
|
enable_tf_gpu_growth: bool,
|
314
|
+
run: Run,
|
315
|
+
exit_event: EventType,
|
316
|
+
flwr_dir: Optional[str] = None,
|
182
317
|
client_app: Optional[ClientApp] = None,
|
183
318
|
client_app_attr: Optional[str] = None,
|
184
319
|
server_app: Optional[ServerApp] = None,
|
185
320
|
server_app_attr: Optional[str] = None,
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
Everything runs on the main thread or a separate one, depening on whether the main
|
190
|
-
thread already contains a running Asyncio event loop. This is the case if running
|
191
|
-
the Simulation Engine on a Jupyter/Colab notebook.
|
192
|
-
"""
|
321
|
+
server_app_run_config: Optional[UserConfig] = None,
|
322
|
+
) -> Context:
|
323
|
+
"""Start ServerApp on a separate thread, then launch Simulation Engine."""
|
193
324
|
# Initialize StateFactory
|
194
|
-
state_factory =
|
195
|
-
|
196
|
-
# Start Driver API
|
197
|
-
driver_server: grpc.Server = run_driver_api_grpc(
|
198
|
-
address=driver_api_address,
|
199
|
-
state_factory=state_factory,
|
200
|
-
certificates=None,
|
201
|
-
)
|
325
|
+
state_factory = LinkStateFactory(":flwr-in-memory-state:")
|
202
326
|
|
203
|
-
f_stop =
|
327
|
+
f_stop = threading.Event()
|
328
|
+
# A Threading event to indicate if an exception was raised in the ServerApp thread
|
329
|
+
server_app_thread_has_exception = threading.Event()
|
204
330
|
serverapp_th = None
|
331
|
+
success = True
|
332
|
+
updated_context = Context(
|
333
|
+
run_id=run.run_id,
|
334
|
+
node_id=0,
|
335
|
+
node_config=UserConfig(),
|
336
|
+
state=RecordSet(),
|
337
|
+
run_config=UserConfig(),
|
338
|
+
)
|
205
339
|
try:
|
340
|
+
# Register run
|
341
|
+
log(DEBUG, "Pre-registering run with id %s", run.run_id)
|
342
|
+
run.status = RunStatus(Status.RUNNING, "", "")
|
343
|
+
run.starting_at = now().isoformat()
|
344
|
+
run.running_at = run.starting_at
|
345
|
+
state_factory.state().run_ids[run.run_id] = RunRecord(run=run) # type: ignore
|
346
|
+
|
347
|
+
if server_app_run_config is None:
|
348
|
+
server_app_run_config = {}
|
349
|
+
|
206
350
|
# Initialize Driver
|
207
|
-
driver =
|
208
|
-
|
209
|
-
|
210
|
-
)
|
351
|
+
driver = InMemoryDriver(state_factory=state_factory)
|
352
|
+
driver.set_run(run_id=run.run_id)
|
353
|
+
output_context_queue: Queue[Context] = Queue()
|
211
354
|
|
212
355
|
# Get and run ServerApp thread
|
213
356
|
serverapp_th = run_serverapp_th(
|
214
357
|
server_app_attr=server_app_attr,
|
215
358
|
server_app=server_app,
|
359
|
+
server_app_run_config=server_app_run_config,
|
216
360
|
driver=driver,
|
217
361
|
app_dir=app_dir,
|
218
362
|
f_stop=f_stop,
|
363
|
+
has_exception=server_app_thread_has_exception,
|
219
364
|
enable_tf_gpu_growth=enable_tf_gpu_growth,
|
365
|
+
run_id=run.run_id,
|
366
|
+
ctx_queue=output_context_queue,
|
220
367
|
)
|
221
368
|
|
222
|
-
#
|
223
|
-
event(EventType.RUN_SUPERLINK_ENTER)
|
369
|
+
# Start Simulation Engine
|
224
370
|
vce.start_vce(
|
225
371
|
num_supernodes=num_supernodes,
|
226
372
|
client_app_attr=client_app_attr,
|
@@ -228,149 +374,141 @@ def _main_loop(
|
|
228
374
|
backend_name=backend_name,
|
229
375
|
backend_config_json_stream=backend_config_stream,
|
230
376
|
app_dir=app_dir,
|
377
|
+
is_app=is_app,
|
231
378
|
state_factory=state_factory,
|
232
379
|
f_stop=f_stop,
|
380
|
+
run=run,
|
381
|
+
flwr_dir=flwr_dir,
|
233
382
|
)
|
234
383
|
|
384
|
+
updated_context = output_context_queue.get(timeout=3)
|
385
|
+
|
386
|
+
except Empty:
|
387
|
+
log(DEBUG, "Queue timeout. No context received.")
|
388
|
+
|
235
389
|
except Exception as ex:
|
236
390
|
log(ERROR, "An exception occurred !! %s", ex)
|
237
391
|
log(ERROR, traceback.format_exc())
|
392
|
+
success = False
|
238
393
|
raise RuntimeError("An error was encountered. Ending simulation.") from ex
|
239
394
|
|
240
395
|
finally:
|
241
|
-
# Stop Driver
|
242
|
-
driver_server.stop(grace=0)
|
243
|
-
driver.close()
|
244
396
|
# Trigger stop event
|
245
397
|
f_stop.set()
|
246
|
-
|
247
|
-
|
398
|
+
event(
|
399
|
+
exit_event,
|
400
|
+
event_details={
|
401
|
+
"run-id-hash": get_sha256_hash(run.run_id),
|
402
|
+
"success": success,
|
403
|
+
},
|
404
|
+
)
|
248
405
|
if serverapp_th:
|
249
406
|
serverapp_th.join()
|
407
|
+
if server_app_thread_has_exception.is_set():
|
408
|
+
raise RuntimeError("Exception in ServerApp thread")
|
250
409
|
|
251
|
-
log(
|
410
|
+
log(DEBUG, "Stopping Simulation Engine now.")
|
411
|
+
return updated_context
|
252
412
|
|
253
413
|
|
254
|
-
# pylint: disable=too-many-arguments,too-many-locals
|
414
|
+
# pylint: disable=too-many-arguments,too-many-locals,too-many-positional-arguments
|
255
415
|
def _run_simulation(
|
256
416
|
num_supernodes: int,
|
417
|
+
exit_event: EventType,
|
257
418
|
client_app: Optional[ClientApp] = None,
|
258
419
|
server_app: Optional[ServerApp] = None,
|
259
420
|
backend_name: str = "ray",
|
260
|
-
backend_config: Optional[
|
421
|
+
backend_config: Optional[BackendConfig] = None,
|
261
422
|
client_app_attr: Optional[str] = None,
|
262
423
|
server_app_attr: Optional[str] = None,
|
424
|
+
server_app_run_config: Optional[UserConfig] = None,
|
263
425
|
app_dir: str = "",
|
264
|
-
|
426
|
+
flwr_dir: Optional[str] = None,
|
427
|
+
run: Optional[Run] = None,
|
265
428
|
enable_tf_gpu_growth: bool = False,
|
266
429
|
verbose_logging: bool = False,
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
num_supernodes : int
|
273
|
-
Number of nodes that run a ClientApp. They can be sampled by a
|
274
|
-
Driver in the ServerApp and receive a Message describing what the ClientApp
|
275
|
-
should perform.
|
276
|
-
|
277
|
-
client_app : Optional[ClientApp]
|
278
|
-
The `ClientApp` to be executed by each of the `SuperNodes`. It will receive
|
279
|
-
messages sent by the `ServerApp`.
|
280
|
-
|
281
|
-
server_app : Optional[ServerApp]
|
282
|
-
The `ServerApp` to be executed.
|
283
|
-
|
284
|
-
backend_name : str (default: ray)
|
285
|
-
A simulation backend that runs `ClientApp`s.
|
286
|
-
|
287
|
-
backend_config : Optional[Dict[str, ConfigsRecordValues]]
|
288
|
-
'A dictionary, e.g {"<keyA>":<value>, "<keyB>":<value>} to configure a
|
289
|
-
backend. Values supported in <value> are those included by
|
290
|
-
`flwr.common.typing.ConfigsRecordValues`.
|
291
|
-
|
292
|
-
client_app_attr : str
|
293
|
-
A path to a `ClientApp` module to be loaded: For example: `client:app` or
|
294
|
-
`project.package.module:wrapper.app`."
|
430
|
+
is_app: bool = False,
|
431
|
+
) -> Context:
|
432
|
+
"""Launch the Simulation Engine."""
|
433
|
+
if backend_config is None:
|
434
|
+
backend_config = {}
|
295
435
|
|
296
|
-
|
297
|
-
|
298
|
-
`project.package.module:wrapper.app`."
|
436
|
+
if "init_args" not in backend_config:
|
437
|
+
backend_config["init_args"] = {}
|
299
438
|
|
300
|
-
|
301
|
-
|
302
|
-
|
439
|
+
# Set default client_resources if not passed
|
440
|
+
if "client_resources" not in backend_config:
|
441
|
+
backend_config["client_resources"] = {"num_cpus": 2, "num_gpus": 0}
|
303
442
|
|
304
|
-
|
305
|
-
|
443
|
+
# Initialization of backend config to enable GPU growth globally when set
|
444
|
+
if "actor" not in backend_config:
|
445
|
+
backend_config["actor"] = {"tensorflow": 0}
|
306
446
|
|
307
|
-
enable_tf_gpu_growth : bool (default: False)
|
308
|
-
A boolean to indicate whether to enable GPU growth on the main thread. This is
|
309
|
-
desirable if you make use of a TensorFlow model on your `ServerApp` while
|
310
|
-
having your `ClientApp` running on the same GPU. Without enabling this, you
|
311
|
-
might encounter an out-of-memory error becasue TensorFlow by default allocates
|
312
|
-
all GPU memory. Read mor about how `tf.config.experimental.set_memory_growth()`
|
313
|
-
works in the TensorFlow documentation: https://www.tensorflow.org/api/stable.
|
314
|
-
|
315
|
-
verbose_logging : bool (default: False)
|
316
|
-
When diabled, only INFO, WARNING and ERROR log messages will be shown. If
|
317
|
-
enabled, DEBUG-level logs will be displayed.
|
318
|
-
"""
|
319
447
|
# Set logging level
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
448
|
+
logger = logging.getLogger("flwr")
|
449
|
+
if verbose_logging:
|
450
|
+
update_console_handler(level=DEBUG, timestamps=True, colored=True)
|
451
|
+
else:
|
452
|
+
backend_config["init_args"]["logging_level"] = backend_config["init_args"].get(
|
453
|
+
"logging_level", WARNING
|
454
|
+
)
|
455
|
+
backend_config["init_args"]["log_to_driver"] = backend_config["init_args"].get(
|
456
|
+
"log_to_driver", True
|
457
|
+
)
|
326
458
|
|
327
459
|
if enable_tf_gpu_growth:
|
328
460
|
# Check that Backend config has also enabled using GPU growth
|
329
|
-
use_tf = backend_config.get("tensorflow", False)
|
461
|
+
use_tf = backend_config.get("actor", {}).get("tensorflow", False)
|
330
462
|
if not use_tf:
|
331
463
|
log(WARNING, "Enabling GPU growth for your backend.")
|
332
|
-
backend_config["tensorflow"] = True
|
464
|
+
backend_config["actor"]["tensorflow"] = True
|
333
465
|
|
334
466
|
# Convert config to original JSON-stream format
|
335
467
|
backend_config_stream = json.dumps(backend_config)
|
336
468
|
|
337
|
-
|
469
|
+
# If no `Run` object is set, create one
|
470
|
+
if run is None:
|
471
|
+
run_id = generate_rand_int_from_bytes(RUN_ID_NUM_BYTES)
|
472
|
+
run = Run.create_empty(run_id=run_id)
|
473
|
+
|
338
474
|
args = (
|
339
475
|
num_supernodes,
|
340
476
|
backend_name,
|
341
477
|
backend_config_stream,
|
342
|
-
driver_api_address,
|
343
478
|
app_dir,
|
479
|
+
is_app,
|
344
480
|
enable_tf_gpu_growth,
|
481
|
+
run,
|
482
|
+
exit_event,
|
483
|
+
flwr_dir,
|
345
484
|
client_app,
|
346
485
|
client_app_attr,
|
347
486
|
server_app,
|
348
487
|
server_app_attr,
|
488
|
+
server_app_run_config,
|
349
489
|
)
|
350
490
|
# Detect if there is an Asyncio event loop already running.
|
351
|
-
# If yes,
|
352
|
-
# like Jupyter/Colab notebooks,
|
353
|
-
|
491
|
+
# If yes, disable logger propagation. In environmnets
|
492
|
+
# like Jupyter/Colab notebooks, it's often better to do this.
|
493
|
+
asyncio_loop_running = False
|
354
494
|
try:
|
355
495
|
_ = (
|
356
496
|
asyncio.get_running_loop()
|
357
497
|
) # Raises RuntimeError if no event loop is present
|
358
498
|
log(DEBUG, "Asyncio event loop already running.")
|
359
499
|
|
360
|
-
|
500
|
+
asyncio_loop_running = True
|
361
501
|
|
362
502
|
except RuntimeError:
|
363
|
-
|
503
|
+
pass
|
364
504
|
|
365
505
|
finally:
|
366
|
-
if
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
log(DEBUG, "Starting Simulation Engine on the main thread.")
|
373
|
-
_main_loop(*args)
|
506
|
+
if asyncio_loop_running:
|
507
|
+
# Set logger propagation to False to prevent duplicated log output in Colab.
|
508
|
+
logger = set_logger_propagation(logger, False)
|
509
|
+
|
510
|
+
updated_context = _main_loop(*args)
|
511
|
+
return updated_context
|
374
512
|
|
375
513
|
|
376
514
|
def _parse_args_run_simulation() -> argparse.ArgumentParser:
|
@@ -379,14 +517,11 @@ def _parse_args_run_simulation() -> argparse.ArgumentParser:
|
|
379
517
|
description="Start a Flower simulation",
|
380
518
|
)
|
381
519
|
parser.add_argument(
|
382
|
-
"--
|
383
|
-
|
384
|
-
help="For example: `server:app` or `project.package.module:wrapper.app`",
|
385
|
-
)
|
386
|
-
parser.add_argument(
|
387
|
-
"--client-app",
|
520
|
+
"--app",
|
521
|
+
type=str,
|
388
522
|
required=True,
|
389
|
-
help="
|
523
|
+
help="Path to a directory containing a FAB-like structure with a "
|
524
|
+
"pyproject.toml.",
|
390
525
|
)
|
391
526
|
parser.add_argument(
|
392
527
|
"--num-supernodes",
|
@@ -395,10 +530,9 @@ def _parse_args_run_simulation() -> argparse.ArgumentParser:
|
|
395
530
|
help="Number of simulated SuperNodes.",
|
396
531
|
)
|
397
532
|
parser.add_argument(
|
398
|
-
"--
|
399
|
-
default=
|
400
|
-
|
401
|
-
help="For example: `server:app` or `project.package.module:wrapper.app`",
|
533
|
+
"--run-config",
|
534
|
+
default=None,
|
535
|
+
help="Override configuration key-value pairs.",
|
402
536
|
)
|
403
537
|
parser.add_argument(
|
404
538
|
"--backend",
|
@@ -409,7 +543,7 @@ def _parse_args_run_simulation() -> argparse.ArgumentParser:
|
|
409
543
|
parser.add_argument(
|
410
544
|
"--backend-config",
|
411
545
|
type=str,
|
412
|
-
default=
|
546
|
+
default="{}",
|
413
547
|
help='A JSON formatted stream, e.g \'{"<keyA>":<value>, "<keyB>":<value>}\' to '
|
414
548
|
"configure a backend. Values supported in <value> are those included by "
|
415
549
|
"`flwr.common.typing.ConfigsRecordValues`. ",
|
@@ -431,11 +565,20 @@ def _parse_args_run_simulation() -> argparse.ArgumentParser:
|
|
431
565
|
"If set, DEBUG-level logs will be displayed. ",
|
432
566
|
)
|
433
567
|
parser.add_argument(
|
434
|
-
"--
|
435
|
-
default=
|
436
|
-
help="
|
437
|
-
|
438
|
-
|
568
|
+
"--flwr-dir",
|
569
|
+
default=None,
|
570
|
+
help="""The path containing installed Flower Apps.
|
571
|
+
By default, this value is equal to:
|
572
|
+
|
573
|
+
- `$FLWR_HOME/` if `$FLWR_HOME` is defined
|
574
|
+
- `$XDG_DATA_HOME/.flwr/` if `$XDG_DATA_HOME` is defined
|
575
|
+
- `$HOME/.flwr/` in all other cases
|
576
|
+
""",
|
577
|
+
)
|
578
|
+
parser.add_argument(
|
579
|
+
"--run-id",
|
580
|
+
type=int,
|
581
|
+
help="Sets the ID of the run started by the Simulation Engine.",
|
439
582
|
)
|
440
583
|
|
441
584
|
return parser
|