flwr 1.16.0__py3-none-any.whl → 1.17.0__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.
Files changed (98) hide show
  1. flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl +1 -1
  2. flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +1 -1
  3. flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +1 -1
  4. flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +1 -1
  5. flwr/cli/new/templates/app/pyproject.jax.toml.tpl +1 -1
  6. flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +1 -1
  7. flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +1 -1
  8. flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +1 -1
  9. flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +1 -1
  10. flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +1 -1
  11. flwr/cli/run/run.py +5 -9
  12. flwr/client/app.py +6 -4
  13. flwr/client/client_app.py +162 -99
  14. flwr/client/clientapp/app.py +2 -2
  15. flwr/client/grpc_client/connection.py +24 -21
  16. flwr/client/message_handler/message_handler.py +27 -27
  17. flwr/client/mod/__init__.py +2 -2
  18. flwr/client/mod/centraldp_mods.py +7 -7
  19. flwr/client/mod/comms_mods.py +16 -22
  20. flwr/client/mod/localdp_mod.py +4 -4
  21. flwr/client/mod/secure_aggregation/secaggplus_mod.py +31 -31
  22. flwr/client/run_info_store.py +2 -2
  23. flwr/common/__init__.py +12 -4
  24. flwr/common/config.py +4 -4
  25. flwr/common/constant.py +6 -6
  26. flwr/common/context.py +4 -4
  27. flwr/common/event_log_plugin/event_log_plugin.py +3 -3
  28. flwr/common/logger.py +2 -2
  29. flwr/common/message.py +327 -102
  30. flwr/common/record/__init__.py +8 -4
  31. flwr/common/record/arrayrecord.py +626 -0
  32. flwr/common/record/{configsrecord.py → configrecord.py} +75 -29
  33. flwr/common/record/conversion_utils.py +1 -1
  34. flwr/common/record/{metricsrecord.py → metricrecord.py} +78 -32
  35. flwr/common/record/recorddict.py +288 -0
  36. flwr/common/recorddict_compat.py +410 -0
  37. flwr/common/secure_aggregation/secaggplus_constants.py +1 -1
  38. flwr/common/serde.py +66 -71
  39. flwr/common/typing.py +8 -8
  40. flwr/proto/exec_pb2.py +3 -3
  41. flwr/proto/exec_pb2.pyi +3 -3
  42. flwr/proto/message_pb2.py +12 -12
  43. flwr/proto/message_pb2.pyi +9 -9
  44. flwr/proto/recorddict_pb2.py +70 -0
  45. flwr/proto/{recordset_pb2.pyi → recorddict_pb2.pyi} +35 -35
  46. flwr/proto/run_pb2.py +31 -31
  47. flwr/proto/run_pb2.pyi +3 -3
  48. flwr/server/__init__.py +3 -1
  49. flwr/server/app.py +56 -1
  50. flwr/server/compat/__init__.py +2 -2
  51. flwr/server/compat/app.py +11 -11
  52. flwr/server/compat/app_utils.py +16 -16
  53. flwr/server/compat/{driver_client_proxy.py → grid_client_proxy.py} +39 -39
  54. flwr/server/fleet_event_log_interceptor.py +94 -0
  55. flwr/server/{driver → grid}/__init__.py +8 -7
  56. flwr/server/{driver/driver.py → grid/grid.py} +47 -18
  57. flwr/server/{driver/grpc_driver.py → grid/grpc_grid.py} +87 -64
  58. flwr/server/{driver/inmemory_driver.py → grid/inmemory_grid.py} +24 -34
  59. flwr/server/run_serverapp.py +4 -4
  60. flwr/server/server_app.py +38 -18
  61. flwr/server/serverapp/app.py +10 -10
  62. flwr/server/superlink/fleet/vce/backend/backend.py +2 -2
  63. flwr/server/superlink/fleet/vce/backend/raybackend.py +2 -2
  64. flwr/server/superlink/fleet/vce/vce_api.py +1 -3
  65. flwr/server/superlink/linkstate/in_memory_linkstate.py +33 -8
  66. flwr/server/superlink/linkstate/linkstate.py +4 -4
  67. flwr/server/superlink/linkstate/sqlite_linkstate.py +61 -27
  68. flwr/server/superlink/linkstate/utils.py +93 -27
  69. flwr/server/superlink/{driver → serverappio}/__init__.py +1 -1
  70. flwr/server/superlink/{driver → serverappio}/serverappio_grpc.py +1 -1
  71. flwr/server/superlink/{driver → serverappio}/serverappio_servicer.py +4 -4
  72. flwr/server/superlink/simulation/simulationio_servicer.py +2 -2
  73. flwr/server/typing.py +3 -3
  74. flwr/server/utils/validator.py +4 -4
  75. flwr/server/workflow/default_workflows.py +48 -57
  76. flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +48 -50
  77. flwr/simulation/app.py +2 -2
  78. flwr/simulation/ray_transport/ray_actor.py +4 -2
  79. flwr/simulation/ray_transport/ray_client_proxy.py +34 -32
  80. flwr/simulation/run_simulation.py +15 -15
  81. flwr/superexec/deployment.py +4 -4
  82. flwr/superexec/exec_event_log_interceptor.py +135 -0
  83. flwr/superexec/exec_grpc.py +10 -4
  84. flwr/superexec/exec_servicer.py +2 -2
  85. flwr/superexec/exec_user_auth_interceptor.py +18 -2
  86. flwr/superexec/executor.py +3 -3
  87. flwr/superexec/simulation.py +3 -3
  88. {flwr-1.16.0.dist-info → flwr-1.17.0.dist-info}/METADATA +2 -2
  89. {flwr-1.16.0.dist-info → flwr-1.17.0.dist-info}/RECORD +94 -92
  90. flwr/common/record/parametersrecord.py +0 -339
  91. flwr/common/record/recordset.py +0 -209
  92. flwr/common/recordset_compat.py +0 -418
  93. flwr/proto/recordset_pb2.py +0 -70
  94. /flwr/proto/{recordset_pb2_grpc.py → recorddict_pb2_grpc.py} +0 -0
  95. /flwr/proto/{recordset_pb2_grpc.pyi → recorddict_pb2_grpc.pyi} +0 -0
  96. {flwr-1.16.0.dist-info → flwr-1.17.0.dist-info}/LICENSE +0 -0
  97. {flwr-1.16.0.dist-info → flwr-1.17.0.dist-info}/WHEEL +0 -0
  98. {flwr-1.16.0.dist-info → flwr-1.17.0.dist-info}/entry_points.txt +0 -0
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -12,13 +12,15 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
  # ==============================================================================
