flwr-nightly 1.23.0.dev20251027__py3-none-any.whl → 1.23.0.dev20251028__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
@@ -17,22 +17,25 @@
17
17
 
18
18
  import hashlib
19
19
  import zipfile
20
+ from collections.abc import Iterable
20
21
  from io import BytesIO
21
22
  from pathlib import Path
22
23
  from typing import Annotated, Any, Optional, Union
23
24
 
24
25
  import pathspec
26
+ import tomli
25
27
  import tomli_w
26
28
  import typer
27
29
 
28
30
  from flwr.common.constant import (
29
- FAB_ALLOWED_EXTENSIONS,
31
+ FAB_CONFIG_FILE,
30
32
  FAB_DATE,
33
+ FAB_EXCLUDE_PATTERNS,
31
34
  FAB_HASH_TRUNCATION,
35
+ FAB_INCLUDE_PATTERNS,
32
36
  FAB_MAX_SIZE,
33
37
  )
34
38
 
35
- from .config_utils import load as load_toml
36
39
  from .config_utils import load_and_validate
37
40
  from .utils import is_valid_project_name
38
41
 
@@ -112,7 +115,10 @@ def build(
112
115
  )
113
116
 
114
117
  # Build FAB
115
- fab_bytes, fab_hash, _ = build_fab(app)
118
+ fab_bytes = build_fab_from_disk(app)
119
+
120
+ # Calculate hash for filename
121
+ fab_hash = hashlib.sha256(fab_bytes).hexdigest()
116
122
 
117
123
  # Get the name of the zip file
118
124
  fab_filename = get_fab_filename(config, fab_hash)
@@ -125,11 +131,10 @@ def build(
125
131
  )
126
132
 
127
133
 
128
- def build_fab(app: Path) -> tuple[bytes, str, dict[str, Any]]:
129
- """Build a FAB in memory and return the bytes, hash, and config.
134
+ def build_fab_from_disk(app: Path) -> bytes:
135
+ """Build a FAB from files on disk and return the FAB as bytes.
130
136
 
131
- This function assumes that the provided path points to a valid Flower app and
132
- bundles it into a FAB without performing additional validation.
137
+ This function reads files from disk and bundles them into a FAB.
133
138
 
134
139
  Parameters
135
140
  ----------
@@ -138,18 +143,67 @@ def build_fab(app: Path) -> tuple[bytes, str, dict[str, Any]]:
138
143
 
139
144
  Returns
140
145
  -------
