flwr-nightly 1.20.0.dev20250627__py3-none-any.whl → 1.20.0.dev20250701__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.
flwr/cli/utils.py CHANGED
@@ -320,8 +320,7 @@ def flwr_cli_grpc_exc_handler() -> Iterator[None]:
320
320
  raise typer.Exit(code=1) from None
321
321
  if e.code() == grpc.StatusCode.PERMISSION_DENIED:
322
322
  typer.secho(
323
- "❌ Authorization failed. Please contact your administrator"
324
- " to check your permissions.",
323
+ "❌ Permission denied. Please contact the SuperLink administrator.",
325
324
  fg=typer.colors.RED,
326
325
  bold=True,
327
326
  )
flwr/common/constant.py CHANGED
@@ -135,6 +135,7 @@ GC_THRESHOLD = 200_000_000 # 200 MB
135
135
  # Constants for Inflatable
136
136
  HEAD_BODY_DIVIDER = b"\x00"
137
137
  HEAD_VALUE_DIVIDER = " "
138
+ MAX_ARRAY_CHUNK_SIZE = 20_971_520 # 20 MB
138
139
 
139
140
  # Constants for serialization
140
141
  INT64_MAX_VALUE = 9223372036854775807 # (1 << 63) - 1
@@ -29,6 +29,7 @@ class ExitCode:
29
29
 
30
30
  # SuperLink-specific exit codes (100-199)
31
31
  SUPERLINK_THREAD_CRASH = 100
32
+ SUPERLINK_LICENSE_INVALID = 101
32
33
 
33
34
  # ServerApp-specific exit codes (200-299)
34
35
 
