flwr-nightly 1.20.0.dev20250630__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 +1 -2
- flwr/common/exit/exit_code.py +5 -0
- flwr/supercore/license_plugin/__init__.py +22 -0
- flwr/supercore/license_plugin/license_plugin.py +34 -0
- flwr/superexec/exec_grpc.py +19 -1
- flwr/superexec/exec_license_interceptor.py +74 -0
- {flwr_nightly-1.20.0.dev20250630.dist-info → flwr_nightly-1.20.0.dev20250701.dist-info}/METADATA +1 -1
- {flwr_nightly-1.20.0.dev20250630.dist-info → flwr_nightly-1.20.0.dev20250701.dist-info}/RECORD +10 -7
- {flwr_nightly-1.20.0.dev20250630.dist-info → flwr_nightly-1.20.0.dev20250701.dist-info}/WHEEL +0 -0
- {flwr_nightly-1.20.0.dev20250630.dist-info → flwr_nightly-1.20.0.dev20250701.dist-info}/entry_points.txt +0 -0
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
|
-
"❌
|
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/exit/exit_code.py
CHANGED
@@ -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: (
|
@@ -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."""
|
flwr/superexec/exec_grpc.py
CHANGED
@@ -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
|
-
|
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
|
+
)
|
{flwr_nightly-1.20.0.dev20250630.dist-info → flwr_nightly-1.20.0.dev20250701.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: flwr-nightly
|
3
|
-
Version: 1.20.0.
|
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
|
{flwr_nightly-1.20.0.dev20250630.dist-info → flwr_nightly-1.20.0.dev20250701.dist-info}/RECORD
RENAMED
@@ -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=
|
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
|
@@ -118,7 +118,7 @@ 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=
|
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
|
@@ -332,6 +332,8 @@ flwr/supercore/ffs/__init__.py,sha256=U3KXwG_SplEvchat27K0LYPoPHzh-cwwT_NHsGlYMt
|
|
332
332
|
flwr/supercore/ffs/disk_ffs.py,sha256=c5VywSaRnq3XM_zuJptNtsF2HFwsRK0pvBd5-5CNONs,3272
|
333
333
|
flwr/supercore/ffs/ffs.py,sha256=6w7wy71i7tbuJwqEgdeCa49JejXMEof3jujURN_R7Rg,2395
|
334
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
|
335
337
|
flwr/supercore/object_store/__init__.py,sha256=cdfPAmjINY6iOp8oI_LdcVh2simg469Mkdl4LLV4kHI,911
|
336
338
|
flwr/supercore/object_store/in_memory_object_store.py,sha256=oflJcOuVNgx9A-B2da4VHDb1qj_Jub9wKFOrUBgtz_U,9630
|
337
339
|
flwr/supercore/object_store/object_store.py,sha256=VlZz-yzoWZtITbnYD8vwLZbFosv7vgr1XVNzByObeY0,5853
|
@@ -341,7 +343,8 @@ flwr/superexec/__init__.py,sha256=YFqER0IJc1XEWfsX6AxZ9LSRq0sawPYrNYki-brvTIc,71
|
|
341
343
|
flwr/superexec/app.py,sha256=U2jjOHb2LGWoU7vrl9_czTzre9O2mPxu3CPGUZ86sK4,1465
|
342
344
|
flwr/superexec/deployment.py,sha256=cFxhFom-0zv93HLNjNcUdBy3Sf6JwshRoXPQtcZunF0,6797
|
343
345
|
flwr/superexec/exec_event_log_interceptor.py,sha256=7aBjZ4lkpOIyWut0s394OpMePr16g_Te594s-9aDM9Q,5774
|
344
|
-
flwr/superexec/exec_grpc.py,sha256=
|
346
|
+
flwr/superexec/exec_grpc.py,sha256=xyyxrhPQLGX7tafGadv5DXLUzVoUIkGLd53_raOfNHk,4202
|
347
|
+
flwr/superexec/exec_license_interceptor.py,sha256=-BZPO-ZwtrPYumcDNdEh2QZK4uX-tuYBsZqKuDK0MZ8,3026
|
345
348
|
flwr/superexec/exec_servicer.py,sha256=c0nwdFBiS6CbKrRA7ffOpsgASOLeaRV_ICwxDfxNGAg,12498
|
346
349
|
flwr/superexec/exec_user_auth_interceptor.py,sha256=HpGHTcDKzB7XUiQHXgntNVFYL-VfP9Wj5tEVc04VOOw,5820
|
347
350
|
flwr/superexec/executor.py,sha256=LaErHRJvNggjWV6FI6eajgKfnwOvSv2UqzFH253yDro,3265
|
@@ -361,7 +364,7 @@ flwr/supernode/servicer/__init__.py,sha256=lucTzre5WPK7G1YLCfaqg3rbFWdNSb7ZTt-ca
|
|
361
364
|
flwr/supernode/servicer/clientappio/__init__.py,sha256=7Oy62Y_oijqF7Dxi6tpcUQyOpLc_QpIRZ83NvwmB0Yg,813
|
362
365
|
flwr/supernode/servicer/clientappio/clientappio_servicer.py,sha256=d3GdIabycUoDBDL_eVlt513knGSjQW3-9lG6Cw4QEk4,5719
|
363
366
|
flwr/supernode/start_client_internal.py,sha256=DAXuReZ1FCXt9Y1KbM0p-dI50ROWPEJXzfKrl14OE6k,18233
|
364
|
-
flwr_nightly-1.20.0.
|
365
|
-
flwr_nightly-1.20.0.
|
366
|
-
flwr_nightly-1.20.0.
|
367
|
-
flwr_nightly-1.20.0.
|
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,,
|
{flwr_nightly-1.20.0.dev20250630.dist-info → flwr_nightly-1.20.0.dev20250701.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|