141
- tuple[bytes, str, dict[str, Any]]
142
- A tuple containing:
143
- - the FAB as bytes
144
- - the SHA256 hash of the FAB
145
- - the project configuration (with the 'federations' field removed)
146
+ bytes
147
+ The FAB as bytes.
146
148
  """
147
149
  app = app.resolve()
148
150
 
149
- # Load the pyproject.toml file
150
- config = load_toml(app / "pyproject.toml")
151
- if config is None:
152
- raise ValueError("Project configuration could not be loaded.")
151
+ # Collect all files recursively (including pyproject.toml and .gitignore)
152
+ all_files = [f for f in app.rglob("*") if f.is_file()]
153
+
154
+ # Create dict mapping relative paths to Path objects
155
+ files_dict: dict[str, Union[bytes, Path]] = {
156
+ # Ensure consistent path separators across platforms
157
+ str(file_path.relative_to(app)).replace("\\", "/"): file_path
158
+ for file_path in all_files
159
+ }
160
+
161
+ # Build FAB from the files dict
162
+ return build_fab_from_files(files_dict)
163
+
164
+
165
+ def build_fab_from_files(files: dict[str, Union[bytes, Path]]) -> bytes:
166
+ r"""Build a FAB from in-memory files and return the FAB as bytes.
167
+
168
+ This is the core FAB building function that works with in-memory data.
169
+ It accepts either bytes or Path objects as file contents, applies filtering
170
+ rules (include/exclude patterns), and builds the FAB.
171
+
172
+ Parameters
173
+ ----------
174
+ files : dict[str, Union[bytes, Path]]
175
+ Dictionary mapping relative file paths to their contents.
176
+ - Keys: Relative paths (strings)
177
+ - Values: Either bytes (file contents) or Path (will be read)
178
+ Must include "pyproject.toml" and optionally ".gitignore".
179
+
180
+ Returns
181
+ -------
182
+ bytes
183
+ The FAB as bytes.
184
+
185
+ Examples
186
+ --------
187
+ Build a FAB from in-memory files::
188
+
189
+ files = {
190
+ "pyproject.toml": b"[project]\nname = 'myapp'\n...",
191
+ ".gitignore": b"*.pyc\n__pycache__/\n",
192
+ "src/client.py": Path("/path/to/client.py"),
193
+ "src/server.py": b"print('hello')",
194
+ "README.md": b"# My App\n",
195
+ }
196
+ fab_bytes = build_fab_from_files(files)
197
+ """
198
+
199
+ def to_bytes(content: Union[bytes, Path]) -> bytes:
200
+ return content.read_bytes() if isinstance(content, Path) else content
201
+
202
+ # Extract, load, and parse pyproject.toml
203
+ if FAB_CONFIG_FILE not in files:
204
+ raise ValueError(f"{FAB_CONFIG_FILE} not found in files")
205
+ pyproject_content = to_bytes(files[FAB_CONFIG_FILE])
206
+ config = tomli.loads(pyproject_content.decode("utf-8"))
153
207
 
154
208
  # Remove the 'federations' field if it exists
155
209
  if (
@@ -159,18 +213,22 @@ def build_fab(app: Path) -> tuple[bytes, str, dict[str, Any]]:
159
213
  ):
160
214
  del config["tool"]["flwr"]["federations"]
161
215
 
162
- # Load .gitignore rules if present
163
- ignore_spec = _load_gitignore(app)
216
+ # Extract and load .gitignore if present
217
+ gitignore_content = None
218
+ if ".gitignore" in files:
219
+ gitignore_content = to_bytes(files[".gitignore"])
220
+
221
+ # Get exclude and include specs
222
+ exclude_spec = get_fab_exclude_pathspec(gitignore_content)
223
+ include_spec = get_fab_include_pathspec()
164
224
 
165
- # Search for all files in the app directory
166
- all_files = [
167
- f
168
- for f in app.rglob("*")
169
- if not ignore_spec.match_file(f)
170
- and f.suffix in FAB_ALLOWED_EXTENSIONS
171
- and f.name != "pyproject.toml" # Exclude the original pyproject.toml
225
+ # Filter files based on include/exclude specs
226
+ filtered_paths = [
227
+ path.replace("\\", "/") # Ensure consistent path separators across platforms
228
+ for path in files.keys()
229
+ if include_spec.match_file(path) and not exclude_spec.match_file(path)
172
230
  ]
173
- all_files.sort()
231
+ filtered_paths.sort() # Sort for deterministic output
174
232
 
175
233
  # Create a zip file in memory
176
234
  list_file_content = ""
@@ -178,41 +236,54 @@ def build_fab(app: Path) -> tuple[bytes, str, dict[str, Any]]:
178
236
  fab_buffer = BytesIO()
179
237
  with zipfile.ZipFile(fab_buffer, "w", zipfile.ZIP_DEFLATED) as fab_file:
180
238
  # Add pyproject.toml
181
- write_to_zip(fab_file, "pyproject.toml", tomli_w.dumps(config))
239
+ write_to_zip(fab_file, FAB_CONFIG_FILE, tomli_w.dumps(config))
182
240
 
183
- for file_path in all_files:
184
- # Read the file content manually
185
- file_contents = file_path.read_bytes()
241
+ for file_path in filtered_paths:
186
242
 
187
- archive_path = str(file_path.relative_to(app)).replace("\\", "/")
188
- write_to_zip(fab_file, archive_path, file_contents)
243
+ # Get file contents as bytes
244
+ file_content = to_bytes(files[file_path])
189
245
 
190
- # Calculate file info
191
- sha256_hash = hashlib.sha256(file_contents).hexdigest()
192
- file_size_bits = len(file_contents) * 8 # size in bits
193
- list_file_content += f"{archive_path},{sha256_hash},{file_size_bits}\n"
246
+ # Write file to FAB
247
+ write_to_zip(fab_file, file_path, file_content)
194
248
 
195
- # Add CONTENT and CONTENT.jwt to the zip file
249
+ # Calculate file info for CONTENT manifest
250
+ sha256_hash = hashlib.sha256(file_content).hexdigest()
251
+ file_size_bits = len(file_content) * 8 # size in bits
252
+ list_file_content += f"{file_path},{sha256_hash},{file_size_bits}\n"
253
+
254
+ # Add CONTENT manifest to the zip file
196
255
  write_to_zip(fab_file, ".info/CONTENT", list_file_content)
197
256
 
198
257
  fab_bytes = fab_buffer.getvalue()
258
+
259
+ # Validate FAB size
199
260
  if len(fab_bytes) > FAB_MAX_SIZE:
200
261
  raise ValueError(
201
- f"FAB size exceeds maximum allowed size of {FAB_MAX_SIZE:,} bytes."
262
+ f"FAB size exceeds maximum allowed size of {FAB_MAX_SIZE:,} bytes. "
202
263
  "To reduce the package size, consider ignoring unnecessary files "
203
264
  "via your `.gitignore` file or excluding them from the build."
204
265
  )
205
266
 
206
- fab_hash = hashlib.sha256(fab_bytes).hexdigest()
267
+ return fab_bytes
207
268
 
208
- return fab_bytes, fab_hash, config
209
269
 
270
+ def build_pathspec(patterns: Iterable[str]) -> pathspec.PathSpec:
271
+ """Build a PathSpec from a list of patterns."""
272
+ return pathspec.PathSpec.from_lines("gitwildmatch", patterns)
273
+
274
+
275
+ def get_fab_include_pathspec() -> pathspec.PathSpec:
276
+ """Get the PathSpec for files to include in a FAB."""
277
+ return build_pathspec(FAB_INCLUDE_PATTERNS)
210
278
 
211
- def _load_gitignore(app: Path) -> pathspec.PathSpec:
212
- """Load and parse .gitignore file, returning a pathspec."""
213
- gitignore_path = app / ".gitignore"
214
- patterns = ["__pycache__/"] # Default pattern
215
- if gitignore_path.exists():
216
- with open(gitignore_path, encoding="UTF-8") as file:
217
- patterns.extend(file.readlines())
279
+
280
+ def get_fab_exclude_pathspec(gitignore_content: Optional[bytes]) -> pathspec.PathSpec:
281
+ """Get the PathSpec for files to exclude from a FAB.
282
+
283
+ If gitignore_content is provided, its patterns will be combined with the default
284
+ exclude patterns.
285
+ """
286
+ patterns = list(FAB_EXCLUDE_PATTERNS)
287
+ if gitignore_content:
288
+ patterns += gitignore_content.decode("UTF-8").splitlines()
218
289
  return pathspec.PathSpec.from_lines("gitwildmatch", patterns)
flwr/cli/run/run.py CHANGED
@@ -15,16 +15,18 @@
15
15
  """Flower command line interface `run` command."""
16
16
 
17
17
 
18
+ import hashlib
18
19
  import io
19
20
  import json
20
21
  import subprocess
21
22
  from pathlib import Path
22
- from typing import Annotated, Any, Optional
23
+ from typing import Annotated, Any, Optional, cast
23
24
 
24
25
  import typer
25
26
  from rich.console import Console
26
27
 
27
- from flwr.cli.build import build_fab, get_fab_filename
28
+ from flwr.cli.build import build_fab_from_disk, get_fab_filename
29
+ from flwr.cli.config_utils import load as load_toml
28
30
  from flwr.cli.config_utils import (
29
31
  load_and_validate,
30
32
  process_loaded_project_config,
@@ -37,7 +39,7 @@ from flwr.common.config import (
37
39
  parse_config_args,
38
40
  user_config_to_configrecord,
39
41
  )
40
- from flwr.common.constant import CliOutputFormat
42
+ from flwr.common.constant import FAB_CONFIG_FILE, CliOutputFormat
41
43
  from flwr.common.logger import print_json_error, redirect_output, restore_output
42
44
  from flwr.common.serde import config_record_to_proto, fab_to_proto, user_config_to_proto
43
45
  from flwr.common.typing import Fab
@@ -152,7 +154,9 @@ def _run_with_control_api(
152
154
  channel = init_channel(app, federation_config, auth_plugin)
153
155
  stub = ControlStub(channel)
154
156
 
155
- fab_bytes, fab_hash, config = build_fab(app)
157
+ fab_bytes = build_fab_from_disk(app)
158
+ fab_hash = hashlib.sha256(fab_bytes).hexdigest()
159
+ config = cast(dict[str, Any], load_toml(app / FAB_CONFIG_FILE))
156
160
  fab_id, fab_version = get_metadata_from_config(config)
157
161
 
158
162
  fab = Fab(fab_hash, fab_bytes, {})
flwr/common/constant.py CHANGED
@@ -72,13 +72,23 @@ NODE_ID_NUM_BYTES = 8
72
72
 
73
73
  # Constants for FAB
74
74
  APP_DIR = "apps"
75
- FAB_ALLOWED_EXTENSIONS = {".py", ".toml", ".md"}
76
75
  FAB_CONFIG_FILE = "pyproject.toml"
77
76
  FAB_DATE = (2024, 10, 1, 0, 0, 0)
78
77
  FAB_HASH_TRUNCATION = 8
79
78
  FAB_MAX_SIZE = 10 * 1024 * 1024 # 10 MB
80
79
  FLWR_DIR = ".flwr" # The default Flower directory: ~/.flwr/
81
80
  FLWR_HOME = "FLWR_HOME" # If set, override the default Flower directory
81
+ # FAB file include patterns (gitignore-style patterns)
82
+ FAB_INCLUDE_PATTERNS = (
83
+ "**/*.py",
84
+ "**/*.toml",
85
+ "**/*.md",
86
+ )
87
+ # FAB file exclude patterns (gitignore-style patterns)
88
+ FAB_EXCLUDE_PATTERNS = (
89
+ "**/__pycache__/**",
90
+ FAB_CONFIG_FILE, # Exclude the original pyproject.toml
91
+ )
82
92
 
83
93
  # Constant for SuperLink
84
94
  SUPERLINK_NODE_ID = 1
@@ -411,10 +411,10 @@ class InMemoryLinkState(LinkState): # pylint: disable=R0902,R0904
411
411
  ) -> Sequence[NodeInfo]:
412
412
  """Retrieve information about nodes based on the specified filters."""
413
413
  with self.lock:
414
- self._check_and_tag_deactivated_nodes()
414
+ self._check_and_tag_offline_nodes()
415
415
  result = []
416
- for node in self.nodes.values():
417
- if node_ids is not None and node.node_id not in node_ids:
416
+ for node_id in self.nodes.keys() if node_ids is None else node_ids:
417
+ if (node := self.nodes.get(node_id)) is None:
418
418
  continue
419
419
  if owner_aids is not None and node.owner_aid not in owner_aids:
420
420
  continue
@@ -423,11 +423,15 @@ class InMemoryLinkState(LinkState): # pylint: disable=R0902,R0904
423
423
  result.append(node)
424
424
  return result
425
425
 
426
- def _check_and_tag_deactivated_nodes(self) -> None:
426
+ def _check_and_tag_offline_nodes(
427
+ self, node_ids: Optional[list[int]] = None
428
+ ) -> None:
427
429
  with self.lock:
428
430
  # Set all nodes of "online" status to "offline" if they've offline
429
431
  current_ts = now().timestamp()
430
- for node in self.nodes.values():
432
+ for node_id in node_ids or self.nodes.keys():
433
+ if (node := self.nodes.get(node_id)) is None:
434
+ continue
431
435
  if node.status == NodeStatus.ONLINE:
432
436
  if node.online_until <= current_ts:
433
437
  node.status = NodeStatus.OFFLINE
@@ -99,6 +99,10 @@ SQL_CREATE_INDEX_OWNER_AID = """
99
99
  CREATE INDEX IF NOT EXISTS idx_node_owner_aid ON node(owner_aid);
