flwr-nightly 1.19.0.dev20250521__py3-none-any.whl → 1.19.0.dev20250522__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/client/grpc_adapter_client/connection.py +4 -4
- flwr/client/grpc_rere_client/connection.py +4 -4
- flwr/client/rest_client/connection.py +4 -4
- flwr/client/start_client_internal.py +85 -198
- flwr/client/supernode/app.py +0 -8
- flwr/common/inflatable.py +23 -0
- flwr/common/inflatable_grpc_utils.py +2 -0
- flwr/compat/client/app.py +2 -2
- flwr/server/app.py +12 -1
- flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +6 -1
- flwr/server/superlink/serverappio/serverappio_grpc.py +3 -0
- flwr/server/superlink/serverappio/serverappio_servicer.py +6 -1
- flwr/supercore/object_store/__init__.py +23 -0
- flwr/supercore/object_store/in_memory_object_store.py +65 -0
- flwr/supercore/object_store/object_store.py +86 -0
- flwr/supercore/object_store/object_store_factory.py +44 -0
- {flwr_nightly-1.19.0.dev20250521.dist-info → flwr_nightly-1.19.0.dev20250522.dist-info}/METADATA +1 -1
- {flwr_nightly-1.19.0.dev20250521.dist-info → flwr_nightly-1.19.0.dev20250522.dist-info}/RECORD +22 -18
- /flwr/{client → compat/client}/grpc_client/__init__.py +0 -0
- /flwr/{client → compat/client}/grpc_client/connection.py +0 -0
- {flwr_nightly-1.19.0.dev20250521.dist-info → flwr_nightly-1.19.0.dev20250522.dist-info}/WHEEL +0 -0
- {flwr_nightly-1.19.0.dev20250521.dist-info → flwr_nightly-1.19.0.dev20250522.dist-info}/entry_points.txt +0 -0
@@ -45,10 +45,10 @@ def grpc_adapter( # pylint: disable=R0913,too-many-positional-arguments
|
|
45
45
|
tuple[
|
46
46
|
Callable[[], Optional[Message]],
|
47
47
|
Callable[[Message], None],
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
48
|
+
Callable[[], Optional[int]],
|
49
|
+
Callable[[], None],
|
50
|
+
Callable[[int], Run],
|
51
|
+
Callable[[str, int], Fab],
|
52
52
|
]
|
53
53
|
]:
|
54
54
|
"""Primitives for request/response-based interaction with a server via GrpcAdapter.
|
@@ -74,10 +74,10 @@ def grpc_request_response( # pylint: disable=R0913,R0914,R0915,R0917
|
|
74
74
|
tuple[
|
75
75
|
Callable[[], Optional[Message]],
|
76
76
|
Callable[[Message], None],
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
77
|
+
Callable[[], Optional[int]],
|
78
|
+
Callable[[], None],
|
79
|
+
Callable[[int], Run],
|
80
|
+
Callable[[str, int], Fab],
|
81
81
|
]
|
82
82
|
]:
|
83
83
|
"""Primitives for request/response-based interaction with a server.
|
@@ -87,10 +87,10 @@ def http_request_response( # pylint: disable=R0913,R0914,R0915,R0917
|
|
87
87
|
tuple[
|
88
88
|
Callable[[], Optional[Message]],
|
89
89
|
Callable[[Message], None],
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
90
|
+
Callable[[], Optional[int]],
|
91
|
+
Callable[[], None],
|
92
|
+
Callable[[int], Run],
|
93
|
+
Callable[[str, int], Fab],
|
94
94
|
]
|
95
95
|
]:
|
96
96
|
"""Primitives for request/response-based interaction with a server.
|
@@ -24,7 +24,7 @@ from contextlib import AbstractContextManager
|
|
24
24
|
from logging import ERROR, INFO, WARN
|
25
25
|
from os import urandom
|
26
26
|
from pathlib import Path
|
27
|
-
from typing import Callable, Optional, Union
|
27
|
+
from typing import Callable, Optional, Union
|
28
28
|
|
29
29
|
import grpc
|
30
30
|
from cryptography.hazmat.primitives.asymmetric import ec
|
@@ -32,32 +32,25 @@ from grpc import RpcError
|
|
32
32
|
|
33
33
|
from flwr.app.error import Error
|
34
34
|
from flwr.cli.config_utils import get_fab_metadata
|
35
|
-
from flwr.cli.install import install_from_fab
|
36
|
-
from flwr.client.client import Client
|
37
|
-
from flwr.client.client_app import ClientApp, LoadClientAppError
|
38
35
|
from flwr.client.clientapp.app import flwr_clientapp
|
39
36
|
from flwr.client.clientapp.clientappio_servicer import (
|
40
37
|
ClientAppInputs,
|
41
38
|
ClientAppIoServicer,
|
42
39
|
)
|
43
40
|
from flwr.client.grpc_adapter_client.connection import grpc_adapter
|
44
|
-
from flwr.client.grpc_client.connection import grpc_connection
|
45
41
|
from flwr.client.grpc_rere_client.connection import grpc_request_response
|
46
42
|
from flwr.client.message_handler.message_handler import handle_control_message
|
47
43
|
from flwr.client.run_info_store import DeprecatedRunInfoStore
|
48
|
-
from flwr.
|
49
|
-
from flwr.common import GRPC_MAX_MESSAGE_LENGTH, Context, Message
|
44
|
+
from flwr.common import GRPC_MAX_MESSAGE_LENGTH, Message
|
50
45
|
from flwr.common.address import parse_address
|
51
46
|
from flwr.common.constant import (
|
52
47
|
CLIENT_OCTET,
|
53
48
|
CLIENTAPPIO_API_DEFAULT_SERVER_ADDRESS,
|
54
|
-
ISOLATION_MODE_PROCESS,
|
55
49
|
ISOLATION_MODE_SUBPROCESS,
|
56
50
|
MAX_RETRY_DELAY,
|
57
51
|
RUN_ID_NUM_BYTES,
|
58
52
|
SERVER_OCTET,
|
59
53
|
TRANSPORT_TYPE_GRPC_ADAPTER,
|
60
|
-
TRANSPORT_TYPE_GRPC_BIDI,
|
61
54
|
TRANSPORT_TYPE_GRPC_RERE,
|
62
55
|
TRANSPORT_TYPE_REST,
|
63
56
|
TRANSPORT_TYPES,
|
@@ -72,20 +65,6 @@ from flwr.proto.clientappio_pb2_grpc import add_ClientAppIoServicer_to_server
|
|
72
65
|
from flwr.supernode.nodestate import NodeStateFactory
|
73
66
|
|
74
67
|
|
75
|
-
def _check_actionable_client(
|
76
|
-
client: Optional[Client], client_fn: Optional[ClientFnExt]
|
77
|
-
) -> None:
|
78
|
-
if client_fn is None and client is None:
|
79
|
-
raise ValueError(
|
80
|
-
"Both `client_fn` and `client` are `None`, but one is required"
|
81
|
-
)
|
82
|
-
|
83
|
-
if client_fn is not None and client is not None:
|
84
|
-
raise ValueError(
|
85
|
-
"Both `client_fn` and `client` are provided, but only one is allowed"
|
86
|
-
)
|
87
|
-
|
88
|
-
|
89
68
|
# pylint: disable=import-outside-toplevel
|
90
69
|
# pylint: disable=too-many-branches
|
91
70
|
# pylint: disable=too-many-locals
|
@@ -95,21 +74,18 @@ def start_client_internal(
|
|
95
74
|
*,
|
96
75
|
server_address: str,
|
97
76
|
node_config: UserConfig,
|
98
|
-
load_client_app_fn: Optional[Callable[[str, str, str], ClientApp]] = None,
|
99
|
-
client_fn: Optional[ClientFnExt] = None,
|
100
|
-
client: Optional[Client] = None,
|
101
77
|
grpc_max_message_length: int = GRPC_MAX_MESSAGE_LENGTH,
|
102
78
|
root_certificates: Optional[Union[bytes, str]] = None,
|
103
79
|
insecure: Optional[bool] = None,
|
104
|
-
transport:
|
80
|
+
transport: str,
|
105
81
|
authentication_keys: Optional[
|
106
82
|
tuple[ec.EllipticCurvePrivateKey, ec.EllipticCurvePublicKey]
|
107
83
|
] = None,
|
108
84
|
max_retries: Optional[int] = None,
|
109
85
|
max_wait_time: Optional[float] = None,
|
110
86
|
flwr_path: Optional[Path] = None,
|
111
|
-
isolation:
|
112
|
-
clientappio_api_address:
|
87
|
+
isolation: str = ISOLATION_MODE_SUBPROCESS,
|
88
|
+
clientappio_api_address: str = CLIENTAPPIO_API_DEFAULT_SERVER_ADDRESS,
|
113
89
|
) -> None:
|
114
90
|
"""Start a Flower client node which connects to a Flower server.
|
115
91
|
|
@@ -121,13 +97,6 @@ def start_client_internal(
|
|
121
97
|
would be `"[::]:8080"`.
|
122
98
|
node_config: UserConfig
|
123
99
|
The configuration of the node.
|
124
|
-
load_client_app_fn : Optional[Callable[[], ClientApp]] (default: None)
|
125
|
-
A function that can be used to load a `ClientApp` instance.
|
126
|
-
client_fn : Optional[ClientFnExt]
|
127
|
-
A callable that instantiates a Client. (default: None)
|
128
|
-
client : Optional[flwr.client.Client]
|
129
|
-
An implementation of the abstract base
|
130
|
-
class `flwr.client.Client` (default: None)
|
131
100
|
grpc_max_message_length : int (default: 536_870_912, this equals 512MB)
|
132
101
|
The maximum length of gRPC messages that can be exchanged with the
|
133
102
|
Flower server. The default should be sufficient for most models.
|
@@ -142,10 +111,10 @@ def start_client_internal(
|
|
142
111
|
insecure : Optional[bool] (default: None)
|
143
112
|
Starts an insecure gRPC connection when True. Enables HTTPS connection
|
144
113
|
when False, using system certificates if `root_certificates` is None.
|
145
|
-
transport :
|
114
|
+
transport : str
|
146
115
|
Configure the transport layer. Allowed values:
|
147
|
-
- 'grpc-
|
148
|
-
- 'grpc-
|
116
|
+
- 'grpc-rere': gRPC, request-response
|
117
|
+
- 'grpc-adapter': gRPC via 3rd party adapter (experimental)
|
149
118
|
- 'rest': HTTP (experimental)
|
150
119
|
authentication_keys : Optional[Tuple[PrivateKey, PublicKey]] (default: None)
|
151
120
|
Tuple containing the elliptic curve private key and public key for
|
@@ -162,56 +131,24 @@ def start_client_internal(
|
|
162
131
|
If set to None, there is no limit to the total time.
|
163
132
|
flwr_path: Optional[Path] (default: None)
|
164
133
|
The fully resolved path containing installed Flower Apps.
|
165
|
-
isolation :
|
134
|
+
isolation : str (default: ISOLATION_MODE_SUBPROCESS)
|
166
135
|
Isolation mode for `ClientApp`. Possible values are `subprocess` and
|
167
|
-
`process`.
|
168
|
-
as the SuperNode. If `subprocess`, the `ClientApp` runs in a subprocess started
|
136
|
+
`process`. If `subprocess`, the `ClientApp` runs in a subprocess started
|
169
137
|
by the SueprNode and communicates using gRPC at the address
|
170
138
|
`clientappio_api_address`. If `process`, the `ClientApp` runs in a separate
|
171
139
|
isolated process and communicates using gRPC at the address
|
172
140
|
`clientappio_api_address`.
|
173
|
-
clientappio_api_address :
|
141
|
+
clientappio_api_address : str
|
174
142
|
(default: `CLIENTAPPIO_API_DEFAULT_SERVER_ADDRESS`)
|
175
143
|
The SuperNode gRPC server address.
|
176
144
|
"""
|
177
145
|
if insecure is None:
|
178
146
|
insecure = root_certificates is None
|
179
147
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
# Wrap `Client` instance in `client_fn`
|
185
|
-
def single_client_factory(
|
186
|
-
context: Context, # pylint: disable=unused-argument
|
187
|
-
) -> Client:
|
188
|
-
if client is None: # Added this to keep mypy happy
|
189
|
-
raise ValueError(
|
190
|
-
"Both `client_fn` and `client` are `None`, but one is required"
|
191
|
-
)
|
192
|
-
return client # Always return the same instance
|
193
|
-
|
194
|
-
client_fn = single_client_factory
|
195
|
-
|
196
|
-
def _load_client_app(_1: str, _2: str, _3: str) -> ClientApp:
|
197
|
-
return ClientApp(client_fn=client_fn)
|
198
|
-
|
199
|
-
load_client_app_fn = _load_client_app
|
200
|
-
|
201
|
-
if isolation:
|
202
|
-
if clientappio_api_address is None:
|
203
|
-
raise ValueError(
|
204
|
-
f"`clientappio_api_address` required when `isolation` is "
|
205
|
-
f"{ISOLATION_MODE_SUBPROCESS} or {ISOLATION_MODE_PROCESS}",
|
206
|
-
)
|
207
|
-
_clientappio_grpc_server, clientappio_servicer = run_clientappio_api_grpc(
|
208
|
-
address=clientappio_api_address,
|
209
|
-
certificates=None,
|
210
|
-
)
|
211
|
-
clientappio_api_address = cast(str, clientappio_api_address)
|
212
|
-
|
213
|
-
# At this point, only `load_client_app_fn` should be used
|
214
|
-
# Both `client` and `client_fn` must not be used directly
|
148
|
+
_clientappio_grpc_server, clientappio_servicer = run_clientappio_api_grpc(
|
149
|
+
address=clientappio_api_address,
|
150
|
+
certificates=None,
|
151
|
+
)
|
215
152
|
|
216
153
|
# Initialize connection context manager
|
217
154
|
connection, address, connection_error_type = _init_connection(
|
@@ -278,30 +215,15 @@ def start_client_internal(
|
|
278
215
|
|
279
216
|
# Register node when connecting the first time
|
280
217
|
if run_info_store is None:
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
node_id=-1,
|
291
|
-
node_config={},
|
292
|
-
)
|
293
|
-
else:
|
294
|
-
# Call create_node fn to register node
|
295
|
-
# and store node_id in state
|
296
|
-
if (node_id := create_node()) is None:
|
297
|
-
raise ValueError(
|
298
|
-
"Failed to register SuperNode with the SuperLink"
|
299
|
-
)
|
300
|
-
state.set_node_id(node_id)
|
301
|
-
run_info_store = DeprecatedRunInfoStore(
|
302
|
-
node_id=state.get_node_id(),
|
303
|
-
node_config=node_config,
|
304
|
-
)
|
218
|
+
# Call create_node fn to register node
|
219
|
+
# and store node_id in state
|
220
|
+
if (node_id := create_node()) is None:
|
221
|
+
raise ValueError("Failed to register SuperNode with the SuperLink")
|
222
|
+
state.set_node_id(node_id)
|
223
|
+
run_info_store = DeprecatedRunInfoStore(
|
224
|
+
node_id=state.get_node_id(),
|
225
|
+
node_config=node_config,
|
226
|
+
)
|
305
227
|
|
306
228
|
# pylint: disable=too-many-nested-blocks
|
307
229
|
while True:
|
@@ -336,18 +258,11 @@ def start_client_internal(
|
|
336
258
|
# Get run info
|
337
259
|
run_id = message.metadata.run_id
|
338
260
|
if run_id not in runs:
|
339
|
-
|
340
|
-
runs[run_id] = get_run(run_id)
|
341
|
-
# If get_run is None, i.e., in grpc-bidi mode
|
342
|
-
else:
|
343
|
-
runs[run_id] = Run.create_empty(run_id=run_id)
|
261
|
+
runs[run_id] = get_run(run_id)
|
344
262
|
|
345
263
|
run: Run = runs[run_id]
|
346
264
|
if get_fab is not None and run.fab_hash:
|
347
265
|
fab = get_fab(run.fab_hash, run_id)
|
348
|
-
if not isolation:
|
349
|
-
# If `ClientApp` runs in the same process, install the FAB
|
350
|
-
install_from_fab(fab.content, flwr_path, True)
|
351
266
|
fab_id, fab_version = get_fab_metadata(fab.content)
|
352
267
|
else:
|
353
268
|
fab = None
|
@@ -374,94 +289,72 @@ def start_client_internal(
|
|
374
289
|
|
375
290
|
# Handle app loading and task message
|
376
291
|
try:
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
)
|
292
|
+
# Two isolation modes:
|
293
|
+
# 1. `subprocess`: SuperNode is starting the ClientApp
|
294
|
+
# process as a subprocess.
|
295
|
+
# 2. `process`: ClientApp process gets started separately
|
296
|
+
# (via `flwr-clientapp`), for example, in a separate
|
297
|
+
# Docker container.
|
298
|
+
|
299
|
+
# Generate SuperNode token
|
300
|
+
token = int.from_bytes(urandom(RUN_ID_NUM_BYTES), "little")
|
301
|
+
|
302
|
+
# Mode 1: SuperNode starts ClientApp as subprocess
|
303
|
+
start_subprocess = isolation == ISOLATION_MODE_SUBPROCESS
|
304
|
+
|
305
|
+
# Share Message and Context with servicer
|
306
|
+
clientappio_servicer.set_inputs(
|
307
|
+
clientapp_input=ClientAppInputs(
|
308
|
+
message=message,
|
309
|
+
context=context,
|
310
|
+
run=run,
|
311
|
+
fab=fab,
|
312
|
+
token=token,
|
313
|
+
),
|
314
|
+
token_returned=start_subprocess,
|
315
|
+
)
|
402
316
|
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
)
|
412
|
-
# Start ClientApp subprocess
|
413
|
-
command = [
|
414
|
-
"flwr-clientapp",
|
415
|
-
"--clientappio-api-address",
|
416
|
-
io_address,
|
417
|
-
"--token",
|
418
|
-
str(token),
|
419
|
-
]
|
420
|
-
command.append("--insecure")
|
421
|
-
|
422
|
-
proc = mp_spawn_context.Process(
|
423
|
-
target=_run_flwr_clientapp,
|
424
|
-
args=(command, os.getpid()),
|
425
|
-
daemon=True,
|
426
|
-
)
|
427
|
-
proc.start()
|
428
|
-
proc.join()
|
429
|
-
else:
|
430
|
-
# Wait for output to become available
|
431
|
-
while not clientappio_servicer.has_outputs():
|
432
|
-
time.sleep(0.1)
|
433
|
-
|
434
|
-
outputs = clientappio_servicer.get_outputs()
|
435
|
-
reply_message, context = outputs.message, outputs.context
|
436
|
-
else:
|
437
|
-
# Load ClientApp instance
|
438
|
-
client_app: ClientApp = load_client_app_fn(
|
439
|
-
fab_id, fab_version, run.fab_hash
|
317
|
+
if start_subprocess:
|
318
|
+
_octet, _colon, _port = clientappio_api_address.rpartition(
|
319
|
+
":"
|
320
|
+
)
|
321
|
+
io_address = (
|
322
|
+
f"{CLIENT_OCTET}:{_port}"
|
323
|
+
if _octet == SERVER_OCTET
|
324
|
+
else clientappio_api_address
|
440
325
|
)
|
326
|
+
# Start ClientApp subprocess
|
327
|
+
command = [
|
328
|
+
"flwr-clientapp",
|
329
|
+
"--clientappio-api-address",
|
330
|
+
io_address,
|
331
|
+
"--token",
|
332
|
+
str(token),
|
333
|
+
]
|
334
|
+
command.append("--insecure")
|
335
|
+
|
336
|
+
proc = mp_spawn_context.Process(
|
337
|
+
target=_run_flwr_clientapp,
|
338
|
+
args=(command, os.getpid()),
|
339
|
+
daemon=True,
|
340
|
+
)
|
341
|
+
proc.start()
|
342
|
+
proc.join()
|
343
|
+
else:
|
344
|
+
# Wait for output to become available
|
345
|
+
while not clientappio_servicer.has_outputs():
|
346
|
+
time.sleep(0.1)
|
441
347
|
|
442
|
-
|
443
|
-
|
348
|
+
outputs = clientappio_servicer.get_outputs()
|
349
|
+
reply_message, context = outputs.message, outputs.context
|
444
350
|
except Exception as ex: # pylint: disable=broad-exception-caught
|
445
351
|
|
446
|
-
# Legacy grpc-bidi
|
447
|
-
if transport in ["grpc-bidi", None]:
|
448
|
-
log(ERROR, "Client raised an exception.", exc_info=ex)
|
449
|
-
# Raise exception, crash process
|
450
|
-
raise ex
|
451
|
-
|
452
352
|
# Don't update/change DeprecatedRunInfoStore
|
453
353
|
|
454
354
|
e_code = ErrorCode.CLIENT_APP_RAISED_EXCEPTION
|
455
355
|
# Ex fmt: "<class 'ZeroDivisionError'>:<'division by zero'>"
|
456
356
|
reason = str(type(ex)) + ":<'" + str(ex) + "'>"
|
457
357
|
exc_entity = "ClientApp"
|
458
|
-
if isinstance(ex, LoadClientAppError):
|
459
|
-
reason = (
|
460
|
-
"An exception was raised when attempting to load "
|
461
|
-
"`ClientApp`"
|
462
|
-
)
|
463
|
-
e_code = ErrorCode.LOAD_CLIENT_APP_EXCEPTION
|
464
|
-
exc_entity = "SuperNode"
|
465
358
|
|
466
359
|
log(ERROR, "%s raised an exception", exc_entity, exc_info=ex)
|
467
360
|
|
@@ -509,7 +402,7 @@ def start_client_internal(
|
|
509
402
|
time.sleep(sleep_duration)
|
510
403
|
|
511
404
|
|
512
|
-
def _init_connection(transport:
|
405
|
+
def _init_connection(transport: str, server_address: str) -> tuple[
|
513
406
|
Callable[
|
514
407
|
[
|
515
408
|
str,
|
@@ -523,10 +416,10 @@ def _init_connection(transport: Optional[str], server_address: str) -> tuple[
|
|
523
416
|
tuple[
|
524
417
|
Callable[[], Optional[Message]],
|
525
418
|
Callable[[Message], None],
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
419
|
+
Callable[[], Optional[int]],
|
420
|
+
Callable[[], None],
|
421
|
+
Callable[[int], Run],
|
422
|
+
Callable[[str, int], Fab],
|
530
423
|
]
|
531
424
|
],
|
532
425
|
],
|
@@ -543,10 +436,6 @@ def _init_connection(transport: Optional[str], server_address: str) -> tuple[
|
|
543
436
|
host, port, is_v6 = parsed_address
|
544
437
|
address = f"[{host}]:{port}" if is_v6 else f"{host}:{port}"
|
545
438
|
|
546
|
-
# Set the default transport layer
|
547
|
-
if transport is None:
|
548
|
-
transport = TRANSPORT_TYPE_GRPC_BIDI
|
549
|
-
|
550
439
|
# Use either gRPC bidirectional streaming or REST request/response
|
551
440
|
if transport == TRANSPORT_TYPE_REST:
|
552
441
|
try:
|
@@ -562,8 +451,6 @@ def _init_connection(transport: Optional[str], server_address: str) -> tuple[
|
|
562
451
|
connection, error_type = grpc_request_response, RpcError
|
563
452
|
elif transport == TRANSPORT_TYPE_GRPC_ADAPTER:
|
564
453
|
connection, error_type = grpc_adapter, RpcError
|
565
|
-
elif transport == TRANSPORT_TYPE_GRPC_BIDI:
|
566
|
-
connection, error_type = grpc_connection, RpcError
|
567
454
|
else:
|
568
455
|
raise ValueError(
|
569
456
|
f"Unknown transport type: {transport} (possible: {TRANSPORT_TYPES})"
|
flwr/client/supernode/app.py
CHANGED
@@ -43,7 +43,6 @@ from flwr.common.exit import ExitCode, flwr_exit
|
|
43
43
|
from flwr.common.exit_handlers import register_exit_handlers
|
44
44
|
from flwr.common.logger import log
|
45
45
|
|
46
|
-
from ..clientapp.utils import get_load_client_app_fn
|
47
46
|
from ..start_client_internal import start_client_internal
|
48
47
|
|
49
48
|
|
@@ -64,12 +63,6 @@ def run_supernode() -> None:
|
|
64
63
|
)
|
65
64
|
|
66
65
|
root_certificates = try_obtain_root_certificates(args, args.superlink)
|
67
|
-
load_fn = get_load_client_app_fn(
|
68
|
-
default_app_ref="",
|
69
|
-
app_path=None,
|
70
|
-
flwr_dir=args.flwr_dir,
|
71
|
-
multi_app=True,
|
72
|
-
)
|
73
66
|
authentication_keys = _try_setup_client_authentication(args)
|
74
67
|
|
75
68
|
log(DEBUG, "Isolation mode: %s", args.isolation)
|
@@ -82,7 +75,6 @@ def run_supernode() -> None:
|
|
82
75
|
|
83
76
|
start_client_internal(
|
84
77
|
server_address=args.superlink,
|
85
|
-
load_client_app_fn=load_fn,
|
86
78
|
transport=args.transport,
|
87
79
|
root_certificates=root_certificates,
|
88
80
|
insecure=args.insecure,
|
flwr/common/inflatable.py
CHANGED
@@ -112,6 +112,29 @@ def _get_object_body(object_content: bytes) -> bytes:
|
|
112
112
|
return object_content.split(HEAD_BODY_DIVIDER, 1)[1]
|
113
113
|
|
114
114
|
|
115
|
+
def is_valid_sha256_hash(object_id: str) -> bool:
|
116
|
+
"""Check if the given string is a valid SHA-256 hash.
|
117
|
+
|
118
|
+
Parameters
|
119
|
+
----------
|
120
|
+
object_id : str
|
121
|
+
The string to check.
|
122
|
+
|
123
|
+
Returns
|
124
|
+
-------
|
125
|
+
bool
|
126
|
+
``True`` if the string is a valid SHA-256 hash, ``False`` otherwise.
|
127
|
+
"""
|
128
|
+
if len(object_id) != 64:
|
129
|
+
return False
|
130
|
+
try:
|
131
|
+
# If base 16 int conversion succeeds, it's a valid hexadecimal str
|
132
|
+
int(object_id, 16)
|
133
|
+
return True
|
134
|
+
except ValueError:
|
135
|
+
return False
|
136
|
+
|
137
|
+
|
115
138
|
def get_object_type_from_object_content(object_content: bytes) -> str:
|
116
139
|
"""Return object type from bytes."""
|
117
140
|
return get_object_head_values_from_object_content(object_content)[0]
|
@@ -31,6 +31,7 @@ from .inflatable import (
|
|
31
31
|
get_object_head_values_from_object_content,
|
32
32
|
get_object_id,
|
33
33
|
)
|
34
|
+
from .message import Message
|
34
35
|
from .record import Array, ArrayRecord, ConfigRecord, MetricRecord, RecordDict
|
35
36
|
|
36
37
|
# Helper registry that maps names of classes to their type
|
@@ -38,6 +39,7 @@ inflatable_class_registry: dict[str, type[InflatableObject]] = {
|
|
38
39
|
Array.__qualname__: Array,
|
39
40
|
ArrayRecord.__qualname__: ArrayRecord,
|
40
41
|
ConfigRecord.__qualname__: ConfigRecord,
|
42
|
+
Message.__qualname__: Message,
|
41
43
|
MetricRecord.__qualname__: MetricRecord,
|
42
44
|
RecordDict.__qualname__: RecordDict,
|
43
45
|
}
|
flwr/compat/client/app.py
CHANGED
@@ -41,7 +41,6 @@ from flwr.client.clientapp.clientappio_servicer import (
|
|
41
41
|
ClientAppIoServicer,
|
42
42
|
)
|
43
43
|
from flwr.client.grpc_adapter_client.connection import grpc_adapter
|
44
|
-
from flwr.client.grpc_client.connection import grpc_connection
|
45
44
|
from flwr.client.grpc_rere_client.connection import grpc_request_response
|
46
45
|
from flwr.client.message_handler.message_handler import handle_control_message
|
47
46
|
from flwr.client.numpy_client import NumPyClient
|
@@ -69,6 +68,7 @@ from flwr.common.grpc import generic_create_grpc_server
|
|
69
68
|
from flwr.common.logger import log, warn_deprecated_feature
|
70
69
|
from flwr.common.retry_invoker import RetryInvoker, RetryState, exponential
|
71
70
|
from flwr.common.typing import Fab, Run, RunNotRunningException, UserConfig
|
71
|
+
from flwr.compat.client.grpc_client.connection import grpc_connection
|
72
72
|
from flwr.proto.clientappio_pb2_grpc import add_ClientAppIoServicer_to_server
|
73
73
|
from flwr.supernode.nodestate import NodeStateFactory
|
74
74
|
|
@@ -794,7 +794,7 @@ def _init_connection(transport: Optional[str], server_address: str) -> tuple[
|
|
794
794
|
elif transport == TRANSPORT_TYPE_GRPC_ADAPTER:
|
795
795
|
connection, error_type = grpc_adapter, RpcError
|
796
796
|
elif transport == TRANSPORT_TYPE_GRPC_BIDI:
|
797
|
-
connection, error_type = grpc_connection, RpcError
|
797
|
+
connection, error_type = grpc_connection, RpcError # type: ignore[assignment]
|
798
798
|
else:
|
799
799
|
raise ValueError(
|
800
800
|
f"Unknown transport type: {transport} (possible: {TRANSPORT_TYPES})"
|
flwr/server/app.py
CHANGED
@@ -71,6 +71,7 @@ from flwr.proto.grpcadapter_pb2_grpc import add_GrpcAdapterServicer_to_server
|
|
71
71
|
from flwr.server.fleet_event_log_interceptor import FleetEventLogInterceptor
|
72
72
|
from flwr.server.serverapp.app import flwr_serverapp
|
73
73
|
from flwr.simulation.app import flwr_simulation
|
74
|
+
from flwr.supercore.object_store import ObjectStoreFactory
|
74
75
|
from flwr.superexec.app import load_executor
|
75
76
|
from flwr.superexec.exec_grpc import run_exec_api_grpc
|
76
77
|
|
@@ -307,6 +308,9 @@ def run_superlink() -> None:
|
|
307
308
|
# Initialize FfsFactory
|
308
309
|
ffs_factory = FfsFactory(args.storage_dir)
|
309
310
|
|
311
|
+
# Initialize ObjectStoreFactory
|
312
|
+
objectstore_factory = ObjectStoreFactory()
|
313
|
+
|
310
314
|
# Start Exec API
|
311
315
|
executor = load_executor(args)
|
312
316
|
exec_server: grpc.Server = run_exec_api_grpc(
|
@@ -343,6 +347,7 @@ def run_superlink() -> None:
|
|
343
347
|
address=serverappio_address,
|
344
348
|
state_factory=state_factory,
|
345
349
|
ffs_factory=ffs_factory,
|
350
|
+
objectstore_factory=objectstore_factory,
|
346
351
|
certificates=None, # ServerAppIo API doesn't support SSL yet
|
347
352
|
)
|
348
353
|
grpc_servers.append(serverappio_server)
|
@@ -421,6 +426,7 @@ def run_superlink() -> None:
|
|
421
426
|
address=fleet_address,
|
422
427
|
state_factory=state_factory,
|
423
428
|
ffs_factory=ffs_factory,
|
429
|
+
objectstore_factory=objectstore_factory,
|
424
430
|
certificates=certificates,
|
425
431
|
interceptors=interceptors,
|
426
432
|
)
|
@@ -430,6 +436,7 @@ def run_superlink() -> None:
|
|
430
436
|
address=fleet_address,
|
431
437
|
state_factory=state_factory,
|
432
438
|
ffs_factory=ffs_factory,
|
439
|
+
objectstore_factory=objectstore_factory,
|
433
440
|
certificates=certificates,
|
434
441
|
)
|
435
442
|
grpc_servers.append(fleet_server)
|
@@ -668,10 +675,11 @@ def _try_obtain_fleet_event_log_writer_plugin() -> Optional[EventLogWriterPlugin
|
|
668
675
|
sys.exit("No Fleet API event log writer plugins are currently supported.")
|
669
676
|
|
670
677
|
|
671
|
-
def _run_fleet_api_grpc_rere(
|
678
|
+
def _run_fleet_api_grpc_rere( # pylint: disable=R0913, R0917
|
672
679
|
address: str,
|
673
680
|
state_factory: LinkStateFactory,
|
674
681
|
ffs_factory: FfsFactory,
|
682
|
+
objectstore_factory: ObjectStoreFactory,
|
675
683
|
certificates: Optional[tuple[bytes, bytes, bytes]],
|
676
684
|
interceptors: Optional[Sequence[grpc.ServerInterceptor]] = None,
|
677
685
|
) -> grpc.Server:
|
@@ -680,6 +688,7 @@ def _run_fleet_api_grpc_rere(
|
|
680
688
|
fleet_servicer = FleetServicer(
|
681
689
|
state_factory=state_factory,
|
682
690
|
ffs_factory=ffs_factory,
|
691
|
+
objectstore_factory=objectstore_factory,
|
683
692
|
)
|
684
693
|
fleet_add_servicer_to_server_fn = add_FleetServicer_to_server
|
685
694
|
fleet_grpc_server = generic_create_grpc_server(
|
@@ -700,6 +709,7 @@ def _run_fleet_api_grpc_adapter(
|
|
700
709
|
address: str,
|
701
710
|
state_factory: LinkStateFactory,
|
702
711
|
ffs_factory: FfsFactory,
|
712
|
+
objectstore_factory: ObjectStoreFactory,
|
703
713
|
certificates: Optional[tuple[bytes, bytes, bytes]],
|
704
714
|
) -> grpc.Server:
|
705
715
|
"""Run Fleet API (GrpcAdapter)."""
|
@@ -707,6 +717,7 @@ def _run_fleet_api_grpc_adapter(
|
|
707
717
|
fleet_servicer = GrpcAdapterServicer(
|
708
718
|
state_factory=state_factory,
|
709
719
|
ffs_factory=ffs_factory,
|
720
|
+
objectstore_factory=objectstore_factory,
|
710
721
|
)
|
711
722
|
fleet_add_servicer_to_server_fn = add_GrpcAdapterServicer_to_server
|
712
723
|
fleet_grpc_server = generic_create_grpc_server(
|
@@ -50,16 +50,21 @@ from flwr.server.superlink.ffs.ffs_factory import FfsFactory
|
|
50
50
|
from flwr.server.superlink.fleet.message_handler import message_handler
|
51
51
|
from flwr.server.superlink.linkstate import LinkStateFactory
|
52
52
|
from flwr.server.superlink.utils import abort_grpc_context
|
53
|
+
from flwr.supercore.object_store import ObjectStoreFactory
|
53
54
|
|
54
55
|
|
55
56
|
class FleetServicer(fleet_pb2_grpc.FleetServicer):
|
56
57
|
"""Fleet API servicer."""
|
57
58
|
|
58
59
|
def __init__(
|
59
|
-
self,
|
60
|
+
self,
|
61
|
+
state_factory: LinkStateFactory,
|
62
|
+
ffs_factory: FfsFactory,
|
63
|
+
objectstore_factory: ObjectStoreFactory,
|
60
64
|
) -> None:
|
61
65
|
self.state_factory = state_factory
|
62
66
|
self.ffs_factory = ffs_factory
|
67
|
+
self.objectstore_factory = objectstore_factory
|
63
68
|
|
64
69
|
def CreateNode(
|
65
70
|
self, request: CreateNodeRequest, context: grpc.ServicerContext
|
@@ -28,6 +28,7 @@ from flwr.proto.serverappio_pb2_grpc import ( # pylint: disable=E0611
|
|
28
28
|
)
|
29
29
|
from flwr.server.superlink.ffs.ffs_factory import FfsFactory
|
30
30
|
from flwr.server.superlink.linkstate import LinkStateFactory
|
31
|
+
from flwr.supercore.object_store import ObjectStoreFactory
|
31
32
|
|
32
33
|
from .serverappio_servicer import ServerAppIoServicer
|
33
34
|
|
@@ -36,6 +37,7 @@ def run_serverappio_api_grpc(
|
|
36
37
|
address: str,
|
37
38
|
state_factory: LinkStateFactory,
|
38
39
|
ffs_factory: FfsFactory,
|
40
|
+
objectstore_factory: ObjectStoreFactory,
|
39
41
|
certificates: Optional[tuple[bytes, bytes, bytes]],
|
40
42
|
) -> grpc.Server:
|
41
43
|
"""Run ServerAppIo API (gRPC, request-response)."""
|
@@ -43,6 +45,7 @@ def run_serverappio_api_grpc(
|
|
43
45
|
serverappio_servicer: grpc.Server = ServerAppIoServicer(
|
44
46
|
state_factory=state_factory,
|
45
47
|
ffs_factory=ffs_factory,
|
48
|
+
objectstore_factory=objectstore_factory,
|
46
49
|
)
|
47
50
|
serverappio_add_servicer_to_server_fn = add_ServerAppIoServicer_to_server
|
48
51
|
serverappio_grpc_server = generic_create_grpc_server(
|
@@ -83,16 +83,21 @@ from flwr.server.superlink.ffs.ffs_factory import FfsFactory
|
|
83
83
|
from flwr.server.superlink.linkstate import LinkState, LinkStateFactory
|
84
84
|
from flwr.server.superlink.utils import abort_if
|
85
85
|
from flwr.server.utils.validator import validate_message
|
86
|
+
from flwr.supercore.object_store import ObjectStoreFactory
|
86
87
|
|
87
88
|
|
88
89
|
class ServerAppIoServicer(serverappio_pb2_grpc.ServerAppIoServicer):
|
89
90
|
"""ServerAppIo API servicer."""
|
90
91
|
|
91
92
|
def __init__(
|
92
|
-
self,
|
93
|
+
self,
|
94
|
+
state_factory: LinkStateFactory,
|
95
|
+
ffs_factory: FfsFactory,
|
96
|
+
objectstore_factory: ObjectStoreFactory,
|
93
97
|
) -> None:
|
94
98
|
self.state_factory = state_factory
|
95
99
|
self.ffs_factory = ffs_factory
|
100
|
+
self.objectstore_factory = objectstore_factory
|
96
101
|
self.lock = threading.RLock()
|
97
102
|
|
98
103
|
def GetNodes(
|
@@ -0,0 +1,23 @@
|
|
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
|
+
"""Flower ObjectStore."""
|
16
|
+
|
17
|
+
from .object_store import ObjectStore
|
18
|
+
from .object_store_factory import ObjectStoreFactory
|
19
|
+
|
20
|
+
__all__ = [
|
21
|
+
"ObjectStore",
|
22
|
+
"ObjectStoreFactory",
|
23
|
+
]
|
@@ -0,0 +1,65 @@
|
|
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
|
+
"""Flower in-memory ObjectStore implementation."""
|
16
|
+
|
17
|
+
|
18
|
+
from typing import Optional
|
19
|
+
|
20
|
+
from flwr.common.inflatable import get_object_id, is_valid_sha256_hash
|
21
|
+
|
22
|
+
from .object_store import ObjectStore
|
23
|
+
|
24
|
+
|
25
|
+
class InMemoryObjectStore(ObjectStore):
|
26
|
+
"""In-memory implementation of the ObjectStore interface."""
|
27
|
+
|
28
|
+
def __init__(self, verify: bool = True) -> None:
|
29
|
+
self.verify = verify
|
30
|
+
self.store: dict[str, bytes] = {}
|
31
|
+
|
32
|
+
def put(self, object_id: str, object_content: bytes) -> None:
|
33
|
+
"""Put an object into the store."""
|
34
|
+
# Verify object ID format (must be a valid sha256 hash)
|
35
|
+
if not is_valid_sha256_hash(object_id):
|
36
|
+
raise ValueError(f"Invalid object ID format: {object_id}")
|
37
|
+
|
38
|
+
# Verify object_id and object_content match
|
39
|
+
if self.verify:
|
40
|
+
object_id_from_content = get_object_id(object_content)
|
41
|
+
if object_id != object_id_from_content:
|
42
|
+
raise ValueError(f"Object ID {object_id} does not match content hash")
|
43
|
+
|
44
|
+
# Return if object is already present in the store
|
45
|
+
if object_id in self.store:
|
46
|
+
return
|
47
|
+
|
48
|
+
self.store[object_id] = object_content
|
49
|
+
|
50
|
+
def get(self, object_id: str) -> Optional[bytes]:
|
51
|
+
"""Get an object from the store."""
|
52
|
+
return self.store.get(object_id)
|
53
|
+
|
54
|
+
def delete(self, object_id: str) -> None:
|
55
|
+
"""Delete an object from the store."""
|
56
|
+
if object_id in self.store:
|
57
|
+
del self.store[object_id]
|
58
|
+
|
59
|
+
def clear(self) -> None:
|
60
|
+
"""Clear the store."""
|
61
|
+
self.store.clear()
|
62
|
+
|
63
|
+
def __contains__(self, object_id: str) -> bool:
|
64
|
+
"""Check if an object_id is in the store."""
|
65
|
+
return object_id in self.store
|
@@ -0,0 +1,86 @@
|
|
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
|
+
"""Flower abstract ObjectStore definition."""
|
16
|
+
|
17
|
+
|
18
|
+
import abc
|
19
|
+
from typing import Optional
|
20
|
+
|
21
|
+
|
22
|
+
class ObjectStore(abc.ABC):
|
23
|
+
"""Abstract base class for `ObjectStore` implementations.
|
24
|
+
|
25
|
+
This class defines the interface for an object store that can store, retrieve, and
|
26
|
+
delete objects identified by object IDs.
|
27
|
+
"""
|
28
|
+
|
29
|
+
@abc.abstractmethod
|
30
|
+
def put(self, object_id: str, object_content: bytes) -> None:
|
31
|
+
"""Put an object into the store.
|
32
|
+
|
33
|
+
Parameters
|
34
|
+
----------
|
35
|
+
object_id : str
|
36
|
+
The object_id under which to store the object.
|
37
|
+
object_content : bytes
|
38
|
+
The deflated object to store.
|
39
|
+
"""
|
40
|
+
|
41
|
+
@abc.abstractmethod
|
42
|
+
def get(self, object_id: str) -> Optional[bytes]:
|
43
|
+
"""Get an object from the store.
|
44
|
+
|
45
|
+
Parameters
|
46
|
+
----------
|
47
|
+
object_id : str
|
48
|
+
The object_id under which the object is stored.
|
49
|
+
|
50
|
+
Returns
|
51
|
+
-------
|
52
|
+
bytes
|
53
|
+
The object stored under the given object_id.
|
54
|
+
"""
|
55
|
+
|
56
|
+
@abc.abstractmethod
|
57
|
+
def delete(self, object_id: str) -> None:
|
58
|
+
"""Delete an object from the store.
|
59
|
+
|
60
|
+
Parameters
|
61
|
+
----------
|
62
|
+
object_id : str
|
63
|
+
The object_id under which the object is stored.
|
64
|
+
"""
|
65
|
+
|
66
|
+
@abc.abstractmethod
|
67
|
+
def clear(self) -> None:
|
68
|
+
"""Clear the store.
|
69
|
+
|
70
|
+
This method should remove all objects from the store.
|
71
|
+
"""
|
72
|
+
|
73
|
+
@abc.abstractmethod
|
74
|
+
def __contains__(self, object_id: str) -> bool:
|
75
|
+
"""Check if an object_id is in the store.
|
76
|
+
|
77
|
+
Parameters
|
78
|
+
----------
|
79
|
+
object_id : str
|
80
|
+
The object_id to check.
|
81
|
+
|
82
|
+
Returns
|
83
|
+
-------
|
84
|
+
bool
|
85
|
+
True if the object_id is in the store, False otherwise.
|
86
|
+
"""
|
@@ -0,0 +1,44 @@
|
|
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
|
+
"""Factory class that creates ObjectStore instances."""
|
16
|
+
|
17
|
+
|
18
|
+
from logging import DEBUG
|
19
|
+
from typing import Optional
|
20
|
+
|
21
|
+
from flwr.common.logger import log
|
22
|
+
|
23
|
+
from .in_memory_object_store import InMemoryObjectStore
|
24
|
+
from .object_store import ObjectStore
|
25
|
+
|
26
|
+
|
27
|
+
class ObjectStoreFactory:
|
28
|
+
"""Factory class that creates ObjectStore instances."""
|
29
|
+
|
30
|
+
def __init__(self) -> None:
|
31
|
+
self.store_instance: Optional[ObjectStore] = None
|
32
|
+
|
33
|
+
def store(self) -> ObjectStore:
|
34
|
+
"""Return an ObjectStore instance and create it, if necessary.
|
35
|
+
|
36
|
+
Returns
|
37
|
+
-------
|
38
|
+
ObjectStore
|
39
|
+
An ObjectStore instance for storing objects by object_id.
|
40
|
+
"""
|
41
|
+
if self.store_instance is None:
|
42
|
+
self.store_instance = InMemoryObjectStore()
|
43
|
+
log(DEBUG, "Using InMemoryObjectStore")
|
44
|
+
return self.store_instance
|
{flwr_nightly-1.19.0.dev20250521.dist-info → flwr_nightly-1.19.0.dev20250522.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.dev20250522
|
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.dev20250521.dist-info → flwr_nightly-1.19.0.dev20250522.dist-info}/RECORD
RENAMED
@@ -83,12 +83,10 @@ flwr/client/clientapp/clientappio_servicer.py,sha256=LmzkxtNQBn5vVrHc0Bhq2WqaK6-
|
|
83
83
|
flwr/client/clientapp/utils.py,sha256=LsiW1OL2VPcjom3xN29pgBQC0UrttQ-xWL_GF1fkKDo,4344
|
84
84
|
flwr/client/dpfedavg_numpy_client.py,sha256=3hul067cT2E9jBhzp7bFnFAZ_D2nWcIUEdHYE05FpzU,7404
|
85
85
|
flwr/client/grpc_adapter_client/__init__.py,sha256=RQWP5mFPROLHKgombiRvPXVWSoVrQ81wvZm0-lOuuBA,742
|
86
|
-
flwr/client/grpc_adapter_client/connection.py,sha256=
|
87
|
-
flwr/client/grpc_client/__init__.py,sha256=MDOckOODn-FJnkkFEfb2JO-2G97wrBr_TTqht-LhOcY,735
|
88
|
-
flwr/client/grpc_client/connection.py,sha256=xAyvcTVr7bkwUfR5P3D_LKlZYiyySpt5sEwORA1h8Gc,9189
|
86
|
+
flwr/client/grpc_adapter_client/connection.py,sha256=aj5tTYyE8z2hQLXPPydsJiz8gBDIWLUhfWvqYkAL1L4,3966
|
89
87
|
flwr/client/grpc_rere_client/__init__.py,sha256=i7iS0Lt8B7q0E2L72e4F_YrKm6ClRKnd71PNA6PW2O0,752
|
90
88
|
flwr/client/grpc_rere_client/client_interceptor.py,sha256=zFaVHw6AxeNO-7eCKKb-RxrPa7zbM5Z-2-1Efc4adQY,2451
|
91
|
-
flwr/client/grpc_rere_client/connection.py,sha256=
|
89
|
+
flwr/client/grpc_rere_client/connection.py,sha256=ErDTr00KNJASx7hsI2sDFkj8fj8k5y2NFQ4wsYELe6Y,12024
|
92
90
|
flwr/client/grpc_rere_client/grpc_adapter.py,sha256=JvMZ7vCFTaTEo6AzKYh3zDmeQAU7VSjdysbC6t3ufWg,6351
|
93
91
|
flwr/client/message_handler/__init__.py,sha256=0lyljDVqre3WljiZbPcwCCf8GiIaSVI_yo_ylEyPwSE,719
|
94
92
|
flwr/client/message_handler/message_handler.py,sha256=-vZKGg2gP81182LFXDmiZtajLlIfZjV6FyMS43qQVwU,6532
|
@@ -102,11 +100,11 @@ flwr/client/mod/secure_aggregation/secaggplus_mod.py,sha256=aKqjZCrikF73y3E-7h40
|
|
102
100
|
flwr/client/mod/utils.py,sha256=FUgD2TfcWqSeF6jUKZ4i6Ke56U4Nrv85AeVb93s6R9g,1201
|
103
101
|
flwr/client/numpy_client.py,sha256=Qq6ghsIAop2slKqAfgiI5NiHJ4LIxGmrik3Ror4_XVc,9581
|
104
102
|
flwr/client/rest_client/__init__.py,sha256=MBiuK62hj439m9rtwSwI184Hth6Tt5GbmpNMyl3zkZY,735
|
105
|
-
flwr/client/rest_client/connection.py,sha256=
|
103
|
+
flwr/client/rest_client/connection.py,sha256=6yBh2Eeso0XLtinAs2kOHkSnge7C-co_a_QfBaAEudU,12766
|
106
104
|
flwr/client/run_info_store.py,sha256=MaJ3UQ-07hWtK67wnWu0zR29jrk0fsfgJX506dvEOfE,4042
|
107
|
-
flwr/client/start_client_internal.py,sha256
|
105
|
+
flwr/client/start_client_internal.py,sha256=-FOBQE65a-ZsuTUiW8WcZoBZt9q_b3ee-JK5-H8ivME,19850
|
108
106
|
flwr/client/supernode/__init__.py,sha256=i3gFbV5ie_FGyRMpzOvqtZAi0Z0ChIEJ7I2Kr0ym0PM,793
|
109
|
-
flwr/client/supernode/app.py,sha256=
|
107
|
+
flwr/client/supernode/app.py,sha256=an-aT2zZEL5Mv7StgE1el0-fgIvKSQIuihJubRUuzyo,8753
|
110
108
|
flwr/client/typing.py,sha256=Jw3rawDzI_-ZDcRmEQcs5gZModY7oeQlEeltYsdOhlU,1048
|
111
109
|
flwr/clientapp/__init__.py,sha256=zGW4z49Ojzoi1hDiRC7kyhLjijUilc6fqHhtM_ATRVA,719
|
112
110
|
flwr/common/__init__.py,sha256=5GCLVk399Az_rTJHNticRlL0Sl_oPw_j5_LuFKfX7-M,4171
|
@@ -129,8 +127,8 @@ flwr/common/exit/exit_code.py,sha256=PNEnCrZfOILjfDAFu5m-2YWEJBrk97xglq4zCUlqV7E
|
|
129
127
|
flwr/common/exit_handlers.py,sha256=MEk5_savTLphn-6lW57UQlos-XrFA39XEBn-OF1vXXg,3174
|
130
128
|
flwr/common/grpc.py,sha256=manTaHaPiyYngUq1ErZvvV2B2GxlXUUUGRy3jc3TBIQ,9798
|
131
129
|
flwr/common/heartbeat.py,sha256=SyEpNDnmJ0lni0cWO67rcoJVKasCLmkNHm3dKLeNrLU,5749
|
132
|
-
flwr/common/inflatable.py,sha256=
|
133
|
-
flwr/common/inflatable_grpc_utils.py,sha256=
|
130
|
+
flwr/common/inflatable.py,sha256=i7tYRS5ql4630wN7O0YLCoWZMXk3iNQwOnnrZs4dlDE,5813
|
131
|
+
flwr/common/inflatable_grpc_utils.py,sha256=On_RT8cYChwE3-1MlY21s_cVXgYCGeKTONRafm-rmEk,3432
|
134
132
|
flwr/common/logger.py,sha256=JbRf6E2vQxXzpDBq1T8IDUJo_usu3gjWEBPQ6uKcmdg,13049
|
135
133
|
flwr/common/message.py,sha256=dfct6ZGizK2zSj2JLiQTRbOfDNu79KzwUplpQaxFg40,18997
|
136
134
|
flwr/common/object_ref.py,sha256=p3SfTeqo3Aj16SkB-vsnNn01zswOPdGNBitcbRnqmUk,9134
|
@@ -161,7 +159,9 @@ flwr/common/typing.py,sha256=97QRfRRS7sQnjkAI5FDZ01-38oQUSz4i1qqewQmBWRg,6886
|
|
161
159
|
flwr/common/version.py,sha256=7GAGzPn73Mkh09qhrjbmjZQtcqVhBuzhFBaK4Mk4VRk,1325
|
162
160
|
flwr/compat/__init__.py,sha256=gbfDQKKKMZzi3GswyVRgyLdDlHiWj3wU6dg7y6m5O_s,752
|
163
161
|
flwr/compat/client/__init__.py,sha256=qpbo0lcxdNL4qy5KHqiGm8OLxSxkYgI_-dLh5rwhtcI,746
|
164
|
-
flwr/compat/client/app.py,sha256=
|
162
|
+
flwr/compat/client/app.py,sha256=49novX4eW9uZiU4zxIPNWzThcs0TuihygLtsT_T8Zjs,34397
|
163
|
+
flwr/compat/client/grpc_client/__init__.py,sha256=MDOckOODn-FJnkkFEfb2JO-2G97wrBr_TTqht-LhOcY,735
|
164
|
+
flwr/compat/client/grpc_client/connection.py,sha256=xAyvcTVr7bkwUfR5P3D_LKlZYiyySpt5sEwORA1h8Gc,9189
|
165
165
|
flwr/compat/common/__init__.py,sha256=OMnKw4ad0qYMSIA9LZRa2gOkhSOXwAZCpAHnBQE_hFc,746
|
166
166
|
flwr/compat/server/__init__.py,sha256=TGVSoOTuf5T5JHUVrK5wuorQF7L6Wvdem8B4uufvMJY,746
|
167
167
|
flwr/compat/simulation/__init__.py,sha256=MApGa-tysDDw34iSdxZ7TWOKtGJM-z3i8fIRJa0qbZ8,750
|
@@ -228,7 +228,7 @@ flwr/proto/transport_pb2_grpc.py,sha256=vLN3EHtx2aEEMCO4f1Upu-l27BPzd3-5pV-u8wPc
|
|
228
228
|
flwr/proto/transport_pb2_grpc.pyi,sha256=AGXf8RiIiW2J5IKMlm_3qT3AzcDa4F3P5IqUjve_esA,766
|
229
229
|
flwr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
230
230
|
flwr/server/__init__.py,sha256=e5PjIYUkzaQpcrUrvgb_6rDGzNqw1RfRsggdHlhfeyY,1569
|
231
|
-
flwr/server/app.py,sha256=
|
231
|
+
flwr/server/app.py,sha256=cWJuEhnizI_7XJObT8dTm6X8yvY-A9Idp6Co8jonSmU,34150
|
232
232
|
flwr/server/client_manager.py,sha256=5jCGavVli7XdupvWWo7ru3PdFTlRU8IGvHFSSoUVLRs,6227
|
233
233
|
flwr/server/client_proxy.py,sha256=sv0E9AldBYOvc3pusqFh-GnyreeMfsXQ1cuTtxTq_wY,2399
|
234
234
|
flwr/server/compat/__init__.py,sha256=0IsttWvY15qO98_1GyzVC-vR1e_ZPXOdu2qUlOkYMPE,886
|
@@ -288,7 +288,7 @@ flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py,sha256=KouR9PUcrPmMtoLooF4O
|
|
288
288
|
flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py,sha256=iSf0mbBAlig7G6subQwBSVjcUCgSihONKdZ1RmQPTOk,4887
|
289
289
|
flwr/server/superlink/fleet/grpc_bidi/grpc_server.py,sha256=OsS-6GgCIzMMZDVu5Y-OKjynHVUrpdc_5OrtuB-IbU0,5174
|
290
290
|
flwr/server/superlink/fleet/grpc_rere/__init__.py,sha256=ahDJJ1e-lDxBpeBMgPk7YZt2wB38_QltcpOC0gLbpFs,758
|
291
|
-
flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py,sha256
|
291
|
+
flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py,sha256=--Xfd3FqBUIKHHQ1oS53Zal3rqqxrCORcpbFQeGSDlY,7081
|
292
292
|
flwr/server/superlink/fleet/grpc_rere/server_interceptor.py,sha256=DrHubsaLgJCwCeeJPYogQTiP0xYqjxwnT9rh7OP7BoU,6984
|
293
293
|
flwr/server/superlink/fleet/message_handler/__init__.py,sha256=fHsRV0KvJ8HtgSA4_YBsEzuhJLjO8p6xx4aCY2oE1p4,731
|
294
294
|
flwr/server/superlink/fleet/message_handler/message_handler.py,sha256=56LC_cPa_pZT1715WTCN0d7MmvdRXlyO7g_hb9zdJrA,5427
|
@@ -306,8 +306,8 @@ flwr/server/superlink/linkstate/linkstate_factory.py,sha256=8RlosqSpKOoD_vhUUQPY
|
|
306
306
|
flwr/server/superlink/linkstate/sqlite_linkstate.py,sha256=z3VABMX_WtAioWJ2aUOsxi53-ecF2c8xEQ69xP3xXW8,43587
|
307
307
|
flwr/server/superlink/linkstate/utils.py,sha256=AJs9jTAEK7JnjF2AODXnOfy0pKAKpe6oUWPCanAP57s,15382
|
308
308
|
flwr/server/superlink/serverappio/__init__.py,sha256=Fy4zJuoccZe5mZSEIpOmQvU6YeXFBa1M4eZuXXmJcn8,717
|
309
|
-
flwr/server/superlink/serverappio/serverappio_grpc.py,sha256=
|
310
|
-
flwr/server/superlink/serverappio/serverappio_servicer.py,sha256=
|
309
|
+
flwr/server/superlink/serverappio/serverappio_grpc.py,sha256=6-FUUt0GiLcBPljj8bBrUNeAITUoDQOLzaMihKo52hg,2326
|
310
|
+
flwr/server/superlink/serverappio/serverappio_servicer.py,sha256=EgTdKwpPZdAN6LnDNWy450aJQ-zh-bmcVTr739yRx_M,15049
|
311
311
|
flwr/server/superlink/simulation/__init__.py,sha256=Ry8DrNaZCMcQXvUc4FoCN2m3dvUQgWjasfp015o3Ec4,718
|
312
312
|
flwr/server/superlink/simulation/simulationio_grpc.py,sha256=0l0F-UjYEk6W7HZmI28PbJQLFxSi_vBHRkdchgdaSMQ,2224
|
313
313
|
flwr/server/superlink/simulation/simulationio_servicer.py,sha256=aJezU8RSJswcmWm7Eoy0BqsU13jrcfuFwX3ljm-cORM,7719
|
@@ -333,6 +333,10 @@ flwr/simulation/ray_transport/utils.py,sha256=KrexpWYCF-dAF3UHc9yDbPQWO-ahMT-BbD
|
|
333
333
|
flwr/simulation/run_simulation.py,sha256=Nvw_6hI71aE2nU95_tt1F9VSo3OJWrvA97e3XZuqE4k,20310
|
334
334
|
flwr/simulation/simulationio_connection.py,sha256=mzS1C6EEREwQDPceDo30anAasmTDLb9qqV2tXlBhOUA,3494
|
335
335
|
flwr/supercore/__init__.py,sha256=pqkFoow_E6UhbBlhmoD1gmTH-33yJRhBsIZqxRPFZ7U,755
|
336
|
+
flwr/supercore/object_store/__init__.py,sha256=7dvl-iNyZjEla9lLijc6Hn6HbdxXwsiA_ktF7PCYcSY,861
|
337
|
+
flwr/supercore/object_store/in_memory_object_store.py,sha256=ifHYpey40jQEFpnhl1SzukL9wqjxJNRdXbg1q_abnpM,2388
|
338
|
+
flwr/supercore/object_store/object_store.py,sha256=T24iVoWLM0D_hJw34jbuI4mxTaDW7PMBO2oKjCPeb_U,2460
|
339
|
+
flwr/supercore/object_store/object_store_factory.py,sha256=QVwE2ywi7vsj2iKfvWWnNw3N_I7Rz91NUt2RpcbJ7iM,1527
|
336
340
|
flwr/superexec/__init__.py,sha256=YFqER0IJc1XEWfsX6AxZ9LSRq0sawPYrNYki-brvTIc,715
|
337
341
|
flwr/superexec/app.py,sha256=U2jjOHb2LGWoU7vrl9_czTzre9O2mPxu3CPGUZ86sK4,1465
|
338
342
|
flwr/superexec/deployment.py,sha256=2wBBZgdNAn1Ik1M3HGg4t23CV8oZqzDz1zkOBzHjZLE,6734
|
@@ -348,7 +352,7 @@ flwr/supernode/nodestate/__init__.py,sha256=CyLLObbmmVgfRO88UCM0VMait1dL57mUauUD
|
|
348
352
|
flwr/supernode/nodestate/in_memory_nodestate.py,sha256=brV7TMMzS93tXk6ntpoYjtPK5qiSF3XD2W-uUdUVucc,1270
|
349
353
|
flwr/supernode/nodestate/nodestate.py,sha256=-LAjZOnS7VyHC05ll3b31cYDjwAt6l4WmYt7duVLRKk,1024
|
350
354
|
flwr/supernode/nodestate/nodestate_factory.py,sha256=UYTDCcwK_baHUmkzkJDxL0UEqvtTfOMlQRrROMCd0Xo,1430
|
351
|
-
flwr_nightly-1.19.0.
|
352
|
-
flwr_nightly-1.19.0.
|
353
|
-
flwr_nightly-1.19.0.
|
354
|
-
flwr_nightly-1.19.0.
|
355
|
+
flwr_nightly-1.19.0.dev20250522.dist-info/METADATA,sha256=wBiKx7nd4BQMsaqKkTQJzxbEEdq0GLoD2aSx6Yy1jpw,15910
|
356
|
+
flwr_nightly-1.19.0.dev20250522.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
357
|
+
flwr_nightly-1.19.0.dev20250522.dist-info/entry_points.txt,sha256=2-1L-GNKhwGw2_7_RoH55vHw2SIHjdAQy3HAVAWl9PY,374
|
358
|
+
flwr_nightly-1.19.0.dev20250522.dist-info/RECORD,,
|
File without changes
|
File without changes
|
{flwr_nightly-1.19.0.dev20250521.dist-info → flwr_nightly-1.19.0.dev20250522.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|