flwr-nightly 1.10.0.dev20240714__py3-none-any.whl → 1.10.0.dev20240715__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 CHANGED
@@ -85,7 +85,7 @@ def build(
85
85
 
86
86
  # Set the name of the zip file
87
87
  fab_filename = (
88
- f"{conf['flower']['publisher']}"
88
+ f"{conf['tool']['flwr']['publisher']}"
89
89
  f".{directory.name}"
90
90
  f".{conf['project']['version'].replace('.', '-')}.fab"
91
91
  )
flwr/cli/config_utils.py CHANGED
@@ -60,7 +60,7 @@ def get_fab_metadata(fab_file: Union[Path, bytes]) -> Tuple[str, str]:
60
60
 
61
61
  return (
62
62
  conf["project"]["version"],
63
- f"{conf['flower']['publisher']}/{conf['project']['name']}",
63
+ f"{conf['tool']['flwr']['publisher']}/{conf['project']['name']}",
64
64
  )
65
65
 
66
66
 
@@ -136,20 +136,20 @@ def validate_fields(config: Dict[str, Any]) -> Tuple[bool, List[str], List[str]]
136
136
  if "authors" not in config["project"]:
137
137
  warnings.append('Recommended property "authors" missing in [project]')
138
138
 
139
- if "flower" not in config:
140
- errors.append("Missing [flower] section")
139
+ if "tool" not in config or "flwr" not in config["tool"]:
140
+ errors.append("Missing [tool.flwr] section")
141
141
  else:
142
- if "publisher" not in config["flower"]:
143
- errors.append('Property "publisher" missing in [flower]')
144
- if "config" in config["flower"]:
145
- _validate_run_config(config["flower"]["config"], errors)
146
- if "components" not in config["flower"]:
147
- errors.append("Missing [flower.components] section")
142
+ if "publisher" not in config["tool"]["flwr"]:
143
+ errors.append('Property "publisher" missing in [tool.flwr]')
144
+ if "config" in config["tool"]["flwr"]:
145
+ _validate_run_config(config["tool"]["flwr"]["config"], errors)
146
+ if "components" not in config["tool"]["flwr"]:
147
+ errors.append("Missing [tool.flwr.components] section")
148
148
  else:
149
- if "serverapp" not in config["flower"]["components"]:
150
- errors.append('Property "serverapp" missing in [flower.components]')
151
- if "clientapp" not in config["flower"]["components"]:
152
- errors.append('Property "clientapp" missing in [flower.components]')
149
+ if "serverapp" not in config["tool"]["flwr"]["components"]:
150
+ errors.append('Property "serverapp" missing in [tool.flwr.components]')
151
+ if "clientapp" not in config["tool"]["flwr"]["components"]:
152
+ errors.append('Property "clientapp" missing in [tool.flwr.components]')
153
153
 
154
154
  return len(errors) == 0, errors, warnings
155
155
 
@@ -165,14 +165,14 @@ def validate(
165
165
 
166
166
  # Validate serverapp
167
167
  is_valid, reason = object_ref.validate(
168
- config["flower"]["components"]["serverapp"], check_module
168
+ config["tool"]["flwr"]["components"]["serverapp"], check_module
169
169
  )
170
170
  if not is_valid and isinstance(reason, str):
171
171
  return False, [reason], []
172
172
 
173
173
  # Validate clientapp
174
174
  is_valid, reason = object_ref.validate(
175
- config["flower"]["components"]["clientapp"], check_module
175
+ config["tool"]["flwr"]["components"]["clientapp"], check_module
176
176
  )
177
177
 
178
178
  if not is_valid and isinstance(reason, str):
flwr/cli/install.py CHANGED
@@ -149,7 +149,7 @@ def validate_and_install(
149
149
  )
150
150
  raise typer.Exit(code=1)
151
151
 
152
- publisher = config["flower"]["publisher"]
152
+ publisher = config["tool"]["flwr"]["publisher"]
153
153
  project_name = config["project"]["name"]
154
154
  version = config["project"]["version"]
155
155
 
@@ -6,9 +6,6 @@ build-backend = "hatchling.build"
6
6
  name = "$package_name"
7
7
  version = "1.0.0"
8
8
  description = ""
9
- authors = [
10
- { name = "The Flower Authors", email = "hello@flower.ai" },
11
- ]
12
9
  license = { text = "Apache License (2.0)" }
13
10
  dependencies = [
14
11
  "flwr[simulation]>=1.9.0,<2.0",
@@ -25,18 +22,15 @@ dependencies = [
25
22
  [tool.hatch.build.targets.wheel]
26
23
  packages = ["."]
27
24
 
28
- [flower]
25
+ [tool.flwr]
29
26
  publisher = "$username"
30
27
 
31
- [flower.components]
28
+ [tool.flwr.components]
32
29
  serverapp = "$import_name.app:server"
33
30
  clientapp = "$import_name.app:client"
34
31
 
35
- [flower.engine]
36
- name = "simulation"
37
-
38
- [flower.engine.simulation.supernode]
39
- num = $num_clients
32
+ [tool.flwr.federations]
33
+ default = "localhost"
40
34
 
41
- [flower.engine.simulation]
42
- backend_config = { client_resources = { num_cpus = 8, num_gpus = 1.0 } }
35
+ [tool.flwr.federations.localhost]
36
+ options.num-supernodes = 10
@@ -6,9 +6,6 @@ build-backend = "hatchling.build"
6
6
  name = "$package_name"
7
7
  version = "1.0.0"
8
8
  description = ""
9
- authors = [
10
- { name = "The Flower Authors", email = "hello@flower.ai" },
11
- ]
12
9
  license = { text = "Apache License (2.0)" }
13
10
  dependencies = [
14
11
  "flwr[simulation]>=1.9.0,<2.0",
@@ -23,15 +20,15 @@ dependencies = [
23
20
  [tool.hatch.build.targets.wheel]
24
21
  packages = ["."]
25
22
 
26
- [flower]
23
+ [tool.flwr]
27
24
  publisher = "$username"
28
25
 
29
- [flower.components]
26
+ [tool.flwr.components]
30
27
  serverapp = "$import_name.server:app"
31
28
  clientapp = "$import_name.client:app"
32
29
 
33
- [flower.engine]
34
- name = "simulation"
30
+ [tool.flwr.federations]
31
+ default = "localhost"
35
32
 
36
- [flower.engine.simulation.supernode]
37
- num = 2
33
+ [tool.flwr.federations.localhost]
34
+ options.num-supernodes = 10
@@ -6,9 +6,6 @@ build-backend = "hatchling.build"
6
6
  name = "$package_name"
7
7
  version = "1.0.0"
8
8
  description = ""
9
- authors = [
10
- { name = "The Flower Authors", email = "hello@flower.ai" },
11
- ]
12
9
  license = {text = "Apache License (2.0)"}
13
10
  dependencies = [
14
11
  "flwr[simulation]>=1.9.0,<2.0",
@@ -20,9 +17,15 @@ dependencies = [
20
17
  [tool.hatch.build.targets.wheel]
21
18
  packages = ["."]
22
19
 
23
- [flower]
20
+ [tool.flwr]
24
21
  publisher = "$username"
25
22
 
26
- [flower.components]
23
+ [tool.flwr.components]
27
24
  serverapp = "$import_name.server:app"
28
25
  clientapp = "$import_name.client:app"
26
+
27
+ [tool.flwr.federations]
28
+ default = "localhost"
29
+
30
+ [tool.flwr.federations.localhost]
31
+ options.num-supernodes = 10
@@ -6,9 +6,6 @@ build-backend = "hatchling.build"
6
6
  name = "$package_name"
7
7
  version = "1.0.0"
8
8
  description = ""
9
- authors = [
10
- { name = "The Flower Authors", email = "hello@flower.ai" },
11
- ]
12
9
  license = { text = "Apache License (2.0)" }
13
10
  dependencies = [
14
11
  "flwr[simulation]>=1.9.0,<2.0",
@@ -20,15 +17,15 @@ dependencies = [
20
17
  [tool.hatch.build.targets.wheel]
21
18
  packages = ["."]
22
19
 
23
- [flower]
20
+ [tool.flwr]
24
21
  publisher = "$username"
25
22
 
26
- [flower.components]
23
+ [tool.flwr.components]
27
24
  serverapp = "$import_name.server:app"
28
25
  clientapp = "$import_name.client:app"
29
26
 
30
- [flower.engine]
31
- name = "simulation"
27
+ [tool.flwr.federations]
28
+ default = "localhost"
32
29
 
33
- [flower.engine.simulation.supernode]
34
- num = 2
30
+ [tool.flwr.federations.localhost]
31
+ options.num-supernodes = 10
@@ -6,9 +6,6 @@ build-backend = "hatchling.build"
6
6
  name = "$package_name"
7
7
  version = "1.0.0"
8
8
  description = ""
9
- authors = [
10
- { name = "The Flower Authors", email = "hello@flower.ai" },
11
- ]
12
9
  license = { text = "Apache License (2.0)" }
13
10
  dependencies = [
14
11
  "flwr[simulation]>=1.9.0,<2.0",
@@ -18,15 +15,15 @@ dependencies = [
18
15
  [tool.hatch.build.targets.wheel]
19
16
  packages = ["."]
20
17
 
21
- [flower]
18
+ [tool.flwr]
22
19
  publisher = "$username"
23
20
 
24
- [flower.components]
21
+ [tool.flwr.components]
25
22
  serverapp = "$import_name.server:app"
26
23
  clientapp = "$import_name.client:app"
27
24
 
28
- [flower.engine]
29
- name = "simulation"
25
+ [tool.flwr.federations]
26
+ default = "localhost"
30
27
 
31
- [flower.engine.simulation.supernode]
32
- num = 2
28
+ [tool.flwr.federations.localhost]
29
+ options.num-supernodes = 10
@@ -6,9 +6,6 @@ build-backend = "hatchling.build"
6
6
  name = "$package_name"
7
7
  version = "1.0.0"
8
8
  description = ""
9
- authors = [
10
- { name = "The Flower Authors", email = "hello@flower.ai" },
11
- ]
12
9
  license = { text = "Apache License (2.0)" }
13
10
  dependencies = [
14
11
  "flwr[simulation]>=1.9.0,<2.0",
@@ -20,15 +17,15 @@ dependencies = [
20
17
  [tool.hatch.build.targets.wheel]
21
18
  packages = ["."]
22
19
 
23
- [flower]
20
+ [tool.flwr]
24
21
  publisher = "$username"
25
22
 
26
- [flower.components]
23
+ [tool.flwr.components]
27
24
  serverapp = "$import_name.server:app"
28
25
  clientapp = "$import_name.client:app"
29
26
 
30
- [flower.engine]
31
- name = "simulation"
27
+ [tool.flwr.federations]
28
+ default = "localhost"
32
29
 
33
- [flower.engine.simulation.supernode]
34
- num = 2
30
+ [tool.flwr.federations.localhost]
31
+ options.num-supernodes = 10
@@ -6,9 +6,6 @@ build-backend = "hatchling.build"
6
6
  name = "$package_name"
7
7
  version = "1.0.0"
8
8
  description = ""
9
- authors = [
10
- { name = "The Flower Authors", email = "hello@flower.ai" },
11
- ]
12
9
  license = { text = "Apache License (2.0)" }
13
10
  dependencies = [
14
11
  "flwr[simulation]>=1.9.0,<2.0",
@@ -19,15 +16,15 @@ dependencies = [
19
16
  [tool.hatch.build.targets.wheel]
20
17
  packages = ["."]
21
18
 
22
- [flower]
19
+ [tool.flwr]
23
20
  publisher = "$username"
24
21
 
25
- [flower.components]
22
+ [tool.flwr.components]
26
23
  serverapp = "$import_name.server:app"
27
24
  clientapp = "$import_name.client:app"
28
25
 
29
- [flower.engine]
30
- name = "simulation"
26
+ [tool.flwr.federations]
27
+ default = "localhost"
31
28
 
32
- [flower.engine.simulation.supernode]
33
- num = 2
29
+ [tool.flwr.federations.localhost]
30
+ options.num-supernodes = 10
@@ -6,9 +6,6 @@ build-backend = "hatchling.build"
6
6
  name = "$package_name"
7
7
  version = "1.0.0"
8
8
  description = ""
9
- authors = [
10
- { name = "The Flower Authors", email = "hello@flower.ai" },
11
- ]
12
9
  license = { text = "Apache License (2.0)" }
13
10
  dependencies = [
14
11
  "flwr[simulation]>=1.9.0,<2.0",
@@ -19,15 +16,15 @@ dependencies = [
19
16
  [tool.hatch.build.targets.wheel]
20
17
  packages = ["."]
21
18
 
22
- [flower]
19
+ [tool.flwr]
23
20
  publisher = "$username"
24
21
 
25
- [flower.components]
22
+ [tool.flwr.components]
26
23
  serverapp = "$import_name.server:app"
27
24
  clientapp = "$import_name.client:app"
28
25
 
29
- [flower.engine]
30
- name = "simulation"
26
+ [tool.flwr.federations]
27
+ default = "localhost"
31
28
 
32
- [flower.engine.simulation.supernode]
33
- num = 2
29
+ [tool.flwr.federations.localhost]
30
+ options.num-supernodes = 10
flwr/cli/run/run.py CHANGED
@@ -15,18 +15,16 @@
15
15
  """Flower command line interface `run` command."""
16
16
 
17
17
  import sys
18
- from enum import Enum
19
18
  from logging import DEBUG
20
19
  from pathlib import Path
21
- from typing import Dict, Optional
20
+ from typing import Any, Dict, Optional
22
21
 
23
22
  import typer
24
23
  from typing_extensions import Annotated
25
24
 
26
- from flwr.cli import config_utils
27
25
  from flwr.cli.build import build
26
+ from flwr.cli.config_utils import load_and_validate
28
27
  from flwr.common.config import parse_config_args
29
- from flwr.common.constant import SUPEREXEC_DEFAULT_ADDRESS
30
28
  from flwr.common.grpc import GRPC_MAX_MESSAGE_LENGTH, create_channel
31
29
  from flwr.common.logger import log
32
30
  from flwr.proto.exec_pb2 import StartRunRequest # pylint: disable=E0611
@@ -34,30 +32,15 @@ from flwr.proto.exec_pb2_grpc import ExecStub
34
32
  from flwr.simulation.run_simulation import _run_simulation
35
33
 
36
34
 
37
- class Engine(str, Enum):
38
- """Enum defining the engine to run on."""
39
-
40
- SIMULATION = "simulation"
41
-
42
-
43
35
  # pylint: disable-next=too-many-locals
44
36
  def run(
45
- engine: Annotated[
46
- Optional[Engine],
47
- typer.Option(
48
- case_sensitive=False,
49
- help="The engine to run FL with (currently only simulation is supported).",
50
- ),
51
- ] = None,
52
- use_superexec: Annotated[
53
- bool,
54
- typer.Option(
55
- case_sensitive=False, help="Use this flag to use the new SuperExec API"
56
- ),
57
- ] = False,
58
37
  directory: Annotated[
59
- Optional[Path],
60
- typer.Option(help="Path of the Flower project to run"),
38
+ Path,
39
+ typer.Argument(help="Path of the Flower project to run"),
40
+ ] = Path("."),
41
+ federation_name: Annotated[
42
+ Optional[str],
43
+ typer.Argument(help="Name of the federation to run the app on"),
61
44
  ] = None,
62
45
  config_overrides: Annotated[
63
46
  Optional[str],
@@ -72,7 +55,7 @@ def run(
72
55
  typer.secho("Loading project configuration... ", fg=typer.colors.BLUE)
73
56
 
74
57
  pyproject_path = directory / "pyproject.toml" if directory else None
75
- config, errors, warnings = config_utils.load_and_validate(path=pyproject_path)
58
+ config, errors, warnings = load_and_validate(path=pyproject_path)
76
59
 
77
60
  if config is None:
78
61
  typer.secho(
@@ -94,50 +77,81 @@ def run(
94
77
 
95
78
  typer.secho("Success", fg=typer.colors.GREEN)
96
79
 
97
- if use_superexec:
98
- _start_superexec_run(
99
- parse_config_args(config_overrides, separator=","), directory
100
- )
101
- return
102
-
103
- server_app_ref = config["flower"]["components"]["serverapp"]
104
- client_app_ref = config["flower"]["components"]["clientapp"]
105
-
106
- if engine is None:
107
- engine = config["flower"]["engine"]["name"]
80
+ federation_name = federation_name or config["tool"]["flwr"]["federations"].get(
81
+ "default"
82
+ )
108
83
 
109
- if engine == Engine.SIMULATION:
110
- num_supernodes = config["flower"]["engine"]["simulation"]["supernode"]["num"]
111
- backend_config = config["flower"]["engine"]["simulation"].get(
112
- "backend_config", None
84
+ if federation_name is None:
85
+ typer.secho(
86
+ "❌ No federation name was provided and the project's `pyproject.toml` "
87
+ "doesn't declare a default federation (with a SuperExec address or an "
88
+ "`options.num-supernodes` value).",
89
+ fg=typer.colors.RED,
90
+ bold=True,
113
91
  )
92
+ raise typer.Exit(code=1)
114
93
 
115
- typer.secho("Starting run... ", fg=typer.colors.BLUE)
116
- _run_simulation(
117
- server_app_attr=server_app_ref,
118
- client_app_attr=client_app_ref,
119
- num_supernodes=num_supernodes,
120
- backend_config=backend_config,
121
- )
122
- else:
94
+ # Validate the federation exists in the configuration
95
+ federation = config["tool"]["flwr"]["federations"].get(federation_name)
96
+ if federation is None:
97
+ available_feds = list(config["tool"]["flwr"]["federations"])
123
98
  typer.secho(
124
- f"Engine '{engine}' is not yet supported in `flwr run`",
99
+ f" There is no `{federation_name}` federation declared in the "
100
+ "`pyproject.toml`.\n The following federations were found:\n\n"
101
+ "\n".join(available_feds) + "\n\n",
125
102
  fg=typer.colors.RED,
126
103
  bold=True,
127
104
  )
105
+ raise typer.Exit(code=1)
106
+
107
+ if "address" in federation:
108
+ _run_with_superexec(federation, directory, config_overrides)
109
+ else:
110
+ _run_without_superexec(config, federation, federation_name)
128
111
 
129
112
 
130
- def _start_superexec_run(
131
- override_config: Dict[str, str], directory: Optional[Path]
113
+ def _run_with_superexec(
114
+ federation: Dict[str, str],
115
+ directory: Optional[Path],
116
+ config_overrides: Optional[str],
132
117
  ) -> None:
118
+
133
119
  def on_channel_state_change(channel_connectivity: str) -> None:
134
120
  """Log channel connectivity."""
135
121
  log(DEBUG, channel_connectivity)
136
122
 
123
+ insecure_str = federation.get("insecure")
124
+ if root_certificates := federation.get("root-certificates"):
125
+ root_certificates_bytes = Path(root_certificates).read_bytes()
126
+ if insecure := bool(insecure_str):
127
+ typer.secho(
128
+ "❌ `root_certificates` were provided but the `insecure` parameter"
129
+ "is set to `True`.",
130
+ fg=typer.colors.RED,
131
+ bold=True,
132
+ )
133
+ raise typer.Exit(code=1)
134
+ else:
135
+ root_certificates_bytes = None
136
+ if insecure_str is None:
137
+ typer.secho(
138
+ "❌ To disable TLS, set `insecure = true` in `pyproject.toml`.",
139
+ fg=typer.colors.RED,
140
+ bold=True,
141
+ )
142
+ raise typer.Exit(code=1)
143
+ if not (insecure := bool(insecure_str)):
144
+ typer.secho(
145
+ "❌ No certificate were given yet `insecure` is set to `False`.",
146
+ fg=typer.colors.RED,
147
+ bold=True,
148
+ )
149
+ raise typer.Exit(code=1)
150
+
137
151
  channel = create_channel(
138
- server_address=SUPEREXEC_DEFAULT_ADDRESS,
139
- insecure=True,
140
- root_certificates=None,
152
+ server_address=federation["address"],
153
+ insecure=insecure,
154
+ root_certificates=root_certificates_bytes,
141
155
  max_message_length=GRPC_MAX_MESSAGE_LENGTH,
142
156
  interceptors=None,
143
157
  )
@@ -148,7 +162,34 @@ def _start_superexec_run(
148
162
 
149
163
  req = StartRunRequest(
150
164
  fab_file=Path(fab_path).read_bytes(),
151
- override_config=override_config,
165
+ override_config=parse_config_args(config_overrides, separator=","),
152
166
  )
153
167
  res = stub.StartRun(req)
154
168
  typer.secho(f"🎊 Successfully started run {res.run_id}", fg=typer.colors.GREEN)
169
+
170
+
171
+ def _run_without_superexec(
172
+ config: Dict[str, Any], federation: Dict[str, Any], federation_name: str
173
+ ) -> None:
174
+ server_app_ref = config["tool"]["flwr"]["components"]["serverapp"]
175
+ client_app_ref = config["tool"]["flwr"]["components"]["clientapp"]
176
+
177
+ try:
178
+ num_supernodes = federation["options"]["num-supernodes"]
179
+ except KeyError as err:
180
+ typer.secho(
181
+ "❌ The project's `pyproject.toml` needs to declare the number of"
182
+ " SuperNodes in the simulation. To simulate 10 SuperNodes,"
183
+ " use the following notation:\n\n"
184
+ f"[tool.flwr.federations.{federation_name}]\n"
185
+ "options.num-supernodes = 10\n",
186
+ fg=typer.colors.RED,
187
+ bold=True,
188
+ )
189
+ raise typer.Exit(code=1) from err
190
+
191
+ _run_simulation(
192
+ server_app_attr=server_app_ref,
193
+ client_app_attr=client_app_ref,
194
+ num_supernodes=num_supernodes,
195
+ )
@@ -60,7 +60,12 @@ def run_supernode() -> None:
60
60
  _warn_deprecated_server_arg(args)
61
61
 
62
62
  root_certificates = _get_certificates(args)
63
- load_fn = _get_load_client_app_fn(args, multi_app=True)
63
+ load_fn = _get_load_client_app_fn(
64
+ default_app_ref=getattr(args, "client-app"),
65
+ dir_arg=args.dir,
66
+ flwr_dir_arg=args.flwr_dir,
67
+ multi_app=True,
68
+ )
64
69
  authentication_keys = _try_setup_client_authentication(args)
65
70
 
66
71
  _start_client_internal(
@@ -93,7 +98,11 @@ def run_client_app() -> None:
93
98
  _warn_deprecated_server_arg(args)
94
99
 
95
100
  root_certificates = _get_certificates(args)
96
- load_fn = _get_load_client_app_fn(args, multi_app=False)
101
+ load_fn = _get_load_client_app_fn(
102
+ default_app_ref=getattr(args, "client-app"),
103
+ dir_arg=args.dir,
104
+ multi_app=False,
105
+ )
97
106
  authentication_keys = _try_setup_client_authentication(args)
98
107
 
99
108
  _start_client_internal(
@@ -166,7 +175,10 @@ def _get_certificates(args: argparse.Namespace) -> Optional[bytes]:
166
175
 
167
176
 
168
177
  def _get_load_client_app_fn(
169
- args: argparse.Namespace, multi_app: bool
178
+ default_app_ref: str,
179
+ dir_arg: str,
180
+ multi_app: bool,
181
+ flwr_dir_arg: Optional[str] = None,
170
182
  ) -> Callable[[str, str], ClientApp]:
171
183
  """Get the load_client_app_fn function.
172
184
 
@@ -178,25 +190,24 @@ def _get_load_client_app_fn(
178
190
  loads a default ClientApp.
179
191
  """
180
192
  # Find the Flower directory containing Flower Apps (only for multi-app)
181
- flwr_dir = Path("")
182
- if "flwr_dir" in args:
183
- if args.flwr_dir is None:
193
+ if not multi_app:
194
+ flwr_dir = Path("")
195
+ else:
196
+ if flwr_dir_arg is None:
184
197
  flwr_dir = get_flwr_dir()
185
198
  else:
186
- flwr_dir = Path(args.flwr_dir).absolute()
199
+ flwr_dir = Path(flwr_dir_arg).absolute()
187
200
 
188
201
  inserted_path = None
189
202
 
190
- default_app_ref: str = getattr(args, "client-app")
191
-
192
203
  if not multi_app:
193
204
  log(
194
205
  DEBUG,
195
206
  "Flower SuperNode will load and validate ClientApp `%s`",
196
- getattr(args, "client-app"),
207
+ default_app_ref,
197
208
  )
198
209
  # Insert sys.path
199
- dir_path = Path(args.dir).absolute()
210
+ dir_path = Path(dir_arg).absolute()
200
211
  sys.path.insert(0, str(dir_path))
201
212
  inserted_path = str(dir_path)
202
213
 
@@ -208,7 +219,7 @@ def _get_load_client_app_fn(
208
219
  # If multi-app feature is disabled
209
220
  if not multi_app:
210
221
  # Get sys path to be inserted
211
- dir_path = Path(args.dir).absolute()
222
+ dir_path = Path(dir_arg).absolute()
212
223
 
213
224
  # Set app reference
214
225
  client_app_ref = default_app_ref
@@ -221,7 +232,7 @@ def _get_load_client_app_fn(
221
232
 
222
233
  log(WARN, "FAB ID is not provided; the default ClientApp will be loaded.")
223
234
  # Get sys path to be inserted
224
- dir_path = Path(args.dir).absolute()
235
+ dir_path = Path(dir_arg).absolute()
225
236
 
226
237
  # Set app reference
227
238
  client_app_ref = default_app_ref
@@ -237,7 +248,7 @@ def _get_load_client_app_fn(
237
248
  dir_path = Path(project_dir).absolute()
238
249
 
239
250
  # Set app reference
240
- client_app_ref = config["flower"]["components"]["clientapp"]
251
+ client_app_ref = config["tool"]["flwr"]["components"]["clientapp"]
241
252
 
242
253
  # Set sys.path
243
254
  nonlocal inserted_path
flwr/common/config.py CHANGED
@@ -97,7 +97,7 @@ def get_fused_config(run: Run, flwr_dir: Optional[Path]) -> Dict[str, str]:
97
97
 
98
98
  project_dir = get_project_dir(run.fab_id, run.fab_version, flwr_dir)
99
99
 
100
- default_config = get_project_config(project_dir)["flower"].get("config", {})
100
+ default_config = get_project_config(project_dir)["tool"]["flwr"].get("config", {})
101
101
  flat_default_config = flatten_dict(default_config)
102
102
 
103
103
  return _fuse_dicts(flat_default_config, run.override_config)
@@ -186,7 +186,7 @@ def run_server_app() -> None: # pylint: disable=too-many-branches
186
186
  run_ = driver.run
187
187
  server_app_dir = str(get_project_dir(run_.fab_id, run_.fab_version, flwr_dir))
188
188
  config = get_project_config(server_app_dir)
189
- server_app_attr = config["flower"]["components"]["serverapp"]
189
+ server_app_attr = config["tool"]["flwr"]["components"]["serverapp"]
190
190
  server_app_run_config = get_fused_config(run_, flwr_dir)
191
191
  else:
192
192
  # User provided `server-app`, but not `--run-id`
@@ -16,7 +16,6 @@
16
16
 
17
17
 
18
18
  import json
19
- import sys
20
19
  import threading
21
20
  import time
22
21
  import traceback
@@ -29,6 +28,7 @@ from typing import Callable, Dict, Optional
29
28
 
30
29
  from flwr.client.client_app import ClientApp, ClientAppException, LoadClientAppError
31
30
  from flwr.client.node_state import NodeState
31
+ from flwr.client.supernode.app import _get_load_client_app_fn
32
32
  from flwr.common.constant import (
33
33
  NUM_PARTITIONS_KEY,
34
34
  PARTITION_ID_KEY,
@@ -37,8 +37,8 @@ from flwr.common.constant import (
37
37
  )
38
38
  from flwr.common.logger import log
39
39
  from flwr.common.message import Error
40
- from flwr.common.object_ref import load_app
41
40
  from flwr.common.serde import message_from_taskins, message_to_taskres
41
+ from flwr.common.typing import Run
42
42
  from flwr.proto.task_pb2 import TaskIns, TaskRes # pylint: disable=E0611
43
43
  from flwr.server.superlink.state import State, StateFactory
44
44
 
@@ -60,6 +60,27 @@ def _register_nodes(
60
60
  return nodes_mapping
61
61
 
62
62
 
63
+ def _register_node_states(
64
+ nodes_mapping: NodeToPartitionMapping, run: Run
65
+ ) -> Dict[int, NodeState]:
66
+ """Create NodeState objects and pre-register the context for the run."""
67
+ node_states: Dict[int, NodeState] = {}
68
+ num_partitions = len(set(nodes_mapping.values()))
69
+ for node_id, partition_id in nodes_mapping.items():
70
+ node_states[node_id] = NodeState(
71
+ node_id=node_id,
72
+ node_config={
73
+ PARTITION_ID_KEY: str(partition_id),
74
+ NUM_PARTITIONS_KEY: str(num_partitions),
75
+ },
76
+ )
77
+
78
+ # Pre-register Context objects
79
+ node_states[node_id].register_context(run_id=run.run_id, run=run)
80
+
81
+ return node_states
82
+
83
+
63
84
  # pylint: disable=too-many-arguments,too-many-locals
64
85
  def worker(
65
86
  app_fn: Callable[[], ClientApp],
@@ -78,8 +99,7 @@ def worker(
78
99
  task_ins: TaskIns = taskins_queue.get(timeout=1.0)
79
100
  node_id = task_ins.task.consumer.node_id
80
101
 
81
- # Register and retrieve context
82
- node_states[node_id].register_context(run_id=task_ins.run_id)
102
+ # Retrieve context
83
103
  context = node_states[node_id].retrieve_context(run_id=task_ins.run_id)
84
104
 
85
105
  # Convert TaskIns to Message
@@ -151,7 +171,7 @@ def put_taskres_into_state(
151
171
  pass
152
172
 
153
173
 
154
- def run(
174
+ def run_api(
155
175
  app_fn: Callable[[], ClientApp],
156
176
  backend_fn: Callable[[], Backend],
157
177
  nodes_mapping: NodeToPartitionMapping,
@@ -237,6 +257,8 @@ def start_vce(
237
257
  backend_config_json_stream: str,
238
258
  app_dir: str,
239
259
  f_stop: threading.Event,
260
+ run: Run,
261
+ flwr_dir: Optional[str] = None,
240
262
  client_app: Optional[ClientApp] = None,
241
263
  client_app_attr: Optional[str] = None,
242
264
  num_supernodes: Optional[int] = None,
@@ -287,17 +309,7 @@ def start_vce(
287
309
  )
288
310
 
289
311
  # Construct mapping of NodeStates
290
- node_states: Dict[int, NodeState] = {}
291
- # Number of unique partitions
292
- num_partitions = len(set(nodes_mapping.values()))
293
- for node_id, partition_id in nodes_mapping.items():
294
- node_states[node_id] = NodeState(
295
- node_id=node_id,
296
- node_config={
297
- PARTITION_ID_KEY: str(partition_id),
298
- NUM_PARTITIONS_KEY: str(num_partitions),
299
- },
300
- )
312
+ node_states = _register_node_states(nodes_mapping=nodes_mapping, run=run)
301
313
 
302
314
  # Load backend config
303
315
  log(DEBUG, "Supported backends: %s", list(supported_backends.keys()))
@@ -326,16 +338,12 @@ def start_vce(
326
338
  def _load() -> ClientApp:
327
339
 
328
340
  if client_app_attr:
329
-
330
- if app_dir is not None:
331
- sys.path.insert(0, app_dir)
332
-
333
- app: ClientApp = load_app(client_app_attr, LoadClientAppError, app_dir)
334
-
335
- if not isinstance(app, ClientApp):
336
- raise LoadClientAppError(
337
- f"Attribute {client_app_attr} is not of type {ClientApp}",
338
- ) from None
341
+ app = _get_load_client_app_fn(
342
+ default_app_ref=client_app_attr,
343
+ dir_arg=app_dir,
344
+ flwr_dir_arg=flwr_dir,
345
+ multi_app=True,
346
+ )(run.fab_id, run.fab_version)
339
347
 
340
348
  if client_app:
341
349
  app = client_app
@@ -345,10 +353,19 @@ def start_vce(
345
353
 
346
354
  try:
347
355
  # Test if ClientApp can be loaded
348
- _ = app_fn()
356
+ client_app = app_fn()
357
+
358
+ # Cache `ClientApp`
359
+ if client_app_attr:
360
+ # Now wrap the loaded ClientApp in a dummy function
361
+ # this prevent unnecesary low-level loading of ClientApp
362
+ def _load_client_app() -> ClientApp:
363
+ return client_app
364
+
365
+ app_fn = _load_client_app
349
366
 
350
367
  # Run main simulation loop
351
- run(
368
+ run_api(
352
369
  app_fn,
353
370
  backend_fn,
354
371
  nodes_mapping,
@@ -26,14 +26,16 @@ from typing import Dict, Optional
26
26
 
27
27
  from flwr.client import ClientApp
28
28
  from flwr.common import EventType, event, log
29
+ from flwr.common.constant import RUN_ID_NUM_BYTES
29
30
  from flwr.common.logger import set_logger_propagation, update_console_handler
30
31
  from flwr.common.typing import Run
31
32
  from flwr.server.driver import Driver, InMemoryDriver
32
- from flwr.server.run_serverapp import run
33
+ from flwr.server.run_serverapp import run as run_server_app
33
34
  from flwr.server.server_app import ServerApp
34
35
  from flwr.server.superlink.fleet import vce
35
36
  from flwr.server.superlink.fleet.vce.backend.backend import BackendConfig
36
37
  from flwr.server.superlink.state import StateFactory
38
+ from flwr.server.superlink.state.utils import generate_rand_int_from_bytes
37
39
  from flwr.simulation.ray_transport.utils import (
38
40
  enable_tf_gpu_growth as enable_gpu_growth,
39
41
  )
@@ -54,7 +56,11 @@ def run_simulation_from_cli() -> None:
54
56
  backend_name=args.backend,
55
57
  backend_config=backend_config_dict,
56
58
  app_dir=args.app_dir,
57
- run_id=args.run_id,
59
+ run=(
60
+ Run(run_id=args.run_id, fab_id="", fab_version="", override_config={})
61
+ if args.run_id
62
+ else None
63
+ ),
58
64
  enable_tf_gpu_growth=args.enable_tf_gpu_growth,
59
65
  verbose_logging=args.verbose,
60
66
  )
@@ -156,7 +162,7 @@ def run_serverapp_th(
156
162
  enable_gpu_growth()
157
163
 
158
164
  # Run ServerApp
159
- run(
165
+ run_server_app(
160
166
  driver=_driver,
161
167
  server_app_dir=_server_app_dir,
162
168
  server_app_run_config=_server_app_run_config,
@@ -193,16 +199,6 @@ def run_serverapp_th(
193
199
  return serverapp_th
194
200
 
195
201
 
196
- def _override_run_id(state: StateFactory, run_id_to_replace: int, run_id: int) -> None:
197
- """Override the run_id of an existing Run."""
198
- log(DEBUG, "Pre-registering run with id %s", run_id)
199
- # Remove run
200
- run_info: Run = state.state().run_ids.pop(run_id_to_replace) # type: ignore
201
- # Update with new run_id and insert back in state
202
- run_info.run_id = run_id
203
- state.state().run_ids[run_id] = run_info # type: ignore
204
-
205
-
206
202
  # pylint: disable=too-many-locals
207
203
  def _main_loop(
208
204
  num_supernodes: int,
@@ -210,7 +206,8 @@ def _main_loop(
210
206
  backend_config_stream: str,
211
207
  app_dir: str,
212
208
  enable_tf_gpu_growth: bool,
213
- run_id: Optional[int] = None,
209
+ run: Run,
210
+ flwr_dir: Optional[str] = None,
214
211
  client_app: Optional[ClientApp] = None,
215
212
  client_app_attr: Optional[str] = None,
216
213
  server_app: Optional[ServerApp] = None,
@@ -225,16 +222,13 @@ def _main_loop(
225
222
  server_app_thread_has_exception = threading.Event()
226
223
  serverapp_th = None
227
224
  try:
228
- # Create run (with empty fab_id and fab_version)
229
- run_id_ = state_factory.state().create_run("", "", {})
225
+ # Register run
226
+ log(DEBUG, "Pre-registering run with id %s", run.run_id)
227
+ state_factory.state().run_ids[run.run_id] = run # type: ignore
230
228
  server_app_run_config: Dict[str, str] = {}
231
229
 
232
- if run_id:
233
- _override_run_id(state_factory, run_id_to_replace=run_id_, run_id=run_id)
234
- run_id_ = run_id
235
-
236
230
  # Initialize Driver
237
- driver = InMemoryDriver(run_id=run_id_, state_factory=state_factory)
231
+ driver = InMemoryDriver(run_id=run.run_id, state_factory=state_factory)
238
232
 
239
233
  # Get and run ServerApp thread
240
234
  serverapp_th = run_serverapp_th(
@@ -259,6 +253,8 @@ def _main_loop(
259
253
  app_dir=app_dir,
260
254
  state_factory=state_factory,
261
255
  f_stop=f_stop,
256
+ run=run,
257
+ flwr_dir=flwr_dir,
262
258
  )
263
259
 
264
260
  except Exception as ex:
@@ -289,7 +285,8 @@ def _run_simulation(
289
285
  client_app_attr: Optional[str] = None,
290
286
  server_app_attr: Optional[str] = None,
291
287
  app_dir: str = "",
292
- run_id: Optional[int] = None,
288
+ flwr_dir: Optional[str] = None,
289
+ run: Optional[Run] = None,
293
290
  enable_tf_gpu_growth: bool = False,
294
291
  verbose_logging: bool = False,
295
292
  ) -> None:
@@ -332,8 +329,11 @@ def _run_simulation(
332
329
  Add specified directory to the PYTHONPATH and load `ClientApp` from there.
333
330
  (Default: current working directory.)
334
331
 
335
- run_id : Optional[int]
336
- An integer specifying the ID of the run started when running this function.
332
+ flwr_dir : Optional[str]
333
+ The path containing installed Flower Apps.
334
+
335
+ run : Optional[Run]
336
+ An object carrying details about the run.
337
337
 
338
338
  enable_tf_gpu_growth : bool (default: False)
339
339
  A boolean to indicate whether to enable GPU growth on the main thread. This is
@@ -371,13 +371,19 @@ def _run_simulation(
371
371
  # Convert config to original JSON-stream format
372
372
  backend_config_stream = json.dumps(backend_config)
373
373
 
374
+ # If no `Run` object is set, create one
375
+ if run is None:
376
+ run_id = generate_rand_int_from_bytes(RUN_ID_NUM_BYTES)
377
+ run = Run(run_id=run_id, fab_id="", fab_version="", override_config={})
378
+
374
379
  args = (
375
380
  num_supernodes,
376
381
  backend_name,
377
382
  backend_config_stream,
378
383
  app_dir,
379
384
  enable_tf_gpu_growth,
380
- run_id,
385
+ run,
386
+ flwr_dir,
381
387
  client_app,
382
388
  client_app_attr,
383
389
  server_app,
@@ -465,6 +471,17 @@ def _parse_args_run_simulation() -> argparse.ArgumentParser:
465
471
  "ClientApp and ServerApp from there."
466
472
  " Default: current working directory.",
467
473
  )
474
+ parser.add_argument(
475
+ "--flwr-dir",
476
+ default=None,
477
+ help="""The path containing installed Flower Apps.
478
+ By default, this value is equal to:
479
+
480
+ - `$FLWR_HOME/` if `$FLWR_HOME` is defined
481
+ - `$XDG_DATA_HOME/.flwr/` if `$XDG_DATA_HOME` is defined
482
+ - `$HOME/.flwr/` in all other cases
483
+ """,
484
+ )
468
485
  parser.add_argument(
469
486
  "--run-id",
470
487
  type=int,
@@ -0,0 +1,157 @@
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
+ """Simulation engine executor."""
16
+
17
+
18
+ import subprocess
19
+ import sys
20
+ from logging import ERROR, INFO, WARN
21
+ from typing import Dict, Optional
22
+
23
+ from typing_extensions import override
24
+
25
+ from flwr.cli.config_utils import load_and_validate
26
+ from flwr.cli.install import install_from_fab
27
+ from flwr.common.constant import RUN_ID_NUM_BYTES
28
+ from flwr.common.logger import log
29
+ from flwr.server.superlink.state.utils import generate_rand_int_from_bytes
30
+
31
+ from .executor import Executor, RunTracker
32
+
33
+
34
+ class SimulationEngine(Executor):
35
+ """Simulation engine executor.
36
+
37
+ Parameters
38
+ ----------
39
+ num_supernodes: Opitonal[str] (default: None)
40
+ Total number of nodes to involve in the simulation.
41
+ """
42
+
43
+ def __init__(
44
+ self,
45
+ num_supernodes: Optional[str] = None,
46
+ ) -> None:
47
+ self.num_supernodes = num_supernodes
48
+
49
+ @override
50
+ def set_config(
51
+ self,
52
+ config: Dict[str, str],
53
+ ) -> None:
54
+ """Set executor config arguments.
55
+
56
+ Parameters
57
+ ----------
58
+ config : Dict[str, str]
59
+ A dictionary for configuration values.
60
+ Supported configuration key/value pairs:
61
+ - "num-supernodes": str
62
+ Number of nodes to register for the simulation.
63
+ """
64
+ if not config:
65
+ return
66
+ if num_supernodes := config.get("num-supernodes"):
67
+ self.num_supernodes = num_supernodes
68
+
69
+ # Validate config
70
+ if self.num_supernodes is None:
71
+ log(
72
+ ERROR,
73
+ "To start a run with the simulation plugin, please specify "
74
+ "the number of SuperNodes. This can be done by using the "
75
+ "`--executor-config` argument when launching the SuperExec.",
76
+ )
77
+ raise ValueError("`num-supernodes` must not be `None`")
78
+
79
+ @override
80
+ def start_run(
81
+ self, fab_file: bytes, override_config: Dict[str, str]
82
+ ) -> Optional[RunTracker]:
83
+ """Start run using the Flower Simulation Engine."""
84
+ try:
85
+ if override_config:
86
+ raise ValueError(
87
+ "Overriding the run config is not yet supported with the "
88
+ "simulation executor.",
89
+ )
90
+
91
+ # Install FAB to flwr dir
92
+ fab_path = install_from_fab(fab_file, None, True)
93
+
94
+ # Install FAB Python package
95
+ subprocess.check_call(
96
+ [sys.executable, "-m", "pip", "install", "--no-deps", str(fab_path)],
97
+ stdout=subprocess.DEVNULL,
98
+ stderr=subprocess.DEVNULL,
99
+ )
100
+
101
+ # Load and validate config
102
+ config, errors, warnings = load_and_validate(fab_path / "pyproject.toml")
103
+ if errors:
104
+ raise ValueError(errors)
105
+
106
+ if warnings:
107
+ log(WARN, warnings)
108
+
109
+ if config is None:
110
+ raise ValueError(
111
+ "Config extracted from FAB's pyproject.toml is not valid"
112
+ )
113
+
114
+ # Get ClientApp and SeverApp components
115
+ flower_components = config["tool"]["flwr"]["components"]
116
+ clientapp = flower_components["clientapp"]
117
+ serverapp = flower_components["serverapp"]
118
+
119
+ # In Simulation there is no SuperLink, still we create a run_id
120
+ run_id = generate_rand_int_from_bytes(RUN_ID_NUM_BYTES)
121
+ log(INFO, "Created run %s", str(run_id))
122
+
123
+ # Prepare commnand
124
+ command = [
125
+ "flower-simulation",
126
+ "--client-app",
127
+ f"{clientapp}",
128
+ "--server-app",
129
+ f"{serverapp}",
130
+ "--num-supernodes",
131
+ f"{self.num_supernodes}",
132
+ "--run-id",
133
+ str(run_id),
134
+ ]
135
+
136
+ # Start Simulation
137
+ proc = subprocess.Popen( # pylint: disable=consider-using-with
138
+ command,
139
+ stdout=subprocess.PIPE,
140
+ stderr=subprocess.PIPE,
141
+ text=True,
142
+ )
143
+
144
+ log(INFO, "Started run %s", str(run_id))
145
+
146
+ return RunTracker(
147
+ run_id=run_id,
148
+ proc=proc,
149
+ )
150
+
151
+ # pylint: disable-next=broad-except
152
+ except Exception as e:
153
+ log(ERROR, "Could not start run: %s", str(e))
154
+ return None
155
+
156
+
157
+ executor = SimulationEngine()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: flwr-nightly
3
- Version: 1.10.0.dev20240714
3
+ Version: 1.10.0.dev20240715
4
4
  Summary: Flower: A Friendly Federated Learning Framework
5
5
  Home-page: https://flower.ai
6
6
  License: Apache-2.0
@@ -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=FBcSrE35ll88VE11ib67qgsJe2GYDN25UswV9-cYcX8,1267
4
- flwr/cli/build.py,sha256=G0wgNrgxir_H0Qb_YlT2itxETEb-9q_3RQflqIqNXTU,4737
5
- flwr/cli/config_utils.py,sha256=jz7ODmLE62tXHorg73zzB7POMe4WXLUzX8lt-WNfNVM,6488
4
+ flwr/cli/build.py,sha256=otFX_fKyMKFDQun9ku7VLK5S5vfmrCFmxxkvgKTa9QQ,4743
5
+ flwr/cli/config_utils.py,sha256=bLkKx4irCvjMX2NU81Oax6OWJUPkjQqHefK_HqD9NgA,6587
6
6
  flwr/cli/example.py,sha256=1bGDYll3BXQY2kRqSN-oICqS5n1b9m0g0RvXTopXHl4,2215
7
- flwr/cli/install.py,sha256=Wz7Hqg2PE9N-w5CnqlH9Zr8mzADN2J7NLcUhgldZLWU,6579
7
+ flwr/cli/install.py,sha256=ve2Bimhxq-p7dXsWkzoGuto3uVcA356DeKCMlgHIL9k,6585
8
8
  flwr/cli/new/__init__.py,sha256=cQzK1WH4JP2awef1t2UQ2xjl1agVEz9rwutV18SWV1k,789
9
9
  flwr/cli/new/new.py,sha256=vSLxyWOtD33U8mTGeqOhw5OjeDpFStekAVLUH9GJq6k,9432
10
10
  flwr/cli/new/templates/__init__.py,sha256=4luU8RL-CK8JJCstQ_ON809W9bNTkY1l9zSaPKBkgwY,725
@@ -41,16 +41,16 @@ flwr/cli/new/templates/app/code/task.jax.py.tpl,sha256=u4o3V019EH79szOw2xzVeC5r9
41
41
  flwr/cli/new/templates/app/code/task.mlx.py.tpl,sha256=nrfZ1aGOs_ayb70j7XdAmwFYa-rN10d9GIMIKLzctUE,2614
42
42
  flwr/cli/new/templates/app/code/task.pytorch.py.tpl,sha256=TU4uNtJ9wtxeVvoHD3_K89EFWmrIvdECdASzRX-4Uvk,3694
43
43
  flwr/cli/new/templates/app/code/task.tensorflow.py.tpl,sha256=cPOUUS07QbblT9PGFucwu9lY1clRA4-W4DQGA7cpcao,1044
44
- flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl,sha256=5kAtRcTvn2-s2i2_WGoDNJQ__ALADQjabX7lQlCNOGA,899
45
- flwr/cli/new/templates/app/pyproject.hf.toml.tpl,sha256=O3-dgH8_knk9uM49IzX06CYC2Ev5xdPuITB40Phvewc,759
46
- flwr/cli/new/templates/app/pyproject.jax.toml.tpl,sha256=QIhp6_eYYFk9aJd_n-tc_Ar76Se1OP6zSibTbGeHV7w,568
47
- flwr/cli/new/templates/app/pyproject.mlx.toml.tpl,sha256=OJ15G7CmjevBsUCVJ3ixv01VFwL4nkPcKkVGKeVW8ew,668
48
- flwr/cli/new/templates/app/pyproject.numpy.toml.tpl,sha256=VYHSqaZzUS8H4jy1Cr1Plyd2kuoNNWoGD3a8UzbOANI,606
49
- flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl,sha256=wxN6I8uvWZ4MErvTbQJTJOTbY4H_fsYCBXmZURJWLGQ,675
50
- flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl,sha256=wFeJuhqnBPQtKCBvnE3ySBpxmbeNdxcsq2Eb_RmSDIg,655
51
- flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl,sha256=zkxLTQRvujF76sIlzNNGPVU7Y9nVCwNBxAx82AOBaJY,654
44
+ flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl,sha256=7BojTJ1ePuRTANNcNBqLY7jgehvbRRawXJFiQhExYWk,742
45
+ flwr/cli/new/templates/app/pyproject.hf.toml.tpl,sha256=NSXJ9qxjjuumg84bX8y7spoKaVHm9DUjofYBGbCBPDc,714
46
+ flwr/cli/new/templates/app/pyproject.jax.toml.tpl,sha256=iIdKwB_FEA2oVtL8uSOj43uredcbRpEzy-rnEzCv-UM,606
47
+ flwr/cli/new/templates/app/pyproject.mlx.toml.tpl,sha256=rq1mERo0-PFJHbIX7tv0B_uzhvnbrqXKkC7j93aGvjE,623
48
+ flwr/cli/new/templates/app/pyproject.numpy.toml.tpl,sha256=C6XuAL9eJzp0SiDGOEQJQvW4qyrzEmiJRCwOKxq8xq4,561
49
+ flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl,sha256=ApZ8sHILExQE20pVjA03_dI_wO-oHtg_C8qXVHzFU3g,630
50
+ flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl,sha256=5-dv9fD-omZA9Y4qXX1_Hcaei_Lh5-p4jdTKvWafU7M,610
51
+ flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl,sha256=md9I4C86ZRF7p50hZhF5bw0Aax7vu_bvEldoaAkU5Uk,609
52
52
  flwr/cli/run/__init__.py,sha256=oCd6HmQDx-sqver1gecgx-uMA38BLTSiiKpl7RGNceg,789
53
- flwr/cli/run/run.py,sha256=b_TcqJAGOiF0RDO5f25HCWYYdG54CxSjtsXwcRYN_J8,4973
53
+ flwr/cli/run/run.py,sha256=PW_b1EprTB5NZyx0hAWWPFtpGR9tM7WJ9tlmDahU8J4,6818
54
54
  flwr/cli/utils.py,sha256=l65Ul0YsSBPuypk0uorAtEDmLEYiUrzpCXi6zCg9mJ4,4506
55
55
  flwr/client/__init__.py,sha256=wzJZsYJIHf_8-PMzvfbinyzzjgh1UP1vLrAw2_yEbKI,1345
56
56
  flwr/client/app.py,sha256=5v5EsA1zbViJAp998dCVRXvsyigZ-x3JEIKQ_fLeA48,26102
@@ -83,11 +83,11 @@ flwr/client/numpy_client.py,sha256=u76GWAdHmJM88Agm2EgLQSvO8Jnk225mJTk-_TmPjFE,1
83
83
  flwr/client/rest_client/__init__.py,sha256=5KGlp7pjc1dhNRkKlaNtUfQmg8wrRFh9lS3P3uRS-7Q,735
84
84
  flwr/client/rest_client/connection.py,sha256=aY_UzrNyE8g-xPAK_POZZZ93mERHTe-pOhNP-uZ8GyU,12147
85
85
  flwr/client/supernode/__init__.py,sha256=SUhWOzcgXRNXk1V9UgB5-FaWukqqrOEajVUHEcPkwyQ,865
86
- flwr/client/supernode/app.py,sha256=cg8momLzMnFrDBNJsSRDYrn8zHuJ-4UC3wIHf51aBXQ,15385
86
+ flwr/client/supernode/app.py,sha256=LMJiuodmTUOAQ9MEbl4V2HjwBVJquJ9lZI38RGBh15Q,15598
87
87
  flwr/client/typing.py,sha256=dxoTBnTMfqXr5J7G3y-uNjqxYCddvxhu89spfj4Lm2U,1048
88
88
  flwr/common/__init__.py,sha256=4cBLNNnNTwHDnL_HCxhU5ILCSZ6fYh3A_aMBtlvHTVw,3721
89
89
  flwr/common/address.py,sha256=wRu1Luezx1PWadwV9OA_KNko01oVvbRnPqfzaDn8QOk,1882
90
- flwr/common/config.py,sha256=7OF4R43st8teFXBWmV92noGEYVe00wT29QMs6od8mlI,4994
90
+ flwr/common/config.py,sha256=W8EEfey1IHytkdXII3fTExc3uFxNm_Ysf35inG3cTkg,5000
91
91
  flwr/common/constant.py,sha256=1XxuRezsr9fl3xvQNPR2kyFkwNeG_f5vZayv0PFh0kY,3012
92
92
  flwr/common/context.py,sha256=CQt4uzCDvCIr2WdkrWq0obAz92k2_ucXGrWtBZCxP_M,2256
93
93
  flwr/common/date.py,sha256=OcQuwpb2HxcblTqYm6H223ufop5UZw5N_fzalbpOVzY,891
@@ -187,7 +187,7 @@ flwr/server/driver/driver.py,sha256=NT_yaeit7_kZEIsCEqOWPID1GrVD3ywH4xZ2wtIh5lM,
187
187
  flwr/server/driver/grpc_driver.py,sha256=4Azmzq4RWzcLbOqBBEF-I78krWVWZ6bT0U42S25zMvY,9659
188
188
  flwr/server/driver/inmemory_driver.py,sha256=RcK94_NtjGZ4aZDIscnU7A3Uv1u8jGx29-xcbjQvZTM,6444
189
189
  flwr/server/history.py,sha256=bBOHKyX1eQONIsUx4EUU-UnAk1i0EbEl8ioyMq_UWQ8,5063
190
- flwr/server/run_serverapp.py,sha256=iq62xuSPYndmsYiJRpRkT9cTfQpZ0FgsWLncktUvh98,9486
190
+ flwr/server/run_serverapp.py,sha256=PkQy67LRxKr1dqH--VUOLtrgLw8JVFVNNr0hFqiINh0,9492
191
191
  flwr/server/server.py,sha256=wsXsxMZ9SQ0B42nBnUlcV83NJPycgrgg5bFwcQ4BYBE,17821
192
192
  flwr/server/server_app.py,sha256=1hul76ospG8L_KooK_ewn1sWPNTNYLTtZMeGNOBNruA,6267
193
193
  flwr/server/server_config.py,sha256=CZaHVAsMvGLjpWVcLPkiYxgJN4xfIyAiUrCI3fETKY4,1349
@@ -239,7 +239,7 @@ flwr/server/superlink/fleet/vce/__init__.py,sha256=36MHKiefnJeyjwMQzVUK4m06Ojon3
239
239
  flwr/server/superlink/fleet/vce/backend/__init__.py,sha256=oBIzmnrSSRvH_H0vRGEGWhWzQQwqe3zn6e13RsNwlIY,1466
240
240
  flwr/server/superlink/fleet/vce/backend/backend.py,sha256=iG3KSIY7DzNfcxmuLfTs7VdQJnqPCvvn5DFkTWKG5lI,2227
241
241
  flwr/server/superlink/fleet/vce/backend/raybackend.py,sha256=SnjZ1WOcrfMZNgiDdTHcFeXJqrY7UHx8kvO62mqU9S4,7489
242
- flwr/server/superlink/fleet/vce/vce_api.py,sha256=jSMmo3AXN_6XlYyzasu5yZ2jir8_Avj2zD9qtMzun6Q,12055
242
+ flwr/server/superlink/fleet/vce/vce_api.py,sha256=GVPxAgFR-K-hfWOphsZo-PFGmkLze4eytrLaE6TV6l8,12632
243
243
  flwr/server/superlink/state/__init__.py,sha256=Gj2OTFLXvA-mAjBvwuKDM3rDrVaQPcIoybSa2uskMTE,1003
244
244
  flwr/server/superlink/state/in_memory_state.py,sha256=fb-f4RGiqXON0DC7aSEMNuNIjH406BhBYrNNX5Kza2g,13061
245
245
  flwr/server/superlink/state/sqlite_state.py,sha256=dO374mTkvhWQSiwbqwUXVnAYHev-j2mHaX9v8wFmmMA,29044
@@ -262,15 +262,16 @@ flwr/simulation/ray_transport/__init__.py,sha256=wzcEEwUUlulnXsg6raCA1nGpP3LlAQD
262
262
  flwr/simulation/ray_transport/ray_actor.py,sha256=3j0HgzjrlYjnzdTRy8aA4Nf6VoUvxi1hGRQkGSU5z6c,19020
263
263
  flwr/simulation/ray_transport/ray_client_proxy.py,sha256=4KWWGSnfEBe3aGc0Ln5_1yRcZ52wKmOA7gXJKkMglvM,7302
264
264
  flwr/simulation/ray_transport/utils.py,sha256=TYdtfg1P9VfTdLMOJlifInGpxWHYs9UfUqIv2wfkRLA,2392
265
- flwr/simulation/run_simulation.py,sha256=qGP8sHKAzJT9nGeqMw36iCsVXm4ZFMBisCORuTswr-g,17277
265
+ flwr/simulation/run_simulation.py,sha256=wjN1spmdEmNZn06LDiuih1k3HHFKkHCKPbq-DieKw3A,17742
266
266
  flwr/superexec/__init__.py,sha256=9h94ogLxi6eJ3bUuJYq3E3pApThSabTPiSmPAGlTkHE,800
267
267
  flwr/superexec/app.py,sha256=Zh9I64XfCoghWoT1k2DKDrcVCXIGOpw03v0WKCOg-mg,6402
268
268
  flwr/superexec/deployment.py,sha256=o_FYkB_vamBPjeVpPbqvzr4kBYID26sXVDrLO3Ac4R0,6130
269
269
  flwr/superexec/exec_grpc.py,sha256=vYbZyV89MuvYDH1XzVYHkKmGfOcU6FWh8rTcIJk2TIQ,1910
270
270
  flwr/superexec/exec_servicer.py,sha256=4R1f_9v0vly_bXpIYaXAeV1tO5LAy1AYygGGGNZmlQk,2194
271
271
  flwr/superexec/executor.py,sha256=5ua0AU2cfisyD79dosP-POF3w0FRH2I5Wko_PPKLWqU,2153
272
- flwr_nightly-1.10.0.dev20240714.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
273
- flwr_nightly-1.10.0.dev20240714.dist-info/METADATA,sha256=9aI10DHtz4gZbec9re5g_yizKlPUjDQptMpkYOShtHE,15632
274
- flwr_nightly-1.10.0.dev20240714.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
275
- flwr_nightly-1.10.0.dev20240714.dist-info/entry_points.txt,sha256=7qBQcA-bDGDxnJmLd9FYqglFQubjCNqyg9M8a-lukps,336
276
- flwr_nightly-1.10.0.dev20240714.dist-info/RECORD,,
272
+ flwr/superexec/simulation.py,sha256=flYwBU9lL3WV2jfGgp6bCjRNRlxmZ-ajkEHXab6HSug,5104
273
+ flwr_nightly-1.10.0.dev20240715.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
274
+ flwr_nightly-1.10.0.dev20240715.dist-info/METADATA,sha256=23pOsDu0OCNNq2lJKcXgdhg__CcotuXHOKOYTO9js04,15632
275
+ flwr_nightly-1.10.0.dev20240715.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
276
+ flwr_nightly-1.10.0.dev20240715.dist-info/entry_points.txt,sha256=7qBQcA-bDGDxnJmLd9FYqglFQubjCNqyg9M8a-lukps,336
277
+ flwr_nightly-1.10.0.dev20240715.dist-info/RECORD,,