15
- """ConfigsRecord."""
15
+ """ConfigRecord."""
16
16
 
17
17
 
18
+ from logging import WARN
18
19
  from typing import Optional, get_args
19
20
 
20
- from flwr.common.typing import ConfigsRecordValues, ConfigsScalar
21
+ from flwr.common.typing import ConfigRecordValues, ConfigScalar
21
22
 
23
+ from ..logger import log
22
24
  from .typeddict import TypedDict
23
25
 
24
26
 
@@ -28,20 +30,20 @@ def _check_key(key: str) -> None:
28
30
  raise TypeError(f"Key must be of type `str` but `{type(key)}` was passed.")
29
31
 
30
32
 
31
- def _check_value(value: ConfigsRecordValues) -> None:
32
- def is_valid(__v: ConfigsScalar) -> None:
33
+ def _check_value(value: ConfigRecordValues) -> None:
34
+ def is_valid(__v: ConfigScalar) -> None:
33
35
  """Check if value is of expected type."""
34
- if not isinstance(__v, get_args(ConfigsScalar)):
36
+ if not isinstance(__v, get_args(ConfigScalar)):
35
37
  raise TypeError(
36
38
  "Not all values are of valid type."
37
- f" Expected `{ConfigsRecordValues}` but `{type(__v)}` was passed."
39
+ f" Expected `{ConfigRecordValues}` but `{type(__v)}` was passed."
38
40
  )
39
41
 
40
42
  if isinstance(value, list):
41
43
  # If your lists are large (e.g. 1M+ elements) this will be slow
42
44
  # 1s to check 10M element list on a M2 Pro
43
45
  # In such settings, you'd be better of treating such config as
44
- # an array and pass it to a ParametersRecord.
46
+ # an array and pass it to a ArrayRecord.
45
47
  # Empty lists are valid
