flwr-nightly 1.18.0.dev20250407__py3-none-any.whl → 1.18.0.dev20250409__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.
@@ -60,7 +60,7 @@ def _check_value(value: ConfigRecordValues) -> None:
60
60
 
61
61
 
62
62
  class ConfigRecord(TypedDict[str, ConfigRecordValues]):
63
- """Configs record.
63
+ """Config record.
64
64
 
65
65
  A :code:`ConfigRecord` is a Python dictionary designed to ensure that
66
66
  each key-value pair adheres to specified data types. A :code:`ConfigRecord`
@@ -90,18 +90,18 @@ class ConfigRecord(TypedDict[str, ConfigRecordValues]):
90
90
  encourage you to use a :code:`ArrayRecord` instead if these are of high
91
91
  dimensionality.
92
92
 
93
- Let's see some examples of how to construct a :code:`ConfigRecord` from scratch:
94
-
95
- >>> from flwr.common import ConfigRecord
96
- >>>
97
- >>> # A `ConfigRecord` is a specialized Python dictionary
98
- >>> record = ConfigRecord({"lr": 0.1, "batch-size": 128})
99
- >>> # You can add more content to an existing record
100
- >>> record["compute-average"] = True
101
- >>> # It also supports lists
102
- >>> record["loss-fn-coefficients"] = [0.4, 0.25, 0.35]
103
- >>> # And string values (among other types)
104
- >>> record["path-to-S3"] = "s3://bucket_name/folder1/fileA.json"
93
+ Let's see some examples of how to construct a :code:`ConfigRecord` from scratch::
94
+
95
+ from flwr.common import ConfigRecord
96
+
97
+ # A `ConfigRecord` is a specialized Python dictionary
98
+ record = ConfigRecord({"lr": 0.1, "batch-size": 128})
99
+ # You can add more content to an existing record
100
+ record["compute-average"] = True
101
+ # It also supports lists
102
+ record["loss-fn-coefficients"] = [0.4, 0.25, 0.35]
103
+ # And string values (among other types)
104
+ record["path-to-S3"] = "s3://bucket_name/folder1/fileA.json"
105
105
 
106
106
  Just like the other types of records in a :code:`flwr.common.RecordDict`, types are
107
107
  enforced. If you need to add a custom data structure or object, we recommend to
@@ -60,7 +60,7 @@ def _check_value(value: MetricRecordValues) -> None:
60
60
 
61
61
 
62
62
  class MetricRecord(TypedDict[str, MetricRecordValues]):
63
- """Metrics recod.
63
+ """Metric record.
64
64
 
65
65
  A :code:`MetricRecord` is a Python dictionary designed to ensure that
66
66
  each key-value pair adheres to specified data types. A :code:`MetricRecord`
@@ -89,27 +89,27 @@ class MetricRecord(TypedDict[str, MetricRecordValues]):
89
89
  Common to these examples is that the output can be typically represented by
90
90
  a single scalar (:code:`int`, :code:`float`) or list of scalars.
91
91
 
92
- Let's see some examples of how to construct a :code:`MetricRecord` from scratch:
92
+ Let's see some examples of how to construct a :code:`MetricRecord` from scratch::
93
93
 
94
- >>> from flwr.common import MetricRecord
95
- >>>
96
- >>> # A `MetricRecord` is a specialized Python dictionary
97
- >>> record = MetricRecord({"accuracy": 0.94})
98
- >>> # You can add more content to an existing record
99
- >>> record["loss"] = 0.01
100
- >>> # It also supports lists
101
- >>> record["loss-historic"] = [0.9, 0.5, 0.01]
94
+ from flwr.common import MetricRecord
95
+
96
+ # A `MetricRecord` is a specialized Python dictionary
97
+ record = MetricRecord({"accuracy": 0.94})
98
+ # You can add more content to an existing record
99
+ record["loss"] = 0.01
100
+ # It also supports lists
101
+ record["loss-historic"] = [0.9, 0.5, 0.01]
102
102
 
