flwr-nightly 1.8.0.dev20240314__py3-none-any.whl → 1.11.0.dev20240813__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.

Files changed (237) hide show
  1. flwr/cli/app.py +7 -0
  2. flwr/cli/build.py +150 -0
  3. flwr/cli/config_utils.py +219 -0
  4. flwr/cli/example.py +3 -1
  5. flwr/cli/install.py +227 -0
  6. flwr/cli/new/new.py +179 -48
  7. flwr/cli/new/templates/app/.gitignore.tpl +160 -0
  8. flwr/cli/new/templates/app/README.flowertune.md.tpl +56 -0
  9. flwr/cli/new/templates/app/README.md.tpl +1 -5
  10. flwr/cli/new/templates/app/code/__init__.py.tpl +1 -1
  11. flwr/cli/new/templates/app/code/client.huggingface.py.tpl +65 -0
  12. flwr/cli/new/templates/app/code/client.jax.py.tpl +56 -0
  13. flwr/cli/new/templates/app/code/client.mlx.py.tpl +93 -0
  14. flwr/cli/new/templates/app/code/client.numpy.py.tpl +3 -2
  15. flwr/cli/new/templates/app/code/client.pytorch.py.tpl +23 -11
  16. flwr/cli/new/templates/app/code/client.sklearn.py.tpl +97 -0
  17. flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +60 -1
  18. flwr/cli/new/templates/app/code/flwr_tune/__init__.py +15 -0
  19. flwr/cli/new/templates/app/code/flwr_tune/app.py.tpl +89 -0
  20. flwr/cli/new/templates/app/code/flwr_tune/client.py.tpl +126 -0
  21. flwr/cli/new/templates/app/code/flwr_tune/config.yaml.tpl +34 -0
  22. flwr/cli/new/templates/app/code/flwr_tune/dataset.py.tpl +57 -0
  23. flwr/cli/new/templates/app/code/flwr_tune/models.py.tpl +59 -0
  24. flwr/cli/new/templates/app/code/flwr_tune/server.py.tpl +48 -0
  25. flwr/cli/new/templates/app/code/flwr_tune/static_config.yaml.tpl +11 -0
  26. flwr/cli/new/templates/app/code/server.huggingface.py.tpl +23 -0
  27. flwr/cli/new/templates/app/code/server.jax.py.tpl +20 -0
  28. flwr/cli/new/templates/app/code/server.mlx.py.tpl +20 -0
  29. flwr/cli/new/templates/app/code/server.numpy.py.tpl +17 -9
  30. flwr/cli/new/templates/app/code/server.pytorch.py.tpl +21 -18
  31. flwr/cli/new/templates/app/code/server.sklearn.py.tpl +24 -0
  32. flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +29 -1
  33. flwr/cli/new/templates/app/code/task.huggingface.py.tpl +99 -0
  34. flwr/cli/new/templates/app/code/task.jax.py.tpl +57 -0
  35. flwr/cli/new/templates/app/code/task.mlx.py.tpl +102 -0
  36. flwr/cli/new/templates/app/code/task.pytorch.py.tpl +28 -23
  37. flwr/cli/new/templates/app/code/task.tensorflow.py.tpl +53 -0
  38. flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +39 -0
  39. flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +38 -0
  40. flwr/cli/new/templates/app/pyproject.jax.toml.tpl +34 -0
  41. flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +39 -0
  42. flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +25 -12
  43. flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +29 -14
  44. flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +33 -0
  45. flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +29 -14
  46. flwr/cli/run/run.py +168 -17
  47. flwr/cli/utils.py +75 -4
  48. flwr/client/__init__.py +6 -1
  49. flwr/client/app.py +239 -248
  50. flwr/client/client_app.py +70 -9
  51. flwr/client/dpfedavg_numpy_client.py +1 -1
  52. flwr/client/grpc_adapter_client/__init__.py +15 -0
  53. flwr/client/grpc_adapter_client/connection.py +97 -0
  54. flwr/client/grpc_client/connection.py +18 -5
  55. flwr/client/grpc_rere_client/__init__.py +1 -1
  56. flwr/client/grpc_rere_client/client_interceptor.py +158 -0
  57. flwr/client/grpc_rere_client/connection.py +127 -33
  58. flwr/client/grpc_rere_client/grpc_adapter.py +140 -0
  59. flwr/client/heartbeat.py +74 -0
  60. flwr/client/message_handler/__init__.py +1 -1
  61. flwr/client/message_handler/message_handler.py +7 -7
  62. flwr/client/mod/__init__.py +5 -5
  63. flwr/client/mod/centraldp_mods.py +4 -2
  64. flwr/client/mod/comms_mods.py +4 -4
  65. flwr/client/mod/localdp_mod.py +9 -4
  66. flwr/client/mod/secure_aggregation/__init__.py +1 -1
  67. flwr/client/mod/secure_aggregation/secaggplus_mod.py +1 -1
  68. flwr/client/mod/utils.py +1 -1
  69. flwr/client/node_state.py +60 -10
  70. flwr/client/node_state_tests.py +4 -3
  71. flwr/client/rest_client/__init__.py +1 -1
  72. flwr/client/rest_client/connection.py +177 -157
  73. flwr/client/supernode/__init__.py +26 -0
  74. flwr/client/supernode/app.py +464 -0
  75. flwr/client/typing.py +1 -0
  76. flwr/common/__init__.py +13 -11
  77. flwr/common/address.py +1 -1
  78. flwr/common/config.py +193 -0
  79. flwr/common/constant.py +42 -1
  80. flwr/common/context.py +26 -1
  81. flwr/common/date.py +1 -1
  82. flwr/common/dp.py +1 -1
  83. flwr/common/grpc.py +6 -2
  84. flwr/common/logger.py +79 -8
  85. flwr/common/message.py +167 -105
  86. flwr/common/object_ref.py +126 -25
  87. flwr/common/record/__init__.py +1 -1
  88. flwr/common/record/parametersrecord.py +0 -1
  89. flwr/common/record/recordset.py +78 -27
  90. flwr/common/recordset_compat.py +8 -1
  91. flwr/common/retry_invoker.py +25 -13
  92. flwr/common/secure_aggregation/__init__.py +1 -1
  93. flwr/common/secure_aggregation/crypto/__init__.py +1 -1
  94. flwr/common/secure_aggregation/crypto/shamir.py +1 -1
  95. flwr/common/secure_aggregation/crypto/symmetric_encryption.py +21 -2
  96. flwr/common/secure_aggregation/ndarrays_arithmetic.py +1 -1
  97. flwr/common/secure_aggregation/quantization.py +1 -1
  98. flwr/common/secure_aggregation/secaggplus_constants.py +1 -1
  99. flwr/common/secure_aggregation/secaggplus_utils.py +1 -1
  100. flwr/common/serde.py +209 -3
  101. flwr/common/telemetry.py +25 -0
  102. flwr/common/typing.py +38 -0
  103. flwr/common/version.py +14 -0
  104. flwr/proto/clientappio_pb2.py +41 -0
  105. flwr/proto/clientappio_pb2.pyi +110 -0
  106. flwr/proto/clientappio_pb2_grpc.py +101 -0
  107. flwr/proto/clientappio_pb2_grpc.pyi +40 -0
  108. flwr/proto/common_pb2.py +36 -0
  109. flwr/proto/common_pb2.pyi +121 -0
  110. flwr/proto/common_pb2_grpc.py +4 -0
  111. flwr/proto/common_pb2_grpc.pyi +4 -0
  112. flwr/proto/driver_pb2.py +26 -19
  113. flwr/proto/driver_pb2.pyi +34 -0
  114. flwr/proto/driver_pb2_grpc.py +70 -0
  115. flwr/proto/driver_pb2_grpc.pyi +28 -0
  116. flwr/proto/exec_pb2.py +43 -0
  117. flwr/proto/exec_pb2.pyi +95 -0
  118. flwr/proto/exec_pb2_grpc.py +101 -0
  119. flwr/proto/exec_pb2_grpc.pyi +41 -0
  120. flwr/proto/fab_pb2.py +30 -0
  121. flwr/proto/fab_pb2.pyi +56 -0
  122. flwr/proto/fab_pb2_grpc.py +4 -0
  123. flwr/proto/fab_pb2_grpc.pyi +4 -0
  124. flwr/proto/fleet_pb2.py +29 -23
  125. flwr/proto/fleet_pb2.pyi +33 -0
  126. flwr/proto/fleet_pb2_grpc.py +102 -0
  127. flwr/proto/fleet_pb2_grpc.pyi +35 -0
  128. flwr/proto/grpcadapter_pb2.py +32 -0
  129. flwr/proto/grpcadapter_pb2.pyi +43 -0
  130. flwr/proto/grpcadapter_pb2_grpc.py +66 -0
  131. flwr/proto/grpcadapter_pb2_grpc.pyi +24 -0
  132. flwr/proto/message_pb2.py +41 -0
  133. flwr/proto/message_pb2.pyi +122 -0
  134. flwr/proto/message_pb2_grpc.py +4 -0
  135. flwr/proto/message_pb2_grpc.pyi +4 -0
  136. flwr/proto/run_pb2.py +35 -0
  137. flwr/proto/run_pb2.pyi +76 -0
  138. flwr/proto/run_pb2_grpc.py +4 -0
  139. flwr/proto/run_pb2_grpc.pyi +4 -0
  140. flwr/proto/task_pb2.py +7 -8
  141. flwr/proto/task_pb2.pyi +8 -5
  142. flwr/server/__init__.py +4 -8
  143. flwr/server/app.py +298 -350
  144. flwr/server/compat/app.py +6 -57
  145. flwr/server/compat/app_utils.py +5 -4
  146. flwr/server/compat/driver_client_proxy.py +29 -48
  147. flwr/server/compat/legacy_context.py +5 -4
  148. flwr/server/driver/__init__.py +2 -0
  149. flwr/server/driver/driver.py +22 -132
  150. flwr/server/driver/grpc_driver.py +224 -74
  151. flwr/server/driver/inmemory_driver.py +183 -0
  152. flwr/server/history.py +20 -20
  153. flwr/server/run_serverapp.py +121 -34
  154. flwr/server/server.py +11 -7
  155. flwr/server/server_app.py +59 -10
  156. flwr/server/serverapp_components.py +52 -0
  157. flwr/server/strategy/__init__.py +2 -2
  158. flwr/server/strategy/bulyan.py +1 -1
  159. flwr/server/strategy/dp_adaptive_clipping.py +3 -3
  160. flwr/server/strategy/dp_fixed_clipping.py +4 -3
  161. flwr/server/strategy/dpfedavg_adaptive.py +1 -1
  162. flwr/server/strategy/dpfedavg_fixed.py +1 -1
  163. flwr/server/strategy/fedadagrad.py +1 -1
  164. flwr/server/strategy/fedadam.py +1 -1
  165. flwr/server/strategy/fedavg_android.py +1 -1
  166. flwr/server/strategy/fedavgm.py +1 -1
  167. flwr/server/strategy/fedmedian.py +1 -1
  168. flwr/server/strategy/fedopt.py +1 -1
  169. flwr/server/strategy/fedprox.py +1 -1
  170. flwr/server/strategy/fedxgb_bagging.py +1 -1
  171. flwr/server/strategy/fedxgb_cyclic.py +1 -1
  172. flwr/server/strategy/fedxgb_nn_avg.py +1 -1
  173. flwr/server/strategy/fedyogi.py +1 -1
  174. flwr/server/strategy/krum.py +1 -1
  175. flwr/server/strategy/qfedavg.py +1 -1
  176. flwr/server/superlink/driver/__init__.py +1 -1
  177. flwr/server/superlink/driver/driver_grpc.py +1 -1
  178. flwr/server/superlink/driver/driver_servicer.py +51 -4
  179. flwr/server/superlink/ffs/__init__.py +24 -0
  180. flwr/server/superlink/ffs/disk_ffs.py +104 -0
  181. flwr/server/superlink/ffs/ffs.py +79 -0
  182. flwr/server/superlink/fleet/__init__.py +1 -1
  183. flwr/server/superlink/fleet/grpc_adapter/__init__.py +15 -0
  184. flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +131 -0
  185. flwr/server/superlink/fleet/grpc_bidi/__init__.py +1 -1
  186. flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +1 -1
  187. flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py +1 -1
  188. flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py +1 -1
  189. flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +8 -2
  190. flwr/server/superlink/fleet/grpc_rere/__init__.py +1 -1
  191. flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +30 -2
  192. flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +214 -0
  193. flwr/server/superlink/fleet/message_handler/__init__.py +1 -1
  194. flwr/server/superlink/fleet/message_handler/message_handler.py +42 -2
  195. flwr/server/superlink/fleet/rest_rere/__init__.py +1 -1
  196. flwr/server/superlink/fleet/rest_rere/rest_api.py +59 -1
  197. flwr/server/superlink/fleet/vce/backend/__init__.py +1 -1
  198. flwr/server/superlink/fleet/vce/backend/backend.py +5 -5
  199. flwr/server/superlink/fleet/vce/backend/raybackend.py +53 -56
  200. flwr/server/superlink/fleet/vce/vce_api.py +190 -127
  201. flwr/server/superlink/state/__init__.py +1 -1
  202. flwr/server/superlink/state/in_memory_state.py +159 -42
  203. flwr/server/superlink/state/sqlite_state.py +243 -39
  204. flwr/server/superlink/state/state.py +81 -6
  205. flwr/server/superlink/state/state_factory.py +11 -2
  206. flwr/server/superlink/state/utils.py +62 -0
  207. flwr/server/typing.py +2 -0
  208. flwr/server/utils/__init__.py +1 -1
  209. flwr/server/utils/tensorboard.py +1 -1
  210. flwr/server/utils/validator.py +23 -9
  211. flwr/server/workflow/default_workflows.py +67 -25
  212. flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +18 -6
  213. flwr/simulation/__init__.py +7 -4
  214. flwr/simulation/app.py +67 -36
  215. flwr/simulation/ray_transport/__init__.py +1 -1
  216. flwr/simulation/ray_transport/ray_actor.py +20 -46
  217. flwr/simulation/ray_transport/ray_client_proxy.py +36 -16
  218. flwr/simulation/run_simulation.py +308 -92
  219. flwr/superexec/__init__.py +21 -0
  220. flwr/superexec/app.py +184 -0
  221. flwr/superexec/deployment.py +185 -0
  222. flwr/superexec/exec_grpc.py +55 -0
  223. flwr/superexec/exec_servicer.py +70 -0
  224. flwr/superexec/executor.py +75 -0
  225. flwr/superexec/simulation.py +193 -0
  226. {flwr_nightly-1.8.0.dev20240314.dist-info → flwr_nightly-1.11.0.dev20240813.dist-info}/METADATA +10 -6
  227. flwr_nightly-1.11.0.dev20240813.dist-info/RECORD +288 -0
  228. flwr_nightly-1.11.0.dev20240813.dist-info/entry_points.txt +10 -0
  229. flwr/cli/flower_toml.py +0 -140
  230. flwr/cli/new/templates/app/flower.toml.tpl +0 -13
  231. flwr/cli/new/templates/app/requirements.numpy.txt.tpl +0 -2
  232. flwr/cli/new/templates/app/requirements.pytorch.txt.tpl +0 -4
  233. flwr/cli/new/templates/app/requirements.tensorflow.txt.tpl +0 -4
  234. flwr_nightly-1.8.0.dev20240314.dist-info/RECORD +0 -211
  235. flwr_nightly-1.8.0.dev20240314.dist-info/entry_points.txt +0 -9
  236. {flwr_nightly-1.8.0.dev20240314.dist-info → flwr_nightly-1.11.0.dev20240813.dist-info}/LICENSE +0 -0
  237. {flwr_nightly-1.8.0.dev20240314.dist-info → flwr_nightly-1.11.0.dev20240813.dist-info}/WHEEL +0 -0
