flwr-nightly 1.10.0.dev20240715__py3-none-any.whl → 1.10.0.dev20240717__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.

Files changed (37) hide show
  1. flwr/cli/build.py +16 -2
  2. flwr/cli/config_utils.py +23 -15
  3. flwr/cli/install.py +17 -1
  4. flwr/cli/new/new.py +4 -3
  5. flwr/cli/new/templates/app/code/flwr_tune/app.py.tpl +2 -2
  6. flwr/cli/new/templates/app/code/flwr_tune/server.py.tpl +1 -1
  7. flwr/cli/new/templates/app/code/server.hf.py.tpl +4 -1
  8. flwr/cli/new/templates/app/code/server.jax.py.tpl +4 -1
  9. flwr/cli/new/templates/app/code/server.mlx.py.tpl +4 -1
  10. flwr/cli/new/templates/app/code/server.numpy.py.tpl +4 -1
  11. flwr/cli/new/templates/app/code/server.pytorch.py.tpl +4 -1
  12. flwr/cli/new/templates/app/code/server.sklearn.py.tpl +4 -1
  13. flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +4 -1
  14. flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +6 -3
  15. flwr/cli/new/templates/app/pyproject.hf.toml.tpl +8 -5
  16. flwr/cli/new/templates/app/pyproject.jax.toml.tpl +8 -5
  17. flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +8 -5
  18. flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +8 -5
  19. flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +8 -5
  20. flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +8 -5
  21. flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +8 -5
  22. flwr/cli/run/run.py +29 -15
  23. flwr/client/app.py +3 -3
  24. flwr/client/node_state.py +17 -3
  25. flwr/client/supernode/app.py +4 -4
  26. flwr/common/config.py +28 -17
  27. flwr/server/run_serverapp.py +1 -1
  28. flwr/server/superlink/fleet/vce/vce_api.py +10 -3
  29. flwr/simulation/run_simulation.py +148 -14
  30. flwr/superexec/app.py +1 -1
  31. flwr/superexec/deployment.py +1 -9
  32. flwr/superexec/simulation.py +8 -18
  33. {flwr_nightly-1.10.0.dev20240715.dist-info → flwr_nightly-1.10.0.dev20240717.dist-info}/METADATA +2 -1
  34. {flwr_nightly-1.10.0.dev20240715.dist-info → flwr_nightly-1.10.0.dev20240717.dist-info}/RECORD +37 -37
  35. {flwr_nightly-1.10.0.dev20240715.dist-info → flwr_nightly-1.10.0.dev20240717.dist-info}/LICENSE +0 -0
  36. {flwr_nightly-1.10.0.dev20240715.dist-info → flwr_nightly-1.10.0.dev20240717.dist-info}/WHEEL +0 -0
  37. {flwr_nightly-1.10.0.dev20240715.dist-info → flwr_nightly-1.10.0.dev20240717.dist-info}/entry_points.txt +0 -0
flwr/cli/build.py CHANGED
@@ -20,6 +20,7 @@ from pathlib import Path
20
20
  from typing import Optional
21
21
 
22
22
  import pathspec
23
+ import tomli_w
23
24
  import typer
24
25
  from typing_extensions import Annotated
25
26
 
@@ -85,7 +86,7 @@ def build(
85
86
 
86
87
  # Set the name of the zip file
87
88
  fab_filename = (
88
- f"{conf['tool']['flwr']['publisher']}"
89
+ f"{conf['tool']['flwr']['app']['publisher']}"
89
90
  f".{directory.name}"
90
91
  f".{conf['project']['version'].replace('.', '-')}.fab"
91
92
  )
@@ -93,15 +94,28 @@ def build(
93
94
 
94
95
  allowed_extensions = {".py", ".toml", ".md"}
95
96
 
97
+ # Remove the 'federations' field from 'tool.flwr' if it exists
98
+ if (
99
+ "tool" in conf
100
+ and "flwr" in conf["tool"]
101
+ and "federations" in conf["tool"]["flwr"]
102
+ ):
103
+ del conf["tool"]["flwr"]["federations"]
104
+
105
+ toml_contents = tomli_w.dumps(conf)
106
+
96
107
  with zipfile.ZipFile(fab_filename, "w", zipfile.ZIP_DEFLATED) as fab_file:
108
+ fab_file.writestr("pyproject.toml", toml_contents)
109
+
110
+ # Continue with adding other files
97
111
  for root, _, files in os.walk(directory, topdown=True):
98
- # Filter directories and files based on .gitignore
99
112
  files = [
100
113
  f
101
114
  for f in files
102
115
  if not ignore_spec.match_file(Path(root) / f)
103
116
  and f != fab_filename
104
117
  and Path(f).suffix in allowed_extensions
118
+ and f != "pyproject.toml" # Exclude the original pyproject.toml
105
119
  ]
106
120
 
107
121
  for file in files:
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['tool']['flwr']['publisher']}/{conf['project']['name']}",
63
+ f"{conf['tool']['flwr']['app']['publisher']}/{conf['project']['name']}",
64
64
  )