103
103
  Since types are enforced, the types of the objects inserted are checked. For a
104
104
  :code:`MetricRecord`, value types allowed are those in defined in
105
105
  :code:`flwr.common.MetricRecordValues`. Similarly, only :code:`str` keys are
106
- allowed.
106
+ allowed::
107
+
108
+ from flwr.common import MetricRecord
107
109
 
108
- >>> from flwr.common import MetricRecord
109
- >>>
110
- >>> record = MetricRecord() # an empty record
111
- >>> # Add unsupported value
112
- >>> record["something-unsupported"] = {'a': 123} # Will throw a `TypeError`
110
+ record = MetricRecord() # an empty record
111
+ # Add unsupported value
112
+ record["something-unsupported"] = {'a': 123} # Will throw a `TypeError`
113
113
 
114
114
  If you need a more versatily type of record try :code:`ConfigRecord` or
115
115
  :code:`ArrayRecord`.
@@ -103,40 +103,40 @@ class RecordDict(TypedDict[str, RecordType]):
103
103
  are Python dictionaries designed to ensure that each key-value pair
104
104
  adheres to specified data types.
105
105
 
106
- Let's see an example.
107
-
108
- >>> from flwr.common import RecordDict
109
- >>> from flwr.common import ArrayRecord, ConfigRecord, MetricRecord
110
- >>>
111
- >>> # Let's begin with an empty record
112
- >>> my_records = RecordDict()
113
- >>>
114
- >>> # We can create a ConfigRecord
115
- >>> c_record = ConfigRecord({"lr": 0.1, "batch-size": 128})
116
- >>> # Adding it to the RecordDict would look like this
117
- >>> my_records["my_config"] = c_record
118
- >>>
119
- >>> # We can create a MetricRecord following a similar process
120
- >>> m_record = MetricRecord({"accuracy": 0.93, "losses": [0.23, 0.1]})
121
- >>> # Adding it to the RecordDict would look like this
122
- >>> my_records["my_metrics"] = m_record
106
+ Let's see an example::
107
+
108
+ from flwr.common import RecordDict
109
+ from flwr.common import ArrayRecord, ConfigRecord, MetricRecord
110
+
111
+ # Let's begin with an empty record
112
+ my_records = RecordDict()
113
+
114
+ # We can create a ConfigRecord
115
+ c_record = ConfigRecord({"lr": 0.1, "batch-size": 128})
116
+ # Adding it to the RecordDict would look like this
117
+ my_records["my_config"] = c_record
118
+
119
+ # We can create a MetricRecord following a similar process
120
+ m_record = MetricRecord({"accuracy": 0.93, "losses": [0.23, 0.1]})
121
+ # Adding it to the RecordDict would look like this
122
+ my_records["my_metrics"] = m_record
123
123
 
124
124
  Adding an :code:`ArrayRecord` follows the same steps as above but first,
125
125
  the array needs to be serialized and represented as a :code:`flwr.common.Array`.
126
- For example:
127
-
128
- >>> from flwr.common import Array
129
- >>> # Creating an ArrayRecord would look like this
130
- >>> arr_np = np.random.randn(3, 3)
131
- >>>
132
- >>> # You can use the built-in tool to serialize the array
133
- >>> arr = Array(arr_np)
134
- >>>
135
- >>> # Finally, create the record
136
- >>> arr_record = ArrayRecord({"my_array": arr})
137
- >>>
138
- >>> # Adding it to the RecordDict would look like this
139
- >>> my_records["my_parameters"] = arr_record
126
+ For example::
127
+
128
+ from flwr.common import Array
129
+ # Creating an ArrayRecord would look like this
130
+ arr_np = np.random.randn(3, 3)
131
+
132
+ # You can use the built-in tool to serialize the array
133
+ arr = Array(arr_np)
134
+
135
+ # Finally, create the record
136
+ arr_record = ArrayRecord({"my_array": arr})
137
+
138
+ # Adding it to the RecordDict would look like this
139
+ my_records["my_parameters"] = arr_record
140
140
 