100
100
  """
101
101
 
102
+ SQL_CREATE_INDEX_NODE_STATUS = """
103
+ CREATE INDEX IF NOT EXISTS idx_node_status ON node(status);
104
+ """
105
+
102
106
  SQL_CREATE_TABLE_RUN = """
103
107
  CREATE TABLE IF NOT EXISTS run(
104
108
  run_id INTEGER UNIQUE,
@@ -199,6 +203,7 @@ class SqliteLinkState(LinkState, SqliteMixin): # pylint: disable=R0904
199
203
  SQL_CREATE_TABLE_TOKEN_STORE,
200
204
  SQL_CREATE_INDEX_ONLINE_UNTIL,
201
205
  SQL_CREATE_INDEX_OWNER_AID,
206
+ SQL_CREATE_INDEX_NODE_STATUS,
202
207
  log_queries=log_queries,
203
208
  )
204
209
 
@@ -638,6 +643,28 @@ class SqliteLinkState(LinkState, SqliteMixin): # pylint: disable=R0904
638
643
  node.node_id for node in self.get_node_info(statuses=[NodeStatus.ONLINE])
639
644
  }
640
645
 
646
+ def _check_and_tag_offline_nodes(
647
+ self, node_ids: Optional[list[int]] = None
648
+ ) -> None:
649
+ """Check and tag offline nodes."""
650
+ # strftime will convert POSIX timestamp to ISO format
651
+ query = """
652
+ UPDATE node SET status = ?,
653
+ last_deactivated_at =
654
+ strftime("%Y-%m-%dT%H:%M:%f+00:00", online_until, "unixepoch")
655
+ WHERE online_until <= ? AND status == ?
656
+ """
657
+ params = [
658
+ NodeStatus.OFFLINE,
659
+ now().timestamp(),
660
+ NodeStatus.ONLINE,
661
+ ]
662
+ if node_ids is not None:
663
+ placeholders = ",".join(["?"] * len(node_ids))
664
+ query += f" AND node_id IN ({placeholders})"
665
+ params.extend(uint64_to_int64(node_id) for node_id in node_ids)
666
+ self.conn.execute(query, params)
667
+
641
668
  def get_node_info(
642
669
  self,
643
670
  *,
@@ -646,29 +673,12 @@ class SqliteLinkState(LinkState, SqliteMixin): # pylint: disable=R0904
646
673
  statuses: Optional[Sequence[str]] = None,
647
674
  ) -> Sequence[NodeInfo]:
