flwr-nightly 1.8.0.dev20240314__py3-none-any.whl → 1.11.0.dev20240813__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of flwr-nightly might be problematic. Click here for more details.
- flwr/cli/app.py +7 -0
- flwr/cli/build.py +150 -0
- flwr/cli/config_utils.py +219 -0
- flwr/cli/example.py +3 -1
- flwr/cli/install.py +227 -0
- flwr/cli/new/new.py +179 -48
- flwr/cli/new/templates/app/.gitignore.tpl +160 -0
- flwr/cli/new/templates/app/README.flowertune.md.tpl +56 -0
- flwr/cli/new/templates/app/README.md.tpl +1 -5
- flwr/cli/new/templates/app/code/__init__.py.tpl +1 -1
- flwr/cli/new/templates/app/code/client.huggingface.py.tpl +65 -0
- flwr/cli/new/templates/app/code/client.jax.py.tpl +56 -0
- flwr/cli/new/templates/app/code/client.mlx.py.tpl +93 -0
- flwr/cli/new/templates/app/code/client.numpy.py.tpl +3 -2
- flwr/cli/new/templates/app/code/client.pytorch.py.tpl +23 -11
- flwr/cli/new/templates/app/code/client.sklearn.py.tpl +97 -0
- flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +60 -1
- flwr/cli/new/templates/app/code/flwr_tune/__init__.py +15 -0
- flwr/cli/new/templates/app/code/flwr_tune/app.py.tpl +89 -0
- flwr/cli/new/templates/app/code/flwr_tune/client.py.tpl +126 -0
- flwr/cli/new/templates/app/code/flwr_tune/config.yaml.tpl +34 -0
- flwr/cli/new/templates/app/code/flwr_tune/dataset.py.tpl +57 -0
- flwr/cli/new/templates/app/code/flwr_tune/models.py.tpl +59 -0
- flwr/cli/new/templates/app/code/flwr_tune/server.py.tpl +48 -0
- flwr/cli/new/templates/app/code/flwr_tune/static_config.yaml.tpl +11 -0
- flwr/cli/new/templates/app/code/server.huggingface.py.tpl +23 -0
- flwr/cli/new/templates/app/code/server.jax.py.tpl +20 -0
- flwr/cli/new/templates/app/code/server.mlx.py.tpl +20 -0
- flwr/cli/new/templates/app/code/server.numpy.py.tpl +17 -9
- flwr/cli/new/templates/app/code/server.pytorch.py.tpl +21 -18
- flwr/cli/new/templates/app/code/server.sklearn.py.tpl +24 -0
- flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +29 -1
- flwr/cli/new/templates/app/code/task.huggingface.py.tpl +99 -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.pytorch.py.tpl +28 -23
- flwr/cli/new/templates/app/code/task.tensorflow.py.tpl +53 -0
- flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +39 -0
- flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +38 -0
- flwr/cli/new/templates/app/pyproject.jax.toml.tpl +34 -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 +33 -0
- flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +29 -14
- flwr/cli/run/run.py +168 -17
- flwr/cli/utils.py +75 -4
- flwr/client/__init__.py +6 -1
- flwr/client/app.py +239 -248
- flwr/client/client_app.py +70 -9
- flwr/client/dpfedavg_numpy_client.py +1 -1
- flwr/client/grpc_adapter_client/__init__.py +15 -0
- flwr/client/grpc_adapter_client/connection.py +97 -0
- flwr/client/grpc_client/connection.py +18 -5
- flwr/client/grpc_rere_client/__init__.py +1 -1
- flwr/client/grpc_rere_client/client_interceptor.py +158 -0
- flwr/client/grpc_rere_client/connection.py +127 -33
- flwr/client/grpc_rere_client/grpc_adapter.py +140 -0
- flwr/client/heartbeat.py +74 -0
- flwr/client/message_handler/__init__.py +1 -1
- flwr/client/message_handler/message_handler.py +7 -7
- flwr/client/mod/__init__.py +5 -5
- flwr/client/mod/centraldp_mods.py +4 -2
- flwr/client/mod/comms_mods.py +4 -4
- flwr/client/mod/localdp_mod.py +9 -4
- flwr/client/mod/secure_aggregation/__init__.py +1 -1
- flwr/client/mod/secure_aggregation/secaggplus_mod.py +1 -1
- flwr/client/mod/utils.py +1 -1
- flwr/client/node_state.py +60 -10
- flwr/client/node_state_tests.py +4 -3
- flwr/client/rest_client/__init__.py +1 -1
- flwr/client/rest_client/connection.py +177 -157
- flwr/client/supernode/__init__.py +26 -0
- flwr/client/supernode/app.py +464 -0
- flwr/client/typing.py +1 -0
- flwr/common/__init__.py +13 -11
- flwr/common/address.py +1 -1
- flwr/common/config.py +193 -0
- flwr/common/constant.py +42 -1
- flwr/common/context.py +26 -1
- flwr/common/date.py +1 -1
- flwr/common/dp.py +1 -1
- flwr/common/grpc.py +6 -2
- flwr/common/logger.py +79 -8
- flwr/common/message.py +167 -105
- flwr/common/object_ref.py +126 -25
- flwr/common/record/__init__.py +1 -1
- flwr/common/record/parametersrecord.py +0 -1
- flwr/common/record/recordset.py +78 -27
- flwr/common/recordset_compat.py +8 -1
- flwr/common/retry_invoker.py +25 -13
- flwr/common/secure_aggregation/__init__.py +1 -1
- flwr/common/secure_aggregation/crypto/__init__.py +1 -1
- flwr/common/secure_aggregation/crypto/shamir.py +1 -1
- flwr/common/secure_aggregation/crypto/symmetric_encryption.py +21 -2
- flwr/common/secure_aggregation/ndarrays_arithmetic.py +1 -1
- flwr/common/secure_aggregation/quantization.py +1 -1
- flwr/common/secure_aggregation/secaggplus_constants.py +1 -1
- flwr/common/secure_aggregation/secaggplus_utils.py +1 -1
- flwr/common/serde.py +209 -3
- flwr/common/telemetry.py +25 -0
- flwr/common/typing.py +38 -0
- flwr/common/version.py +14 -0
- flwr/proto/clientappio_pb2.py +41 -0
- flwr/proto/clientappio_pb2.pyi +110 -0
- flwr/proto/clientappio_pb2_grpc.py +101 -0
- flwr/proto/clientappio_pb2_grpc.pyi +40 -0
- flwr/proto/common_pb2.py +36 -0
- flwr/proto/common_pb2.pyi +121 -0
- flwr/proto/common_pb2_grpc.py +4 -0
- flwr/proto/common_pb2_grpc.pyi +4 -0
- flwr/proto/driver_pb2.py +26 -19
- flwr/proto/driver_pb2.pyi +34 -0
- flwr/proto/driver_pb2_grpc.py +70 -0
- flwr/proto/driver_pb2_grpc.pyi +28 -0
- flwr/proto/exec_pb2.py +43 -0
- flwr/proto/exec_pb2.pyi +95 -0
- flwr/proto/exec_pb2_grpc.py +101 -0
- flwr/proto/exec_pb2_grpc.pyi +41 -0
- flwr/proto/fab_pb2.py +30 -0
- flwr/proto/fab_pb2.pyi +56 -0
- flwr/proto/fab_pb2_grpc.py +4 -0
- flwr/proto/fab_pb2_grpc.pyi +4 -0
- flwr/proto/fleet_pb2.py +29 -23
- flwr/proto/fleet_pb2.pyi +33 -0
- flwr/proto/fleet_pb2_grpc.py +102 -0
- flwr/proto/fleet_pb2_grpc.pyi +35 -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/message_pb2.py +41 -0
- flwr/proto/message_pb2.pyi +122 -0
- flwr/proto/message_pb2_grpc.py +4 -0
- flwr/proto/message_pb2_grpc.pyi +4 -0
- flwr/proto/run_pb2.py +35 -0
- flwr/proto/run_pb2.pyi +76 -0
- flwr/proto/run_pb2_grpc.py +4 -0
- flwr/proto/run_pb2_grpc.pyi +4 -0
- flwr/proto/task_pb2.py +7 -8
- flwr/proto/task_pb2.pyi +8 -5
- flwr/server/__init__.py +4 -8
- flwr/server/app.py +298 -350
- flwr/server/compat/app.py +6 -57
- flwr/server/compat/app_utils.py +5 -4
- flwr/server/compat/driver_client_proxy.py +29 -48
- flwr/server/compat/legacy_context.py +5 -4
- flwr/server/driver/__init__.py +2 -0
- flwr/server/driver/driver.py +22 -132
- flwr/server/driver/grpc_driver.py +224 -74
- flwr/server/driver/inmemory_driver.py +183 -0
- flwr/server/history.py +20 -20
- flwr/server/run_serverapp.py +121 -34
- flwr/server/server.py +11 -7
- flwr/server/server_app.py +59 -10
- flwr/server/serverapp_components.py +52 -0
- flwr/server/strategy/__init__.py +2 -2
- flwr/server/strategy/bulyan.py +1 -1
- flwr/server/strategy/dp_adaptive_clipping.py +3 -3
- flwr/server/strategy/dp_fixed_clipping.py +4 -3
- flwr/server/strategy/dpfedavg_adaptive.py +1 -1
- flwr/server/strategy/dpfedavg_fixed.py +1 -1
- flwr/server/strategy/fedadagrad.py +1 -1
- flwr/server/strategy/fedadam.py +1 -1
- flwr/server/strategy/fedavg_android.py +1 -1
- flwr/server/strategy/fedavgm.py +1 -1
- flwr/server/strategy/fedmedian.py +1 -1
- flwr/server/strategy/fedopt.py +1 -1
- flwr/server/strategy/fedprox.py +1 -1
- flwr/server/strategy/fedxgb_bagging.py +1 -1
- flwr/server/strategy/fedxgb_cyclic.py +1 -1
- flwr/server/strategy/fedxgb_nn_avg.py +1 -1
- flwr/server/strategy/fedyogi.py +1 -1
- flwr/server/strategy/krum.py +1 -1
- flwr/server/strategy/qfedavg.py +1 -1
- flwr/server/superlink/driver/__init__.py +1 -1
- flwr/server/superlink/driver/driver_grpc.py +1 -1
- flwr/server/superlink/driver/driver_servicer.py +51 -4
- flwr/server/superlink/ffs/__init__.py +24 -0
- flwr/server/superlink/ffs/disk_ffs.py +104 -0
- flwr/server/superlink/ffs/ffs.py +79 -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 +131 -0
- flwr/server/superlink/fleet/grpc_bidi/__init__.py +1 -1
- flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +1 -1
- flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py +1 -1
- flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py +1 -1
- flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +8 -2
- flwr/server/superlink/fleet/grpc_rere/__init__.py +1 -1
- flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +30 -2
- flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +214 -0
- flwr/server/superlink/fleet/message_handler/__init__.py +1 -1
- flwr/server/superlink/fleet/message_handler/message_handler.py +42 -2
- flwr/server/superlink/fleet/rest_rere/__init__.py +1 -1
- flwr/server/superlink/fleet/rest_rere/rest_api.py +59 -1
- flwr/server/superlink/fleet/vce/backend/__init__.py +1 -1
- flwr/server/superlink/fleet/vce/backend/backend.py +5 -5
- flwr/server/superlink/fleet/vce/backend/raybackend.py +53 -56
- flwr/server/superlink/fleet/vce/vce_api.py +190 -127
- flwr/server/superlink/state/__init__.py +1 -1
- flwr/server/superlink/state/in_memory_state.py +159 -42
- flwr/server/superlink/state/sqlite_state.py +243 -39
- flwr/server/superlink/state/state.py +81 -6
- flwr/server/superlink/state/state_factory.py +11 -2
- flwr/server/superlink/state/utils.py +62 -0
- flwr/server/typing.py +2 -0
- flwr/server/utils/__init__.py +1 -1
- flwr/server/utils/tensorboard.py +1 -1
- flwr/server/utils/validator.py +23 -9
- flwr/server/workflow/default_workflows.py +67 -25
- flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +18 -6
- flwr/simulation/__init__.py +7 -4
- flwr/simulation/app.py +67 -36
- flwr/simulation/ray_transport/__init__.py +1 -1
- flwr/simulation/ray_transport/ray_actor.py +20 -46
- flwr/simulation/ray_transport/ray_client_proxy.py +36 -16
- flwr/simulation/run_simulation.py +308 -92
- flwr/superexec/__init__.py +21 -0
- flwr/superexec/app.py +184 -0
- flwr/superexec/deployment.py +185 -0
- flwr/superexec/exec_grpc.py +55 -0
- flwr/superexec/exec_servicer.py +70 -0
- flwr/superexec/executor.py +75 -0
- flwr/superexec/simulation.py +193 -0
- {flwr_nightly-1.8.0.dev20240314.dist-info → flwr_nightly-1.11.0.dev20240813.dist-info}/METADATA +10 -6
- flwr_nightly-1.11.0.dev20240813.dist-info/RECORD +288 -0
- flwr_nightly-1.11.0.dev20240813.dist-info/entry_points.txt +10 -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_nightly-1.8.0.dev20240314.dist-info/RECORD +0 -211
- flwr_nightly-1.8.0.dev20240314.dist-info/entry_points.txt +0 -9
- {flwr_nightly-1.8.0.dev20240314.dist-info → flwr_nightly-1.11.0.dev20240813.dist-info}/LICENSE +0 -0
- {flwr_nightly-1.8.0.dev20240314.dist-info → flwr_nightly-1.11.0.dev20240813.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,464 @@
|
|
|
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 SuperNode."""
|
|
16
|
+
|
|
17
|
+
import argparse
|
|
18
|
+
import sys
|
|
19
|
+
from logging import DEBUG, INFO, WARN
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import Callable, Optional, Tuple
|
|
22
|
+
|
|
23
|
+
from cryptography.exceptions import UnsupportedAlgorithm
|
|
24
|
+
from cryptography.hazmat.primitives.asymmetric import ec
|
|
25
|
+
from cryptography.hazmat.primitives.serialization import (
|
|
26
|
+
load_ssh_private_key,
|
|
27
|
+
load_ssh_public_key,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
from flwr.client.client_app import ClientApp, LoadClientAppError
|
|
31
|
+
from flwr.common import EventType, event
|
|
32
|
+
from flwr.common.config import (
|
|
33
|
+
get_flwr_dir,
|
|
34
|
+
get_metadata_from_config,
|
|
35
|
+
get_project_config,
|
|
36
|
+
get_project_dir,
|
|
37
|
+
parse_config_args,
|
|
38
|
+
)
|
|
39
|
+
from flwr.common.constant import (
|
|
40
|
+
TRANSPORT_TYPE_GRPC_ADAPTER,
|
|
41
|
+
TRANSPORT_TYPE_GRPC_RERE,
|
|
42
|
+
TRANSPORT_TYPE_REST,
|
|
43
|
+
)
|
|
44
|
+
from flwr.common.exit_handlers import register_exit_handlers
|
|
45
|
+
from flwr.common.logger import log, warn_deprecated_feature
|
|
46
|
+
from flwr.common.object_ref import load_app, validate
|
|
47
|
+
|
|
48
|
+
from ..app import _start_client_internal
|
|
49
|
+
|
|
50
|
+
ADDRESS_FLEET_API_GRPC_RERE = "0.0.0.0:9092"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def run_supernode() -> None:
|
|
54
|
+
"""Run Flower SuperNode."""
|
|
55
|
+
log(INFO, "Starting Flower SuperNode")
|
|
56
|
+
|
|
57
|
+
event(EventType.RUN_SUPERNODE_ENTER)
|
|
58
|
+
|
|
59
|
+
args = _parse_args_run_supernode().parse_args()
|
|
60
|
+
|
|
61
|
+
_warn_deprecated_server_arg(args)
|
|
62
|
+
|
|
63
|
+
root_certificates = _get_certificates(args)
|
|
64
|
+
load_fn = _get_load_client_app_fn(
|
|
65
|
+
default_app_ref="",
|
|
66
|
+
app_path=args.app,
|
|
67
|
+
flwr_dir=args.flwr_dir,
|
|
68
|
+
multi_app=True,
|
|
69
|
+
)
|
|
70
|
+
authentication_keys = _try_setup_client_authentication(args)
|
|
71
|
+
|
|
72
|
+
_start_client_internal(
|
|
73
|
+
server_address=args.superlink,
|
|
74
|
+
load_client_app_fn=load_fn,
|
|
75
|
+
transport=args.transport,
|
|
76
|
+
root_certificates=root_certificates,
|
|
77
|
+
insecure=args.insecure,
|
|
78
|
+
authentication_keys=authentication_keys,
|
|
79
|
+
max_retries=args.max_retries,
|
|
80
|
+
max_wait_time=args.max_wait_time,
|
|
81
|
+
node_config=parse_config_args([args.node_config]),
|
|
82
|
+
flwr_path=get_flwr_dir(args.flwr_dir),
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# Graceful shutdown
|
|
86
|
+
register_exit_handlers(
|
|
87
|
+
event_type=EventType.RUN_SUPERNODE_LEAVE,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def run_client_app() -> None:
|
|
92
|
+
"""Run Flower client app."""
|
|
93
|
+
log(INFO, "Long-running Flower client starting")
|
|
94
|
+
|
|
95
|
+
event(EventType.RUN_CLIENT_APP_ENTER)
|
|
96
|
+
|
|
97
|
+
args = _parse_args_run_client_app().parse_args()
|
|
98
|
+
|
|
99
|
+
_warn_deprecated_server_arg(args)
|
|
100
|
+
|
|
101
|
+
root_certificates = _get_certificates(args)
|
|
102
|
+
load_fn = _get_load_client_app_fn(
|
|
103
|
+
default_app_ref=getattr(args, "client-app"),
|
|
104
|
+
app_path=args.dir,
|
|
105
|
+
multi_app=False,
|
|
106
|
+
)
|
|
107
|
+
authentication_keys = _try_setup_client_authentication(args)
|
|
108
|
+
|
|
109
|
+
_start_client_internal(
|
|
110
|
+
server_address=args.superlink,
|
|
111
|
+
node_config=parse_config_args([args.node_config]),
|
|
112
|
+
load_client_app_fn=load_fn,
|
|
113
|
+
transport=args.transport,
|
|
114
|
+
root_certificates=root_certificates,
|
|
115
|
+
insecure=args.insecure,
|
|
116
|
+
authentication_keys=authentication_keys,
|
|
117
|
+
max_retries=args.max_retries,
|
|
118
|
+
max_wait_time=args.max_wait_time,
|
|
119
|
+
)
|
|
120
|
+
register_exit_handlers(event_type=EventType.RUN_CLIENT_APP_LEAVE)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def flwr_clientapp() -> None:
|
|
124
|
+
"""Run process-isolated Flower ClientApp."""
|
|
125
|
+
log(INFO, "Starting Flower ClientApp")
|
|
126
|
+
|
|
127
|
+
parser = argparse.ArgumentParser(
|
|
128
|
+
description="Run a Flower ClientApp",
|
|
129
|
+
)
|
|
130
|
+
parser.add_argument(
|
|
131
|
+
"--address",
|
|
132
|
+
help="Address of SuperNode ClientAppIo gRPC servicer",
|
|
133
|
+
)
|
|
134
|
+
parser.add_argument(
|
|
135
|
+
"--token",
|
|
136
|
+
help="Unique token generated by SuperNode for each ClientApp execution",
|
|
137
|
+
)
|
|
138
|
+
args = parser.parse_args()
|
|
139
|
+
log(
|
|
140
|
+
DEBUG,
|
|
141
|
+
"Staring isolated `ClientApp` connected to SuperNode ClientAppIo at %s "
|
|
142
|
+
"with the token %s",
|
|
143
|
+
args.address,
|
|
144
|
+
args.token,
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _warn_deprecated_server_arg(args: argparse.Namespace) -> None:
|
|
149
|
+
"""Warn about the deprecated argument `--server`."""
|
|
150
|
+
if args.server != ADDRESS_FLEET_API_GRPC_RERE:
|
|
151
|
+
warn = "Passing flag --server is deprecated. Use --superlink instead."
|
|
152
|
+
warn_deprecated_feature(warn)
|
|
153
|
+
|
|
154
|
+
if args.superlink != ADDRESS_FLEET_API_GRPC_RERE:
|
|
155
|
+
# if `--superlink` also passed, then
|
|
156
|
+
# warn user that this argument overrides what was passed with `--server`
|
|
157
|
+
log(
|
|
158
|
+
WARN,
|
|
159
|
+
"Both `--server` and `--superlink` were passed. "
|
|
160
|
+
"`--server` will be ignored. Connecting to the Superlink Fleet API "
|
|
161
|
+
"at %s.",
|
|
162
|
+
args.superlink,
|
|
163
|
+
)
|
|
164
|
+
else:
|
|
165
|
+
args.superlink = args.server
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _get_certificates(args: argparse.Namespace) -> Optional[bytes]:
|
|
169
|
+
"""Load certificates if specified in args."""
|
|
170
|
+
# Obtain certificates
|
|
171
|
+
if args.insecure:
|
|
172
|
+
if args.root_certificates is not None:
|
|
173
|
+
sys.exit(
|
|
174
|
+
"Conflicting options: The '--insecure' flag disables HTTPS, "
|
|
175
|
+
"but '--root-certificates' was also specified. Please remove "
|
|
176
|
+
"the '--root-certificates' option when running in insecure mode, "
|
|
177
|
+
"or omit '--insecure' to use HTTPS."
|
|
178
|
+
)
|
|
179
|
+
log(
|
|
180
|
+
WARN,
|
|
181
|
+
"Option `--insecure` was set. "
|
|
182
|
+
"Starting insecure HTTP client connected to %s.",
|
|
183
|
+
args.superlink,
|
|
184
|
+
)
|
|
185
|
+
root_certificates = None
|
|
186
|
+
else:
|
|
187
|
+
# Load the certificates if provided, or load the system certificates
|
|
188
|
+
cert_path = args.root_certificates
|
|
189
|
+
if cert_path is None:
|
|
190
|
+
root_certificates = None
|
|
191
|
+
else:
|
|
192
|
+
root_certificates = Path(cert_path).read_bytes()
|
|
193
|
+
log(
|
|
194
|
+
DEBUG,
|
|
195
|
+
"Starting secure HTTPS client connected to %s "
|
|
196
|
+
"with the following certificates: %s.",
|
|
197
|
+
args.superlink,
|
|
198
|
+
cert_path,
|
|
199
|
+
)
|
|
200
|
+
return root_certificates
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _get_load_client_app_fn(
|
|
204
|
+
default_app_ref: str,
|
|
205
|
+
app_path: Optional[str],
|
|
206
|
+
multi_app: bool,
|
|
207
|
+
flwr_dir: Optional[str] = None,
|
|
208
|
+
) -> Callable[[str, str], ClientApp]:
|
|
209
|
+
"""Get the load_client_app_fn function.
|
|
210
|
+
|
|
211
|
+
If `multi_app` is True, this function loads the specified ClientApp
|
|
212
|
+
based on `fab_id` and `fab_version`. If `fab_id` is empty, a default
|
|
213
|
+
ClientApp will be loaded.
|
|
214
|
+
|
|
215
|
+
If `multi_app` is False, it ignores `fab_id` and `fab_version` and
|
|
216
|
+
loads a default ClientApp.
|
|
217
|
+
"""
|
|
218
|
+
if not multi_app:
|
|
219
|
+
log(
|
|
220
|
+
DEBUG,
|
|
221
|
+
"Flower SuperNode will load and validate ClientApp `%s`",
|
|
222
|
+
default_app_ref,
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
valid, error_msg = validate(default_app_ref, project_dir=app_path)
|
|
226
|
+
if not valid and error_msg:
|
|
227
|
+
raise LoadClientAppError(error_msg) from None
|
|
228
|
+
|
|
229
|
+
def _load(fab_id: str, fab_version: str) -> ClientApp:
|
|
230
|
+
runtime_app_dir = Path(app_path if app_path else "").absolute()
|
|
231
|
+
# If multi-app feature is disabled
|
|
232
|
+
if not multi_app:
|
|
233
|
+
# Set app reference
|
|
234
|
+
client_app_ref = default_app_ref
|
|
235
|
+
# If multi-app feature is enabled but app directory is provided
|
|
236
|
+
elif app_path is not None:
|
|
237
|
+
config = get_project_config(runtime_app_dir)
|
|
238
|
+
this_fab_version, this_fab_id = get_metadata_from_config(config)
|
|
239
|
+
|
|
240
|
+
if this_fab_version != fab_version or this_fab_id != fab_id:
|
|
241
|
+
raise LoadClientAppError(
|
|
242
|
+
f"FAB ID or version mismatch: Expected FAB ID '{this_fab_id}' and "
|
|
243
|
+
f"FAB version '{this_fab_version}', but received FAB ID '{fab_id}' "
|
|
244
|
+
f"and FAB version '{fab_version}'.",
|
|
245
|
+
) from None
|
|
246
|
+
|
|
247
|
+
# log(WARN, "FAB ID is not provided; the default ClientApp will be loaded.")
|
|
248
|
+
|
|
249
|
+
# Set app reference
|
|
250
|
+
client_app_ref = config["tool"]["flwr"]["app"]["components"]["clientapp"]
|
|
251
|
+
# If multi-app feature is enabled
|
|
252
|
+
else:
|
|
253
|
+
try:
|
|
254
|
+
runtime_app_dir = get_project_dir(
|
|
255
|
+
fab_id, fab_version, get_flwr_dir(flwr_dir)
|
|
256
|
+
)
|
|
257
|
+
config = get_project_config(runtime_app_dir)
|
|
258
|
+
except Exception as e:
|
|
259
|
+
raise LoadClientAppError("Failed to load ClientApp") from e
|
|
260
|
+
|
|
261
|
+
# Set app reference
|
|
262
|
+
client_app_ref = config["tool"]["flwr"]["app"]["components"]["clientapp"]
|
|
263
|
+
|
|
264
|
+
# Load ClientApp
|
|
265
|
+
log(
|
|
266
|
+
DEBUG,
|
|
267
|
+
"Loading ClientApp `%s`",
|
|
268
|
+
client_app_ref,
|
|
269
|
+
)
|
|
270
|
+
client_app = load_app(client_app_ref, LoadClientAppError, runtime_app_dir)
|
|
271
|
+
|
|
272
|
+
if not isinstance(client_app, ClientApp):
|
|
273
|
+
raise LoadClientAppError(
|
|
274
|
+
f"Attribute {client_app_ref} is not of type {ClientApp}",
|
|
275
|
+
) from None
|
|
276
|
+
|
|
277
|
+
return client_app
|
|
278
|
+
|
|
279
|
+
return _load
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def _parse_args_run_supernode() -> argparse.ArgumentParser:
|
|
283
|
+
"""Parse flower-supernode command line arguments."""
|
|
284
|
+
parser = argparse.ArgumentParser(
|
|
285
|
+
description="Start a Flower SuperNode",
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
parser.add_argument(
|
|
289
|
+
"app",
|
|
290
|
+
nargs="?",
|
|
291
|
+
default=None,
|
|
292
|
+
help="Specify the path of the Flower App to load and run the `ClientApp`. "
|
|
293
|
+
"The `pyproject.toml` file must be located in the root of this path. "
|
|
294
|
+
"When this argument is provided, the SuperNode will exclusively respond to "
|
|
295
|
+
"messages from the corresponding `ServerApp` by matching the FAB ID and FAB "
|
|
296
|
+
"version. An error will be raised if a message is received from any other "
|
|
297
|
+
"`ServerApp`.",
|
|
298
|
+
)
|
|
299
|
+
_parse_args_common(parser)
|
|
300
|
+
parser.add_argument(
|
|
301
|
+
"--flwr-dir",
|
|
302
|
+
default=None,
|
|
303
|
+
help="""The path containing installed Flower Apps.
|
|
304
|
+
By default, this value is equal to:
|
|
305
|
+
|
|
306
|
+
- `$FLWR_HOME/` if `$FLWR_HOME` is defined
|
|
307
|
+
- `$XDG_DATA_HOME/.flwr/` if `$XDG_DATA_HOME` is defined
|
|
308
|
+
- `$HOME/.flwr/` in all other cases
|
|
309
|
+
""",
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
return parser
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def _parse_args_run_client_app() -> argparse.ArgumentParser:
|
|
316
|
+
"""Parse flower-client-app command line arguments."""
|
|
317
|
+
parser = argparse.ArgumentParser(
|
|
318
|
+
description="Start a Flower client app",
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
parser.add_argument(
|
|
322
|
+
"client-app",
|
|
323
|
+
help="For example: `client:app` or `project.package.module:wrapper.app`",
|
|
324
|
+
)
|
|
325
|
+
_parse_args_common(parser=parser)
|
|
326
|
+
parser.add_argument(
|
|
327
|
+
"--dir",
|
|
328
|
+
default="",
|
|
329
|
+
help="Add specified directory to the PYTHONPATH and load Flower "
|
|
330
|
+
"app from there."
|
|
331
|
+
" Default: current working directory.",
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
return parser
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def _parse_args_common(parser: argparse.ArgumentParser) -> None:
|
|
338
|
+
parser.add_argument(
|
|
339
|
+
"--insecure",
|
|
340
|
+
action="store_true",
|
|
341
|
+
help="Run the client without HTTPS. By default, the client runs with "
|
|
342
|
+
"HTTPS enabled. Use this flag only if you understand the risks.",
|
|
343
|
+
)
|
|
344
|
+
ex_group = parser.add_mutually_exclusive_group()
|
|
345
|
+
ex_group.add_argument(
|
|
346
|
+
"--grpc-rere",
|
|
347
|
+
action="store_const",
|
|
348
|
+
dest="transport",
|
|
349
|
+
const=TRANSPORT_TYPE_GRPC_RERE,
|
|
350
|
+
default=TRANSPORT_TYPE_GRPC_RERE,
|
|
351
|
+
help="Use grpc-rere as a transport layer for the client.",
|
|
352
|
+
)
|
|
353
|
+
ex_group.add_argument(
|
|
354
|
+
"--grpc-adapter",
|
|
355
|
+
action="store_const",
|
|
356
|
+
dest="transport",
|
|
357
|
+
const=TRANSPORT_TYPE_GRPC_ADAPTER,
|
|
358
|
+
help="Use grpc-adapter as a transport layer for the client.",
|
|
359
|
+
)
|
|
360
|
+
ex_group.add_argument(
|
|
361
|
+
"--rest",
|
|
362
|
+
action="store_const",
|
|
363
|
+
dest="transport",
|
|
364
|
+
const=TRANSPORT_TYPE_REST,
|
|
365
|
+
help="Use REST as a transport layer for the client.",
|
|
366
|
+
)
|
|
367
|
+
parser.add_argument(
|
|
368
|
+
"--root-certificates",
|
|
369
|
+
metavar="ROOT_CERT",
|
|
370
|
+
type=str,
|
|
371
|
+
help="Specifies the path to the PEM-encoded root certificate file for "
|
|
372
|
+
"establishing secure HTTPS connections.",
|
|
373
|
+
)
|
|
374
|
+
parser.add_argument(
|
|
375
|
+
"--server",
|
|
376
|
+
default=ADDRESS_FLEET_API_GRPC_RERE,
|
|
377
|
+
help="Server address",
|
|
378
|
+
)
|
|
379
|
+
parser.add_argument(
|
|
380
|
+
"--superlink",
|
|
381
|
+
default=ADDRESS_FLEET_API_GRPC_RERE,
|
|
382
|
+
help="SuperLink Fleet API (gRPC-rere) address (IPv4, IPv6, or a domain name)",
|
|
383
|
+
)
|
|
384
|
+
parser.add_argument(
|
|
385
|
+
"--max-retries",
|
|
386
|
+
type=int,
|
|
387
|
+
default=None,
|
|
388
|
+
help="The maximum number of times the client will try to reconnect to the"
|
|
389
|
+
"SuperLink before giving up in case of a connection error. By default,"
|
|
390
|
+
"it is set to None, meaning there is no limit to the number of tries.",
|
|
391
|
+
)
|
|
392
|
+
parser.add_argument(
|
|
393
|
+
"--max-wait-time",
|
|
394
|
+
type=float,
|
|
395
|
+
default=None,
|
|
396
|
+
help="The maximum duration before the client stops trying to"
|
|
397
|
+
"connect to the SuperLink in case of connection error. By default, it"
|
|
398
|
+
"is set to None, meaning there is no limit to the total time.",
|
|
399
|
+
)
|
|
400
|
+
parser.add_argument(
|
|
401
|
+
"--auth-supernode-private-key",
|
|
402
|
+
type=str,
|
|
403
|
+
help="The SuperNode's private key (as a path str) to enable authentication.",
|
|
404
|
+
)
|
|
405
|
+
parser.add_argument(
|
|
406
|
+
"--auth-supernode-public-key",
|
|
407
|
+
type=str,
|
|
408
|
+
help="The SuperNode's public key (as a path str) to enable authentication.",
|
|
409
|
+
)
|
|
410
|
+
parser.add_argument(
|
|
411
|
+
"--node-config",
|
|
412
|
+
type=str,
|
|
413
|
+
help="A comma separated list of key/value pairs (separated by `=`) to "
|
|
414
|
+
"configure the SuperNode. "
|
|
415
|
+
"E.g. --node-config 'key1=\"value1\",partition-id=0,num-partitions=100'",
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
def _try_setup_client_authentication(
|
|
420
|
+
args: argparse.Namespace,
|
|
421
|
+
) -> Optional[Tuple[ec.EllipticCurvePrivateKey, ec.EllipticCurvePublicKey]]:
|
|
422
|
+
if not args.auth_supernode_private_key and not args.auth_supernode_public_key:
|
|
423
|
+
return None
|
|
424
|
+
|
|
425
|
+
if not args.auth_supernode_private_key or not args.auth_supernode_public_key:
|
|
426
|
+
sys.exit(
|
|
427
|
+
"Authentication requires file paths to both "
|
|
428
|
+
"'--auth-supernode-private-key' and '--auth-supernode-public-key'"
|
|
429
|
+
"to be provided (providing only one of them is not sufficient)."
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
try:
|
|
433
|
+
ssh_private_key = load_ssh_private_key(
|
|
434
|
+
Path(args.auth_supernode_private_key).read_bytes(),
|
|
435
|
+
None,
|
|
436
|
+
)
|
|
437
|
+
if not isinstance(ssh_private_key, ec.EllipticCurvePrivateKey):
|
|
438
|
+
raise ValueError()
|
|
439
|
+
except (ValueError, UnsupportedAlgorithm):
|
|
440
|
+
sys.exit(
|
|
441
|
+
"Error: Unable to parse the private key file in "
|
|
442
|
+
"'--auth-supernode-private-key'. Authentication requires elliptic "
|
|
443
|
+
"curve private and public key pair. Please ensure that the file "
|
|
444
|
+
"path points to a valid private key file and try again."
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
try:
|
|
448
|
+
ssh_public_key = load_ssh_public_key(
|
|
449
|
+
Path(args.auth_supernode_public_key).read_bytes()
|
|
450
|
+
)
|
|
451
|
+
if not isinstance(ssh_public_key, ec.EllipticCurvePublicKey):
|
|
452
|
+
raise ValueError()
|
|
453
|
+
except (ValueError, UnsupportedAlgorithm):
|
|
454
|
+
sys.exit(
|
|
455
|
+
"Error: Unable to parse the public key file in "
|
|
456
|
+
"'--auth-supernode-public-key'. Authentication requires elliptic "
|
|
457
|
+
"curve private and public key pair. Please ensure that the file "
|
|
458
|
+
"path points to a valid public key file and try again."
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
return (
|
|
462
|
+
ssh_private_key,
|
|
463
|
+
ssh_public_key,
|
|
464
|
+
)
|
flwr/client/typing.py
CHANGED
|
@@ -23,6 +23,7 @@ from .client import Client as Client
|
|
|
23
23
|
|
|
24
24
|
# Compatibility
|
|
25
25
|
ClientFn = Callable[[str], Client]
|
|
26
|
+
ClientFnExt = Callable[[Context], Client]
|
|
26
27
|
|
|
27
28
|
ClientAppCallable = Callable[[Message, Context], Message]
|
|
28
29
|
Mod = Callable[[Message, Context, ClientAppCallable], Message]
|
flwr/common/__init__.py
CHANGED
|
@@ -22,6 +22,7 @@ from .date import now as now
|
|
|
22
22
|
from .grpc import GRPC_MAX_MESSAGE_LENGTH
|
|
23
23
|
from .logger import configure as configure
|
|
24
24
|
from .logger import log as log
|
|
25
|
+
from .message import DEFAULT_TTL
|
|
25
26
|
from .message import Error as Error
|
|
26
27
|
from .message import Message as Message
|
|
27
28
|
from .message import Metadata as Metadata
|
|
@@ -62,28 +63,24 @@ from .typing import Status as Status
|
|
|
62
63
|
|
|
63
64
|
__all__ = [
|
|
64
65
|
"Array",
|
|
65
|
-
"array_from_numpy",
|
|
66
|
-
"bytes_to_ndarray",
|
|
67
66
|
"ClientMessage",
|
|
68
67
|
"Code",
|
|
69
68
|
"Config",
|
|
70
69
|
"ConfigsRecord",
|
|
71
|
-
"configure",
|
|
72
70
|
"Context",
|
|
71
|
+
"DEFAULT_TTL",
|
|
73
72
|
"DisconnectRes",
|
|
73
|
+
"Error",
|
|
74
74
|
"EvaluateIns",
|
|
75
75
|
"EvaluateRes",
|
|
76
|
-
"event",
|
|
77
76
|
"EventType",
|
|
78
77
|
"FitIns",
|
|
79
78
|
"FitRes",
|
|
80
|
-
"
|
|
79
|
+
"GRPC_MAX_MESSAGE_LENGTH",
|
|
81
80
|
"GetParametersIns",
|
|
82
81
|
"GetParametersRes",
|
|
83
82
|
"GetPropertiesIns",
|
|
84
83
|
"GetPropertiesRes",
|
|
85
|
-
"GRPC_MAX_MESSAGE_LENGTH",
|
|
86
|
-
"log",
|
|
87
84
|
"Message",
|
|
88
85
|
"MessageType",
|
|
89
86
|
"MessageTypeLegacy",
|
|
@@ -91,13 +88,9 @@ __all__ = [
|
|
|
91
88
|
"Metrics",
|
|
92
89
|
"MetricsAggregationFn",
|
|
93
90
|
"MetricsRecord",
|
|
94
|
-
"ndarray_to_bytes",
|
|
95
|
-
"now",
|
|
96
91
|
"NDArray",
|
|
97
92
|
"NDArrays",
|
|
98
|
-
"ndarrays_to_parameters",
|
|
99
93
|
"Parameters",
|
|
100
|
-
"parameters_to_ndarrays",
|
|
101
94
|
"ParametersRecord",
|
|
102
95
|
"Properties",
|
|
103
96
|
"ReconnectIns",
|
|
@@ -105,4 +98,13 @@ __all__ = [
|
|
|
105
98
|
"Scalar",
|
|
106
99
|
"ServerMessage",
|
|
107
100
|
"Status",
|
|
101
|
+
"array_from_numpy",
|
|
102
|
+
"bytes_to_ndarray",
|
|
103
|
+
"configure",
|
|
104
|
+
"event",
|
|
105
|
+
"log",
|
|
106
|
+
"ndarray_to_bytes",
|
|
107
|
+
"ndarrays_to_parameters",
|
|
108
|
+
"now",
|
|
109
|
+
"parameters_to_ndarrays",
|
|
108
110
|
]
|
flwr/common/address.py
CHANGED