flwr/common/config.py ADDED
@@ -0,0 +1,193 @@
1
+ # Copyright 2024 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
+ """Provide functions for managing global Flower config."""
16
+
17
+ import os
18
+ from pathlib import Path
19
+ from typing import Any, Dict, List, Optional, Tuple, Union, cast, get_args
20
+
21
+ import tomli
22
+
23
+ from flwr.cli.config_utils import validate_fields
24
+ from flwr.common.constant import APP_DIR, FAB_CONFIG_FILE, FLWR_HOME
25
+ from flwr.common.typing import Run, UserConfig, UserConfigValue
26
+
27
+
28
+ def get_flwr_dir(provided_path: Optional[str] = None) -> Path:
29
+ """Return the Flower home directory based on env variables."""
30
+ if provided_path is None or not Path(provided_path).is_dir():
31
+ return Path(
32
+ os.getenv(
33
+ FLWR_HOME,
34
+ Path(f"{os.getenv('XDG_DATA_HOME', os.getenv('HOME'))}") / ".flwr",
35
+ )
36
+ )
37
+ return Path(provided_path).absolute()
38
+
39
+
40
+ def get_project_dir(
41
+ fab_id: str, fab_version: str, flwr_dir: Optional[Union[str, Path]] = None
42
+ ) -> Path:
43
+ """Return the project directory based on the given fab_id and fab_version."""
44
+ # Check the fab_id
45
+ if fab_id.count("/") != 1:
46
+ raise ValueError(
47
+ f"Invalid FAB ID: {fab_id}",
48
+ )
49
+ publisher, project_name = fab_id.split("/")
50
+ if flwr_dir is None:
51
+ flwr_dir = get_flwr_dir()
52
+ return Path(flwr_dir) / APP_DIR / publisher / project_name / fab_version
53
+
54
+
55
+ def get_project_config(project_dir: Union[str, Path]) -> Dict[str, Any]:
56
+ """Return pyproject.toml in the given project directory."""
57
+ # Load pyproject.toml file
58
+ toml_path = Path(project_dir) / FAB_CONFIG_FILE
59
+ if not toml_path.is_file():
60
+ raise FileNotFoundError(
61
+ f"Cannot find {FAB_CONFIG_FILE} in {project_dir}",
62
+ )
63
+ with toml_path.open(encoding="utf-8") as toml_file:
64
+ config = tomli.loads(toml_file.read())
65
+
66
+ # Validate pyproject.toml fields
67
+ is_valid, errors, _ = validate_fields(config)
68
+ if not is_valid:
69
+ error_msg = "\n".join([f" - {error}" for error in errors])
70
+ raise ValueError(
71
+ f"Invalid {FAB_CONFIG_FILE}:\n{error_msg}",
72
+ )
73
+
74
+ return config
75
+
76
+
77
+ def _fuse_dicts(
78
+ main_dict: UserConfig,
79
+ override_dict: UserConfig,
80
+ ) -> UserConfig:
81
+ fused_dict = main_dict.copy()
82
+
83
+ for key, value in override_dict.items():
84
+ if key in main_dict:
85
+ fused_dict[key] = value
86
+
87
+ return fused_dict
88
+
89
+
90
+ def get_fused_config_from_dir(
91
+ project_dir: Path, override_config: UserConfig
92
+ ) -> UserConfig:
93
+ """Merge the overrides from a given dict with the config from a Flower App."""
94
+ default_config = get_project_config(project_dir)["tool"]["flwr"]["app"].get(
95
+ "config", {}
96
+ )
97
+ flat_default_config = flatten_dict(default_config)
98
+
99
+ return _fuse_dicts(flat_default_config, override_config)
100
+
101
+
102
+ def get_fused_config(run: Run, flwr_dir: Optional[Path]) -> UserConfig:
103
+ """Merge the overrides from a `Run` with the config from a FAB.
104
+
105
+ Get the config using the fab_id and the fab_version, remove the nesting by adding
106
+ the nested keys as prefixes separated by dots, and fuse it with the override dict.
107
+ """
108
+ # Return empty dict if fab_id or fab_version is empty
109
+ if not run.fab_id or not run.fab_version:
110
+ return {}
111
+
112
+ project_dir = get_project_dir(run.fab_id, run.fab_version, flwr_dir)
113
+
114
+ # Return empty dict if project directory does not exist
115
+ if not project_dir.is_dir():
116
+ return {}
117
+
118
+ return get_fused_config_from_dir(project_dir, run.override_config)
119
+
120
+
121
+ def flatten_dict(
122
+ raw_dict: Optional[Dict[str, Any]], parent_key: str = ""
123
+ ) -> UserConfig:
124
+ """Flatten dict by joining nested keys with a given separator."""
125
+ if raw_dict is None:
126
+ return {}
127
+
128
+ items: List[Tuple[str, UserConfigValue]] = []
129
+ separator: str = "."
130
+ for k, v in raw_dict.items():
131
+ new_key = f"{parent_key}{separator}{k}" if parent_key else k
132
+ if isinstance(v, dict):
133
+ items.extend(flatten_dict(v, parent_key=new_key).items())
134
+ elif isinstance(v, get_args(UserConfigValue)):
135
+ items.append((new_key, cast(UserConfigValue, v)))
136
+ else:
137
+ raise ValueError(
138
+ f"The value for key {k} needs to be of type `int`, `float`, "
139
+ "`bool, `str`, or a `dict` of those.",
140
+ )
141
+ return dict(items)
142
+
143
+
144
+ def unflatten_dict(flat_dict: Dict[str, Any]) -> Dict[str, Any]:
145
+ """Unflatten a dict with keys containing separators into a nested dict."""
146
+ unflattened_dict: Dict[str, Any] = {}
147
+ separator: str = "."
148
+
149
+ for key, value in flat_dict.items():
150
+ parts = key.split(separator)
151
+ d = unflattened_dict
152
+ for part in parts[:-1]:
153
+ if part not in d:
154
+ d[part] = {}
155
+ d = d[part]
156
+ d[parts[-1]] = value
157
+
158
+ return unflattened_dict
159
+
160
+
161
+ def parse_config_args(
162
+ config: Optional[List[str]],
163
+ separator: str = ",",
164
+ ) -> UserConfig:
165
+ """Parse separator separated list of key-value pairs separated by '='."""
166
+ overrides: UserConfig = {}
167
+
168
+ if config is None:
169
+ return overrides
170
+
171
+ for config_line in config:
172
+ if config_line:
173
+ overrides_list = config_line.split(separator)
174
+ if (
175
+ len(overrides_list) == 1
176
+ and "=" not in overrides_list
177
+ and overrides_list[0].endswith(".toml")
178
+ ):
179
+ with Path(overrides_list[0]).open("rb") as config_file:
180
+ overrides = flatten_dict(tomli.load(config_file))
181
+ else:
182
+ toml_str = "\n".join(overrides_list)
183
+ overrides.update(tomli.loads(toml_str))
184
+
185
+ return overrides
186
+
187
+
188
+ def get_metadata_from_config(config: Dict[str, Any]) -> Tuple[str, str]:
189
+ """Extract `fab_version` and `fab_id` from a project config."""
190
+ return (
191
+ config["project"]["version"],
192
+ f"{config['tool']['flwr']['app']['publisher']}/{config['project']['name']}",
193
+ )
flwr/common/constant.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2020 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2023 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.
@@ -27,6 +27,7 @@ To use the REST API, install `flwr` with the `rest` extra:
27
27
 