141
141
  For additional examples on how to construct each of the records types shown
142
142
  above, please refer to the documentation for :code:`ConfigRecord`,
@@ -166,15 +166,15 @@ class RetryInvoker:
166
166
 
167
167
  Examples
168
168
  --------
169
- Initialize a `RetryInvoker` with exponential backoff and invoke a function:
170
-
171
- >>> invoker = RetryInvoker(
172
- ... exponential, # Or use `lambda: exponential(3, 2)` to pass arguments
173
- ... grpc.RpcError,
174
- ... max_tries=3,
175
- ... max_time=None,
176
- ... )
177
- >>> invoker.invoke(my_func, arg1, arg2, kw1=kwarg1)
169
+ Initialize a `RetryInvoker` with exponential backoff and invoke a function::
170
+
171
+ invoker = RetryInvoker(
172
+ exponential, # Or use `lambda: exponential(3, 2)` to pass arguments
173
+ grpc.RpcError,
174
+ max_tries=3,
175
+ max_time=None,
176
+ )
177
+ invoker.invoke(my_func, arg1, arg2, kw1=kwarg1)
178
178
  """
179
179
 
180
180
  # pylint: disable-next=too-many-arguments
flwr/server/app.py CHANGED
@@ -183,19 +183,19 @@ def start_server( # pylint: disable=too-many-arguments,too-many-locals
183
183
 
184
184
  Examples
185
185
  --------
186
- Starting an insecure server:
186
+ Starting an insecure server::
187
187
 
188
- >>> start_server()
188
+ start_server()
189
189
 
190
- Starting an SSL-enabled server:
190
+ Starting a TLS-enabled server::
191
191
 
192
- >>> start_server(
193
- >>> certificates=(
194
- >>> Path("/crts/root.pem").read_bytes(),
195
- >>> Path("/crts/localhost.crt").read_bytes(),
196
- >>> Path("/crts/localhost.key").read_bytes()
197
- >>> )
198
- >>> )
192
+ start_server(
193
+ certificates=(
194
+ Path("/crts/root.pem").read_bytes(),
195
+ Path("/crts/localhost.crt").read_bytes(),
196
+ Path("/crts/localhost.key").read_bytes()
197
+ )
198
+ )
199
199
  """
