flwr 1.17.0__py3-none-any.whl → 1.19.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- flwr/__init__.py +1 -1
- flwr/app/__init__.py +15 -0
- flwr/app/error.py +68 -0
- flwr/app/metadata.py +223 -0
- flwr/cli/__init__.py +1 -1
- flwr/cli/app.py +21 -2
- flwr/cli/build.py +83 -58
- flwr/cli/cli_user_auth_interceptor.py +1 -1
- flwr/cli/config_utils.py +53 -17
- flwr/cli/example.py +1 -1
- flwr/cli/install.py +1 -1
- flwr/cli/log.py +4 -4
- flwr/cli/login/__init__.py +1 -1
- flwr/cli/login/login.py +15 -8
- flwr/cli/ls.py +16 -37
- flwr/cli/new/__init__.py +1 -1
- flwr/cli/new/new.py +4 -4
- flwr/cli/new/templates/__init__.py +1 -1
- flwr/cli/new/templates/app/__init__.py +1 -1
- flwr/cli/new/templates/app/code/__init__.py +1 -1
- flwr/cli/new/templates/app/code/client.baseline.py.tpl +1 -1
- flwr/cli/new/templates/app/code/flwr_tune/__init__.py +1 -1
- flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl +4 -4
- flwr/cli/new/templates/app/code/model.baseline.py.tpl +1 -1
- flwr/cli/new/templates/app/code/server.baseline.py.tpl +2 -3
- flwr/cli/new/templates/app/code/task.sklearn.py.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +14 -17
- flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +4 -4
- flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.jax.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +1 -1
- flwr/cli/run/__init__.py +1 -1
- flwr/cli/run/run.py +11 -19
- flwr/cli/stop.py +3 -3
- flwr/cli/utils.py +42 -17
- flwr/client/__init__.py +3 -3
- flwr/client/client.py +1 -1
- flwr/client/client_app.py +140 -138
- flwr/client/clientapp/__init__.py +1 -8
- flwr/client/clientapp/utils.py +1 -1
- flwr/client/dpfedavg_numpy_client.py +1 -1
- flwr/client/grpc_adapter_client/__init__.py +1 -1
- flwr/client/grpc_adapter_client/connection.py +5 -5
- flwr/client/grpc_rere_client/__init__.py +1 -1
- flwr/client/grpc_rere_client/client_interceptor.py +1 -1
- flwr/client/grpc_rere_client/connection.py +131 -61
- flwr/client/grpc_rere_client/grpc_adapter.py +35 -7
- flwr/client/message_handler/__init__.py +1 -1
- flwr/client/message_handler/message_handler.py +2 -2
- flwr/client/mod/__init__.py +1 -1
- flwr/client/mod/centraldp_mods.py +1 -1
- flwr/client/mod/comms_mods.py +39 -20
- flwr/client/mod/localdp_mod.py +6 -6
- flwr/client/mod/secure_aggregation/__init__.py +1 -1
- flwr/client/mod/secure_aggregation/secagg_mod.py +1 -1
- flwr/client/mod/secure_aggregation/secaggplus_mod.py +1 -1
- flwr/client/mod/utils.py +1 -1
- flwr/client/numpy_client.py +1 -1
- flwr/client/rest_client/__init__.py +1 -1
- flwr/client/rest_client/connection.py +174 -68
- flwr/client/run_info_store.py +1 -1
- flwr/client/typing.py +1 -1
- flwr/clientapp/__init__.py +15 -0
- flwr/common/__init__.py +3 -3
- flwr/common/address.py +1 -1
- flwr/common/args.py +1 -1
- flwr/common/auth_plugin/__init__.py +3 -1
- flwr/common/auth_plugin/auth_plugin.py +30 -4
- flwr/common/config.py +1 -1
- flwr/common/constant.py +37 -8
- flwr/common/context.py +1 -1
- flwr/common/date.py +1 -1
- flwr/common/differential_privacy.py +1 -1
- flwr/common/differential_privacy_constants.py +1 -1
- flwr/common/dp.py +1 -1
- flwr/common/event_log_plugin/event_log_plugin.py +3 -3
- flwr/common/exit/exit.py +6 -6
- flwr/common/exit_handlers.py +31 -1
- flwr/common/grpc.py +1 -1
- flwr/common/heartbeat.py +165 -0
- flwr/common/inflatable.py +290 -0
- flwr/common/inflatable_grpc_utils.py +99 -0
- flwr/common/inflatable_rest_utils.py +99 -0
- flwr/common/inflatable_utils.py +341 -0
- flwr/common/logger.py +1 -1
- flwr/common/message.py +137 -252
- flwr/common/object_ref.py +1 -1
- flwr/common/parameter.py +1 -1
- flwr/common/pyproject.py +1 -1
- flwr/common/record/__init__.py +3 -2
- flwr/common/record/array.py +323 -0
- flwr/common/record/arrayrecord.py +121 -243
- flwr/common/record/configrecord.py +71 -16
- flwr/common/record/conversion_utils.py +2 -2
- flwr/common/record/metricrecord.py +71 -20
- flwr/common/record/recorddict.py +207 -90
- flwr/common/record/typeddict.py +1 -1
- flwr/common/recorddict_compat.py +2 -2
- flwr/common/retry_invoker.py +15 -11
- flwr/common/secure_aggregation/__init__.py +1 -1
- flwr/common/secure_aggregation/crypto/__init__.py +1 -1
- flwr/common/secure_aggregation/crypto/shamir.py +52 -30
- flwr/common/secure_aggregation/crypto/symmetric_encryption.py +1 -1
- 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 +60 -184
- flwr/common/serde_utils.py +175 -0
- flwr/common/telemetry.py +2 -2
- flwr/common/typing.py +6 -4
- flwr/common/version.py +1 -1
- flwr/compat/__init__.py +15 -0
- flwr/compat/client/__init__.py +15 -0
- flwr/{client → compat/client}/app.py +71 -211
- flwr/{client → compat/client}/grpc_client/__init__.py +1 -1
- flwr/{client → compat/client}/grpc_client/connection.py +13 -13
- flwr/compat/common/__init__.py +15 -0
- flwr/compat/server/__init__.py +15 -0
- flwr/compat/server/app.py +174 -0
- flwr/compat/simulation/__init__.py +15 -0
- flwr/proto/__init__.py +1 -1
- flwr/proto/fleet_pb2.py +32 -27
- flwr/proto/fleet_pb2.pyi +49 -35
- flwr/proto/fleet_pb2_grpc.py +117 -13
- flwr/proto/fleet_pb2_grpc.pyi +47 -6
- flwr/proto/heartbeat_pb2.py +33 -0
- flwr/proto/heartbeat_pb2.pyi +66 -0
- flwr/proto/heartbeat_pb2_grpc.py +4 -0
- flwr/proto/heartbeat_pb2_grpc.pyi +4 -0
- flwr/proto/message_pb2.py +28 -11
- flwr/proto/message_pb2.pyi +125 -0
- flwr/proto/recorddict_pb2.py +16 -28
- flwr/proto/recorddict_pb2.pyi +46 -64
- flwr/proto/run_pb2.py +24 -32
- flwr/proto/run_pb2.pyi +4 -52
- flwr/proto/serverappio_pb2.py +32 -23
- flwr/proto/serverappio_pb2.pyi +45 -3
- flwr/proto/serverappio_pb2_grpc.py +138 -34
- flwr/proto/serverappio_pb2_grpc.pyi +54 -13
- flwr/proto/simulationio_pb2.py +12 -11
- flwr/proto/simulationio_pb2_grpc.py +35 -0
- flwr/proto/simulationio_pb2_grpc.pyi +14 -0
- flwr/server/__init__.py +2 -2
- flwr/server/app.py +69 -187
- flwr/server/client_manager.py +1 -1
- flwr/server/client_proxy.py +1 -1
- flwr/server/compat/__init__.py +1 -1
- flwr/server/compat/app.py +1 -1
- flwr/server/compat/app_utils.py +51 -29
- flwr/server/compat/legacy_context.py +1 -1
- flwr/server/criterion.py +1 -1
- flwr/server/fleet_event_log_interceptor.py +2 -2
- flwr/server/grid/grid.py +3 -3
- flwr/server/grid/grpc_grid.py +104 -34
- flwr/server/grid/inmemory_grid.py +5 -4
- flwr/server/history.py +1 -1
- flwr/server/run_serverapp.py +1 -1
- flwr/server/server.py +1 -1
- flwr/server/server_app.py +65 -58
- flwr/server/server_config.py +1 -1
- flwr/server/serverapp/__init__.py +1 -1
- flwr/server/serverapp/app.py +19 -1
- flwr/server/serverapp_components.py +1 -1
- flwr/server/strategy/__init__.py +1 -1
- flwr/server/strategy/aggregate.py +1 -1
- flwr/server/strategy/bulyan.py +2 -2
- flwr/server/strategy/dp_adaptive_clipping.py +17 -17
- flwr/server/strategy/dp_fixed_clipping.py +17 -17
- flwr/server/strategy/dpfedavg_adaptive.py +1 -1
- flwr/server/strategy/dpfedavg_fixed.py +1 -1
- flwr/server/strategy/fault_tolerant_fedavg.py +1 -1
- flwr/server/strategy/fedadagrad.py +1 -1
- flwr/server/strategy/fedadam.py +1 -1
- flwr/server/strategy/fedavg.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/fedtrimmedavg.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 +3 -2
- flwr/server/strategy/fedyogi.py +1 -1
- flwr/server/strategy/krum.py +1 -1
- flwr/server/strategy/qfedavg.py +1 -1
- flwr/server/strategy/strategy.py +1 -1
- flwr/server/superlink/__init__.py +1 -1
- flwr/server/superlink/ffs/__init__.py +3 -1
- flwr/server/superlink/ffs/disk_ffs.py +1 -1
- flwr/server/superlink/ffs/ffs.py +1 -1
- flwr/server/superlink/ffs/ffs_factory.py +1 -1
- flwr/server/superlink/fleet/__init__.py +1 -1
- flwr/server/superlink/fleet/grpc_adapter/__init__.py +1 -1
- flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +14 -4
- 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 +13 -13
- flwr/server/superlink/fleet/grpc_rere/__init__.py +1 -1
- flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +102 -8
- flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +1 -1
- flwr/server/superlink/fleet/message_handler/__init__.py +1 -1
- flwr/server/superlink/fleet/message_handler/message_handler.py +136 -19
- flwr/server/superlink/fleet/rest_rere/__init__.py +1 -1
- flwr/server/superlink/fleet/rest_rere/rest_api.py +73 -12
- flwr/server/superlink/fleet/vce/__init__.py +1 -1
- flwr/server/superlink/fleet/vce/backend/__init__.py +1 -1
- flwr/server/superlink/fleet/vce/backend/backend.py +1 -1
- flwr/server/superlink/fleet/vce/backend/raybackend.py +1 -1
- flwr/server/superlink/fleet/vce/vce_api.py +7 -4
- flwr/server/superlink/linkstate/__init__.py +1 -1
- flwr/server/superlink/linkstate/in_memory_linkstate.py +139 -44
- flwr/server/superlink/linkstate/linkstate.py +54 -21
- flwr/server/superlink/linkstate/linkstate_factory.py +1 -1
- flwr/server/superlink/linkstate/sqlite_linkstate.py +150 -56
- flwr/server/superlink/linkstate/utils.py +34 -30
- flwr/server/superlink/serverappio/serverappio_grpc.py +3 -0
- flwr/server/superlink/serverappio/serverappio_servicer.py +211 -57
- flwr/server/superlink/simulation/__init__.py +1 -1
- flwr/server/superlink/simulation/simulationio_grpc.py +1 -1
- flwr/server/superlink/simulation/simulationio_servicer.py +26 -2
- flwr/server/superlink/utils.py +45 -3
- flwr/server/typing.py +1 -1
- flwr/server/utils/__init__.py +1 -1
- flwr/server/utils/tensorboard.py +1 -1
- flwr/server/utils/validator.py +3 -3
- flwr/server/workflow/__init__.py +1 -1
- flwr/server/workflow/constant.py +1 -1
- flwr/server/workflow/default_workflows.py +1 -1
- flwr/server/workflow/secure_aggregation/__init__.py +1 -1
- flwr/server/workflow/secure_aggregation/secagg_workflow.py +1 -1
- flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +1 -1
- flwr/serverapp/__init__.py +15 -0
- flwr/simulation/__init__.py +1 -1
- flwr/simulation/app.py +18 -1
- flwr/simulation/legacy_app.py +1 -1
- flwr/simulation/ray_transport/__init__.py +1 -1
- flwr/simulation/ray_transport/ray_actor.py +1 -1
- flwr/simulation/ray_transport/ray_client_proxy.py +1 -1
- flwr/simulation/ray_transport/utils.py +1 -1
- flwr/simulation/run_simulation.py +2 -2
- flwr/simulation/simulationio_connection.py +1 -1
- flwr/supercore/__init__.py +15 -0
- flwr/supercore/object_store/__init__.py +24 -0
- flwr/supercore/object_store/in_memory_object_store.py +229 -0
- flwr/supercore/object_store/object_store.py +192 -0
- flwr/supercore/object_store/object_store_factory.py +44 -0
- flwr/superexec/__init__.py +1 -1
- flwr/superexec/app.py +1 -1
- flwr/superexec/deployment.py +7 -3
- flwr/superexec/exec_event_log_interceptor.py +4 -4
- flwr/superexec/exec_grpc.py +8 -4
- flwr/superexec/exec_servicer.py +126 -24
- flwr/superexec/exec_user_auth_interceptor.py +38 -9
- flwr/superexec/executor.py +5 -1
- flwr/superexec/simulation.py +8 -2
- flwr/superlink/__init__.py +15 -0
- flwr/{client/supernode → supernode}/__init__.py +1 -8
- flwr/{client/nodestate/nodestate.py → supernode/cli/__init__.py} +8 -15
- flwr/{client/supernode/app.py → supernode/cli/flower_supernode.py} +4 -13
- flwr/supernode/cli/flwr_clientapp.py +81 -0
- flwr/{client → supernode}/nodestate/__init__.py +1 -1
- flwr/supernode/nodestate/in_memory_nodestate.py +190 -0
- flwr/supernode/nodestate/nodestate.py +212 -0
- flwr/{client → supernode}/nodestate/nodestate_factory.py +1 -1
- flwr/supernode/runtime/__init__.py +15 -0
- flwr/{client/clientapp/app.py → supernode/runtime/run_clientapp.py} +26 -57
- flwr/supernode/servicer/__init__.py +15 -0
- flwr/supernode/servicer/clientappio/__init__.py +24 -0
- flwr/{client/clientapp → supernode/servicer/clientappio}/clientappio_servicer.py +1 -1
- flwr/supernode/start_client_internal.py +491 -0
- {flwr-1.17.0.dist-info → flwr-1.19.0.dist-info}/METADATA +6 -5
- flwr-1.19.0.dist-info/RECORD +365 -0
- {flwr-1.17.0.dist-info → flwr-1.19.0.dist-info}/WHEEL +1 -1
- {flwr-1.17.0.dist-info → flwr-1.19.0.dist-info}/entry_points.txt +2 -2
- flwr/client/heartbeat.py +0 -74
- flwr/client/nodestate/in_memory_nodestate.py +0 -38
- flwr-1.17.0.dist-info/LICENSE +0 -202
- flwr-1.17.0.dist-info/RECORD +0 -333
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# Copyright 2025 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
|
+
"""Utils for serde."""
|
|
16
|
+
|
|
17
|
+
from collections.abc import MutableMapping
|
|
18
|
+
from typing import Any, TypeVar, cast
|
|
19
|
+
|
|
20
|
+
from google.protobuf.message import Message as GrpcMessage
|
|
21
|
+
|
|
22
|
+
# pylint: disable=E0611
|
|
23
|
+
from flwr.proto.error_pb2 import Error as ProtoError
|
|
24
|
+
from flwr.proto.message_pb2 import Metadata as ProtoMetadata
|
|
25
|
+
from flwr.proto.recorddict_pb2 import (
|
|
26
|
+
BoolList,
|
|
27
|
+
BytesList,
|
|
28
|
+
DoubleList,
|
|
29
|
+
SintList,
|
|
30
|
+
StringList,
|
|
31
|
+
UintList,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
from ..app.error import Error
|
|
35
|
+
from ..app.metadata import Metadata
|
|
36
|
+
from .constant import INT64_MAX_VALUE
|
|
37
|
+
from .record.typeddict import TypedDict
|
|
38
|
+
|
|
39
|
+
# pylint: enable=E0611
|
|
40
|
+
|
|
41
|
+
_type_to_field: dict[type, str] = {
|
|
42
|
+
float: "double",
|
|
43
|
+
int: "sint64",
|
|
44
|
+
bool: "bool",
|
|
45
|
+
str: "string",
|
|
46
|
+
bytes: "bytes",
|
|
47
|
+
}
|
|
48
|
+
_list_type_to_class_and_field: dict[type, tuple[type[GrpcMessage], str]] = {
|
|
49
|
+
float: (DoubleList, "double_list"),
|
|
50
|
+
int: (SintList, "sint_list"),
|
|
51
|
+
bool: (BoolList, "bool_list"),
|
|
52
|
+
str: (StringList, "string_list"),
|
|
53
|
+
bytes: (BytesList, "bytes_list"),
|
|
54
|
+
}
|
|
55
|
+
T = TypeVar("T")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _is_uint64(value: Any) -> bool:
|
|
59
|
+
"""Check if a value is uint64."""
|
|
60
|
+
return isinstance(value, int) and value > INT64_MAX_VALUE
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _record_value_to_proto(
|
|
64
|
+
value: Any, allowed_types: list[type], proto_class: type[T]
|
|
65
|
+
) -> T:
|
|
66
|
+
"""Serialize `*RecordValue` to ProtoBuf.
|
|
67
|
+
|
|
68
|
+
Note: `bool` MUST be put in the front of allowd_types if it exists.
|
|
69
|
+
"""
|
|
70
|
+
arg = {}
|
|
71
|
+
for t in allowed_types:
|
|
72
|
+
# Single element
|
|
73
|
+
# Note: `isinstance(False, int) == True`.
|
|
74
|
+
if isinstance(value, t):
|
|
75
|
+
fld = _type_to_field[t]
|
|
76
|
+
if t is int and _is_uint64(value):
|
|
77
|
+
fld = "uint64"
|
|
78
|
+
arg[fld] = value
|
|
79
|
+
return proto_class(**arg)
|
|
80
|
+
# List
|
|
81
|
+
if isinstance(value, list) and all(isinstance(item, t) for item in value):
|
|
82
|
+
list_class, fld = _list_type_to_class_and_field[t]
|
|
83
|
+
# Use UintList if any element is of type `uint64`.
|
|
84
|
+
if t is int and any(_is_uint64(v) for v in value):
|
|
85
|
+
list_class, fld = UintList, "uint_list"
|
|
86
|
+
arg[fld] = list_class(vals=value)
|
|
87
|
+
return proto_class(**arg)
|
|
88
|
+
# Invalid types
|
|
89
|
+
raise TypeError(
|
|
90
|
+
f"The type of the following value is not allowed "
|
|
91
|
+
f"in '{proto_class.__name__}':\n{value}"
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _record_value_from_proto(value_proto: GrpcMessage) -> Any:
|
|
96
|
+
"""Deserialize `*RecordValue` from ProtoBuf."""
|
|
97
|
+
value_field = cast(str, value_proto.WhichOneof("value"))
|
|
98
|
+
if value_field.endswith("list"):
|
|
99
|
+
value = list(getattr(value_proto, value_field).vals)
|
|
100
|
+
else:
|
|
101
|
+
value = getattr(value_proto, value_field)
|
|
102
|
+
return value
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def record_value_dict_to_proto(
|
|
106
|
+
value_dict: TypedDict[str, Any],
|
|
107
|
+
allowed_types: list[type],
|
|
108
|
+
value_proto_class: type[T],
|
|
109
|
+
) -> dict[str, T]:
|
|
110
|
+
"""Serialize the record value dict to ProtoBuf.
|
|
111
|
+
|
|
112
|
+
This function will preserve the order of the keys in the input dictionary.
|
|
113
|
+
|
|
114
|
+
Note: `bool` MUST be put in the front of allowd_types if it exists.
|
|
115
|
+
"""
|
|
116
|
+
# Move bool to the front
|
|
117
|
+
if bool in allowed_types and allowed_types[0] != bool:
|
|
118
|
+
allowed_types.remove(bool)
|
|
119
|
+
allowed_types.insert(0, bool)
|
|
120
|
+
|
|
121
|
+
def proto(_v: Any) -> T:
|
|
122
|
+
return _record_value_to_proto(_v, allowed_types, value_proto_class)
|
|
123
|
+
|
|
124
|
+
return {k: proto(v) for k, v in value_dict.items()}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def record_value_dict_from_proto(
|
|
128
|
+
value_dict_proto: MutableMapping[str, Any]
|
|
129
|
+
) -> dict[str, Any]:
|
|
130
|
+
"""Deserialize the record value dict from ProtoBuf."""
|
|
131
|
+
return {k: _record_value_from_proto(v) for k, v in value_dict_proto.items()}
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def error_to_proto(error: Error) -> ProtoError:
|
|
135
|
+
"""Serialize Error to ProtoBuf."""
|
|
136
|
+
reason = error.reason if error.reason else ""
|
|
137
|
+
return ProtoError(code=error.code, reason=reason)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def error_from_proto(error_proto: ProtoError) -> Error:
|
|
141
|
+
"""Deserialize Error from ProtoBuf."""
|
|
142
|
+
reason = error_proto.reason if len(error_proto.reason) > 0 else None
|
|
143
|
+
return Error(code=error_proto.code, reason=reason)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def metadata_to_proto(metadata: Metadata) -> ProtoMetadata:
|
|
147
|
+
"""Serialize `Metadata` to ProtoBuf."""
|
|
148
|
+
proto = ProtoMetadata( # pylint: disable=E1101
|
|
149
|
+
run_id=metadata.run_id,
|
|
150
|
+
message_id=metadata.message_id,
|
|
151
|
+
src_node_id=metadata.src_node_id,
|
|
152
|
+
dst_node_id=metadata.dst_node_id,
|
|
153
|
+
reply_to_message_id=metadata.reply_to_message_id,
|
|
154
|
+
group_id=metadata.group_id,
|
|
155
|
+
ttl=metadata.ttl,
|
|
156
|
+
message_type=metadata.message_type,
|
|
157
|
+
created_at=metadata.created_at,
|
|
158
|
+
)
|
|
159
|
+
return proto
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def metadata_from_proto(metadata_proto: ProtoMetadata) -> Metadata:
|
|
163
|
+
"""Deserialize `Metadata` from ProtoBuf."""
|
|
164
|
+
metadata = Metadata(
|
|
165
|
+
run_id=metadata_proto.run_id,
|
|
166
|
+
message_id=metadata_proto.message_id,
|
|
167
|
+
src_node_id=metadata_proto.src_node_id,
|
|
168
|
+
dst_node_id=metadata_proto.dst_node_id,
|
|
169
|
+
reply_to_message_id=metadata_proto.reply_to_message_id,
|
|
170
|
+
group_id=metadata_proto.group_id,
|
|
171
|
+
created_at=metadata_proto.created_at,
|
|
172
|
+
ttl=metadata_proto.ttl,
|
|
173
|
+
message_type=metadata_proto.message_type,
|
|
174
|
+
)
|
|
175
|
+
return metadata
|
flwr/common/telemetry.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
1
|
+
# Copyright 2025 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.
|
|
@@ -124,7 +124,7 @@ class EventType(str, Enum):
|
|
|
124
124
|
# This method combined with auto() will set the property value to
|
|
125
125
|
# the property name e.g.
|
|
126
126
|
# `START_CLIENT = auto()` becomes `START_CLIENT = "START_CLIENT"`
|
|
127
|
-
# The type signature is not compatible with mypy
|
|
127
|
+
# The type signature is not compatible with mypy and pylint
|
|
128
128
|
# so each of those needs to be disabled for this line.
|
|
129
129
|
# pylint: disable-next=no-self-argument,arguments-differ,line-too-long
|
|
130
130
|
def _generate_next_value_(name: str, start: int, count: int, last_values: list[Any]) -> Any: # type: ignore # noqa: E501
|
flwr/common/typing.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
1
|
+
# Copyright 2025 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.
|
|
@@ -230,6 +230,7 @@ class Run: # pylint: disable=too-many-instance-attributes
|
|
|
230
230
|
running_at: str
|
|
231
231
|
finished_at: str
|
|
232
232
|
status: RunStatus
|
|
233
|
+
flwr_aid: str
|
|
233
234
|
|
|
234
235
|
@classmethod
|
|
235
236
|
def create_empty(cls, run_id: int) -> "Run":
|
|
@@ -245,6 +246,7 @@ class Run: # pylint: disable=too-many-instance-attributes
|
|
|
245
246
|
running_at="",
|
|
246
247
|
finished_at="",
|
|
247
248
|
status=RunStatus(status="", sub_status="", details=""),
|
|
249
|
+
flwr_aid="",
|
|
248
250
|
)
|
|
249
251
|
|
|
250
252
|
|
|
@@ -289,11 +291,11 @@ class UserAuthCredentials:
|
|
|
289
291
|
|
|
290
292
|
|
|
291
293
|
@dataclass
|
|
292
|
-
class
|
|
294
|
+
class AccountInfo:
|
|
293
295
|
"""User information for event log."""
|
|
294
296
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
+
flwr_aid: Optional[str]
|
|
298
|
+
account_name: Optional[str]
|
|
297
299
|
|
|
298
300
|
|
|
299
301
|
@dataclass
|
flwr/common/version.py
CHANGED
flwr/compat/__init__.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Copyright 2025 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
|
+
"""Compatibility package containing deprecated legacy components."""
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Copyright 2025 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
|
+
"""Legacy components previously located in ``flwr.client``."""
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
1
|
+
# Copyright 2025 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.
|
|
@@ -15,38 +15,30 @@
|
|
|
15
15
|
"""Flower client app."""
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
import multiprocessing
|
|
19
|
-
import os
|
|
20
|
-
import sys
|
|
21
|
-
import threading
|
|
22
18
|
import time
|
|
23
19
|
from contextlib import AbstractContextManager
|
|
24
20
|
from logging import ERROR, INFO, WARN
|
|
25
|
-
from os import urandom
|
|
26
21
|
from pathlib import Path
|
|
27
|
-
from typing import Callable, Optional, Union
|
|
22
|
+
from typing import Callable, Optional, Union
|
|
28
23
|
|
|
29
|
-
import grpc
|
|
30
24
|
from cryptography.hazmat.primitives.asymmetric import ec
|
|
31
25
|
from grpc import RpcError
|
|
32
26
|
|
|
27
|
+
from flwr.app.error import Error
|
|
33
28
|
from flwr.cli.config_utils import get_fab_metadata
|
|
34
29
|
from flwr.cli.install import install_from_fab
|
|
35
30
|
from flwr.client.client import Client
|
|
36
31
|
from flwr.client.client_app import ClientApp, LoadClientAppError
|
|
37
|
-
from flwr.client.
|
|
38
|
-
from flwr.client.
|
|
32
|
+
from flwr.client.grpc_adapter_client.connection import grpc_adapter
|
|
33
|
+
from flwr.client.grpc_rere_client.connection import grpc_request_response
|
|
34
|
+
from flwr.client.message_handler.message_handler import handle_control_message
|
|
35
|
+
from flwr.client.numpy_client import NumPyClient
|
|
36
|
+
from flwr.client.run_info_store import DeprecatedRunInfoStore
|
|
39
37
|
from flwr.client.typing import ClientFnExt
|
|
40
38
|
from flwr.common import GRPC_MAX_MESSAGE_LENGTH, Context, EventType, Message, event
|
|
41
39
|
from flwr.common.address import parse_address
|
|
42
40
|
from flwr.common.constant import (
|
|
43
|
-
CLIENT_OCTET,
|
|
44
|
-
CLIENTAPPIO_API_DEFAULT_SERVER_ADDRESS,
|
|
45
|
-
ISOLATION_MODE_PROCESS,
|
|
46
|
-
ISOLATION_MODE_SUBPROCESS,
|
|
47
41
|
MAX_RETRY_DELAY,
|
|
48
|
-
RUN_ID_NUM_BYTES,
|
|
49
|
-
SERVER_OCTET,
|
|
50
42
|
TRANSPORT_TYPE_GRPC_ADAPTER,
|
|
51
43
|
TRANSPORT_TYPE_GRPC_BIDI,
|
|
52
44
|
TRANSPORT_TYPE_GRPC_RERE,
|
|
@@ -55,20 +47,11 @@ from flwr.common.constant import (
|
|
|
55
47
|
ErrorCode,
|
|
56
48
|
)
|
|
57
49
|
from flwr.common.exit import ExitCode, flwr_exit
|
|
58
|
-
from flwr.common.grpc import generic_create_grpc_server
|
|
59
50
|
from flwr.common.logger import log, warn_deprecated_feature
|
|
60
|
-
from flwr.common.message import Error
|
|
61
51
|
from flwr.common.retry_invoker import RetryInvoker, RetryState, exponential
|
|
62
52
|
from flwr.common.typing import Fab, Run, RunNotRunningException, UserConfig
|
|
63
|
-
from flwr.
|
|
64
|
-
|
|
65
|
-
from .clientapp.clientappio_servicer import ClientAppInputs, ClientAppIoServicer
|
|
66
|
-
from .grpc_adapter_client.connection import grpc_adapter
|
|
67
|
-
from .grpc_client.connection import grpc_connection
|
|
68
|
-
from .grpc_rere_client.connection import grpc_request_response
|
|
69
|
-
from .message_handler.message_handler import handle_control_message
|
|
70
|
-
from .numpy_client import NumPyClient
|
|
71
|
-
from .run_info_store import DeprecatedRunInfoStore
|
|
53
|
+
from flwr.compat.client.grpc_client.connection import grpc_connection
|
|
54
|
+
from flwr.supernode.nodestate import NodeStateFactory
|
|
72
55
|
|
|
73
56
|
|
|
74
57
|
def _check_actionable_client(
|
|
@@ -158,33 +141,33 @@ def start_client(
|
|
|
158
141
|
|
|
159
142
|
Examples
|
|
160
143
|
--------
|
|
161
|
-
Starting a gRPC client with an insecure server connection
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
Starting
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
Starting
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
144
|
+
Starting a gRPC client with an insecure server connection::
|
|
145
|
+
|
|
146
|
+
start_client(
|
|
147
|
+
server_address=localhost:8080,
|
|
148
|
+
client_fn=client_fn,
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
Starting a TLS-enabled gRPC client using system certificates::
|
|
152
|
+
|
|
153
|
+
def client_fn(context: Context):
|
|
154
|
+
return FlowerClient().to_client()
|
|
155
|
+
|
|
156
|
+
start_client(
|
|
157
|
+
server_address=localhost:8080,
|
|
158
|
+
client_fn=client_fn,
|
|
159
|
+
insecure=False,
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
Starting a TLS-enabled gRPC client using provided certificates::
|
|
163
|
+
|
|
164
|
+
from pathlib import Path
|
|
165
|
+
|
|
166
|
+
start_client(
|
|
167
|
+
server_address=localhost:8080,
|
|
168
|
+
client_fn=client_fn,
|
|
169
|
+
root_certificates=Path("/crts/root.pem").read_bytes(),
|
|
170
|
+
)
|
|
188
171
|
"""
|
|
189
172
|
msg = (
|
|
190
173
|
"flwr.client.start_client() is deprecated."
|
|
@@ -236,8 +219,6 @@ def start_client_internal(
|
|
|
236
219
|
max_retries: Optional[int] = None,
|
|
237
220
|
max_wait_time: Optional[float] = None,
|
|
238
221
|
flwr_path: Optional[Path] = None,
|
|
239
|
-
isolation: Optional[str] = None,
|
|
240
|
-
clientappio_api_address: Optional[str] = CLIENTAPPIO_API_DEFAULT_SERVER_ADDRESS,
|
|
241
222
|
) -> None:
|
|
242
223
|
"""Start a Flower client node which connects to a Flower server.
|
|
243
224
|
|
|
@@ -290,17 +271,6 @@ def start_client_internal(
|
|
|
290
271
|
If set to None, there is no limit to the total time.
|
|
291
272
|
flwr_path: Optional[Path] (default: None)
|
|
292
273
|
The fully resolved path containing installed Flower Apps.
|
|
293
|
-
isolation : Optional[str] (default: None)
|
|
294
|
-
Isolation mode for `ClientApp`. Possible values are `subprocess` and
|
|
295
|
-
`process`. Defaults to `None`, which runs the `ClientApp` in the same process
|
|
296
|
-
as the SuperNode. If `subprocess`, the `ClientApp` runs in a subprocess started
|
|
297
|
-
by the SueprNode and communicates using gRPC at the address
|
|
298
|
-
`clientappio_api_address`. If `process`, the `ClientApp` runs in a separate
|
|
299
|
-
isolated process and communicates using gRPC at the address
|
|
300
|
-
`clientappio_api_address`.
|
|
301
|
-
clientappio_api_address : Optional[str]
|
|
302
|
-
(default: `CLIENTAPPIO_API_DEFAULT_SERVER_ADDRESS`)
|
|
303
|
-
The SuperNode gRPC server address.
|
|
304
274
|
"""
|
|
305
275
|
if insecure is None:
|
|
306
276
|
insecure = root_certificates is None
|
|
@@ -326,18 +296,6 @@ def start_client_internal(
|
|
|
326
296
|
|
|
327
297
|
load_client_app_fn = _load_client_app
|
|
328
298
|
|
|
329
|
-
if isolation:
|
|
330
|
-
if clientappio_api_address is None:
|
|
331
|
-
raise ValueError(
|
|
332
|
-
f"`clientappio_api_address` required when `isolation` is "
|
|
333
|
-
f"{ISOLATION_MODE_SUBPROCESS} or {ISOLATION_MODE_PROCESS}",
|
|
334
|
-
)
|
|
335
|
-
_clientappio_grpc_server, clientappio_servicer = run_clientappio_api_grpc(
|
|
336
|
-
address=clientappio_api_address,
|
|
337
|
-
certificates=None,
|
|
338
|
-
)
|
|
339
|
-
clientappio_api_address = cast(str, clientappio_api_address)
|
|
340
|
-
|
|
341
299
|
# At this point, only `load_client_app_fn` should be used
|
|
342
300
|
# Both `client` and `client_fn` must not be used directly
|
|
343
301
|
|
|
@@ -388,7 +346,6 @@ def start_client_internal(
|
|
|
388
346
|
run_info_store: Optional[DeprecatedRunInfoStore] = None
|
|
389
347
|
state_factory = NodeStateFactory()
|
|
390
348
|
state = state_factory.state()
|
|
391
|
-
mp_spawn_context = multiprocessing.get_context("spawn")
|
|
392
349
|
|
|
393
350
|
runs: dict[int, Run] = {}
|
|
394
351
|
|
|
@@ -473,9 +430,8 @@ def start_client_internal(
|
|
|
473
430
|
run: Run = runs[run_id]
|
|
474
431
|
if get_fab is not None and run.fab_hash:
|
|
475
432
|
fab = get_fab(run.fab_hash, run_id)
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
install_from_fab(fab.content, flwr_path, True)
|
|
433
|
+
# If `ClientApp` runs in the same process, install the FAB
|
|
434
|
+
install_from_fab(fab.content, flwr_path, True)
|
|
479
435
|
fab_id, fab_version = get_fab_metadata(fab.content)
|
|
480
436
|
else:
|
|
481
437
|
fab = None
|
|
@@ -502,73 +458,13 @@ def start_client_internal(
|
|
|
502
458
|
|
|
503
459
|
# Handle app loading and task message
|
|
504
460
|
try:
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
# 2. `process`: ClientApp process gets started separately
|
|
510
|
-
# (via `flwr-clientapp`), for example, in a separate
|
|
511
|
-
# Docker container.
|
|
512
|
-
|
|
513
|
-
# Generate SuperNode token
|
|
514
|
-
token = int.from_bytes(urandom(RUN_ID_NUM_BYTES), "little")
|
|
515
|
-
|
|
516
|
-
# Mode 1: SuperNode starts ClientApp as subprocess
|
|
517
|
-
start_subprocess = isolation == ISOLATION_MODE_SUBPROCESS
|
|
518
|
-
|
|
519
|
-
# Share Message and Context with servicer
|
|
520
|
-
clientappio_servicer.set_inputs(
|
|
521
|
-
clientapp_input=ClientAppInputs(
|
|
522
|
-
message=message,
|
|
523
|
-
context=context,
|
|
524
|
-
run=run,
|
|
525
|
-
fab=fab,
|
|
526
|
-
token=token,
|
|
527
|
-
),
|
|
528
|
-
token_returned=start_subprocess,
|
|
529
|
-
)
|
|
530
|
-
|
|
531
|
-
if start_subprocess:
|
|
532
|
-
_octet, _colon, _port = (
|
|
533
|
-
clientappio_api_address.rpartition(":")
|
|
534
|
-
)
|
|
535
|
-
io_address = (
|
|
536
|
-
f"{CLIENT_OCTET}:{_port}"
|
|
537
|
-
if _octet == SERVER_OCTET
|
|
538
|
-
else clientappio_api_address
|
|
539
|
-
)
|
|
540
|
-
# Start ClientApp subprocess
|
|
541
|
-
command = [
|
|
542
|
-
"flwr-clientapp",
|
|
543
|
-
"--clientappio-api-address",
|
|
544
|
-
io_address,
|
|
545
|
-
"--token",
|
|
546
|
-
str(token),
|
|
547
|
-
]
|
|
548
|
-
command.append("--insecure")
|
|
549
|
-
|
|
550
|
-
proc = mp_spawn_context.Process(
|
|
551
|
-
target=_run_flwr_clientapp,
|
|
552
|
-
args=(command, os.getpid()),
|
|
553
|
-
daemon=True,
|
|
554
|
-
)
|
|
555
|
-
proc.start()
|
|
556
|
-
proc.join()
|
|
557
|
-
else:
|
|
558
|
-
# Wait for output to become available
|
|
559
|
-
while not clientappio_servicer.has_outputs():
|
|
560
|
-
time.sleep(0.1)
|
|
561
|
-
|
|
562
|
-
outputs = clientappio_servicer.get_outputs()
|
|
563
|
-
reply_message, context = outputs.message, outputs.context
|
|
564
|
-
else:
|
|
565
|
-
# Load ClientApp instance
|
|
566
|
-
client_app: ClientApp = load_client_app_fn(
|
|
567
|
-
fab_id, fab_version, run.fab_hash
|
|
568
|
-
)
|
|
461
|
+
# Load ClientApp instance
|
|
462
|
+
client_app: ClientApp = load_client_app_fn(
|
|
463
|
+
fab_id, fab_version, run.fab_hash
|
|
464
|
+
)
|
|
569
465
|
|
|
570
|
-
|
|
571
|
-
|
|
466
|
+
# Execute ClientApp
|
|
467
|
+
reply_message = client_app(message=message, context=context)
|
|
572
468
|
except Exception as ex: # pylint: disable=broad-exception-caught
|
|
573
469
|
|
|
574
470
|
# Legacy grpc-bidi
|
|
@@ -684,30 +580,30 @@ def start_numpy_client(
|
|
|
684
580
|
|
|
685
581
|
Examples
|
|
686
582
|
--------
|
|
687
|
-
Starting a gRPC client with an insecure server connection
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
Starting
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
Starting
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
583
|
+
Starting a gRPC client with an insecure server connection::
|
|
584
|
+
|
|
585
|
+
start_numpy_client(
|
|
586
|
+
server_address=localhost:8080,
|
|
587
|
+
client=FlowerClient(),
|
|
588
|
+
)
|
|
589
|
+
|
|
590
|
+
Starting a TLS-enabled gRPC client using system certificates::
|
|
591
|
+
|
|
592
|
+
start_numpy_client(
|
|
593
|
+
server_address=localhost:8080,
|
|
594
|
+
client=FlowerClient(),
|
|
595
|
+
insecure=False,
|
|
596
|
+
)
|
|
597
|
+
|
|
598
|
+
Starting a TLS-enabled gRPC client using provided certificates::
|
|
599
|
+
|
|
600
|
+
from pathlib import Path
|
|
601
|
+
|
|
602
|
+
start_numpy_client(
|
|
603
|
+
server_address=localhost:8080,
|
|
604
|
+
client=FlowerClient(),
|
|
605
|
+
root_certificates=Path("/crts/root.pem").read_bytes(),
|
|
606
|
+
)
|
|
711
607
|
"""
|
|
712
608
|
mssg = (
|
|
713
609
|
"flwr.client.start_numpy_client() is deprecated. \n\tInstead, use "
|
|
@@ -781,7 +677,7 @@ def _init_connection(transport: Optional[str], server_address: str) -> tuple[
|
|
|
781
677
|
try:
|
|
782
678
|
from requests.exceptions import ConnectionError as RequestsConnectionError
|
|
783
679
|
|
|
784
|
-
from .rest_client.connection import http_request_response
|
|
680
|
+
from flwr.client.rest_client.connection import http_request_response
|
|
785
681
|
except ModuleNotFoundError:
|
|
786
682
|
flwr_exit(ExitCode.COMMON_MISSING_EXTRA_REST)
|
|
787
683
|
if server_address[:4] != "http":
|
|
@@ -792,46 +688,10 @@ def _init_connection(transport: Optional[str], server_address: str) -> tuple[
|
|
|
792
688
|
elif transport == TRANSPORT_TYPE_GRPC_ADAPTER:
|
|
793
689
|
connection, error_type = grpc_adapter, RpcError
|
|
794
690
|
elif transport == TRANSPORT_TYPE_GRPC_BIDI:
|
|
795
|
-
connection, error_type = grpc_connection, RpcError
|
|
691
|
+
connection, error_type = grpc_connection, RpcError # type: ignore[assignment]
|
|
796
692
|
else:
|
|
797
693
|
raise ValueError(
|
|
798
694
|
f"Unknown transport type: {transport} (possible: {TRANSPORT_TYPES})"
|
|
799
695
|
)
|
|
800
696
|
|
|
801
697
|
return connection, address, error_type
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
def _run_flwr_clientapp(args: list[str], main_pid: int) -> None:
|
|
805
|
-
# Monitor the main process in case of SIGKILL
|
|
806
|
-
def main_process_monitor() -> None:
|
|
807
|
-
while True:
|
|
808
|
-
time.sleep(1)
|
|
809
|
-
if os.getppid() != main_pid:
|
|
810
|
-
os.kill(os.getpid(), 9)
|
|
811
|
-
|
|
812
|
-
threading.Thread(target=main_process_monitor, daemon=True).start()
|
|
813
|
-
|
|
814
|
-
# Run the command
|
|
815
|
-
sys.argv = args
|
|
816
|
-
flwr_clientapp()
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
def run_clientappio_api_grpc(
|
|
820
|
-
address: str,
|
|
821
|
-
certificates: Optional[tuple[bytes, bytes, bytes]],
|
|
822
|
-
) -> tuple[grpc.Server, ClientAppIoServicer]:
|
|
823
|
-
"""Run ClientAppIo API gRPC server."""
|
|
824
|
-
clientappio_servicer: grpc.Server = ClientAppIoServicer()
|
|
825
|
-
clientappio_add_servicer_to_server_fn = add_ClientAppIoServicer_to_server
|
|
826
|
-
clientappio_grpc_server = generic_create_grpc_server(
|
|
827
|
-
servicer_and_add_fn=(
|
|
828
|
-
clientappio_servicer,
|
|
829
|
-
clientappio_add_servicer_to_server_fn,
|
|
830
|
-
),
|
|
831
|
-
server_address=address,
|
|
832
|
-
max_message_length=GRPC_MAX_MESSAGE_LENGTH,
|
|
833
|
-
certificates=certificates,
|
|
834
|
-
)
|
|
835
|
-
log(INFO, "Starting Flower ClientAppIo gRPC server on %s", address)
|
|
836
|
-
clientappio_grpc_server.start()
|
|
837
|
-
return clientappio_grpc_server, clientappio_servicer
|