65
65
 
66
66
 
@@ -136,20 +136,28 @@ 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 "tool" not in config or "flwr" not in config["tool"]:
140
- errors.append("Missing [tool.flwr] section")
139
+ if (
140
+ "tool" not in config
141
+ or "flwr" not in config["tool"]
142
+ or "app" not in config["tool"]["flwr"]
143
+ ):
144
+ errors.append("Missing [tool.flwr.app] section")
141
145
  else:
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")
146
+ if "publisher" not in config["tool"]["flwr"]["app"]:
147
+ errors.append('Property "publisher" missing in [tool.flwr.app]')
148
+ if "config" in config["tool"]["flwr"]["app"]:
149
+ _validate_run_config(config["tool"]["flwr"]["app"]["config"], errors)
150
+ if "components" not in config["tool"]["flwr"]["app"]:
151
+ errors.append("Missing [tool.flwr.app.components] section")
148
152
  else:
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
+ if "serverapp" not in config["tool"]["flwr"]["app"]["components"]:
154
+ errors.append(
155
+ 'Property "serverapp" missing in [tool.flwr.app.components]'
156
+ )
157
+ if "clientapp" not in config["tool"]["flwr"]["app"]["components"]:
158
+ errors.append(
159
+ 'Property "clientapp" missing in [tool.flwr.app.components]'
160
+ )
153
161
 
154
162
  return len(errors) == 0, errors, warnings
155
163
 
