flwr-nightly 1.14.0.dev20241203__py3-none-any.whl → 1.14.0.dev20241205__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/ls.py CHANGED
@@ -15,16 +15,19 @@
15
15
  """Flower command line interface `ls` command."""
16
16
 
17
17
 
18
+ import io
19
+ import json
18
20
  from datetime import datetime, timedelta
19
21
  from logging import DEBUG
20
22
  from pathlib import Path
21
- from typing import Annotated, Any, Optional
23
+ from typing import Annotated, Any, Optional, Union
22
24
 
23
25
  import grpc
24
26
  import typer
25
27
  from rich.console import Console
26
28
  from rich.table import Table
27
29
  from rich.text import Text
30
+ from typer import Exit
28
31
 
29
32
  from flwr.cli.config_utils import (
30
33
  load_and_validate,
@@ -32,10 +35,10 @@ from flwr.cli.config_utils import (
32
35
  validate_federation_in_project_config,
33
36
  validate_project_config,
34
37
  )
35
- from flwr.common.constant import FAB_CONFIG_FILE, SubStatus
38
+ from flwr.common.constant import FAB_CONFIG_FILE, CliOutputFormat, SubStatus
36
39
  from flwr.common.date import format_timedelta, isoformat8601_utc
37
40
  from flwr.common.grpc import GRPC_MAX_MESSAGE_LENGTH, create_channel
38
- from flwr.common.logger import log
41
+ from flwr.common.logger import log, redirect_output, remove_emojis, restore_output
39
42
  from flwr.common.serde import run_from_proto
40
43
  from flwr.common.typing import Run
41
44
  from flwr.proto.exec_pb2 import ( # pylint: disable=E0611
@@ -44,8 +47,10 @@ from flwr.proto.exec_pb2 import ( # pylint: disable=E0611
44
47
  )
45
48
  from flwr.proto.exec_pb2_grpc import ExecStub
46
49
 
50
+ _RunListType = tuple[int, str, str, str, str, str, str, str, str]
47
51
 
48
- def ls(
52
+
53
+ def ls( # pylint: disable=too-many-locals, too-many-branches
49
54
  app: Annotated[
50
55
  Path,
51
56
  typer.Argument(help="Path of the Flower project"),
@@ -68,54 +73,85 @@ def ls(
68
73
  help="Specific run ID to display",
69
74
  ),
70
75
  ] = None,
76
+ output_format: Annotated[
77
+ str,
78
+ typer.Option(
79
+ "--format",
80
+ case_sensitive=False,
81
+ help="Format output using 'default' view or 'json'",
82
+ ),
83
+ ] = CliOutputFormat.DEFAULT,
71
84
  ) -> None:
72
85
  """List runs."""
73
- # Load and validate federation config
74
- typer.secho("Loading project configuration... ", fg=typer.colors.BLUE)
75
-
76
- pyproject_path = app / FAB_CONFIG_FILE if app else None
77
- config, errors, warnings = load_and_validate(path=pyproject_path)
78
- config = validate_project_config(config, errors, warnings)
79
- federation, federation_config = validate_federation_in_project_config(
80
- federation, config
81
- )
86
+ suppress_output = output_format == CliOutputFormat.JSON
87
+ captured_output = io.StringIO()
88
+ try:
89
+ if suppress_output:
90
+ redirect_output(captured_output)
91
+
92
+ # Load and validate federation config
93
+ typer.secho("Loading project configuration... ", fg=typer.colors.BLUE)
82
94
 
83
- if "address" not in federation_config:
84
- typer.secho(
85
- "❌ `flwr ls` currently works with Exec API. Ensure that the correct"
86
- "Exec API address is provided in the `pyproject.toml`.",
87
- fg=typer.colors.RED,
88
- bold=True,
95
+ pyproject_path = app / FAB_CONFIG_FILE if app else None
96
+ config, errors, warnings = load_and_validate(path=pyproject_path)
97
+ config = validate_project_config(config, errors, warnings)
98
+ federation, federation_config = validate_federation_in_project_config(
99
+ federation, config
89
100
  )
90
- raise typer.Exit(code=1)
91
101
 
92
- try:
93
- if runs and run_id is not None:
94
- raise ValueError(
95
- "The options '--runs' and '--run-id' are mutually exclusive."
102
+ if "address" not in federation_config:
103
+ typer.secho(
104
+ "❌ `flwr ls` currently works with Exec API. Ensure that the correct"
105
+ "Exec API address is provided in the `pyproject.toml`.",
106
+ fg=typer.colors.RED,
107
+ bold=True,
96
108
  )
97
-
98
- channel = _init_channel(app, federation_config)
99
- stub = ExecStub(channel)
100
-
101
- # Display information about a specific run ID
102
- if run_id is not None:
103
- typer.echo(f"🔍 Displaying information for run ID {run_id}...")
104
- _display_one_run(stub, run_id)
105
- # By default, list all runs
109
+ raise typer.Exit(code=1)
110
+
111
+ try:
112
+ if runs and run_id is not None:
113
+ raise ValueError(
114
+ "The options '--runs' and '--run-id' are mutually exclusive."
115
+ )
116
+
117
+ channel = _init_channel(app, federation_config)
118
+ stub = ExecStub(channel)
119
+
120
+ # Display information about a specific run ID
121
+ if run_id is not None:
122
+ typer.echo(f"🔍 Displaying information for run ID {run_id}...")
123
+ restore_output()
124
+ _display_one_run(stub, run_id, output_format)
125
+ # By default, list all runs
126
+ else:
127
+ typer.echo("📄 Listing all runs...")
128
+ restore_output()
129
+ _list_runs(stub, output_format)
130
+
131
+ except ValueError as err:
132
+ typer.secho(
133
+ f"❌ {err}",
134
+ fg=typer.colors.RED,
135
+ bold=True,
136
+ )
137
+ raise typer.Exit(code=1) from err
138
+ finally:
139
+ channel.close()
140
+ except (typer.Exit, Exception) as err: # pylint: disable=broad-except
141
+ if suppress_output:
142
+ restore_output()
143
+ e_message = captured_output.getvalue()
144
+ _print_json_error(e_message, err)
106
145
  else:
107
- typer.echo("📄 Listing all runs...")
108
- _list_runs(stub)
109
-
110
- except ValueError as err:
111
- typer.secho(
112
- f"❌ {err}",
113
- fg=typer.colors.RED,
114
- bold=True,
115
- )
116
- raise typer.Exit(code=1) from err
146
+ typer.secho(
147
+ f"{err}",
148
+ fg=typer.colors.RED,
149
+ bold=True,
150
+ )
117
151
  finally:
118
- channel.close()
152
+ if suppress_output:
153
+ restore_output()
154
+ captured_output.close()
119
155
 
120
156
 
121
157
  def on_channel_state_change(channel_connectivity: str) -> None:
@@ -139,23 +175,13 @@ def _init_channel(app: Path, federation_config: dict[str, Any]) -> grpc.Channel:
139
175
  return channel
140
176
 
141
177
 
142
- def _format_run_table(run_dict: dict[int, Run], now_isoformat: str) -> Table:
143
- """Format run status as a rich Table."""
144
- table = Table(header_style="bold cyan", show_lines=True)
178
+ def _format_runs(run_dict: dict[int, Run], now_isoformat: str) -> list[_RunListType]:
179
+ """Format runs to a list."""
145
180
 
146
181
  def _format_datetime(dt: Optional[datetime]) -> str:
147
182
  return isoformat8601_utc(dt).replace("T", " ") if dt else "N/A"
148
183
 
149
- # Add columns
150
- table.add_column(
151
- Text("Run ID", justify="center"), style="bright_white", overflow="fold"
152
- )
153
- table.add_column(Text("FAB", justify="center"), style="dim white")
154
- table.add_column(Text("Status", justify="center"))
155
- table.add_column(Text("Elapsed", justify="center"), style="blue")
156
- table.add_column(Text("Created At", justify="center"), style="dim white")
157
- table.add_column(Text("Running At", justify="center"), style="dim white")
158
- table.add_column(Text("Finished At", justify="center"), style="dim white")
184
+ run_list: list[_RunListType] = []
159
185
 
160
186
  # Add rows
161
187
  for run in sorted(
@@ -167,15 +193,6 @@ def _format_run_table(run_dict: dict[int, Run], now_isoformat: str) -> Table:
167
193
  else:
168
194
  status_text = f"{run.status.status}:{run.status.sub_status}"
169
195
 
170
- # Style the status based on its value
171
- sub_status = run.status.sub_status
172
- if sub_status == SubStatus.COMPLETED:
173
- status_style = "green"
174
- elif sub_status == SubStatus.FAILED:
175
- status_style = "red"
176
- else:
177
- status_style = "yellow"
178
-
179
196
  # Convert isoformat to datetime
180
197
  pending_at = datetime.fromisoformat(run.pending_at) if run.pending_at else None
181
198
  running_at = datetime.fromisoformat(run.running_at) if run.running_at else None
@@ -192,31 +209,124 @@ def _format_run_table(run_dict: dict[int, Run], now_isoformat: str) -> Table:
192
209
  end_time = datetime.fromisoformat(now_isoformat)
193
210
  elapsed_time = end_time - running_at
194
211
 
195
- table.add_row(
196
- f"[bold]{run.run_id}[/bold]",
197
- f"{run.fab_id} (v{run.fab_version})",
212
+ run_list.append(
213
+ (
214
+ run.run_id,
215
+ run.fab_id,
216
+ run.fab_version,
217
+ run.fab_hash,
218
+ status_text,
219
+ format_timedelta(elapsed_time),
220
+ _format_datetime(pending_at),
221
+ _format_datetime(running_at),
222
+ _format_datetime(finished_at),
223
+ )
224
+ )
225
+ return run_list
226
+
227
+
228
+ def _to_table(run_list: list[_RunListType]) -> Table:
229
+ """Format the provided run list to a rich Table."""
230
+ table = Table(header_style="bold cyan", show_lines=True)
231
+
232
+ # Add columns
233
+ table.add_column(
234
+ Text("Run ID", justify="center"), style="bright_white", overflow="fold"
235
+ )
236
+ table.add_column(Text("FAB", justify="center"), style="dim white")
237
+ table.add_column(Text("Status", justify="center"))
238
+ table.add_column(Text("Elapsed", justify="center"), style="blue")
239
+ table.add_column(Text("Created At", justify="center"), style="dim white")
240
+ table.add_column(Text("Running At", justify="center"), style="dim white")
241
+ table.add_column(Text("Finished At", justify="center"), style="dim white")
242
+
243
+ for row in run_list:
244
+ (
245
+ run_id,
246
+ fab_id,
247
+ fab_version,
248
+ _,
249
+ status_text,
250
+ elapsed,
251
+ created_at,
252
+ running_at,
253
+ finished_at,
254
+ ) = row
255
+ # Style the status based on its value
256
+ sub_status = status_text.rsplit(":", maxsplit=1)[-1]
257
+ if sub_status == SubStatus.COMPLETED:
258
+ status_style = "green"
259
+ elif sub_status == SubStatus.FAILED:
260
+ status_style = "red"
261
+ else:
262
+ status_style = "yellow"
263
+
264
+ formatted_row = (
265
+ f"[bold]{run_id}[/bold]",
266
+ f"{fab_id} (v{fab_version})",
198
267
  f"[{status_style}]{status_text}[/{status_style}]",
199
- format_timedelta(elapsed_time),
200
- _format_datetime(pending_at),
201
- _format_datetime(running_at),
202
- _format_datetime(finished_at),
268
+ elapsed,
269
+ created_at,
270
+ running_at,
271
+ finished_at,
203
272
  )
273
+ table.add_row(*formatted_row)
274
+
204
275
  return table
205
276
 
206
277
 
278
+ def _to_json(run_list: list[_RunListType]) -> str:
279
+ """Format run status list to a JSON formatted string."""
280
+ runs_list = []
281
+ for row in run_list:
282
+ (
283
+ run_id,
284
+ fab_id,
285
+ fab_version,
286
+ fab_hash,
287
+ status_text,
288
+ elapsed,
289
+ created_at,
290
+ running_at,
291
+ finished_at,
292
+ ) = row
293
+ runs_list.append(
294
+ {
295
+ "run-id": run_id,
296
+ "fab-id": fab_id,
297
+ "fab-name": fab_id.split("/")[-1],
298
+ "fab-version": fab_version,
299
+ "fab-hash": fab_hash[:8],
300
+ "status": status_text,
301
+ "elapsed": elapsed,
302
+ "created-at": created_at,
303
+ "running-at": running_at,
304
+ "finished-at": finished_at,
305
+ }
306
+ )
307
+
308
+ return json.dumps({"success": True, "runs": runs_list})
309
+
310
+
207
311
  def _list_runs(
208
312
  stub: ExecStub,
313
+ output_format: str = CliOutputFormat.DEFAULT,
209
314
  ) -> None:
210
315
  """List all runs."""
211
316
  res: ListRunsResponse = stub.ListRuns(ListRunsRequest())
212
317
  run_dict = {run_id: run_from_proto(proto) for run_id, proto in res.run_dict.items()}
213
318
 
214
- Console().print(_format_run_table(run_dict, res.now))
319
+ formatted_runs = _format_runs(run_dict, res.now)
320
+ if output_format == CliOutputFormat.JSON:
321
+ Console().print_json(_to_json(formatted_runs))
322
+ else:
323
+ Console().print(_to_table(formatted_runs))
215
324
 
216
325
 
217
326
  def _display_one_run(
218
327
  stub: ExecStub,
219
328
  run_id: int,
329
+ output_format: str = CliOutputFormat.DEFAULT,
220
330
  ) -> None:
221
331
  """Display information about a specific run."""
222
332
  res: ListRunsResponse = stub.ListRuns(ListRunsRequest(run_id=run_id))
@@ -225,4 +335,20 @@ def _display_one_run(
225
335
 
226
336
  run_dict = {run_id: run_from_proto(proto) for run_id, proto in res.run_dict.items()}
227
337
 
228
- Console().print(_format_run_table(run_dict, res.now))
338
+ formatted_runs = _format_runs(run_dict, res.now)
339
+ if output_format == CliOutputFormat.JSON:
340
+ Console().print_json(_to_json(formatted_runs))
341
+ else:
342
+ Console().print(_to_table(formatted_runs))
343
+
344
+
345
+ def _print_json_error(msg: str, e: Union[Exit, Exception]) -> None:
346
+ """Print error message as JSON."""
347
+ Console().print_json(
348
+ json.dumps(
349
+ {
350
+ "success": False,
351
+ "error-message": remove_emojis(str(msg) + "\n" + str(e)),
352
+ }
353
+ )
354
+ )
flwr/cli/run/run.py CHANGED
@@ -14,16 +14,19 @@
14
14
  # ==============================================================================
15
15
  """Flower command line interface `run` command."""
16
16
 
17
+ import io
17
18
  import json
18
19
  import subprocess
19
20
  from logging import DEBUG
20
21
  from pathlib import Path
21
- from typing import Annotated, Any, Optional
22
+ from typing import Annotated, Any, Optional, Union
22
23
 
23
24
  import typer
25
+ from rich.console import Console
24
26
 
25
27
  from flwr.cli.build import build
26
28
  from flwr.cli.config_utils import (
29
+ get_fab_metadata,
27
30
  load_and_validate,
28
31
  validate_certificate_in_federation_config,
29
32
  validate_federation_in_project_config,
@@ -34,8 +37,9 @@ from flwr.common.config import (
34
37
  parse_config_args,
35
38
  user_config_to_configsrecord,
36
39
  )
40
+ from flwr.common.constant import CliOutputFormat
37
41
  from flwr.common.grpc import GRPC_MAX_MESSAGE_LENGTH, create_channel
38
- from flwr.common.logger import log
42
+ from flwr.common.logger import log, redirect_output, remove_emojis, restore_output
39
43
  from flwr.common.serde import (
40
44
  configs_record_to_proto,
41
45
  fab_to_proto,
@@ -85,21 +89,51 @@ def run(
85
89
  "logs are not streamed by default.",
86
90
  ),
87
91
  ] = False,
92
+ output_format: Annotated[
93
+ str,
94
+ typer.Option(
95
+ "--format",
96
+ case_sensitive=False,
97
+ help="Format output using 'default' view or 'json'",
98
+ ),
99
+ ] = CliOutputFormat.DEFAULT,
88
100
  ) -> None:
89
101
  """Run Flower App."""
90
- typer.secho("Loading project configuration... ", fg=typer.colors.BLUE)
102
+ suppress_output = output_format == CliOutputFormat.JSON
103
+ captured_output = io.StringIO()
104
+ try:
105
+ if suppress_output:
106
+ redirect_output(captured_output)
107
+ typer.secho("Loading project configuration... ", fg=typer.colors.BLUE)
91
108
 
92
- pyproject_path = app / "pyproject.toml" if app else None
93
- config, errors, warnings = load_and_validate(path=pyproject_path)
94
- config = validate_project_config(config, errors, warnings)
95
- federation, federation_config = validate_federation_in_project_config(
96
- federation, config
97
- )
109
+ pyproject_path = app / "pyproject.toml" if app else None
110
+ config, errors, warnings = load_and_validate(path=pyproject_path)
111
+ config = validate_project_config(config, errors, warnings)
112
+ federation, federation_config = validate_federation_in_project_config(
113
+ federation, config
114
+ )
98
115
 
99
- if "address" in federation_config:
100
- _run_with_exec_api(app, federation_config, config_overrides, stream)
101
- else:
102
- _run_without_exec_api(app, federation_config, config_overrides, federation)
116
+ if "address" in federation_config:
117
+ _run_with_exec_api(
118
+ app, federation_config, config_overrides, stream, output_format
119
+ )
120
+ else:
121
+ _run_without_exec_api(app, federation_config, config_overrides, federation)
122
+ except (typer.Exit, Exception) as err: # pylint: disable=broad-except
123
+ if suppress_output:
124
+ restore_output()
125
+ e_message = captured_output.getvalue()
126
+ _print_json_error(e_message, err)
127
+ else:
128
+ typer.secho(
129
+ f"{err}",
130
+ fg=typer.colors.RED,
131
+ bold=True,
132
+ )
133
+ finally:
134
+ if suppress_output:
135
+ restore_output()
136
+ captured_output.close()
103
137
 
104
138
 
105
139
  # pylint: disable-next=too-many-locals
@@ -108,6 +142,7 @@ def _run_with_exec_api(
108
142
  federation_config: dict[str, Any],
109
143
  config_overrides: Optional[list[str]],
110
144
  stream: bool,
145
+ output_format: str,
111
146
  ) -> None:
112
147
 
113
148
  insecure, root_certificates_bytes = validate_certificate_in_federation_config(
@@ -125,6 +160,7 @@ def _run_with_exec_api(
125
160
 
126
161
  fab_path, fab_hash = build(app)
127
162
  content = Path(fab_path).read_bytes()
163
+ fab_id, fab_version = get_fab_metadata(Path(fab_path))
128
164
 
129
165
  # Delete FAB file once the bytes is computed
130
166
  Path(fab_path).unlink()
@@ -142,7 +178,26 @@ def _run_with_exec_api(
142
178
  )
143
179
  res = stub.StartRun(req)
144
180
 
145
- typer.secho(f"🎊 Successfully started run {res.run_id}", fg=typer.colors.GREEN)
181
+ if res.HasField("run_id"):
182
+ typer.secho(f"🎊 Successfully started run {res.run_id}", fg=typer.colors.GREEN)
183
+ else:
184
+ typer.secho("❌ Failed to start run", fg=typer.colors.RED)
185
+ raise typer.Exit(code=1)
186
+
187
+ if output_format == CliOutputFormat.JSON:
188
+ run_output = json.dumps(
189
+ {
190
+ "success": res.HasField("run_id"),
191
+ "run-id": res.run_id if res.HasField("run_id") else None,
192
+ "fab-id": fab_id,
193
+ "fab-name": fab_id.rsplit("/", maxsplit=1)[-1],
194
+ "fab-version": fab_version,
195
+ "fab-hash": fab_hash[:8],
196
+ "fab-filename": fab_path,
197
+ }
198
+ )
199
+ restore_output()
200
+ Console().print_json(run_output)
146
201
 
147
202
  if stream:
148
203
  start_stream(res.run_id, channel, CONN_REFRESH_PERIOD)
@@ -194,3 +249,15 @@ def _run_without_exec_api(
194
249
  check=True,
195
250
  text=True,
196
251
  )
252
+
253
+
254
+ def _print_json_error(msg: str, e: Union[typer.Exit, Exception]) -> None:
255
+ """Print error message as JSON."""
256
+ Console().print_json(
257
+ json.dumps(
258
+ {
259
+ "success": False,
260
+ "error-message": remove_emojis(str(msg) + "\n" + str(e)),
261
+ }
262
+ )
263
+ )
flwr/common/constant.py CHANGED
@@ -181,3 +181,14 @@ class SubStatus:
181
181
  def __new__(cls) -> SubStatus:
182
182
  """Prevent instantiation."""
183
183
  raise TypeError(f"{cls.__name__} cannot be instantiated.")
184
+
185
+
186
+ class CliOutputFormat:
187
+ """Define output format for `flwr` CLI commands."""
188
+
189
+ DEFAULT = "default"
190
+ JSON = "json"
191
+
192
+ def __new__(cls) -> CliOutputFormat:
193
+ """Prevent instantiation."""
194
+ raise TypeError(f"{cls.__name__} cannot be instantiated.")
flwr/common/logger.py CHANGED
@@ -14,11 +14,12 @@
14
14
  # ==============================================================================
15
15
  """Flower Logger."""
16
16
 
17
-
18
17
  import logging
18
+ import re
19
19
  import sys
20
20
  import threading
21
21
  import time
22
+ from io import StringIO
22
23
  from logging import WARN, LogRecord
23
24
  from logging.handlers import HTTPHandler
24
25
  from queue import Empty, Queue
@@ -303,6 +304,13 @@ def restore_output() -> None:
303
304
  console_handler.stream = sys.stdout
304
305
 
305
306
 
307
+ def redirect_output(output_buffer: StringIO) -> None:
308
+ """Redirect stdout and stderr to text I/O buffer."""
309
+ sys.stdout = output_buffer
310
+ sys.stderr = output_buffer
311
+ console_handler.stream = sys.stdout
312
+
313
+
306
314
  def _log_uploader(
307
315
  log_queue: Queue[Optional[str]], node_id: int, run_id: int, stub: ServerAppIoStub
308
316
  ) -> None:
@@ -366,3 +374,19 @@ def stop_log_uploader(
366
374
  """Stop the log uploader thread."""
367
375
  log_queue.put(None)
368
376
  log_uploader.join()
377
+
378
+
379
+ def remove_emojis(text: str) -> str:
380
+ """Remove emojis from the provided text."""
381
+ emoji_pattern = re.compile(
382
+ "["
383
+ "\U0001F600-\U0001F64F" # Emoticons
384
+ "\U0001F300-\U0001F5FF" # Symbols & Pictographs
385
+ "\U0001F680-\U0001F6FF" # Transport & Map Symbols
386
+ "\U0001F1E0-\U0001F1FF" # Flags
387
+ "\U00002702-\U000027B0" # Dingbats
388
+ "\U000024C2-\U0001F251"
389
+ "]+",
390
+ flags=re.UNICODE,
391
+ )
392
+ return emoji_pattern.sub(r"", text)
flwr/proto/exec_pb2.py CHANGED
@@ -18,7 +18,7 @@ from flwr.proto import recordset_pb2 as flwr_dot_proto_dot_recordset__pb2
18
18
  from flwr.proto import run_pb2 as flwr_dot_proto_dot_run__pb2
19
19
 
20
20
 
21
- DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x66lwr/proto/exec.proto\x12\nflwr.proto\x1a\x14\x66lwr/proto/fab.proto\x1a\x1a\x66lwr/proto/transport.proto\x1a\x1a\x66lwr/proto/recordset.proto\x1a\x14\x66lwr/proto/run.proto\"\xfb\x01\n\x0fStartRunRequest\x12\x1c\n\x03\x66\x61\x62\x18\x01 \x01(\x0b\x32\x0f.flwr.proto.Fab\x12H\n\x0foverride_config\x18\x02 \x03(\x0b\x32/.flwr.proto.StartRunRequest.OverrideConfigEntry\x12\x35\n\x12\x66\x65\x64\x65ration_options\x18\x03 \x01(\x0b\x32\x19.flwr.proto.ConfigsRecord\x1aI\n\x13OverrideConfigEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12!\n\x05value\x18\x02 \x01(\x0b\x32\x12.flwr.proto.Scalar:\x02\x38\x01\"\"\n\x10StartRunResponse\x12\x0e\n\x06run_id\x18\x01 \x01(\x04\"<\n\x11StreamLogsRequest\x12\x0e\n\x06run_id\x18\x01 \x01(\x04\x12\x17\n\x0f\x61\x66ter_timestamp\x18\x02 \x01(\x01\"B\n\x12StreamLogsResponse\x12\x12\n\nlog_output\x18\x01 \x01(\t\x12\x18\n\x10latest_timestamp\x18\x02 \x01(\x01\"1\n\x0fListRunsRequest\x12\x13\n\x06run_id\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\t\n\x07_run_id\"\x9d\x01\n\x10ListRunsResponse\x12;\n\x08run_dict\x18\x01 \x03(\x0b\x32).flwr.proto.ListRunsResponse.RunDictEntry\x12\x0b\n\x03now\x18\x02 \x01(\t\x1a?\n\x0cRunDictEntry\x12\x0b\n\x03key\x18\x01 \x01(\x04\x12\x1e\n\x05value\x18\x02 \x01(\x0b\x32\x0f.flwr.proto.Run:\x02\x38\x01\x32\xe9\x01\n\x04\x45xec\x12G\n\x08StartRun\x12\x1b.flwr.proto.StartRunRequest\x1a\x1c.flwr.proto.StartRunResponse\"\x00\x12O\n\nStreamLogs\x12\x1d.flwr.proto.StreamLogsRequest\x1a\x1e.flwr.proto.StreamLogsResponse\"\x00\x30\x01\x12G\n\x08ListRuns\x12\x1b.flwr.proto.ListRunsRequest\x1a\x1c.flwr.proto.ListRunsResponse\"\x00\x62\x06proto3')
21
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x66lwr/proto/exec.proto\x12\nflwr.proto\x1a\x14\x66lwr/proto/fab.proto\x1a\x1a\x66lwr/proto/transport.proto\x1a\x1a\x66lwr/proto/recordset.proto\x1a\x14\x66lwr/proto/run.proto\"\xfb\x01\n\x0fStartRunRequest\x12\x1c\n\x03\x66\x61\x62\x18\x01 \x01(\x0b\x32\x0f.flwr.proto.Fab\x12H\n\x0foverride_config\x18\x02 \x03(\x0b\x32/.flwr.proto.StartRunRequest.OverrideConfigEntry\x12\x35\n\x12\x66\x65\x64\x65ration_options\x18\x03 \x01(\x0b\x32\x19.flwr.proto.ConfigsRecord\x1aI\n\x13OverrideConfigEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12!\n\x05value\x18\x02 \x01(\x0b\x32\x12.flwr.proto.Scalar:\x02\x38\x01\"2\n\x10StartRunResponse\x12\x13\n\x06run_id\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\t\n\x07_run_id\"<\n\x11StreamLogsRequest\x12\x0e\n\x06run_id\x18\x01 \x01(\x04\x12\x17\n\x0f\x61\x66ter_timestamp\x18\x02 \x01(\x01\"B\n\x12StreamLogsResponse\x12\x12\n\nlog_output\x18\x01 \x01(\t\x12\x18\n\x10latest_timestamp\x18\x02 \x01(\x01\"1\n\x0fListRunsRequest\x12\x13\n\x06run_id\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\t\n\x07_run_id\"\x9d\x01\n\x10ListRunsResponse\x12;\n\x08run_dict\x18\x01 \x03(\x0b\x32).flwr.proto.ListRunsResponse.RunDictEntry\x12\x0b\n\x03now\x18\x02 \x01(\t\x1a?\n\x0cRunDictEntry\x12\x0b\n\x03key\x18\x01 \x01(\x04\x12\x1e\n\x05value\x18\x02 \x01(\x0b\x32\x0f.flwr.proto.Run:\x02\x38\x01\" \n\x0eStopRunRequest\x12\x0e\n\x06run_id\x18\x01 \x01(\x04\"\"\n\x0fStopRunResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x32\xaf\x02\n\x04\x45xec\x12G\n\x08StartRun\x12\x1b.flwr.proto.StartRunRequest\x1a\x1c.flwr.proto.StartRunResponse\"\x00\x12\x44\n\x07StopRun\x12\x1a.flwr.proto.StopRunRequest\x1a\x1b.flwr.proto.StopRunResponse\"\x00\x12O\n\nStreamLogs\x12\x1d.flwr.proto.StreamLogsRequest\x1a\x1e.flwr.proto.StreamLogsResponse\"\x00\x30\x01\x12G\n\x08ListRuns\x12\x1b.flwr.proto.ListRunsRequest\x1a\x1c.flwr.proto.ListRunsResponse\"\x00\x62\x06proto3')
22
22
 
23
23
  _globals = globals()
24
24
  _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
@@ -34,17 +34,21 @@ if _descriptor._USE_C_DESCRIPTORS == False:
34
34
  _globals['_STARTRUNREQUEST_OVERRIDECONFIGENTRY']._serialized_start=316
35
35
  _globals['_STARTRUNREQUEST_OVERRIDECONFIGENTRY']._serialized_end=389
36
36
  _globals['_STARTRUNRESPONSE']._serialized_start=391
37
- _globals['_STARTRUNRESPONSE']._serialized_end=425
38
- _globals['_STREAMLOGSREQUEST']._serialized_start=427
39
- _globals['_STREAMLOGSREQUEST']._serialized_end=487
40
- _globals['_STREAMLOGSRESPONSE']._serialized_start=489
41
- _globals['_STREAMLOGSRESPONSE']._serialized_end=555
42
- _globals['_LISTRUNSREQUEST']._serialized_start=557
43
- _globals['_LISTRUNSREQUEST']._serialized_end=606
44
- _globals['_LISTRUNSRESPONSE']._serialized_start=609
45
- _globals['_LISTRUNSRESPONSE']._serialized_end=766
46
- _globals['_LISTRUNSRESPONSE_RUNDICTENTRY']._serialized_start=703
47
- _globals['_LISTRUNSRESPONSE_RUNDICTENTRY']._serialized_end=766
48
- _globals['_EXEC']._serialized_start=769
49
- _globals['_EXEC']._serialized_end=1002
37
+ _globals['_STARTRUNRESPONSE']._serialized_end=441
38
+ _globals['_STREAMLOGSREQUEST']._serialized_start=443
39
+ _globals['_STREAMLOGSREQUEST']._serialized_end=503
40
+ _globals['_STREAMLOGSRESPONSE']._serialized_start=505
41
+ _globals['_STREAMLOGSRESPONSE']._serialized_end=571
42
+ _globals['_LISTRUNSREQUEST']._serialized_start=573
43
+ _globals['_LISTRUNSREQUEST']._serialized_end=622
44
+ _globals['_LISTRUNSRESPONSE']._serialized_start=625
45
+ _globals['_LISTRUNSRESPONSE']._serialized_end=782
46
+ _globals['_LISTRUNSRESPONSE_RUNDICTENTRY']._serialized_start=719
47
+ _globals['_LISTRUNSRESPONSE_RUNDICTENTRY']._serialized_end=782
48
+ _globals['_STOPRUNREQUEST']._serialized_start=784
49
+ _globals['_STOPRUNREQUEST']._serialized_end=816
50
+ _globals['_STOPRUNRESPONSE']._serialized_start=818
51
+ _globals['_STOPRUNRESPONSE']._serialized_end=852
52
+ _globals['_EXEC']._serialized_start=855
53
+ _globals['_EXEC']._serialized_end=1158
50
54
  # @@protoc_insertion_point(module_scope)
flwr/proto/exec_pb2.pyi CHANGED
@@ -57,9 +57,11 @@ class StartRunResponse(google.protobuf.message.Message):
57
57
  run_id: builtins.int
58
58
  def __init__(self,
59
59
  *,
60
- run_id: builtins.int = ...,
60
+ run_id: typing.Optional[builtins.int] = ...,
61
61
  ) -> None: ...
62
- def ClearField(self, field_name: typing_extensions.Literal["run_id",b"run_id"]) -> None: ...
62
+ def HasField(self, field_name: typing_extensions.Literal["_run_id",b"_run_id","run_id",b"run_id"]) -> builtins.bool: ...
63
+ def ClearField(self, field_name: typing_extensions.Literal["_run_id",b"_run_id","run_id",b"run_id"]) -> None: ...
64
+ def WhichOneof(self, oneof_group: typing_extensions.Literal["_run_id",b"_run_id"]) -> typing.Optional[typing_extensions.Literal["run_id"]]: ...
63
65
  global___StartRunResponse = StartRunResponse
64
66
 
65
67
  class StreamLogsRequest(google.protobuf.message.Message):
@@ -132,3 +134,25 @@ class ListRunsResponse(google.protobuf.message.Message):
132
134
  ) -> None: ...
133
135
  def ClearField(self, field_name: typing_extensions.Literal["now",b"now","run_dict",b"run_dict"]) -> None: ...
134
136
  global___ListRunsResponse = ListRunsResponse
137
+
138
+ class StopRunRequest(google.protobuf.message.Message):
139
+ DESCRIPTOR: google.protobuf.descriptor.Descriptor
140
+ RUN_ID_FIELD_NUMBER: builtins.int
141
+ run_id: builtins.int
142
+ def __init__(self,
143
+ *,
144
+ run_id: builtins.int = ...,
145
+ ) -> None: ...
146
+ def ClearField(self, field_name: typing_extensions.Literal["run_id",b"run_id"]) -> None: ...
147
+ global___StopRunRequest = StopRunRequest
148
+
149
+ class StopRunResponse(google.protobuf.message.Message):
150
+ DESCRIPTOR: google.protobuf.descriptor.Descriptor
151
+ SUCCESS_FIELD_NUMBER: builtins.int
152
+ success: builtins.bool
153
+ def __init__(self,
154
+ *,
155
+ success: builtins.bool = ...,
156
+ ) -> None: ...
157
+ def ClearField(self, field_name: typing_extensions.Literal["success",b"success"]) -> None: ...
158
+ global___StopRunResponse = StopRunResponse