flwr-nightly 1.19.0.dev20250612__py3-none-any.whl → 1.19.0.dev20250616__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/ls.py +12 -33
- flwr/cli/utils.py +18 -1
- flwr/common/constant.py +4 -0
- flwr/server/app.py +1 -12
- flwr/server/superlink/linkstate/in_memory_linkstate.py +6 -2
- flwr/server/superlink/serverappio/serverappio_servicer.py +15 -13
- flwr/superexec/exec_grpc.py +3 -0
- flwr/superexec/exec_servicer.py +122 -22
- flwr/supernode/start_client_internal.py +10 -1
- {flwr_nightly-1.19.0.dev20250612.dist-info → flwr_nightly-1.19.0.dev20250616.dist-info}/METADATA +1 -1
- {flwr_nightly-1.19.0.dev20250612.dist-info → flwr_nightly-1.19.0.dev20250616.dist-info}/RECORD +13 -13
- {flwr_nightly-1.19.0.dev20250612.dist-info → flwr_nightly-1.19.0.dev20250616.dist-info}/WHEEL +0 -0
- {flwr_nightly-1.19.0.dev20250612.dist-info → flwr_nightly-1.19.0.dev20250616.dist-info}/entry_points.txt +0 -0
flwr/cli/ls.py
CHANGED
@@ -130,23 +130,16 @@ def ls( # pylint: disable=too-many-locals, too-many-branches, R0913, R0917
|
|
130
130
|
# Display information about a specific run ID
|
131
131
|
if run_id is not None:
|
132
132
|
typer.echo(f"🔍 Displaying information for run ID {run_id}...")
|
133
|
-
|
134
|
-
_display_one_run(stub, run_id, output_format)
|
133
|
+
formatted_runs = _display_one_run(stub, run_id)
|
135
134
|
# By default, list all runs
|
136
135
|
else:
|
137
136
|
typer.echo("📄 Listing all runs...")
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
typer.secho(
|
145
|
-
f"❌ {err}",
|
146
|
-
fg=typer.colors.RED,
|
147
|
-
bold=True,
|
148
|
-
)
|
149
|
-
raise typer.Exit(code=1) from err
|
137
|
+
formatted_runs = _list_runs(stub)
|
138
|
+
restore_output()
|
139
|
+
if output_format == CliOutputFormat.JSON:
|
140
|
+
Console().print_json(_to_json(formatted_runs))
|
141
|
+
else:
|
142
|
+
Console().print(_to_table(formatted_runs))
|
150
143
|
finally:
|
151
144
|
if channel:
|
152
145
|
channel.close()
|
@@ -300,37 +293,23 @@ def _to_json(run_list: list[_RunListType]) -> str:
|
|
300
293
|
return json.dumps({"success": True, "runs": runs_list})
|
301
294
|
|
302
295
|
|
303
|
-
def _list_runs(
|
304
|
-
stub: ExecStub,
|
305
|
-
output_format: str = CliOutputFormat.DEFAULT,
|
306
|
-
) -> None:
|
296
|
+
def _list_runs(stub: ExecStub) -> list[_RunListType]:
|
307
297
|
"""List all runs."""
|
308
298
|
with flwr_cli_grpc_exc_handler():
|
309
299
|
res: ListRunsResponse = stub.ListRuns(ListRunsRequest())
|
310
300
|
run_dict = {run_id: run_from_proto(proto) for run_id, proto in res.run_dict.items()}
|
311
301
|
|
312
|
-
|
313
|
-
if output_format == CliOutputFormat.JSON:
|
314
|
-
Console().print_json(_to_json(formatted_runs))
|
315
|
-
else:
|
316
|
-
Console().print(_to_table(formatted_runs))
|
302
|
+
return _format_runs(run_dict, res.now)
|
317
303
|
|
318
304
|
|
319
|
-
def _display_one_run(
|
320
|
-
stub: ExecStub,
|
321
|
-
run_id: int,
|
322
|
-
output_format: str = CliOutputFormat.DEFAULT,
|
323
|
-
) -> None:
|
305
|
+
def _display_one_run(stub: ExecStub, run_id: int) -> list[_RunListType]:
|
324
306
|
"""Display information about a specific run."""
|
325
307
|
with flwr_cli_grpc_exc_handler():
|
326
308
|
res: ListRunsResponse = stub.ListRuns(ListRunsRequest(run_id=run_id))
|
327
309
|
if not res.run_dict:
|
310
|
+
# This won't be reached as an gRPC error is raised if run_id is invalid
|
328
311
|
raise ValueError(f"Run ID {run_id} not found")
|
329
312
|
|
330
313
|
run_dict = {run_id: run_from_proto(proto) for run_id, proto in res.run_dict.items()}
|
331
314
|
|
332
|
-
|
333
|
-
if output_format == CliOutputFormat.JSON:
|
334
|
-
Console().print_json(_to_json(formatted_runs))
|
335
|
-
else:
|
336
|
-
Console().print(_to_table(formatted_runs))
|
315
|
+
return _format_runs(run_dict, res.now)
|
flwr/cli/utils.py
CHANGED
@@ -28,7 +28,12 @@ import typer
|
|
28
28
|
|
29
29
|
from flwr.cli.cli_user_auth_interceptor import CliUserAuthInterceptor
|
30
30
|
from flwr.common.auth_plugin import CliAuthPlugin
|
31
|
-
from flwr.common.constant import
|
31
|
+
from flwr.common.constant import (
|
32
|
+
AUTH_TYPE_JSON_KEY,
|
33
|
+
CREDENTIALS_DIR,
|
34
|
+
FLWR_DIR,
|
35
|
+
RUN_ID_NOT_FOUND_MESSAGE,
|
36
|
+
)
|
32
37
|
from flwr.common.grpc import (
|
33
38
|
GRPC_MAX_MESSAGE_LENGTH,
|
34
39
|
create_channel,
|
@@ -320,5 +325,17 @@ def flwr_cli_grpc_exc_handler() -> Iterator[None]:
|
|
320
325
|
fg=typer.colors.RED,
|
321
326
|
bold=True,
|
322
327
|
)
|
328
|
+
# pylint: disable=E1101
|
329
|
+
typer.secho(e.details(), fg=typer.colors.RED, bold=True)
|
330
|
+
raise typer.Exit(code=1) from None
|
331
|
+
if (
|
332
|
+
e.code() == grpc.StatusCode.NOT_FOUND
|
333
|
+
and e.details() == RUN_ID_NOT_FOUND_MESSAGE
|
334
|
+
):
|
335
|
+
typer.secho(
|
336
|
+
"❌ Run ID not found.",
|
337
|
+
fg=typer.colors.RED,
|
338
|
+
bold=True,
|
339
|
+
)
|
323
340
|
raise typer.Exit(code=1) from None
|
324
341
|
raise
|
flwr/common/constant.py
CHANGED
@@ -150,6 +150,10 @@ PULL_INITIAL_BACKOFF = 1 # Initial backoff time for pulling objects
|
|
150
150
|
PULL_BACKOFF_CAP = 10 # Maximum backoff time for pulling objects
|
151
151
|
|
152
152
|
|
153
|
+
# ExecServicer constants
|
154
|
+
RUN_ID_NOT_FOUND_MESSAGE = "Run ID not found"
|
155
|
+
|
156
|
+
|
153
157
|
class MessageType:
|
154
158
|
"""Message type."""
|
155
159
|
|
flwr/server/app.py
CHANGED
@@ -91,7 +91,6 @@ P = TypeVar("P", ExecAuthPlugin, ExecAuthzPlugin)
|
|
91
91
|
try:
|
92
92
|
from flwr.ee import (
|
93
93
|
add_ee_args_superlink,
|
94
|
-
get_dashboard_server,
|
95
94
|
get_exec_auth_plugins,
|
96
95
|
get_exec_authz_plugins,
|
97
96
|
get_exec_event_log_writer_plugins,
|
@@ -178,6 +177,7 @@ def run_superlink() -> None:
|
|
178
177
|
address=exec_address,
|
179
178
|
state_factory=state_factory,
|
180
179
|
ffs_factory=ffs_factory,
|
180
|
+
objectstore_factory=objectstore_factory,
|
181
181
|
executor=executor,
|
182
182
|
certificates=certificates,
|
183
183
|
config=parse_config_args(
|
@@ -332,17 +332,6 @@ def run_superlink() -> None:
|
|
332
332
|
scheduler_th.start()
|
333
333
|
bckg_threads.append(scheduler_th)
|
334
334
|
|
335
|
-
# Add Dashboard server if available
|
336
|
-
if dashboard_address := getattr(args, "dashboard_address", None):
|
337
|
-
dashboard_address_str, _, _ = _format_address(dashboard_address)
|
338
|
-
dashboard_server = get_dashboard_server(
|
339
|
-
address=dashboard_address_str,
|
340
|
-
state_factory=state_factory,
|
341
|
-
certificates=None,
|
342
|
-
)
|
343
|
-
|
344
|
-
grpc_servers.append(dashboard_server)
|
345
|
-
|
346
335
|
# Graceful shutdown
|
347
336
|
register_exit_handlers(
|
348
337
|
event_type=EventType.RUN_SUPERLINK_LEAVE,
|
@@ -249,7 +249,9 @@ class InMemoryLinkState(LinkState): # pylint: disable=R0902,R0904
|
|
249
249
|
inquired_in_message_ids=message_ids,
|
250
250
|
found_in_message_dict=self.message_ins_store,
|
251
251
|
node_id_to_online_until={
|
252
|
-
node_id: self.node_ids[node_id][0]
|
252
|
+
node_id: self.node_ids[node_id][0]
|
253
|
+
for node_id in dst_node_ids
|
254
|
+
if node_id in self.node_ids
|
253
255
|
},
|
254
256
|
current_time=current,
|
255
257
|
)
|
@@ -478,7 +480,9 @@ class InMemoryLinkState(LinkState): # pylint: disable=R0902,R0904
|
|
478
480
|
if they have not sent a heartbeat before `active_until`.
|
479
481
|
"""
|
480
482
|
current = now()
|
481
|
-
for record in
|
483
|
+
for record in (self.run_ids.get(run_id) for run_id in run_ids):
|
484
|
+
if record is None:
|
485
|
+
continue
|
482
486
|
with record.lock:
|
483
487
|
if record.run.status.status in (Status.STARTING, Status.RUNNING):
|
484
488
|
if record.active_until < current.timestamp():
|
@@ -25,7 +25,7 @@ from flwr.common import Message
|
|
25
25
|
from flwr.common.constant import SUPERLINK_NODE_ID, Status
|
26
26
|
from flwr.common.inflatable import (
|
27
27
|
UnexpectedObjectContentError,
|
28
|
-
|
28
|
+
get_all_nested_objects,
|
29
29
|
get_object_tree,
|
30
30
|
no_object_id_recompute,
|
31
31
|
)
|
@@ -181,7 +181,7 @@ class ServerAppIoServicer(serverappio_pb2_grpc.ServerAppIoServicer):
|
|
181
181
|
objects_to_push=objects_to_push,
|
182
182
|
)
|
183
183
|
|
184
|
-
def PullMessages(
|
184
|
+
def PullMessages( # pylint: disable=R0914
|
185
185
|
self, request: PullResMessagesRequest, context: grpc.ServicerContext
|
186
186
|
) -> PullResMessagesResponse:
|
187
187
|
"""Pull a set of Messages."""
|
@@ -209,14 +209,18 @@ class ServerAppIoServicer(serverappio_pb2_grpc.ServerAppIoServicer):
|
|
209
209
|
for msg_res in messages_res:
|
210
210
|
if msg_res.metadata.src_node_id == SUPERLINK_NODE_ID:
|
211
211
|
with no_object_id_recompute():
|
212
|
-
|
212
|
+
all_objects = get_all_nested_objects(msg_res)
|
213
|
+
descendants = list(all_objects.keys())[:-1]
|
213
214
|
message_obj_id = msg_res.metadata.message_id
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
215
|
+
# Store mapping
|
216
|
+
store.set_message_descendant_ids(
|
217
|
+
msg_object_id=message_obj_id, descendant_ids=descendants
|
218
|
+
)
|
219
|
+
# Preregister
|
220
|
+
store.preregister(request.run_id, get_object_tree(msg_res))
|
221
|
+
# Store objects
|
222
|
+
for obj_id, obj in all_objects.items():
|
223
|
+
store.put(obj_id, obj.deflate())
|
220
224
|
|
221
225
|
# Delete the instruction Messages and their replies if found
|
222
226
|
message_ins_ids_to_delete = {
|
@@ -243,10 +247,8 @@ class ServerAppIoServicer(serverappio_pb2_grpc.ServerAppIoServicer):
|
|
243
247
|
try:
|
244
248
|
msg_object_id = msg.metadata.message_id
|
245
249
|
descendants = store.get_message_descendant_ids(msg_object_id)
|
246
|
-
#
|
247
|
-
objects_to_pull[msg_object_id] = ObjectIDs(
|
248
|
-
object_ids=descendants + [msg_object_id]
|
249
|
-
)
|
250
|
+
# Add mapping of message object ID to its descendants
|
251
|
+
objects_to_pull[msg_object_id] = ObjectIDs(object_ids=descendants)
|
250
252
|
except NoObjectInStoreError as e:
|
251
253
|
log(ERROR, e.message)
|
252
254
|
# Delete message ins from state
|
flwr/superexec/exec_grpc.py
CHANGED
@@ -29,6 +29,7 @@ from flwr.common.typing import UserConfig
|
|
29
29
|
from flwr.proto.exec_pb2_grpc import add_ExecServicer_to_server
|
30
30
|
from flwr.server.superlink.ffs.ffs_factory import FfsFactory
|
31
31
|
from flwr.server.superlink.linkstate import LinkStateFactory
|
32
|
+
from flwr.supercore.object_store import ObjectStoreFactory
|
32
33
|
from flwr.superexec.exec_event_log_interceptor import ExecEventLogInterceptor
|
33
34
|
from flwr.superexec.exec_user_auth_interceptor import ExecUserAuthInterceptor
|
34
35
|
|
@@ -42,6 +43,7 @@ def run_exec_api_grpc(
|
|
42
43
|
executor: Executor,
|
43
44
|
state_factory: LinkStateFactory,
|
44
45
|
ffs_factory: FfsFactory,
|
46
|
+
objectstore_factory: ObjectStoreFactory,
|
45
47
|
certificates: Optional[tuple[bytes, bytes, bytes]],
|
46
48
|
config: UserConfig,
|
47
49
|
auth_plugin: Optional[ExecAuthPlugin] = None,
|
@@ -54,6 +56,7 @@ def run_exec_api_grpc(
|
|
54
56
|
exec_servicer: grpc.Server = ExecServicer(
|
55
57
|
linkstate_factory=state_factory,
|
56
58
|
ffs_factory=ffs_factory,
|
59
|
+
objectstore_factory=objectstore_factory,
|
57
60
|
executor=executor,
|
58
61
|
auth_plugin=auth_plugin,
|
59
62
|
)
|
flwr/superexec/exec_servicer.py
CHANGED
@@ -18,20 +18,25 @@
|
|
18
18
|
import time
|
19
19
|
from collections.abc import Generator
|
20
20
|
from logging import ERROR, INFO
|
21
|
-
from typing import Any, Optional
|
21
|
+
from typing import Any, Optional, cast
|
22
22
|
|
23
23
|
import grpc
|
24
24
|
|
25
25
|
from flwr.common import now
|
26
26
|
from flwr.common.auth_plugin import ExecAuthPlugin
|
27
|
-
from flwr.common.constant import
|
27
|
+
from flwr.common.constant import (
|
28
|
+
LOG_STREAM_INTERVAL,
|
29
|
+
RUN_ID_NOT_FOUND_MESSAGE,
|
30
|
+
Status,
|
31
|
+
SubStatus,
|
32
|
+
)
|
28
33
|
from flwr.common.logger import log
|
29
34
|
from flwr.common.serde import (
|
30
35
|
config_record_from_proto,
|
31
36
|
run_to_proto,
|
32
37
|
user_config_from_proto,
|
33
38
|
)
|
34
|
-
from flwr.common.typing import RunStatus
|
39
|
+
from flwr.common.typing import Run, RunStatus
|
35
40
|
from flwr.proto import exec_pb2_grpc # pylint: disable=E0611
|
36
41
|
from flwr.proto.exec_pb2 import ( # pylint: disable=E0611
|
37
42
|
GetAuthTokensRequest,
|
@@ -49,6 +54,7 @@ from flwr.proto.exec_pb2 import ( # pylint: disable=E0611
|
|
49
54
|
)
|
50
55
|
from flwr.server.superlink.ffs.ffs_factory import FfsFactory
|
51
56
|
from flwr.server.superlink.linkstate import LinkState, LinkStateFactory
|
57
|
+
from flwr.supercore.object_store import ObjectStore, ObjectStoreFactory
|
52
58
|
|
53
59
|
from .exec_user_auth_interceptor import shared_account_info
|
54
60
|
from .executor import Executor
|
@@ -57,15 +63,17 @@ from .executor import Executor
|
|
57
63
|
class ExecServicer(exec_pb2_grpc.ExecServicer):
|
58
64
|
"""SuperExec API servicer."""
|
59
65
|
|
60
|
-
def __init__(
|
66
|
+
def __init__( # pylint: disable=R0913, R0917
|
61
67
|
self,
|
62
68
|
linkstate_factory: LinkStateFactory,
|
63
69
|
ffs_factory: FfsFactory,
|
70
|
+
objectstore_factory: ObjectStoreFactory,
|
64
71
|
executor: Executor,
|
65
72
|
auth_plugin: Optional[ExecAuthPlugin] = None,
|
66
73
|
) -> None:
|
67
74
|
self.linkstate_factory = linkstate_factory
|
68
75
|
self.ffs_factory = ffs_factory
|
76
|
+
self.objectstore_factory = objectstore_factory
|
69
77
|
self.executor = executor
|
70
78
|
self.executor.initialize(linkstate_factory, ffs_factory)
|
71
79
|
self.auth_plugin = auth_plugin
|
@@ -97,12 +105,20 @@ class ExecServicer(exec_pb2_grpc.ExecServicer):
|
|
97
105
|
log(INFO, "ExecServicer.StreamLogs")
|
98
106
|
state = self.linkstate_factory.state()
|
99
107
|
|
100
|
-
# Retrieve run ID
|
108
|
+
# Retrieve run ID and run
|
101
109
|
run_id = request.run_id
|
110
|
+
run = state.get_run(run_id)
|
102
111
|
|
103
112
|
# Exit if `run_id` not found
|
104
|
-
if not
|
105
|
-
context.abort(grpc.StatusCode.NOT_FOUND,
|
113
|
+
if not run:
|
114
|
+
context.abort(grpc.StatusCode.NOT_FOUND, RUN_ID_NOT_FOUND_MESSAGE)
|
115
|
+
|
116
|
+
# If user auth is enabled, check if `flwr_aid` matches the run's `flwr_aid`
|
117
|
+
if self.auth_plugin:
|
118
|
+
flwr_aid = shared_account_info.get().flwr_aid
|
119
|
+
_check_flwr_aid_in_run(
|
120
|
+
flwr_aid=flwr_aid, run=cast(Run, run), context=context
|
121
|
+
)
|
106
122
|
|
107
123
|
after_timestamp = request.after_timestamp + 1e-6
|
108
124
|
while context.is_active():
|
@@ -121,7 +137,10 @@ class ExecServicer(exec_pb2_grpc.ExecServicer):
|
|
121
137
|
# is returned at this point and the server ends the stream.
|
122
138
|
run_status = state.get_run_status({run_id})[run_id]
|
123
139
|
if run_status.status == Status.FINISHED:
|
124
|
-
log(INFO, "All logs for run ID `%s` returned",
|
140
|
+
log(INFO, "All logs for run ID `%s` returned", run_id)
|
141
|
+
|
142
|
+
# Delete objects of the run from the object store
|
143
|
+
self.objectstore_factory.store().delete_objects_in_run(run_id)
|
125
144
|
break
|
126
145
|
|
127
146
|
time.sleep(LOG_STREAM_INTERVAL) # Sleep briefly to avoid busy waiting
|
@@ -133,11 +152,44 @@ class ExecServicer(exec_pb2_grpc.ExecServicer):
|
|
133
152
|
log(INFO, "ExecServicer.List")
|
134
153
|
state = self.linkstate_factory.state()
|
135
154
|
|
136
|
-
#
|
155
|
+
# Build a set of run IDs for `flwr ls --runs`
|
137
156
|
if not request.HasField("run_id"):
|
138
|
-
|
139
|
-
|
140
|
-
|
157
|
+
if self.auth_plugin:
|
158
|
+
# If no `run_id` is specified and user auth is enabled,
|
159
|
+
# return run IDs for the authenticated user
|
160
|
+
flwr_aid = shared_account_info.get().flwr_aid
|
161
|
+
if flwr_aid is None:
|
162
|
+
context.abort(
|
163
|
+
grpc.StatusCode.PERMISSION_DENIED,
|
164
|
+
"️⛔️ User authentication is enabled, but `flwr_aid` is None",
|
165
|
+
)
|
166
|
+
run_ids = state.get_run_ids(flwr_aid=flwr_aid)
|
167
|
+
else:
|
168
|
+
# If no `run_id` is specified and no user auth is enabled,
|
169
|
+
# return all run IDs
|
170
|
+
run_ids = state.get_run_ids(None)
|
171
|
+
# Build a set of run IDs for `flwr ls --run-id <run_id>`
|
172
|
+
else:
|
173
|
+
# Retrieve run ID and run
|
174
|
+
run_id = request.run_id
|
175
|
+
run = state.get_run(run_id)
|
176
|
+
|
177
|
+
# Exit if `run_id` not found
|
178
|
+
if not run:
|
179
|
+
context.abort(grpc.StatusCode.NOT_FOUND, RUN_ID_NOT_FOUND_MESSAGE)
|
180
|
+
|
181
|
+
# If user auth is enabled, check if `flwr_aid` matches the run's `flwr_aid`
|
182
|
+
if self.auth_plugin:
|
183
|
+
flwr_aid = shared_account_info.get().flwr_aid
|
184
|
+
_check_flwr_aid_in_run(
|
185
|
+
flwr_aid=flwr_aid, run=cast(Run, run), context=context
|
186
|
+
)
|
187
|
+
|
188
|
+
run_ids = {run_id}
|
189
|
+
|
190
|
+
# Init the object store
|
191
|
+
store = self.objectstore_factory.store()
|
192
|
+
return _create_list_runs_response(run_ids, state, store)
|
141
193
|
|
142
194
|
def StopRun(
|
143
195
|
self, request: StopRunRequest, context: grpc.ServicerContext
|
@@ -146,30 +198,42 @@ class ExecServicer(exec_pb2_grpc.ExecServicer):
|
|
146
198
|
log(INFO, "ExecServicer.StopRun")
|
147
199
|
state = self.linkstate_factory.state()
|
148
200
|
|
201
|
+
# Retrieve run ID and run
|
202
|
+
run_id = request.run_id
|
203
|
+
run = state.get_run(run_id)
|
204
|
+
|
149
205
|
# Exit if `run_id` not found
|
150
|
-
if not
|
151
|
-
context.abort(
|
152
|
-
|
206
|
+
if not run:
|
207
|
+
context.abort(grpc.StatusCode.NOT_FOUND, RUN_ID_NOT_FOUND_MESSAGE)
|
208
|
+
|
209
|
+
# If user auth is enabled, check if `flwr_aid` matches the run's `flwr_aid`
|
210
|
+
if self.auth_plugin:
|
211
|
+
flwr_aid = shared_account_info.get().flwr_aid
|
212
|
+
_check_flwr_aid_in_run(
|
213
|
+
flwr_aid=flwr_aid, run=cast(Run, run), context=context
|
153
214
|
)
|
154
215
|
|
155
|
-
run_status = state.get_run_status({
|
216
|
+
run_status = state.get_run_status({run_id})[run_id]
|
156
217
|
if run_status.status == Status.FINISHED:
|
157
218
|
context.abort(
|
158
219
|
grpc.StatusCode.FAILED_PRECONDITION,
|
159
|
-
f"Run ID {
|
220
|
+
f"Run ID {run_id} is already finished",
|
160
221
|
)
|
161
222
|
|
162
223
|
update_success = state.update_run_status(
|
163
|
-
run_id=
|
224
|
+
run_id=run_id,
|
164
225
|
new_status=RunStatus(Status.FINISHED, SubStatus.STOPPED, ""),
|
165
226
|
)
|
166
227
|
|
167
228
|
if update_success:
|
168
|
-
message_ids: set[str] = state.get_message_ids_from_run_id(
|
229
|
+
message_ids: set[str] = state.get_message_ids_from_run_id(run_id)
|
169
230
|
|
170
231
|
# Delete Messages and their replies for the `run_id`
|
171
232
|
state.delete_messages(message_ids)
|
172
233
|
|
234
|
+
# Delete objects of the run from the object store
|
235
|
+
self.objectstore_factory.store().delete_objects_in_run(run_id)
|
236
|
+
|
173
237
|
return StopRunResponse(success=update_success)
|
174
238
|
|
175
239
|
def GetLoginDetails(
|
@@ -224,10 +288,46 @@ class ExecServicer(exec_pb2_grpc.ExecServicer):
|
|
224
288
|
)
|
225
289
|
|
226
290
|
|
227
|
-
def _create_list_runs_response(
|
291
|
+
def _create_list_runs_response(
|
292
|
+
run_ids: set[int], state: LinkState, store: ObjectStore
|
293
|
+
) -> ListRunsResponse:
|
228
294
|
"""Create response for `flwr ls --runs` and `flwr ls --run-id <run_id>`."""
|
229
|
-
run_dict = {run_id:
|
295
|
+
run_dict = {run_id: run for run_id in run_ids if (run := state.get_run(run_id))}
|
296
|
+
|
297
|
+
# Delete objects of finished runs from the object store
|
298
|
+
for run_id, run in run_dict.items():
|
299
|
+
if run.status.status == Status.FINISHED:
|
300
|
+
store.delete_objects_in_run(run_id)
|
301
|
+
|
230
302
|
return ListRunsResponse(
|
231
|
-
run_dict={run_id: run_to_proto(run) for run_id, run in run_dict.items()
|
303
|
+
run_dict={run_id: run_to_proto(run) for run_id, run in run_dict.items()},
|
232
304
|
now=now().isoformat(),
|
233
305
|
)
|
306
|
+
|
307
|
+
|
308
|
+
def _check_flwr_aid_in_run(
|
309
|
+
flwr_aid: Optional[str], run: Run, context: grpc.ServicerContext
|
310
|
+
) -> None:
|
311
|
+
"""Guard clause to check if `flwr_aid` matches the run's `flwr_aid`."""
|
312
|
+
# `flwr_aid` must not be None. Abort if it is None.
|
313
|
+
if flwr_aid is None:
|
314
|
+
context.abort(
|
315
|
+
grpc.StatusCode.PERMISSION_DENIED,
|
316
|
+
"️⛔️ User authentication is enabled, but `flwr_aid` is None",
|
317
|
+
)
|
318
|
+
|
319
|
+
# `run.flwr_aid` must not be an empty string. Abort if it is empty.
|
320
|
+
run_flwr_aid = run.flwr_aid
|
321
|
+
if not run_flwr_aid:
|
322
|
+
context.abort(
|
323
|
+
grpc.StatusCode.PERMISSION_DENIED,
|
324
|
+
"⛔️ User authentication is enabled, but the run is not associated "
|
325
|
+
"with a `flwr_aid`.",
|
326
|
+
)
|
327
|
+
|
328
|
+
# Exit if `flwr_aid` does not match the run's `flwr_aid`
|
329
|
+
if run_flwr_aid != flwr_aid:
|
330
|
+
context.abort(
|
331
|
+
grpc.StatusCode.PERMISSION_DENIED,
|
332
|
+
"⛔️ Run ID does not belong to the user",
|
333
|
+
)
|
@@ -241,11 +241,20 @@ def start_client_internal(
|
|
241
241
|
outputs = clientappio_servicer.get_outputs()
|
242
242
|
reply_message, context = outputs.message, outputs.context
|
243
243
|
|
244
|
-
# Update
|
244
|
+
# Update context in the state
|
245
245
|
state.store_context(context)
|
246
246
|
|
247
247
|
# Send
|
248
248
|
send(reply_message)
|
249
|
+
|
250
|
+
# Delete messages from the state
|
251
|
+
state.delete_messages(
|
252
|
+
message_ids=[
|
253
|
+
message.metadata.message_id,
|
254
|
+
message.metadata.reply_to_message_id,
|
255
|
+
]
|
256
|
+
)
|
257
|
+
|
249
258
|
log(INFO, "Sent reply")
|
250
259
|
|
251
260
|
except RunNotRunningException:
|
{flwr_nightly-1.19.0.dev20250612.dist-info → flwr_nightly-1.19.0.dev20250616.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: flwr-nightly
|
3
|
-
Version: 1.19.0.
|
3
|
+
Version: 1.19.0.dev20250616
|
4
4
|
Summary: Flower: A Friendly Federated AI Framework
|
5
5
|
License: Apache-2.0
|
6
6
|
Keywords: Artificial Intelligence,Federated AI,Federated Analytics,Federated Evaluation,Federated Learning,Flower,Machine Learning
|
{flwr_nightly-1.19.0.dev20250612.dist-info → flwr_nightly-1.19.0.dev20250616.dist-info}/RECORD
RENAMED
@@ -15,7 +15,7 @@ flwr/cli/install.py,sha256=Jr883qR7qssVpUr3hEOEcLK-dfW67Rsve3lZchjA9RU,8180
|
|
15
15
|
flwr/cli/log.py,sha256=CKh5l2nnpognDy3lSubrlYX2-gNJ0qa-UFGVPRKS_YQ,6523
|
16
16
|
flwr/cli/login/__init__.py,sha256=B1SXKU3HCQhWfFDMJhlC7FOl8UsvH4mxysxeBnrfyUE,800
|
17
17
|
flwr/cli/login/login.py,sha256=pDkx5o4XBFBi7aGp_0Ys2FUMKmcWjzwWdoQRumXUsd8,4297
|
18
|
-
flwr/cli/ls.py,sha256=
|
18
|
+
flwr/cli/ls.py,sha256=zBBO3Yom3DIYwIJg09OSxy347wiYffOviqh-c2U4A04,10939
|
19
19
|
flwr/cli/new/__init__.py,sha256=QA1E2QtzPvFCjLTUHnFnJbufuFiGyT_0Y53Wpbvg1F0,790
|
20
20
|
flwr/cli/new/new.py,sha256=2e4ACJqeZ0W0_FyksQTi7PEzQpXT8KRpBPthFoac6zQ,9917
|
21
21
|
flwr/cli/new/templates/__init__.py,sha256=FpjWCfIySU2DB4kh0HOXLAjlZNNFDTVU4w3HoE2TzcI,725
|
@@ -73,7 +73,7 @@ flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl,sha256=7aOtbvAAnVXyTEYL
|
|
73
73
|
flwr/cli/run/__init__.py,sha256=RPyB7KbYTFl6YRiilCch6oezxrLQrl1kijV7BMGkLbA,790
|
74
74
|
flwr/cli/run/run.py,sha256=U_EHebzwiH8Lszm40oJM8a2I0vaiu1Iizci_GHSl25Y,8211
|
75
75
|
flwr/cli/stop.py,sha256=l8DcRkA---CESVJgc7iTHLWIBAPGxWIfoem8qSU3lZQ,4972
|
76
|
-
flwr/cli/utils.py,sha256=
|
76
|
+
flwr/cli/utils.py,sha256=Vyxpd8LOn2tg5wwId18CHDKq2xaoFDxHMnMW0IlG9Yw,12048
|
77
77
|
flwr/client/__init__.py,sha256=boIhKaK6I977zrILmoTutNx94x5jB0e6F1gnAjaRJnI,1250
|
78
78
|
flwr/client/client.py,sha256=3HAchxvknKG9jYbB7swNyDj-e5vUWDuMKoLvbT7jCVM,7895
|
79
79
|
flwr/client/client_app.py,sha256=zVhi-l3chAb06ozFsKwix3hU_RpOLjST13Ha50AVIPE,16918
|
@@ -108,7 +108,7 @@ flwr/common/args.py,sha256=-aX_jVnSaDrJR2KZ8Wq0Y3dQHII4R4MJtJOIXzVUA0c,5417
|
|
108
108
|
flwr/common/auth_plugin/__init__.py,sha256=3rzPkVLn9WyB5n7HLk1XGDw3SLCqRWAU1_CnglcWPfw,970
|
109
109
|
flwr/common/auth_plugin/auth_plugin.py,sha256=kXx5o39vJchaPv28sK9qO6H_UXSWym6zRBbCa7sUwtQ,4825
|
110
110
|
flwr/common/config.py,sha256=glcZDjco-amw1YfQcYTFJ4S1pt9APoexT-mf1QscuHs,13960
|
111
|
-
flwr/common/constant.py,sha256=
|
111
|
+
flwr/common/constant.py,sha256=yCJk_m7sJiMz8fTmOMWLBzltGRVUjFmt9NoYJt3Fz-w,8150
|
112
112
|
flwr/common/context.py,sha256=Be8obQR_OvEDy1OmshuUKxGRQ7Qx89mf5F4xlhkR10s,2407
|
113
113
|
flwr/common/date.py,sha256=1ZT2cRSpC2DJqprOVTLXYCR_O2_OZR0zXO_brJ3LqWc,1554
|
114
114
|
flwr/common/differential_privacy.py,sha256=FdlpdpPl_H_2HJa8CQM1iCUGBBQ5Dc8CzxmHERM-EoE,6148
|
@@ -226,7 +226,7 @@ flwr/proto/transport_pb2_grpc.py,sha256=vLN3EHtx2aEEMCO4f1Upu-l27BPzd3-5pV-u8wPc
|
|
226
226
|
flwr/proto/transport_pb2_grpc.pyi,sha256=AGXf8RiIiW2J5IKMlm_3qT3AzcDa4F3P5IqUjve_esA,766
|
227
227
|
flwr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
228
228
|
flwr/server/__init__.py,sha256=LQQHiuL2jy7TpNaKastRdGsexlxSt5ZWAQNVqitDnrY,1598
|
229
|
-
flwr/server/app.py,sha256=
|
229
|
+
flwr/server/app.py,sha256=reOCH1YmngNxWXBvxp8UfQNTfLP95NMh8j8jU0dW1IU,29492
|
230
230
|
flwr/server/client_manager.py,sha256=5jCGavVli7XdupvWWo7ru3PdFTlRU8IGvHFSSoUVLRs,6227
|
231
231
|
flwr/server/client_proxy.py,sha256=sv0E9AldBYOvc3pusqFh-GnyreeMfsXQ1cuTtxTq_wY,2399
|
232
232
|
flwr/server/compat/__init__.py,sha256=0IsttWvY15qO98_1GyzVC-vR1e_ZPXOdu2qUlOkYMPE,886
|
@@ -298,14 +298,14 @@ flwr/server/superlink/fleet/vce/backend/backend.py,sha256=-wDHjgAy5mrfEgXj0GxkJI
|
|
298
298
|
flwr/server/superlink/fleet/vce/backend/raybackend.py,sha256=Hx9hxL7lju1_VJoAwkhBOGerZ3628u0P1zgkPhGWRPY,7154
|
299
299
|
flwr/server/superlink/fleet/vce/vce_api.py,sha256=xSjQbBYHmUTinw7Q_-UxqR7qt07kqj9FCSpPYRsUKf8,12909
|
300
300
|
flwr/server/superlink/linkstate/__init__.py,sha256=OtsgvDTnZLU3k0sUbkHbqoVwW6ql2FDmb6uT6DbNkZo,1064
|
301
|
-
flwr/server/superlink/linkstate/in_memory_linkstate.py,sha256=
|
301
|
+
flwr/server/superlink/linkstate/in_memory_linkstate.py,sha256=OuyuW_btDL-Nuon2aAcyXDapzwzZX4PrRWTjb0epoaI,26478
|
302
302
|
flwr/server/superlink/linkstate/linkstate.py,sha256=JZHshn7NnUHr_hvHqO593YWwuCQVJm7InXor60DRcX0,13238
|
303
303
|
flwr/server/superlink/linkstate/linkstate_factory.py,sha256=8RlosqSpKOoD_vhUUQPY0jtE3A84GeF96Z7sWNkRRcA,2069
|
304
304
|
flwr/server/superlink/linkstate/sqlite_linkstate.py,sha256=E699Ak0jMF3N7i1SIeFRutjorg51Fd7qBqaPX_gkWU0,43687
|
305
305
|
flwr/server/superlink/linkstate/utils.py,sha256=IeLh7iGRCHU5MEWOl7iriaSE4L__8GWOa2OleXadK5M,15444
|
306
306
|
flwr/server/superlink/serverappio/__init__.py,sha256=Fy4zJuoccZe5mZSEIpOmQvU6YeXFBa1M4eZuXXmJcn8,717
|
307
307
|
flwr/server/superlink/serverappio/serverappio_grpc.py,sha256=6-FUUt0GiLcBPljj8bBrUNeAITUoDQOLzaMihKo52hg,2326
|
308
|
-
flwr/server/superlink/serverappio/serverappio_servicer.py,sha256=
|
308
|
+
flwr/server/superlink/serverappio/serverappio_servicer.py,sha256=pjBjZ6NQdOKO3AYZ8tGD-wzGHh4LaVS3lpPZb6XpKU8,18823
|
309
309
|
flwr/server/superlink/simulation/__init__.py,sha256=Ry8DrNaZCMcQXvUc4FoCN2m3dvUQgWjasfp015o3Ec4,718
|
310
310
|
flwr/server/superlink/simulation/simulationio_grpc.py,sha256=0l0F-UjYEk6W7HZmI28PbJQLFxSi_vBHRkdchgdaSMQ,2224
|
311
311
|
flwr/server/superlink/simulation/simulationio_servicer.py,sha256=8jACbVPe7YPLaR0hbpEofBBwWjc-uQPhf2FU1eNOtsw,7743
|
@@ -339,8 +339,8 @@ flwr/superexec/__init__.py,sha256=YFqER0IJc1XEWfsX6AxZ9LSRq0sawPYrNYki-brvTIc,71
|
|
339
339
|
flwr/superexec/app.py,sha256=U2jjOHb2LGWoU7vrl9_czTzre9O2mPxu3CPGUZ86sK4,1465
|
340
340
|
flwr/superexec/deployment.py,sha256=aDTRCM2V9iVS_gvwTSGaRIPGkm2Ro-CxFYin-Mba0P8,6853
|
341
341
|
flwr/superexec/exec_event_log_interceptor.py,sha256=7aBjZ4lkpOIyWut0s394OpMePr16g_Te594s-9aDM9Q,5774
|
342
|
-
flwr/superexec/exec_grpc.py,sha256=
|
343
|
-
flwr/superexec/exec_servicer.py,sha256=
|
342
|
+
flwr/superexec/exec_grpc.py,sha256=oM9-cwTpZGRI3ma4wc9Xt5RwtM1GzrmCRmiOgr3jT34,3517
|
343
|
+
flwr/superexec/exec_servicer.py,sha256=E70y-r17GOsCEwQvVmcDyJYp_H7fse4cxha6Gbgnaqo,12254
|
344
344
|
flwr/superexec/exec_user_auth_interceptor.py,sha256=HpGHTcDKzB7XUiQHXgntNVFYL-VfP9Wj5tEVc04VOOw,5820
|
345
345
|
flwr/superexec/executor.py,sha256=6kazVVypj-jtfeaHsqGElOv7FCrfqeQotYxVx1m4y84,3284
|
346
346
|
flwr/superexec/simulation.py,sha256=2iRVsC2dGLuqIAvwsznt_qE3u9A-yuPsj9q94Oit_As,4212
|
@@ -358,8 +358,8 @@ flwr/supernode/runtime/run_clientapp.py,sha256=cvWSby7u31u97QapWHxJM-Wer6F1k6mbb
|
|
358
358
|
flwr/supernode/servicer/__init__.py,sha256=lucTzre5WPK7G1YLCfaqg3rbFWdNSb7ZTt-ca8gxdEo,717
|
359
359
|
flwr/supernode/servicer/clientappio/__init__.py,sha256=vJyOjO2FXZ2URbnthmdsgs6948wbYfdq1L1V8Um-Lr8,895
|
360
360
|
flwr/supernode/servicer/clientappio/clientappio_servicer.py,sha256=LmzkxtNQBn5vVrHc0Bhq2WqaK6-LM2v4kfLBN0PiNNM,8522
|
361
|
-
flwr/supernode/start_client_internal.py,sha256=
|
362
|
-
flwr_nightly-1.19.0.
|
363
|
-
flwr_nightly-1.19.0.
|
364
|
-
flwr_nightly-1.19.0.
|
365
|
-
flwr_nightly-1.19.0.
|
361
|
+
flwr/supernode/start_client_internal.py,sha256=M1_sYScUzhXAg1obVf64E6UxYP0m9g6OmA2vZiB75As,18453
|
362
|
+
flwr_nightly-1.19.0.dev20250616.dist-info/METADATA,sha256=dzYxYe5YgeuTZHTSGgctOaiLXYFPBczS5POBfjrRTv4,15910
|
363
|
+
flwr_nightly-1.19.0.dev20250616.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
364
|
+
flwr_nightly-1.19.0.dev20250616.dist-info/entry_points.txt,sha256=jNpDXGBGgs21RqUxelF_jwGaxtqFwm-MQyfz-ZqSjrA,367
|
365
|
+
flwr_nightly-1.19.0.dev20250616.dist-info/RECORD,,
|
{flwr_nightly-1.19.0.dev20250612.dist-info → flwr_nightly-1.19.0.dev20250616.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|