46
48
  if len(value) > 0:
47
49
  is_valid(value[0])
@@ -51,24 +53,24 @@ def _check_value(value: ConfigsRecordValues) -> None:
51
53
  if not all(isinstance(v, value_type) for v in value):
52
54
  raise TypeError(
53
55
  "All values in a list must be of the same valid type. "
54
- f"One of {ConfigsScalar}."
56
+ f"One of {ConfigScalar}."
55
57
  )
56
58
  else:
57
59
  is_valid(value)
58
60
 
59
61
 
60
- class ConfigsRecord(TypedDict[str, ConfigsRecordValues]):
62
+ class ConfigRecord(TypedDict[str, ConfigRecordValues]):
61
63
  """Configs record.
62
64
 
63
- A :code:`ConfigsRecord` is a Python dictionary designed to ensure that
64
- each key-value pair adheres to specified data types. A :code:`ConfigsRecord`
65
+ A :code:`ConfigRecord` is a Python dictionary designed to ensure that
66
+ each key-value pair adheres to specified data types. A :code:`ConfigRecord`
65
67
  is one of the types of records that a
66
- `flwr.common.RecordSet <flwr.common.RecordSet.html#recordset>`_ supports and
68
+ `flwr.common.RecordDict <flwr.common.RecordDict.html#recorddict>`_ supports and
67
69
  can therefore be used to construct :code:`common.Message` objects.
68
70
 
69
71
  Parameters
70
72
  ----------
71
- configs_dict : Optional[Dict[str, ConfigsRecordValues]]
73
+ config_dict : Optional[Dict[str, ConfigRecordValues]]
72
74
  A dictionary that stores basic types (i.e. `str`, `int`, `float`, `bytes` as
73
75
  defined in `ConfigsScalar`) and lists of such types (see
74
76
  `ConfigsScalarList`).
@@ -80,20 +82,20 @@ class ConfigsRecord(TypedDict[str, ConfigsRecordValues]):
80
82
 
81
83
  Examples
82
84
  --------
83
- The usage of a :code:`ConfigsRecord` is envisioned for sending configuration values
85
+ The usage of a :code:`ConfigRecord` is envisioned for sending configuration values
84
86
  telling the target node how to perform a certain action (e.g. train/evaluate a model
85
87
  ). You can use standard Python built-in types such as :code:`float`, :code:`str`
86
88
  , :code:`bytes`. All types allowed are defined in
87
- :code:`flwr.common.ConfigsRecordValues`. While lists are supported, we
88
- encourage you to use a :code:`ParametersRecord` instead if these are of high
89
+ :code:`flwr.common.ConfigRecordValues`. While lists are supported, we
90
+ encourage you to use a :code:`ArrayRecord` instead if these are of high
89
91
  dimensionality.
90
92
 
91
- Let's see some examples of how to construct a :code:`ConfigsRecord` from scratch:
93
+ Let's see some examples of how to construct a :code:`ConfigRecord` from scratch:
92
94
 
93
- >>> from flwr.common import ConfigsRecord
95
+ >>> from flwr.common import ConfigRecord
94
96
  >>>
95
- >>> # A `ConfigsRecord` is a specialized Python dictionary
96
- >>> record = ConfigsRecord({"lr": 0.1, "batch-size": 128})
97
+ >>> # A `ConfigRecord` is a specialized Python dictionary
98
+ >>> record = ConfigRecord({"lr": 0.1, "batch-size": 128})
97
99
  >>> # You can add more content to an existing record
98
100
  >>> record["compute-average"] = True
99
101
  >>> # It also supports lists
@@ -101,24 +103,24 @@ class ConfigsRecord(TypedDict[str, ConfigsRecordValues]):
101
103
  >>> # And string values (among other types)
102
104
  >>> record["path-to-S3"] = "s3://bucket_name/folder1/fileA.json"
103
105
 
104
- Just like the other types of records in a :code:`flwr.common.RecordSet`, types are
106
+ Just like the other types of records in a :code:`flwr.common.RecordDict`, types are
105
107
  enforced. If you need to add a custom data structure or object, we recommend to
106
108
  serialise it into bytes and save it as such (bytes are allowed in a
107
- :code:`ConfigsRecord`)
109
+ :code:`ConfigRecord`)
108
110
  """
