diff --git a/src/py/flwr/cli/config_utils.py b/src/py/flwr/cli/config_utils.py index 40fa1e0d3b98..4a081985efc2 100644 --- a/src/py/flwr/cli/config_utils.py +++ b/src/py/flwr/cli/config_utils.py @@ -230,10 +230,14 @@ def load_from_string(toml_content: str) -> Optional[dict[str, Any]]: return None -def validate_project_config( +def process_loaded_project_config( config: Union[dict[str, Any], None], errors: list[str], warnings: list[str] ) -> dict[str, Any]: - """Validate and return the Flower project configuration.""" + """Process and return the loaded project configuration. + + This function handles errors and warnings from the `load_and_validate` function, + exits on critical issues, and returns the validated configuration. + """ if config is None: typer.secho( "Project configuration could not be loaded.\n" @@ -324,3 +328,15 @@ def validate_certificate_in_federation_config( raise typer.Exit(code=1) return insecure, root_certificates_bytes + + +def exit_if_no_address(federation_config: dict[str, Any], cmd: str) -> None: + """Exit if the provided federation_config has no "address" key.""" + if "address" not in federation_config: + typer.secho( + f"❌ `flwr {cmd}` currently works with a SuperLink. Ensure that the correct" + "SuperLink (Exec API) address is provided in `pyproject.toml`.", + fg=typer.colors.RED, + bold=True, + ) + raise typer.Exit(code=1) diff --git a/src/py/flwr/cli/config_utils_test.py b/src/py/flwr/cli/config_utils_test.py index 01003b25abed..f58c1fd75082 100644 --- a/src/py/flwr/cli/config_utils_test.py +++ b/src/py/flwr/cli/config_utils_test.py @@ -24,11 +24,11 @@ from .config_utils import ( load, + process_loaded_project_config, validate, validate_certificate_in_federation_config, validate_federation_in_project_config, validate_fields, - validate_project_config, ) @@ -349,7 +349,7 @@ def test_validate_project_config_fail() -> None: # Execute with pytest.raises(click.exceptions.Exit) as excinfo: - _ = validate_project_config(config, errors, warnings) + _ = process_loaded_project_config(config, errors, warnings) # Assert assert excinfo.value.exit_code == 1 diff --git a/src/py/flwr/cli/log.py b/src/py/flwr/cli/log.py index c0bd5c8726e9..f0ee02b05cc1 100644 --- a/src/py/flwr/cli/log.py +++ b/src/py/flwr/cli/log.py @@ -23,9 +23,10 @@ import typer from flwr.cli.config_utils import ( + exit_if_no_address, load_and_validate, + process_loaded_project_config, validate_federation_in_project_config, - validate_project_config, ) from flwr.common.constant import CONN_RECONNECT_INTERVAL, CONN_REFRESH_PERIOD from flwr.common.logger import log as logger @@ -152,19 +153,11 @@ def log( pyproject_path = app / "pyproject.toml" if app else None config, errors, warnings = load_and_validate(path=pyproject_path) - config = validate_project_config(config, errors, warnings) + config = process_loaded_project_config(config, errors, warnings) federation, federation_config = validate_federation_in_project_config( federation, config ) - - if "address" not in federation_config: - typer.secho( - "❌ `flwr log` currently works with Exec API. Ensure that the correct" - "Exec API address is provided in the `pyproject.toml`.", - fg=typer.colors.RED, - bold=True, - ) - raise typer.Exit(code=1) + exit_if_no_address(federation_config, "log") _log_with_exec_api(app, federation, federation_config, run_id, stream) diff --git a/src/py/flwr/cli/login/login.py b/src/py/flwr/cli/login/login.py index 83448c42d662..83d4d2723e32 100644 --- a/src/py/flwr/cli/login/login.py +++ b/src/py/flwr/cli/login/login.py @@ -20,9 +20,10 @@ import typer from flwr.cli.config_utils import ( + exit_if_no_address, load_and_validate, + process_loaded_project_config, validate_federation_in_project_config, - validate_project_config, ) from flwr.common.constant import AUTH_TYPE from flwr.proto.exec_pb2 import ( # pylint: disable=E0611 @@ -50,20 +51,11 @@ def login( # pylint: disable=R0914 pyproject_path = app / "pyproject.toml" if app else None config, errors, warnings = load_and_validate(path=pyproject_path) - config = validate_project_config(config, errors, warnings) + config = process_loaded_project_config(config, errors, warnings) federation, federation_config = validate_federation_in_project_config( federation, config ) - - if "address" not in federation_config: - typer.secho( - "❌ `flwr login` currently works with a SuperLink. Ensure that the correct" - "SuperLink (Exec API) address is provided in `pyproject.toml`.", - fg=typer.colors.RED, - bold=True, - ) - raise typer.Exit(code=1) - + exit_if_no_address(federation_config, "login") channel = init_channel(app, federation_config, None) stub = ExecStub(channel) diff --git a/src/py/flwr/cli/ls.py b/src/py/flwr/cli/ls.py index e5d2b2380cbc..cc9b87219abf 100644 --- a/src/py/flwr/cli/ls.py +++ b/src/py/flwr/cli/ls.py @@ -28,9 +28,10 @@ from typer import Exit from flwr.cli.config_utils import ( + exit_if_no_address, load_and_validate, + process_loaded_project_config, validate_federation_in_project_config, - validate_project_config, ) from flwr.common.constant import FAB_CONFIG_FILE, CliOutputFormat, SubStatus from flwr.common.date import format_timedelta, isoformat8601_utc @@ -92,19 +93,11 @@ def ls( # pylint: disable=too-many-locals, too-many-branches pyproject_path = app / FAB_CONFIG_FILE if app else None config, errors, warnings = load_and_validate(path=pyproject_path) - config = validate_project_config(config, errors, warnings) + config = process_loaded_project_config(config, errors, warnings) federation, federation_config = validate_federation_in_project_config( federation, config ) - - if "address" not in federation_config: - typer.secho( - "❌ `flwr ls` currently works with Exec API. Ensure that the correct" - "Exec API address is provided in the `pyproject.toml`.", - fg=typer.colors.RED, - bold=True, - ) - raise typer.Exit(code=1) + exit_if_no_address(federation_config, "ls") try: if runs and run_id is not None: diff --git a/src/py/flwr/cli/run/run.py b/src/py/flwr/cli/run/run.py index d85d56401491..aae7e88747c8 100644 --- a/src/py/flwr/cli/run/run.py +++ b/src/py/flwr/cli/run/run.py @@ -27,8 +27,8 @@ from flwr.cli.config_utils import ( get_fab_metadata, load_and_validate, + process_loaded_project_config, validate_federation_in_project_config, - validate_project_config, ) from flwr.common.config import ( flatten_dict, @@ -101,7 +101,7 @@ def run( pyproject_path = app / "pyproject.toml" if app else None config, errors, warnings = load_and_validate(path=pyproject_path) - config = validate_project_config(config, errors, warnings) + config = process_loaded_project_config(config, errors, warnings) federation, federation_config = validate_federation_in_project_config( federation, config ) diff --git a/src/py/flwr/cli/stop.py b/src/py/flwr/cli/stop.py index 6274372d05b3..bcf07f8a6156 100644 --- a/src/py/flwr/cli/stop.py +++ b/src/py/flwr/cli/stop.py @@ -21,9 +21,10 @@ import typer from flwr.cli.config_utils import ( + exit_if_no_address, load_and_validate, + process_loaded_project_config, validate_federation_in_project_config, - validate_project_config, ) from flwr.common.constant import FAB_CONFIG_FILE from flwr.proto.exec_pb2 import StopRunRequest, StopRunResponse # pylint: disable=E0611 @@ -52,19 +53,11 @@ def stop( pyproject_path = app / FAB_CONFIG_FILE if app else None config, errors, warnings = load_and_validate(path=pyproject_path) - config = validate_project_config(config, errors, warnings) + config = process_loaded_project_config(config, errors, warnings) federation, federation_config = validate_federation_in_project_config( federation, config ) - - if "address" not in federation_config: - typer.secho( - "❌ `flwr stop` currently works with Exec API. Ensure that the correct" - "Exec API address is provided in the `pyproject.toml`.", - fg=typer.colors.RED, - bold=True, - ) - raise typer.Exit(code=1) + exit_if_no_address(federation_config, "stop") try: auth_plugin = try_obtain_cli_auth_plugin(app, federation, federation_config)