diff --git a/.generator/cli.py b/.generator/cli.py index 2d216d0aac3a..417ef2d0a922 100644 --- a/.generator/cli.py +++ b/.generator/cli.py @@ -21,6 +21,16 @@ import subprocess from typing import Dict, List +try: + import synthtool + from synthtool import gcp + + SYNTHTOOL_INSTALLED = True + SYNTHTOOL_IMPORT_ERROR = None +except ImportError as e: + SYNTHTOOL_IMPORT_ERROR = e + SYNTHTOOL_INSTALLED = False + logger = logging.getLogger() LIBRARIAN_DIR = "librarian" @@ -174,6 +184,18 @@ def _locate_and_extract_artifact(bazel_rule: str, library_id: str): ) from e +def _run_post_processor(): + """Runs the synthtool post-processor on the output directory. + """ + logger.info("Running Python post-processor...") + if SYNTHTOOL_INSTALLED: + command = ["python3", "-m", "synthtool.languages.python_mono_repo"] + subprocess.run(command, cwd=OUTPUT_DIR, text=True, check=True) + else: + raise SYNTHTOOL_IMPORT_ERROR + logger.info("Python post-processor ran successfully.") + + def handle_generate(): """The main coordinator for the code generation process. @@ -185,8 +207,8 @@ def handle_generate(): ValueError: If the `generate-request.json` file is not found or read. """ - # Read a generate-request.json file try: + # Read a generate-request.json file request_data = _read_json_file(f"{LIBRARIAN_DIR}/{GENERATE_REQUEST_FILE}") library_id = _get_library_id(request_data) @@ -196,8 +218,8 @@ def handle_generate(): bazel_rule = _determine_bazel_rule(api_path) _build_bazel_target(bazel_rule) _locate_and_extract_artifact(bazel_rule, library_id) + _run_post_processor() - logger.info(json.dumps(request_data, indent=2)) except Exception as e: raise ValueError("Generation failed.") from e diff --git a/.generator/test_cli.py b/.generator/test_cli.py index d14619443a72..552ebaee024e 100644 --- a/.generator/test_cli.py +++ b/.generator/test_cli.py @@ -31,6 +31,7 @@ _read_json_file, _run_individual_session, _run_nox_sessions, + _run_post_processor, handle_build, handle_configure, handle_generate, @@ -205,6 +206,33 @@ def test_locate_and_extract_artifact_fails(mocker, caplog): ) +def test_run_post_processor_success(mocker, caplog): + """ + Tests that the post-processor helper calls the correct command. + """ + caplog.set_level(logging.INFO) + mocker.patch("cli.SYNTHTOOL_INSTALLED", return_value=True) + mock_subprocess = mocker.patch("cli.subprocess.run") + + _run_post_processor() + + mock_subprocess.assert_called_once() + + assert mock_subprocess.call_args.kwargs["cwd"] == "output" + assert "Python post-processor ran successfully." in caplog.text + + +def test_locate_and_extract_artifact_fails(mocker, caplog): + """ + Tests that an exception is raised if the subprocess command fails. + """ + caplog.set_level(logging.INFO) + mocker.patch("cli.SYNTHTOOL_INSTALLED", return_value=True) + + with pytest.raises(FileNotFoundError): + _run_post_processor() + + def test_handle_generate_success(caplog, mock_generate_request_file, mocker): """ Tests the successful execution path of handle_generate. @@ -215,8 +243,8 @@ def test_handle_generate_success(caplog, mock_generate_request_file, mocker): "cli._determine_bazel_rule", return_value="mock-rule" ) mock_build_target = mocker.patch("cli._build_bazel_target") - mock_locate_and_extract_artifact = mocker.patch("cli._locate_and_extract_artifact") + mock_run_post_processor = mocker.patch("cli._run_post_processor") handle_generate()