648
675
  """Retrieve information about nodes based on the specified filters."""
649
- if self.conn is None:
650
- raise AttributeError("LinkState is not initialized.")
651
-
652
676
  with self.conn:
653
- # Check and tag offline nodes
654
- current_dt = now()
655
- # strftime will convert POSIX timestamp to ISO format
656
- query = """
657
- UPDATE node SET status = ?,
658
- last_deactivated_at =
659
- strftime("%Y-%m-%dT%H:%M:%f+00:00", online_until, "unixepoch")
660
- WHERE online_until <= ? AND status == ?
661
- """
662
- params: list[Any] = [
663
- NodeStatus.OFFLINE,
664
- current_dt.timestamp(),
665
- NodeStatus.ONLINE,
666
- ]
667
- self.conn.execute(query, params)
677
+ self._check_and_tag_offline_nodes()
668
678
 
669
679
  # Build the WHERE clause based on provided filters
670
680
  conditions = []
671
- params = []
681
+ params: list[Any] = []
672
682
  if node_ids is not None:
673
683
  sint64_node_ids = [uint64_to_int64(node_id) for node_id in node_ids]
674
684
  placeholders = ",".join(["?"] * len(sint64_node_ids))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: flwr-nightly
3
- Version: 1.23.0.dev20251027
3
+ Version: 1.23.0.dev20251028
4
4
  Summary: Flower: A Friendly Federated AI Framework