200
200
  msg = (
201
201
  "flwr.server.start_server() is deprecated."
flwr/server/grid/grid.py CHANGED
@@ -172,7 +172,7 @@ class Driver(Grid):
172
172
 
173
173
  .. warning::
174
174
  ``Driver`` is deprecated and will be removed in a future release.
175
- Use `Grid` in the signature of your ServerApp.
175
+ Use ``Grid`` in the signature of your ServerApp.
176
176
 
177
177
  Examples
178
178
  --------
flwr/server/server_app.py CHANGED
@@ -52,6 +52,30 @@ GRID_USAGE_EXAMPLE = """
52
52
  # Your existing ServerApp code ...
53
53
  """
54
54
 
55
+ BOTH_MAIN_FN_SERVER_FN_PROVIDED_ERROR_MSG = (
56
+ "Use either a custom main function or a `Strategy`, but not both."
57
+ """
58
+
59
+ Use the `ServerApp` with an existing `Strategy`:
60
+
61
+ server_config = ServerConfig(num_rounds=3)
62
+ strategy = FedAvg()
63
+
64
+ app = ServerApp(
65
+ server_config=server_config,
66
+ strategy=strategy,
67
+ )
68
+
69
+ Use the `ServerApp` with a custom main function:
70
+
71
+ app = ServerApp()
72
+
73
+ @app.main()
74
+ def main(grid: Grid, context: Context) -> None:
75
+ print("ServerApp running")
76
+ """
77
+ )
78
+
55
79
  DRIVER_DEPRECATION_MSG = """
56
80
  The `Driver` class is deprecated, it will be removed in a future release.
57
81
  """
@@ -70,25 +94,25 @@ class ServerApp: # pylint: disable=too-many-instance-attributes
70
94
 
71
95
  Examples
72
96
  --------
73
- Use the ``ServerApp`` with an existing ``Strategy``:
74
-
75
- >>> def server_fn(context: Context):
76
- >>> server_config = ServerConfig(num_rounds=3)
77
- >>> strategy = FedAvg()
78
- >>> return ServerAppComponents(
79
- >>> strategy=strategy,
80
- >>> server_config=server_config,
81
- >>> )
82
- >>>
83
- >>> app = ServerApp(server_fn=server_fn)
84
-
85
- Use the ``ServerApp`` with a custom main function:
86
-
87
- >>> app = ServerApp()
88
- >>>
89
- >>> @app.main()
90
- >>> def main(grid: Grid, context: Context) -> None:
91
- >>> print("ServerApp running")
97
+ Use the ``ServerApp`` with an existing ``Strategy``::
98
+
99
+ def server_fn(context: Context):
100
+ server_config = ServerConfig(num_rounds=3)
101
+ strategy = FedAvg()
102
+ return ServerAppComponents(
103
+ strategy=strategy,
104
+ server_config=server_config,
105
+ )
106
+
107
+ app = ServerApp(server_fn=server_fn)
108
+
109
+ Use the ``ServerApp`` with a custom main function::
110
+
111
+ app = ServerApp()
112
+
113
+ @app.main()
114
+ def main(grid: Grid, context: Context) -> None:
115
+ print("ServerApp running")
92
116
  """
93
117
 
94
118
  # pylint: disable=too-many-arguments,too-many-positional-arguments
@@ -156,38 +180,19 @@ class ServerApp: # pylint: disable=too-many-instance-attributes
156
180
 
157
181
  Examples
158
182
  --------
159
- >>> app = ServerApp()
160
- >>>
161
- >>> @app.main()
162
- >>> def main(grid: Grid, context: Context) -> None:
163
- >>> print("ServerApp running")
183
+ ::
184
+
185
+ app = ServerApp()
186
+
187
+ @app.main()
188
+ def main(grid: Grid, context: Context) -> None:
189
+ print("ServerApp running")
164
190
  """
165
191
 
166
192
  def main_decorator(main_fn: ServerAppCallable) -> ServerAppCallable:
167
193
  """Register the main fn with the ServerApp object."""
168
194
  if self._server or self._config or self._strategy or self._client_manager:
169
- raise ValueError(
170
- """Use either a custom main function or a `Strategy`, but not both.
171
-
172
- Use the `ServerApp` with an existing `Strategy`:
173
-
174
- >>> server_config = ServerConfig(num_rounds=3)
175
- >>> strategy = FedAvg()
176
- >>>
177
- >>> app = ServerApp(
178
- >>> server_config=server_config,
179
- >>> strategy=strategy,
180
- >>> )
181
-
182
- Use the `ServerApp` with a custom main function:
183
-
184
- >>> app = ServerApp()
185
- >>>
186
- >>> @app.main()
187
- >>> def main(grid: Grid, context: Context) -> None:
188
- >>> print("ServerApp running")
189
- """,
190
- )
195
+ raise ValueError(BOTH_MAIN_FN_SERVER_FN_PROVIDED_ERROR_MSG)
191
196
 
192
197
  sig = inspect.signature(main_fn)
193
198
  param = list(sig.parameters.values())[0]
@@ -219,17 +224,19 @@ class ServerApp: # pylint: disable=too-many-instance-attributes
219
224
 
220
225
  Examples
221
226
  --------
222
- >>> app = ServerApp()
223
- >>>
224
- >>> @app.lifespan()
225
- >>> def lifespan(context: Context) -> None:
226
- >>> # Perform initialization tasks before the app starts
227
- >>> print("Initializing ServerApp")
228
- >>>
229
- >>> yield # ServerApp is running
230
- >>>
231
- >>> # Perform cleanup tasks after the app stops
232
- >>> print("Cleaning up ServerApp")
227
+ ::
228
+
229
+ app = ServerApp()
230
+
231
+ @app.lifespan()
232
+ def lifespan(context: Context) -> None:
233
+ # Perform initialization tasks before the app starts
234
+ print("Initializing ServerApp")
235
+
236
+ yield # ServerApp is running
237
+
238
+ # Perform cleanup tasks after the app stops
239
+ print("Cleaning up ServerApp")
233
240
  """
234
241
 
235
242
  def lifespan_decorator(
@@ -77,15 +77,15 @@ class DifferentialPrivacyServerSideAdaptiveClipping(Strategy):
77
77
 
78
78
  Examples
79
79
  --------
80
- Create a strategy:
80
+ Create a strategy::
81
81
 
82
- >>> strategy = fl.server.strategy.FedAvg( ... )
82
+ strategy = fl.server.strategy.FedAvg( ... )
83
83
 
84
- Wrap the strategy with the DifferentialPrivacyServerSideAdaptiveClipping wrapper
84
+ Wrap the strategy with the DifferentialPrivacyServerSideAdaptiveClipping wrapper::
85
85
 
86
- >>> dp_strategy = DifferentialPrivacyServerSideAdaptiveClipping(
87
- >>> strategy, cfg.noise_multiplier, cfg.num_sampled_clients, ...
88
- >>> )
86
+ dp_strategy = DifferentialPrivacyServerSideAdaptiveClipping(
87
+ strategy, cfg.noise_multiplier, cfg.num_sampled_clients, ...
88
+ )
89
89
  """
90
90
 
91
91
  # pylint: disable=too-many-arguments,too-many-instance-attributes,too-many-positional-arguments
@@ -290,21 +290,21 @@ class DifferentialPrivacyClientSideAdaptiveClipping(Strategy):
290
290
 
291
291
  Examples
292
292
  --------
293
- Create a strategy:
293
+ Create a strategy::
294
294
 
295
- >>> strategy = fl.server.strategy.FedAvg(...)
295
+ strategy = fl.server.strategy.FedAvg(...)
296
296
 
297
- Wrap the strategy with the `DifferentialPrivacyClientSideAdaptiveClipping` wrapper:
297
+ Wrap the strategy with the `DifferentialPrivacyClientSideAdaptiveClipping` wrapper::
298
298
 
299
- >>> dp_strategy = DifferentialPrivacyClientSideAdaptiveClipping(
300
- >>> strategy, cfg.noise_multiplier, cfg.num_sampled_clients
301
- >>> )
299
+ dp_strategy = DifferentialPrivacyClientSideAdaptiveClipping(
300
+ strategy, cfg.noise_multiplier, cfg.num_sampled_clients
301
+ )
302
302
 
303
- On the client, add the `adaptiveclipping_mod` to the client-side mods:
303
+ On the client, add the `adaptiveclipping_mod` to the client-side mods::
304
304
 
305
- >>> app = fl.client.ClientApp(
306
- >>> client_fn=client_fn, mods=[adaptiveclipping_mod]
307
- >>> )
305
+ app = fl.client.ClientApp(
306
+ client_fn=client_fn, mods=[adaptiveclipping_mod]
307
+ )
308
308
  """
309
309
 
310
310
  # pylint: disable=too-many-arguments,too-many-instance-attributes,too-many-positional-arguments
@@ -64,15 +64,15 @@ class DifferentialPrivacyServerSideFixedClipping(Strategy):
64
64
 
65
65
  Examples
66
66
  --------
67
- Create a strategy:
67
+ Create a strategy::
68
68
 
69
- >>> strategy = fl.server.strategy.FedAvg( ... )
69
+ strategy = fl.server.strategy.FedAvg( ... )
70
70
 
71
- Wrap the strategy with the DifferentialPrivacyServerSideFixedClipping wrapper
71
+ Wrap the strategy with the DifferentialPrivacyServerSideFixedClipping wrapper::
72
72
 
73
- >>> dp_strategy = DifferentialPrivacyServerSideFixedClipping(
74
- >>> strategy, cfg.noise_multiplier, cfg.clipping_norm, cfg.num_sampled_clients
75
- >>> )
73
+ dp_strategy = DifferentialPrivacyServerSideFixedClipping(
74
+ strategy, cfg.noise_multiplier, cfg.clipping_norm, cfg.num_sampled_clients
75
+ )
76
76
  """
77
77
 
78
78
  # pylint: disable=too-many-arguments,too-many-instance-attributes
@@ -228,21 +228,21 @@ class DifferentialPrivacyClientSideFixedClipping(Strategy):
228
228
 
229
229
  Examples
230
230
  --------
231
- Create a strategy:
231
+ Create a strategy::
232
232
 
233
- >>> strategy = fl.server.strategy.FedAvg(...)
233
+ strategy = fl.server.strategy.FedAvg(...)
234
234
 
235
- Wrap the strategy with the `DifferentialPrivacyClientSideFixedClipping` wrapper:
235
+ Wrap the strategy with the `DifferentialPrivacyClientSideFixedClipping` wrapper::
236
236
 
237
- >>> dp_strategy = DifferentialPrivacyClientSideFixedClipping(
238
- >>> strategy, cfg.noise_multiplier, cfg.clipping_norm, cfg.num_sampled_clients
239
- >>> )
237
+ dp_strategy = DifferentialPrivacyClientSideFixedClipping(
238
+ strategy, cfg.noise_multiplier, cfg.clipping_norm, cfg.num_sampled_clients
239
+ )
240
240
 
241
- On the client, add the `fixedclipping_mod` to the client-side mods:
241
+ On the client, add the `fixedclipping_mod` to the client-side mods::
242
242
 
243
- >>> app = fl.client.ClientApp(
244
- >>> client_fn=client_fn, mods=[fixedclipping_mod]
245
- >>> )
243
+ app = fl.client.ClientApp(
244
+ client_fn=client_fn, mods=[fixedclipping_mod]
245
+ )
246
246
  """
247
247
 
248
248
  # pylint: disable=too-many-arguments,too-many-instance-attributes
@@ -96,18 +96,18 @@ def start_grpc_server( # pylint: disable=too-many-arguments,R0917
96
96
 
97
97
  Examples
98
98
  --------
99
- Starting a SSL-enabled server.
100
-
101
- >>> from pathlib import Path
102
- >>> start_grpc_server(
103
- >>> client_manager=ClientManager(),
104
- >>> server_address="localhost:8080",
105
- >>> certificates=(
106
- >>> Path("/crts/root.pem").read_bytes(),
107
- >>> Path("/crts/localhost.crt").read_bytes(),
108
- >>> Path("/crts/localhost.key").read_bytes(),
109
- >>> ),
110
- >>> )
99
+ Starting a TLS-enabled server::
100
+
101
+ from pathlib import Path
102
+ start_grpc_server(
103
+ client_manager=ClientManager(),
104
+ server_address="localhost:8080",
105
+ certificates=(
106
+ Path("/crts/root.pem").read_bytes(),
107
+ Path("/crts/localhost.crt").read_bytes(),
108
+ Path("/crts/localhost.key").read_bytes(),
109
+ ),
110
+ )
111
111
  """
112
112
  servicer = FlowerServiceServicer(client_manager)
113
113
  add_servicer_to_server_fn = add_FlowerServiceServicer_to_server
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: flwr-nightly
3
- Version: 1.18.0.dev20250407
3
+ Version: 1.18.0.dev20250409
4
4
  Summary: Flower: A Friendly Federated AI Framework
5
5
  Home-page: https://flower.ai
6
6
  License: Apache-2.0