@@ -60,6 +61,10 @@ EXIT_CODE_HELP = {
60
61
  ExitCode.GRACEFUL_EXIT_SIGTERM: "",
61
62
  # SuperLink-specific exit codes (100-199)
62
63
  ExitCode.SUPERLINK_THREAD_CRASH: "An important background thread has crashed.",
64
+ ExitCode.SUPERLINK_LICENSE_INVALID: (
65
+ "The license is invalid or has expired. "
66
+ "Please contact `hello@flower.ai` for assistance."
67
+ ),
63
68
  # ServerApp-specific exit codes (200-299)
64
69
  # SuperNode-specific exit codes (300-399)
65
70
  ExitCode.SUPERNODE_REST_ADDRESS_INVALID: (
@@ -40,9 +40,11 @@ from .inflatable import (
40
40
  )
41
41
  from .message import Message
42
42
  from .record import Array, ArrayRecord, ConfigRecord, MetricRecord, RecordDict
43
+ from .record.arraychunk import ArrayChunk
43
44
 
44
45
  # Helper registry that maps names of classes to their type
45
46
  inflatable_class_registry: dict[str, type[InflatableObject]] = {
47
+ ArrayChunk.__qualname__: ArrayChunk,
46
48
  Array.__qualname__: Array,
47
49
  ArrayRecord.__qualname__: ArrayRecord,
48
50
  ConfigRecord.__qualname__: ConfigRecord,
@@ -17,6 +17,7 @@
17
17
 
18
18
  from __future__ import annotations
19
19
 
20
+ import json
20
21
  import sys
21
22
  from dataclasses import dataclass
22
23
  from io import BytesIO
@@ -24,11 +25,15 @@ from typing import TYPE_CHECKING, Any, cast, overload
24
25
 
25
26
  import numpy as np
26
27
 
27
- from flwr.proto.recorddict_pb2 import Array as ArrayProto # pylint: disable=E0611
28
-
29
- from ..constant import SType
30
- from ..inflatable import InflatableObject, add_header_to_object_body, get_object_body
28
+ from ..constant import MAX_ARRAY_CHUNK_SIZE, SType
29
+ from ..inflatable import (
30
+ InflatableObject,
31
+ add_header_to_object_body,
32
+ get_object_body,
33
+ get_object_children_ids_from_object_content,
34
+ )
31
35
  from ..typing import NDArray
36
+ from .arraychunk import ArrayChunk
32
37
 
33
38
  if TYPE_CHECKING:
34
39
  import torch
@@ -252,16 +257,48 @@ class Array(InflatableObject):
252
257
  ndarray_deserialized = np.load(bytes_io, allow_pickle=False)
253
258
  return cast(NDArray, ndarray_deserialized)
254
259
 
260
+ @property
261
+ def children(self) -> dict[str, InflatableObject]:
262
+ """Return a dictionary of ArrayChunks with their Object IDs as keys."""
263
+ return dict(self.slice_array())
264
+
265
+ def slice_array(self) -> list[tuple[str, InflatableObject]]:
266
+ """Slice Array data and construct a list of ArrayChunks."""
267
+ children: list[tuple[str, InflatableObject]] = []
268
+ # memoryview allows for zero-copy slicing
269
+ data_view = memoryview(self.data)
270
+ for start in range(0, len(data_view), MAX_ARRAY_CHUNK_SIZE):
271
+ end = min(start + MAX_ARRAY_CHUNK_SIZE, len(data_view))
272
+ ac = ArrayChunk(data_view[start:end])
273
+ children.append((ac.object_id, ac))
274
+ return children
275
+
255
276
  def deflate(self) -> bytes:
256
277
  """Deflate the Array."""
257
- array_proto = ArrayProto(
258
- dtype=self.dtype,
259
- shape=self.shape,
260
- stype=self.stype,
261
- data=self.data,
262
- )
263
-
264
- obj_body = array_proto.SerializeToString(deterministic=True)
278
+ array_metadata: dict[str, str | tuple[int, ...] | list[int]] = {}
279
+
280
+ # We want to record all object_id even if repeated
281
+ # it can happend that chunks carry the exact same data
282
+ # for example when the array has only zeros
283
+ children_list = self.slice_array()
284
+ # Let's not save the entire object_id but a mapping to those
285
+ # that will be carried in the object head
286
+ # (replace a long object_id with a single scalar)
287
+ unique_children = list(self.children.keys())
288
+ arraychunk_ids = [unique_children.index(ch_id) for ch_id, _ in children_list]
289
+
290
+ # The deflated Array carries everything but the data
291
+ # The `arraychunk_ids` will be used during Array inflation
292
+ # to rematerialize the data from ArrayChunk objects.
293
+ array_metadata = {
294
+ "dtype": self.dtype,
295
+ "shape": self.shape,
296
+ "stype": self.stype,
297
+ "arraychunk_ids": arraychunk_ids,
298
+ }
299
+
300
+ # Serialize metadata dict
301
+ obj_body = json.dumps(array_metadata).encode("utf-8")
265
302
  return add_header_to_object_body(object_body=obj_body, obj=self)
266
303
 
267
304
  @classmethod
@@ -276,26 +313,55 @@ class Array(InflatableObject):
276
313
  The deflated object content of the Array.
277
314
 
278
315
  children : Optional[dict[str, InflatableObject]] (default: None)
279
- Must be ``None``. ``Array`` does not support child objects.
280
- Providing any children will raise a ``ValueError``.
316
+ Must be ``None``. ``Array`` must have child objects.
317
+ Providing no children will raise a ``ValueError``.
281
318
 
282
319
  Returns
283
320
  -------
284
321
  Array
285
322
  The inflated Array.
286
323
  """
287
- if children:
288
- raise ValueError("`Array` objects do not have children.")
324
+ if not children:
325
+ raise ValueError("`Array` objects must have children.")
289
326
 
290
327
  obj_body = get_object_body(object_content, cls)
291
- proto_array = ArrayProto.FromString(obj_body)
292
- return cls(
293
- dtype=proto_array.dtype,
294
- shape=tuple(proto_array.shape),
295
- stype=proto_array.stype,
296
- data=proto_array.data,
328
+
329
+ # Extract children IDs from head
330
+ children_ids = get_object_children_ids_from_object_content(object_content)
331
+ # Decode the Array body
332
+ array_metadata: dict[str, str | tuple[int, ...] | list[int]] = json.loads(
333
+ obj_body.decode(encoding="utf-8")
297
334
  )
298
335
 
336
+ # Verify children ids in body match those passed for inflation
337
+ chunk_ids_indices = cast(list[int], array_metadata["arraychunk_ids"])
338
+ # Convert indices back to IDs
339
+ chunk_ids = [children_ids[i] for i in chunk_ids_indices]
340
+ # Check consistency
341
+ unique_arrayschunks = set(chunk_ids)
342
+ children_obj_ids = set(children.keys())
343
+ if unique_arrayschunks != children_obj_ids:
344
+ raise ValueError(
345
+ "Unexpected set of `children`. "
346
+ f"Expected {unique_arrayschunks} but got {children_obj_ids}."
347
+ )
348
+
349
+ # Materialize Array with empty data
350
+ array = cls(
351
+ dtype=cast(str, array_metadata["dtype"]),
352
+ shape=cast(tuple[int], tuple(array_metadata["shape"])),
353
+ stype=cast(str, array_metadata["stype"]),
354
+ data=b"",
355
+ )
356
+
357
+ # Now inject data from chunks
358
+ buff = bytearray()
359
+ for ch_id in chunk_ids:
360
+ buff += cast(ArrayChunk, children[ch_id]).data
361
+
362
+ array.data = bytes(buff)
363
+ return array
364
+
299
365
  @property
300
366
  def object_id(self) -> str:
301
367
  """Get object ID."""
@@ -0,0 +1,62 @@
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+ """ArrayChunk."""
16
+
17
+
18
+ from __future__ import annotations
19
+
20
+ from dataclasses import dataclass
21
+
22
+ from ..inflatable import InflatableObject, add_header_to_object_body, get_object_body
23
+
24
+
25
+ @dataclass
26
+ class ArrayChunk(InflatableObject):
27
+ """ArrayChunk type."""
28
+
29
+ data: memoryview
30
+
31
+ def __init__(self, data: bytes) -> None:
32
+ self.data = memoryview(data)
33
+
34
+ def deflate(self) -> bytes:
35
+ """Deflate the ArrayChunk."""
36
+ return add_header_to_object_body(object_body=self.data, obj=self)
37
+
38
+ @classmethod
39
+ def inflate(
40
+ cls, object_content: bytes, children: dict[str, InflatableObject] | None = None
41
+ ) -> ArrayChunk:
42
+ """Inflate an ArrayChunk from bytes.
43
+
44
+ Parameters
45
+ ----------
46
+ object_content : bytes
47
+ The deflated object content of the ArrayChunk.
48
+
49
+ children : Optional[dict[str, InflatableObject]] (default: None)
50
+ Must be ``None``. ``ArrayChunk`` does not support child objects.
51
+ Providing any children will raise a ``ValueError``.
52
+
53
+ Returns
54
+ -------
55
+ ArrayChunk
56
+ The inflated ArrayChunk.
57
+ """
58
+ if children:
59
+ raise ValueError("`ArrayChunk` objects do not have children.")
60
+
61
+ obj_body = get_object_body(object_content, cls)
62
+ return cls(data=memoryview(obj_body))
@@ -0,0 +1,22 @@
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+ """Flower license plugin components."""
16
+
17
+
18
+ from .license_plugin import LicensePlugin as LicensePlugin
19
+
20
+ __all__ = [
21
+ "LicensePlugin",
22
+ ]
@@ -0,0 +1,34 @@
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+ """Abstract class for Flower License Plugin."""
16
+
17
+
18
+ from abc import ABC, abstractmethod
19
+
20
+
21
+ class LicensePlugin(ABC):
22
+ """Abstract Flower License Plugin class."""
23
+
24
+ @abstractmethod
25
+ def __init__(self) -> None:
26
+ """Abstract constructor."""
27
+
28
+ @abstractmethod
29
+ def check_license(self) -> bool:
30
+ """Check if the license is valid."""
31
+
32
+ @abstractmethod
33
+ def get_license_info(self) -> None:
34
+ """Get information about the license."""
@@ -23,21 +23,31 @@ import grpc
23
23
  from flwr.common import GRPC_MAX_MESSAGE_LENGTH
24
24
  from flwr.common.auth_plugin import ExecAuthPlugin, ExecAuthzPlugin
25
25
  from flwr.common.event_log_plugin import EventLogWriterPlugin
26
+ from flwr.common.exit import ExitCode, flwr_exit
26
27
  from flwr.common.grpc import generic_create_grpc_server
27
28
  from flwr.common.logger import log
28
29
  from flwr.common.typing import UserConfig
29
30
  from flwr.proto.exec_pb2_grpc import add_ExecServicer_to_server
30
31
  from flwr.server.superlink.linkstate import LinkStateFactory
31
32
  from flwr.supercore.ffs import FfsFactory
33
+ from flwr.supercore.license_plugin import LicensePlugin
32
34
  from flwr.supercore.object_store import ObjectStoreFactory
33
35
  from flwr.superexec.exec_event_log_interceptor import ExecEventLogInterceptor
36
+ from flwr.superexec.exec_license_interceptor import ExecLicenseInterceptor
34
37
  from flwr.superexec.exec_user_auth_interceptor import ExecUserAuthInterceptor
35
38
 
36
39
  from .exec_servicer import ExecServicer
37
40
  from .executor import Executor
38
41
 
42
+ try:
43
+ from flwr.ee import get_license_plugin
44
+ except ImportError:
39
45
 
40
- # pylint: disable-next=too-many-arguments, too-many-positional-arguments
46
+ def get_license_plugin() -> Optional[LicensePlugin]:
47
+ """Return the license plugin."""
48
+
49
+
50
+ # pylint: disable-next=too-many-arguments,too-many-positional-arguments,too-many-locals
41
51
  def run_exec_api_grpc(
42
52
  address: str,
43
53
  executor: Executor,
@@ -53,6 +63,12 @@ def run_exec_api_grpc(
53
63
  """Run Exec API (gRPC, request-response)."""
54
64
  executor.set_config(config)
55
65
 
66
+ license_plugin: Optional[LicensePlugin] = get_license_plugin()
67
+ if license_plugin:
68
+ license_plugin.get_license_info()
69
+ if not license_plugin.check_license():
70
+ flwr_exit(ExitCode.SUPERLINK_LICENSE_INVALID)
71
+
56
72
  exec_servicer: grpc.Server = ExecServicer(
57
73
  linkstate_factory=state_factory,
58
74
  ffs_factory=ffs_factory,
@@ -61,6 +77,8 @@ def run_exec_api_grpc(
61
77
  auth_plugin=auth_plugin,
62
78
  )
63
79
  interceptors: list[grpc.ServerInterceptor] = []
80
+ if license_plugin is not None:
81
+ interceptors.append(ExecLicenseInterceptor(license_plugin))
64
82
  if auth_plugin is not None and authz_plugin is not None:
65
83
  interceptors.append(ExecUserAuthInterceptor(auth_plugin, authz_plugin))
66
84
  # Event log interceptor must be added after user auth interceptor
@@ -0,0 +1,74 @@
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+ """Flower Exec API license interceptor."""
16
+
17
+
18
+ from collections.abc import Iterator
19
+ from typing import Any, Callable, Union
20
+
21
+ import grpc
22
+ from google.protobuf.message import Message as GrpcMessage
23
+
24
+ from flwr.supercore.license_plugin import LicensePlugin
25
+
26
+
27
+ class ExecLicenseInterceptor(grpc.ServerInterceptor): # type: ignore
28
+ """Exec API interceptor for license checking."""
29
+
30
+ def __init__(self, license_plugin: LicensePlugin) -> None:
31
+ """Initialize the interceptor with a license plugin."""
32
+ self.license_plugin = license_plugin
33
+
34
+ def intercept_service(
35
+ self,
36
+ continuation: Callable[[Any], Any],
37
+ handler_call_details: grpc.HandlerCallDetails,
38
+ ) -> grpc.RpcMethodHandler:
39
+ """Flower server interceptor license logic.
40
+
41
+ Intercept all unary-unary/unary-stream calls from users and check the license.
42
+ Continue RPC call if license check is enabled and passes, else, terminate RPC
43
+ call by setting context to abort.
44
+ """
45
+ # One of the method handlers in
46
+ # `flwr.superexec.exec_servicer.ExecServicer`
47
+ method_handler: grpc.RpcMethodHandler = continuation(handler_call_details)
48
+ return self._generic_license_unary_method_handler(method_handler)
49
+
50
+ def _generic_license_unary_method_handler(
51
+ self, method_handler: grpc.RpcMethodHandler
52
+ ) -> grpc.RpcMethodHandler:
53
+ def _generic_method_handler(
54
+ request: GrpcMessage,
55
+ context: grpc.ServicerContext,
56
+ ) -> Union[GrpcMessage, Iterator[GrpcMessage]]:
57
+ """Handle the method call with license checking."""
58
+ call = method_handler.unary_unary or method_handler.unary_stream
59
+
60
+ if not self.license_plugin.check_license():
61
+ context.abort(grpc.StatusCode.PERMISSION_DENIED, "License check failed")
62
+ raise grpc.RpcError()
63
+
64
+ return call(request, context) # type: ignore
65
+
66
+ if method_handler.unary_unary:
67
+ message_handler = grpc.unary_unary_rpc_method_handler
68
+ else:
69
+ message_handler = grpc.unary_stream_rpc_method_handler
70
+ return message_handler(
71
+ _generic_method_handler,
72
+ request_deserializer=method_handler.request_deserializer,
73
+ response_serializer=method_handler.response_serializer,
74
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: flwr-nightly
3
- Version: 1.20.0.dev20250627
3
+ Version: 1.20.0.dev20250701
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
@@ -73,7 +73,7 @@ flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl,sha256=3-gDei-K7zskh5dD
73
73
  flwr/cli/run/__init__.py,sha256=RPyB7KbYTFl6YRiilCch6oezxrLQrl1kijV7BMGkLbA,790
74
74
  flwr/cli/run/run.py,sha256=psmr215gkV0e0QtX9NFp7KUwKSA_ZwekdJmoL1zFyfw,8478
75
75
  flwr/cli/stop.py,sha256=l8DcRkA---CESVJgc7iTHLWIBAPGxWIfoem8qSU3lZQ,4972
76
- flwr/cli/utils.py,sha256=Vyxpd8LOn2tg5wwId18CHDKq2xaoFDxHMnMW0IlG9Yw,12048
76
+ flwr/cli/utils.py,sha256=X7Jdvwf70bkOpKynFgMGVYPMDxLQiSVG8Y3G1Y9iRjs,12009
77
77
  flwr/client/__init__.py,sha256=boIhKaK6I977zrILmoTutNx94x5jB0e6F1gnAjaRJnI,1250
78
78
  flwr/client/client.py,sha256=3HAchxvknKG9jYbB7swNyDj-e5vUWDuMKoLvbT7jCVM,7895
79
79
  flwr/client/client_app.py,sha256=zVhi-l3chAb06ozFsKwix3hU_RpOLjST13Ha50AVIPE,16918
@@ -108,7 +108,7 @@ flwr/common/args.py,sha256=-aX_jVnSaDrJR2KZ8Wq0Y3dQHII4R4MJtJOIXzVUA0c,5417
108
108
  flwr/common/auth_plugin/__init__.py,sha256=3rzPkVLn9WyB5n7HLk1XGDw3SLCqRWAU1_CnglcWPfw,970
109
109
  flwr/common/auth_plugin/auth_plugin.py,sha256=kXx5o39vJchaPv28sK9qO6H_UXSWym6zRBbCa7sUwtQ,4825
110
110
  flwr/common/config.py,sha256=glcZDjco-amw1YfQcYTFJ4S1pt9APoexT-mf1QscuHs,13960
111
- flwr/common/constant.py,sha256=HO1y0YYHZUCIDt5QQnvCTSYVeKhkIJve0RbQ3nW7jHU,8191
111
+ flwr/common/constant.py,sha256=HMLDvtiWGcGUfHpyyMx1rau34eXdMuBsvn-koRD8-pQ,8234
112
112
  flwr/common/context.py,sha256=Be8obQR_OvEDy1OmshuUKxGRQ7Qx89mf5F4xlhkR10s,2407
113
113
  flwr/common/date.py,sha256=1ZT2cRSpC2DJqprOVTLXYCR_O2_OZR0zXO_brJ3LqWc,1554
114
114
  flwr/common/differential_privacy.py,sha256=FdlpdpPl_H_2HJa8CQM1iCUGBBQ5Dc8CzxmHERM-EoE,6148
@@ -118,21 +118,22 @@ flwr/common/event_log_plugin/__init__.py,sha256=ts3VAL3Fk6Grp1EK_1Qg_V-BfOof9F86
118
118
  flwr/common/event_log_plugin/event_log_plugin.py,sha256=eK8OaDFagQRwqpb9eV0cJcm2ErtEBpMxFbhxJNx6n5w,2061
119
119
  flwr/common/exit/__init__.py,sha256=-ZOJYLaNnR729a7VzZiFsLiqngzKQh3xc27svYStZ_Q,826
120
120
  flwr/common/exit/exit.py,sha256=mJgbqMlVlwAgYtq-Vedj53wO4VxcDcy_P-GzqGK-1GQ,3452
121
- flwr/common/exit/exit_code.py,sha256=PNEnCrZfOILjfDAFu5m-2YWEJBrk97xglq4zCUlqV7E,3470
121
+ flwr/common/exit/exit_code.py,sha256=rvr9ftYL3yp9vJF0epkx8S61R1xcivgPPBJsiS_al9A,3664
122
122
  flwr/common/exit_handlers.py,sha256=IaqJ60fXZuu7McaRYnoYKtlbH9t4Yl9goNExKqtmQbs,4304
123
123
  flwr/common/grpc.py,sha256=manTaHaPiyYngUq1ErZvvV2B2GxlXUUUGRy3jc3TBIQ,9798
124
124
  flwr/common/heartbeat.py,sha256=SyEpNDnmJ0lni0cWO67rcoJVKasCLmkNHm3dKLeNrLU,5749
125
125
  flwr/common/inflatable.py,sha256=GDL9oBKs16_yyVdlH6kBf493O5xll_h9V7XB5Mpx1Hc,9524
126
126
  flwr/common/inflatable_grpc_utils.py,sha256=ZpwtgF1tGD6NwQkCidbhbeBPDBZ1Nx9eGMHQ04eNEE8,3554
127
127
  flwr/common/inflatable_rest_utils.py,sha256=KiZd06XRiXcl_WewOrag0JTvUQt5kZ74UIsQ3FCAXGc,3580
128
- flwr/common/inflatable_utils.py,sha256=-GTdgR1zLS9WtXrbOGJMpaoyVEL8KmoQ2yF4HeLxTI0,12406
128
+ flwr/common/inflatable_utils.py,sha256=XfgkPl8GKuR7tuOpYVZ3zK4n-LbgMZtwoNxMfUbpiCE,12489
129
129
  flwr/common/logger.py,sha256=JbRf6E2vQxXzpDBq1T8IDUJo_usu3gjWEBPQ6uKcmdg,13049
130
130
  flwr/common/message.py,sha256=xAL7iZN5-n-xPQpgoSFvxNrzs8fmiiPfoU0DjNQEhRw,19953
131
131
  flwr/common/object_ref.py,sha256=p3SfTeqo3Aj16SkB-vsnNn01zswOPdGNBitcbRnqmUk,9134
132
132
  flwr/common/parameter.py,sha256=UVw6sOgehEFhFs4uUCMl2kfVq1PD6ncmWgPLMsZPKPE,2095
133
133
  flwr/common/pyproject.py,sha256=2SU6yJW7059SbMXgzjOdK1GZRWO6AixDH7BmdxbMvHI,1386
134
134
  flwr/common/record/__init__.py,sha256=cNGccdDoxttqgnUgyKRIqLWULjW-NaSmOufVxtXq-sw,1197
135
- flwr/common/record/array.py,sha256=3K01tAf_jedub2r2-vkRshbsjBSiKErAO4KqDgdDaSo,11776
135
+ flwr/common/record/array.py,sha256=oApWRwxh1bY2MivQcAz9YHkrspkYZVvy1OkBsWolMec,14541
136
+ flwr/common/record/arraychunk.py,sha256=2ubMzsRNZFL4cc-wXNrJkcTSJUD3nL8CeX5PZpEhNSo,2019
136
137
  flwr/common/record/arrayrecord.py,sha256=CpoqYXM6Iv4XEc9SryCMYmw-bIvP8ut6xWJzRwYJzdU,18008
137
138
  flwr/common/record/configrecord.py,sha256=G7U0q39kB0Kyi0zMxFmPxcVemL9NgwVS1qjvI4BRQuU,9763
138
139
  flwr/common/record/conversion_utils.py,sha256=wbNCzy7oAqaA3-arhls_EqRZYXRC4YrWIoE-Gy82fJ0,1191
@@ -331,6 +332,8 @@ flwr/supercore/ffs/__init__.py,sha256=U3KXwG_SplEvchat27K0LYPoPHzh-cwwT_NHsGlYMt
331
332
  flwr/supercore/ffs/disk_ffs.py,sha256=c5VywSaRnq3XM_zuJptNtsF2HFwsRK0pvBd5-5CNONs,3272
332
333
  flwr/supercore/ffs/ffs.py,sha256=6w7wy71i7tbuJwqEgdeCa49JejXMEof3jujURN_R7Rg,2395
333
334
  flwr/supercore/ffs/ffs_factory.py,sha256=pK-g3LMelvWTV6N9Cd-j-_-FdcGbRFTKNsWaqmlBDSk,1490
335
+ flwr/supercore/license_plugin/__init__.py,sha256=d8OgHTn2BwjoNSPy8jQQxTC_iT3-ENLwKM8yhHKvCRM,820
336
+ flwr/supercore/license_plugin/license_plugin.py,sha256=mqr9jn3WxukXembW8EmxsbGJ6kc4ANfc2U9ehmUm5MQ,1147
334
337
  flwr/supercore/object_store/__init__.py,sha256=cdfPAmjINY6iOp8oI_LdcVh2simg469Mkdl4LLV4kHI,911
335
338
  flwr/supercore/object_store/in_memory_object_store.py,sha256=oflJcOuVNgx9A-B2da4VHDb1qj_Jub9wKFOrUBgtz_U,9630
336
339
  flwr/supercore/object_store/object_store.py,sha256=VlZz-yzoWZtITbnYD8vwLZbFosv7vgr1XVNzByObeY0,5853
@@ -340,7 +343,8 @@ flwr/superexec/__init__.py,sha256=YFqER0IJc1XEWfsX6AxZ9LSRq0sawPYrNYki-brvTIc,71
340
343
  flwr/superexec/app.py,sha256=U2jjOHb2LGWoU7vrl9_czTzre9O2mPxu3CPGUZ86sK4,1465
341
344
  flwr/superexec/deployment.py,sha256=cFxhFom-0zv93HLNjNcUdBy3Sf6JwshRoXPQtcZunF0,6797
342
345
  flwr/superexec/exec_event_log_interceptor.py,sha256=7aBjZ4lkpOIyWut0s394OpMePr16g_Te594s-9aDM9Q,5774
343
- flwr/superexec/exec_grpc.py,sha256=lpc_rgRjtHHMzcdOzznl12D4vT22JqV5acdy45YDb0k,3498
346
+ flwr/superexec/exec_grpc.py,sha256=xyyxrhPQLGX7tafGadv5DXLUzVoUIkGLd53_raOfNHk,4202
347
+ flwr/superexec/exec_license_interceptor.py,sha256=-BZPO-ZwtrPYumcDNdEh2QZK4uX-tuYBsZqKuDK0MZ8,3026
344
348
  flwr/superexec/exec_servicer.py,sha256=c0nwdFBiS6CbKrRA7ffOpsgASOLeaRV_ICwxDfxNGAg,12498
345
349
  flwr/superexec/exec_user_auth_interceptor.py,sha256=HpGHTcDKzB7XUiQHXgntNVFYL-VfP9Wj5tEVc04VOOw,5820
346
350
  flwr/superexec/executor.py,sha256=LaErHRJvNggjWV6FI6eajgKfnwOvSv2UqzFH253yDro,3265
@@ -360,7 +364,7 @@ flwr/supernode/servicer/__init__.py,sha256=lucTzre5WPK7G1YLCfaqg3rbFWdNSb7ZTt-ca
360
364
  flwr/supernode/servicer/clientappio/__init__.py,sha256=7Oy62Y_oijqF7Dxi6tpcUQyOpLc_QpIRZ83NvwmB0Yg,813
361
365
  flwr/supernode/servicer/clientappio/clientappio_servicer.py,sha256=d3GdIabycUoDBDL_eVlt513knGSjQW3-9lG6Cw4QEk4,5719
362
366
  flwr/supernode/start_client_internal.py,sha256=DAXuReZ1FCXt9Y1KbM0p-dI50ROWPEJXzfKrl14OE6k,18233
363
- flwr_nightly-1.20.0.dev20250627.dist-info/METADATA,sha256=FV-_wPKa9RQ3rLCjBW6TJht1mmGqT_-OU2Fi2DvCmIM,15910
364
- flwr_nightly-1.20.0.dev20250627.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
365
- flwr_nightly-1.20.0.dev20250627.dist-info/entry_points.txt,sha256=jNpDXGBGgs21RqUxelF_jwGaxtqFwm-MQyfz-ZqSjrA,367
366
- flwr_nightly-1.20.0.dev20250627.dist-info/RECORD,,
367
+ flwr_nightly-1.20.0.dev20250701.dist-info/METADATA,sha256=j0r0nY5a-FlldBWbh47eja74PsvI0AW0c6lDYkV4ch0,15910
368
+ flwr_nightly-1.20.0.dev20250701.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
369
+ flwr_nightly-1.20.0.dev20250701.dist-info/entry_points.txt,sha256=jNpDXGBGgs21RqUxelF_jwGaxtqFwm-MQyfz-ZqSjrA,367
370
+ flwr_nightly-1.20.0.dev20250701.dist-info/RECORD,,