109
111
 
110
112
  def __init__(
111
113
  self,
112
- configs_dict: Optional[dict[str, ConfigsRecordValues]] = None,
114
+ config_dict: Optional[dict[str, ConfigRecordValues]] = None,
113
115
  keep_input: bool = True,
114
116
  ) -> None:
115
117
 
116
118
  super().__init__(_check_key, _check_value)
117
- if configs_dict:
118
- for k in list(configs_dict.keys()):
119
- self[k] = configs_dict[k]
119
+ if config_dict:
120
+ for k in list(config_dict.keys()):
121
+ self[k] = config_dict[k]
120
122
  if not keep_input:
121
- del configs_dict[k]
123
+ del config_dict[k]
122
124
 
123
125
  def count_bytes(self) -> int:
124
126
  """Return number of Bytes stored in this object.
@@ -126,7 +128,7 @@ class ConfigsRecord(TypedDict[str, ConfigsRecordValues]):
126
128
  This function counts booleans as occupying 1 Byte.
127
129
  """
128
130
 
129
- def get_var_bytes(value: ConfigsScalar) -> int:
131
+ def get_var_bytes(value: ConfigScalar) -> int:
130
132
  """Return Bytes of value passed."""
131
133
  var_bytes = 0
132
134
  if isinstance(value, bool):
@@ -161,3 +163,47 @@ class ConfigsRecord(TypedDict[str, ConfigsRecordValues]):
161
163
  num_bytes += len(k)
162
164
 
163
165
  return num_bytes
166
+
167
+
168
+ class ConfigsRecord(ConfigRecord):
169
+ """Deprecated class ``ConfigsRecord``, use ``ConfigRecord`` instead.
170
+
171
+ This class exists solely for backward compatibility with legacy
172
+ code that previously used ``ConfigsRecord``. It has been renamed
173
+ to ``ConfigRecord``.
174
+
175
+ .. warning::
176
+ ``ConfigsRecord`` is deprecated and will be removed in a future release.
177
+ Use ``ConfigRecord`` instead.
178
+
179
+ Examples
180
+ --------
181
+ Legacy (deprecated) usage::
182
+
183
+ from flwr.common import ConfigsRecord
184
+
185
+ record = ConfigsRecord()
186
+
187
+ Updated usage::
188
+
189
+ from flwr.common import ConfigRecord
190
+
191
+ record = ConfigRecord()
192
+ """
193
+
194
+ _warning_logged = False
195
+
196
+ def __init__(
197
+ self,
198
+ config_dict: Optional[dict[str, ConfigRecordValues]] = None,
199
+ keep_input: bool = True,
200
+ ):
201
+ if not ConfigsRecord._warning_logged:
202
+ ConfigsRecord._warning_logged = True
203
+ log(
204
+ WARN,
205
+ "The `ConfigsRecord` class has been renamed to `ConfigRecord`. "
206
+ "Support for `ConfigsRecord` will be removed in a future release. "
207
+ "Please update your code accordingly.",
208
+ )
209
+ super().__init__(config_dict, keep_input)
@@ -17,7 +17,7 @@
17
17
 
18
18
  from ..logger import warn_deprecated_feature
19
19
  from ..typing import NDArray
20
- from .parametersrecord import Array
20
+ from .arrayrecord import Array
21
21
 