5
5
  License: Apache-2.0
6
6
  Keywords: Artificial Intelligence,Federated AI,Federated Analytics,Federated Evaluation,Federated Learning,Flower,Machine Learning
@@ -9,7 +9,7 @@ flwr/cli/auth_plugin/__init__.py,sha256=qpebWV9uLpx72_J8yTVgT1DlH2Y9MK_RraDoTYEq
9
9
  flwr/cli/auth_plugin/auth_plugin.py,sha256=LK-DYGk8J85Mjsbc5BX9ueDVOOPcFSo9cKWJX25X-QI,3050
10
10
  flwr/cli/auth_plugin/noop_auth_plugin.py,sha256=KRyxoYtrzOWAIlSuXznli9gqL-rmHSG3zESmYSCQ_rg,2093
11
11
  flwr/cli/auth_plugin/oidc_cli_plugin.py,sha256=9Oyd23g-2vgsXhRQdqG9JNocPylGXZM4UVPBB6l0GXA,5147
12
- flwr/cli/build.py,sha256=hE54Q_eMdWLpVKSVC2aQaUxVaiUlWnAosGNvIPSEg6Y,7284
12
+ flwr/cli/build.py,sha256=G3pbe1IlwXZQPM8ZbzpvkhOwuhYQUxhkOF09VVUV3GI,9601
13
13
  flwr/cli/cli_account_auth_interceptor.py,sha256=o_D6-al3TgLRUAX2OZhzLlYF_LXDsK5dLkjbi082JNU,3131