@@ -165,14 +173,14 @@ def validate(
165
173
 
166
174
  # Validate serverapp
167
175
  is_valid, reason = object_ref.validate(
168
- config["tool"]["flwr"]["components"]["serverapp"], check_module
176
+ config["tool"]["flwr"]["app"]["components"]["serverapp"], check_module
169
177
  )
170
178
  if not is_valid and isinstance(reason, str):
171
179
  return False, [reason], []
172
180
 
173
181
  # Validate clientapp
174
182
  is_valid, reason = object_ref.validate(
175
- config["tool"]["flwr"]["components"]["clientapp"], check_module
183
+ config["tool"]["flwr"]["app"]["components"]["clientapp"], check_module
176
184
  )
177
185
 
178
186
  if not is_valid and isinstance(reason, str):
flwr/cli/install.py CHANGED
@@ -16,6 +16,7 @@
16
16
 
17
17
 
18
18
  import shutil
19
+ import subprocess
19
20
  import tempfile
20
21
  import zipfile
21
22
  from io import BytesIO
@@ -149,7 +150,7 @@ def validate_and_install(
149
150
  )
150
151
  raise typer.Exit(code=1)
151
152
 
152
- publisher = config["tool"]["flwr"]["publisher"]
153
+ publisher = config["tool"]["flwr"]["app"]["publisher"]
153
154
  project_name = config["project"]["name"]
154
155
  version = config["project"]["version"]
155
156
 
@@ -192,6 +193,21 @@ def validate_and_install(
192
193
  else:
193
194
  shutil.copy2(item, install_dir / item.name)
194
195
 
196
+ try:
197
+ subprocess.run(
198
+ ["pip", "install", "-e", install_dir, "--no-deps"],
199
+ capture_output=True,
200
+ text=True,
201
+ check=True,
202
+ )
203
+ except subprocess.CalledProcessError as e:
204
+ typer.secho(
205
+ f"❌ Failed to `pip install` package(s) from {install_dir}:\n{e.stderr}",
206
+ fg=typer.colors.RED,
207
+ bold=True,
208
+ )
209
+ raise typer.Exit(code=1) from e
210
+
195
211
  typer.secho(
196
212
  f"🎊 Successfully installed {project_name} to {install_dir}.",
197
213
  fg=typer.colors.GREEN,
flwr/cli/new/new.py CHANGED
@@ -136,6 +136,7 @@ def new(
136
136
 
137
137
  framework_str = framework_str.lower()
138
138
 
139
+ llm_challenge_str = None
139
140
  if framework_str == "flowertune":
140
141
  llm_challenge_value = prompt_options(
141
142
  "Please select LLM challenge by typing in the number",
@@ -171,7 +172,7 @@ def new(
171
172
  }
172
173
 
173
174
  # List of files to render
174
- if framework_str == "flowertune":
175
+ if llm_challenge_str:
175
176
  files = {
176
177
  ".gitignore": {"template": "app/.gitignore.tpl"},
177
178
  "pyproject.toml": {"template": f"app/pyproject.{framework_str}.toml.tpl"},
@@ -228,10 +229,10 @@ def new(
228
229
  "README.md": {"template": "app/README.md.tpl"},
229
230
  "pyproject.toml": {"template": f"app/pyproject.{framework_str}.toml.tpl"},
230
231
  f"{import_name}/__init__.py": {"template": "app/code/__init__.py.tpl"},
231
- f"{import_name}/server.py": {
232
+ f"{import_name}/server_app.py": {
232
233
  "template": f"app/code/server.{framework_str}.py.tpl"
233
234
  },
234
- f"{import_name}/client.py": {
235
+ f"{import_name}/client_app.py": {
235
236
  "template": f"app/code/client.{framework_str}.py.tpl"
236
237
  },
237
238
  }
@@ -12,10 +12,10 @@ from flwr.client import ClientApp
12
12
  from flwr.common import ndarrays_to_parameters
13
13
  from flwr.server import ServerApp, ServerConfig
14
14
 
15
- from $import_name.client import gen_client_fn, get_parameters
15
+ from $import_name.client_app import gen_client_fn, get_parameters
16
16
  from $import_name.dataset import get_tokenizer_and_data_collator_and_propt_formatting
17
17
  from $import_name.models import get_model
18
- from $import_name.server import fit_weighted_average, get_evaluate_fn, get_on_fit_config
18
+ from $import_name.server_app import fit_weighted_average, get_evaluate_fn, get_on_fit_config
19
19
 
20
20
  # Avoid warnings
21
21
  warnings.filterwarnings("ignore", category=UserWarning)
@@ -1,6 +1,6 @@
1
1
  """$project_name: A Flower / FlowerTune app."""
2
2
 
3
- from $import_name.client import set_parameters
3
+ from $import_name.client_app import set_parameters
4
4
  from $import_name.models import get_model
5
5
 
6
6
 
@@ -6,12 +6,15 @@ from flwr.server import ServerApp, ServerAppComponents, ServerConfig
6
6
 
7
7
 
8
8
  def server_fn(context: Context):
9
+ # Read from config
10
+ num_rounds = int(context.run_config["num-server-rounds"])
11
+
9
12
  # Define strategy
10
13
  strategy = FedAvg(
11
14
  fraction_fit=1.0,
12
15
  fraction_evaluate=1.0,
13
16
  )
14
- config = ServerConfig(num_rounds=3)
17
+ config = ServerConfig(num_rounds=num_rounds)
15
18
 
16
19
  return ServerAppComponents(strategy=strategy, config=config)
17
20
 
@@ -6,9 +6,12 @@ from flwr.server import ServerApp, ServerAppComponents, ServerConfig
6
6
 
7
7
 
8
8
  def server_fn(context: Context):
9
+ # Read from config
10
+ num_rounds = int(context.run_config["num-server-rounds"])
11
+
9
12
  # Define strategy
10
13
  strategy = FedAvg()
11
- config = ServerConfig(num_rounds=3)
14
+ config = ServerConfig(num_rounds=num_rounds)
12
15
 
13
16
  return ServerAppComponents(strategy=strategy, config=config)
14
17
 
@@ -6,9 +6,12 @@ from flwr.server.strategy import FedAvg
6
6
 
7
7
 
8
8
  def server_fn(context: Context):
9
+ # Read from config
10
+ num_rounds = int(context.run_config["num-server-rounds"])
11
+
9
12
  # Define strategy
10
13
  strategy = FedAvg()
11
- config = ServerConfig(num_rounds=3)
14
+ config = ServerConfig(num_rounds=num_rounds)
12
15
 
13
16
  return ServerAppComponents(strategy=strategy, config=config)
14
17
 
@@ -6,9 +6,12 @@ from flwr.server.strategy import FedAvg
6
6
 
7
7
 
8
8
  def server_fn(context: Context):
9
+ # Read from config
10
+ num_rounds = int(context.run_config["num-server-rounds"])
11
+
9
12
  # Define strategy
10
13
  strategy = FedAvg()
11
- config = ServerConfig(num_rounds=3)
14
+ config = ServerConfig(num_rounds=num_rounds)
12
15
 
13
16
  return ServerAppComponents(strategy=strategy, config=config)
14
17
 
@@ -12,6 +12,9 @@ ndarrays = get_weights(Net())
12
12
  parameters = ndarrays_to_parameters(ndarrays)
13
13
 
14
14
  def server_fn(context: Context):
15
+ # Read from config
16
+ num_rounds = int(context.run_config["num-server-rounds"])
17
+
15
18
  # Define strategy
16
19
  strategy = FedAvg(
17
20
  fraction_fit=1.0,
@@ -19,7 +22,7 @@ def server_fn(context: Context):
19
22
  min_available_clients=2,
20
23
  initial_parameters=parameters,
21
24
  )
22
- config = ServerConfig(num_rounds=3)
25
+ config = ServerConfig(num_rounds=num_rounds)
23
26
 
24
27
  return ServerAppComponents(strategy=strategy, config=config)
25
28
 
@@ -6,13 +6,16 @@ from flwr.server.strategy import FedAvg
6
6
 
7
7
 
8
8
  def server_fn(context: Context):
9
+ # Read from config
10
+ num_rounds = int(context.run_config["num-server-rounds"])
11
+
9
12
  # Define strategy
10
13
  strategy = FedAvg(
11
14
  fraction_fit=1.0,
12
15
  fraction_evaluate=1.0,
13
16
  min_available_clients=2,
14
17
  )
15
- config = ServerConfig(num_rounds=3)
18
+ config = ServerConfig(num_rounds=num_rounds)
16
19
 
17
20
  return ServerAppComponents(strategy=strategy, config=config)
18
21
 
@@ -12,6 +12,9 @@ config = ServerConfig(num_rounds=3)
12
12
  parameters = ndarrays_to_parameters(load_model().get_weights())
13
13
 
14
14
  def server_fn(context: Context):
15
+ # Read from config
16
+ num_rounds = int(context.run_config["num-server-rounds"])
17
+
15
18
  # Define strategy
16
19
  strategy = strategy = FedAvg(
17
20
  fraction_fit=1.0,
@@ -19,7 +22,7 @@ def server_fn(context: Context):
19
22
  min_available_clients=2,
20
23
  initial_parameters=parameters,
21
24
  )
22
- config = ServerConfig(num_rounds=3)
25
+ config = ServerConfig(num_rounds=num_rounds)
23
26
 
24
27
  return ServerAppComponents(strategy=strategy, config=config)
25
28
 
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
6
6
  name = "$package_name"
7
7
  version = "1.0.0"
8
8
  description = ""
9
- license = { text = "Apache License (2.0)" }
9
+ license = "Apache-2.0"
10
10
  dependencies = [
11
11
  "flwr[simulation]>=1.9.0,<2.0",
12
12
  "flwr-datasets>=0.1.0,<1.0.0",
@@ -22,13 +22,16 @@ dependencies = [
22
22
  [tool.hatch.build.targets.wheel]
23
23
  packages = ["."]
24
24
 
25
- [tool.flwr]
25
+ [tool.flwr.app]
26
26
  publisher = "$username"
27
27
 
28
- [tool.flwr.components]
28
+ [tool.flwr.app.components]
29
29
  serverapp = "$import_name.app:server"
30
30
  clientapp = "$import_name.app:client"
31
31
 
32
+ [tool.flwr.app.config]
33
+ num-server-rounds = "3"
34
+
32
35
  [tool.flwr.federations]
33
36
  default = "localhost"
34
37
 
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
6
6
  name = "$package_name"
7
7
  version = "1.0.0"
8
8
  description = ""
9
- license = { text = "Apache License (2.0)" }
9
+ license = "Apache-2.0"
10
10
  dependencies = [
11
11
  "flwr[simulation]>=1.9.0,<2.0",
12
12
  "flwr-datasets>=0.0.2,<1.0.0",
@@ -20,12 +20,15 @@ dependencies = [
20
20
  [tool.hatch.build.targets.wheel]
21
21
  packages = ["."]
22
22
 
23
- [tool.flwr]
23
+ [tool.flwr.app]
24
24
  publisher = "$username"
25
25
 
26
- [tool.flwr.components]
27
- serverapp = "$import_name.server:app"
28
- clientapp = "$import_name.client:app"
26
+ [tool.flwr.app.components]
27
+ serverapp = "$import_name.server_app:app"
28
+ clientapp = "$import_name.client_app:app"
29
+
30
+ [tool.flwr.app.config]
31
+ num-server-rounds = "3"
29
32
 
30
33
  [tool.flwr.federations]
31
34
  default = "localhost"
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
6
6
  name = "$package_name"
7
7
  version = "1.0.0"
8
8
  description = ""
9
- license = {text = "Apache License (2.0)"}
9
+ license = "Apache-2.0"
10
10
  dependencies = [
11
11
  "flwr[simulation]>=1.9.0,<2.0",
12
12
  "jax==0.4.26",
@@ -17,12 +17,15 @@ dependencies = [
17
17
  [tool.hatch.build.targets.wheel]
18
18
  packages = ["."]
19
19
 
20
- [tool.flwr]
20
+ [tool.flwr.app]
21
21
  publisher = "$username"
22
22
 
23
- [tool.flwr.components]
24
- serverapp = "$import_name.server:app"
25
- clientapp = "$import_name.client:app"
23
+ [tool.flwr.app.components]
24
+ serverapp = "$import_name.server_app:app"
25
+ clientapp = "$import_name.client_app:app"
26
+
27
+ [tool.flwr.app.config]
28
+ num-server-rounds = "3"
26
29
 
27
30
  [tool.flwr.federations]
28
31
  default = "localhost"
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
6
6
  name = "$package_name"
7
7
  version = "1.0.0"
8
8
  description = ""
9
- license = { text = "Apache License (2.0)" }
9
+ license = "Apache-2.0"
10
10
  dependencies = [
11
11
  "flwr[simulation]>=1.9.0,<2.0",
12
12
  "flwr-datasets[vision]>=0.0.2,<1.0.0",
@@ -17,12 +17,15 @@ dependencies = [
17
17
  [tool.hatch.build.targets.wheel]
18
18
  packages = ["."]
19
19
 
20
- [tool.flwr]
20
+ [tool.flwr.app]
21
21
  publisher = "$username"
22
22
 
23
- [tool.flwr.components]
24
- serverapp = "$import_name.server:app"
25
- clientapp = "$import_name.client:app"
23
+ [tool.flwr.app.components]
24
+ serverapp = "$import_name.server_app:app"
25
+ clientapp = "$import_name.client_app:app"
26
+
27
+ [tool.flwr.app.config]
28
+ num-server-rounds = "3"
26
29
 
27
30
  [tool.flwr.federations]
28
31
  default = "localhost"
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
6
6
  name = "$package_name"
7
7
  version = "1.0.0"
8
8
  description = ""
9
- license = { text = "Apache License (2.0)" }
9
+ license = "Apache-2.0"
10
10
  dependencies = [
11
11
  "flwr[simulation]>=1.9.0,<2.0",
12
12
  "numpy>=1.21.0",
@@ -15,12 +15,15 @@ dependencies = [
15
15
  [tool.hatch.build.targets.wheel]
16
16
  packages = ["."]
17
17
 
18
- [tool.flwr]
18
+ [tool.flwr.app]
19
19
  publisher = "$username"
20
20
 
21
- [tool.flwr.components]
22
- serverapp = "$import_name.server:app"
23
- clientapp = "$import_name.client:app"
21
+ [tool.flwr.app.components]
22
+ serverapp = "$import_name.server_app:app"
23
+ clientapp = "$import_name.client_app:app"
24
+
25
+ [tool.flwr.app.config]
26
+ num-server-rounds = "3"
24
27
 
25
28
  [tool.flwr.federations]
26
29
  default = "localhost"
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
6
6
  name = "$package_name"
7
7
  version = "1.0.0"
8
8
  description = ""
9
- license = { text = "Apache License (2.0)" }
9
+ license = "Apache-2.0"
10
10
  dependencies = [
11
11
  "flwr[simulation]>=1.9.0,<2.0",
12
12
  "flwr-datasets[vision]>=0.0.2,<1.0.0",
@@ -17,12 +17,15 @@ dependencies = [
17
17
  [tool.hatch.build.targets.wheel]
18
18
  packages = ["."]
19
19
 
20
- [tool.flwr]
20
+ [tool.flwr.app]
21
21
  publisher = "$username"
22
22
 
23
- [tool.flwr.components]
24
- serverapp = "$import_name.server:app"
25
- clientapp = "$import_name.client:app"
23
+ [tool.flwr.app.components]
24
+ serverapp = "$import_name.server_app:app"
25
+ clientapp = "$import_name.client_app:app"
26
+
27
+ [tool.flwr.app.config]
28
+ num-server-rounds = "3"
26
29
 
27
30
  [tool.flwr.federations]
28
31
  default = "localhost"
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
6
6
  name = "$package_name"
7
7
  version = "1.0.0"
8
8
  description = ""
9
- license = { text = "Apache License (2.0)" }
9
+ license = "Apache-2.0"
10
10
  dependencies = [
11
11
  "flwr[simulation]>=1.9.0,<2.0",
12
12
  "flwr-datasets[vision]>=0.0.2,<1.0.0",
@@ -16,12 +16,15 @@ dependencies = [
16
16
  [tool.hatch.build.targets.wheel]
17
17
  packages = ["."]
18
18
 
19
- [tool.flwr]
19
+ [tool.flwr.app]
20
20
  publisher = "$username"
21
21
 
22
- [tool.flwr.components]
23
- serverapp = "$import_name.server:app"
24
- clientapp = "$import_name.client:app"
22
+ [tool.flwr.app.components]
23
+ serverapp = "$import_name.server_app:app"
24
+ clientapp = "$import_name.client_app:app"
25
+
26
+ [tool.flwr.app.config]
27
+ num-server-rounds = "3"
25
28
 
26
29
  [tool.flwr.federations]
27
30
  default = "localhost"
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
6
6
  name = "$package_name"
7
7
  version = "1.0.0"
8
8
  description = ""
9
- license = { text = "Apache License (2.0)" }
9
+ license = "Apache-2.0"
10
10
  dependencies = [
11
11
  "flwr[simulation]>=1.9.0,<2.0",
12
12
  "flwr-datasets[vision]>=0.0.2,<1.0.0",
@@ -16,12 +16,15 @@ dependencies = [
16
16
  [tool.hatch.build.targets.wheel]
17
17
  packages = ["."]
18
18
 
19
- [tool.flwr]
19
+ [tool.flwr.app]
20
20
  publisher = "$username"
21
21
 
22
- [tool.flwr.components]
23
- serverapp = "$import_name.server:app"
24
- clientapp = "$import_name.client:app"
22
+ [tool.flwr.app.components]
23
+ serverapp = "$import_name.server_app:app"
24
+ clientapp = "$import_name.client_app:app"
25
+
26
+ [tool.flwr.app.config]
27
+ num-server-rounds = "3"
25
28
 
26
29
  [tool.flwr.federations]
27
30
  default = "localhost"
flwr/cli/run/run.py CHANGED
@@ -14,10 +14,11 @@
14
14
  # ==============================================================================
15
15
  """Flower command line interface `run` command."""
16
16
 
17
+ import subprocess
17
18
  import sys
18
19
  from logging import DEBUG
19
20
  from pathlib import Path
20
- from typing import Any, Dict, Optional
21
+ from typing import Any, Dict, List, Optional
21
22
 
22
23
  import typer
23
24
  from typing_extensions import Annotated
@@ -29,7 +30,6 @@ from flwr.common.grpc import GRPC_MAX_MESSAGE_LENGTH, create_channel
29
30
  from flwr.common.logger import log
30
31
  from flwr.proto.exec_pb2 import StartRunRequest # pylint: disable=E0611
31
32
  from flwr.proto.exec_pb2_grpc import ExecStub
32
- from flwr.simulation.run_simulation import _run_simulation
33
33
 
34
34
 
35
35
  # pylint: disable-next=too-many-locals
@@ -43,7 +43,7 @@ def run(
43
43
  typer.Argument(help="Name of the federation to run the app on"),
44
44
  ] = None,
45
45
  config_overrides: Annotated[
46
- Optional[str],
46
+ Optional[List[str]],
47
47
  typer.Option(
48
48
  "--run-config",
49
49
  "-c",
@@ -94,11 +94,13 @@ def run(
94
94
  # Validate the federation exists in the configuration
95
95
  federation = config["tool"]["flwr"]["federations"].get(federation_name)
96
96
  if federation is None:
97
- available_feds = list(config["tool"]["flwr"]["federations"])
97
+ available_feds = {
98
+ fed for fed in config["tool"]["flwr"]["federations"] if fed != "default"
99
+ }
98
100
  typer.secho(
99
101
  f"❌ There is no `{federation_name}` federation declared in the "
100
102
  "`pyproject.toml`.\n The following federations were found:\n\n"
101
- "\n".join(available_feds) + "\n\n",
103
+ + "\n".join(available_feds),
102
104
  fg=typer.colors.RED,
103
105
  bold=True,
104
106
  )
@@ -107,13 +109,13 @@ def run(
107
109
  if "address" in federation:
108
110
  _run_with_superexec(federation, directory, config_overrides)
109
111
  else:
110
- _run_without_superexec(config, federation, federation_name)
112
+ _run_without_superexec(directory, federation, federation_name, config_overrides)
111
113
 
112
114
 
113
115
  def _run_with_superexec(
114
116
  federation: Dict[str, str],
115
117
  directory: Optional[Path],
116
- config_overrides: Optional[str],
118
+ config_overrides: Optional[List[str]],
117
119
  ) -> None:
118
120
 
119
121
  def on_channel_state_change(channel_connectivity: str) -> None:
@@ -169,11 +171,11 @@ def _run_with_superexec(
169
171
 
170
172
 
171
173
  def _run_without_superexec(
172
- config: Dict[str, Any], federation: Dict[str, Any], federation_name: str
174
+ app_path: Optional[Path],
175
+ federation: Dict[str, Any],
176
+ federation_name: str,
177
+ config_overrides: Optional[List[str]],
173
178
  ) -> None:
174
- server_app_ref = config["tool"]["flwr"]["components"]["serverapp"]
175
- client_app_ref = config["tool"]["flwr"]["components"]["clientapp"]
176
-
177
179
  try:
178
180
  num_supernodes = federation["options"]["num-supernodes"]
179
181
  except KeyError as err:
@@ -188,8 +190,20 @@ def _run_without_superexec(
188
190
  )
189
191
  raise typer.Exit(code=1) from err
190
192
 
191
- _run_simulation(
192
- server_app_attr=server_app_ref,
193
- client_app_attr=client_app_ref,
194
- num_supernodes=num_supernodes,
193
+ command = [
194
+ "flower-simulation",
195
+ "--app",
196
+ f"{app_path}",
197
+ "--num-supernodes",
198
+ f"{num_supernodes}",
199
+ ]
200
+
201
+ if config_overrides:
202
+ command.extend(["--run-config", f"{config_overrides}"])
203
+
204
+ # Run the simulation
205
+ subprocess.run(
206
+ command,
207
+ check=True,
208
+ text=True,
195
209
  )