22
22
  WARN_DEPRECATED_MESSAGE = (
23
23
  "`array_from_numpy` is deprecated. Instead, use the `Array(ndarray)` class "
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -12,13 +12,15 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
  # ==============================================================================
15
- """MetricsRecord."""
15
+ """MetricRecord."""
16
16
 
17
17
 
18
+ from logging import WARN
18
19
  from typing import Optional, get_args
19
20
 
20
- from flwr.common.typing import MetricsRecordValues, MetricsScalar
21
+ from flwr.common.typing import MetricRecordValues, MetricScalar
21
22
 
23
+ from ..logger import log
22
24
  from .typeddict import TypedDict
23
25
 
24
26
 
@@ -28,20 +30,20 @@ def _check_key(key: str) -> None:
28
30
  raise TypeError(f"Key must be of type `str` but `{type(key)}` was passed.")
29
31
 
30
32
 
31
- def _check_value(value: MetricsRecordValues) -> None:
32
- def is_valid(__v: MetricsScalar) -> None:
33
+ def _check_value(value: MetricRecordValues) -> None:
34
+ def is_valid(__v: MetricScalar) -> None:
33
35
  """Check if value is of expected type."""
34
- if not isinstance(__v, get_args(MetricsScalar)) or isinstance(__v, bool):
36
+ if not isinstance(__v, get_args(MetricScalar)) or isinstance(__v, bool):
35
37
  raise TypeError(
36
38
  "Not all values are of valid type."
37
- f" Expected `{MetricsRecordValues}` but `{type(__v)}` was passed."
39
+ f" Expected `{MetricRecordValues}` but `{type(__v)}` was passed."
38
40
  )
39
41
 
40
42
  if isinstance(value, list):
41
43
  # If your lists are large (e.g. 1M+ elements) this will be slow
42
44
  # 1s to check 10M element list on a M2 Pro
43
45
  # In such settings, you'd be better of treating such metric as
44
- # an array and pass it to a ParametersRecord.
46
+ # an array and pass it to an ArrayRecord.
45
47
  # Empty lists are valid
46
48
  if len(value) > 0:
47
49
  is_valid(value[0])
@@ -51,26 +53,26 @@ def _check_value(value: MetricsRecordValues) -> None:
51
53
  if not all(isinstance(v, value_type) for v in value):
52
54
  raise TypeError(
53
55
  "All values in a list must be of the same valid type. "
54
- f"One of {MetricsScalar}."
56
+ f"One of {MetricScalar}."
55
57
  )
56
58
  else:
57
59
  is_valid(value)
58
60
 
59
61
 
60
- class MetricsRecord(TypedDict[str, MetricsRecordValues]):
62
+ class MetricRecord(TypedDict[str, MetricRecordValues]):
61
63
  """Metrics recod.
62
64
 
63
- A :code:`MetricsRecord` is a Python dictionary designed to ensure that
64
- each key-value pair adheres to specified data types. A :code:`MetricsRecord`
65
+ A :code:`MetricRecord` is a Python dictionary designed to ensure that
66
+ each key-value pair adheres to specified data types. A :code:`MetricRecord`
65
67
  is one of the types of records that a
66
- `flwr.common.RecordSet <flwr.common.RecordSet.html#recordset>`_ supports and
68
+ `flwr.common.RecordDict <flwr.common.RecordDict.html#recorddict>`_ supports and
67
69
  can therefore be used to construct :code:`common.Message` objects.
68
70
 
69
71
  Parameters
70
72
  ----------
71
- metrics_dict : Optional[Dict[str, MetricsRecordValues]]
73
+ metric_dict : Optional[Dict[str, MetricRecordValues]]
72
74
  A dictionary that stores basic types (i.e. `int`, `float` as defined
73
- in `MetricsScalar`) and list of such types (see `MetricsScalarList`).
75
+ in `MetricScalar`) and list of such types (see `MetricScalarList`).
74
76
  keep_input : bool (default: True)
75
77
  A boolean indicating whether metrics should be deleted from the input
76
78
  dictionary immediately after adding them to the record. When set
@@ -79,7 +81,7 @@ class MetricsRecord(TypedDict[str, MetricsRecordValues]):
79
81
 
80
82
  Examples
81
83
  --------
82
- The usage of a :code:`MetricsRecord` is envisioned for communicating results
84
+ The usage of a :code:`MetricRecord` is envisioned for communicating results
83
85
  obtained when a node performs an action. A few typical examples include:
84
86
  communicating the training accuracy after a model is trained locally by a
85
87
  :code:`ClientApp`, reporting the validation loss obtained at a :code:`ClientApp`,
@@ -87,43 +89,43 @@ class MetricsRecord(TypedDict[str, MetricsRecordValues]):
87
89
  Common to these examples is that the output can be typically represented by
88
90
  a single scalar (:code:`int`, :code:`float`) or list of scalars.
89
91
 
90
- Let's see some examples of how to construct a :code:`MetricsRecord` from scratch:
92
+ Let's see some examples of how to construct a :code:`MetricRecord` from scratch:
91
93
 
92
- >>> from flwr.common import MetricsRecord
94
+ >>> from flwr.common import MetricRecord
93
95
  >>>
94
- >>> # A `MetricsRecord` is a specialized Python dictionary
95
- >>> record = MetricsRecord({"accuracy": 0.94})
96
+ >>> # A `MetricRecord` is a specialized Python dictionary
97
+ >>> record = MetricRecord({"accuracy": 0.94})
96
98
  >>> # You can add more content to an existing record
97
99
  >>> record["loss"] = 0.01
98
100
  >>> # It also supports lists
99
101
  >>> record["loss-historic"] = [0.9, 0.5, 0.01]
100
102
 
101
103
  Since types are enforced, the types of the objects inserted are checked. For a
102
- :code:`MetricsRecord`, value types allowed are those in defined in
103
- :code:`flwr.common.MetricsRecordValues`. Similarly, only :code:`str` keys are
104
+ :code:`MetricRecord`, value types allowed are those in defined in
105
+ :code:`flwr.common.MetricRecordValues`. Similarly, only :code:`str` keys are
104
106
  allowed.
105
107
 
106
- >>> from flwr.common import MetricsRecord
108
+ >>> from flwr.common import MetricRecord
107
109
  >>>
108
- >>> record = MetricsRecord() # an empty record
110
+ >>> record = MetricRecord() # an empty record
109
111
  >>> # Add unsupported value
110
112
  >>> record["something-unsupported"] = {'a': 123} # Will throw a `TypeError`
111
113
 
112
- If you need a more versatily type of record try :code:`ConfigsRecord` or
113
- :code:`ParametersRecord`.
114
+ If you need a more versatily type of record try :code:`ConfigRecord` or
115
+ :code:`ArrayRecord`.
114
116
  """
115
117
 
116
118
  def __init__(
117
119
  self,
118
- metrics_dict: Optional[dict[str, MetricsRecordValues]] = None,
120
+ metric_dict: Optional[dict[str, MetricRecordValues]] = None,
119
121
  keep_input: bool = True,
120
- ):
122
+ ) -> None:
121
123
  super().__init__(_check_key, _check_value)
