flwr-nightly 1.14.0.dev20241214__py3-none-any.whl → 1.14.0.dev20241216__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/log.py CHANGED
@@ -170,7 +170,7 @@ def _log_with_exec_api(
170
170
  run_id: int,
171
171
  stream: bool,
172
172
  ) -> None:
173
- auth_plugin = try_obtain_cli_auth_plugin(app, federation, federation_config)
173
+ auth_plugin = try_obtain_cli_auth_plugin(app, federation)
174
174
  channel = init_channel(app, federation_config, auth_plugin)
175
175
 
176
176
  if stream:
flwr/cli/login/login.py CHANGED
@@ -65,9 +65,7 @@ def login( # pylint: disable=R0914
65
65
 
66
66
  # Get the auth plugin
67
67
  auth_type = login_response.login_details.get(AUTH_TYPE)
68
- auth_plugin = try_obtain_cli_auth_plugin(
69
- app, federation, federation_config, auth_type
70
- )
68
+ auth_plugin = try_obtain_cli_auth_plugin(app, federation, auth_type)
71
69
  if auth_plugin is None:
72
70
  typer.secho(
73
71
  f'❌ Authentication type "{auth_type}" not found',
flwr/cli/ls.py CHANGED
@@ -19,13 +19,12 @@ import io
19
19
  import json
20
20
  from datetime import datetime, timedelta
21
21
  from pathlib import Path
22
- from typing import Annotated, Optional, Union
22
+ from typing import Annotated, Optional
23
23
 
24
24
  import typer
25
25
  from rich.console import Console
26
26
  from rich.table import Table
27
27
  from rich.text import Text
28
- from typer import Exit
29
28
 
30
29
  from flwr.cli.config_utils import (
31
30
  exit_if_no_address,
@@ -35,7 +34,7 @@ from flwr.cli.config_utils import (
35
34
  )
36
35
  from flwr.common.constant import FAB_CONFIG_FILE, CliOutputFormat, SubStatus
37
36
  from flwr.common.date import format_timedelta, isoformat8601_utc
38
- from flwr.common.logger import redirect_output, remove_emojis, restore_output
37
+ from flwr.common.logger import print_json_error, redirect_output, restore_output
39
38
  from flwr.common.serde import run_from_proto
40
39
  from flwr.common.typing import Run
41
40
  from flwr.proto.exec_pb2 import ( # pylint: disable=E0611
@@ -81,7 +80,20 @@ def ls( # pylint: disable=too-many-locals, too-many-branches
81
80
  ),
82
81
  ] = CliOutputFormat.DEFAULT,
83
82
  ) -> None:
84
- """List runs."""
83
+ """List the details of one provided run ID or all runs in a Flower federation.
84
+
85
+ The following details are displayed:
86
+
87
+ - **Run ID:** Unique identifier for the run.
88
+ - **FAB:** Name of the FAB associated with the run (``{FAB_ID} (v{FAB_VERSION})``).
89
+ - **Status:** Current status of the run (pending, starting, running, finished).
90
+ - **Elapsed:** Time elapsed since the run started (``HH:MM:SS``).
91
+ - **Created At:** Timestamp when the run was created.
92
+ - **Running At:** Timestamp when the run started running.
93
+ - **Finished At:** Timestamp when the run finished.
94
+
95
+ All timestamps follow ISO 8601, UTC and are formatted as ``YYYY-MM-DD HH:MM:SSZ``.
96
+ """
85
97
  suppress_output = output_format == CliOutputFormat.JSON
86
98
  captured_output = io.StringIO()
87
99
  try:
@@ -104,7 +116,7 @@ def ls( # pylint: disable=too-many-locals, too-many-branches
104
116
  raise ValueError(
105
117
  "The options '--runs' and '--run-id' are mutually exclusive."
106
118
  )
107
- auth_plugin = try_obtain_cli_auth_plugin(app, federation, federation_config)
119
+ auth_plugin = try_obtain_cli_auth_plugin(app, federation)
108
120
  channel = init_channel(app, federation_config, auth_plugin)
109
121
  stub = ExecStub(channel)
110
122
 
@@ -132,7 +144,7 @@ def ls( # pylint: disable=too-many-locals, too-many-branches
132
144
  if suppress_output:
133
145
  restore_output()
134
146
  e_message = captured_output.getvalue()
135
- _print_json_error(e_message, err)
147
+ print_json_error(e_message, err)
136
148
  else:
137
149
  typer.secho(
138
150
  f"{err}",
@@ -310,15 +322,3 @@ def _display_one_run(
310
322
  Console().print_json(_to_json(formatted_runs))
311
323
  else:
312
324
  Console().print(_to_table(formatted_runs))
313
-
314
-
315
- def _print_json_error(msg: str, e: Union[Exit, Exception]) -> None:
316
- """Print error message as JSON."""
317
- Console().print_json(
318
- json.dumps(
319
- {
320
- "success": False,
321
- "error-message": remove_emojis(str(msg) + "\n" + str(e)),
322
- }
323
- )
324
- )
flwr/cli/run/run.py CHANGED
@@ -19,7 +19,7 @@ import io
19
19
  import json
20
20
  import subprocess
21
21
  from pathlib import Path
22
- from typing import Annotated, Any, Optional, Union
22
+ from typing import Annotated, Any, Optional
23
23
 
24
24
  import typer
25
25
  from rich.console import Console
@@ -37,7 +37,7 @@ from flwr.common.config import (
37
37
  user_config_to_configsrecord,
38
38
  )
39
39
  from flwr.common.constant import CliOutputFormat
40
- from flwr.common.logger import redirect_output, remove_emojis, restore_output
40
+ from flwr.common.logger import print_json_error, redirect_output, restore_output
41
41
  from flwr.common.serde import (
42
42
  configs_record_to_proto,
43
43
  fab_to_proto,
@@ -122,7 +122,7 @@ def run(
122
122
  if suppress_output:
123
123
  restore_output()
124
124
  e_message = captured_output.getvalue()
125
- _print_json_error(e_message, err)
125
+ print_json_error(e_message, err)
126
126
  else:
127
127
  typer.secho(
128
128
  f"{err}",
@@ -144,7 +144,7 @@ def _run_with_exec_api(
144
144
  stream: bool,
145
145
  output_format: str,
146
146
  ) -> None:
147
- auth_plugin = try_obtain_cli_auth_plugin(app, federation, federation_config)
147
+ auth_plugin = try_obtain_cli_auth_plugin(app, federation)
148
148
  channel = init_channel(app, federation_config, auth_plugin)
149
149
  stub = ExecStub(channel)
150
150
 
@@ -239,15 +239,3 @@ def _run_without_exec_api(
239
239
  check=True,
240
240
  text=True,
241
241
  )
242
-
243
-
244
- def _print_json_error(msg: str, e: Union[typer.Exit, Exception]) -> None:
245
- """Print error message as JSON."""
246
- Console().print_json(
247
- json.dumps(
248
- {
249
- "success": False,
250
- "error-message": remove_emojis(str(msg) + "\n" + str(e)),
251
- }
252
- )
253
- )
flwr/cli/stop.py CHANGED
@@ -15,10 +15,13 @@
15
15
  """Flower command line interface `stop` command."""
16
16
 
17
17
 
18
+ import io
19
+ import json
18
20
  from pathlib import Path
19
21
  from typing import Annotated, Optional
20
22
 
21
23
  import typer
24
+ from rich.console import Console
22
25
 
23
26
  from flwr.cli.config_utils import (
24
27
  exit_if_no_address,
@@ -26,14 +29,15 @@ from flwr.cli.config_utils import (
26
29
  process_loaded_project_config,
27
30
  validate_federation_in_project_config,
28
31
  )
29
- from flwr.common.constant import FAB_CONFIG_FILE
32
+ from flwr.common.constant import FAB_CONFIG_FILE, CliOutputFormat
33
+ from flwr.common.logger import print_json_error, redirect_output, restore_output
30
34
  from flwr.proto.exec_pb2 import StopRunRequest, StopRunResponse # pylint: disable=E0611
31
35
  from flwr.proto.exec_pb2_grpc import ExecStub
32
36
 
33
37
  from .utils import init_channel, try_obtain_cli_auth_plugin
34
38
 
35
39
 
36
- def stop(
40
+ def stop( # pylint: disable=R0914
37
41
  run_id: Annotated[ # pylint: disable=unused-argument
38
42
  int,
39
43
  typer.Argument(help="The Flower run ID to stop"),
@@ -46,46 +50,80 @@ def stop(
46
50
  Optional[str],
47
51
  typer.Argument(help="Name of the federation"),
48
52
  ] = None,
53
+ output_format: Annotated[
54
+ str,
55
+ typer.Option(
56
+ "--format",
57
+ case_sensitive=False,
58
+ help="Format output using 'default' view or 'json'",
59
+ ),
60
+ ] = CliOutputFormat.DEFAULT,
49
61
  ) -> None:
50
62
  """Stop a run."""
51
- # Load and validate federation config
52
- typer.secho("Loading project configuration... ", fg=typer.colors.BLUE)
53
-
54
- pyproject_path = app / FAB_CONFIG_FILE if app else None
55
- config, errors, warnings = load_and_validate(path=pyproject_path)
56
- config = process_loaded_project_config(config, errors, warnings)
57
- federation, federation_config = validate_federation_in_project_config(
58
- federation, config
59
- )
60
- exit_if_no_address(federation_config, "stop")
61
-
63
+ suppress_output = output_format == CliOutputFormat.JSON
64
+ captured_output = io.StringIO()
62
65
  try:
63
- auth_plugin = try_obtain_cli_auth_plugin(app, federation, federation_config)
64
- channel = init_channel(app, federation_config, auth_plugin)
65
- stub = ExecStub(channel) # pylint: disable=unused-variable # noqa: F841
66
+ if suppress_output:
67
+ redirect_output(captured_output)
66
68
 
67
- typer.secho(f"✋ Stopping run ID {run_id}...", fg=typer.colors.GREEN)
68
- _stop_run(stub, run_id=run_id)
69
+ # Load and validate federation config
70
+ typer.secho("Loading project configuration... ", fg=typer.colors.BLUE)
69
71
 
70
- except ValueError as err:
71
- typer.secho(
72
- f"❌ {err}",
73
- fg=typer.colors.RED,
74
- bold=True,
72
+ pyproject_path = app / FAB_CONFIG_FILE if app else None
73
+ config, errors, warnings = load_and_validate(path=pyproject_path)
74
+ config = process_loaded_project_config(config, errors, warnings)
75
+ federation, federation_config = validate_federation_in_project_config(
76
+ federation, config
75
77
  )
76
- raise typer.Exit(code=1) from err
78
+ exit_if_no_address(federation_config, "stop")
79
+
80
+ try:
81
+ auth_plugin = try_obtain_cli_auth_plugin(app, federation)
82
+ channel = init_channel(app, federation_config, auth_plugin)
83
+ stub = ExecStub(channel) # pylint: disable=unused-variable # noqa: F841
84
+
85
+ typer.secho(f"✋ Stopping run ID {run_id}...", fg=typer.colors.GREEN)
86
+ _stop_run(stub=stub, run_id=run_id, output_format=output_format)
87
+
88
+ except ValueError as err:
89
+ typer.secho(
90
+ f"❌ {err}",
91
+ fg=typer.colors.RED,
92
+ bold=True,
93
+ )
94
+ raise typer.Exit(code=1) from err
95
+ finally:
96
+ channel.close()
97
+ except (typer.Exit, Exception) as err: # pylint: disable=broad-except
98
+ if suppress_output:
99
+ restore_output()
100
+ e_message = captured_output.getvalue()
101
+ print_json_error(e_message, err)
102
+ else:
103
+ typer.secho(
104
+ f"{err}",
105
+ fg=typer.colors.RED,
106
+ bold=True,
107
+ )
77
108
  finally:
78
- channel.close()
109
+ if suppress_output:
110
+ restore_output()
111
+ captured_output.close()
79
112
 
80
113
 
81
- def _stop_run(
82
- stub: ExecStub, # pylint: disable=unused-argument
83
- run_id: int, # pylint: disable=unused-argument
84
- ) -> None:
114
+ def _stop_run(stub: ExecStub, run_id: int, output_format: str) -> None:
85
115
  """Stop a run."""
86
116
  response: StopRunResponse = stub.StopRun(request=StopRunRequest(run_id=run_id))
87
-
88
117
  if response.success:
89
118
  typer.secho(f"✅ Run {run_id} successfully stopped.", fg=typer.colors.GREEN)
119
+ if output_format == CliOutputFormat.JSON:
120
+ run_output = json.dumps(
121
+ {
122
+ "success": True,
123
+ "run-id": run_id,
124
+ }
125
+ )
126
+ restore_output()
127
+ Console().print_json(run_output)
90
128
  else:
91
129
  typer.secho(f"❌ Run {run_id} couldn't be stopped.", fg=typer.colors.RED)
flwr/cli/utils.py CHANGED
@@ -26,7 +26,6 @@ import grpc
26
26
  import typer
27
27
 
28
28
  from flwr.cli.cli_user_auth_interceptor import CliUserAuthInterceptor
29
- from flwr.common.address import parse_address
30
29
  from flwr.common.auth_plugin import CliAuthPlugin
31
30
  from flwr.common.constant import AUTH_TYPE, CREDENTIALS_DIR, FLWR_DIR
32
31
  from flwr.common.grpc import GRPC_MAX_MESSAGE_LENGTH, create_channel
@@ -159,33 +158,21 @@ def get_sha256_hash(file_path: Path) -> str:
159
158
  return sha256.hexdigest()
160
159
 
161
160
 
162
- def get_user_auth_config_path(
163
- root_dir: Path, federation: str, server_address: str
164
- ) -> Path:
161
+ def get_user_auth_config_path(root_dir: Path, federation: str) -> Path:
165
162
  """Return the path to the user auth config file."""
166
- # Parse the server address
167
- parsed_addr = parse_address(server_address)
168
- if parsed_addr is None:
169
- raise ValueError(f"Invalid server address: {server_address}")
170
- host, port, is_v6 = parsed_addr
171
- formatted_addr = f"[{host}]_{port}" if is_v6 else f"{host}_{port}"
172
-
173
163
  # Locate the credentials directory
174
164
  credentials_dir = root_dir.absolute() / FLWR_DIR / CREDENTIALS_DIR
175
165
  credentials_dir.mkdir(parents=True, exist_ok=True)
176
- return credentials_dir / f"{federation}_{formatted_addr}.json"
166
+ return credentials_dir / f"{federation}.json"
177
167
 
178
168
 
179
169
  def try_obtain_cli_auth_plugin(
180
170
  root_dir: Path,
181
171
  federation: str,
182
- federation_config: dict[str, Any],
183
172
  auth_type: Optional[str] = None,
184
173
  ) -> Optional[CliAuthPlugin]:
185
174
  """Load the CLI-side user auth plugin for the given auth type."""
186
- config_path = get_user_auth_config_path(
187
- root_dir, federation, federation_config["address"]
188
- )
175
+ config_path = get_user_auth_config_path(root_dir, federation)
189
176
 
190
177
  # Load the config file if it exists
191
178
  config: dict[str, Any] = {}
flwr/client/app.py CHANGED
@@ -56,7 +56,7 @@ from flwr.common.constant import (
56
56
  from flwr.common.logger import log, warn_deprecated_feature
57
57
  from flwr.common.message import Error
58
58
  from flwr.common.retry_invoker import RetryInvoker, RetryState, exponential
59
- from flwr.common.typing import Fab, Run, UserConfig
59
+ from flwr.common.typing import Fab, Run, RunNotRunningException, UserConfig
60
60
  from flwr.proto.clientappio_pb2_grpc import add_ClientAppIoServicer_to_server
61
61
  from flwr.server.superlink.fleet.grpc_bidi.grpc_server import generic_create_grpc_server
62
62
  from flwr.server.superlink.linkstate.utils import generate_rand_int_from_bytes
@@ -612,6 +612,16 @@ def start_client_internal(
612
612
  send(reply_message)
613
613
  log(INFO, "Sent reply")
614
614
 
615
+ except RunNotRunningException:
616
+ log(INFO, "")
617
+ log(
618
+ INFO,
619
+ "SuperNode aborted sending the reply message. "
620
+ "Run ID %s is not in `RUNNING` status.",
621
+ run_id,
622
+ )
623
+ log(INFO, "")
624
+
615
625
  except StopIteration:
616
626
  sleep_duration = 0
617
627
  break
@@ -32,6 +32,7 @@ from flwr.common.constant import CLIENTAPPIO_API_DEFAULT_CLIENT_ADDRESS, ErrorCo
32
32
  from flwr.common.grpc import create_channel
33
33
  from flwr.common.logger import log
34
34
  from flwr.common.message import Error
35
+ from flwr.common.retry_invoker import _make_simple_grpc_retry_invoker, _wrap_stub
35
36
  from flwr.common.serde import (
36
37
  context_from_proto,
37
38
  context_to_proto,
@@ -106,9 +107,9 @@ def run_clientapp( # pylint: disable=R0914
106
107
 
107
108
  # Resolve directory where FABs are installed
108
109
  flwr_dir_ = get_flwr_dir(flwr_dir)
109
-
110
110
  try:
111
111
  stub = ClientAppIoStub(channel)
112
+ _wrap_stub(stub, _make_simple_grpc_retry_invoker())
112
113
 
113
114
  while True:
114
115
  # If token is not set, loop until token is received from SuperNode
@@ -139,6 +140,7 @@ def run_clientapp( # pylint: disable=R0914
139
140
 
140
141
  # Execute ClientApp
141
142
  reply_message = client_app(message=message, context=context)
143
+
142
144
  except Exception as ex: # pylint: disable=broad-exception-caught
143
145
  # Don't update/change NodeState
144
146
 
@@ -42,7 +42,7 @@ from flwr.common.logger import log
42
42
  from flwr.common.message import Message, Metadata
43
43
  from flwr.common.retry_invoker import RetryInvoker
44
44
  from flwr.common.serde import message_from_taskins, message_to_taskres, run_from_proto
45
- from flwr.common.typing import Fab, Run
45
+ from flwr.common.typing import Fab, Run, RunNotRunningException
46
46
  from flwr.proto.fab_pb2 import GetFabRequest, GetFabResponse # pylint: disable=E0611
47
47
  from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
48
48
  CreateNodeRequest,
@@ -155,10 +155,16 @@ def grpc_request_response( # pylint: disable=R0913,R0914,R0915,R0917
155
155
  ping_thread: Optional[threading.Thread] = None
156
156
  ping_stop_event = threading.Event()
157
157
 
158
+ def _should_giveup_fn(e: Exception) -> bool:
159
+ if e.code() == grpc.StatusCode.PERMISSION_DENIED: # type: ignore
160
+ raise RunNotRunningException
161
+ if e.code() == grpc.StatusCode.UNAVAILABLE: # type: ignore
162
+ return False
163
+ return True
164
+
158
165
  # Restrict retries to cases where the status code is UNAVAILABLE
159
- retry_invoker.should_giveup = (
160
- lambda e: e.code() != grpc.StatusCode.UNAVAILABLE # type: ignore
161
- )
166
+ # If the status code is PERMISSION_DENIED, additionally raise RunNotRunningException
167
+ retry_invoker.should_giveup = _should_giveup_fn
162
168
 
163
169
  ###########################################################################
164
170
  # ping/create_node/delete_node/receive/send/get_run functions
flwr/common/logger.py CHANGED
@@ -15,6 +15,7 @@
15
15
  """Flower Logger."""
16
16
 
17
17
 
18
+ import json as _json
18
19
  import logging
19
20
  import re
20
21
  import sys
@@ -27,6 +28,8 @@ from queue import Empty, Queue
27
28
  from typing import TYPE_CHECKING, Any, Optional, TextIO, Union
28
29
 
29
30
  import grpc
31
+ import typer
32
+ from rich.console import Console
30
33
 
31
34
  from flwr.proto.log_pb2 import PushLogsRequest # pylint: disable=E0611
32
35
  from flwr.proto.node_pb2 import Node # pylint: disable=E0611
@@ -377,7 +380,7 @@ def stop_log_uploader(
377
380
  log_uploader.join()
378
381
 
379
382
 
380
- def remove_emojis(text: str) -> str:
383
+ def _remove_emojis(text: str) -> str:
381
384
  """Remove emojis from the provided text."""
382
385
  emoji_pattern = re.compile(
383
386
  "["
@@ -391,3 +394,15 @@ def remove_emojis(text: str) -> str:
391
394
  flags=re.UNICODE,
392
395
  )
393
396
  return emoji_pattern.sub(r"", text)
397
+
398
+
399
+ def print_json_error(msg: str, e: Union[typer.Exit, Exception]) -> None:
400
+ """Print error message as JSON."""
401
+ Console().print_json(
402
+ _json.dumps(
403
+ {
404
+ "success": False,
405
+ "error-message": _remove_emojis(str(msg) + "\n" + str(e)),
406
+ }
407
+ )
408
+ )
@@ -30,6 +30,7 @@ from flwr.common.logger import log
30
30
  from flwr.common.typing import RunNotRunningException
31
31
  from flwr.proto.clientappio_pb2_grpc import ClientAppIoStub
32
32
  from flwr.proto.serverappio_pb2_grpc import ServerAppIoStub
33
+ from flwr.proto.simulationio_pb2_grpc import SimulationIoStub
33
34
 
34
35
 
35
36
  def exponential(
@@ -365,7 +366,8 @@ def _make_simple_grpc_retry_invoker() -> RetryInvoker:
365
366
 
366
367
 
367
368
  def _wrap_stub(
368
- stub: Union[ServerAppIoStub, ClientAppIoStub], retry_invoker: RetryInvoker
369
+ stub: Union[ServerAppIoStub, ClientAppIoStub, SimulationIoStub],
370
+ retry_invoker: RetryInvoker,
369
371
  ) -> None:
370
372
  """Wrap a gRPC stub with a retry invoker."""
371
373
 
flwr/server/app.py CHANGED
@@ -93,9 +93,13 @@ BASE_DIR = get_flwr_dir() / "superlink" / "ffs"
93
93
 
94
94
 
95
95
  try:
96
- from flwr.ee import get_exec_auth_plugins
96
+ from flwr.ee import add_ee_args_superlink, get_exec_auth_plugins
97
97
  except ImportError:
98
98
 
99
+ # pylint: disable-next=unused-argument
100
+ def add_ee_args_superlink(parser: argparse.ArgumentParser) -> None:
101
+ """Add EE-specific arguments to the parser."""
102
+
99
103
  def get_exec_auth_plugins() -> dict[str, type[ExecAuthPlugin]]:
100
104
  """Return all Exec API authentication plugins."""
101
105
  raise NotImplementedError("No authentication plugins are currently supported.")
@@ -580,7 +584,7 @@ def _try_setup_node_authentication(
580
584
 
581
585
 
582
586
  def _try_obtain_user_auth_config(args: argparse.Namespace) -> Optional[dict[str, Any]]:
583
- if args.user_auth_config is not None:
587
+ if getattr(args, "user_auth_config", None) is not None:
584
588
  with open(args.user_auth_config, encoding="utf-8") as file:
585
589
  config: dict[str, Any] = yaml.safe_load(file)
586
590
  return config
@@ -703,6 +707,7 @@ def _parse_args_run_superlink() -> argparse.ArgumentParser:
703
707
  )
704
708
 
705
709
  _add_args_common(parser=parser)
710
+ add_ee_args_superlink(parser=parser)
706
711
  _add_args_serverappio_api(parser=parser)
707
712
  _add_args_fleet_api(parser=parser)
708
713
  _add_args_exec_api(parser=parser)
@@ -792,12 +797,6 @@ def _add_args_common(parser: argparse.ArgumentParser) -> None:
792
797
  type=str,
793
798
  help="The SuperLink's public key (as a path str) to enable authentication.",
794
799
  )
795
- parser.add_argument(
796
- "--user-auth-config",
797
- help="The path to the user authentication configuration YAML file.",
798
- type=str,
799
- default=None,
800
- )
801
800
 
802
801
 
803
802
  def _add_args_serverappio_api(parser: argparse.ArgumentParser) -> None:
@@ -123,9 +123,7 @@ def push_task_res(request: PushTaskResRequest, state: LinkState) -> PushTaskResR
123
123
  return response
124
124
 
125
125
 
126
- def get_run(
127
- request: GetRunRequest, state: LinkState # pylint: disable=W0613
128
- ) -> GetRunResponse:
126
+ def get_run(request: GetRunRequest, state: LinkState) -> GetRunResponse:
129
127
  """Get run information."""
130
128
  run = state.get_run(request.run_id)
131
129
 
@@ -24,7 +24,6 @@ import threading
24
24
  import traceback
25
25
  from logging import DEBUG, ERROR, INFO, WARNING
26
26
  from pathlib import Path
27
- from time import sleep
28
27
  from typing import Any, Optional
29
28
 
30
29
  from flwr.cli.config_utils import load_and_validate
@@ -136,7 +135,6 @@ def run_simulation_from_cli() -> None:
136
135
  app_dir=args.app,
137
136
  run=run,
138
137
  enable_tf_gpu_growth=args.enable_tf_gpu_growth,
139
- delay_start=args.delay_start,
140
138
  verbose_logging=args.verbose,
141
139
  server_app_run_config=fused_config,
142
140
  is_app=True,
@@ -309,7 +307,6 @@ def _main_loop(
309
307
  enable_tf_gpu_growth: bool,
310
308
  run: Run,
311
309
  exit_event: EventType,
312
- delay_start: int,
313
310
  flwr_dir: Optional[str] = None,
314
311
  client_app: Optional[ClientApp] = None,
315
312
  client_app_attr: Optional[str] = None,
@@ -354,9 +351,6 @@ def _main_loop(
354
351
  run_id=run.run_id,
355
352
  )
356
353
 
357
- # Buffer time so the `ServerApp` in separate thread is ready
358
- log(DEBUG, "Buffer time delay: %ds", delay_start)
359
- sleep(delay_start)
360
354
  # Start Simulation Engine
361
355
  vce.start_vce(
362
356
  num_supernodes=num_supernodes,
@@ -405,7 +399,6 @@ def _run_simulation(
405
399
  flwr_dir: Optional[str] = None,
406
400
  run: Optional[Run] = None,
407
401
  enable_tf_gpu_growth: bool = False,
408
- delay_start: int = 5,
409
402
  verbose_logging: bool = False,
410
403
  is_app: bool = False,
411
404
  ) -> None:
@@ -460,7 +453,6 @@ def _run_simulation(
460
453
  enable_tf_gpu_growth,
461
454
  run,
462
455
  exit_event,
463
- delay_start,
464
456
  flwr_dir,
465
457
  client_app,
466
458
  client_app_attr,
@@ -538,13 +530,6 @@ def _parse_args_run_simulation() -> argparse.ArgumentParser:
538
530
  "Read more about how `tf.config.experimental.set_memory_growth()` works in "
539
531
  "the TensorFlow documentation: https://www.tensorflow.org/api/stable.",
540
532
  )
541
- parser.add_argument(
542
- "--delay-start",
543
- type=int,
544
- default=3,
545
- help="Buffer time (in seconds) to delay the start the simulation engine after "
546
- "the `ServerApp`, which runs in a separate thread, has been launched.",
547
- )
548
533
  parser.add_argument(
549
534
  "--verbose",
550
535
  action="store_true",
@@ -23,6 +23,7 @@ import grpc
23
23
  from flwr.common.constant import SIMULATIONIO_API_DEFAULT_CLIENT_ADDRESS
24
24
  from flwr.common.grpc import create_channel
25
25
  from flwr.common.logger import log
26
+ from flwr.common.retry_invoker import _make_simple_grpc_retry_invoker, _wrap_stub
26
27
  from flwr.proto.simulationio_pb2_grpc import SimulationIoStub # pylint: disable=E0611
27
28
 
28
29
 
@@ -48,6 +49,7 @@ class SimulationIoConnection:
48
49
  self._cert = root_certificates
49
50
  self._grpc_stub: Optional[SimulationIoStub] = None
50
51
  self._channel: Optional[grpc.Channel] = None
52
+ self._retry_invoker = _make_simple_grpc_retry_invoker()
51
53
 
52
54
  @property
53
55
  def _is_connected(self) -> bool:
@@ -72,6 +74,7 @@ class SimulationIoConnection:
72
74
  root_certificates=self._cert,
73
75
  )
74
76
  self._grpc_stub = SimulationIoStub(self._channel)
77
+ _wrap_stub(self._grpc_stub, self._retry_invoker)
75
78
  log(DEBUG, "[SimulationIO] Connected to %s", self._addr)
76
79
 
77
80
  def _disconnect(self) -> None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: flwr-nightly
3
- Version: 1.14.0.dev20241214
3
+ Version: 1.14.0.dev20241216
4
4
  Summary: Flower: A Friendly Federated AI Framework
5
5
  Home-page: https://flower.ai
6
6
  License: Apache-2.0
@@ -6,10 +6,10 @@ flwr/cli/cli_user_auth_interceptor.py,sha256=rEjgAZmzHO0GjwdyZib6bkTI2X59ErJAZlu
6
6
  flwr/cli/config_utils.py,sha256=I4_EMv2f68mfrL_QuOYoAG--yDfKisE7tGiIg09G2YQ,12079
7
7
  flwr/cli/example.py,sha256=uk5CoD0ZITgpY_ffsTbEKf8XOOCSUzByjHPcMSPqV18,2216
8
8
  flwr/cli/install.py,sha256=0AD0qJD79SKgBnWOQlphcubfr4zHk8jTpFgwZbJBI_g,8180
9
- flwr/cli/log.py,sha256=1HUCUnSMk1yXwjR5G4w12xGoIZmCDGA7HWBbOePsDlQ,5995
9
+ flwr/cli/log.py,sha256=SXr12PepUUU0YDNIMl3m8lguL7Tmf6MrpFBsxZcRmcg,5976
10
10
  flwr/cli/login/__init__.py,sha256=6_9zOzbPOAH72K2wX3-9dXTAbS7Mjpa5sEn2lA6eHHI,800
11
- flwr/cli/login/login.py,sha256=_G16s07CJOT1l_jlqRgIDCch0PWIjg0WPpmxI1GC7y8,2797
12
- flwr/cli/ls.py,sha256=5uO0YG0XXn7paS4oUs1T7rwicApxMV3ac9ejBZfLN3k,10545
11
+ flwr/cli/login/login.py,sha256=bZZ3hVeGpF5805R0Eg_SBZUGwrLAWmyaoLhLw6vQFcg,2764
12
+ flwr/cli/ls.py,sha256=II1aCv6q0yi6yl5AcBQ-en250ZUkh82rw_Kt-RewtBQ,10859
13
13
  flwr/cli/new/__init__.py,sha256=pOQtPT9W4kCIttcKne5m-FtJbvTqdjTVJxzQ9AUYK8I,790
14
14
  flwr/cli/new/new.py,sha256=scyyKt8mzkc3El1bypgkHjKwVQEc2-q4I50PxriPFdI,9922
15
15
  flwr/cli/new/templates/__init__.py,sha256=4luU8RL-CK8JJCstQ_ON809W9bNTkY1l9zSaPKBkgwY,725
@@ -65,15 +65,15 @@ flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl,sha256=UtH3Vslg2S8fIKIHC-d
65
65
  flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl,sha256=01HArBqRrbZT3O7pXOM9MqduXMNm525wv7Sj6dvYMJE,686
66
66
  flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl,sha256=KVCIOEYNWnq6j7XOboXqZshc9aQ2PyRDUu7bZtmfJ24,710
67
67
  flwr/cli/run/__init__.py,sha256=cCsKVB0SFzh2b3QmGba6BHckB85xlhjh3mh4pBpACtY,790
68
- flwr/cli/run/run.py,sha256=O-avAXq9uk27BBGyRlTlaX4a1xvLwwNKJDg0XozxZQc,8140
69
- flwr/cli/stop.py,sha256=pa3etMLCLxfGl9w2c2o6e5u46j6LimEmNp2zuQGxAIk,3143
70
- flwr/cli/utils.py,sha256=aVTtQ2RD5RM8dnmqHNEp1kIBVyjF4tJ7KupczmegWc8,8331
68
+ flwr/cli/run/run.py,sha256=hUYedvUAxX28i0ytMIyKhO4kJM9MhUdN5KbzKLl_0Y0,7803
69
+ flwr/cli/stop.py,sha256=cIvTpS9rRGzHbpdlWz03XOSXNCxMVXORV-7YsmSV1Vg,4540
70
+ flwr/cli/utils.py,sha256=25S3IqC2yiC94AcSJ6XQXruDCCH7iZU3mVRHBaAKExo,7873
71
71
  flwr/client/__init__.py,sha256=DGDoO0AEAfz-0CUFmLdyUUweAS64-07AOnmDfWUefK4,1192
72
- flwr/client/app.py,sha256=eznpoeYPRI-pjgtb3BCZVQGz9L91SSRrXbZOMzbIt54,34398
72
+ flwr/client/app.py,sha256=XJWu-kPswM52oLYXaOLKr0gj87KPNRI7M0Na9oBsDK4,34784
73
73
  flwr/client/client.py,sha256=lIIUgxHk4tofk2RljVBL5wFzPR_Ob9iURg0NpyKufPY,9081
74
74
  flwr/client/client_app.py,sha256=cTig-N00YzTucbo9zNi6I21J8PlbflU_8J_f5CI-Wpw,10390
75
75
  flwr/client/clientapp/__init__.py,sha256=kZqChGnTChQ1WGSUkIlW2S5bc0d0mzDubCAmZUGRpEY,800
76
- flwr/client/clientapp/app.py,sha256=w94GzkFJw89xMZ69znEz6vNs0tELhMSlLlqttFuJDe8,8807
76
+ flwr/client/clientapp/app.py,sha256=n3IbbQ__QBjd4n7hhP2oydYg66IvrnSXvwi3sZvnWeU,8949
77
77
  flwr/client/clientapp/clientappio_servicer.py,sha256=5L6bjw_j3Mnx9kRFwYwxDNABKurBO5q1jZOWE_X11wQ,8522
78
78
  flwr/client/clientapp/utils.py,sha256=TTihPRO_AUhA3ZCszPsLyLZ30D_tnhTfe1ndMNVOBPg,4344
79
79
  flwr/client/dpfedavg_numpy_client.py,sha256=4KsEvzavDKyVDU1V0kMqffTwu1lNdUCYQN-i0DTYVN8,7404
@@ -83,7 +83,7 @@ flwr/client/grpc_client/__init__.py,sha256=LsnbqXiJhgQcB0XzAlUQgPx011Uf7Y7yabIC1
83
83
  flwr/client/grpc_client/connection.py,sha256=gMwB87mlmRBbvPOvUA1m3C-Ci6bjMEmTRI4bJpgbyic,9416
84
84
  flwr/client/grpc_rere_client/__init__.py,sha256=MK-oSoV3kwUEQnIwl0GN4OpiHR7eLOrMA8ikunET130,752
85
85
  flwr/client/grpc_rere_client/client_interceptor.py,sha256=q08lIEeJLvvonNOiejNXvmySbPObteGnbDHhEKDmWzE,5380
86
- flwr/client/grpc_rere_client/connection.py,sha256=fqqorPCieqfFHbFXPniAM-qga5Lf-6-kp6DHdbk8Rrg,11148
86
+ flwr/client/grpc_rere_client/connection.py,sha256=NqKSoYIJblB4lElZ7EKIgDjLb6KYEcI-7CBrTbyiKfg,11475
87
87
  flwr/client/grpc_rere_client/grpc_adapter.py,sha256=sQo0I9T65t97LFGoW_PrmgaTbd18GFgi2DoAI5wQJ4k,5589
88
88
  flwr/client/heartbeat.py,sha256=cx37mJBH8LyoIN4Lks85wtqT1mnU5GulQnr4pGCvAq0,2404
89
89
  flwr/client/message_handler/__init__.py,sha256=QxxQuBNpFPTHx3KiUNvQSlqMKlEnbRR1kFfc1KVje08,719
@@ -122,7 +122,7 @@ flwr/common/differential_privacy_constants.py,sha256=c7b7tqgvT7yMK0XN9ndiTBs4mQf
122
122
  flwr/common/dp.py,sha256=vddkvyjV2FhRoN4VuU2LeAM1UBn7dQB8_W-Qdiveal8,1978
123
123
  flwr/common/exit_handlers.py,sha256=MracJaBeoCOC7TaXK9zCJQxhrMSx9ZtczK237qvhBpU,2806
124
124
  flwr/common/grpc.py,sha256=AIPMAHsvcTlduaYKCgnoBnst1A7RZEgGqh0Ulm7qfJ0,2621
125
- flwr/common/logger.py,sha256=mdo-jhSL5--dSzaLX0oPf_XluSBEYcF93Nnloz6orC4,11941
125
+ flwr/common/logger.py,sha256=qwiOc9N_Dqh-NlxtENcMa-dCPqint20ZLuWEvnAEwHU,12323
126
126
  flwr/common/message.py,sha256=Zv4ID2BLQsbff0F03DI_MeFoHbSqVZAdDD9NcKYv6Zo,13832
127
127
  flwr/common/object_ref.py,sha256=fIXf8aP5mG6Nuni7dvcKK5Di3zRfRWGs4ljvqIXplds,10115
128
128
  flwr/common/parameter.py,sha256=-bFAUayToYDF50FZGrBC1hQYJCQDtB2bbr3ZuVLMtdE,2095
@@ -135,7 +135,7 @@ flwr/common/record/parametersrecord.py,sha256=SasHn35JRHsj8G1UT76FgRjaP4ZJasejvg
135
135
  flwr/common/record/recordset.py,sha256=sSofrBycZSqiHR4TzfI4_QoIIN-5B1LnMG0C9CiByAo,8312
136
136
  flwr/common/record/typeddict.py,sha256=q5hL2xkXymuiCprHWb69mUmLpWQk_XXQq0hGQ69YPaw,3599
137
137
  flwr/common/recordset_compat.py,sha256=ViSwA26h6Q55ZmV1LLjSJpcKiipV-p_JpCj4wxdE-Ow,14230
138
- flwr/common/retry_invoker.py,sha256=nCA-dfBw6YoWkOgop71QfhTDmYj1JIgXsdpzlyqgZK4,14396
138
+ flwr/common/retry_invoker.py,sha256=UIDKsn0AitS3fOr43WTqZAdD-TaHkBeTj1QxD7SGba0,14481
139
139
  flwr/common/secure_aggregation/__init__.py,sha256=erPnTWdOfMH0K0HQTmj5foDJ6t3iYcExy2aACy8iZNQ,731
140
140
  flwr/common/secure_aggregation/crypto/__init__.py,sha256=nlHesCWy8xxE5s6qHWnauCtyClcMQ2K0CEXAHakY5n0,738
141
141
  flwr/common/secure_aggregation/crypto/shamir.py,sha256=wCSfEfeaPgJ9Om580-YPUF2ljiyRhq33TRC4HtwxYl8,2779
@@ -211,7 +211,7 @@ flwr/proto/transport_pb2_grpc.py,sha256=vLN3EHtx2aEEMCO4f1Upu-l27BPzd3-5pV-u8wPc
211
211
  flwr/proto/transport_pb2_grpc.pyi,sha256=AGXf8RiIiW2J5IKMlm_3qT3AzcDa4F3P5IqUjve_esA,766
212
212
  flwr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
213
213
  flwr/server/__init__.py,sha256=cEg1oecBu4cKB69iJCqWEylC8b5XW47bl7rQiJsdTvM,1528
214
- flwr/server/app.py,sha256=JDF_dEnLvnZwcQgGz2E7ur8eBDC-xVU04hf2adirDvU,30869
214
+ flwr/server/app.py,sha256=t2N5Q2CEOptaCBxmos397Re9UDPDvNvgnXxNy7xqj-g,30944
215
215
  flwr/server/client_manager.py,sha256=7Ese0tgrH-i-ms363feYZJKwB8gWnXSmg_hYF2Bju4U,6227
216
216
  flwr/server/client_proxy.py,sha256=4G-oTwhb45sfWLx2uZdcXD98IZwdTS6F88xe3akCdUg,2399
217
217
  flwr/server/compat/__init__.py,sha256=VxnJtJyOjNFQXMNi9hIuzNlZM5n0Hj1p3aq_Pm2udw4,892
@@ -276,7 +276,7 @@ flwr/server/superlink/fleet/grpc_rere/__init__.py,sha256=j2hyC342am-_Hgp1g80Y3fG
276
276
  flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py,sha256=KBVsGt57G2_OWB_74N29TYVzD36G0xJg2l5m0ArPoEU,5389
277
277
  flwr/server/superlink/fleet/grpc_rere/server_interceptor.py,sha256=8PHzqtW_rKBvqI5XVwYN-CBEpEonnj85iN0daSWliyI,8299
278
278
  flwr/server/superlink/fleet/message_handler/__init__.py,sha256=h8oLD7uo5lKICPy0rRdKRjTYe62u8PKkT_fA4xF5JPA,731
279
- flwr/server/superlink/fleet/message_handler/message_handler.py,sha256=BVc30QUQBP8aMZ77jT4-VEbbqMI7b4kbw33ERzlbwWk,5329
279
+ flwr/server/superlink/fleet/message_handler/message_handler.py,sha256=hzL8t6uUqO1lu5UHLF_NerdJuce4S5cK9fIWkKDzJfA,5298
280
280
  flwr/server/superlink/fleet/rest_rere/__init__.py,sha256=5jbYbAn75sGv-gBwOPDySE0kz96F6dTYLeMrGqNi4lM,735
281
281
  flwr/server/superlink/fleet/rest_rere/rest_api.py,sha256=0b10l9zz381GXgCmTZGoz76Z_fdRaa8XcqMGwuLqJ38,6723
282
282
  flwr/server/superlink/fleet/vce/__init__.py,sha256=TZJsKTpYO_djv2EXx9Ji62I8TA0JiZF8jvRyJRZkAes,784
@@ -311,8 +311,8 @@ flwr/simulation/ray_transport/__init__.py,sha256=wzcEEwUUlulnXsg6raCA1nGpP3LlAQD
311
311
  flwr/simulation/ray_transport/ray_actor.py,sha256=k11yoAPQzFGQU-KnCCP0ZrfPPdUPXXrBe-1DKM5VdW4,18997
312
312
  flwr/simulation/ray_transport/ray_client_proxy.py,sha256=2vjOKoom3B74C6XU-jC3N6DwYmsLdB-lmkHZ_Xrv96o,7367
313
313
  flwr/simulation/ray_transport/utils.py,sha256=wtbQhKQ4jGoiQDLJNQP17m1DSfL22ERhDBGuoeUFaAQ,2393
314
- flwr/simulation/run_simulation.py,sha256=9zIDWA0fL901cobVRfVuJlTdxAIYJJqjiPV4_a1_v3U,20023
315
- flwr/simulation/simulationio_connection.py,sha256=m31L9Iej-61va48E5x-wJypA6p5s82WM4PKIAmKizQA,3209
314
+ flwr/simulation/run_simulation.py,sha256=GY1TIg5jnEoiW3Ak06swYo4jIho1iZposKbq_Bq-bp4,19478
315
+ flwr/simulation/simulationio_connection.py,sha256=8aAh6MKQkQPMSnWEqA5vua_QzZtoMxG-_-AB23RPhS4,3412
316
316
  flwr/superexec/__init__.py,sha256=fcj366jh4RFby_vDwLroU4kepzqbnJgseZD_jUr_Mko,715
317
317
  flwr/superexec/app.py,sha256=Z6kYHWd62YL0Q4YKyCAbt_BcefNfbKH6V-jCC-1NkZM,1842
318
318
  flwr/superexec/deployment.py,sha256=wZ9G42gGS91knfplswh95MnQ83Fzu-rs6wcuNgDmmvY,6735
@@ -321,8 +321,8 @@ flwr/superexec/exec_servicer.py,sha256=8tFwj1fDBF6PzwLhByTlxM-KNZc83bG1UdE92-8DS
321
321
  flwr/superexec/exec_user_auth_interceptor.py,sha256=K06OU-l4LnYhTDg071hGJuOaQWEJbZsYi5qxUmmtiG0,3704
322
322
  flwr/superexec/executor.py,sha256=_B55WW2TD1fBINpabSSDRenVHXYmvlfhv-k8hJKU4lQ,3115
323
323
  flwr/superexec/simulation.py,sha256=WQDon15oqpMopAZnwRZoTICYCfHqtkvFSqiTQ2hLD_g,4088
324
- flwr_nightly-1.14.0.dev20241214.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
325
- flwr_nightly-1.14.0.dev20241214.dist-info/METADATA,sha256=72g5Ca7GneSO6WBBKA8GwfcdBQHGK1ZU2tcKEuymeYI,15799
326
- flwr_nightly-1.14.0.dev20241214.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
327
- flwr_nightly-1.14.0.dev20241214.dist-info/entry_points.txt,sha256=JlNxX3qhaV18_2yj5a3kJW1ESxm31cal9iS_N_pf1Rk,538
328
- flwr_nightly-1.14.0.dev20241214.dist-info/RECORD,,
324
+ flwr_nightly-1.14.0.dev20241216.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
325
+ flwr_nightly-1.14.0.dev20241216.dist-info/METADATA,sha256=f4OsNAS5i9xKEW7m-ntvOAXHJA-EOUSsUXb7-rQg4YE,15799
326
+ flwr_nightly-1.14.0.dev20241216.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
327
+ flwr_nightly-1.14.0.dev20241216.dist-info/entry_points.txt,sha256=JlNxX3qhaV18_2yj5a3kJW1ESxm31cal9iS_N_pf1Rk,538
328
+ flwr_nightly-1.14.0.dev20241216.dist-info/RECORD,,