14
14
  flwr/cli/config_utils.py,sha256=o75PJzgCTl9FdFo_I9OjCB02-ykK0VWZdhIAeR0A8QA,9130
15
15
  flwr/cli/constant.py,sha256=LtxufmhkEqNWQ9doWbbbkUKa12vN_RK_Of5u0So-GHA,1729
@@ -84,7 +84,7 @@ flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl,sha256=g7SYiAJr6Uhwg4Zs
84
84
  flwr/cli/new/templates/app/pyproject.xgboost.toml.tpl,sha256=yAJ9jL2q6U_XXYwwUT9fpIqIKFuQR_Kgg82GpWfQ5J8,1661
85
85
  flwr/cli/pull.py,sha256=sT3-f2zf7JcXfB-pHWLXmENrOzJeUDBoqK3oruJxzao,3352
86
86
  flwr/cli/run/__init__.py,sha256=RPyB7KbYTFl6YRiilCch6oezxrLQrl1kijV7BMGkLbA,790
87
- flwr/cli/run/run.py,sha256=f5ysmBytcme8E7feroCtvpLe3kEebSFV42dcqoSFzzY,8165
87
+ flwr/cli/run/run.py,sha256=2QYhD4o0CfChN68eV0MFZULP3G1DoQInNKrWsupu0Dw,8386
88
88
  flwr/cli/stop.py,sha256=W7ynTYLm0-_1nC5Il4IaZTji6A3GCCm_0R-HQUudAsI,4988
89
89
  flwr/cli/supernode/__init__.py,sha256=DBkjoPo2hS2Y-ghJxwLbrAbCQFBgD82_Itl2_892UBo,917
90
90
  flwr/cli/supernode/ls.py,sha256=OVEs9zPSomxxiwCg1Jz8AN5MkZJDf3xAE-lxCG22zyE,8640
@@ -126,7 +126,7 @@ flwr/common/__init__.py,sha256=5GCLVk399Az_rTJHNticRlL0Sl_oPw_j5_LuFKfX7-M,4171
126
126
  flwr/common/address.py,sha256=9JucdTwlc-jpeJkRKeUboZoacUtErwSVtnDR9kAtLqE,4119
127
127
  flwr/common/args.py,sha256=Nq2u4yePbkSY0CWFamn0hZY6Rms8G1xYDeDGIcLIITE,5849
128
128
  flwr/common/config.py,sha256=glcZDjco-amw1YfQcYTFJ4S1pt9APoexT-mf1QscuHs,13960
129
- flwr/common/constant.py,sha256=y9U6LdgMzNYe-LGTjooGn0QJj5MVTlWjbriE1l5rkag,9898
129
+ flwr/common/constant.py,sha256=EjVoFZZgkxREzd312A2K4LlNk8H27rU4hWGW-CZ9qCI,10145
130
130
  flwr/common/context.py,sha256=Be8obQR_OvEDy1OmshuUKxGRQ7Qx89mf5F4xlhkR10s,2407
131
131
  flwr/common/date.py,sha256=1ZT2cRSpC2DJqprOVTLXYCR_O2_OZR0zXO_brJ3LqWc,1554
132
132
  flwr/common/differential_privacy.py,sha256=FdlpdpPl_H_2HJa8CQM1iCUGBBQ5Dc8CzxmHERM-EoE,6148
@@ -317,10 +317,10 @@ flwr/server/superlink/fleet/vce/backend/backend.py,sha256=cSrHZ5SjCCvy4vI0pgsyjt
317
317
  flwr/server/superlink/fleet/vce/backend/raybackend.py,sha256=cBZYTmfiAsb1HmVUmOQXYLU-UJmJTFWkj1wW4RYRDuc,7218