122
- if metrics_dict:
123
- for k in list(metrics_dict.keys()):
124
- self[k] = metrics_dict[k]
124
+ if metric_dict:
125
+ for k in list(metric_dict.keys()):
126
+ self[k] = metric_dict[k]
125
127
  if not keep_input:
126
- del metrics_dict[k]
128
+ del metric_dict[k]
127
129
 
128
130
  def count_bytes(self) -> int:
129
131
  """Return number of Bytes stored in this object."""
@@ -140,3 +142,47 @@ class MetricsRecord(TypedDict[str, MetricsRecordValues]):
140
142
  # We also count the bytes footprint of the keys
141
143
  num_bytes += len(k)
142
144
  return num_bytes
145
+
146
+
147
+ class MetricsRecord(MetricRecord):
148
+ """Deprecated class ``MetricsRecord``, use ``MetricRecord`` instead.
149
+
150
+ This class exists solely for backward compatibility with legacy
151
+ code that previously used ``MetricsRecord``. It has been renamed
152
+ to ``MetricRecord``.
153
+
154
+ .. warning::
155
+ ``MetricsRecord`` is deprecated and will be removed in a future release.
156
+ Use ``MetricRecord`` instead.
157
+
158
+ Examples
159
+ --------
160
+ Legacy (deprecated) usage::
161
+
162
+ from flwr.common import MetricsRecord
163
+
164
+ record = MetricsRecord()
165
+
166
+ Updated usage::
167
+
168
+ from flwr.common import MetricRecord
169
+
170
+ record = MetricRecord()
171
+ """
172
+
173
+ _warning_logged = False
174
+
175
+ def __init__(
176
+ self,
177
+ metric_dict: Optional[dict[str, MetricRecordValues]] = None,
178
+ keep_input: bool = True,
179
+ ):
180
+ if not MetricsRecord._warning_logged:
181
+ MetricsRecord._warning_logged = True
182
+ log(
183
+ WARN,
184
+ "The `MetricsRecord` class has been renamed to `MetricRecord`. "
185
+ "Support for `MetricsRecord` will be removed in a future release. "
186
+ "Please update your code accordingly.",
187
+ )
188
+ super().__init__(metric_dict, keep_input)