28
28
  TRANSPORT_TYPE_GRPC_BIDI = "grpc-bidi"
29
29
  TRANSPORT_TYPE_GRPC_RERE = "grpc-rere"
30
+ TRANSPORT_TYPE_GRPC_ADAPTER = "grpc-adapter"
30
31
  TRANSPORT_TYPE_REST = "rest"
31
32
  TRANSPORT_TYPE_VCE = "vce"
32
33
  TRANSPORT_TYPES = [
@@ -36,6 +37,33 @@ TRANSPORT_TYPES = [
36
37
  TRANSPORT_TYPE_VCE,
37
38
  ]
38
39
 
40
+ SUPEREXEC_DEFAULT_ADDRESS = "0.0.0.0:9093"
41
+
42
+ # Constants for ping
43
+ PING_DEFAULT_INTERVAL = 30
44
+ PING_CALL_TIMEOUT = 5
45
+ PING_BASE_MULTIPLIER = 0.8
46
+ PING_RANDOM_RANGE = (-0.1, 0.1)
47
+ PING_MAX_INTERVAL = 1e300
48
+
49
+ # IDs
50
+ RUN_ID_NUM_BYTES = 8
51
+ NODE_ID_NUM_BYTES = 8
52
+ GRPC_ADAPTER_METADATA_FLOWER_VERSION_KEY = "flower-version"
53
+ GRPC_ADAPTER_METADATA_SHOULD_EXIT_KEY = "should-exit"
54
+
55
+ # Constants for FAB
56
+ APP_DIR = "apps"
57
+ FAB_CONFIG_FILE = "pyproject.toml"
58
+ FLWR_HOME = "FLWR_HOME"
59
+
60
+ # Constants entries in Node config for Simulation
61
+ PARTITION_ID_KEY = "partition-id"
62
+ NUM_PARTITIONS_KEY = "num-partitions"
63
+
64
+ GRPC_ADAPTER_METADATA_FLOWER_VERSION_KEY = "flower-version"
65
+ GRPC_ADAPTER_METADATA_SHOULD_EXIT_KEY = "should-exit"
66
+
39
67
 
40
68
  class MessageType:
41
69
  """Message type."""
@@ -68,3 +96,16 @@ class SType:
68
96
  def __new__(cls) -> SType:
69
97
  """Prevent instantiation."""
70
98
  raise TypeError(f"{cls.__name__} cannot be instantiated.")
99
+
100
+
101
+ class ErrorCode:
102
+ """Error codes for Message's Error."""
103
+
104
+ UNKNOWN = 0
105
+ LOAD_CLIENT_APP_EXCEPTION = 1
106
+ CLIENT_APP_RAISED_EXCEPTION = 2
107
+ NODE_UNAVAILABLE = 3
108
+
109
+ def __new__(cls) -> ErrorCode:
110
+ """Prevent instantiation."""
111
+ raise TypeError(f"{cls.__name__} cannot be instantiated.")
flwr/common/context.py CHANGED
@@ -18,14 +18,20 @@
18
18
  from dataclasses import dataclass
19
19
 
20
20
  from .record import RecordSet
21
+ from .typing import UserConfig
21
22
 
22
23
 
23
24
  @dataclass
24
25
  class Context:
25
- """State of your run.
26
+ """Context of your run.
26
27
 
27
28
  Parameters
28
29
  ----------
30
+ node_id : int
31
+ The ID that identifies the node.
32
+ node_config : UserConfig
33
+ A config (key/value mapping) unique to the node and independent of the
34
+ `run_config`. This config persists across all runs this node participates in.
29
35
  state : RecordSet
30
36
  Holds records added by the entity in a given run and that will stay local.
31
37
  This means that the data it holds will never leave the system it's running from.
@@ -33,6 +39,25 @@ class Context:
33
39
  executing mods. It can also be used as a memory to access
34
40
  at different points during the lifecycle of this entity (e.g. across
35
41
  multiple rounds)
42
+ run_config : UserConfig
43
+ A config (key/value mapping) held by the entity in a given run and that will
44
+ stay local. It can be used at any point during the lifecycle of this entity
45
+ (e.g. across multiple rounds)
36
46
  """
37
47
 
48
+ node_id: int
49
+ node_config: UserConfig
38
50
  state: RecordSet
51
+ run_config: UserConfig
52
+
53
+ def __init__( # pylint: disable=too-many-arguments
54
+ self,
55
+ node_id: int,
56
+ node_config: UserConfig,
57
+ state: RecordSet,
58
+ run_config: UserConfig,
59
+ ) -> None:
60
+ self.node_id = node_id
61
+ self.node_config = node_config
62
+ self.state = state
63
+ self.run_config = run_config
flwr/common/date.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2020 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2023 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.
flwr/common/dp.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2020 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2022 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.
flwr/common/grpc.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2020 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2022 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.
@@ -16,7 +16,7 @@
16
16
 
17
17
 
18
18
  from logging import DEBUG
19
- from typing import Optional
19
+ from typing import Optional, Sequence
20
20
 
21
21
  import grpc
22
22
 
@@ -30,6 +30,7 @@ def create_channel(
30
30
  insecure: bool,
31
31
  root_certificates: Optional[bytes] = None,
32
32
  max_message_length: int = GRPC_MAX_MESSAGE_LENGTH,
33
+ interceptors: Optional[Sequence[grpc.UnaryUnaryClientInterceptor]] = None,
33
34
  ) -> grpc.Channel:
34
35
  """Create a gRPC channel, either secure or insecure."""
35
36
  # Check for conflicting parameters
@@ -57,4 +58,7 @@ def create_channel(
57
58
  )
58
59
  log(DEBUG, "Opened secure gRPC connection using certificates")
59
60
 
61
+ if interceptors is not None:
62
+ channel = grpc.intercept_channel(channel, interceptors)
63
+
60
64
  return channel
flwr/common/logger.py CHANGED
@@ -82,13 +82,20 @@ class ConsoleHandler(StreamHandler):
82
82
  return formatter.format(record)
83
83
 
84
84
 
85
- def update_console_handler(level: int, timestamps: bool, colored: bool) -> None:
85
+ def update_console_handler(
86
+ level: Optional[int] = None,
87
+ timestamps: Optional[bool] = None,
88
+ colored: Optional[bool] = None,
89
+ ) -> None:
86
90
  """Update the logging handler."""
87
91
  for handler in logging.getLogger(LOGGER_NAME).handlers:
88
92
  if isinstance(handler, ConsoleHandler):
89
- handler.setLevel(level)
90
- handler.timestamps = timestamps
91
- handler.colored = colored
93
+ if level is not None:
94
+ handler.setLevel(level)
95
+ if timestamps is not None:
96
+ handler.timestamps = timestamps
97
+ if colored is not None:
98
+ handler.colored = colored
92
99
 
93
100
 
94
101
  # Configure console logger
@@ -164,13 +171,13 @@ logger = logging.getLogger(LOGGER_NAME) # pylint: disable=invalid-name
164
171
  log = logger.log # pylint: disable=invalid-name
165
172
 
166
173
 
167
- def warn_experimental_feature(name: str) -> None:
168
- """Warn the user when they use an experimental feature."""
174
+ def warn_preview_feature(name: str) -> None:
175
+ """Warn the user when they use a preview feature."""
169
176
  log(
170
177
  WARN,
171
- """EXPERIMENTAL FEATURE: %s
178
+ """PREVIEW FEATURE: %s
172
179
 
173
- This is an experimental feature. It could change significantly or be removed
180
+ This is a preview feature. It could change significantly or be removed
174
181
  entirely in future versions of Flower.
175
182
  """,
176
183
  name,
@@ -188,3 +195,67 @@ def warn_deprecated_feature(name: str) -> None:
188
195
  """,
189
196
  name,
190
197
  )
198
+
199
+
200
+ def warn_deprecated_feature_with_example(
201
+ deprecation_message: str, example_message: str, code_example: str
202
+ ) -> None:
203
+ """Warn if a feature is deprecated and show code example."""
204
+ log(
205
+ WARN,
206
+ """DEPRECATED FEATURE: %s
207
+
208
+ Check the following `FEATURE UPDATE` warning message for the preferred
209
+ new mechanism to use this feature in Flower.
210
+ """,
211
+ deprecation_message,
212
+ )
213
+ log(
214
+ WARN,
215
+ """FEATURE UPDATE: %s
216
+ ------------------------------------------------------------
217
+ %s
218
+ ------------------------------------------------------------
219
+ """,
220
+ example_message,
221
+ code_example,
222
+ )
223
+
224
+
225
+ def warn_unsupported_feature(name: str) -> None:
226
+ """Warn the user when they use an unsupported feature."""
227
+ log(
228
+ WARN,
229
+ """UNSUPPORTED FEATURE: %s
230
+
231
+ This is an unsupported feature. It will be removed
232
+ entirely in future versions of Flower.
233
+ """,
234
+ name,
235
+ )
236
+
237
+
238
+ def set_logger_propagation(
239
+ child_logger: logging.Logger, value: bool = True
240
+ ) -> logging.Logger:
241
+ """Set the logger propagation attribute.
242
+
243
+ Parameters
244
+ ----------
245
+ child_logger : logging.Logger
246
+ Child logger object
247
+ value : bool
248
+ Boolean setting for propagation. If True, both parent and child logger
249
+ display messages. Otherwise, only the child logger displays a message.
250
+ This False setting prevents duplicate logs in Colab notebooks.
251
+ Reference: https://stackoverflow.com/a/19561320
252
+
253
+ Returns
254
+ -------
255
+ logging.Logger
256
+ Child logger object with updated propagation setting
257
+ """
258
+ child_logger.propagate = value
259
+ if not child_logger.propagate:
260
+ child_logger.log(logging.DEBUG, "Logger propagate set to False")
261
+ return child_logger