flwr-nightly 1.10.0.dev20240612__py3-none-any.whl → 1.10.0.dev20240613__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of flwr-nightly might be problematic. Click here for more details.
- flwr/cli/build.py +3 -1
- flwr/cli/config_utils.py +53 -3
- flwr/cli/install.py +35 -20
- flwr/client/app.py +18 -10
- flwr/client/supernode/app.py +141 -38
- flwr/common/config.py +28 -0
- flwr/common/constant.py +2 -0
- flwr/common/telemetry.py +4 -0
- flwr/proto/exec_pb2.py +30 -0
- flwr/proto/exec_pb2.pyi +32 -0
- flwr/proto/exec_pb2_grpc.py +67 -0
- flwr/proto/exec_pb2_grpc.pyi +27 -0
- flwr/server/app.py +15 -18
- flwr/superexec/__init__.py +19 -0
- {flwr_nightly-1.10.0.dev20240612.dist-info → flwr_nightly-1.10.0.dev20240613.dist-info}/METADATA +1 -1
- {flwr_nightly-1.10.0.dev20240612.dist-info → flwr_nightly-1.10.0.dev20240613.dist-info}/RECORD +19 -13
- {flwr_nightly-1.10.0.dev20240612.dist-info → flwr_nightly-1.10.0.dev20240613.dist-info}/entry_points.txt +1 -0
- {flwr_nightly-1.10.0.dev20240612.dist-info → flwr_nightly-1.10.0.dev20240613.dist-info}/LICENSE +0 -0
- {flwr_nightly-1.10.0.dev20240612.dist-info → flwr_nightly-1.10.0.dev20240613.dist-info}/WHEEL +0 -0
flwr/cli/build.py
CHANGED
|
@@ -33,7 +33,7 @@ def build(
|
|
|
33
33
|
Optional[Path],
|
|
34
34
|
typer.Option(help="The Flower project directory to bundle into a FAB"),
|
|
35
35
|
] = None,
|
|
36
|
-
) ->
|
|
36
|
+
) -> str:
|
|
37
37
|
"""Build a Flower project into a Flower App Bundle (FAB).
|
|
38
38
|
|
|
39
39
|
You can run `flwr build` without any argument to bundle the current directory:
|
|
@@ -125,6 +125,8 @@ def build(
|
|
|
125
125
|
f"🎊 Successfully built {fab_filename}.", fg=typer.colors.GREEN, bold=True
|
|
126
126
|
)
|
|
127
127
|
|
|
128
|
+
return fab_filename
|
|
129
|
+
|
|
128
130
|
|
|
129
131
|
def _load_gitignore(directory: Path) -> pathspec.PathSpec:
|
|
130
132
|
"""Load and parse .gitignore file, returning a pathspec."""
|
flwr/cli/config_utils.py
CHANGED
|
@@ -14,14 +14,56 @@
|
|
|
14
14
|
# ==============================================================================
|
|
15
15
|
"""Utility to validate the `pyproject.toml` file."""
|
|
16
16
|
|
|
17
|
+
import zipfile
|
|
18
|
+
from io import BytesIO
|
|
17
19
|
from pathlib import Path
|
|
18
|
-
from typing import Any, Dict, List, Optional, Tuple
|
|
20
|
+
from typing import IO, Any, Dict, List, Optional, Tuple, Union
|
|
19
21
|
|
|
20
22
|
import tomli
|
|
21
23
|
|
|
22
24
|
from flwr.common import object_ref
|
|
23
25
|
|
|
24
26
|
|
|
27
|
+
def get_fab_metadata(fab_file: Union[Path, bytes]) -> Tuple[str, str]:
|
|
28
|
+
"""Extract the fab_id and the fab_version from a FAB file or path.
|
|
29
|
+
|
|
30
|
+
Parameters
|
|
31
|
+
----------
|
|
32
|
+
fab_file : Union[Path, bytes]
|
|
33
|
+
The Flower App Bundle file to validate and extract the metadata from.
|
|
34
|
+
It can either be a path to the file or the file itself as bytes.
|
|
35
|
+
|
|
36
|
+
Returns
|
|
37
|
+
-------
|
|
38
|
+
Tuple[str, str]
|
|
39
|
+
The `fab_version` and `fab_id` of the given Flower App Bundle.
|
|
40
|
+
"""
|
|
41
|
+
fab_file_archive: Union[Path, IO[bytes]]
|
|
42
|
+
if isinstance(fab_file, bytes):
|
|
43
|
+
fab_file_archive = BytesIO(fab_file)
|
|
44
|
+
elif isinstance(fab_file, Path):
|
|
45
|
+
fab_file_archive = fab_file
|
|
46
|
+
else:
|
|
47
|
+
raise ValueError("fab_file must be either a Path or bytes")
|
|
48
|
+
|
|
49
|
+
with zipfile.ZipFile(fab_file_archive, "r") as zipf:
|
|
50
|
+
with zipf.open("pyproject.toml") as file:
|
|
51
|
+
toml_content = file.read().decode("utf-8")
|
|
52
|
+
|
|
53
|
+
conf = load_from_string(toml_content)
|
|
54
|
+
if conf is None:
|
|
55
|
+
raise ValueError("Invalid TOML content in pyproject.toml")
|
|
56
|
+
|
|
57
|
+
is_valid, errors, _ = validate(conf)
|
|
58
|
+
if not is_valid:
|
|
59
|
+
raise ValueError(errors)
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
conf["project"]["version"],
|
|
63
|
+
f"{conf['flower']['publisher']}/{conf['project']['name']}",
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
25
67
|
def load_and_validate(
|
|
26
68
|
path: Optional[Path] = None,
|
|
27
69
|
check_module: bool = True,
|
|
@@ -63,8 +105,7 @@ def load(path: Optional[Path] = None) -> Optional[Dict[str, Any]]:
|
|
|
63
105
|
return None
|
|
64
106
|
|
|
65
107
|
with toml_path.open(encoding="utf-8") as toml_file:
|
|
66
|
-
|
|
67
|
-
return data
|
|
108
|
+
return load_from_string(toml_file.read())
|
|
68
109
|
|
|
69
110
|
|
|
70
111
|
# pylint: disable=too-many-branches
|
|
@@ -128,3 +169,12 @@ def validate(
|
|
|
128
169
|
return False, [reason], []
|
|
129
170
|
|
|
130
171
|
return True, [], []
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def load_from_string(toml_content: str) -> Optional[Dict[str, Any]]:
|
|
175
|
+
"""Load TOML content from a string and return as dict."""
|
|
176
|
+
try:
|
|
177
|
+
data = tomli.loads(toml_content)
|
|
178
|
+
return data
|
|
179
|
+
except tomli.TOMLDecodeError:
|
|
180
|
+
return None
|
flwr/cli/install.py
CHANGED
|
@@ -15,16 +15,18 @@
|
|
|
15
15
|
"""Flower command line interface `install` command."""
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
import os
|
|
19
18
|
import shutil
|
|
20
19
|
import tempfile
|
|
21
20
|
import zipfile
|
|
21
|
+
from io import BytesIO
|
|
22
22
|
from pathlib import Path
|
|
23
|
-
from typing import Optional
|
|
23
|
+
from typing import IO, Optional, Union
|
|
24
24
|
|
|
25
25
|
import typer
|
|
26
26
|
from typing_extensions import Annotated
|
|
27
27
|
|
|
28
|
+
from flwr.common.config import get_flwr_dir
|
|
29
|
+
|
|
28
30
|
from .config_utils import load_and_validate
|
|
29
31
|
from .utils import get_sha256_hash
|
|
30
32
|
|
|
@@ -80,11 +82,24 @@ def install(
|
|
|
80
82
|
|
|
81
83
|
|
|
82
84
|
def install_from_fab(
|
|
83
|
-
fab_file: Path,
|
|
84
|
-
|
|
85
|
+
fab_file: Union[Path, bytes],
|
|
86
|
+
flwr_dir: Optional[Path],
|
|
87
|
+
skip_prompt: bool = False,
|
|
88
|
+
) -> Path:
|
|
85
89
|
"""Install from a FAB file after extracting and validating."""
|
|
90
|
+
fab_file_archive: Union[Path, IO[bytes]]
|
|
91
|
+
fab_name: Optional[str]
|
|
92
|
+
if isinstance(fab_file, bytes):
|
|
93
|
+
fab_file_archive = BytesIO(fab_file)
|
|
94
|
+
fab_name = None
|
|
95
|
+
elif isinstance(fab_file, Path):
|
|
96
|
+
fab_file_archive = fab_file
|
|
97
|
+
fab_name = fab_file.stem
|
|
98
|
+
else:
|
|
99
|
+
raise ValueError("fab_file must be either a Path or bytes")
|
|
100
|
+
|
|
86
101
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
87
|
-
with zipfile.ZipFile(
|
|
102
|
+
with zipfile.ZipFile(fab_file_archive, "r") as zipf:
|
|
88
103
|
zipf.extractall(tmpdir)
|
|
89
104
|
tmpdir_path = Path(tmpdir)
|
|
90
105
|
info_dir = tmpdir_path / ".info"
|
|
@@ -110,15 +125,19 @@ def install_from_fab(
|
|
|
110
125
|
|
|
111
126
|
shutil.rmtree(info_dir)
|
|
112
127
|
|
|
113
|
-
validate_and_install(
|
|
128
|
+
installed_path = validate_and_install(
|
|
129
|
+
tmpdir_path, fab_name, flwr_dir, skip_prompt
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
return installed_path
|
|
114
133
|
|
|
115
134
|
|
|
116
135
|
def validate_and_install(
|
|
117
136
|
project_dir: Path,
|
|
118
|
-
fab_name: str,
|
|
137
|
+
fab_name: Optional[str],
|
|
119
138
|
flwr_dir: Optional[Path],
|
|
120
139
|
skip_prompt: bool = False,
|
|
121
|
-
) ->
|
|
140
|
+
) -> Path:
|
|
122
141
|
"""Validate TOML files and install the project to the desired directory."""
|
|
123
142
|
config, _, _ = load_and_validate(project_dir / "pyproject.toml", check_module=False)
|
|
124
143
|
|
|
@@ -134,7 +153,10 @@ def validate_and_install(
|
|
|
134
153
|
project_name = config["project"]["name"]
|
|
135
154
|
version = config["project"]["version"]
|
|
136
155
|
|
|
137
|
-
if
|
|
156
|
+
if (
|
|
157
|
+
fab_name
|
|
158
|
+
and fab_name != f"{publisher}.{project_name}.{version.replace('.', '-')}"
|
|
159
|
+
):
|
|
138
160
|
typer.secho(
|
|
139
161
|
"❌ FAB file has incorrect name. The file name must follow the format "
|
|
140
162
|
"`<publisher>.<project_name>.<version>.fab`.",
|
|
@@ -144,16 +166,7 @@ def validate_and_install(
|
|
|
144
166
|
raise typer.Exit(code=1)
|
|
145
167
|
|
|
146
168
|
install_dir: Path = (
|
|
147
|
-
(
|
|
148
|
-
Path(
|
|
149
|
-
os.getenv(
|
|
150
|
-
"FLWR_HOME",
|
|
151
|
-
f"{os.getenv('XDG_DATA_HOME', os.getenv('HOME'))}/.flwr",
|
|
152
|
-
)
|
|
153
|
-
)
|
|
154
|
-
if not flwr_dir
|
|
155
|
-
else flwr_dir
|
|
156
|
-
)
|
|
169
|
+
(get_flwr_dir() if not flwr_dir else flwr_dir)
|
|
157
170
|
/ "apps"
|
|
158
171
|
/ publisher
|
|
159
172
|
/ project_name
|
|
@@ -168,7 +181,7 @@ def validate_and_install(
|
|
|
168
181
|
bold=True,
|
|
169
182
|
)
|
|
170
183
|
):
|
|
171
|
-
return
|
|
184
|
+
return install_dir
|
|
172
185
|
|
|
173
186
|
install_dir.mkdir(parents=True, exist_ok=True)
|
|
174
187
|
|
|
@@ -185,6 +198,8 @@ def validate_and_install(
|
|
|
185
198
|
bold=True,
|
|
186
199
|
)
|
|
187
200
|
|
|
201
|
+
return install_dir
|
|
202
|
+
|
|
188
203
|
|
|
189
204
|
def _verify_hashes(list_content: str, tmpdir: Path) -> bool:
|
|
190
205
|
"""Verify file hashes based on the LIST content."""
|
flwr/client/app.py
CHANGED
|
@@ -19,7 +19,7 @@ import sys
|
|
|
19
19
|
import time
|
|
20
20
|
from dataclasses import dataclass
|
|
21
21
|
from logging import DEBUG, ERROR, INFO, WARN
|
|
22
|
-
from typing import Callable, ContextManager, Optional, Tuple, Type, Union
|
|
22
|
+
from typing import Callable, ContextManager, Dict, Optional, Tuple, Type, Union
|
|
23
23
|
|
|
24
24
|
from cryptography.hazmat.primitives.asymmetric import ec
|
|
25
25
|
from grpc import RpcError
|
|
@@ -177,7 +177,7 @@ def start_client(
|
|
|
177
177
|
def _start_client_internal(
|
|
178
178
|
*,
|
|
179
179
|
server_address: str,
|
|
180
|
-
load_client_app_fn: Optional[Callable[[], ClientApp]] = None,
|
|
180
|
+
load_client_app_fn: Optional[Callable[[str, str], ClientApp]] = None,
|
|
181
181
|
client_fn: Optional[ClientFn] = None,
|
|
182
182
|
client: Optional[Client] = None,
|
|
183
183
|
grpc_max_message_length: int = GRPC_MAX_MESSAGE_LENGTH,
|
|
@@ -252,7 +252,7 @@ def _start_client_internal(
|
|
|
252
252
|
|
|
253
253
|
client_fn = single_client_factory
|
|
254
254
|
|
|
255
|
-
def _load_client_app() -> ClientApp:
|
|
255
|
+
def _load_client_app(_1: str, _2: str) -> ClientApp:
|
|
256
256
|
return ClientApp(client_fn=client_fn)
|
|
257
257
|
|
|
258
258
|
load_client_app_fn = _load_client_app
|
|
@@ -308,6 +308,8 @@ def _start_client_internal(
|
|
|
308
308
|
)
|
|
309
309
|
|
|
310
310
|
node_state = NodeState()
|
|
311
|
+
# run_id -> (fab_id, fab_version)
|
|
312
|
+
run_info: Dict[int, Tuple[str, str]] = {}
|
|
311
313
|
|
|
312
314
|
while not app_state_tracker.interrupt:
|
|
313
315
|
sleep_duration: int = 0
|
|
@@ -319,7 +321,6 @@ def _start_client_internal(
|
|
|
319
321
|
root_certificates,
|
|
320
322
|
authentication_keys,
|
|
321
323
|
) as conn:
|
|
322
|
-
# pylint: disable-next=W0612
|
|
323
324
|
receive, send, create_node, delete_node, get_run = conn
|
|
324
325
|
|
|
325
326
|
# Register node
|
|
@@ -356,13 +357,20 @@ def _start_client_internal(
|
|
|
356
357
|
send(out_message)
|
|
357
358
|
break
|
|
358
359
|
|
|
360
|
+
# Get run info
|
|
361
|
+
run_id = message.metadata.run_id
|
|
362
|
+
if run_id not in run_info:
|
|
363
|
+
if get_run is not None:
|
|
364
|
+
run_info[run_id] = get_run(run_id)
|
|
365
|
+
# If get_run is None, i.e., in grpc-bidi mode
|
|
366
|
+
else:
|
|
367
|
+
run_info[run_id] = ("", "")
|
|
368
|
+
|
|
359
369
|
# Register context for this run
|
|
360
|
-
node_state.register_context(run_id=
|
|
370
|
+
node_state.register_context(run_id=run_id)
|
|
361
371
|
|
|
362
372
|
# Retrieve context for this run
|
|
363
|
-
context = node_state.retrieve_context(
|
|
364
|
-
run_id=message.metadata.run_id
|
|
365
|
-
)
|
|
373
|
+
context = node_state.retrieve_context(run_id=run_id)
|
|
366
374
|
|
|
367
375
|
# Create an error reply message that will never be used to prevent
|
|
368
376
|
# the used-before-assignment linting error
|
|
@@ -373,7 +381,7 @@ def _start_client_internal(
|
|
|
373
381
|
# Handle app loading and task message
|
|
374
382
|
try:
|
|
375
383
|
# Load ClientApp instance
|
|
376
|
-
client_app: ClientApp = load_client_app_fn()
|
|
384
|
+
client_app: ClientApp = load_client_app_fn(*run_info[run_id])
|
|
377
385
|
|
|
378
386
|
# Execute ClientApp
|
|
379
387
|
reply_message = client_app(message=message, context=context)
|
|
@@ -411,7 +419,7 @@ def _start_client_internal(
|
|
|
411
419
|
else:
|
|
412
420
|
# No exception, update node state
|
|
413
421
|
node_state.update_context(
|
|
414
|
-
run_id=
|
|
422
|
+
run_id=run_id,
|
|
415
423
|
context=context,
|
|
416
424
|
)
|
|
417
425
|
|
flwr/client/supernode/app.py
CHANGED
|
@@ -20,6 +20,7 @@ from logging import DEBUG, INFO, WARN
|
|
|
20
20
|
from pathlib import Path
|
|
21
21
|
from typing import Callable, Optional, Tuple
|
|
22
22
|
|
|
23
|
+
import tomli
|
|
23
24
|
from cryptography.exceptions import UnsupportedAlgorithm
|
|
24
25
|
from cryptography.hazmat.primitives.asymmetric import ec
|
|
25
26
|
from cryptography.hazmat.primitives.serialization import (
|
|
@@ -27,8 +28,10 @@ from cryptography.hazmat.primitives.serialization import (
|
|
|
27
28
|
load_ssh_public_key,
|
|
28
29
|
)
|
|
29
30
|
|
|
31
|
+
from flwr.cli.config_utils import validate_fields
|
|
30
32
|
from flwr.client.client_app import ClientApp, LoadClientAppError
|
|
31
33
|
from flwr.common import EventType, event
|
|
34
|
+
from flwr.common.config import get_flwr_dir
|
|
32
35
|
from flwr.common.exit_handlers import register_exit_handlers
|
|
33
36
|
from flwr.common.logger import log, warn_deprecated_feature
|
|
34
37
|
from flwr.common.object_ref import load_app, validate
|
|
@@ -44,11 +47,23 @@ def run_supernode() -> None:
|
|
|
44
47
|
|
|
45
48
|
event(EventType.RUN_SUPERNODE_ENTER)
|
|
46
49
|
|
|
47
|
-
|
|
50
|
+
args = _parse_args_run_supernode().parse_args()
|
|
48
51
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
_warn_deprecated_server_arg(args)
|
|
53
|
+
|
|
54
|
+
root_certificates = _get_certificates(args)
|
|
55
|
+
load_fn = _get_load_client_app_fn(args, multi_app=True)
|
|
56
|
+
authentication_keys = _try_setup_client_authentication(args)
|
|
57
|
+
|
|
58
|
+
_start_client_internal(
|
|
59
|
+
server_address=args.server,
|
|
60
|
+
load_client_app_fn=load_fn,
|
|
61
|
+
transport="rest" if args.rest else "grpc-rere",
|
|
62
|
+
root_certificates=root_certificates,
|
|
63
|
+
insecure=args.insecure,
|
|
64
|
+
authentication_keys=authentication_keys,
|
|
65
|
+
max_retries=args.max_retries,
|
|
66
|
+
max_wait_time=args.max_wait_time,
|
|
52
67
|
)
|
|
53
68
|
|
|
54
69
|
# Graceful shutdown
|
|
@@ -65,6 +80,27 @@ def run_client_app() -> None:
|
|
|
65
80
|
|
|
66
81
|
args = _parse_args_run_client_app().parse_args()
|
|
67
82
|
|
|
83
|
+
_warn_deprecated_server_arg(args)
|
|
84
|
+
|
|
85
|
+
root_certificates = _get_certificates(args)
|
|
86
|
+
load_fn = _get_load_client_app_fn(args, multi_app=False)
|
|
87
|
+
authentication_keys = _try_setup_client_authentication(args)
|
|
88
|
+
|
|
89
|
+
_start_client_internal(
|
|
90
|
+
server_address=args.superlink,
|
|
91
|
+
load_client_app_fn=load_fn,
|
|
92
|
+
transport="rest" if args.rest else "grpc-rere",
|
|
93
|
+
root_certificates=root_certificates,
|
|
94
|
+
insecure=args.insecure,
|
|
95
|
+
authentication_keys=authentication_keys,
|
|
96
|
+
max_retries=args.max_retries,
|
|
97
|
+
max_wait_time=args.max_wait_time,
|
|
98
|
+
)
|
|
99
|
+
register_exit_handlers(event_type=EventType.RUN_CLIENT_APP_LEAVE)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _warn_deprecated_server_arg(args: argparse.Namespace) -> None:
|
|
103
|
+
"""Warn about the deprecated argument `--server`."""
|
|
68
104
|
if args.server != ADDRESS_FLEET_API_GRPC_RERE:
|
|
69
105
|
warn = "Passing flag --server is deprecated. Use --superlink instead."
|
|
70
106
|
warn_deprecated_feature(warn)
|
|
@@ -82,27 +118,6 @@ def run_client_app() -> None:
|
|
|
82
118
|
else:
|
|
83
119
|
args.superlink = args.server
|
|
84
120
|
|
|
85
|
-
root_certificates = _get_certificates(args)
|
|
86
|
-
log(
|
|
87
|
-
DEBUG,
|
|
88
|
-
"Flower will load ClientApp `%s`",
|
|
89
|
-
getattr(args, "client-app"),
|
|
90
|
-
)
|
|
91
|
-
load_fn = _get_load_client_app_fn(args)
|
|
92
|
-
authentication_keys = _try_setup_client_authentication(args)
|
|
93
|
-
|
|
94
|
-
_start_client_internal(
|
|
95
|
-
server_address=args.superlink,
|
|
96
|
-
load_client_app_fn=load_fn,
|
|
97
|
-
transport="rest" if args.rest else "grpc-rere",
|
|
98
|
-
root_certificates=root_certificates,
|
|
99
|
-
insecure=args.insecure,
|
|
100
|
-
authentication_keys=authentication_keys,
|
|
101
|
-
max_retries=args.max_retries,
|
|
102
|
-
max_wait_time=args.max_wait_time,
|
|
103
|
-
)
|
|
104
|
-
register_exit_handlers(event_type=EventType.RUN_CLIENT_APP_LEAVE)
|
|
105
|
-
|
|
106
121
|
|
|
107
122
|
def _get_certificates(args: argparse.Namespace) -> Optional[bytes]:
|
|
108
123
|
"""Load certificates if specified in args."""
|
|
@@ -140,24 +155,112 @@ def _get_certificates(args: argparse.Namespace) -> Optional[bytes]:
|
|
|
140
155
|
|
|
141
156
|
|
|
142
157
|
def _get_load_client_app_fn(
|
|
143
|
-
args: argparse.Namespace,
|
|
144
|
-
) -> Callable[[], ClientApp]:
|
|
145
|
-
"""Get the load_client_app_fn function.
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
158
|
+
args: argparse.Namespace, multi_app: bool
|
|
159
|
+
) -> Callable[[str, str], ClientApp]:
|
|
160
|
+
"""Get the load_client_app_fn function.
|
|
161
|
+
|
|
162
|
+
If `multi_app` is True, this function loads the specified ClientApp
|
|
163
|
+
based on `fab_id` and `fab_version`. If `fab_id` is empty, a default
|
|
164
|
+
ClientApp will be loaded.
|
|
165
|
+
|
|
166
|
+
If `multi_app` is False, it ignores `fab_id` and `fab_version` and
|
|
167
|
+
loads a default ClientApp.
|
|
168
|
+
"""
|
|
169
|
+
# Find the Flower directory containing Flower Apps (only for multi-app)
|
|
170
|
+
flwr_dir = Path("")
|
|
171
|
+
if "flwr_dir" in args:
|
|
172
|
+
if args.flwr_dir is None:
|
|
173
|
+
flwr_dir = get_flwr_dir()
|
|
174
|
+
else:
|
|
175
|
+
flwr_dir = Path(args.flwr_dir)
|
|
176
|
+
|
|
177
|
+
sys.path.insert(0, str(flwr_dir))
|
|
149
178
|
|
|
150
|
-
|
|
151
|
-
valid, error_msg = validate(app_ref)
|
|
152
|
-
if not valid and error_msg:
|
|
153
|
-
raise LoadClientAppError(error_msg) from None
|
|
179
|
+
default_app_ref: str = getattr(args, "client-app")
|
|
154
180
|
|
|
155
|
-
|
|
156
|
-
|
|
181
|
+
if not multi_app:
|
|
182
|
+
log(
|
|
183
|
+
DEBUG,
|
|
184
|
+
"Flower SuperNode will load and validate ClientApp `%s`",
|
|
185
|
+
getattr(args, "client-app"),
|
|
186
|
+
)
|
|
187
|
+
valid, error_msg = validate(default_app_ref)
|
|
188
|
+
if not valid and error_msg:
|
|
189
|
+
raise LoadClientAppError(error_msg) from None
|
|
190
|
+
|
|
191
|
+
def _load(fab_id: str, fab_version: str) -> ClientApp:
|
|
192
|
+
# If multi-app feature is disabled
|
|
193
|
+
if not multi_app:
|
|
194
|
+
# Set sys.path
|
|
195
|
+
sys.path[0] = args.dir
|
|
196
|
+
|
|
197
|
+
# Set app reference
|
|
198
|
+
client_app_ref = default_app_ref
|
|
199
|
+
# If multi-app feature is enabled but the fab id is not specified
|
|
200
|
+
elif fab_id == "":
|
|
201
|
+
if default_app_ref == "":
|
|
202
|
+
raise LoadClientAppError(
|
|
203
|
+
"Invalid FAB ID: The FAB ID is empty.",
|
|
204
|
+
) from None
|
|
205
|
+
|
|
206
|
+
log(WARN, "FAB ID is not provided; the default ClientApp will be loaded.")
|
|
207
|
+
# Set sys.path
|
|
208
|
+
sys.path[0] = args.dir
|
|
209
|
+
|
|
210
|
+
# Set app reference
|
|
211
|
+
client_app_ref = default_app_ref
|
|
212
|
+
# If multi-app feature is enabled
|
|
213
|
+
else:
|
|
214
|
+
# Check the fab_id
|
|
215
|
+
if fab_id.count("/") != 1:
|
|
216
|
+
raise LoadClientAppError(
|
|
217
|
+
f"Invalid FAB ID: {fab_id}",
|
|
218
|
+
) from None
|
|
219
|
+
username, project_name = fab_id.split("/")
|
|
220
|
+
|
|
221
|
+
# Locate the directory
|
|
222
|
+
project_dir = flwr_dir / "apps" / username / project_name / fab_version
|
|
223
|
+
|
|
224
|
+
# Check if the directory exists
|
|
225
|
+
if not project_dir.exists():
|
|
226
|
+
raise LoadClientAppError(
|
|
227
|
+
f"Invalid Flower App directory: {project_dir}",
|
|
228
|
+
) from None
|
|
229
|
+
|
|
230
|
+
# Load pyproject.toml file
|
|
231
|
+
toml_path = project_dir / "pyproject.toml"
|
|
232
|
+
if not toml_path.is_file():
|
|
233
|
+
raise LoadClientAppError(
|
|
234
|
+
f"Cannot find pyproject.toml in {project_dir}",
|
|
235
|
+
) from None
|
|
236
|
+
with open(toml_path, encoding="utf-8") as toml_file:
|
|
237
|
+
config = tomli.loads(toml_file.read())
|
|
238
|
+
|
|
239
|
+
# Validate pyproject.toml fields
|
|
240
|
+
is_valid, errors, _ = validate_fields(config)
|
|
241
|
+
if not is_valid:
|
|
242
|
+
error_msg = "\n".join([f" - {error}" for error in errors])
|
|
243
|
+
raise LoadClientAppError(
|
|
244
|
+
f"Invalid pyproject.toml:\n{error_msg}",
|
|
245
|
+
) from None
|
|
246
|
+
|
|
247
|
+
# Set sys.path
|
|
248
|
+
sys.path[0] = str(project_dir)
|
|
249
|
+
|
|
250
|
+
# Set app reference
|
|
251
|
+
client_app_ref = config["flower"]["components"]["clientapp"]
|
|
252
|
+
|
|
253
|
+
# Load ClientApp
|
|
254
|
+
log(
|
|
255
|
+
DEBUG,
|
|
256
|
+
"Loading ClientApp `%s`",
|
|
257
|
+
client_app_ref,
|
|
258
|
+
)
|
|
259
|
+
client_app = load_app(client_app_ref, LoadClientAppError)
|
|
157
260
|
|
|
158
261
|
if not isinstance(client_app, ClientApp):
|
|
159
262
|
raise LoadClientAppError(
|
|
160
|
-
f"Attribute {
|
|
263
|
+
f"Attribute {client_app_ref} is not of type {ClientApp}",
|
|
161
264
|
) from None
|
|
162
265
|
|
|
163
266
|
return client_app
|
flwr/common/config.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
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
|
+
"""Provide functions for managing global Flower config."""
|
|
16
|
+
|
|
17
|
+
import os
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def get_flwr_dir() -> Path:
|
|
22
|
+
"""Return the Flower home directory based on env variables."""
|
|
23
|
+
return Path(
|
|
24
|
+
os.getenv(
|
|
25
|
+
"FLWR_HOME",
|
|
26
|
+
f"{os.getenv('XDG_DATA_HOME', os.getenv('HOME'))}/.flwr",
|
|
27
|
+
)
|
|
28
|
+
)
|
flwr/common/constant.py
CHANGED
flwr/common/telemetry.py
CHANGED
|
@@ -164,6 +164,10 @@ class EventType(str, Enum):
|
|
|
164
164
|
RUN_SUPERNODE_ENTER = auto()
|
|
165
165
|
RUN_SUPERNODE_LEAVE = auto()
|
|
166
166
|
|
|
167
|
+
# SuperExec
|
|
168
|
+
RUN_SUPEREXEC_ENTER = auto()
|
|
169
|
+
RUN_SUPEREXEC_LEAVE = auto()
|
|
170
|
+
|
|
167
171
|
|
|
168
172
|
# Use the ThreadPoolExecutor with max_workers=1 to have a queue
|
|
169
173
|
# and also ensure that telemetry calls are not blocking.
|
flwr/proto/exec_pb2.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# source: flwr/proto/exec.proto
|
|
4
|
+
# Protobuf Python Version: 4.25.0
|
|
5
|
+
"""Generated protocol buffer code."""
|
|
6
|
+
from google.protobuf import descriptor as _descriptor
|
|
7
|
+
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
8
|
+
from google.protobuf import symbol_database as _symbol_database
|
|
9
|
+
from google.protobuf.internal import builder as _builder
|
|
10
|
+
# @@protoc_insertion_point(imports)
|
|
11
|
+
|
|
12
|
+
_sym_db = _symbol_database.Default()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x66lwr/proto/exec.proto\x12\nflwr.proto\"#\n\x0fStartRunRequest\x12\x10\n\x08\x66\x61\x62_file\x18\x01 \x01(\x0c\"\"\n\x10StartRunResponse\x12\x0e\n\x06run_id\x18\x01 \x01(\x12\x32O\n\x04\x45xec\x12G\n\x08StartRun\x12\x1b.flwr.proto.StartRunRequest\x1a\x1c.flwr.proto.StartRunResponse\"\x00\x62\x06proto3')
|
|
18
|
+
|
|
19
|
+
_globals = globals()
|
|
20
|
+
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
21
|
+
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'flwr.proto.exec_pb2', _globals)
|
|
22
|
+
if _descriptor._USE_C_DESCRIPTORS == False:
|
|
23
|
+
DESCRIPTOR._options = None
|
|
24
|
+
_globals['_STARTRUNREQUEST']._serialized_start=37
|
|
25
|
+
_globals['_STARTRUNREQUEST']._serialized_end=72
|
|
26
|
+
_globals['_STARTRUNRESPONSE']._serialized_start=74
|
|
27
|
+
_globals['_STARTRUNRESPONSE']._serialized_end=108
|
|
28
|
+
_globals['_EXEC']._serialized_start=110
|
|
29
|
+
_globals['_EXEC']._serialized_end=189
|
|
30
|
+
# @@protoc_insertion_point(module_scope)
|
flwr/proto/exec_pb2.pyi
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""
|
|
2
|
+
@generated by mypy-protobuf. Do not edit manually!
|
|
3
|
+
isort:skip_file
|
|
4
|
+
"""
|
|
5
|
+
import builtins
|
|
6
|
+
import google.protobuf.descriptor
|
|
7
|
+
import google.protobuf.message
|
|
8
|
+
import typing_extensions
|
|
9
|
+
|
|
10
|
+
DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
|
|
11
|
+
|
|
12
|
+
class StartRunRequest(google.protobuf.message.Message):
|
|
13
|
+
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
14
|
+
FAB_FILE_FIELD_NUMBER: builtins.int
|
|
15
|
+
fab_file: builtins.bytes
|
|
16
|
+
def __init__(self,
|
|
17
|
+
*,
|
|
18
|
+
fab_file: builtins.bytes = ...,
|
|
19
|
+
) -> None: ...
|
|
20
|
+
def ClearField(self, field_name: typing_extensions.Literal["fab_file",b"fab_file"]) -> None: ...
|
|
21
|
+
global___StartRunRequest = StartRunRequest
|
|
22
|
+
|
|
23
|
+
class StartRunResponse(google.protobuf.message.Message):
|
|
24
|
+
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
25
|
+
RUN_ID_FIELD_NUMBER: builtins.int
|
|
26
|
+
run_id: builtins.int
|
|
27
|
+
def __init__(self,
|
|
28
|
+
*,
|
|
29
|
+
run_id: builtins.int = ...,
|
|
30
|
+
) -> None: ...
|
|
31
|
+
def ClearField(self, field_name: typing_extensions.Literal["run_id",b"run_id"]) -> None: ...
|
|
32
|
+
global___StartRunResponse = StartRunResponse
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
|
|
2
|
+
"""Client and server classes corresponding to protobuf-defined services."""
|
|
3
|
+
import grpc
|
|
4
|
+
|
|
5
|
+
from flwr.proto import exec_pb2 as flwr_dot_proto_dot_exec__pb2
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ExecStub(object):
|
|
9
|
+
"""Missing associated documentation comment in .proto file."""
|
|
10
|
+
|
|
11
|
+
def __init__(self, channel):
|
|
12
|
+
"""Constructor.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
channel: A grpc.Channel.
|
|
16
|
+
"""
|
|
17
|
+
self.StartRun = channel.unary_unary(
|
|
18
|
+
'/flwr.proto.Exec/StartRun',
|
|
19
|
+
request_serializer=flwr_dot_proto_dot_exec__pb2.StartRunRequest.SerializeToString,
|
|
20
|
+
response_deserializer=flwr_dot_proto_dot_exec__pb2.StartRunResponse.FromString,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ExecServicer(object):
|
|
25
|
+
"""Missing associated documentation comment in .proto file."""
|
|
26
|
+
|
|
27
|
+
def StartRun(self, request, context):
|
|
28
|
+
"""Start run upon request
|
|
29
|
+
"""
|
|
30
|
+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
31
|
+
context.set_details('Method not implemented!')
|
|
32
|
+
raise NotImplementedError('Method not implemented!')
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def add_ExecServicer_to_server(servicer, server):
|
|
36
|
+
rpc_method_handlers = {
|
|
37
|
+
'StartRun': grpc.unary_unary_rpc_method_handler(
|
|
38
|
+
servicer.StartRun,
|
|
39
|
+
request_deserializer=flwr_dot_proto_dot_exec__pb2.StartRunRequest.FromString,
|
|
40
|
+
response_serializer=flwr_dot_proto_dot_exec__pb2.StartRunResponse.SerializeToString,
|
|
41
|
+
),
|
|
42
|
+
}
|
|
43
|
+
generic_handler = grpc.method_handlers_generic_handler(
|
|
44
|
+
'flwr.proto.Exec', rpc_method_handlers)
|
|
45
|
+
server.add_generic_rpc_handlers((generic_handler,))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# This class is part of an EXPERIMENTAL API.
|
|
49
|
+
class Exec(object):
|
|
50
|
+
"""Missing associated documentation comment in .proto file."""
|
|
51
|
+
|
|
52
|
+
@staticmethod
|
|
53
|
+
def StartRun(request,
|
|
54
|
+
target,
|
|
55
|
+
options=(),
|
|
56
|
+
channel_credentials=None,
|
|
57
|
+
call_credentials=None,
|
|
58
|
+
insecure=False,
|
|
59
|
+
compression=None,
|
|
60
|
+
wait_for_ready=None,
|
|
61
|
+
timeout=None,
|
|
62
|
+
metadata=None):
|
|
63
|
+
return grpc.experimental.unary_unary(request, target, '/flwr.proto.Exec/StartRun',
|
|
64
|
+
flwr_dot_proto_dot_exec__pb2.StartRunRequest.SerializeToString,
|
|
65
|
+
flwr_dot_proto_dot_exec__pb2.StartRunResponse.FromString,
|
|
66
|
+
options, channel_credentials,
|
|
67
|
+
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""
|
|
2
|
+
@generated by mypy-protobuf. Do not edit manually!
|
|
3
|
+
isort:skip_file
|
|
4
|
+
"""
|
|
5
|
+
import abc
|
|
6
|
+
import flwr.proto.exec_pb2
|
|
7
|
+
import grpc
|
|
8
|
+
|
|
9
|
+
class ExecStub:
|
|
10
|
+
def __init__(self, channel: grpc.Channel) -> None: ...
|
|
11
|
+
StartRun: grpc.UnaryUnaryMultiCallable[
|
|
12
|
+
flwr.proto.exec_pb2.StartRunRequest,
|
|
13
|
+
flwr.proto.exec_pb2.StartRunResponse]
|
|
14
|
+
"""Start run upon request"""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ExecServicer(metaclass=abc.ABCMeta):
|
|
18
|
+
@abc.abstractmethod
|
|
19
|
+
def StartRun(self,
|
|
20
|
+
request: flwr.proto.exec_pb2.StartRunRequest,
|
|
21
|
+
context: grpc.ServicerContext,
|
|
22
|
+
) -> flwr.proto.exec_pb2.StartRunResponse:
|
|
23
|
+
"""Start run upon request"""
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def add_ExecServicer_to_server(servicer: ExecServicer, server: grpc.Server) -> None: ...
|
flwr/server/app.py
CHANGED
|
@@ -200,15 +200,7 @@ def run_superlink() -> None:
|
|
|
200
200
|
args = _parse_args_run_superlink().parse_args()
|
|
201
201
|
|
|
202
202
|
# Parse IP address
|
|
203
|
-
|
|
204
|
-
if not parsed_driver_address:
|
|
205
|
-
sys.exit(f"Driver IP address ({args.driver_api_address}) cannot be parsed.")
|
|
206
|
-
driver_host, driver_port, driver_is_v6 = parsed_driver_address
|
|
207
|
-
driver_address = (
|
|
208
|
-
f"[{driver_host}]:{driver_port}"
|
|
209
|
-
if driver_is_v6
|
|
210
|
-
else f"{driver_host}:{driver_port}"
|
|
211
|
-
)
|
|
203
|
+
driver_address, _, _ = _format_address(args.driver_api_address)
|
|
212
204
|
|
|
213
205
|
# Obtain certificates
|
|
214
206
|
certificates = _try_obtain_certificates(args)
|
|
@@ -231,13 +223,8 @@ def run_superlink() -> None:
|
|
|
231
223
|
if args.fleet_api_type == TRANSPORT_TYPE_GRPC_RERE
|
|
232
224
|
else ADDRESS_FLEET_API_REST
|
|
233
225
|
)
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
sys.exit(f"Fleet IP address ({args.fleet_api_address}) cannot be parsed.")
|
|
237
|
-
fleet_host, fleet_port, fleet_is_v6 = parsed_fleet_address
|
|
238
|
-
fleet_address = (
|
|
239
|
-
f"[{fleet_host}]:{fleet_port}" if fleet_is_v6 else f"{fleet_host}:{fleet_port}"
|
|
240
|
-
)
|
|
226
|
+
|
|
227
|
+
fleet_address, host, port = _format_address(args.fleet_api_address)
|
|
241
228
|
|
|
242
229
|
num_workers = args.fleet_api_num_workers
|
|
243
230
|
if num_workers != 1:
|
|
@@ -267,8 +254,8 @@ def run_superlink() -> None:
|
|
|
267
254
|
fleet_thread = threading.Thread(
|
|
268
255
|
target=_run_fleet_api_rest,
|
|
269
256
|
args=(
|
|
270
|
-
|
|
271
|
-
|
|
257
|
+
host,
|
|
258
|
+
port,
|
|
272
259
|
ssl_keyfile,
|
|
273
260
|
ssl_certfile,
|
|
274
261
|
state_factory,
|
|
@@ -325,6 +312,16 @@ def run_superlink() -> None:
|
|
|
325
312
|
driver_server.wait_for_termination(timeout=1)
|
|
326
313
|
|
|
327
314
|
|
|
315
|
+
def _format_address(address: str) -> Tuple[str, str, int]:
|
|
316
|
+
parsed_address = parse_address(address)
|
|
317
|
+
if not parsed_address:
|
|
318
|
+
sys.exit(
|
|
319
|
+
f"Address ({address}) cannot be parsed (expected: URL or IPv4 or IPv6)."
|
|
320
|
+
)
|
|
321
|
+
host, port, is_v6 = parsed_address
|
|
322
|
+
return (f"[{host}]:{port}" if is_v6 else f"{host}:{port}", host, port)
|
|
323
|
+
|
|
324
|
+
|
|
328
325
|
def _try_setup_client_authentication(
|
|
329
326
|
args: argparse.Namespace,
|
|
330
327
|
certificates: Optional[Tuple[bytes, bytes, bytes]],
|
|
@@ -0,0 +1,19 @@
|
|
|
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
|
+
"""Fower SuperExec package."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def run_superexec() -> None:
|
|
19
|
+
"""Empty stub."""
|
{flwr_nightly-1.10.0.dev20240612.dist-info → flwr_nightly-1.10.0.dev20240613.dist-info}/RECORD
RENAMED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
flwr/__init__.py,sha256=VmBWedrCxqmt4QvUHBLqyVEH6p7zaFMD_oCHerXHSVw,937
|
|
2
2
|
flwr/cli/__init__.py,sha256=cZJVgozlkC6Ni2Hd_FAIrqefrkCGOV18fikToq-6iLw,720
|
|
3
3
|
flwr/cli/app.py,sha256=FvzW2iw3iagrTmgsk4zBLjLvGANRsBvIw11dWWs5-6g,1193
|
|
4
|
-
flwr/cli/build.py,sha256=
|
|
5
|
-
flwr/cli/config_utils.py,sha256=
|
|
4
|
+
flwr/cli/build.py,sha256=oiFnigccMkI9jjracsslvDJe-w1tomTr579SQd66_-Y,4741
|
|
5
|
+
flwr/cli/config_utils.py,sha256=BRYDMVOyYuOL8Ac10bjVA_RXMxSQ2KUKMcTFBaLHZlk,6017
|
|
6
6
|
flwr/cli/example.py,sha256=1bGDYll3BXQY2kRqSN-oICqS5n1b9m0g0RvXTopXHl4,2215
|
|
7
|
-
flwr/cli/install.py,sha256=
|
|
7
|
+
flwr/cli/install.py,sha256=Wz7Hqg2PE9N-w5CnqlH9Zr8mzADN2J7NLcUhgldZLWU,6579
|
|
8
8
|
flwr/cli/new/__init__.py,sha256=cQzK1WH4JP2awef1t2UQ2xjl1agVEz9rwutV18SWV1k,789
|
|
9
9
|
flwr/cli/new/new.py,sha256=7BWziuEOE15MXX4xNLH-w0-x0ytOEfYn_AUrbaDp13Y,6223
|
|
10
10
|
flwr/cli/new/templates/__init__.py,sha256=4luU8RL-CK8JJCstQ_ON809W9bNTkY1l9zSaPKBkgwY,725
|
|
@@ -43,7 +43,7 @@ flwr/cli/run/__init__.py,sha256=oCd6HmQDx-sqver1gecgx-uMA38BLTSiiKpl7RGNceg,789
|
|
|
43
43
|
flwr/cli/run/run.py,sha256=Oadt7JsJX549JG-2P1UPdF11vnblLWS8uGvuVx0modA,2687
|
|
44
44
|
flwr/cli/utils.py,sha256=l65Ul0YsSBPuypk0uorAtEDmLEYiUrzpCXi6zCg9mJ4,4506
|
|
45
45
|
flwr/client/__init__.py,sha256=tcgMyAW8brnmAIk4NmXkonVjYV3lafQJD4vfZ3OJ6kA,1279
|
|
46
|
-
flwr/client/app.py,sha256=
|
|
46
|
+
flwr/client/app.py,sha256=GhL-eR_Y2H8e2XhUnq5AUGQWQya51b5iVr4I6RDW7Hc,24189
|
|
47
47
|
flwr/client/client.py,sha256=Vp9UkOkoHdNfn6iMYZsj_5m_GICiFfUlKEVaLad-YhM,8183
|
|
48
48
|
flwr/client/client_app.py,sha256=2jyVTzu8pwDtg66z4FjAa_kPzg31Q8-hx-RkDhguIqw,8635
|
|
49
49
|
flwr/client/dpfedavg_numpy_client.py,sha256=9Tnig4iml2J88HBKNahegjXjbfvIQyBtaIQaqjbeqsA,7435
|
|
@@ -70,11 +70,12 @@ flwr/client/numpy_client.py,sha256=u76GWAdHmJM88Agm2EgLQSvO8Jnk225mJTk-_TmPjFE,1
|
|
|
70
70
|
flwr/client/rest_client/__init__.py,sha256=ThwOnkMdzxo_UuyTI47Q7y9oSpuTgNT2OuFvJCfuDiw,735
|
|
71
71
|
flwr/client/rest_client/connection.py,sha256=GvDPX2BdPwhBQGH6LQE50AzUxQvC7bisWK1pk_OR7eE,11567
|
|
72
72
|
flwr/client/supernode/__init__.py,sha256=SUhWOzcgXRNXk1V9UgB5-FaWukqqrOEajVUHEcPkwyQ,865
|
|
73
|
-
flwr/client/supernode/app.py,sha256=
|
|
73
|
+
flwr/client/supernode/app.py,sha256=sxRSLPkuKrihEjYMTPg9H1WbHtVl_izcg2se2fXvKR4,14701
|
|
74
74
|
flwr/client/typing.py,sha256=c9EvjlEjasxn1Wqx6bGl6Xg6vM1gMFfmXht-E2i5J-k,1006
|
|
75
75
|
flwr/common/__init__.py,sha256=dHOptgKxna78CEQLD5Yu0QIsoSgpIIw5AhIUZCHDWAU,3721
|
|
76
76
|
flwr/common/address.py,sha256=iTAN9jtmIGMrWFnx9XZQl45ZEtQJVZZLYPRBSNVARGI,1882
|
|
77
|
-
flwr/common/
|
|
77
|
+
flwr/common/config.py,sha256=WEPvVsGPrJKVppNlEGK23p33vKbOuMqSvi8DVtyW_dU,1022
|
|
78
|
+
flwr/common/constant.py,sha256=k3QmlqQL4rfgjsO4xqTJ1QiGBHyJ5rTRE01FDEiNFCQ,2468
|
|
78
79
|
flwr/common/context.py,sha256=ounF-mWPPtXGwtae3sg5EhF58ScviOa3MVqxRpGVu-8,1313
|
|
79
80
|
flwr/common/date.py,sha256=UWhBZj49yX9LD4BmatS_ZFZu_-kweGh0KQJ1djyWWH4,891
|
|
80
81
|
flwr/common/differential_privacy.py,sha256=WZWrL7C9XaB9l9NDkLDI5PvM7jwcoTTFu08ZVG8-M5Q,6113
|
|
@@ -105,7 +106,7 @@ flwr/common/secure_aggregation/quantization.py,sha256=appui7GGrkRPsupF59TkapeV4N
|
|
|
105
106
|
flwr/common/secure_aggregation/secaggplus_constants.py,sha256=Fh7-n6pgL4TUnHpNYXo8iW-n5cOGQgQa-c7RcU80tqQ,2183
|
|
106
107
|
flwr/common/secure_aggregation/secaggplus_utils.py,sha256=87bNZX6CmQekj935R4u3m5hsaEkkfKtGSA-VG2c-O9w,3221
|
|
107
108
|
flwr/common/serde.py,sha256=Yn83kbSf9vJndTa5ldL4DR_bL_wy_bD4lTlD3ZbB658,22250
|
|
108
|
-
flwr/common/telemetry.py,sha256=
|
|
109
|
+
flwr/common/telemetry.py,sha256=IGzOp87BYReCj5bEoZS6zDSKH0aXsmhMvhHx8fdC1v0,7948
|
|
109
110
|
flwr/common/typing.py,sha256=3Wu6Ol1Ja6Gb0WdlcXVEn1EHYJbc4oRRJA81vEegxBo,4382
|
|
110
111
|
flwr/common/version.py,sha256=_RDSMGZPEuGKYViZuXPotDtXMvh4iyDH9XOCO4qtPO8,666
|
|
111
112
|
flwr/proto/__init__.py,sha256=hbY7JYakwZwCkYgCNlmHdc8rtvfoJbAZLalMdc--CGc,683
|
|
@@ -117,6 +118,10 @@ flwr/proto/error_pb2.py,sha256=LarjKL90LbwkXKlhzNrDssgl4DXcvIPve8NVCXHpsKA,1084
|
|
|
117
118
|
flwr/proto/error_pb2.pyi,sha256=ZNH4HhJTU_KfMXlyCeg8FwU-fcUYxTqEmoJPtWtHikc,734
|
|
118
119
|
flwr/proto/error_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159
|
|
119
120
|
flwr/proto/error_pb2_grpc.pyi,sha256=ff2TSiLVnG6IVQcTGzb2DIH3XRSoAvAo_RMcvbMFyc0,76
|
|
121
|
+
flwr/proto/exec_pb2.py,sha256=fwuyyK6WEwx-RngMZm6z5dqDeYcMKKxBBhwh9MJfvMA,1450
|
|
122
|
+
flwr/proto/exec_pb2.pyi,sha256=IMUD1GlhK0mizORQ-lkiU-JbpAefmo9GLpDNfAib2GM,1068
|
|
123
|
+
flwr/proto/exec_pb2_grpc.py,sha256=7yyi_J1Sri8LXzj5_MjhI7_hoxUMcjIAQcaE-Ghnvco,2480
|
|
124
|
+
flwr/proto/exec_pb2_grpc.pyi,sha256=w721aoQ_ggktlIbR973bFRhwbIlEhfNMjOH4hnwgQE0,743
|
|
120
125
|
flwr/proto/fleet_pb2.py,sha256=sASthuSb5R0v1NH2g6vRDC4BeJUFPAso8bjcihAflo0,4543
|
|
121
126
|
flwr/proto/fleet_pb2.pyi,sha256=45kQ9YINv3VG0nxWSjCN4SppdepjKW8rRBlxKxz7ud4,7571
|
|
122
127
|
flwr/proto/fleet_pb2_grpc.py,sha256=4eP1jkEVuckzSxo16yNaOBrHPzxHqQfj2MaNtPql4Wk,10655
|
|
@@ -147,7 +152,7 @@ flwr/proto/transport_pb2_grpc.py,sha256=vLN3EHtx2aEEMCO4f1Upu-l27BPzd3-5pV-u8wPc
|
|
|
147
152
|
flwr/proto/transport_pb2_grpc.pyi,sha256=AGXf8RiIiW2J5IKMlm_3qT3AzcDa4F3P5IqUjve_esA,766
|
|
148
153
|
flwr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
149
154
|
flwr/server/__init__.py,sha256=qS6A16gywlLJSQWIaI41Fy_fWa5NzHi6iyrOfLf2vQc,1575
|
|
150
|
-
flwr/server/app.py,sha256=
|
|
155
|
+
flwr/server/app.py,sha256=a7WA24EYpZBQ7qL6DgOCGvYqgE3oJYu_SpiGBs0jI0g,22249
|
|
151
156
|
flwr/server/client_manager.py,sha256=T8UDSRJBVD3fyIDI7NTAA-NA7GPrMNNgH2OAF54RRxE,6127
|
|
152
157
|
flwr/server/client_proxy.py,sha256=4G-oTwhb45sfWLx2uZdcXD98IZwdTS6F88xe3akCdUg,2399
|
|
153
158
|
flwr/server/compat/__init__.py,sha256=VxnJtJyOjNFQXMNi9hIuzNlZM5n0Hj1p3aq_Pm2udw4,892
|
|
@@ -234,8 +239,9 @@ flwr/simulation/ray_transport/ray_actor.py,sha256=_wv2eP7qxkCZ-6rMyYWnjLrGPBZRxj
|
|
|
234
239
|
flwr/simulation/ray_transport/ray_client_proxy.py,sha256=oDu4sEPIOu39vrNi-fqDAe10xtNUXMO49bM2RWfRcyw,6738
|
|
235
240
|
flwr/simulation/ray_transport/utils.py,sha256=TYdtfg1P9VfTdLMOJlifInGpxWHYs9UfUqIv2wfkRLA,2392
|
|
236
241
|
flwr/simulation/run_simulation.py,sha256=Jmc6DyN5UCY1U1PcDvL04NgYmEQ6ufJ1JisjG5yqfY8,15098
|
|
237
|
-
|
|
238
|
-
flwr_nightly-1.10.0.
|
|
239
|
-
flwr_nightly-1.10.0.
|
|
240
|
-
flwr_nightly-1.10.0.
|
|
241
|
-
flwr_nightly-1.10.0.
|
|
242
|
+
flwr/superexec/__init__.py,sha256=aoByPmwiFT1okczzloEZ3zH7BBvKlA_s1TL_mqQW2jQ,767
|
|
243
|
+
flwr_nightly-1.10.0.dev20240613.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
|
244
|
+
flwr_nightly-1.10.0.dev20240613.dist-info/METADATA,sha256=B1svyAE6DUOY-lBKqD6LW1qTf3jo9L8nTH7ZOJbsKmo,15518
|
|
245
|
+
flwr_nightly-1.10.0.dev20240613.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
|
246
|
+
flwr_nightly-1.10.0.dev20240613.dist-info/entry_points.txt,sha256=7qBQcA-bDGDxnJmLd9FYqglFQubjCNqyg9M8a-lukps,336
|
|
247
|
+
flwr_nightly-1.10.0.dev20240613.dist-info/RECORD,,
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
flower-client-app=flwr.client:run_client_app
|
|
3
3
|
flower-server-app=flwr.server:run_server_app
|
|
4
4
|
flower-simulation=flwr.simulation.run_simulation:run_simulation_from_cli
|
|
5
|
+
flower-superexec=flwr.superexec:run_superexec
|
|
5
6
|
flower-superlink=flwr.server:run_superlink
|
|
6
7
|
flower-supernode=flwr.client:run_supernode
|
|
7
8
|
flwr=flwr.cli.app:app
|
{flwr_nightly-1.10.0.dev20240612.dist-info → flwr_nightly-1.10.0.dev20240613.dist-info}/LICENSE
RENAMED
|
File without changes
|
{flwr_nightly-1.10.0.dev20240612.dist-info → flwr_nightly-1.10.0.dev20240613.dist-info}/WHEEL
RENAMED
|
File without changes
|