flwr 1.12.0__py3-none-any.whl → 1.13.1__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/cli/app.py +2 -0
- flwr/cli/build.py +2 -2
- flwr/cli/config_utils.py +97 -0
- flwr/cli/install.py +0 -16
- flwr/cli/log.py +63 -97
- flwr/cli/ls.py +228 -0
- flwr/cli/new/new.py +23 -13
- flwr/cli/new/templates/app/README.md.tpl +11 -0
- flwr/cli/new/templates/app/code/flwr_tune/dataset.py.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +2 -1
- 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 +2 -2
- flwr/cli/run/run.py +37 -89
- flwr/client/app.py +73 -34
- flwr/client/clientapp/app.py +58 -37
- flwr/client/grpc_rere_client/connection.py +7 -12
- flwr/client/nodestate/__init__.py +25 -0
- flwr/client/nodestate/in_memory_nodestate.py +38 -0
- flwr/client/nodestate/nodestate.py +30 -0
- flwr/client/nodestate/nodestate_factory.py +37 -0
- flwr/client/rest_client/connection.py +4 -14
- flwr/client/{node_state.py → run_info_store.py} +4 -3
- flwr/client/supernode/app.py +34 -58
- flwr/common/args.py +152 -0
- flwr/common/config.py +10 -0
- flwr/common/constant.py +59 -7
- flwr/common/context.py +9 -4
- flwr/common/date.py +21 -3
- flwr/common/grpc.py +4 -1
- flwr/common/logger.py +108 -1
- flwr/common/object_ref.py +47 -16
- flwr/common/serde.py +34 -0
- flwr/common/telemetry.py +0 -6
- flwr/common/typing.py +32 -2
- flwr/proto/exec_pb2.py +23 -17
- flwr/proto/exec_pb2.pyi +58 -22
- flwr/proto/exec_pb2_grpc.py +34 -0
- flwr/proto/exec_pb2_grpc.pyi +13 -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 +8 -8
- flwr/proto/message_pb2.pyi +4 -1
- flwr/proto/run_pb2.py +32 -27
- flwr/proto/run_pb2.pyi +44 -1
- flwr/proto/serverappio_pb2.py +52 -0
- flwr/proto/{driver_pb2.pyi → serverappio_pb2.pyi} +54 -0
- flwr/proto/serverappio_pb2_grpc.py +376 -0
- flwr/proto/serverappio_pb2_grpc.pyi +147 -0
- flwr/proto/simulationio_pb2.py +38 -0
- flwr/proto/simulationio_pb2.pyi +65 -0
- flwr/proto/simulationio_pb2_grpc.py +205 -0
- flwr/proto/simulationio_pb2_grpc.pyi +81 -0
- flwr/server/app.py +297 -162
- flwr/server/driver/driver.py +15 -1
- flwr/server/driver/grpc_driver.py +89 -50
- flwr/server/driver/inmemory_driver.py +6 -16
- flwr/server/run_serverapp.py +11 -235
- flwr/server/{superlink/state → serverapp}/__init__.py +3 -9
- flwr/server/serverapp/app.py +234 -0
- flwr/server/strategy/aggregate.py +4 -4
- flwr/server/strategy/fedadam.py +11 -1
- flwr/server/superlink/driver/__init__.py +1 -1
- flwr/server/superlink/driver/{driver_grpc.py → serverappio_grpc.py} +19 -16
- flwr/server/superlink/driver/{driver_servicer.py → serverappio_servicer.py} +125 -39
- flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +4 -2
- flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +2 -2
- flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +4 -2
- flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +2 -2
- flwr/server/superlink/fleet/message_handler/message_handler.py +7 -7
- flwr/server/superlink/fleet/rest_rere/rest_api.py +10 -9
- flwr/server/superlink/fleet/vce/vce_api.py +23 -23
- flwr/server/superlink/linkstate/__init__.py +28 -0
- flwr/server/superlink/{state/in_memory_state.py → linkstate/in_memory_linkstate.py} +237 -64
- flwr/server/superlink/{state/state.py → linkstate/linkstate.py} +166 -22
- flwr/server/superlink/{state/state_factory.py → linkstate/linkstate_factory.py} +9 -9
- flwr/server/superlink/{state/sqlite_state.py → linkstate/sqlite_linkstate.py} +383 -174
- 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 +153 -0
- flwr/simulation/__init__.py +5 -1
- flwr/simulation/app.py +236 -347
- flwr/simulation/legacy_app.py +402 -0
- flwr/simulation/ray_transport/ray_client_proxy.py +2 -2
- flwr/simulation/run_simulation.py +56 -141
- flwr/simulation/simulationio_connection.py +86 -0
- flwr/superexec/app.py +6 -134
- flwr/superexec/deployment.py +70 -69
- flwr/superexec/exec_grpc.py +15 -8
- flwr/superexec/exec_servicer.py +65 -65
- flwr/superexec/executor.py +26 -7
- flwr/superexec/simulation.py +62 -150
- {flwr-1.12.0.dist-info → flwr-1.13.1.dist-info}/METADATA +9 -7
- {flwr-1.12.0.dist-info → flwr-1.13.1.dist-info}/RECORD +105 -85
- {flwr-1.12.0.dist-info → flwr-1.13.1.dist-info}/entry_points.txt +2 -0
- flwr/client/node_state_tests.py +0 -66
- flwr/proto/driver_pb2.py +0 -42
- flwr/proto/driver_pb2_grpc.py +0 -239
- flwr/proto/driver_pb2_grpc.pyi +0 -94
- flwr/server/superlink/state/utils.py +0 -148
- {flwr-1.12.0.dist-info → flwr-1.13.1.dist-info}/LICENSE +0 -0
- {flwr-1.12.0.dist-info → flwr-1.13.1.dist-info}/WHEEL +0 -0
flwr/client/clientapp/app.py
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"""Flower ClientApp process."""
|
|
16
16
|
|
|
17
17
|
import argparse
|
|
18
|
+
import sys
|
|
18
19
|
import time
|
|
19
20
|
from logging import DEBUG, ERROR, INFO
|
|
20
21
|
from typing import Optional
|
|
@@ -24,7 +25,9 @@ import grpc
|
|
|
24
25
|
from flwr.cli.install import install_from_fab
|
|
25
26
|
from flwr.client.client_app import ClientApp, LoadClientAppError
|
|
26
27
|
from flwr.common import Context, Message
|
|
27
|
-
from flwr.common.
|
|
28
|
+
from flwr.common.args import add_args_flwr_app_common
|
|
29
|
+
from flwr.common.config import get_flwr_dir
|
|
30
|
+
from flwr.common.constant import CLIENTAPPIO_API_DEFAULT_CLIENT_ADDRESS, ErrorCode
|
|
28
31
|
from flwr.common.grpc import create_channel
|
|
29
32
|
from flwr.common.logger import log
|
|
30
33
|
from flwr.common.message import Error
|
|
@@ -54,32 +57,30 @@ from .utils import get_load_client_app_fn
|
|
|
54
57
|
|
|
55
58
|
def flwr_clientapp() -> None:
|
|
56
59
|
"""Run process-isolated Flower ClientApp."""
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
help="Address of SuperNode ClientAppIo gRPC servicer",
|
|
66
|
-
)
|
|
67
|
-
parser.add_argument(
|
|
68
|
-
"--token",
|
|
69
|
-
type=int,
|
|
70
|
-
required=False,
|
|
71
|
-
help="Unique token generated by SuperNode for each ClientApp execution",
|
|
72
|
-
)
|
|
73
|
-
args = parser.parse_args()
|
|
60
|
+
args = _parse_args_run_flwr_clientapp().parse_args()
|
|
61
|
+
if not args.insecure:
|
|
62
|
+
log(
|
|
63
|
+
ERROR,
|
|
64
|
+
"flwr-clientapp does not support TLS yet. "
|
|
65
|
+
"Please use the '--insecure' flag.",
|
|
66
|
+
)
|
|
67
|
+
sys.exit(1)
|
|
74
68
|
|
|
69
|
+
log(INFO, "Starting Flower ClientApp")
|
|
75
70
|
log(
|
|
76
71
|
DEBUG,
|
|
77
|
-
"
|
|
72
|
+
"Starting isolated `ClientApp` connected to SuperNode's ClientAppIo API at %s "
|
|
78
73
|
"with token %s",
|
|
79
|
-
args.
|
|
74
|
+
args.clientappio_api_address,
|
|
80
75
|
args.token,
|
|
81
76
|
)
|
|
82
|
-
run_clientapp(
|
|
77
|
+
run_clientapp(
|
|
78
|
+
clientappio_api_address=args.clientappio_api_address,
|
|
79
|
+
run_once=(args.token is not None),
|
|
80
|
+
token=args.token,
|
|
81
|
+
flwr_dir=args.flwr_dir,
|
|
82
|
+
certificates=None,
|
|
83
|
+
)
|
|
83
84
|
|
|
84
85
|
|
|
85
86
|
def on_channel_state_change(channel_connectivity: str) -> None:
|
|
@@ -88,28 +89,26 @@ def on_channel_state_change(channel_connectivity: str) -> None:
|
|
|
88
89
|
|
|
89
90
|
|
|
90
91
|
def run_clientapp( # pylint: disable=R0914
|
|
91
|
-
|
|
92
|
+
clientappio_api_address: str,
|
|
93
|
+
run_once: bool,
|
|
92
94
|
token: Optional[int] = None,
|
|
95
|
+
flwr_dir: Optional[str] = None,
|
|
96
|
+
certificates: Optional[bytes] = None,
|
|
93
97
|
) -> None:
|
|
94
|
-
"""Run Flower ClientApp process.
|
|
95
|
-
|
|
96
|
-
Parameters
|
|
97
|
-
----------
|
|
98
|
-
supernode : str
|
|
99
|
-
Address of SuperNode
|
|
100
|
-
token : Optional[int] (default: None)
|
|
101
|
-
Unique SuperNode token for ClientApp-SuperNode authentication
|
|
102
|
-
"""
|
|
98
|
+
"""Run Flower ClientApp process."""
|
|
103
99
|
channel = create_channel(
|
|
104
|
-
server_address=
|
|
105
|
-
insecure=
|
|
100
|
+
server_address=clientappio_api_address,
|
|
101
|
+
insecure=(certificates is None),
|
|
102
|
+
root_certificates=certificates,
|
|
106
103
|
)
|
|
107
104
|
channel.subscribe(on_channel_state_change)
|
|
108
105
|
|
|
106
|
+
# Resolve directory where FABs are installed
|
|
107
|
+
flwr_dir_ = get_flwr_dir(flwr_dir)
|
|
108
|
+
|
|
109
109
|
try:
|
|
110
110
|
stub = ClientAppIoStub(channel)
|
|
111
111
|
|
|
112
|
-
only_once = token is not None
|
|
113
112
|
while True:
|
|
114
113
|
# If token is not set, loop until token is received from SuperNode
|
|
115
114
|
while token is None:
|
|
@@ -122,13 +121,13 @@ def run_clientapp( # pylint: disable=R0914
|
|
|
122
121
|
# Install FAB, if provided
|
|
123
122
|
if fab:
|
|
124
123
|
log(DEBUG, "Flower ClientApp starts FAB installation.")
|
|
125
|
-
install_from_fab(fab.content, flwr_dir=
|
|
124
|
+
install_from_fab(fab.content, flwr_dir=flwr_dir_, skip_prompt=True)
|
|
126
125
|
|
|
127
126
|
load_client_app_fn = get_load_client_app_fn(
|
|
128
127
|
default_app_ref="",
|
|
129
128
|
app_path=None,
|
|
130
129
|
multi_app=True,
|
|
131
|
-
flwr_dir=
|
|
130
|
+
flwr_dir=str(flwr_dir_),
|
|
132
131
|
)
|
|
133
132
|
|
|
134
133
|
try:
|
|
@@ -170,7 +169,7 @@ def run_clientapp( # pylint: disable=R0914
|
|
|
170
169
|
|
|
171
170
|
# Stop the loop if `flwr-clientapp` is expected to process only a single
|
|
172
171
|
# message
|
|
173
|
-
if
|
|
172
|
+
if run_once:
|
|
174
173
|
break
|
|
175
174
|
|
|
176
175
|
except KeyboardInterrupt:
|
|
@@ -233,3 +232,25 @@ def push_message(
|
|
|
233
232
|
except grpc.RpcError as e:
|
|
234
233
|
log(ERROR, "[PushClientAppOutputs] gRPC error occurred: %s", str(e))
|
|
235
234
|
raise e
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def _parse_args_run_flwr_clientapp() -> argparse.ArgumentParser:
|
|
238
|
+
"""Parse flwr-clientapp command line arguments."""
|
|
239
|
+
parser = argparse.ArgumentParser(
|
|
240
|
+
description="Run a Flower ClientApp",
|
|
241
|
+
)
|
|
242
|
+
parser.add_argument(
|
|
243
|
+
"--clientappio-api-address",
|
|
244
|
+
default=CLIENTAPPIO_API_DEFAULT_CLIENT_ADDRESS,
|
|
245
|
+
type=str,
|
|
246
|
+
help="Address of SuperNode's ClientAppIo API (IPv4, IPv6, or a domain name)."
|
|
247
|
+
f"By default, it is set to {CLIENTAPPIO_API_DEFAULT_CLIENT_ADDRESS}.",
|
|
248
|
+
)
|
|
249
|
+
parser.add_argument(
|
|
250
|
+
"--token",
|
|
251
|
+
type=int,
|
|
252
|
+
required=False,
|
|
253
|
+
help="Unique token generated by SuperNode for each ClientApp execution",
|
|
254
|
+
)
|
|
255
|
+
add_args_flwr_app_common(parser=parser)
|
|
256
|
+
return parser
|
|
@@ -41,11 +41,7 @@ from flwr.common.grpc import create_channel
|
|
|
41
41
|
from flwr.common.logger import log
|
|
42
42
|
from flwr.common.message import Message, Metadata
|
|
43
43
|
from flwr.common.retry_invoker import RetryInvoker
|
|
44
|
-
from flwr.common.serde import
|
|
45
|
-
message_from_taskins,
|
|
46
|
-
message_to_taskres,
|
|
47
|
-
user_config_from_proto,
|
|
48
|
-
)
|
|
44
|
+
from flwr.common.serde import message_from_taskins, message_to_taskres, run_from_proto
|
|
49
45
|
from flwr.common.typing import Fab, Run
|
|
50
46
|
from flwr.proto.fab_pb2 import GetFabRequest, GetFabResponse # pylint: disable=E0611
|
|
51
47
|
from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
|
|
@@ -159,6 +155,11 @@ def grpc_request_response( # pylint: disable=R0913,R0914,R0915,R0917
|
|
|
159
155
|
ping_thread: Optional[threading.Thread] = None
|
|
160
156
|
ping_stop_event = threading.Event()
|
|
161
157
|
|
|
158
|
+
# Restrict retries to cases where the status code is UNAVAILABLE
|
|
159
|
+
retry_invoker.should_giveup = (
|
|
160
|
+
lambda e: e.code() != grpc.StatusCode.UNAVAILABLE # type: ignore
|
|
161
|
+
)
|
|
162
|
+
|
|
162
163
|
###########################################################################
|
|
163
164
|
# ping/create_node/delete_node/receive/send/get_run functions
|
|
164
165
|
###########################################################################
|
|
@@ -287,13 +288,7 @@ def grpc_request_response( # pylint: disable=R0913,R0914,R0915,R0917
|
|
|
287
288
|
)
|
|
288
289
|
|
|
289
290
|
# Return fab_id and fab_version
|
|
290
|
-
return
|
|
291
|
-
run_id,
|
|
292
|
-
get_run_response.run.fab_id,
|
|
293
|
-
get_run_response.run.fab_version,
|
|
294
|
-
get_run_response.run.fab_hash,
|
|
295
|
-
user_config_from_proto(get_run_response.run.override_config),
|
|
296
|
-
)
|
|
291
|
+
return run_from_proto(get_run_response.run)
|
|
297
292
|
|
|
298
293
|
def get_fab(fab_hash: str) -> Fab:
|
|
299
294
|
# Call FleetAPI
|
|
@@ -0,0 +1,25 @@
|
|
|
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 NodeState."""
|
|
16
|
+
|
|
17
|
+
from .in_memory_nodestate import InMemoryNodeState as InMemoryNodeState
|
|
18
|
+
from .nodestate import NodeState as NodeState
|
|
19
|
+
from .nodestate_factory import NodeStateFactory as NodeStateFactory
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"InMemoryNodeState",
|
|
23
|
+
"NodeState",
|
|
24
|
+
"NodeStateFactory",
|
|
25
|
+
]
|
|
@@ -0,0 +1,38 @@
|
|
|
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
|
+
"""In-memory NodeState implementation."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
from typing import Optional
|
|
19
|
+
|
|
20
|
+
from flwr.client.nodestate.nodestate import NodeState
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class InMemoryNodeState(NodeState):
|
|
24
|
+
"""In-memory NodeState implementation."""
|
|
25
|
+
|
|
26
|
+
def __init__(self) -> None:
|
|
27
|
+
# Store node_id
|
|
28
|
+
self.node_id: Optional[int] = None
|
|
29
|
+
|
|
30
|
+
def set_node_id(self, node_id: Optional[int]) -> None:
|
|
31
|
+
"""Set the node ID."""
|
|
32
|
+
self.node_id = node_id
|
|
33
|
+
|
|
34
|
+
def get_node_id(self) -> int:
|
|
35
|
+
"""Get the node ID."""
|
|
36
|
+
if self.node_id is None:
|
|
37
|
+
raise ValueError("Node ID not set")
|
|
38
|
+
return self.node_id
|
|
@@ -0,0 +1,30 @@
|
|
|
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
|
+
"""Abstract base class NodeState."""
|
|
16
|
+
|
|
17
|
+
import abc
|
|
18
|
+
from typing import Optional
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class NodeState(abc.ABC):
|
|
22
|
+
"""Abstract NodeState."""
|
|
23
|
+
|
|
24
|
+
@abc.abstractmethod
|
|
25
|
+
def set_node_id(self, node_id: Optional[int]) -> None:
|
|
26
|
+
"""Set the node ID."""
|
|
27
|
+
|
|
28
|
+
@abc.abstractmethod
|
|
29
|
+
def get_node_id(self) -> int:
|
|
30
|
+
"""Get the node ID."""
|
|
@@ -0,0 +1,37 @@
|
|
|
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
|
+
"""Factory class that creates NodeState instances."""
|
|
16
|
+
|
|
17
|
+
import threading
|
|
18
|
+
from typing import Optional
|
|
19
|
+
|
|
20
|
+
from .in_memory_nodestate import InMemoryNodeState
|
|
21
|
+
from .nodestate import NodeState
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class NodeStateFactory:
|
|
25
|
+
"""Factory class that creates NodeState instances."""
|
|
26
|
+
|
|
27
|
+
def __init__(self) -> None:
|
|
28
|
+
self.state_instance: Optional[NodeState] = None
|
|
29
|
+
self.lock = threading.RLock()
|
|
30
|
+
|
|
31
|
+
def state(self) -> NodeState:
|
|
32
|
+
"""Return a State instance and create it, if necessary."""
|
|
33
|
+
# Lock access to NodeStateFactory to prevent returning different instances
|
|
34
|
+
with self.lock:
|
|
35
|
+
if self.state_instance is None:
|
|
36
|
+
self.state_instance = InMemoryNodeState()
|
|
37
|
+
return self.state_instance
|
|
@@ -41,11 +41,7 @@ from flwr.common.constant import (
|
|
|
41
41
|
from flwr.common.logger import log
|
|
42
42
|
from flwr.common.message import Message, Metadata
|
|
43
43
|
from flwr.common.retry_invoker import RetryInvoker
|
|
44
|
-
from flwr.common.serde import
|
|
45
|
-
message_from_taskins,
|
|
46
|
-
message_to_taskres,
|
|
47
|
-
user_config_from_proto,
|
|
48
|
-
)
|
|
44
|
+
from flwr.common.serde import message_from_taskins, message_to_taskres, run_from_proto
|
|
49
45
|
from flwr.common.typing import Fab, Run
|
|
50
46
|
from flwr.proto.fab_pb2 import GetFabRequest, GetFabResponse # pylint: disable=E0611
|
|
51
47
|
from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
|
|
@@ -361,15 +357,9 @@ def http_request_response( # pylint: disable=R0913,R0914,R0915,R0917
|
|
|
361
357
|
# Send the request
|
|
362
358
|
res = _request(req, GetRunResponse, PATH_GET_RUN)
|
|
363
359
|
if res is None:
|
|
364
|
-
return Run(run_id
|
|
365
|
-
|
|
366
|
-
return
|
|
367
|
-
run_id,
|
|
368
|
-
res.run.fab_id,
|
|
369
|
-
res.run.fab_version,
|
|
370
|
-
res.run.fab_hash,
|
|
371
|
-
user_config_from_proto(res.run.override_config),
|
|
372
|
-
)
|
|
360
|
+
return Run.create_empty(run_id)
|
|
361
|
+
|
|
362
|
+
return run_from_proto(res.run)
|
|
373
363
|
|
|
374
364
|
def get_fab(fab_hash: str) -> Fab:
|
|
375
365
|
# Construct the request
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
1
|
+
# Copyright 2024 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.
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
# ==============================================================================
|
|
15
|
-
"""
|
|
15
|
+
"""Deprecated Run Info Store."""
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
from dataclasses import dataclass
|
|
@@ -36,7 +36,7 @@ class RunInfo:
|
|
|
36
36
|
initial_run_config: UserConfig
|
|
37
37
|
|
|
38
38
|
|
|
39
|
-
class
|
|
39
|
+
class DeprecatedRunInfoStore:
|
|
40
40
|
"""State of a node where client nodes execute runs."""
|
|
41
41
|
|
|
42
42
|
def __init__(
|
|
@@ -83,6 +83,7 @@ class NodeState:
|
|
|
83
83
|
self.run_infos[run_id] = RunInfo(
|
|
84
84
|
initial_run_config=initial_run_config,
|
|
85
85
|
context=Context(
|
|
86
|
+
run_id=run_id,
|
|
86
87
|
node_id=self.node_id,
|
|
87
88
|
node_config=self.node_config,
|
|
88
89
|
state=RecordSet(),
|
flwr/client/supernode/app.py
CHANGED
|
@@ -28,9 +28,13 @@ from cryptography.hazmat.primitives.serialization import (
|
|
|
28
28
|
)
|
|
29
29
|
|
|
30
30
|
from flwr.common import EventType, event
|
|
31
|
+
from flwr.common.args import try_obtain_root_certificates
|
|
31
32
|
from flwr.common.config import parse_config_args
|
|
32
33
|
from flwr.common.constant import (
|
|
34
|
+
CLIENTAPPIO_API_DEFAULT_SERVER_ADDRESS,
|
|
33
35
|
FLEET_API_GRPC_RERE_DEFAULT_ADDRESS,
|
|
36
|
+
ISOLATION_MODE_PROCESS,
|
|
37
|
+
ISOLATION_MODE_SUBPROCESS,
|
|
34
38
|
TRANSPORT_TYPE_GRPC_ADAPTER,
|
|
35
39
|
TRANSPORT_TYPE_GRPC_RERE,
|
|
36
40
|
TRANSPORT_TYPE_REST,
|
|
@@ -38,11 +42,7 @@ from flwr.common.constant import (
|
|
|
38
42
|
from flwr.common.exit_handlers import register_exit_handlers
|
|
39
43
|
from flwr.common.logger import log, warn_deprecated_feature
|
|
40
44
|
|
|
41
|
-
from ..app import
|
|
42
|
-
ISOLATION_MODE_PROCESS,
|
|
43
|
-
ISOLATION_MODE_SUBPROCESS,
|
|
44
|
-
start_client_internal,
|
|
45
|
-
)
|
|
45
|
+
from ..app import start_client_internal
|
|
46
46
|
from ..clientapp.utils import get_load_client_app_fn
|
|
47
47
|
|
|
48
48
|
|
|
@@ -63,10 +63,21 @@ def run_supernode() -> None:
|
|
|
63
63
|
"Ignoring `--flwr-dir`.",
|
|
64
64
|
)
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
# Exit if unsupported argument is passed by the user
|
|
67
|
+
if args.app is not None:
|
|
68
|
+
log(
|
|
69
|
+
ERROR,
|
|
70
|
+
"The `app` argument is deprecated. The SuperNode now automatically "
|
|
71
|
+
"uses the ClientApp delivered from the SuperLink. Providing the app "
|
|
72
|
+
"directory manually is no longer supported. Please remove the `app` "
|
|
73
|
+
"argument from your command.",
|
|
74
|
+
)
|
|
75
|
+
sys.exit(1)
|
|
76
|
+
|
|
77
|
+
root_certificates = try_obtain_root_certificates(args, args.superlink)
|
|
67
78
|
load_fn = get_load_client_app_fn(
|
|
68
79
|
default_app_ref="",
|
|
69
|
-
app_path=
|
|
80
|
+
app_path=None,
|
|
70
81
|
flwr_dir=args.flwr_dir,
|
|
71
82
|
multi_app=True,
|
|
72
83
|
)
|
|
@@ -88,7 +99,7 @@ def run_supernode() -> None:
|
|
|
88
99
|
),
|
|
89
100
|
flwr_path=args.flwr_dir,
|
|
90
101
|
isolation=args.isolation,
|
|
91
|
-
|
|
102
|
+
clientappio_api_address=args.clientappio_api_address,
|
|
92
103
|
)
|
|
93
104
|
|
|
94
105
|
# Graceful shutdown
|
|
@@ -128,41 +139,6 @@ def _warn_deprecated_server_arg(args: argparse.Namespace) -> None:
|
|
|
128
139
|
args.superlink = args.server
|
|
129
140
|
|
|
130
141
|
|
|
131
|
-
def _get_certificates(args: argparse.Namespace) -> Optional[bytes]:
|
|
132
|
-
"""Load certificates if specified in args."""
|
|
133
|
-
# Obtain certificates
|
|
134
|
-
if args.insecure:
|
|
135
|
-
if args.root_certificates is not None:
|
|
136
|
-
sys.exit(
|
|
137
|
-
"Conflicting options: The '--insecure' flag disables HTTPS, "
|
|
138
|
-
"but '--root-certificates' was also specified. Please remove "
|
|
139
|
-
"the '--root-certificates' option when running in insecure mode, "
|
|
140
|
-
"or omit '--insecure' to use HTTPS."
|
|
141
|
-
)
|
|
142
|
-
log(
|
|
143
|
-
WARN,
|
|
144
|
-
"Option `--insecure` was set. "
|
|
145
|
-
"Starting insecure HTTP client connected to %s.",
|
|
146
|
-
args.superlink,
|
|
147
|
-
)
|
|
148
|
-
root_certificates = None
|
|
149
|
-
else:
|
|
150
|
-
# Load the certificates if provided, or load the system certificates
|
|
151
|
-
cert_path = args.root_certificates
|
|
152
|
-
if cert_path is None:
|
|
153
|
-
root_certificates = None
|
|
154
|
-
else:
|
|
155
|
-
root_certificates = Path(cert_path).read_bytes()
|
|
156
|
-
log(
|
|
157
|
-
DEBUG,
|
|
158
|
-
"Starting secure HTTPS client connected to %s "
|
|
159
|
-
"with the following certificates: %s.",
|
|
160
|
-
args.superlink,
|
|
161
|
-
cert_path,
|
|
162
|
-
)
|
|
163
|
-
return root_certificates
|
|
164
|
-
|
|
165
|
-
|
|
166
142
|
def _parse_args_run_supernode() -> argparse.ArgumentParser:
|
|
167
143
|
"""Parse flower-supernode command line arguments."""
|
|
168
144
|
parser = argparse.ArgumentParser(
|
|
@@ -173,12 +149,12 @@ def _parse_args_run_supernode() -> argparse.ArgumentParser:
|
|
|
173
149
|
"app",
|
|
174
150
|
nargs="?",
|
|
175
151
|
default=None,
|
|
176
|
-
help=
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
152
|
+
help=(
|
|
153
|
+
"(REMOVED) This argument is removed. The SuperNode now automatically "
|
|
154
|
+
"uses the ClientApp delivered from the SuperLink, so there is no need to "
|
|
155
|
+
"provide the app directory manually. This argument will be removed in a "
|
|
156
|
+
"future version."
|
|
157
|
+
),
|
|
182
158
|
)
|
|
183
159
|
_parse_args_common(parser)
|
|
184
160
|
parser.add_argument(
|
|
@@ -194,22 +170,22 @@ def _parse_args_run_supernode() -> argparse.ArgumentParser:
|
|
|
194
170
|
)
|
|
195
171
|
parser.add_argument(
|
|
196
172
|
"--isolation",
|
|
197
|
-
default=
|
|
173
|
+
default=ISOLATION_MODE_SUBPROCESS,
|
|
198
174
|
required=False,
|
|
199
175
|
choices=[
|
|
200
176
|
ISOLATION_MODE_SUBPROCESS,
|
|
201
177
|
ISOLATION_MODE_PROCESS,
|
|
202
178
|
],
|
|
203
|
-
help="Isolation mode when running `ClientApp` (
|
|
204
|
-
"`subprocess`, `process`).
|
|
205
|
-
"
|
|
206
|
-
"
|
|
207
|
-
"independent process gets created outside of SuperNode.",
|
|
179
|
+
help="Isolation mode when running a `ClientApp` (`subprocess` by default, "
|
|
180
|
+
"possible values: `subprocess`, `process`). Use `subprocess` to configure "
|
|
181
|
+
"SuperNode to run a `ClientApp` in a subprocess. Use `process` to indicate "
|
|
182
|
+
"that a separate independent process gets created outside of SuperNode.",
|
|
208
183
|
)
|
|
209
184
|
parser.add_argument(
|
|
210
|
-
"--
|
|
211
|
-
default=
|
|
212
|
-
help="
|
|
185
|
+
"--clientappio-api-address",
|
|
186
|
+
default=CLIENTAPPIO_API_DEFAULT_SERVER_ADDRESS,
|
|
187
|
+
help="ClientAppIo API (gRPC) server address (IPv4, IPv6, or a domain name). "
|
|
188
|
+
f"By default, it is set to {CLIENTAPPIO_API_DEFAULT_SERVER_ADDRESS}.",
|
|
213
189
|
)
|
|
214
190
|
|
|
215
191
|
return parser
|