diff --git a/src/py/flwr/cli/flower_toml.py b/src/py/flwr/cli/config_utils.py similarity index 91% rename from src/py/flwr/cli/flower_toml.py rename to src/py/flwr/cli/config_utils.py index 103f83532054..a6bf1d5a6b3a 100644 --- a/src/py/flwr/cli/flower_toml.py +++ b/src/py/flwr/cli/config_utils.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # ============================================================================== -"""Utility to validate the `flower.toml` file.""" +"""Utility to validate the `pyproject.toml` file.""" import os from typing import Any, Dict, List, Optional, Tuple @@ -25,7 +25,7 @@ def load_and_validate_with_defaults( path: Optional[str] = None, ) -> Tuple[Optional[Dict[str, Any]], List[str], List[str]]: - """Load and validate flower.toml as dict. + """Load and validate pyproject.toml as dict. Returns ------- @@ -37,7 +37,7 @@ def load_and_validate_with_defaults( if config is None: errors = [ - "Project configuration could not be loaded. flower.toml does not exist." + "Project configuration could not be loaded. pyproject.toml does not exist." ] return (None, errors, []) @@ -58,10 +58,10 @@ def load_and_validate_with_defaults( def load(path: Optional[str] = None) -> Optional[Dict[str, Any]]: - """Load flower.toml and return as dict.""" + """Load pyproject.toml and return as dict.""" if path is None: cur_dir = os.getcwd() - toml_path = os.path.join(cur_dir, "flower.toml") + toml_path = os.path.join(cur_dir, "pyproject.toml") else: toml_path = path @@ -74,7 +74,7 @@ def load(path: Optional[str] = None) -> Optional[Dict[str, Any]]: def validate_fields(config: Dict[str, Any]) -> Tuple[bool, List[str], List[str]]: - """Validate flower.toml fields.""" + """Validate pyproject.toml fields.""" errors = [] warnings = [] @@ -106,7 +106,7 @@ def validate_fields(config: Dict[str, Any]) -> Tuple[bool, List[str], List[str]] def validate(config: Dict[str, Any]) -> Tuple[bool, List[str], List[str]]: - """Validate flower.toml.""" + """Validate pyproject.toml.""" is_valid, errors, warnings = validate_fields(config) if not is_valid: diff --git a/src/py/flwr/cli/flower_toml_test.py b/src/py/flwr/cli/config_utils_test.py similarity index 65% rename from src/py/flwr/cli/flower_toml_test.py rename to src/py/flwr/cli/config_utils_test.py index 72a52e4e8b9b..4d949c0042a4 100644 --- a/src/py/flwr/cli/flower_toml_test.py +++ b/src/py/flwr/cli/config_utils_test.py @@ -18,15 +18,29 @@ import textwrap from typing import Any, Dict -from .flower_toml import load, validate, validate_fields +from .config_utils import load, validate, validate_fields -def test_load_flower_toml_load_from_cwd(tmp_path: str) -> None: +def test_load_pyproject_toml_load_from_cwd(tmp_path: str) -> None: """Test if load_template returns a string.""" # Prepare - flower_toml_content = """ + pyproject_toml_content = """ + [build-system] + requires = ["hatchling"] + build-backend = "hatchling.build" + [project] name = "fedgpt" + version = "1.0.0" + description = "" + authors = [ + { name = "The Flower Authors", email = "hello@flower.ai" }, + ] + license = {text = "Apache License (2.0)"} + dependencies = [ + "flwr[simulation]>=1.8.0,<2.0", + "numpy>=1.21.0", + ] [flower.components] serverapp = "fedgpt.server:app" @@ -39,8 +53,14 @@ def test_load_flower_toml_load_from_cwd(tmp_path: str) -> None: count = 10 # optional """ expected_config = { + "build-system": {"build-backend": "hatchling.build", "requires": ["hatchling"]}, "project": { "name": "fedgpt", + "version": "1.0.0", + "description": "", + "authors": [{"email": "hello@flower.ai", "name": "The Flower Authors"}], + "license": {"text": "Apache License (2.0)"}, + "dependencies": ["flwr[simulation]>=1.8.0,<2.0", "numpy>=1.21.0"], }, "flower": { "components": { @@ -60,8 +80,8 @@ def test_load_flower_toml_load_from_cwd(tmp_path: str) -> None: try: # Change into the temporary directory os.chdir(tmp_path) - with open("flower.toml", "w", encoding="utf-8") as f: - f.write(textwrap.dedent(flower_toml_content)) + with open("pyproject.toml", "w", encoding="utf-8") as f: + f.write(textwrap.dedent(pyproject_toml_content)) # Execute config = load() @@ -72,12 +92,26 @@ def test_load_flower_toml_load_from_cwd(tmp_path: str) -> None: os.chdir(origin) -def test_load_flower_toml_from_path(tmp_path: str) -> None: +def test_load_pyproject_toml_from_path(tmp_path: str) -> None: """Test if load_template returns a string.""" # Prepare - flower_toml_content = """ + pyproject_toml_content = """ + [build-system] + requires = ["hatchling"] + build-backend = "hatchling.build" + [project] name = "fedgpt" + version = "1.0.0" + description = "" + authors = [ + { name = "The Flower Authors", email = "hello@flower.ai" }, + ] + license = {text = "Apache License (2.0)"} + dependencies = [ + "flwr[simulation]>=1.8.0,<2.0", + "numpy>=1.21.0", + ] [flower.components] serverapp = "fedgpt.server:app" @@ -90,8 +124,14 @@ def test_load_flower_toml_from_path(tmp_path: str) -> None: count = 10 # optional """ expected_config = { + "build-system": {"build-backend": "hatchling.build", "requires": ["hatchling"]}, "project": { "name": "fedgpt", + "version": "1.0.0", + "description": "", + "authors": [{"email": "hello@flower.ai", "name": "The Flower Authors"}], + "license": {"text": "Apache License (2.0)"}, + "dependencies": ["flwr[simulation]>=1.8.0,<2.0", "numpy>=1.21.0"], }, "flower": { "components": { @@ -111,11 +151,11 @@ def test_load_flower_toml_from_path(tmp_path: str) -> None: try: # Change into the temporary directory os.chdir(tmp_path) - with open("flower.toml", "w", encoding="utf-8") as f: - f.write(textwrap.dedent(flower_toml_content)) + with open("pyproject.toml", "w", encoding="utf-8") as f: + f.write(textwrap.dedent(pyproject_toml_content)) # Execute - config = load(path=os.path.join(tmp_path, "flower.toml")) + config = load(path=os.path.join(tmp_path, "pyproject.toml")) # Assert assert config == expected_config @@ -123,8 +163,8 @@ def test_load_flower_toml_from_path(tmp_path: str) -> None: os.chdir(origin) -def test_validate_flower_toml_fields_empty() -> None: - """Test that validate_flower_toml_fields fails correctly.""" +def test_validate_pyproject_toml_fields_empty() -> None: + """Test that validate_pyproject_toml_fields fails correctly.""" # Prepare config: Dict[str, Any] = {} @@ -137,8 +177,8 @@ def test_validate_flower_toml_fields_empty() -> None: assert len(warnings) == 0 -def test_validate_flower_toml_fields_no_flower() -> None: - """Test that validate_flower_toml_fields fails correctly.""" +def test_validate_pyproject_toml_fields_no_flower() -> None: + """Test that validate_pyproject_toml_fields fails correctly.""" # Prepare config = { "project": { @@ -159,8 +199,8 @@ def test_validate_flower_toml_fields_no_flower() -> None: assert len(warnings) == 0 -def test_validate_flower_toml_fields_no_flower_components() -> None: - """Test that validate_flower_toml_fields fails correctly.""" +def test_validate_pyproject_toml_fields_no_flower_components() -> None: + """Test that validate_pyproject_toml_fields fails correctly.""" # Prepare config = { "project": { @@ -182,8 +222,8 @@ def test_validate_flower_toml_fields_no_flower_components() -> None: assert len(warnings) == 0 -def test_validate_flower_toml_fields_no_server_and_client_app() -> None: - """Test that validate_flower_toml_fields fails correctly.""" +def test_validate_pyproject_toml_fields_no_server_and_client_app() -> None: + """Test that validate_pyproject_toml_fields fails correctly.""" # Prepare config = { "project": { @@ -205,8 +245,8 @@ def test_validate_flower_toml_fields_no_server_and_client_app() -> None: assert len(warnings) == 0 -def test_validate_flower_toml_fields() -> None: - """Test that validate_flower_toml_fields succeeds correctly.""" +def test_validate_pyproject_toml_fields() -> None: + """Test that validate_pyproject_toml_fields succeeds correctly.""" # Prepare config = { "project": { @@ -228,8 +268,8 @@ def test_validate_flower_toml_fields() -> None: assert len(warnings) == 0 -def test_validate_flower_toml() -> None: - """Test that validate_flower_toml succeeds correctly.""" +def test_validate_pyproject_toml() -> None: + """Test that validate_pyproject_toml succeeds correctly.""" # Prepare config = { "project": { @@ -256,8 +296,8 @@ def test_validate_flower_toml() -> None: assert not warnings -def test_validate_flower_toml_fail() -> None: - """Test that validate_flower_toml fails correctly.""" +def test_validate_pyproject_toml_fail() -> None: + """Test that validate_pyproject_toml fails correctly.""" # Prepare config = { "project": { diff --git a/src/py/flwr/cli/new/new.py b/src/py/flwr/cli/new/new.py index 0c429ce34cf2..ee451d710a02 100644 --- a/src/py/flwr/cli/new/new.py +++ b/src/py/flwr/cli/new/new.py @@ -128,7 +128,6 @@ def new( # List of files to render files = { "README.md": {"template": "app/README.md.tpl"}, - "flower.toml": {"template": "app/flower.toml.tpl"}, "pyproject.toml": {"template": f"app/pyproject.{framework_str}.toml.tpl"}, f"{pnl}/__init__.py": {"template": "app/code/__init__.py.tpl"}, f"{pnl}/server.py": {"template": f"app/code/server.{framework_str}.py.tpl"}, diff --git a/src/py/flwr/cli/new/new_test.py b/src/py/flwr/cli/new/new_test.py index 11620d234191..93372758265d 100644 --- a/src/py/flwr/cli/new/new_test.py +++ b/src/py/flwr/cli/new/new_test.py @@ -68,7 +68,6 @@ def test_new(tmp_path: str) -> None: expected_files_top_level = { "fedgpt", "README.md", - "flower.toml", "pyproject.toml", } expected_files_module = { diff --git a/src/py/flwr/cli/new/templates/app/flower.toml.tpl b/src/py/flwr/cli/new/templates/app/flower.toml.tpl deleted file mode 100644 index 07a6ffaf9e49..000000000000 --- a/src/py/flwr/cli/new/templates/app/flower.toml.tpl +++ /dev/null @@ -1,13 +0,0 @@ -[project] -name = "$project_name" -version = "1.0.0" -description = "" -license = "Apache-2.0" -authors = [ - "The Flower Authors ", -] -readme = "README.md" - -[flower.components] -serverapp = "$project_name.server:app" -clientapp = "$project_name.client:app" diff --git a/src/py/flwr/cli/new/templates/app/pyproject.numpy.toml.tpl b/src/py/flwr/cli/new/templates/app/pyproject.numpy.toml.tpl index 9701c62af6f0..47e5dda2a4a4 100644 --- a/src/py/flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +++ b/src/py/flwr/cli/new/templates/app/pyproject.numpy.toml.tpl @@ -17,3 +17,7 @@ dependencies = [ [tool.hatch.build.targets.wheel] packages = ["."] + +[flower.components] +serverapp = "$project_name.server:app" +clientapp = "$project_name.client:app" diff --git a/src/py/flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl b/src/py/flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl index 0661c7b730c1..dec132890e81 100644 --- a/src/py/flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +++ b/src/py/flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl @@ -19,3 +19,7 @@ dependencies = [ [tool.hatch.build.targets.wheel] packages = ["."] + +[flower.components] +serverapp = "$project_name.server:app" +clientapp = "$project_name.client:app" diff --git a/src/py/flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl b/src/py/flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl index 5a017eb6ed74..2afe9a86e2c5 100644 --- a/src/py/flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +++ b/src/py/flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl @@ -18,3 +18,7 @@ dependencies = [ [tool.hatch.build.targets.wheel] packages = ["."] + +[flower.components] +serverapp = "$project_name.server:app" +clientapp = "$project_name.client:app" diff --git a/src/py/flwr/cli/run/run.py b/src/py/flwr/cli/run/run.py index 98b5da1843a6..4cd5124e1668 100644 --- a/src/py/flwr/cli/run/run.py +++ b/src/py/flwr/cli/run/run.py @@ -18,7 +18,7 @@ import typer -from flwr.cli import flower_toml +from flwr.cli import config_utils from flwr.simulation.run_simulation import _run_simulation @@ -26,7 +26,7 @@ def run() -> None: """Run Flower project.""" typer.secho("Loading project configuration... ", fg=typer.colors.BLUE) - config, errors, warnings = flower_toml.load_and_validate_with_defaults() + config, errors, warnings = config_utils.load_and_validate_with_defaults() if config is None: typer.secho(