318
318
  flwr/server/superlink/fleet/vce/vce_api.py,sha256=sgsAx7dgIADA-Ae71s5dwoPY0bw3-qA7RukJgnh8pgc,13389
319
319
  flwr/server/superlink/linkstate/__init__.py,sha256=OtsgvDTnZLU3k0sUbkHbqoVwW6ql2FDmb6uT6DbNkZo,1064
320
- flwr/server/superlink/linkstate/in_memory_linkstate.py,sha256=9JBJSWh7TAEf7WFcpdn03rL0tLrroVUPZcyNJd8Qz88,29997
320
+ flwr/server/superlink/linkstate/in_memory_linkstate.py,sha256=vtQZnVIQuLX6rOvKg6qLdPRjfWbxKyE84rEXhlFFdbI,30168
321
321
  flwr/server/superlink/linkstate/linkstate.py,sha256=JKgVEPnH2T-nixW3LHp8jR3g4ITAZYNwEoDIWZaUYmQ,14701
322
322
  flwr/server/superlink/linkstate/linkstate_factory.py,sha256=KVBpc8UxVrJCQ7IkOjVZVWb-kqWx08AGVi7qXzZS190,2126
323
- flwr/server/superlink/linkstate/sqlite_linkstate.py,sha256=vmD74L8fphb0xIhArQUg1p7oy_k6nEzxXIZHCSxFxh0,45219
323
+ flwr/server/superlink/linkstate/sqlite_linkstate.py,sha256=1r9Jo4GLKlyhRzFOic-J5uv2Dbk6qZ0wNKgOZyTJZSo,45555
324
324
  flwr/server/superlink/linkstate/utils.py,sha256=ZtyqSo4HzGrHXW3Wn_4irYMpIiq4onNI2XCIVOOJmJM,13971
325
325
  flwr/server/superlink/serverappio/__init__.py,sha256=Fy4zJuoccZe5mZSEIpOmQvU6YeXFBa1M4eZuXXmJcn8,717
326
326
  flwr/server/superlink/serverappio/serverappio_grpc.py,sha256=-I7kBbr4w4ZVYwBZoAIle-xHKthFnZrsVfxa6WR8uxA,2310
@@ -433,7 +433,7 @@ flwr/supernode/servicer/__init__.py,sha256=lucTzre5WPK7G1YLCfaqg3rbFWdNSb7ZTt-ca
433
433
  flwr/supernode/servicer/clientappio/__init__.py,sha256=7Oy62Y_oijqF7Dxi6tpcUQyOpLc_QpIRZ83NvwmB0Yg,813
434
434
  flwr/supernode/servicer/clientappio/clientappio_servicer.py,sha256=ZvKosLV7GN1_fOF-tOmhqFQysYQywCysRc-m23DVIWA,10265
435
435
  flwr/supernode/start_client_internal.py,sha256=wKqh9-_rQYi7JFKpYBLRiUeq9YJUSyUd9b-SnVnuvwI,21567
436
- flwr_nightly-1.23.0.dev20251027.dist-info/METADATA,sha256=PvUk4HrZo9-Ztc5hxec-ihDiAaIq8QFxdrZkid0eNe4,14559
437
- flwr_nightly-1.23.0.dev20251027.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
438
- flwr_nightly-1.23.0.dev20251027.dist-info/entry_points.txt,sha256=hxHD2ixb_vJFDOlZV-zB4Ao32_BQlL34ftsDh1GXv14,420
439
- flwr_nightly-1.23.0.dev20251027.dist-info/RECORD,,
436
+ flwr_nightly-1.23.0.dev20251028.dist-info/METADATA,sha256=_BUO-CINcA_9ZN6iiZpJSlOLP_D6K9VIgBvxDuYI37A,14559
437
+ flwr_nightly-1.23.0.dev20251028.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
438
+ flwr_nightly-1.23.0.dev20251028.dist-info/entry_points.txt,sha256=hxHD2ixb_vJFDOlZV-zB4Ao32_BQlL34ftsDh1GXv14,420
439
+ flwr_nightly-1.23.0.dev20251028.dist-info/RECORD,,