From fae9880f49fd8dd46ee5d4ecb59040038d832197 Mon Sep 17 00:00:00 2001 From: Hiroyuki Nakazato Date: Tue, 7 Oct 2025 14:02:02 +0900 Subject: [PATCH 01/17] feat: add llm-transpile command with Switch integration Implement llm-transpile command for LLM-based code transpilation: - Add SwitchInstaller for Switch transpiler package management - Install Switch package and deploy to workspace - Create and manage Databricks jobs with job-level parameters - Configure Switch resources (catalog, schema, volume) - Add SwitchRunner for executing Switch transpilation jobs - Upload source files to workspace volume - Execute transpilation via Databricks job - Download results and handle job lifecycle - Add llm-transpile CLI command with Switch transpiler support - Add comprehensive unit and integration tests --- .gitignore | 1 + labs.yml | 12 + src/databricks/labs/lakebridge/cli.py | 232 +++++++++++- .../lakebridge/transpiler/switch_runner.py | 191 ++++++++++ tests/conftest.py | 15 + tests/integration/transpile/test_switch.py | 85 +++++ ...ricks_switch_plugin-0.1.2-py3-none-any.whl | Bin 0 -> 116809 bytes tests/unit/test_cli_transpile.py | 111 ++++++ tests/unit/transpiler/test_switch_runner.py | 346 ++++++++++++++++++ 9 files changed, 992 insertions(+), 1 deletion(-) create mode 100644 src/databricks/labs/lakebridge/transpiler/switch_runner.py create mode 100644 tests/integration/transpile/test_switch.py create mode 100644 tests/resources/transpiler_configs/switch/wheel/databricks_switch_plugin-0.1.2-py3-none-any.whl create mode 100644 tests/unit/transpiler/test_switch_runner.py diff --git a/.gitignore b/.gitignore index 466c618f0a..12de8f3425 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ remorph_transpile/ /linter/src/main/antlr4/library/gen/ .databricks-login.json .mypy_cache +.env diff --git a/labs.yml b/labs.yml index 5d4629ddfe..74c37c21f4 100644 --- a/labs.yml +++ b/labs.yml @@ -49,6 +49,18 @@ commands: total_files_processed\ttotal_queries_processed\tanalysis_error_count\tparsing_error_count\tvalidation_error_count\tgeneration_error_count\terror_log_file {{range .}}{{.total_files_processed}}\t{{.total_queries_processed}}\t{{.analysis_error_count}}\t{{.parsing_error_count}}\t{{.validation_error_count}}\t{{.generation_error_count}}\t{{.error_log_file}} {{end}} + - name: llm-transpile + description: Transpile source code to Databricks using LLM Transpiler (Switch) + flags: + - name: input-source + description: Input Script Folder or File (local path) + default: null + - name: output-ws-folder + description: Output folder path (Databricks Workspace path starting with /Workspace/) + default: null + - name: source-dialect + description: Source dialect name (e.g., 'snowflake', 'teradata') + default: null - name: reconcile description: Reconcile is an utility to streamline the reconciliation process between source data and target data residing on Databricks. - name: aggregates-reconcile diff --git a/src/databricks/labs/lakebridge/cli.py b/src/databricks/labs/lakebridge/cli.py index 699add3f36..ce9008dbf6 100644 --- a/src/databricks/labs/lakebridge/cli.py +++ b/src/databricks/labs/lakebridge/cli.py @@ -17,6 +17,7 @@ from databricks.labs.blueprint.cli import App from databricks.labs.blueprint.entrypoint import get_logger, is_in_debug from databricks.labs.blueprint.installation import RootJsonValue +from databricks.labs.blueprint.installer import InstallState from databricks.labs.blueprint.tui import Prompts @@ -33,9 +34,10 @@ from databricks.labs.lakebridge.reconcile.recon_config import RECONCILE_OPERATION_NAME, AGG_RECONCILE_OPERATION_NAME from databricks.labs.lakebridge.transpiler.describe import TranspilersDescription from databricks.labs.lakebridge.transpiler.execute import transpile as do_transpile -from databricks.labs.lakebridge.transpiler.lsp.lsp_engine import LSPEngine +from databricks.labs.lakebridge.transpiler.lsp.lsp_engine import LSPConfig, LSPEngine from databricks.labs.lakebridge.transpiler.repository import TranspilerRepository from databricks.labs.lakebridge.transpiler.sqlglot.sqlglot_engine import SqlglotEngine +from databricks.labs.lakebridge.transpiler.switch_runner import SwitchConfig, SwitchRunner from databricks.labs.lakebridge.transpiler.transpile_engine import TranspileEngine from databricks.labs.lakebridge.transpiler.transpile_status import ErrorSeverity @@ -530,6 +532,234 @@ def _override_workspace_client_config(ctx: ApplicationContext, overrides: dict[s ctx.connect_config.cluster_id = cluster_id +@lakebridge.command +def llm_transpile( + *, + w: WorkspaceClient, + input_source: str | None = None, + output_ws_folder: str | None = None, + source_dialect: str | None = None, + transpiler_repository: TranspilerRepository = TranspilerRepository.user_home(), +) -> None: + """Transpile source code to Databricks using LLM Transpiler (Switch)""" + ctx = ApplicationContext(w) + ctx.add_user_agent_extra("cmd", "llm-transpile") + user = ctx.current_user + logger.debug(f"User: {user}") + + checker = _LLMTranspileConfigChecker(ctx.transpile_config, ctx.prompts, ctx.install_state, transpiler_repository) + checker.use_input_source(input_source) + checker.use_output_ws_folder(output_ws_folder) + checker.use_source_dialect(source_dialect) + params = checker.check() + + result = _llm_transpile(ctx, params) + print(json.dumps(result)) + + +class _LLMTranspileConfigChecker: + """Helper class for 'llm-transpile' command configuration validation""" + + _transpile_config: TranspileConfig | None + _prompts: Prompts + _install_state: InstallState + _transpiler_repository: TranspilerRepository + _input_source: str | None = None + _output_ws_folder: str | None = None + _source_dialect: str | None = None + + def __init__( + self, + transpile_config: TranspileConfig | None, + prompts: Prompts, + install_state: InstallState, + transpiler_repository: TranspilerRepository, + ): + self._transpile_config = transpile_config + self._prompts = prompts + self._install_state = install_state + self._transpiler_repository = transpiler_repository + + @staticmethod + def _validate_input_source_path(input_source: str, msg: str) -> None: + """Validate the input source: it must be a path that exists.""" + if not Path(input_source).exists(): + raise_validation_exception(msg) + + def use_input_source(self, input_source: str | None) -> None: + if input_source is not None: + logger.debug(f"Setting input_source to: {input_source!r}") + self._validate_input_source_path(input_source, f"Invalid path for '--input-source': {input_source}") + self._input_source = input_source + + def _prompt_input_source(self) -> None: + default_input = None + if self._transpile_config and self._transpile_config.input_source: + default_input = self._transpile_config.input_source + + if default_input: + prompt_text = f"Enter input source path (press for default: {default_input})" + prompted = self._prompts.question(prompt_text).strip() + self._input_source = prompted if prompted else default_input + else: + prompted = self._prompts.question("Enter input source path (directory or file)").strip() + self._input_source = prompted + + logger.debug(f"Setting input_source to: {self._input_source!r}") + self._validate_input_source_path(self._input_source, f"Invalid input source: {self._input_source}") + + def _check_input_source(self) -> None: + if self._input_source is None: + self._prompt_input_source() + + def use_output_ws_folder(self, output_ws_folder: str | None) -> None: + if output_ws_folder is not None: + logger.debug(f"Setting output_ws_folder to: {output_ws_folder!r}") + self._validate_output_ws_folder_path( + output_ws_folder, f"Invalid path for '--output-ws-folder': {output_ws_folder}" + ) + self._output_ws_folder = output_ws_folder + + @staticmethod + def _validate_output_ws_folder_path(output_ws_folder: str, msg: str) -> None: + """Validate output folder is a Workspace path.""" + if not output_ws_folder.startswith("/Workspace/"): + raise_validation_exception(f"{msg}. Must start with /Workspace/") + + def _prompt_output_ws_folder(self) -> None: + prompted_output_ws_folder = self._prompts.question( + "Enter output folder path (Databricks Workspace path starting with /Workspace/)" + ).strip() + logger.debug(f"Setting output_ws_folder to: {prompted_output_ws_folder!r}") + self._validate_output_ws_folder_path( + prompted_output_ws_folder, f"Invalid output folder: {prompted_output_ws_folder}" + ) + self._output_ws_folder = prompted_output_ws_folder + + def _check_output_ws_folder(self) -> None: + if self._output_ws_folder is None: + self._prompt_output_ws_folder() + + def use_source_dialect(self, source_dialect: str | None) -> None: + if source_dialect is not None: + logger.debug(f"Setting source_dialect to: {source_dialect!r}") + self._source_dialect = source_dialect + + def _prompt_source_dialect(self) -> None: + """Prompt for source dialect from Switch dialects.""" + available_dialects = self._get_switch_dialects() + + if not available_dialects: + raise_validation_exception( + "No Switch dialects available. " + "Install with: databricks labs lakebridge install-transpile --include-llm-transpiler" + ) + + logger.debug(f"Available dialects: {available_dialects!r}") + source_dialect = self._prompts.choice("Select the source dialect:", list(sorted(available_dialects))) + + self._source_dialect = source_dialect + + def _check_source_dialect(self) -> None: + """Validate and prompt for source dialect if not provided.""" + available_dialects = self._get_switch_dialects() + + if self._source_dialect is None: + self._prompt_source_dialect() + elif self._source_dialect not in available_dialects: + supported = ", ".join(sorted(available_dialects)) + raise_validation_exception(f"Invalid source-dialect: '{self._source_dialect}'. " f"Available: {supported}") + + def _get_switch_dialects(self) -> set[str]: + """Get Switch dialects from config.yml using LSPConfig.""" + config_path = self._transpiler_repository.transpiler_config_path("Switch") + if not config_path.exists(): + return set() + + try: + lsp_config = LSPConfig.load(config_path) + return set(lsp_config.remorph.dialects) + except (OSError, ValueError) as e: + logger.warning(f"Failed to load Switch dialects: {e}") + return set() + + def _get_switch_options_with_defaults(self) -> dict[str, str]: + """Get default values for Switch options from config.yml.""" + config_path = self._transpiler_repository.transpiler_config_path("Switch") + if not config_path.exists(): + return {} + + try: + lsp_config = LSPConfig.load(config_path) + except (OSError, ValueError) as e: + logger.warning(f"Failed to load Switch options: {e}") + return {} + + options_all = lsp_config.options_for_dialect("all") + result = {} + for option in options_all: + if option.default and option.default != "": + result[option.flag] = option.default + + logger.debug(f"Loaded {len(result)} Switch options with defaults from config.yml") + return result + + def _validate_switch_options(self, options: dict[str, str]) -> None: + """Validate options against config.yml choices.""" + config_path = self._transpiler_repository.transpiler_config_path("Switch") + if not config_path.exists(): + return + + try: + lsp_config = LSPConfig.load(config_path) + except (OSError, ValueError) as e: + logger.warning(f"Failed to validate Switch options: {e}") + return + + options_all = lsp_config.options_for_dialect("all") + for option in options_all: + if option.flag in options and option.choices: + value = options[option.flag] + if value not in option.choices: + raise_validation_exception( + f"Invalid value for '{option.flag}': {value!r}. " f"Must be one of: {', '.join(option.choices)}" + ) + + def check(self) -> dict: + """Validate all parameters and return configuration dict.""" + logger.debug("Checking llm-transpile configuration") + + self._check_input_source() + self._check_output_ws_folder() + self._check_source_dialect() + + switch_options = self._get_switch_options_with_defaults() + self._validate_switch_options(switch_options) + + wait_for_completion = str(switch_options.pop("wait_for_completion", "false")).lower() == "true" + + return { + "input_source": self._input_source, + "output_ws_folder": self._output_ws_folder, + "source_dialect": self._source_dialect, + "switch_options": switch_options, + "wait_for_completion": wait_for_completion, + } + + +def _llm_transpile(ctx: ApplicationContext, params: dict) -> RootJsonValue: + """Execute LLM transpilation via Switch job.""" + config = SwitchConfig(ctx.install_state) + resources = config.get_resources() + job_id = config.get_job_id() + + runner = SwitchRunner(ctx.workspace_client, ctx.installation) + + return runner.run( + catalog=resources["catalog"], schema=resources["schema"], volume=resources["volume"], job_id=job_id, **params + ) + + @lakebridge.command def reconcile(*, w: WorkspaceClient) -> None: """[EXPERIMENTAL] Reconciles source to Databricks datasets""" diff --git a/src/databricks/labs/lakebridge/transpiler/switch_runner.py b/src/databricks/labs/lakebridge/transpiler/switch_runner.py new file mode 100644 index 0000000000..88ebc62663 --- /dev/null +++ b/src/databricks/labs/lakebridge/transpiler/switch_runner.py @@ -0,0 +1,191 @@ +import io +import logging +import os +import random +import string +from datetime import datetime, timezone +from pathlib import Path + +from databricks.labs.blueprint.installation import Installation, RootJsonValue +from databricks.labs.blueprint.installer import InstallState +from databricks.sdk import WorkspaceClient + +logger = logging.getLogger(__name__) + + +class SwitchConfig: + """Helper to load Switch configuration from InstallState.""" + + def __init__(self, install_state: InstallState): + self._install_state = install_state + + def get_resources(self) -> dict[str, str]: + """Get catalog, schema, volume from switch_resources.""" + resources = self._install_state.switch_resources + + if not resources or not all(k in resources for k in ("catalog", "schema", "volume")): + raise SystemExit( + "Switch resources not configured. " + "Please run 'databricks labs lakebridge install-transpile --include-llm-transpiler' first." + ) + + return { + "catalog": resources["catalog"], + "schema": resources["schema"], + "volume": resources["volume"], + } + + def get_job_id(self) -> int: + """Get Switch job ID from InstallState.""" + if "Switch" in self._install_state.jobs: + logger.debug("Switch job ID found in InstallState") + return int(self._install_state.jobs["Switch"]) + + raise SystemExit( + "Switch Job ID not found. " + "Please run 'databricks labs lakebridge install-transpile --include-llm-transpiler' first." + ) + + +class SwitchRunner: + """Runner for Switch LLM transpilation jobs.""" + + def __init__( + self, + ws: WorkspaceClient, + installation: Installation, + ): + self._ws = ws + self._installation = installation + + def run( + self, + input_source: str, + output_ws_folder: str, + source_dialect: str, + catalog: str, + schema: str, + volume: str, + job_id: int, + switch_options: dict[str, str], + wait_for_completion: bool = False, + ) -> RootJsonValue: + """Upload local files to Volume and trigger Switch job.""" + volume_input_path = self._upload_to_volume(Path(input_source), catalog, schema, volume) + + job_params = self._build_job_parameters( + volume_input_path=volume_input_path, + output_ws_folder=output_ws_folder, + catalog=catalog, + schema=schema, + source_dialect=source_dialect, + switch_options=switch_options, + ) + logger.info(f"Triggering Switch job with job_id: {job_id}") + + return self._run_job(job_id, job_params, wait_for_completion) + + def _upload_to_volume( + self, + local_path: Path, + catalog: str, + schema: str, + volume: str, + ) -> str: + """Upload local files to UC Volume with unique timestamped path.""" + now = datetime.now(timezone.utc) + time_part = now.strftime("%Y%m%d%H%M%S") + random_part = ''.join(random.choices(string.ascii_lowercase + string.digits, k=4)) + timestamp_suffix = f"{time_part}_{random_part}" + volume_base_path = f"/Volumes/{catalog}/{schema}/{volume}" + volume_input_path = f"{volume_base_path}/input_{timestamp_suffix}" + + logger.info(f"Uploading {local_path} to {volume_input_path}...") + + # File upload + if local_path.is_file(): + volume_file_path = f"{volume_input_path}/{local_path.name}" + with open(local_path, 'rb') as f: + content = f.read() + self._ws.files.upload(file_path=volume_file_path, contents=io.BytesIO(content), overwrite=True) + logger.debug(f"Uploaded: {local_path} -> {volume_file_path}") + + # Directory upload + else: + for root, _, files in os.walk(local_path): + for file in files: + local_file = Path(root) / file + relative_path = local_file.relative_to(local_path) + volume_file_path = f"{volume_input_path}/{relative_path}" + + with open(local_file, 'rb') as f: + content = f.read() + + self._ws.files.upload(file_path=volume_file_path, contents=io.BytesIO(content), overwrite=True) + logger.debug(f"Uploaded: {local_file} -> {volume_file_path}") + + logger.info(f"Upload complete: {volume_input_path}") + return volume_input_path + + def _build_job_parameters( + self, + volume_input_path: str, + output_ws_folder: str, + catalog: str, + schema: str, + source_dialect: str, + switch_options: dict[str, str], + ) -> dict[str, str]: + """Build Switch job parameters.""" + return { + "input_dir": volume_input_path, + "output_dir": output_ws_folder, + "result_catalog": catalog, + "result_schema": schema, + "builtin_prompt": source_dialect, + **switch_options, + } + + def _run_job( + self, + job_id: int, + job_params: dict[str, str], + wait_for_completion: bool, + ) -> RootJsonValue: + """Execute Switch job and return run information.""" + if wait_for_completion: + run = self._ws.jobs.run_now_and_wait(job_id, job_parameters=job_params) + + if not run.run_id: + raise SystemExit(f"Job {job_id} execution failed.") + + job_run_url = f"{self._ws.config.host}/jobs/{job_id}/runs/{run.run_id}" + logger.info(f"Switch LLM transpilation job completed: {job_run_url}") + + return [ + { + "job_id": job_id, + "run_id": run.run_id, + "run_url": job_run_url, + "state": ( + run.state.life_cycle_state.value if run.state and run.state.life_cycle_state else "UNKNOWN" + ), + "result_state": run.state.result_state.value if run.state and run.state.result_state else None, + } + ] + + wait = self._ws.jobs.run_now(job_id, job_parameters=job_params) + + if not wait.run_id: + raise SystemExit(f"Job {job_id} execution failed.") + + job_run_url = f"{self._ws.config.host}/jobs/{job_id}/runs/{wait.run_id}" + logger.info(f"Switch LLM transpilation job started: {job_run_url}") + + return [ + { + "job_id": job_id, + "run_id": wait.run_id, + "run_url": job_run_url, + } + ] diff --git a/tests/conftest.py b/tests/conftest.py index 3ab95ca759..000f07d6d1 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -312,6 +312,21 @@ def morpheus_artifact() -> Path: return artifact +@pytest.fixture +def switch_artifact() -> Path: + """Get Switch wheel for testing.""" + artifact = ( + Path(__file__).parent + / "resources" + / "transpiler_configs" + / "switch" + / "wheel" + / "databricks_switch_plugin-0.1.2-py3-none-any.whl" + ) + assert artifact.exists(), f"Switch artifact not found: {artifact}" + return artifact + + class FakeDataSource(DataSource): def __init__(self, start_delimiter: str, end_delimiter: str): diff --git a/tests/integration/transpile/test_switch.py b/tests/integration/transpile/test_switch.py new file mode 100644 index 0000000000..2cad5aab2f --- /dev/null +++ b/tests/integration/transpile/test_switch.py @@ -0,0 +1,85 @@ +from pathlib import Path + +from databricks.labs.blueprint.installer import InstallState +from databricks.sdk import WorkspaceClient +from databricks.sdk.errors import NotFound + +from databricks.labs.lakebridge.contexts.application import ApplicationContext +from databricks.labs.lakebridge.transpiler.installers import SwitchInstaller +from databricks.labs.lakebridge.transpiler.repository import TranspilerRepository +from databricks.labs.lakebridge.transpiler.switch_runner import SwitchConfig + + +def test_switch_installation(ws: WorkspaceClient, switch_artifact: Path): + """Test Switch installation, job creation, resource persistence, and cleanup.""" + context = ApplicationContext(ws) + installation = context.installation + + install_state = InstallState.from_installation(installation) + install_state.switch_resources["catalog"] = "test_catalog" + install_state.switch_resources["schema"] = "test_schema" + install_state.switch_resources["volume"] = "test_volume" + install_state.save() + + transpiler_repository = TranspilerRepository.user_home() + installer = SwitchInstaller(transpiler_repository, ws, installation) + + result = installer.install(switch_artifact) + assert result, "Switch installation failed" + + try: + install_state = InstallState.from_installation(installation) + job_id = _verify_job_creation(ws, install_state) + _verify_resource_persistence(install_state) + _verify_job_id_retrieval(install_state, job_id) + finally: + try: + installer.uninstall() + installation.remove() + except NotFound: + pass + + +def _verify_job_creation(ws: WorkspaceClient, install_state: InstallState): + """Verify job creation and registration.""" + assert "Switch" in install_state.jobs + job_id = int(install_state.jobs["Switch"]) + + job = ws.jobs.get(job_id) + assert job is not None + assert job.settings is not None + assert job.settings.name is not None + assert "switch" in job.settings.name.lower() + + assert job.settings.tasks is not None + assert len(job.settings.tasks) > 0 + task = job.settings.tasks[0] + assert task.notebook_task is not None + assert "switch" in task.notebook_task.notebook_path.lower() + return job_id + + +def _verify_resource_persistence(install_state: InstallState): + """Verify resource persistence.""" + assert install_state.switch_resources is not None + resources = install_state.switch_resources + assert "catalog" in resources + assert "schema" in resources + assert "volume" in resources + + switch_config = SwitchConfig(install_state) + retrieved_resources = switch_config.get_resources() + + assert retrieved_resources["catalog"] == "test_catalog" + assert retrieved_resources["schema"] == "test_schema" + assert retrieved_resources["volume"] == "test_volume" + + +def _verify_job_id_retrieval(install_state: InstallState, expected_job_id: int): + """Verify job ID retrieval.""" + switch_config = SwitchConfig(install_state) + job_id_from_config = switch_config.get_job_id() + + assert isinstance(job_id_from_config, int) + assert job_id_from_config > 0 + assert job_id_from_config == expected_job_id diff --git a/tests/resources/transpiler_configs/switch/wheel/databricks_switch_plugin-0.1.2-py3-none-any.whl b/tests/resources/transpiler_configs/switch/wheel/databricks_switch_plugin-0.1.2-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..81655cea01108fda41055ed87bccb05c16f14b76 GIT binary patch literal 116809 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 Shct2^Gdkd%OffJF0R11>o`Sak literal 0 HcmV?d00001 diff --git a/tests/unit/test_cli_transpile.py b/tests/unit/test_cli_transpile.py index 4164cc9649..02d11ffd3c 100644 --- a/tests/unit/test_cli_transpile.py +++ b/tests/unit/test_cli_transpile.py @@ -10,6 +10,7 @@ from databricks.labs.lakebridge import cli +from databricks.labs.blueprint.installer import InstallState from databricks.labs.blueprint.tui import MockPrompts from databricks.labs.lakebridge.config import TranspileConfig from databricks.sdk import WorkspaceClient @@ -414,3 +415,113 @@ def test_describe_transpile(mock_cli_transpile_no_config, transpiler_repository: } ], } + + +class TestLLMTranspile: + """Test suite for llm-transpile command.""" + + @pytest.fixture + def switch_transpiler_repository(self, tmp_path: Path) -> TranspilerRepository: + """Create TranspilerRepository with Switch config.""" + switch_config = { + "remorph": { + "version": 1, + "name": "Switch", + "dialects": ["snowflake", "teradata", "oracle"], + "command_line": ["/usr/bin/true"], + }, + "options": {"all": []}, + } + config_path = tmp_path / "remorph-transpilers" / "Switch" / "lib" / "config.yml" + config_path.parent.mkdir(parents=True) + with config_path.open("w") as f: + yaml.dump(switch_config, f) + return TranspilerRepository(tmp_path) + + def test_with_valid_inputs( + self, empty_input_source: Path, switch_transpiler_repository: TranspilerRepository + ) -> None: + """Test llm-transpile with valid parameters.""" + ws = create_autospec(WorkspaceClient) + mock_runner_run = MagicMock(return_value=[{"job_id": 123, "run_id": 456, "run_url": "http://test"}]) + + with ( + patch("databricks.labs.lakebridge.cli.ApplicationContext") as mock_ctx, + patch( + "databricks.labs.lakebridge.cli._llm_transpile", return_value=mock_runner_run.return_value + ) as mock_llm, + ): + mock_ctx.return_value.transpile_config = None + mock_ctx.return_value.prompts = MockPrompts({}) + mock_ctx.return_value.install_state = create_autospec(InstallState) + + cli.llm_transpile( + w=ws, + input_source=str(empty_input_source), + output_ws_folder="/Workspace/Users/test/output", + source_dialect="snowflake", + transpiler_repository=switch_transpiler_repository, + ) + + # Verify _llm_transpile was called with correct parameters + mock_llm.assert_called_once() + call_args = mock_llm.call_args + ctx_arg = call_args[0][0] + params_arg = call_args[0][1] + + # Verify context + assert ctx_arg == mock_ctx.return_value + + # Verify parameters + assert params_arg["input_source"] == str(empty_input_source) + assert params_arg["output_ws_folder"] == "/Workspace/Users/test/output" + assert params_arg["source_dialect"] == "snowflake" + assert "switch_options" in params_arg + assert "wait_for_completion" in params_arg + + @pytest.mark.parametrize( + ("input_source_type", "output_ws_folder", "source_dialect", "expected_error"), + ( + pytest.param( + "invalid", "/Workspace/Users/test/output", "snowflake", "Invalid path", id="invalid_input_source" + ), + pytest.param( + "valid", "/invalid/path", "snowflake", "Must start with /Workspace/", id="invalid_output_folder" + ), + pytest.param( + "valid", + "/Workspace/Users/test/output", + "invalid_dialect", + "Invalid source-dialect", + id="invalid_dialect", + ), + ), + ) + def test_with_invalid_params( + self, + empty_input_source: Path, + switch_transpiler_repository: TranspilerRepository, + input_source_type: str, + output_ws_folder: str, + source_dialect: str, + expected_error: str, + ) -> None: + """Test llm-transpile with invalid parameters.""" + ws = create_autospec(WorkspaceClient) + input_source = "/invalid/path/does/not/exist" if input_source_type == "invalid" else str(empty_input_source) + + with ( + patch("databricks.labs.lakebridge.cli.ApplicationContext") as mock_ctx, + pytest.raises(ValueError, match=expected_error), + ): + mock_ctx.return_value.transpile_config = None + mock_ctx.return_value.prompts = MockPrompts({}) + mock_ctx.return_value.install_state = create_autospec(InstallState) + + cli.llm_transpile( + w=ws, + input_source=input_source, + output_ws_folder=output_ws_folder, + source_dialect=source_dialect, + transpiler_repository=switch_transpiler_repository, + ) diff --git a/tests/unit/transpiler/test_switch_runner.py b/tests/unit/transpiler/test_switch_runner.py new file mode 100644 index 0000000000..40099cdc91 --- /dev/null +++ b/tests/unit/transpiler/test_switch_runner.py @@ -0,0 +1,346 @@ +from pathlib import Path +from unittest.mock import Mock, create_autospec + +import pytest + +from databricks.labs.blueprint.installer import InstallState +from databricks.labs.lakebridge.transpiler.switch_runner import SwitchConfig, SwitchRunner + + +class FriendOfSwitchRunner(SwitchRunner): + """Friend class to access protected methods for testing.""" + + def upload_to_volume(self, local_path: Path, catalog: str, schema: str, volume: str) -> str: + return self._upload_to_volume(local_path, catalog, schema, volume) + + def build_job_parameters( + self, + volume_input_path: str, + output_ws_folder: str, + catalog: str, + schema: str, + source_dialect: str, + switch_options: dict[str, str], + ) -> dict[str, str]: + return self._build_job_parameters( + volume_input_path, output_ws_folder, catalog, schema, source_dialect, switch_options + ) + + +class TestSwitchConfig: + """Test suite for SwitchConfig.""" + + @pytest.fixture + def install_state_with_switch(self) -> InstallState: + """Create InstallState with Switch configured.""" + state = create_autospec(InstallState) + state.switch_resources = { + "catalog": "test_catalog", + "schema": "test_schema", + "volume": "test_volume", + } + state.jobs = {"Switch": "12345"} + return state + + @pytest.fixture + def install_state_without_switch(self) -> InstallState: + """Create InstallState without Switch configured.""" + state = create_autospec(InstallState) + state.switch_resources = None + state.jobs = {} + return state + + def test_get_resources_success(self, install_state_with_switch: InstallState) -> None: + """Verify successful resource retrieval from InstallState.""" + config = SwitchConfig(install_state_with_switch) + resources = config.get_resources() + + assert resources == { + "catalog": "test_catalog", + "schema": "test_schema", + "volume": "test_volume", + } + + @pytest.mark.parametrize( + ("switch_resources", "error_msg_fragment"), + ( + pytest.param(None, "Switch resources not configured", id="resources_none"), + pytest.param({"catalog": "cat"}, "Switch resources not configured", id="missing_schema_volume"), + pytest.param({"catalog": "cat", "schema": "sch"}, "Switch resources not configured", id="missing_volume"), + ), + ) + def test_get_resources_not_configured(self, switch_resources: dict | None, error_msg_fragment: str) -> None: + """Test error when switch_resources missing or incomplete.""" + state = create_autospec(InstallState) + state.switch_resources = switch_resources + + config = SwitchConfig(state) + + with pytest.raises(SystemExit, match=error_msg_fragment): + config.get_resources() + + @pytest.mark.parametrize( + ("jobs", "should_succeed"), + ( + pytest.param({"Switch": "12345"}, True, id="job_exists"), + pytest.param({"Switch": 67890}, True, id="job_exists_int"), + pytest.param({}, False, id="job_missing"), + pytest.param({"OtherJob": "99999"}, False, id="other_job_only"), + ), + ) + def test_get_job_id(self, jobs: dict, should_succeed: bool) -> None: + """Test job ID retrieval from InstallState.""" + state = create_autospec(InstallState) + state.jobs = jobs + + config = SwitchConfig(state) + + if should_succeed: + job_id = config.get_job_id() + assert isinstance(job_id, int) + assert job_id == int(jobs["Switch"]) + else: + with pytest.raises(SystemExit, match="Switch Job ID not found"): + config.get_job_id() + + +class TestSwitchRunner: + """Test suite for SwitchRunner.""" + + @pytest.fixture + def mock_ws(self) -> Mock: + """Create mock WorkspaceClient.""" + ws = Mock() + ws.config.host = "https://test.databricks.com" + ws.files.upload = Mock() + ws.jobs.run_now = Mock() + ws.jobs.run_now_and_wait = Mock() + return ws + + @pytest.fixture + def mock_installation(self) -> Mock: + """Create mock Installation.""" + return Mock() + + @pytest.fixture + def runner(self, mock_ws: Mock, mock_installation: Mock) -> FriendOfSwitchRunner: + """Create SwitchRunner instance.""" + return FriendOfSwitchRunner(mock_ws, mock_installation) + + @pytest.mark.parametrize( + "is_file", + ( + pytest.param(True, id="single_file"), + pytest.param(False, id="directory"), + ), + ) + def test_upload_to_volume(self, runner: FriendOfSwitchRunner, mock_ws: Mock, tmp_path: Path, is_file: bool) -> None: + """Test file/directory upload to Volume.""" + # Setup test files + if is_file: + test_file = tmp_path / "test.sql" + test_file.write_text("SELECT 1;") + local_path = test_file + expected_calls = 1 + else: + test_dir = tmp_path / "queries" + test_dir.mkdir() + (test_dir / "query1.sql").write_text("SELECT 1;") + (test_dir / "query2.sql").write_text("SELECT 2;") + subdir = test_dir / "subdir" + subdir.mkdir() + (subdir / "query3.sql").write_text("SELECT 3;") + local_path = test_dir + expected_calls = 3 + + # Execute upload + volume_path = runner.upload_to_volume( + local_path=local_path, catalog="test_cat", schema="test_sch", volume="test_vol" + ) + + # Verify results + assert volume_path.startswith("/Volumes/test_cat/test_sch/test_vol/input_") + assert mock_ws.files.upload.call_count == expected_calls + + def test_upload_unique_paths(self, runner: FriendOfSwitchRunner, tmp_path: Path) -> None: + """Verify UUID usage prevents path collisions.""" + test_file = tmp_path / "test.sql" + test_file.write_text("SELECT 1;") + + # Generate two upload paths + path1 = runner.upload_to_volume(test_file, "cat", "sch", "vol") + path2 = runner.upload_to_volume(test_file, "cat", "sch", "vol") + + # Paths should be different due to UUID + assert path1 != path2 + assert path1.startswith("/Volumes/cat/sch/vol/input_") + assert path2.startswith("/Volumes/cat/sch/vol/input_") + + def test_upload_preserves_hierarchy(self, runner: FriendOfSwitchRunner, mock_ws: Mock, tmp_path: Path) -> None: + """Verify subdirectory structure is maintained.""" + # Create nested directory structure + root = tmp_path / "root" + root.mkdir() + (root / "file1.sql").write_text("SELECT 1;") + subdir = root / "sub" / "deep" + subdir.mkdir(parents=True) + (subdir / "file2.sql").write_text("SELECT 2;") + + # Upload directory + runner.upload_to_volume(root, "cat", "sch", "vol") + + # Verify hierarchy is preserved + calls = [call[1]["file_path"] for call in mock_ws.files.upload.call_args_list] + assert any("file1.sql" in path and "/sub/" not in path for path in calls) + assert any("file2.sql" in path and "/sub/deep/" in path for path in calls) + + @pytest.mark.parametrize( + ("switch_options", "expected_extra_keys"), + ( + pytest.param({}, 0, id="no_options"), + pytest.param({"endpoint_name": "claude", "concurrency": "4", "log_level": "DEBUG"}, 3, id="with_options"), + ), + ) + def test_build_job_parameters( + self, runner: FriendOfSwitchRunner, switch_options: dict, expected_extra_keys: int + ) -> None: + """Test job parameter construction.""" + params = runner.build_job_parameters( + volume_input_path="/Volumes/cat/sch/vol/input_123", + output_ws_folder="/Workspace/Users/test/output", + catalog="test_cat", + schema="test_sch", + source_dialect="snowflake", + switch_options=switch_options, + ) + + # Verify required parameters + assert params["input_dir"] == "/Volumes/cat/sch/vol/input_123" + assert params["output_dir"] == "/Workspace/Users/test/output" + assert params["result_catalog"] == "test_cat" + assert params["result_schema"] == "test_sch" + assert params["builtin_prompt"] == "snowflake" + + # Verify options are included + assert len(params) == 5 + expected_extra_keys + for key, value in switch_options.items(): + assert params[key] == value + + @pytest.mark.parametrize( + "wait_for_completion", + ( + pytest.param(False, id="async_execution"), + pytest.param(True, id="sync_execution"), + ), + ) + def test_run_job_execution( + self, runner: FriendOfSwitchRunner, mock_ws: Mock, tmp_path: Path, wait_for_completion: bool + ) -> None: + """Test async/sync job execution based on wait_for_completion flag.""" + # Setup test file + test_file = tmp_path / "test.sql" + test_file.write_text("SELECT 1;") + + # Mock job execution + mock_run = Mock() + mock_run.run_id = 99999 + if wait_for_completion: + mock_run.state.life_cycle_state.value = "TERMINATED" + mock_run.state.result_state.value = "SUCCESS" + mock_ws.jobs.run_now_and_wait.return_value = mock_run + else: + mock_ws.jobs.run_now.return_value = mock_run + + # Execute run + result = runner.run( + input_source=str(test_file), + output_ws_folder="/Workspace/output", + source_dialect="snowflake", + catalog="cat", + schema="sch", + volume="vol", + job_id=12345, + switch_options={"log_level": "DEBUG"}, + wait_for_completion=wait_for_completion, + ) + + # Verify correct method called with proper job_parameters + expected_params = { + "result_catalog": "cat", + "result_schema": "sch", + "builtin_prompt": "snowflake", + "log_level": "DEBUG", + } + + if wait_for_completion: + call_args = mock_ws.jobs.run_now_and_wait.call_args + mock_ws.jobs.run_now.assert_not_called() + else: + call_args = mock_ws.jobs.run_now.call_args + mock_ws.jobs.run_now_and_wait.assert_not_called() + + # Verify job_id and job_parameters + assert call_args[0][0] == 12345 # job_id + actual_params = call_args[1]["job_parameters"] + assert actual_params["result_catalog"] == expected_params["result_catalog"] + assert actual_params["result_schema"] == expected_params["result_schema"] + assert actual_params["builtin_prompt"] == expected_params["builtin_prompt"] + assert actual_params["log_level"] == expected_params["log_level"] + assert "input_dir" in actual_params + assert "output_dir" in actual_params + + # Verify result structure + assert isinstance(result, list) + assert len(result) == 1 + first_item = result[0] + assert isinstance(first_item, dict) + + if wait_for_completion: + assert first_item["state"] == "TERMINATED" + assert first_item["result_state"] == "SUCCESS" + else: + assert "state" not in first_item + assert "result_state" not in first_item + + # Verify common result fields + assert first_item["job_id"] == 12345 + assert first_item["run_id"] == 99999 + # Verify run_url format + assert first_item["run_url"] == "https://test.databricks.com/jobs/12345/runs/99999" + + @pytest.mark.parametrize( + "wait_for_completion", + ( + pytest.param(False, id="async_missing_run_id"), + pytest.param(True, id="sync_missing_run_id"), + ), + ) + def test_run_job_execution_with_missing_run_id( + self, runner: FriendOfSwitchRunner, mock_ws: Mock, tmp_path: Path, wait_for_completion: bool + ) -> None: + """Test SystemExit when run_id is missing.""" + # Setup test file + test_file = tmp_path / "test.sql" + test_file.write_text("SELECT 1;") + + # Mock job execution with missing run_id + mock_run = Mock() + mock_run.run_id = None # Simulate missing run_id + if wait_for_completion: + mock_ws.jobs.run_now_and_wait.return_value = mock_run + else: + mock_ws.jobs.run_now.return_value = mock_run + + # Execute and expect SystemExit + with pytest.raises(SystemExit, match="Job 12345 execution failed"): + runner.run( + input_source=str(test_file), + output_ws_folder="/Workspace/output", + source_dialect="snowflake", + catalog="cat", + schema="sch", + volume="vol", + job_id=12345, + switch_options={}, + wait_for_completion=wait_for_completion, + ) From 2ee157f1540b1662fc39d0442f5fa4d1c00cc4a7 Mon Sep 17 00:00:00 2001 From: Hiroyuki Nakazato Date: Thu, 9 Oct 2025 17:08:52 +0900 Subject: [PATCH 02/17] refactor: encapsulate Switch package path resolution in SwitchDeployment Move _get_switch_package_path() from WorkspaceInstallation to SwitchDeployment as a protected method, following Single Responsibility Principle. SwitchDeployment now resolves its own package path internally. Changes: - Add _get_switch_package_path() protected method to SwitchDeployment - Update SwitchDeployment.install() signature to remove path parameter - Remove duplicate _get_switch_package_path() from WorkspaceInstallation - Remove unused sys and TranspilerRepository imports from installation.py - Update tests to use new interface with mocked path resolution --- .../lakebridge/deployment/installation.py | 16 +---------- .../labs/lakebridge/deployment/switch.py | 15 +++++++++-- tests/integration/transpile/test_switch.py | 27 ++++++++++++------- tests/unit/deployment/test_switch.py | 9 ++++--- 4 files changed, 37 insertions(+), 30 deletions(-) diff --git a/src/databricks/labs/lakebridge/deployment/installation.py b/src/databricks/labs/lakebridge/deployment/installation.py index 3bc661c5bf..709e8bc7c9 100644 --- a/src/databricks/labs/lakebridge/deployment/installation.py +++ b/src/databricks/labs/lakebridge/deployment/installation.py @@ -1,5 +1,4 @@ import logging -import sys from ast import literal_eval from pathlib import Path @@ -15,7 +14,6 @@ from databricks.labs.lakebridge.config import LakebridgeConfiguration from databricks.labs.lakebridge.deployment.recon import ReconDeployment from databricks.labs.lakebridge.deployment.switch import SwitchDeployment -from databricks.labs.lakebridge.transpiler.repository import TranspilerRepository logger = logging.getLogger("databricks.labs.lakebridge.install") @@ -110,8 +108,7 @@ def install(self, config: LakebridgeConfiguration): ) else: logger.info("Installing Switch transpiler to workspace.") - switch_package_path = self._get_switch_package_path() - self._switch_deployment.install(switch_package_path, resources) + self._switch_deployment.install(resources) def uninstall(self, config: LakebridgeConfiguration): # This will remove all the Lakebridge modules @@ -151,14 +148,3 @@ def _uninstall_switch_job(self) -> None: f"schema=`{resources['schema']}`, volume=`{resources['volume']}`. " "Please remove them manually if needed." ) - - def _get_switch_package_path(self) -> Path: - """Get Switch package path (databricks directory) from site-packages.""" - repository = TranspilerRepository.user_home() - product_path = repository.transpilers_path() / "switch" - venv_path = product_path / "lib" / ".venv" - - if sys.platform != "win32": - major, minor = sys.version_info[:2] - return venv_path / "lib" / f"python{major}.{minor}" / "site-packages" / "databricks" - return venv_path / "Lib" / "site-packages" / "databricks" diff --git a/src/databricks/labs/lakebridge/deployment/switch.py b/src/databricks/labs/lakebridge/deployment/switch.py index 2880440c7c..1a47bc2447 100644 --- a/src/databricks/labs/lakebridge/deployment/switch.py +++ b/src/databricks/labs/lakebridge/deployment/switch.py @@ -1,5 +1,6 @@ import logging import os +import sys from pathlib import Path from databricks.labs.blueprint.installation import Installation @@ -35,10 +36,10 @@ def __init__( self._job_deployer = job_deployer self._transpiler_repository = transpiler_repository - def install(self, switch_package_path: Path, resources: SwitchResourcesConfig) -> None: + def install(self, resources: SwitchResourcesConfig) -> None: """Deploy Switch to workspace and configure resources.""" logger.info("Deploying Switch to workspace...") - self._deploy_workspace(switch_package_path) + self._deploy_workspace(self._get_switch_package_path()) self._setup_job() self._record_resources(resources) logger.info("Switch deployment completed") @@ -216,3 +217,13 @@ def _record_resources(self, resources: SwitchResourcesConfig) -> None: f"Switch resources stored: catalog=`{resources.catalog}`, " f"schema=`{resources.schema}`, volume=`{resources.volume}`" ) + + def _get_switch_package_path(self) -> Path: + """Get Switch package path (databricks directory) from site-packages.""" + product_path = self._transpiler_repository.transpilers_path() / self._TRANSPILER_ID + venv_path = product_path / "lib" / ".venv" + + if sys.platform != "win32": + major, minor = sys.version_info[:2] + return venv_path / "lib" / f"python{major}.{minor}" / "site-packages" / "databricks" + return venv_path / "Lib" / "site-packages" / "databricks" diff --git a/tests/integration/transpile/test_switch.py b/tests/integration/transpile/test_switch.py index 2cad5aab2f..49531f460a 100644 --- a/tests/integration/transpile/test_switch.py +++ b/tests/integration/transpile/test_switch.py @@ -1,10 +1,14 @@ from pathlib import Path from databricks.labs.blueprint.installer import InstallState +from databricks.labs.blueprint.wheels import ProductInfo from databricks.sdk import WorkspaceClient from databricks.sdk.errors import NotFound +from databricks.labs.lakebridge.config import SwitchResourcesConfig from databricks.labs.lakebridge.contexts.application import ApplicationContext +from databricks.labs.lakebridge.deployment.job import JobDeployment +from databricks.labs.lakebridge.deployment.switch import SwitchDeployment from databricks.labs.lakebridge.transpiler.installers import SwitchInstaller from databricks.labs.lakebridge.transpiler.repository import TranspilerRepository from databricks.labs.lakebridge.transpiler.switch_runner import SwitchConfig @@ -14,18 +18,23 @@ def test_switch_installation(ws: WorkspaceClient, switch_artifact: Path): """Test Switch installation, job creation, resource persistence, and cleanup.""" context = ApplicationContext(ws) installation = context.installation - install_state = InstallState.from_installation(installation) - install_state.switch_resources["catalog"] = "test_catalog" - install_state.switch_resources["schema"] = "test_schema" - install_state.switch_resources["volume"] = "test_volume" - install_state.save() - transpiler_repository = TranspilerRepository.user_home() - installer = SwitchInstaller(transpiler_repository, ws, installation) + # Phase 1: Local installation + installer = SwitchInstaller(transpiler_repository) result = installer.install(switch_artifact) - assert result, "Switch installation failed" + assert result, "Switch local installation failed" + + # Phase 2: Workspace deployment + product_info = ProductInfo.from_class(SwitchDeployment) + job_deployer = JobDeployment(ws, installation, install_state, product_info) + switch_deployment = SwitchDeployment( + ws, installation, install_state, product_info, job_deployer, transpiler_repository + ) + + resources = SwitchResourcesConfig(catalog="test_catalog", schema="test_schema", volume="test_volume") + switch_deployment.install(resources) try: install_state = InstallState.from_installation(installation) @@ -34,7 +43,7 @@ def test_switch_installation(ws: WorkspaceClient, switch_artifact: Path): _verify_job_id_retrieval(install_state, job_id) finally: try: - installer.uninstall() + switch_deployment.uninstall() installation.remove() except NotFound: pass diff --git a/tests/unit/deployment/test_switch.py b/tests/unit/deployment/test_switch.py index d1be10c35a..08020ded92 100644 --- a/tests/unit/deployment/test_switch.py +++ b/tests/unit/deployment/test_switch.py @@ -49,10 +49,11 @@ def test_record_resources_persists_install_state(switch_deployment, install_stat install_state.switch_resources = {} install_state.save.reset_mock() + monkeypatch.setattr(switch_deployment, "_get_switch_package_path", lambda: tmp_path) monkeypatch.setattr(switch_deployment, "_deploy_workspace", lambda _: None) monkeypatch.setattr(switch_deployment, "_setup_job", lambda: None) - switch_deployment.install(tmp_path, resources) + switch_deployment.install(resources) saved = install_state.switch_resources assert saved["catalog"] == "cat" @@ -63,16 +64,16 @@ def test_record_resources_persists_install_state(switch_deployment, install_stat def test_install_records_resources(switch_deployment, monkeypatch, tmp_path): resources = SwitchResourcesConfig(catalog="cat", schema="sch", volume="vol") - package = tmp_path call_order = [] + monkeypatch.setattr(switch_deployment, "_get_switch_package_path", lambda: tmp_path) monkeypatch.setattr(switch_deployment, "_deploy_workspace", lambda pkg: call_order.append(("deploy", pkg))) monkeypatch.setattr(switch_deployment, "_setup_job", lambda: call_order.append(("setup", None))) monkeypatch.setattr(switch_deployment, "_record_resources", lambda res: call_order.append(("record", res))) - switch_deployment.install(package, resources) + switch_deployment.install(resources) - assert ("deploy", package) in call_order + assert ("deploy", tmp_path) in call_order assert ("setup", None) in call_order assert ("record", resources) in call_order From b736965ebafd86b4603827f13a179a0d80fa8c5e Mon Sep 17 00:00:00 2001 From: Hiroyuki Nakazato Date: Thu, 9 Oct 2025 17:35:41 +0900 Subject: [PATCH 03/17] test: update Switch installation tests for refactored interface Update test_installation.py to match the refactored SwitchDeployment.install() interface that now takes only resources parameter (path resolution is internal). Changes: - Remove switch_repository fixture parameter from test methods - Delete unused _StubTranspilerRepository stub class - Remove unused imports (Path, TranspilerRepository) - Update assertions to check only resources argument The tests verify that: 1. Switch installation uses configured resources correctly 2. Missing resources logs appropriate error message --- tests/unit/deployment/test_installation.py | 32 +++------------------- 1 file changed, 4 insertions(+), 28 deletions(-) diff --git a/tests/unit/deployment/test_installation.py b/tests/unit/deployment/test_installation.py index d6dee68847..157d55d970 100644 --- a/tests/unit/deployment/test_installation.py +++ b/tests/unit/deployment/test_installation.py @@ -1,5 +1,4 @@ import logging -from pathlib import Path from unittest.mock import create_autospec import pytest @@ -23,7 +22,6 @@ from databricks.labs.lakebridge.deployment.installation import WorkspaceInstallation from databricks.labs.lakebridge.deployment.recon import ReconDeployment from databricks.labs.lakebridge.deployment.switch import SwitchDeployment -from databricks.labs.lakebridge.transpiler.repository import TranspilerRepository @pytest.fixture @@ -249,28 +247,7 @@ def test_uninstall_configs_missing(ws): class TestSwitchInstallation: """Tests for Switch transpiler installation.""" - class _StubTranspilerRepository: - def __init__(self, names: set[str], base_path: Path): - self._names = names - self._base_path = base_path - - def all_transpiler_names(self) -> set[str]: - return self._names - - def transpilers_path(self) -> Path: - return self._base_path - - @pytest.fixture - def switch_repository(self, monkeypatch, tmp_path) -> "_StubTranspilerRepository": - repository = self._StubTranspilerRepository({"switch"}, tmp_path / "repository") - - def _user_home(_: type[TranspilerRepository]) -> "TestSwitchInstallation._StubTranspilerRepository": - return repository - - monkeypatch.setattr(TranspilerRepository, "user_home", classmethod(_user_home)) - return repository - - def test_switch_install_uses_configured_resources(self, ws, switch_repository): + def test_switch_install_uses_configured_resources(self, ws): prompts = MockPrompts({}) recon_deployment = create_autospec(ReconDeployment) switch_deployment = create_autospec(SwitchDeployment) @@ -292,11 +269,10 @@ def test_switch_install_uses_configured_resources(self, ws, switch_repository): ws_installation.install(config) switch_deployment.install.assert_called_once() - args, _kwargs = switch_deployment.install.call_args - assert isinstance(args[0], Path) - assert args[1] is switch_resources + args, _ = switch_deployment.install.call_args + assert args[0] is switch_resources - def test_switch_install_missing_resources_logs_error(self, ws, switch_repository, caplog): + def test_switch_install_missing_resources_logs_error(self, ws, caplog): prompts = MockPrompts({}) recon_deployment = create_autospec(ReconDeployment) switch_deployment = create_autospec(SwitchDeployment) From bacd5f6613af2730b5ff2e5b85a076110b18313b Mon Sep 17 00:00:00 2001 From: Hiroyuki Nakazato Date: Thu, 9 Oct 2025 17:55:02 +0900 Subject: [PATCH 04/17] fix: update error messages to include 'true' flag for install-transpile command --- src/databricks/labs/lakebridge/transpiler/switch_runner.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/databricks/labs/lakebridge/transpiler/switch_runner.py b/src/databricks/labs/lakebridge/transpiler/switch_runner.py index 88ebc62663..b6afdbb673 100644 --- a/src/databricks/labs/lakebridge/transpiler/switch_runner.py +++ b/src/databricks/labs/lakebridge/transpiler/switch_runner.py @@ -26,7 +26,7 @@ def get_resources(self) -> dict[str, str]: if not resources or not all(k in resources for k in ("catalog", "schema", "volume")): raise SystemExit( "Switch resources not configured. " - "Please run 'databricks labs lakebridge install-transpile --include-llm-transpiler' first." + "Please run 'databricks labs lakebridge install-transpile --include-llm-transpiler true' first." ) return { @@ -43,7 +43,7 @@ def get_job_id(self) -> int: raise SystemExit( "Switch Job ID not found. " - "Please run 'databricks labs lakebridge install-transpile --include-llm-transpiler' first." + "Please run 'databricks labs lakebridge install-transpile --include-llm-transpiler true' first." ) From 81c32e531c4a1d260dabb5b4c9ba83b4eba07273 Mon Sep 17 00:00:00 2001 From: Hiroyuki Nakazato Date: Fri, 10 Oct 2025 17:01:15 +0900 Subject: [PATCH 05/17] fix: exclude wait_for_completion from Switch job parameters The wait_for_completion option is intended for local CLI execution only and should not be included in Databricks job parameters. This change filters it out when building job parameter definitions. Changes: - Add excluded_options set to filter local-only options - Skip wait_for_completion when converting config.yml options - Add test using FriendOfSwitchDeployment pattern to verify exclusion --- .../labs/lakebridge/deployment/switch.py | 8 +++ tests/unit/deployment/test_switch.py | 55 ++++++++++++++++++- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/databricks/labs/lakebridge/deployment/switch.py b/src/databricks/labs/lakebridge/deployment/switch.py index 1a47bc2447..63aae1ff0f 100644 --- a/src/databricks/labs/lakebridge/deployment/switch.py +++ b/src/databricks/labs/lakebridge/deployment/switch.py @@ -192,9 +192,17 @@ def _get_switch_job_parameters(self) -> list[JobParameterDefinition]: "builtin_prompt": "", } + # Options to exclude from job parameters (local execution only) + excluded_options = {"wait_for_completion"} + # Then add parameters from config.yml for option in config.options.get("all", []): flag = option.flag + + # Skip local execution-only options + if flag in excluded_options: + continue + default = option.default or "" # Convert special values diff --git a/tests/unit/deployment/test_switch.py b/tests/unit/deployment/test_switch.py index 08020ded92..c0fac06562 100644 --- a/tests/unit/deployment/test_switch.py +++ b/tests/unit/deployment/test_switch.py @@ -5,12 +5,20 @@ from databricks.labs.blueprint.installation import Installation from databricks.labs.blueprint.installer import InstallState from databricks.labs.blueprint.wheels import ProductInfo -from databricks.labs.lakebridge.config import SwitchResourcesConfig +from databricks.labs.lakebridge.config import LSPConfigOptionV1, LSPPromptMethod, SwitchResourcesConfig from databricks.labs.lakebridge.deployment.job import JobDeployment from databricks.labs.lakebridge.deployment.switch import SwitchDeployment from databricks.labs.lakebridge.transpiler.repository import TranspilerRepository from databricks.sdk import WorkspaceClient from databricks.sdk.errors import NotFound +from databricks.sdk.service.jobs import JobParameterDefinition + + +class FriendOfSwitchDeployment(SwitchDeployment): + """A friend class to access protected members for testing purposes.""" + + def get_switch_job_parameters(self) -> list[JobParameterDefinition]: + return self._get_switch_job_parameters() @pytest.fixture() @@ -109,3 +117,48 @@ def test_get_configured_resources_returns_mapping(switch_deployment, install_sta def test_get_configured_resources_none_when_absent(switch_deployment, install_state): install_state.switch_resources = {} assert switch_deployment.get_configured_resources() is None + + +def test_get_switch_job_parameters_excludes_wait_for_completion(): + config_options = { + "all": [ + LSPConfigOptionV1( + flag="wait_for_completion", + method=LSPPromptMethod.CONFIRM, + prompt="Wait for completion?", + default="true", + ), + LSPConfigOptionV1( + flag="some_other_option", + method=LSPPromptMethod.QUESTION, + prompt="Enter value", + default="default_value", + ), + ] + } + + mock_config = Mock() + mock_config.options = config_options + + mock_repository = create_autospec(TranspilerRepository) + mock_repository.all_transpiler_configs.return_value = {"switch": mock_config} + + ws = create_autospec(WorkspaceClient) + installation = create_autospec(Installation) + state = create_autospec(InstallState) + state.jobs = {} + product_info = create_autospec(ProductInfo) + job_deployer = create_autospec(JobDeployment) + + deployment = FriendOfSwitchDeployment(ws, installation, state, product_info, job_deployer, mock_repository) + + job_params = deployment.get_switch_job_parameters() + param_names = {param.name for param in job_params} + + assert "wait_for_completion" not in param_names + assert "some_other_option" in param_names + assert "input_dir" in param_names + assert "output_dir" in param_names + assert "result_catalog" in param_names + assert "result_schema" in param_names + assert "builtin_prompt" in param_names From 13bcc155bd0c9c20ffd28f0cd6e6a6b7dd738a1e Mon Sep 17 00:00:00 2001 From: Hiroyuki Nakazato Date: Fri, 10 Oct 2025 17:05:43 +0900 Subject: [PATCH 06/17] chore: update Switch wheel with wait_for_completion fix --- ...ricks_switch_plugin-0.1.2-py3-none-any.whl | Bin 116809 -> 116597 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/tests/resources/transpiler_configs/switch/wheel/databricks_switch_plugin-0.1.2-py3-none-any.whl b/tests/resources/transpiler_configs/switch/wheel/databricks_switch_plugin-0.1.2-py3-none-any.whl index 81655cea01108fda41055ed87bccb05c16f14b76..90de13f372d157e02342efb12450fc3d377184f3 100644 GIT binary patch delta 35224 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

^A_L^PE(Ls%h#LID{?qtaE z|E6MsmaubenD$+i^aTwd;tdUM<&uRe91+G0W|er}4PezVY3>>`G2XGp!X>U0^1$Ai ze|qA;2RF@Prai>d{;8n*BfM6IP481#1_$`C7>fg8JwHQsC~D9N-J_)>^*gDZYf|D9SIS*nPNTbBj2ugsM&|W=FFVl zXmSR`zr1rRo1i~eo)vzwdn9)8jsR+dj*=R~z}|7bjLqpp*dY>KAG;cF9VnPjEaO*< z7vZ6qgmZx7u6(rp`i?n{HIw+3t^05Kn6-A#$V7!iGNPYR#XU682>7({ylX8hdO$E3 zIeRc@(g%fVJ9nnfnI01z1{!_C+Tp&_F5!es7_x{PO<-=B!MQ;ZS(#Dy9*K7&w%xcf7`0Y@yC62-dr6A^{5UX9p(s?7okNX&!t4bxQ$^(-_Okq9X#5pRkY zEhvBF+e#w~+he~MN&2g;$QU2|2|qx zhn)$$W0aSA3aIALx9VM*zK2RgnqTi2KN9Co09YqFI!!udC}i$PKxd z`a=n4<;F>y(iYJ%pB~8cPAJd~OuyHTwd7VE$}YP`mk@I8W+i990n+bxb+~pAg*xy+ zm{>$#QyGe*)qNg3TqIsc_jA`eYn~zuI5vBEC*^r=HqZ#4W!3oBL{~K^jVR~6IjF8N z&6kd0dEe}PH~jZT8|Ko#aFSqE33Xu27AnWRrB(9G22&Y1C-V2`95ydod)`}RKEGhm z$m@p%v~_%XyOfvZ0d~uiyN#BNHl}`le@~qH}X4J5+dd#@Gtlm7%D7dsakekn3&l>nyDng)YG$0z*BMXs~{W&~M z_>VXa_!0xtGq8k@Va8=rMI3Lbh16zXP#(0fG0J!m#Tl%>fbr`Nu#esfZf?{+0jDfU z`3)3ST88}w@u11Zdk^wmM@H|v?n3^{e0YRzYefGDh-~gjb6|HaT#@>;9Cz3^JCJ?G zaGHkIyE#}}Up8yYG7c3I_bix1%_Yco^u)1bkDNk$rYxzThL>-`eD4{=PyO8TW6D`&4k47f~3WO-JSr4^NvnsSLX0aDd)=JXqh~&n)fRR>QL)bD7y< z$a3g^q;x%vi4wljDGCHJba>~Zc48+gWwB?zGbQKI0bH$UQ_TI4;Tle=f(f1sa zldkm<0L5DbA}%ZXsjxG4*!+UwN(du42#s7rsHsX=UfI)58tD3rbHv{9l&#Fp1ZJ1Y z0iHoV)xOcm;LZjQ4mzMJE=&}M`4Q{h!ErFPzBgpB?|m3DMj*M_An`fd1g}W-zhT4k z&BBY0l)ui4@wLm-Iu!TVAIbF3!h_b^Qpx9a0ozA#Ss1+JQw1c030|iAzScG9+ z0N0=wj&*RQBC41gh+xpac=?8!?Z_q1Bv=xz1zBeTpbMV< z%3GJZw%9vO2xGhFfJPwWRiT;aj^2*|pcu}cH;t;c-bdx*MD+IIkH}j!%R2PsC)1T@ z?ou5&x|)nBpfz|pk?nBaFgdbcmgmVxKOQ&LVmbzeOanBL#MR~M?X)~TWC6ov2IYBM zjA!hvDlSc6F?bk@@R5%A{sH&{OZ+oCWqoHx@G$KDf|{az6R^*9huTSA`8*f^4UBAv z5n0bVXT$6x2zc98ehu`Cmwh8bNWTRf@*8v>gnkDO9F%_PzKgHtFTQWm%hw)n_|&b^ zR1Xh57$>Z7BnA(5aW~6xfs1_ z-Y;yc!v%iBz`UUWW@t2T6*u1QP9IQQ%I;xQ%g*lWmJGYefD%}%g$1vcF~gzKn%+Hs zO9X3qz1%&GOwmb>$inkj)~h8VT;|7Xupih)ZvXT}MOl%Aj*uv+UP)a6Dspt+3A-Ek zVkm-@z~NG2vLVFGJ*@E0feBf4*{m%k1QASF_IPol+cGoPdc1JSQ-2CdJs%U&1#!|- z=*70KlDZqiZbZzSu*W;ItKh%Ve5Xt4JzQR&x9?l!K-i19@pg-0t|E@lcnf6n!U$Dm z)!C>e2vIHt;^29owZ#a`V^S?LR6#k;#naU&93V=W?|nv@!Q^( z22plD&ETi=Li|CS-eO==Hsw>@$>8KZ8HO0?qf9m=^74rB4l7pFiUU$4Sy!3?ygpR4 z>%gP51<0K5Hqul!-5sY`2m$s3rKt;&u5El>UF#<=J>L6a!mSZtM(QHtrpN9v+21@3 z@)k3g0#hHHT}V1CS6J;M5JuvwV*ia*e2<7#no&la<+sD5KSpgw7$x(nv*NrX7D%#H zX@mx~)<|K}J85|Db<{;`U(F+GX6b){XDTs6C;hF<4bH(a21$5~b>%-M@TU_7C(0>)nnX<5Mf z{L6O8+x{MjzmWStAjOO}6wXR3#aRZK7xZ}>KkeCgGG}o<^f&c2|T^pML!D@$WO*5_2H;s!^`O~k?$ zBcMMbyZ7K4)w}S@2KzJC;>P%Z7D>rWX2t&jE`6%LpAtbz-vBRmg(J5w^Zn`m*!)qB z)sdLmKY$5XL`7%FlYis|rJEJK>}#nSFZ?kqc&msPlENTHt+{=n)V~nxt|v5*(cS8s2dYRb5*8{VS5KUtfGA%M2X6&qT2bcBy0_{_g69YG%lNQx^Rsm)(8rub7MAp9O+X~g_F0fD;bh3SV6M8 zJTPXwC!le~;-4qi%X3qdN5XtmA<5|EW+%VGdW1k*N$Vd++0#*oQr4p3-@y?PoRh55 zwzU9Q>hqIQ7=y>1@&cT13#0+6D z+2Oq+$m&Gp;)7H)AJ3H~g3uyVB`#QsX-+tsCJ1kC9}gCp76mR^DmWP}k5bV^+}(Xk zWkZ=Yf)00S|JfwEm(8|HhDZH)gp1(#3SI_Kh6{pHMQvXxhP6Vu3pP_{%z!m4>v(2U zV-Z{vna#ak{yPiN0_5=qPGc;N3SCMK5-K%h~N-bAk*O>I|PpR<^#SITq93^kuia@nZtQ z(XV}E%72zWH)?gg1Rmc}rxMlce|zi?m{H}IE&?k6$3T67d|vxqWgka>xDh+pU?{gs?hZsx@|zzA*ZDs=3FWY~Ma3n6zd zzYr)|0UJ2+%HDW9HOyX7nNMBB!Bzp17Kkcvb-b6HTPwRPJg1vAUjLoruK%GiI?=J( z-&rQ~LjAkZtAtm85!xWemU=V@4RH(!4WEG&7#`df9;Xb?@&HBWeNdx)%zrvW$V8Yj zJ&0q#+AD*5>cuTPF4L5z1$_H`D&x)kB_Ha}G&8OUy?9f1A%*`{qT=GHX}k$!HEzd# zXX_Y_icpVWD@0G~>-wS|cC}eg9V+9RZYpy;?^>N8Nnvfz$Z2Z_fr2{G>f+?Fue6~OKxGq&W9pzth91`0xvl)>6SNftW`YwCOj`E#plsGy|6!L`UFgCk?(_G`{~ z!176%!9AFvRbve4)w%fDCd>hM4;PjQ1%j!#wk^6b`9-_qrYtDcp{qy}-XY7RqsZ{n z*_J}@i4vBEqO?2di%#MDakD}uL&%ZU2^zCA98}uqR)r~&s4vN@cIl-;?if;C z)?DmY^+E;x(^?DU;jh&a;Ri6JA{7Zte|g1`mlDR$l=?J6i&9kesRRN2!#vdEb!mgj z@m!XY%3|dli+T=d=LCJTEF}dRcGbjQ`^O<(Z=`1XAW(s~dX-)9Mq>>OIF+g;?D7Bf z;h;r)h_u=C+r>n)BH`Z*^QVwwg<%>0*{5KLHsA<%T*XMq92Sk1)>Il=+Y~j)aN(+p ziYIHMUF%9T^qr~eI>x08N*we{pi_9H6(&3~^CLBqmn`tI z{vm$%3%`z6wB?HrC%eQ*U(B^+-$M}g=ZdREw6*!1DK6VpPNY_G4N;kRKb7;-#pN1erLwY2k)5lz)nrLUl3O@7zontQsI4XghS$}9tSmsJ}8J*h7AJmc~Rd4Vk~I2^1D+85fjV$>!-!uwrnn*UEvN=jy! zcCqH>Gd@Jq=}wNG9xUW)LZtZkP~%NLx{rZ{Vj_amA$kdDg zRW~>4!q5$aQ@+#Vq6p8Iy!(rt_fzGy{SxmP?&)vTDFgts)Wcwgj>GR{94CScwJ-GX zZ8pN7_6wzOwS#^vE#KZ{Ka$E*{)GSGApNp~0DGwaZz=(cx}~VTEJenIBtgGm`H5Rm zxsmdA-Bsun4q*?!N1|@l=kiG7L}i(mv_|unaZI~^AFlMlN6>Ypq(I<>464*r^rTA_ z6F?U6QyRduK#lJF%|msYSL_%(OFcs$Ic=2`*D;uw7~a8b7}ZL)G&YlxXrmJyS5$fQ zFH2=xgf?sbR2mU=JP5Bsh@&$46=tB#I%q}l&jj(hAA2sz7sVCg2-DKZhDdC?+BVCm z6qqI1iXAeVO%FmyiX*GcUwJCtP&KnhHhK3jz69VKvosC{qDoDb_@-YTqtzKjxV=A^ zLn7@cWL4{L5m(RU)!Rl9(kF$WM~(VYap)PtsV4kaR0_s1EuOYCk9ozq3xe(;xAiqqEYW7(C5zNSNu$0H%u-pkX=GuFOC%kN9#j19Y( zSW%$2w355>KHv)sCI|R2Amd|(auVD~UjJns^>1Y6;SXM!l(RL8g-R;E;R4~ z2@M(Y5w6mr05RBCy@7v*0NL&?1DxiMeGkW4>dyEpqn1r}`nm%$X<}JO5ebO!6F>~a z1k@e@$T!5M)41EgZAuw{`p{6zonMrA-b=K(`BC#(Z}TA{hO+pTu+!JwU|8h~9A1I6 zn<=p%7uGYO=H!S=^ZUNV@CM_AJaK5?-@Yh<2R7YkktxpG;J=PSFB#|bdrHX zNg;5KUrqGLpz=_(%H{XcwbFk)!y>?g4rsBMPDDB4u;~mzurgcVI}DTy#OS!3Fz;ye z-m34=&m{QlZsIkx2z~xVVFlkqHjIkif|1KaF_s-v7(?-DYA9V{$KV*@1P)`M6r0v& zFZE{pO}j$X{Uevb1_UHH?Uq&8*#!QwVO)fjv%eXZ`a>U}rbNSdDWCA6gyIMw*2y2o zt0)P6dKMI&99i%ZG@iFyN%E>Q_ZZx)H}TAYIT#VT5kQp;@Hnno@h?oZRWQmOVSl5P z1pf2lpKv{SBRK}43T)2N2P9M{nriaOyi|TqCakzYcD7c8d+*y>^J-VyrYr_3oNFNl z6>N|n>Q%9xA{N#oP^J1RGqfFJ!Frf-3^(;UcnGm+36+r8>H`J?-RPDh= z6O48`dj%a=J5vsk{wN>4+GE=<@9JD}h}cNZeVEL8Z>~zjG3hkv2Vx~d6(c;yp*Z{W z?9;RJ5TTTe&=Vd}2U$dN?CJZ*pOziH&~)LuXUJ@dgEFFAuE*?5%Kv&HCyL{ymKqGY zR<8K48WOJE5N?27)p_$=+~H7_Sa)+$95AF622A+cI zTR6`fBRN@Yp4&`_K_N?V%MXYHm%j?bnY6HUcuN;Je8(P1+_qO5X>eImyfwEk77eLG z9jzWz6-@lmD|~Zlt~OlO(V50CO&HN%8FA-6_PtEPtT*Mtz`p7E4Lahm4g|T{%9kf zqZ5;lbWBHB?D_3KHqW8zrWbiMp2=p2<^NqaEd9{i-zNDK_`mUnZIzED3!{7{@*y^)qes)~fW@;YD-*bUC4-ZUy_t8-MBW$IRE96=o|wpLt@m&lqmt^%tX?VD^i493fuxhjpTApVVw zF6-8!86^4iQ<;}7bnvH8!>rV9EGrUC@_g@|$o( zU`hIt?SO)d-evlF5SqgIf^18J+N7a>%9)>WWWnrcja_ILp=R)Pix$z3!kN2D$t8{g zYAX}6&WraJZG^vP>cGdx!_(vW1c!iGEyMQb`w&k&Nc$_~N#3Ap3x|GUz7nLQHr0aa z!}6$NKz3$xT!E@v*PsbL3%oshl)KRHf}hNv!N30CR{AGB9t%iH3-dso)R1mJb18Ja?sfD8MkqeYO0E6wD}0FhV`x~Qi3y=@6qQM-7O zj{uKss*KJz*_L1_Ol2@-hzcg*?>|FQj5P1Fs?LL7K;xXk{riZ)RGc;N$YDdJQlF=ufOaPweTEpaeD3XJ)rhzWuuP*MFzR&!BRh^hGL3hf*jo_bgdPfD_VThZB!C zqe+r`uG0DW5ce)wcAh^YCEi9wa&Cja_^xhL{KJ-lRSHv7}|xz~){m(71GS?2#)bQJ{4XhIPwcV!uzzl^w%S)|@_=e~`v z*1#YihJD-r(I#m>*A+(LWR*~04Y|5Ir940Ox#k-VWM zcA!h#2TUVXRBxgU;*g();ORK>Dc5+O@vC*}lF9y|9Ry6+sNVR*Q5FsEsqvDk%w%bs z>zv(|$<2*9ae+4E+_z6za?2c34Z2q93I0f@TM3_-^-L@ik4mE8i51BvC+H3<4BeTZ z7iqN2H3POt>!7r*3a*YSK>1;Xjj=<`WbeH;V&?gm(BYR-<$LzHmwXffcAb6Id$Nj? z_i3(WgMk)*lPA?pih0``vYRa$zy+}cZfyO2b zu0@e(_M_1*Z@-;n%>bXmf8Y8m6T7gkB;toDBg=kzh6B~8L`Q=&b<%W1u&X{()ZK5D z^D9^^>VNdnD%IopKva*40oPIOM@JA2cVYC1d4`(g680NB^~6N@R@~QY66m#&=09B$ zi>>)Q+L1X?j4SrS!bQm)^dRW!sYea{dpP-ua{=K)wencxKX6ecm6gFvIDdn)KO)ZB zZuPpN-lbH;HX5zZPEyG?HK1I4P*}26_fX$*Z7}ae|7V(K_#;wFM z|NI~AWKM?H0lJ1B;BE5;!r*g~3?)1QW@drpt`2Pvj1Qv@8@6!11w)ZK^5R*HF|0i7 zRO6UfP&Q>}3@|g#h=%yFs$}=utLoei zx60pEMQaQnU|^)@O#5iD4v8uEY&4*8eTg7U@lWGt_-Gp#nBBv@{kXWeIjQcF%VF3E zy&pASz!?V1i25d01zmx!fOzZSF0L7g)exNO6R|Zl0mC*RCuPRj)t12OMS??=8mQg9q~}=?2Jj4q%t@Q*r9*qbKy@9>ke*g@LVYXIGP)_$`1Tm`@sH{b|KxC3J{9# z9q+v^CVdZH&kBBth=Yy%_Jp>_=J67wH8Ob_>3R$}Bd|7$P9x}7!rT7k(XA@ZqZ>B)nlk20!=PnUhu7Z4GLP~8AD$hhdBke{6nYB!M6{@+v z97tKKgiLibHh*>IYJip@Kl(x;&l=c|Qj;SDl=RTsvpSmc)gd}$o7u5Q3o1w`#`q-l z{>b<_?*xQfl2D96;f=D-hJ4~H^15xiQ(MU-{C{=^SpOw5Gx8SY zbDBZLJ;DY4ic9ln+pz0aObF|Qw$lgxUyxE5 z1L&{=@$w#r(O9^RY`HM~FR~E)7QCa=-8rrSb6|-bN^seHLbfWrBDnM5g#;IKO2+ja z`%snN@tB|wDmoCUyzen(tZbLw9ev`LfSnDNUjMwhx%iDKcdu(*&(7;be=gw2_2)MM zT%ca*BkG8RikpZP$ee0$2)*KJ;F!KyoQs`>k`2>TSU7BCfNdL*-+>JtZy8wi-*jwLw@Zw&0Sa5Ew77stBW?)0gd7Xipe&~Qp_`E_2kR2hF zDe)Xh*e;LT^dpx23=gl^c|#&9$7AKx&k*~!@sF(r92$Pv8dnh&6ZacL0tijxVYc$X z{S*x}>w2NT#-JsKkh0oPbV%=xT4$%-u6}uNLASY?Zx#&fam-aj_%o$+qIezU9WLeX z@>nab5S&&>P)2~Nx${p(wl~Ni0RJPBwLTCR_Q+BgugbUJwNK$>g?&YEOGN;0V}JF2 zgA4D-$rM34*gtXy?|nvtC7C7vcYKI6m9eyF&ne-lPK{YEiOIw9_L>Ai#R`9=P@n{KljDbl8(Bk2gm0CVIha6!)(ZW03mICY>heu zOttK_dE5?JXne~SR4f|jV_RFb;QmTWL)HDOxp%72#n@%+2lW&M7xkN86}t>vWebLz z3>+sZkB`DoPv#zl!m@jVhPWsWl1Th1T@I^ergvsUt6eJ?RpDYsC==2Bt3+awSuCI3obe@Dct>9sp#L2ZNP=fvIp1v(PlK>~c|K|F@C(B^g zcFS4CF2jH!3n3!}>mn+!#NwfYlDZ9^Hkiv0o8yjAEdx_i9DKmyk&G&%U2&+mT12(8 zrQ{@G#^Z#6@*w{UU^PNdy{{YKX?*w>N!vA8Y*(IxTqF&59nX!EHj(!hHpcq|+N*TCKezbATV+-LQFfZ=uQ}zpws;T`3xmeu3j{&@FWwC3BOa~da^s;EH8KW@>g$Pm1MUWbCu)*+lm7Ag1F)h zA<$x!2I-+|hV8!?>lKp=H8w^0{$AcWm5!;C$M>tW0~Q4-rn!~T@}fFyVt{wA)_Xsm zCEhAu57$E6S6+)LyOSJZ!|3H`g+6CiFnKOEo?X5G_H?^smGMu%5N>M|@BG?KptCTN_9ggGS0n!{^#n~pNcq9|| zcakNX7N^+i%qp=ztmny@6GEx&+RZB(QzNE`4|;zPRyC>Dmam+<6cJCSOfIw4XB(gb zjgRD}T5$s|^b^@Wji}d^sC)VS=8p)oVn=5Tb?T#cJ9&bn^i~vq8t==5Qjb&SnJ`k5 zDb1)`A5*Y?1)T240$|_7(vJ(6BQZ5Py*556Tb;vtxX?5ytDC`3O@}FOnXPRw_msNNv0k2}Fh+7?k<hlKTTPD7$_H1w?)iKw?!FK#zu=92^$zLhl~-yPB=L&6$(B#W z?{cSVt*CiK2<~d`tv^7l2r2R*A_m0X-+X>32`HG1sD~00W+D=h(bt)O?X0_JF~lN_ zh>s(uy&(ns8OoRuy3#?>+ygHnb>P$}Vj*cmANA6J1q6AZdEilK9kAh6+e+?~qrKTK zs6~XarrwoNnqqT#yF-3oIsB>5MwkO42i~Bnr#tkn_2klM_cW8H`j$iPXC#UZj8AZ~ z|JF{rET)`Von12wWm-2SBT@&gX_K^IM8(cYNJX ztFoQ!0W5f80Z&p|I4bTG*?VrCLKG^t92jRu)Xpn;T;+0?)+Jm~kF8Yl6ImS|)PsLf zigAPas1-J36v-{+CLnj{sU!AhTq|W%vYLIwtVOhpifQv!`}F^a>yk)qJDEdC2rQx3 z6rZ{6AQK!3pbXR4upVb%o)nU1s|ShwR37&@0oLe9k4peZZ&UPH6v)(^3C^epQXH)l zq+DT3P&70!X3^CToc6$?fvNb2Jw`aS{ekp1-hW0sqo>=??qsZ%T7Ii)r+;8V0_JlI z(I%TXwH-umI(puY$O-xx39+gOUzKMeJvs^ zdKTCF15^0^M-RtKk>4rXTb{eyhlmVNXAXw3d=4BbAYgR7 z2GUaThu}JIQg2AGwF&y_BSggFUFR4v)n@|N3a&fCI7YwhC zVEw0MB>_7O>^*U+k8w!ZDiwzH0R~R)lzx3tX1q$SPN!zs^(j0Y%s{E&opQniUoWRBU6ARX98i*>hj-uUT;+U^Ko{oYn{M0W23Rr4^?!vc(W7@A zL5mLqv|^wfi-|Et$@sdvzaYyK7|IaTP+2n+)zLx?upru1XG5)6zZ=)Xypq`FlcT<6 zYi#Xy;QLEUOZ&}!AVDmITK2*_r7lpIq)>YNqWQ%QB}yQ4jR(Qu+M!?saI>jScL?@0 zR$REAHeJo6XyWMeqbegJZ3BYf7oAEt5cJuI&ye%ex4L+j4*HV64QmY)Rg&XuC5qQC z{sxJNdz0(bUi!Je7i_o)cx|gR){0o!rs^-lMmdEE&J+vYBhJMdB&p%ErdVh}v;Mt- zQfHnc#~9~Xx^%&nTOnTpPG@RR$obUSPD%;>LaL&VHW8!zvL9-kR8Yk5w-vpel4w(8 zkhQ>N75wQrOCnQ=YDyM4r~0tjq(1z4b1{zWeR0p8mO8DGO7e6zVHMrxk4x^r(_zbH z({;?1KVEE!5nKCDy`+;%7L01@r#AC$-}+dW7e6+3dDYK^pp}skz^LT~J~fS>Bl+Um zbp#g={YqKmd zktkq=_>qf_lJ1)gT>52kioZKJVE!v}KwS=yA}a`fx6W(gVX}>MY~{h=A7@-EUMY}B zwE*g3`-)kMOZ*{|G&bp4h#jdqm!o+v%H?+yes{OU_7=jsAug0WqiwMBwVyiQobr^l zzTd6-y*QNn6xJi#;>9@0Vu|&QTn7-21OIy&|8)q3<)1MVTrmG*i2IoTzp+Fxj0;HF zZLlMEU)N$;BUZX#U|ZP1m6`7lS@;#z7L~Fh8;3WnT8ai2C9IspUc*aB6b){2n%lAI z8w?R2^KHX7Z`$VGr}$$6^@!9=&KYhT@~&)Osh8P%usFoKiEc+=y3L33 zjjg}iNRrc~H`kI+aD34)Ut~iJvIb#6@Y&1-@grZZLQY*BDMyQA4 zOHU_4PF1D|=#bZK^bx=7TL1}v7Z(Qk@Bm{R{y1ELQl?#$wJJZ-t7_U*wL0Ac0w^5za~&=qNjE{kD*6eWb}FEI_h=luH124HmTyCUfMtMoeRo!y|CMa zJ9L)%qO4+>5;d47WXi{*Je9|^sabK5!vDTLWBe&xyR1&^dx!m^9RR!=DDHepo^_u7 ztk^D77b+Tfu?-Rt;z&~iFsR^8w*oVrF}H&`9TR<{)^Av&onjTI1-c>oW;$&atDN6T z2o(^v0w#WJyq5lF8bi3cT*sV`FcKRMa6NkYFCuJuFR6+QwvoB>%EB6HmLBf5|7OLL zP>%1?6%7^~xrZ`MumPwRTu9zD1LYvy=TYi0$;-IW8!U`J!Y!2%6iC*sUUHwg^>_!A zHCe|bpt+-oqJ=<}%ppN9Jdd4vP47wY47;nvckvOPd_G znRX4^@BY4hA1?*1(xgL!n^nQ?DC)Fpj0Y&e4Y(y!=>hJPvH-h(rxlav6nSOc^A@@P z=z+Nd@@-Y){pnupHCyc_;4Jl}dxk?>*( zY6B)`PAX|rtllJPq=b}kw%v^gp0IO)Tfc^>!6@voBXX04c2g##g=8@h!bv~f%@ovB=1#n) zF#BRS*cZk?wwN$WONs>iA@MVEqsk0=Ox>eTQ&jhO@6t1f z99a)v?Gg?N0}~gX<7@@~ts*j|G*Q^d$IW+(cusq@FLaX%j*tQN#IVkANCRsOb=Tl( z#I>S?azOFv2Yg2oH_)KhdmOq%=u5eX9}^8Cs+FJuMnC-K{gzyF4Z)?Cr$*34=SPFI z1=jbzGvz@;+%3K)3GY9Lijb!HyodbJ}NyrPqyYy2^t zj%fv#vge(G(K0&sM=fnffwEOMqb@_tr+%N_z59+xImi1i#{RtCBmkQB8{Ua7L_CqA6 z7H}YfAAq@C;$iQA6gtLwT@8S?kEc}{a3OI6%IsW!U4RhQR}(j~{1j$f>Qqo$G8_8y6rGD<2asuqPwXg^?(=ks51Eh59)Yg zP@vhPB6L)U}L>(A+L|S!QSEXG4 znsqK?o(jj4drpa!D920gT^#mo!%O^)E4bJk$QB@DAM@a3Kr##2*tK`Z9y8afdkG}s zO?1vQA^f8~^QR#yF^n?E_g|xMf?`9GgqSEJ9wIcjt9;@5pPB2PN?#MhI+_M>2>^se zmXx@R$h3k0=Y(8{D3;4_XmwYx9ezG+J9UfZ0gIK|kDhJN*l7ryYYFt`*c5|)=M2PH;% zke`Z})>@L(tc<=`0UtxH>LF2Tw<_X#bC>BcC? zFoX|v5F%rkDw1wF8gD+ow6*3z1oiM1gpVbqcbH zcngJyD+Q}CPDgAX3eQ;~ac|)j#1;3d+d;i@1SqcwxBti0H#X_Q1WS%>+c;y}wr$TD z+j_>fZQJ&ov2EM7_Pw!R_U=FEh>ot#s?5st_xLN+8=}PO{XN^JuWJNMWA1&xK@P-Q zFR~b2AhXejbIVqw_qdFgk&qRAImUw_CuOVDQkk|QQ<5@m&1W7SLwWN%ab-4egWaw> zOT&$t?ap;AfWof~kQgLxmS-bAKD#B0hA)GkiL**gUstDIoSexI4)hIJ9BceA%YGx5p8euAFHw5b<-uK&9iKl6uKM9rs6lF0$I*srmwrZwy5{XjDPt%FEWP!g% zEdOUNf6UkrprUEbkHWew7hTc}g^n0J&RHYv$w9&JyF;)nbzCb2nSy!mT-62)N=hE& z*48iC`i&ZiWxC}(VTmlHecw5tXs&!%m;jz)HqbRjF^O$Kj>{D5H-u$@!?7-@nO+Y< zeYGNwXYP)wlL8*~ss3Z#uJt*+L@;SDcWh+4S)eWg=wvaK{BxEWXIHU zRHL!iLP+CbP!2;qfktM2w5`Xd>GYyeG0QVUBoRohT9CmSAyk9S<%h+Q%6eL!nd3qO zba(_ish1p4GP>qA!qWKD?=%%riD)Ez3bu6}KUvg*ff2xf#|ENSUE@lfgyt|M-bGw^ zIz}J{$S5xMP)BdBctAwRP7>;C<<7kbiZ<>p1|3c0Vqbq+LY^DdK~@=EaoM!ZmM*Og zqj0DUDJ$UNb1GtVhGeAkcVyXA+@x)Tn2k0cc=^12+8W#DWIUXe>F|DUJ3VxaSG3MQ z%>5HHiN?27X)IdeX>Ha9H#2sorow)y=*g`JfLRXp;h|udaxMCnqH-_C=EUDvYl_%R zy}+lvi!H(Qlqa6c4OZVquRM(G=tp5k6)Gm`6mMqgYI<{o;^mpXjlewQE$cS^Q8zmI z@{6ptVlk(7dil2&x3E7`&2oV0f}6fx`e#2j@I9ikeBT~~?uzo`i*y-3AE}F3F7Cq# zAeU4rRlhND!E$2~T^ppfm4&kFzU5l*+Uk6gudvCcz3iR-i0P5jAgbe>a;~sVd{@L> ztT%45+{L=QYxPFc-}z|i;vq#pmMae3Y)K8T?ZnHinDXZumhG@JClFtU6W0c&vMRE7 z;$RsH*fnQm;X3ENp))P8*jttI+9SA#10^JGD=a$zs>bfrS=BmPX(zG{vTC_0^H&>> zEPDa#X2w(6Y>3_&_Hk}fW-jw55{)G>0w@Q`u(iKH{@Hp)>zM#Z`GpjUd0bw;KC$w2b_Y3%xVt=NsCp+^nsYZOJNgcCE8LXtc?F(L zTV2KENINq7KCksE4@`Ws1;C8}4tc+;NB`~Gvt#y!Jg!E)XF0TJ{yw-hweqbwh_)b? z*OrMX#jwtF%!-tC^7Srqs3PzmJ)PVmT-l4T35BFLX-~3-OD<{a)~xR9s)p?QD0<60NCJ1euSnE+FzG@6-|9a*#y=hCR#-tOTy-b9yoJMiRF19$ixS^XIBwD0~Xwl$$iyK`8gbE2D`uY_p%g5W~Z17uvd+Nt0LDMBDuIu!wAn8CVg8(@q~x zT{|3|yL{ZAy>x_VYxxgU49kcL${m_hQ{1rVwlTML0o1n79Ts(JSRth8G zKnQkC8YdeMZqbKtx#?M$Wu_?xXvqT!lsAGf96xMWjmEhSjt4Ex46YnOBGs!fD2s$R z=vW~Rat9Idfiz}K&{eNW)3oVJsGDzM+4=b+cBJkw_@2EC{$$nYP|A!YyDC^*W83YY zcQAaCM5AQM7HU=^ozQjnwB1nOSvN{*t`eYn=dmnY%p}e+6KG&$klEh>QW~apUT5j# zVJdrn!k6PQ7LnwEG%7%hgDiDradhIm&Gw1ro{3R{K&*0eT^iuwoVfSdmtBLr%Qa0tbm1udK9` zb=SgTg*uNoD93bPA@T|@q?@@#aVHY)^`D`kGhF^np!$bdDxGAuhn>Zj2qHd39a(X7 zgIux_CgvO!c5x-|WMCee7kJObac2Bj!=uIFnYnXz2Br`TTzgi4pVh5nJwS#s`bQua-r8w#$%{k zjM*=5rpvPePJje}s>>=PtcF=kzS@>K3_EiBZonqt1bqjOHVRh}=&_>AWXC~)c^*>@ zZjYjL^Fj}Is6b}`JN}IeS7US~ua{nQUwLf)MOeDWv(PxDN(lkZE+OhXX7C4G+iyV8 zA^bJaEF!U92ociD0yaRE!`=p!oGp`6VjY=1R21X%EFu#?MY5HfEuY`{PYjLmQp1*g z3E{Z#TvF6Qh7&^^0~;jP9B{dgXU-VK(hVsq_Z;m&N;9DLSafv44abN9LVU5*$hEi@bgs{&d*OK&BThVnWA85vz8>54=*D z6FXWaJ?UaSz^q?2>%q)p`6h3=?%4z_y(Io=R>>Dzc|4HIur^wXu>%uFs!`Nsu!0wd zspaWgP-!zDjaY;DNupct*C}bgAG|L0H?P0t%AZH)QaA-8} zep3OnB(b#!G$dTo{MvZq(eTW#0I$^bFPO&Jz`i7zl2FCIsBDQT`qlk|MLJcN-bdr`I9Vtg^CJ#TWuo)ySSj_anrpR1a8f zSLr+(uA657tIol~&696keZ(%CG_xm2z&WGoRV6r+WBL9E>S2q%MufjH>}(Wq-8Ld1}9n?`mP>Vfj;W zscvTvDu4C;0?A>mGHU^TLQc(&40BBq!p`0(wCmS~43nAPDuQL@a?l$wxn^b5V}GXj z#m=Flk?Gt=z>dQw^m;pdAA((f?JEm!*#kt8m_Vw?#47@S`>)L5E)GWFpJMslNd%wo66<|O>&+tk5Thag>`%P{*zu7vZ?l3aJtRBp@ zfd$rUugn##Z0ATjEV$tUT)Nm}QmX5P!ryO2Oq$k1{SW0RG^)g132ErSJ(o#l*C{7u zs9q{Gr7n;$6y;QwVGnw#3GfJl3~BWsA}H=0v{&S@HrzpWaLmZ)`%P)sN_A|VG|~21KIGpWKND??zJ)ln^(t>(+=3 z7{&&R$+J(g+_3v~2Y%ihz1Z494=;{ijGccR`#wH6p&Cc9&_@8RYLD*XNO~}t6=r%j ztKmn^Qy7fwI?%(agW_hpw_5HR51MUOgbHuMkiUS#R}r~46n>OTTh1=-yx=zYI6zog zg4^TRF!%JE$Y)+&O5s-&Q&~EL_LX16{qnK3gQatvgv)g&q6to6|2Zv zF|wY7c5r|#S)h?hdoPU{4?`FB?5KbrTnj5CxPrpc#L&6}E4DQgChK^=beR!rUwTYV zRv!ftT$ar|zAGnoq@%hKt_U(}Vg&)S%&Cl7mm z^muPm4qJl`1GcR8{GR)cl7vE$c=rVyyrgFu)M7}gZ`O^WAvzX(rK=~1AiUbT>|=jg zh!0OmL6$K$wQS|VwKz4WJP~Xj;QWJbL<zc+{xZ;^Gd!6LaC z4dtTUMh4F!8M5683+>yg!L9vAc@2>}4x z2tEx2KBwV@BcJ!N`PX?!VdE?L4XIgSSWy4s*ivp zmB3jKgHWH%UWF}l_kg}7x37Xa4uVDtJMKOe_6)x;X5wF+uQ>=&$3_d?6{^Yn4Oe<= zRJHDWlz7X+Rm!~QuOuHDl=uLBx+}n5+_vT_vo#rzP9~qAZu(>w-_J)a2XjV;FFWv~ zxJC9?2hI8S_hhs@vZ^;)`|OpzmXQ@BsUC<0UIHQWWV zk89FZI>=kGVKp`R>iP4zX%hf3xbv4~=%y+5u7rn3sY3!W@&WfW*cR-pTmS8)ozYn3 z9d6(&g7K@;U`9(^Kc+H7$`W(9`P!XG1KXma zi*2$1(#erisaaC9Wx_wMX?4g5E1aPv7pdVfPU%nr!`9^q2|V5{;XOjE1Q9?u{V0+* z8D!`j-zs)u0Yppt=N3(Jv^ycsdJ3ofh7_%xsY?v}$lE6aj~7qpVFah&oANjngqf<( zIq%M~-x=Q0*Q>6=EjU4~f_!P(V(y ziiC<3Cas&`^16`ISsfTs{VY)TE|ej8`)K)e9}jaq>KK;(hT1xxA}P;73ehHsUD3qp+#2 zs;#doOd(&U(=?KKdBG{$)p*cL{?&CP$(R+vtkk4}l`3?c$-w>9{fl(nlXu6HWF1o> zn}0oTPecMvhbvDNvI4-J4EBq^Z7xi*v%0#NCP{F8$wmI}&BIEobT~0xPgpYc=DYLP zL&04+6S{>5U&oh0LJbNpJEwoFq*%1u2k@$x2t6gdCK<1gSVTu=f5 z#KWc%1oq!q@0*6+eIF%{cdSFLj*Di|X(II!v47XGbDch(e>gzAVt&V->&eigH-lqV zZ@t=tW`HUL#*8Mu3FB{!ux4zfwzdl2h36s;n{OC28N@G6W<&pF`1*aHT^{VO4%!WH z&pgm4XzS290M_u2zE?{>dlHpg3nF7>ndify{ok)WtuJBY`D7uWqoNXm+Hy<~%3cTcR%1Ps&l{p2q z4K6-E7W|}{_I^{-?g|Yok7a&|ON%XCE)&s>16GD*KKlSjZKIq=%Me<;SSN5xv1m$I z$aaEUMyHpS7-cgorjvdp47RL{R*_P{B4(B>Fn#CcrO<#0Z6<`VD^ZJ`%15f zR#UeQGXeIICRtkHsv&B&qU>hvQx6!ht4sxPaEjZ!tK!C+u5Ro8^Wi#lX8VCwh6hfP zthmVj2bKUD30J5AnOb`_ui_v7Z=+&*DSkgln`e$ z5a+(6WRv|TXK83tPBL59OwbNI@Mp(IJn+!g#B6zV@neTtbg^(4!&3ZW=K9zSEg~Y5 zIdiATOo(p6wVR}4L6fu+9ua(xY6aQ0=ij9%+T*&EhgoS32X%OB>>z!U{ic6|b)%&hP1n@7$fg zbuRt!={y&s((U36l|Bw2F4y>t80;nGn(%fD%!N9xjJoejF>@?!gb>zr)nG>T2ID2+ zj>#QF-Ax(XnY20O!GBFIoF1wUsGj%N+w%lPP_=^>9Cbv3Kk3f^n&eioM|A*i+6|x` z0$2WN`?!c%uOA7_@cLOL-ynwqsJc-Rs22>%vy0U)M&g{gso+WiyBLYt_WF5k6xwjR z>EY^0;%H6!eVva;=lF!PJ#ano7m2x#rphE8nZe^m4`M-GpH`isj0W%pE7|pPnH57R z08@FJHq?a%=SecB*%PjoOJB`kunX{L)uy7=ILRmbB9rW3@`T+ngl}(bzxV5SIIP1p z(V%fB2y{OFi%PQhunZf&Pgu9RrK$Bk+f{{214!0jNM-2O_BPP;{x&~6CXAmt7BH~& zX@|dqCK-H^wz4}l*0WRJnS!Ex$%;;C_!^m5oQPVV2x^$<3A?nfyJmss;0J8!dOPD5 z?$(06Y2^NRewV2^=z_t-M%^E*#)U9J!K&O&9K&Bfu8$g= zlKm~sQ-w(zNQgmXNmu32R|C-hhL0b5DM9Yi2V+X;<#qDADfii{Nji?x(|h(@YK1+J z60i4I6>};$t=SFIFFN9Cs_}qP_;=HGMlDOla_ff4!6yfo0-J|&uT62Y+el#J^A)Q& z0+DZOo^dJ znRRs=9sZIa8h0R(P;@eSSX3T=Pb{g%j8ZWP}aYfRI*arStn&pgQ|5&O_KM}lUJ5==UGEkI8&?A{F_ zXq^Qa=m&Q=tmuj9C2Ne5>G>PzE+ht_Q0AjfSiA^MmXRe&l1+00~>MECA+WEC?=XNY{Q$5-e4h zx!^sW(_(7)F(4h0oCJh0Ga}(s=V)SFn^MDB-t-g`Wt_UGKP6^++t5=P#G4I!o@>oG z)a-yEI1`c29r4i`IDAG!KnM@Dj056c!OaZIU-aD)e9}eLfB`Kc*&qvYS*VX#igM(L zr}+aOUWSOL)h#OzyR>($Dxin&}$0Ey^njd^NV-d~Lq};3~3^ zP7s`uq%q2p%tG>dJU+XiA&2Il$Pi#F$2q;9fm!)#$lZlx=!yYZnk0Jqv({8ga;gx2 z*FAC%aBvV9$t5zgs5)jSZYP7oQ-UdqB1LcoYI(0@D?oj-b~*z)!5{ygQt6;C+>Npo z2uWn~EI&$R-J`WE{Y&7up9mZ;AS9|zyG}hQ7Te9EQGeFNh8_yEu>fG|Po#@nD$5#0 z>VPgB$MnJ?zAK@8pp1U|ohwBXw7pcmaFPqwdT%t6`EC_<+cXgb`FZ>e#4#6#zX14f ziVsB7Qozhmy(|LIlT3ZGMJ3fNV5;;2Bo7$A3rK||2stz0Tfo9qZ$ z^%xW_m~ImB63AdIvuUute%&-`peH4ksg!NUc!|cMc=&AO2gu~u5J_h7y#7E8O*zYn z*7VQ3FzO%knt{mq+qG8aYO0!i{1329r8yK?EPxzwhnO=lOGuWCCG~CXve4C?t3?#+ zyun;G2AeNe1{1~|a4HF}j4P=LMf>##$ToOUWf1D-mQ>!G^JkfQ{6@~M)brqF4NYBD z-9rnHNijQ*Ru7Mal?F!Z!{oBW?Y_+Qh^%d+pq2aI%0&+A5clOzaAjYnR#)d*ZWfI$ zM?i(!v6<_Iva1zw;%rMt$i+~{^fFT;TTeGzS2%Ou7s3Yp@{MfbMhVvsl(xo*qN8o4 zV%Mj$t)DnfWemfr9i8p;jBRf^rA|{)i_U7BIqV;etUNLJy9)MawBZvBoJxw8C=KEX z#p2J(KNjx%3hV~-8fgYe)2AkTBww1_J^&?vW=kImh)VstPobA>zz|`9?ty_GoejT1 z?Tp|Z-onQAe_pZqgj?oui*HIeEMu7Yyu8AAQTC z|NDq$#&hdpZTK@>QUyB3V(0|O)yYE9xqnG!g2YzrU<=zng5fuF3GW}=z1o}8Q1Gz% zXF$A3j5{dteW(|(6&$zLjkh8=iW*!X_M`gDhvy@(O*Q%%dqQLWg3Yl=0bmt!2g9I$ z2HlUlUn4(aEJFPX$l)`~aqAE~Q;kFc(}}`v*l7Qi4{`vH&9I#im=Dofz`d`c#Xvvn zb2k-y1l3D3NR_S^Nlj>vdJ?30)=qCPqYfBX4GX7d_1e{9VwXL9sz8w(P2FcAPAk$L zYk6CEe+fMjIUV`TMU7X*381i}YLjBxpqNRIKJ(*_5THFU+j>WhB-;bEaY}`Bm~~4_(P9M2Co3ZChxTWGx1Ajs z`o_OXjOC>rehNLE8lXQ@NTmQbxEw7jh_+&*O$qJcv_MFqXKAoA@R-}; zve(NLjnNji*=leAqth~nI{Omfa@D+VVEW?tKI8kv?>2`x)Frjj8&tzmyT*|l-AxMJ zKRB|%y;lk4D7zKR-wr8)uIH{b0VbDacDVB@$|c~s9k*sy%2aFvRK2~{!80E?@tVJm z{A*>UK7M=`sd$#jVezE(XKnQ7_KdQk8S{B#%X9*VhiQnkDB?9{q3}~w#o`~2&l%~Hk(2i zgeRKVDWI~_i%#GH)sNm(tP;3A$kA!V8MKDH$MtQH*aiS`T^95FZv%JB;>4mYB7r(y zn&kOYd6 zlEtnt)@i0&ebbq?m%eS@_SV4WhojOg`bvX{I&aJxkmD3l2pT+f$7y~?KLs49m%99C zZ9cur#6AF6FQA$bwQ6Mqq`-cZsDOsy0z6i7ynz`d`EF~^)CThrHPW9ZrI3_Iwk&Xq)ZWy40l{PhS`y{N(ymuIrR^qM#+Gc zxlKB=dYDZw@i1b3)+GZ(znJngd^e|afnm6#NfJ=q2&9MR{LB`Y4E0(t?SD4dO<5{;3FbGZ_wa(;HbAZiR;@vDocVZ zh6qlWq52Ed6!JU_2A_AogAn>33(AEG1SFW200YdLX3ztS2uRVi{hwTq{}EMr{2}vf zjJCs41iOpO*J%UM+GN`DPMbP; zY&`GM6Q6kn&~d!o#UDcqd9Y7>=Xfp_w{XpGH1}%IT(XfsTNE)UgrQK8=YbHfhg`AC zSvlDLY$I%p0uK;i+^cEIvoIBiWtEU4!SW)HWhyaHeQwoJn37HTfBDVC=9#~ZZIb>^nV%l zTjZuV(u1dXm(~eK8xt=%hfrvXT$h=j_7Rjjg_bXcFb#6+s4BHYPq-AOt+cogM6A!E z4AOTbIcb?1cq38aA`CEiS0Ylw-(tqYWF$P#7TYHG1`KScD?ENy384lNg-fc1Vi!XQ z{+mGoET7R~KsfO|Nm7`h*Nn4)L5p69H%i04n+9)l&;oa=SC{oWt0&o6xGs8!$e(c# zvC0d=P_^(48YbVpy_oH-HI`_aKn3hi;^hgXpT;@sUd^`y{#h0MSaMW>8{DjEhNQX1)L|MAp3LjZ1ODx+xp&>Y18+6A8aVCCM7o# zPosxhn+LA#?QrryCK5qtT~vYHp8}AVn=04Y=I%$;b+YYGi4MEoPer}j~Jl32}jJ=!0$d9isY)qzxjnX=PB#K z8EKyt4V#jMD{V=%b{8ValVBWSKu+~aBv&FJ)dwqJtx7HhMN2sQtF+GI?|3K$2GKe& zUTV>2zb+FDWfqrLqX^5xp(bO4IQAbhyBOn<>jUg zyZ0LHZKoyhx~+@HKh{oyWsl8srN9%f!S{5m1Ru z0-RBwF8P%RzCCGM+V5Z`=bXr837ysjBF)b4mXS0^OY0qOF8(>}cwm3VU{3rN(++GN zwb3!lXjlh`IB1F{%i?G62WA%eL(g3wu?g;_<`}|lYk6AwLwQmQ>`n|-9zUIqcGfZ& zj9=jYE#DCjJh~Y8FP5ql1_(&{|9)I^z`!`D>T>p*;z&J@8gS`BVuBR$1mNvy-@w4w z0C%@ELK-c%d9-l)~$?6!|Rt!-Ybn3(qq6%~2*rdhD`2an#enR&lzCNs;G;*h^`k_XqL(TJwIq+~yndTpDUU%&XzCa)yE@Z0?p-m_@$f^Jez!k)} ze~C-tL} z*>A}|cwVBK?0lLNt7G43+LbK089<0KhRfe^hhEbg(09j-_E|b*YtA2S6?E#u&Fee{ zjj%o09s_=q(#uPtC6JeXK8#sWz;?`5asXr^58TpqJhfgRsnQZ#YEcoX6$pM}X9q07 zc=)LH^^u5}ze3DW7of#PbrkwVk`jkE1P+{l+(f{6Z1y zf0~(IBGHp%qGA6gFVh6wy(Mchv@38l;9<9ayM$c=YtIw5r1PNg`<46ePV=$F z(5Cujt_kn(Y0Ht;&ZVjlhiTggRh`sdBw8>3gfhse6r|1+eM{` zf*ZevkX+kEZQuA*PRC*{|rX>#7)>RaZF*43?1L z7)x7dam1lgrWMXRNurmBP5&J=|MZCkkq2V~Rr@`}0B*NegyZxLaGg_XdQxt4J%1+D z{*w#j*uCjiTs+MR)#1$&>gz zlYr)HiTFY6xR0!04`m|_8|L}=PIGmBs2hko!#Mag-4}-4rTqVec{DS=cP{_+E3f~N zkrMy+WsCI!W2C)<19Jh!JRb?h_ni~T0E$vNb426#wwh9GsM$}MY_cqh#VCoo+bnu` zP?$J&!R=~ViDXsi);z>)@1HB6@TOQFO;H92`yR@q z7u^9o7GGyFN(Py)9_b7uw?5kw3U9nVIM@{)m8FdkIa!zj#~XmqS|$`kwZb&;Bgx*9 z4aDBY0R{F}?V89?YBmcN;h$?(G^aL_w|qqruq9P-6vloAND$@1wdOw`t@c;koCR`) zDAfO90hXLoTyVJ-SD+K7x{RD^w_+n{fReT!x%b1dHKV7d-dMnEja)(|`mK?KgU5xs zKwAQ%+{MHcS1Q0t!ft#RGC6ao!QY>pX+3TVs)p)Ny6XyI)|OBpO^x2llsJ0SEV_t- zm1V=Ve^M^xYeMg&%p_%&r_rwwI(HSjPTD$CFZwa_Ln={j3JN6`c9 z21sqSK_APbiuS|s1$kAm=#PYFIthWfehT61(Kj}8@Do64vuKZl(Oh1Nj&;F3b1LaL z@D$JLLjXy<92QZGoiKjSE#MPSMqr#Rvdh}A-#m*K)5i3BeepVLj&;c9NcH!8c&XVZ z8uWxo&B&m*WMhqL(*leaZLZ@#cc`g2#c&iqM>$bs&gs`qKR3@WOExK`HuCA4kZv{n zVfJ+;Uo3zri@>RxOhoW4U(yMg@uzjYfS%0JQ)4<|MJY!jdfOT)UoN;-{EDi*fQtS` z8ZxW0NZ<{5Ue?Zc?CLJr?a%$c9daEKGA{i4v_To;`^)73 z5^lepUhS{(R83BSC-8j^j4eGap=&Da30b+6-f}>VfUo(5Gs^z#P33+f{lF4zw;&?3 zP0ij10ekY9>*^&Q0jopE>amvZxOs;nK3cW0*j%RY*m)~V&5)l$VkBF9wD(nU>}o1; z#tc*&a)$sABJ!&>Xd8mU{vS-+%>V2Hkh?n+^y1Y_yXQ!?Ms|z%(-~PQLJK#?*g=ue zvC9F~BOLLEH`%)9f zZ4!giRfR2(S=f1#?++p&d{LE5*UTt}q%0XdDE%j8V*}4-k{EkfUJ~!4j`(TsMUp#* zNbf(G_X@gOiXt+E50QLF?G**r&4a?F1tC@kJALKn|6t+jMbc=nUoMW`67g#KceHi< z+sxEj_`k(+u#1L+UOu96+A(a08DG_+8?t2C+9LC zHbr#xU*fVEVgx&@FTC_O9=dUmd3EB%s+6M?wjGZ@`e`5fzAgGQnVQ&q33J6L02WJ| zNH`u>%$hgLae+ZbqsCAZud|{v4@J{ouWk9#3hL=M++MvVHuMRjSw(Fj%bz#??tis;caF0AUFmz3-DC8fqpqG(nK2y-PGeUi!k#rPiB6h* zfO%QN_OI#-u*!r_?pBgmOR`k{>fi@QB!40L+vQeaBg3Lqu=ZCl-fAwP78wGhW6 zl2Ic*ViOsKnrXA&*}~x1>h5o!of5zyMDeib-g4B_RGUi+BwMMrLu(5^>iYU*1;>n~ z%5REqpaM1usa}sz&oA@WQ2Z3LpEiZa^ctt-)qLaYsRp&>U~%HAxV&be0Z(sa7o2*Qgc6GA8NrT z_2j!7g25c)N9Jq}EdHt99slz>e8b_)^H3ymF^Vz{S3O_MX0%J#>l?fdO~!(0K^EW&oZfopD`^d)ZK$+1wX$3;!{o4X zA?ll4Y@kbO>m`h<2vBP1Uq*6FR{(OJ@-AyK0IFSK%6ky+#xcFXCuJgcCdNKwOhib3 z*>Gr}J*X#D`--5c5MU}PxtKC$;!vU}$gstFJAZ_0Ouu+>urx0v`J}272l<(8UbDbJ zW?$5P>NZjZ2t!(*pEy>#%M01^X@*U=@PQmXRC7oS!qS8vFiM#PHZH#2qn|pY7zr*% z02?mna4;DvoO5Ky2FA=3MkE(&8o-(MA;Agphf>P>YoWaRTRYBbO{fMql(hkHHqMo4 zkfTg(z|U%vr)bp4tFMFo14_){h&KePq9}4#hENb)Xe&HwpoN7$OO`?@7>LPaPb4pr zrH)g$u5pk<7MyTTTzGm~ymE~p3?)XdfPE(FC`2B+SJdV}NSoM_dT&-rX}VDsw*nWT zZUFhR#R#}c^$`Cii0he&s7yk%tn+OFteC0sdPHAXHe>Y|(pmZZRTmQ44~`zag=~5O zQm8Yo(hZmKJnAkA1f5ykHaHArs%k3K%kIPE#i+?Z5zW#14nAXyt>G>jof4si`I-t7 zUjIVJy)aJbS}r~rdEni&Lz<77SicB#jv1iC+^d4EH0-dzG|>8EMlZGDF{c4>g+mYU z=`@_t{^1zdulJkoV*dQ4EuXni^^oZ+KlDZZ7a;GwZHChpynLS%szQax3bSt9<6r*T^X?1ot27>mdo^(WsNr4K+Z4bW9&I` z_UQdz*TN!meB|u(0+HN?_(saZ7GF~8Inp?qpaRDBOHtiJaWsD=<43~T8=JC-?dESQ zEk|U4d+KvZW^S6uDl4f5^nk6Jx0$6+Z5?C4gZH||0v=CfN`4kCO2zjLoPV6PS?ePv zhRtZZbCH?eCe>#e>`<+LtB@m1q~snbCN~PF_(12hFsk;W>U_~lFNh-#g%}X|j=}pa zV{IwJw(9%NV_}u_w&K5^{)W+R7#Yu!_{d;@Qd_1ZsCO5C$bxGcOUO7mZ~efIPRsoz zzungsL+6W(`(>_1oh~f)e;VCRGd{C1TG^)>^0sJEnV|vFL)%O41~$7bc5tG*Z9_D( zQy#MpADBROyU}40)m9RU8(R*{HTFk!R>|yH#V{n%DTj-RWJVnt$sStdEj&ypm!=;8 z+t_a8H)8OqyZY|1)~7#ecB!*8jg>z|I-M%}zb>y4igv?beQqTroK1(MPzxDdV62Vm zdjk`AHw*^EVY7SGjcE)G7W%;d&cv8}V1{lF4@e6~kFlA}l&TzP?b)7!UjX^j{?q^S z)>di5-RH>?p39mmPa_hB&Y>6#XE^}))3aYYouuW~83Q4!jqkWI{bvB^?1aTI>I=?q zQET^bBSl^3Z=9vDwP&qamF6%=OFU5z0uye;f7zwFmIcbmgEEWg0dM^c`45#xTaHO_@OaDH}V0rR{uw4KuFGRV7gx<2DQDE&+apdIf3jI+mW zNJIGfSZ&$CN;~Z}M#jrv6#1xwvwE4^T3S!XIK^qY1M&`_$b-AOfXR`VuU6};S|4wB zh39+YHoZiO=DLY4_TpA&9qqpg>>#?^rE`BOl5acGhe5GEwE77(esCRU)-%F1IBWre zl#rprb9WTk^oua`XCP`?8LvoyDtmU0``Qkor?4}7FkU6`JR?se^0i|&$4@Au?!7{h z(?uYfQsVoDMG<(jnLqVHyQ|-XaFwMl->trs8~g9sl9aX@%zS2dC)A`lGm3~`z^mqV z6qF_@dvr;W=Llbl&4hDTgEOV;28>lDE!(8u<=93BdD{urG>ZiCB|eh?-NEX=L5w64#OuA})~+Vm)*~LEcYWl@&JG+Y5Ow`gIHTiZ>)fQ=YqAgCv5|9E9h-&?7?J z%*b%N^S^_12$~Qg9YdZ0S|?I_TxD(v?#VlJ1-ywD^z~(!0+O4$sJIg6{(WcYp<#Ev z*#RMFsOx8`bsl;_qXUBt^5+vH?(Jp<*{i8w(%~P(O!2dN2d8of*+-wxX@A04Z^2b; zm;h9sdj6HplUZ&$Aef=`JDh>GsL9oA{!@FkciAA+OvqEaWi9Ok#GbEBqQctkaro_j ztSp`)!jTNXC|C59#2lCn*T}t^Zr+NTW^$4`oX{2_Qvm76op{aPF8Uhk8_%nMIk@8gNh}+Xg2}f0G0GRdx4nBauNkS;pn< zPahK!UgpbYR@P>`+SrownIyxIkn2_H`ik9ElK|lb{w9kbw)wWI8k}qn&zgQz ztYow4pRFswpWF7Al>T)d!6rEWFRG(*Z$K!;sm&P{gfYCwI_{S+L(DQYHSuGZ1ladp zT(lOs@=VMy6v3Nw#*kkn8EF{*&*hd6Uj!<{>}qLu@bbbYfDz;~eCJ2>pv(TloZ63s z*gLkZ#BD0T!O&;6bX25=2|CD8%l&qACUUzPn?hAx!vJgr>p>^Mf~HIAw$;|Cp;`MC z9*2iM1aXBv4-p%e73J#nQ<=7~sLb`eTgsJi5wzzLvZI||8w^l<>s#hX81LKj% z!FhRb8tKvhU;W{dYaTg4C*C+)4J>2!R+0YIekMvd6zPb zo1gw{O9}KJwtyMUXp#l6^yf#MpT;27oJD!Cy|S92q5Uy1v1Qau(wc(*snCB7-c9vw zV)f6rar$&@Lao#v(In@#V4aUCGa?i2*)<$sZctyd^=&5@<#w+fmSpM2d5>Y$A(-{K z+~k@Dh&V-Y@V2Qr&TtFAr{;k@MFh}oB5lCvHB!(2*> ziC8a;CRj18S1@J)>w>+lVv1K3aNPfdln3@OmRt-Vmcj0!Xa1#H1B7k?T4M8%Zik;n zqJ8-zMkJv=%-ZKl8DYH6NXPyeR*iHuw0r9U)O4Ljvx#b+UQ6lc1)!4&UERqm|6${;?tpPtMC zqlb{-Ctjzf$P1>b#po^EA|Z~KshWtpYgTFIz;X-)VLPLGvV#I@pRbJ4h&aD) zo_-~owO41ZB-EK_IejR|Kh*Y~%}Fu=wk0-JJeff*_6C-q)1vX%cs+KHXuD)84%P0XS#I#hmk$!iw)`^)Kg7j!AkfMbKF=)Wnhd) z-~XqpE01g9h{8L&K?GtrrG_Lx<&s#gDB=O$AXXZ{f~XM`K|sYShjJ+)ioy>Hg0USH z722wx7$E_Xa2RfsL$IP&sTPoGsTasqueOr~3^Dm5?|t*VZ(nw^`D13@+gpQ+rPuzd zXnoU}zSwB1Q=#enp?TXscVLQO^zXXwrM*V627=Dl;S~+@p1Sg z_-(`szt=k-Uq?$U5B7#^y|h8Ep6*)iq}yB9n7bx8=AlY=cs;kOd8N?~y79f|QDpZPDFN#{KMorxF_+)W1pUKsYzt9%3GRQ2(=>)~lXga+iR%^X(zp829U~yADFZf@R zeAlp|(AKJqr=IUN%m|!%cgX)#z`>~txoka~O2bY499nQfLZ(b|$os**Zb?=1F3Lbj zR!?kVjHuhuV^OX#$21_tEbe6?uepr+R{!#=8kU;YZ|^NS_^7kxMgA^5)kh`;+jLR` zO^!t2D^9952nI79FV1c@tXRDG@SIf7n6z2PU6QI|KVDz-udd%N&-7(Mxi%%7t_Q<{ z)$N0SC#vj8%-hm*H0y@_fyn${fEd3W`8FK-*=Z;D@aBfd4%4j=hAVq)jrXL+Qq z%koa_9j9EbgI8hatl}(QB|p*^-siPvh-2K^TH~>mx4wf%ft740}2)%K8+4+p+L?;M?Sp`6BN znx^{7LSTYXbjpHDEpr}=3kTLbj}U(48+sssa$2xuW*lx7<-5Ddt!c!eLBBTBH~!uf zK|C=K{i}Y+Mk_d2ce-*ea9596M zRA{hQ1lTw+1w~syrz^;QqAm2-u_liJbFTmc+(ritnh4u+6SBE1-U@7i!NC5;Dqchu|^a&82PG*edW-A3$sCgBg$(*BT*b2H`MY)a0 zZF}Lx3=Jql-+;OP@IISvCWg(5#xS>uD}p}H3A{2$rfMb~I+Y-%(o;*6nAsvZQ~nSW zQ>LO1FNSfISoCsIub@T)8eRj&%<)!*W1R|>n1MB<-qCBoj;ZL{_IZ9C^`xF*10AwU zQGTD=^RPZ=dbN;xsinY@sVKq!Jiq%Lh~)u|rRV~AcM5+Ro+B^2WH1#Km*~f>l&5%% z)h7jCG8Lsv7`9T0Rn{ffLUm2LFtiM569c4na~WVVmG(+0MPU~k1@b#aNyX#}Fdz8A>I&e_P}*aoBwQvZkUMUSSV{|;O02UOfMONE zfoOq~4xd&6Etb+6i4wVyLXuq}?>gcsW;Zcxi4reIZCn)1cFrdr(g3`z39QC$R*no9 z5ayaBG!u5!qMOTI3k_b80X89-{uyopZL7c>Ld2YmpQr-9M5Os-=!^w$ghACvBHBuU zbKtXT6kV}kG89&Wpn;W>Ax9TfbvvaNMVGoy#*=EnT*8j0fOZh#Ace<)AA-@XC#pk% zYr-bu*7d-Fs85&-X&pq=8}*3VpQ^yMAUzFOKyCxV1(^UE=KwZ5-vBtoOgQdQ8i5T_ zH{oq*1Pckrv*TmPQ-ke|K%b>FvoiT&0LyHWY^(3FMg?ug!mwrN%|rhLlzf1^{{dZ6 B+JoBB6D%e_RB)~y>uC!jEcWxz2rHe3zbd4vH z$jN1_1-@OR3Yi91nQTiMz@SfX;<+r*3 zn58u>)jm=Bnj<7tq+1DT#%D{meCd_3xxDrp@&^w|Wr+mCtUeRZk=&lN1}^>3u0iO9n}dMD|1kNUZ$d0yUU zyxp)vk3k06Qe2c~fVsGMal-L8MS&*++4p>LvHO^o`zgRCQtJFGTGE3aZG~E&x2T<` z1`u#3u&gbwCZ}(~tjxPa&OeJ>YQIOtC$~?MvqyQ%)e7reQrH@6VBp5VJ+8OUQuuJ0;qmbCXLEj& z;QbC?k^&G?!Ts8^cSI8-!}vHQ+9xtJfZ%hTndg7B`>Sm-NtH{v{2`i;?H33V2>Xd& zDDQH6QVq0!`!EbUH_TsY;?Cpn1p2!Hcqi0_`6_}`+q7O>+3AZJEyGhcSl#+MqSvk2 zoQXv>`%TM*ImH&O+>`!jZGB#c32^Ky%2(4*ryYPT1jcWE6I#cB@g+z!ZA&h>#k6F> zV2nYouf>NY;xxa0^WRP~S7~Y?Jew*sx~@fgWL?4`TJvM7=zj9++d?INS%vLz%PaWZ z?S*@@-eUJ1dep2W>9S4*w4;^R5I??<6jgH zJc7NQS#0w5);@$)Jwbf(t2+y;rBB1`fF;|8%J@Z&Mr)0stquIY7gFsTM*8t&I50j+ zBJ7IJs9_RqvS_nZsNnwoWfq6E%1n33}wHv7|Cp&1WYK&X8d377Q^lmfL zS!XQPc{-<~yQT|(^wA@wgBxN=1sXy2! zdkF##TE`i2)IM-``e*9eli&D27JP3)TXV1zgOdADKKNA~fNi;m;xHWCviTOoci5l^ za3eP8>e+)Q+P(cboz8BzJ-tzr#+I;cxZ04~ZwDeKFlKz~*Qj6*ON8Oy*J4 z&Bk-HTJ;FO77@XqzSTD|P^_Sf{=TyY+unO&oUz*@CKX@uP+b3t((7}96i<&GR`nC3hHMI z7v01GMb!d&U#{+;wY{qjwKIUg$L%>x?$_Nb)*ZP$Q%bdUy<>e?tFzNlThfjh(UO!c zio6mOlZTZc=7EEx@nZYBfh{%mAyGA)jhf|yHWnza?&7HkQQAP_t-sYUSkWG z_(Gq|U`gi*J6)ideQ9&}EvgGnK{I_<`qb+1O!i zG*!stEi?~T@%Hj)`Re_Bx7{wO^oA1g-J0xD{I?fW8}Ykv^$_$+utu4Jthb!tb{CZ* zbHLW72a-L=Dkw7$-L?sJffg36VaY1r?57f}^?i$t7b#?)qQT4?scZnk;Ljv&0gx|X zl>vY6HMuX|Z8uGDz#G!7th1(5gHL>*^--$O#G4(L=#fSr)S!=|v)6NQ2{+?LA4(z| zB=L>QmdzBd>jxl}WxmWor@V22le)@&QEw9VbqR4uu~WJfkd)pS_rsYEIIH`e98=<0w1zaT zWw?pS7>TOahD;Dor>2_8rpj-S5D|&wE2{>rPzihOR@)o57oVr=zCn~uziQ$+nM$IXikI7F6t+C>YUJb2=7sT{VMjvRu{3>G@PK`f#x1_hYX9r92=AnXCvD* zsB*W~_~0V~+mdf3{}4#9(u-_Hv#;eq|#KmLv09DRf;%r@=4 z;B_R-`orUg4hLSNT)uvOArgwE7tEtaIlOX*{|r7&`=_e4Pt{7^DfP7Ek+#6hW*jMr zQbFET#?)Rqt%N1?;97nI2$>1t&h(oMpk>y>Zp7(^DvI-87Bb(LC_*;rGFI6#tk*!TINjVC z$Bk32!bzFSDsrEhAZ=s3xwYY9$fgfX$Xf1az`Pp#`4PiJW~|Bpxxa~QQkCpdB2~(b z-Xgkdf6IgYSj{AXxL(YtJ&GPYfqR@-x%^HV*B)4qaFa5mwGM_RA&Sj92-%W;iLAPd zXF}hViE0fAsX$bFc=(UD8iwxq)*0W)3>Dc*;dzAqXK5@(XVN}DyB6qXT9mq%THVXb zWK_xrwObZtH5>uJCJ5e^TG#xq`H#)sim1F8#^uH#Ln=|(pB_|SbFycIcdYdwq$Xw{ z1@PajVbjMXIp<*`y5l%&nhNXev1u~67URWYK4uB{6?c*=T%x>cAqGL2*RcdL>r{9Q zMcWOTtZP-|`A<+HBU2)54JkiARoS;B1P4-7_pb(MzC>z(B{D}yePCH4;%Gzlhbs!E zS9D5p!c}I+23&MrI7c&OOc$2%s}icM%RD$f(cBC4;mzoa_9tDWf)WnGGIT5Ij7;$M z>DVT1{MfK^md-5QZ}TLGqoDdnkK_D;;Ltm1UCfU(O}hM{Ql@~U-H_C(#`RNw-Cy+e zAsGcGAg_AB>x8ol$Ev4@>z!<%1YriM{3VFJcSf$+1g;D7<+IjL6cJ7f;h9qNu~6-IoNL zjR!sm6clQts?KY21VHXOtyyI#bp#KYSMQ7+q1LXc~d5hm5jIuAyYi^^W}qdK>uSO{pgV z9-&MC9md+IZEw7PR!heulD}s#{H~8Q+%gv2^fEys!%!(bQ!#U;;-_%enn~1Uj#?E< zu}KIbJth;i&Hi7U!&kSs-R?Oue-Tj{_RblYK$2M<{GP~lJ@)tqt&A-1Sr!?UFyx z(eZ8tRR}HmSnrBib6dOg(Y$8G^PruE! zHSNdVi^jS#M*E3|J(QL>j^j8 zaAzo?AX#^Z>^!NXb9_U36ZP9WO5maRpho~Eb=ZOU-k1J`z<2SXB+cH0B^lwY6T&d+ zJ}3_z-cyI)nm8i&QoUO*+(=|)-dZRZi&mS*I6PQH=;}*(<9c7N3#5VQ&L6<6Y|=g! z9T7h36qck|rZ5-=T-LuVDv;?TzJgw_rs1!ox&~QFM^tJdH_W8@(&bCsXccyuc?(M9 zdg3t9KjWrhNvkD8LG7brZ!gDtq`KVfzonlho8hiCM>LtB;#259+M^T8RSW~pyD6kZ zJgSNC{vqsb=WuiHW57~KWD8JTT!AWvW4o{9`O8Ye9V@WA9{ynH)jGI`1uuETfJD-8t287AiP0WQa>CO?F(E$5m|A;Yu9OR zkV%32I}MO^K%fuzAv#99@U6jUnj=infPx_NNTM*%z#WDDLM$?#65XIwSSC45C-cT-c?^h8hbWClXoGA7aUx=0I25Y1cjyDRNjk2$?h4fsc)H%n2_=zmMMV?jy#zQ8rAh| zNjo{Rqw=0?SJd+I2Oq4QN#UtgivWr4_6%#-q>@w#b+Lb5fiA`&g~ya zX5jwxg)aiC6spX`i%>rh4m?D-1yUFVt9u0AwRqP>3ab6^*BDu$-04O}#kSI7o*a_8 zo6Nl2IuTi@ZYr1gXyR6wp(~cnaSF0YaQ^Th3zVCuI^dD$$nI~@PZ3sIPq7|HwxjV7 z%gj&-BQD=f*+cY)(d`EYqMD6S@ca7~rg_BL&9_!C|3aH?uy8F;=rs8~f&sVID|K4z4UaLo5sJeuxC>@YesTaufBgZULA zMLt+aT7dg)kkB^d-HOaF2}fQZMgmDy&j^BKV=}KD?raPz9>M)8<88B%Gtd7r=Y2Gy zSM1%+CqJ5HR<(sTKb){p%bZwD103Xy&56hW(PEMY$`_Jzb(C{eW@>*`sD*HA2+HYc zfK!#-y6Fx~O(FQ18Z~gdnu{y;KhiL#EPm?a3uF@%UoBOJi&I(T=%Kv|=wZ$cj$`kJrNaw>*}AFUr3$@Baq25-t; z6R;nb+qQ_#O?>3Oq(jFB^>O_{lB$^g{n)ArO82;Op1YCS5;>gpqsqOJP$(cH&**Q_ zhsW_yG^~f3kzt7Q-#-|ijGG)JIp%-G1xz#%KZI^|+xdn9`m6uU?`WdcIoFL88s4*& zSBY@-!RNwF_R)amr3Pk}r+(nAnh3NRz>MH~J0HUIJUCziz>CiuODg6q9oVgXrlvE{vd<;@w%;Kt>e%0s0ed z+5rk&@w&UTUqjmiV>k)pxTA!1hgeSDyfQ`?pE#i-rZ{}q+$Y8tYa#ejy(;WT8Yk)G zMp&by&t;OgCp~~~491jL5j6iW>qvA=sh4`QoRHk3vdG1ZgFtXKdx#kHBk(%b^#VF7nDOIK@cm* zQHK`AEm2{i9VnSbkk-{C^0)k9A_`qS64^}6E-egUrq!gls_L$o|IcuP?h6_PByYN$ z`Zo_+>vk&WG-Cu?6&BMEf0L|N%#a|{aKaZEp^86T>LtVXuU_o^0eU7 zhQ$0NdyG@W?BH7YNrbJh(D&LwfbfGcQ#}%=F^g%2adk*&{1P?~hbZHB(8rT61xCF# z=yi(*;UF(HC)s{qM{cGQa9Q?=_QF=|2j^kN0`JbCkN;ilSBhRBK;K|LvrahrZ-zmc zbaL29Lahj;rR$SU<)3Hcpq@gr@d`e=3t>+fLKF;Id}$t6QOw0ewyW}~kTp+0fWN+U z=xrN>6ExQ5++jq{U-4ie$bs`<4)Eq;*FL$NX9>vz`wB(rj=F|IpaL&oYz=88NT&Uf zE=MP)7u!LFENac}$2M7sx$vhoe4)^4rfo~EtLlQIdE@VhmBGt!bAdrr#RZ|;t#P`I zJUJEAhm!zHjal+IQ3*>#G*P-1nH{ly@`4DWVfDmtGuR3x8uz5SuueAYiC(&~Vxgbu zU(L~wLtW_7CT}1h?PY#hT-ZFCP z#x5*ZMLiS8IB6fBAT0DgH5D~qvJ`2Zx^AO+$9)F13mPEMr^fT>T+z|=DMwvi$u>!0 zd0J8RF)>{OK>A=#=aUT4=Mv5P4HVzl&`ao`_^YC_8Xo;74AnCr$~+LSkN6r}=<+jd zlrkmrBMBPhNzYPhHR{YkLX32?Qc{JOxwwf`b>u5$sZ_uX?DWwp2Xn7x2qfA4?;J-4 z6bk~^$cwX*gJpbZ950{ouBCpVdIN*5%)SfQp<`%^^nsg&OCywO800S|g!2H8rC$ zMyk}Nx@Zb%p?w5tHuvX`nHQ$au&<9>y$}jMK$|2~5Awp(Cw`_H7~Y4*L`LSfBoTv< z9x+f41;;y58gm*_=cb04k0`H68n>I>CC@nX{daNwc$uI?)n8%>CD-C zae)U8o7}1HiyXh+y(``o2eM>fr=Ode*;*>SNd>@m6wx>=jMz66WR=~* zu^&GZ9^BHY>Ptv&n&5~E;NpUm_T-SvivAl`=-XAgX_xhx_gQ}OWBDdo$b84mx_2hH zKLEN7@te<&rNl!yNQNiQve;sxwAUS^>8>Vw zm*|Iy>~r_8vlM;G$#ViTV3BfBGJ1VS@v76DppM0luz{vOpBd)vKdn6}ly`ns)JTs! z(ZTeX+CEmE+esuCAeo<&KAV{b)ze=l-Vx=wYzG9Qb=&n_8P4q^cTva(Mr-Q#2}*N0 ztRi!UKMjD*tL@CW45{Jz(f9R%JO6wK26EB7XNL+#;#KID))9eaG^O5LD(Vg|g80$P zj_OX7=+_fYIR7knNgX2l%OdhYAt=w|s~Up3wz3*!|cF`vXo~Y%-efiXi2mN z{x-caXv^^ig={AmCMIPQxq;0TT1lfUob*!Xxv|j|NFNBS_v~HKQ#}nUj_EFqe+Z_$ zdya*uWcpNK#wUQM>p6_g0O#|cPeDGBVF^&ACUUTP@tUq&Nc8y>%zu&;H0;mrvtoyN zEYu^c9);#g4oQ41o2^v-+{=hZ;nWJs9p_lb=^k#_>lmFlhL)r)QEjTD_^U~z-(RY7 z^!xjNRgr#E;;I6 zytJK`M#9F8dKm*cXX3*Z7%J$_V}ftOSq3(s2l~Wsak3o9_yuGGaplyXnIxZUW3=kz z{-{3q#qK~zs-n=w3T}zFG-1L%3r^_K4lTH+X|5*3n;KN?fDs0KYCv_GoS_gjJmeU z|1L_NWb0EI#MdOY&!E?0H`KDVCopwb?+1C!G&;aVpp8^aDe+F*Ao?j0<8=f-PH~;T zndIWk;kq*GHv9+2$?wXLEdXT~7@tf%Q%t>lj%_kL`Xokqp=k1BcyC0h=gE69d7<>5 z%!3z~n|XI(z7Ik?H`FS?nA8)ataX4)G*pTd>yQc_G~Z8Qm)C-8i0R(#+xx+-mOOUuG3o#4^Ikc5y-hn>mjZ#>P`<@cB)hS+{Sm}#-EW?xC+kf z>^|hqVR#U(bXRZho*=F^V*y5o%Oh}v7+Hp)72GeVr&W11;*#*lS8%Z**B>slHzkO3 zv;GdBu(d_bG;}1^tt3Yotd{c(*-ziB901pzFML;lE*+Ku*J%a&$4N(R#I{mO6^0HNN^;m-!<4k=%zo=Ru#S0yv5%= z95)Y2L~x}-%wMo7pki3j3m>`Ma`B-RL1UniEQXQb^J(4tHvnjKBZV3@0a2D@1AO~Y z?rT8&$8eMrK6UlM@K~N-bj9+zWTOpw;=Dof&N~d%3#PX>E)6NIXPVRQYuWJD$l7%d z>Pw3#bREAqAa}Oy06MSf@gcbsa2LWkkPTz$9WlknqhEBe%#sIWkHn~vz zUuyek5DTo$G+0Px{%Qxn$j6)xGh>PaU zNO@s)B^U?2>|1%aA#B5;ooo&qS*0^)BXT!tg0`&1wHwLF$z4hMlm;N1go%Ium0MSB zG@^nEn!*1wjG*litVgTlHAt*;HYPNlqtdnVEqIC6No?2}{%#WAglp_~PD4Tu zP&F+zBt12kPv~IJFueEBr;FC|q!9v)JH38Bch~06&j*7JwE__kW7D%jkmMW<-HQAap3U^$PW`X zT&&bUu@bj;yh(bWgot1C1~yml{kklIa+L*f_EfDL(Gar=K!oC*>Vf+K33M&K8 zvdwBWF7{5c5p|al+hlEBZ%fpLDmL6Kdu~+#3AP`mCmi+Hh=i==e?D)VG7*i$_@@jk z?ejl2;6PtB*q4^FRK~RxcMTK0*;Ad8W=0)BKu^od2JL$T0jZ$~aol=Vq(&vrFV?xn1U# zegAx`wPTVs1o?vOclSIEMNS_65fr)OdHdE|(*En!%2~EmOC@e4fqYbe_zE8tg~CJz z!q6=DcA0D5l)gSu@Xne;`tRRKRRqr&%0Z-|7JyOdn4@w^Qvts8ACQU;<&8O9(Fm+x zk5@D46_T!kqeo2Q3%0j!2KCK8V7jD~+X+RoHDMFzirdH5o8XTJf_+eC(pQ>g0~yj9 ztd#CLOv_j5KT$GLW4>mGFr{?V3r~KfEjt%ATvl>9@i+_hRan?U%=boQNBW-7;>}k9w{Q7?CgEaq;xcK3&!5_@^_%=56vTgJ~!TU<}gHuIRz7 zPhbgU0LSNk7x)9B7|KRL8_CY3l`NRqS>WxLgs+*{B&JC}EZ)jssDTL$+7Y%qBW-mA z>xxR>{AX2d{@^&;g)zE?^r3Cfbc~9YiZ}#o#_v11ZpX1PGsx^8AT{lWt|>s{>tsbrFRWWE@=S2bpmm(L7<}_3ShF^IduDKYmgZ>wpc} zv=AaY-N|#MW4)$i)p6f{hN5-WWQNeGzx;@4||1_VPgmEJ<%}4XS$#DoD5T zuDZ*EUycC?d5rW?BHl^Hj3EXCos?-#w)$LD?Q@)GzcL6Qzb_*>3;8j6IN?_yIV-D= z(%@qtp;C8KrstT|9XM=Vs{JudR`S z415xg=-gYMhWNY|c3@!1sx9nhZ`2o7k-^v#G!WP~Jex zVfT2?@at?)I95U@-6UOne;;~s(@Wuv9if6+|4n?8p%QDvo2VF|2b6;bjOADZFo909JHgERmpB7c1O4dCJf%7+HNFjVk%B0Mq%Di_&p71myw^y@~RI}sqcAAEhIp7ODl6W?Nq zoT%ClQ53U6)12urN5Qr+BV56oTEW>a>09yF#==)@jXlSC0ea<7uG_wnCf;4u8Rj)8 zQMo`QJn5A5oz8kE?FVffy3NQwpP1?|u}ddPssbi$KK9IY3+JMPah>qU0Av{VW9nS3 zNGILp|CDAB4|U9b62&r76!~TDJo^jfoT8lrNZz$2ltsz@zz;#^L`JKNOW{7k#g$0W;>ykf_BAV71yRh3%RYj-@v)B9rV9Jt7Mo(B z+QPjCNF2`Z5cb+ALdZ(O-?E?0G8oR<3;QiU!FaseF$w(eUPojerRoNDhjd%KUhs*s0xF0yR4%ZV(JKg*0Z_F`Yr0pi)C~mn)crd)EbNf6 zm=`7R(dq_@a+BcqZ0rmSRIWR|Gtag3EEUC<67VBEBG+Eqi_vba!T+$;v`(H7np0cp z!Ai+>uu=Xd^4=c-DIZYRFqu)7IqRf3Mxx6-!{YF(+`~gJ%mK{L_?HHQxz9aM$4i_) z#I%(+K)j$pRx_a=S9l!`FAVB@Keq!LO@cO^phz6aS4P5kMuh*Y;6jJqbdMO!aV@Z7 zm~ghnk54PtOID}dXlNKWI*zsEbPOnYHo;tqhPADzA7m8lr!%vsIyX~hMz4hiSz8Uh z#)Clo-5l3p4g za_1uxnU1u>2&!OEikw+(&nikDWG~i3jv$z6G^J)W72iamy{Fr=nT~fdNniyflQy1! zJ1Qg&h3-cY)0WA&Ln<%7YwX=29avgw=s>N zn+GxSaxSzT8zrS#A`m(6s}`cNHW5>bZb)7rxu*#4CO6(zQpKBgj>|a7FpIfv#PbVr45VDn zQQ`ULkNBNwY`iNBe+sJEpAVOzjq-~~B1*4>qw>}uQoCZWe-4q770#M4eNWY9cY$5&P3CF&PW5RUWVC)7Lgc$zt_2x zkwD%OrTjCM818#S&;abl0RsNk#m$m*-fJ>69+=|rgNx?IVqIkkPeJJ-q&3~&U+Ky5 zhH@Dhcc**4odD(kiIv>sj@#V^YQ*517cWFTo!y{ zPP05W+fH@x)-}5IC3a@?5!&l-1NID_r&fi-nVN)8@ zpx0JR@NDz<@jAZ6c4Pev$kORr@MHA^F_&Bua zltd`Xeo?+t$i35Q5lkG3tBJi~eHor4bx8kcmzTAeOeL-R7t9i33wfdox64U_8k%^z z`Wr~BhC1~{DUCHLjP?XQni3B2q}u1(^I-Funt5r99VtBg*8Z1X>LyuK;X2*mufjBo z^r~hhaea0PUtmD?qwmrNZ+-ipvfY%59&PL{=Le38&*2No^o)i5oB>9Ajvjhl>QQIb zKiKWQ1lCgy={bQjv+c^Qa_3_e$0DH4L-1~T-L_@CWaR*c0N7Ngxc*|Cu>=#$fZIl) zHmg+2w$flH2_9vQJ^aBpUN|ERs}9o$J}@L|53WPW3UEBGWJpx&f^XoIDykkkGT1xf ze4(n3P-IIZi#Euone13woxDaHlZZ92J%}%42?3DNCe>}xi3P>7Ly#sp){9d?Z>~k) zrc}M+=ayqOR^CFFC3U7h;VGcdU0F|JG2$mK#kM(&xDSO2`r*$yXqt7Z^nP~-pIekQ zHBN__WK z?X4s%dAce)BwDiWPOU@Pu+E(m=I!QK!=bNQsdZQvF)h2OUS>7lkkJuzF9=9Y2hNt6 zY`XD8k9XNFDy0Vp_eXNaEf*+<;!^l$(ndsZz%5R%Zb;PSA7P?)m6`%vc9MqvvMid= zq1lpuif3dirxQ`2C-%bKN0XjJA%6$ZOI!k}hW!kpkp^M^nJ_Pz!CI8OYzWoUB!Faq zX<8D`^pGxrHvvCvIXppTh7?fjQ7C;HRL_GUH%jr;gLL35F&B{NNE`Lk-8Ba#%%cJi z0Wq;_qCCiYV@*^o3RLX)TOn+nM7VQMn522#G7FDUVQ#d#)x&#@qq7vX)49sg2Jlc{ zcmmOpXoDKxktY&2Y&r$Tg~~&eqowd4Wt@8{lv+RLp{j-aTrO&(CtaiKlDb6npp6OR zlNX$)p8gfEQlgpj&`tTi_j}$W{ZPba00slA>_a;oSJjTLPuk`UX!ryJf1`!fqZzSc zSzXYiQ;{>y=a^c%7`4qDL%OTyk|?do`u-*W<&~-&?H6{$4V^9O^e>!1s7DYLV8h>% z(lc^;9B#eCqep~iL`WfKdLo2KjAAbc?h+QaibPC^8``%@REXG9A-XZA2D!St0+__= zhBz=9oOCBm+3%O@s^ESa<2_Z7Shac(7>bg-aLtu+Nd3bJf7)yk_}jDFo9h^bQIBcf z_|m-3=Bn}w3%f(al91L#z1jS|=2zv3Ue~OI?0LRO`82sn*8G_g@SWfVTM&4iG0gjVi0y2+%_~fyh zsrxP~U6%wzvCsaFod6xR4A^${jb&su-H4J39BcavmtHvru~!WjIP@bKp03T` zu(!}3Nuk5q&^$kk(+oU%I{@yvCa}$h+mMO>;)7`xVf~&;FaLg0^Y|#0J(W57r0`_Uc{k|ZG!#4l1|yd+C=`F9>r43?EfII_9% z?+b<&F*NQ?b}}z7H{XJ+6Xo%sc|#apS>{>5DeSZ9N4P!LKUCA< zWf8OCV5^f@TR57w)4t-&i@pq=L~~mr-Z#U*tl~}g?F|%y?gFPfsZznhX~T)8shRsY zf=FjBx|vcgEUDmOX)Wm1F|k({D@Csc*!IGE)6=ST$yIbBl zfkNx<&KczLZOI=B+mX4Bc%3caG?}YTu-y?+Rj%3jw~(lV5^x*vx1qc3mkyRi{OU7P zibFXoq>e>fsZC-_*JZPLyy4i8Ii>;#k#P)-cj=?rk+6Lu0P=rtv=KhZ>Mt-55QzV( zQELBRV?wY66gIhUfD8a!T+nqw$hMhok=Al?+jWs5@%bM0tn`zE+`odBf`sH z_k3J;-Xt->jZUm7)TjrIIJO52IQP>_g8yDffHq6AkC*5rQocuT-j@lIP=&VXFvfxu zE?tJXq$uO+nMiG6G7_n4@8gJRs~1Kh3&I8u#@XKihlBox6bIru2m^RSBvjFViwYpv>^ zeSJCHK6)qqX3gFjI_yo(>eOuuxP5!$enB4~{i&|>Bg5n*YNJi6k&D*ML_fOJT*(aC zm7yDYv6^DBq8{*Gfqdts-L!8}I3=(vC;^2WFb|s8yZb;a5B#!%3GORd+$XktFY!T= z^ufzONvv6AV(5&LZf#q#d)^tSN7|L%wArNG>7n-EpW0De#>k5ACV49lvi8I+$jdG#w2~LN7o&Tj$euTP2jL!my@z8Oy%gtJ59&yF8mi3(R<7PEDTv zk;R63I!=|?>Z?v_%hHd{Cg@t_E}AK^W{Y>2(JJkRrvyzofo1r8yEkpJ!=LvjMr{RF zS_JRlkQu=Bg_M;uxKKb)>^#PhsFoJJ|L`8C&&(!={)6{~hi|yR^-d+P$>nn2h3+P6 z24FxKQg0^09)oR+Uzu585J;5d3RXK^h-RCe@q!zy03TmX8R;h(g z2O@p$VK|Ox-q-i2iF5NRIgS!ghWgAyL;mfba|Hxub4z}>IbkULazt4Tk|ir_|FFn! z^5MQlUba`D@l7&rQmhwFW>_@%hZJn{3q~FmmTJ*j$5!@8%dHmIYfZIaC^^E{i7=27 z`}&7k=?m@_8-M$G*S~Y#^2UGn>p`z>6~0BxnD+PzE(%x@{i8QQh7-X5@i~zGYYM8P zfEXhGhxgtw{-;mG-}=A%L|W7KTO3F|f2kv;jhkyoNU!Wb(c$sP;P=?$V0BaVW$8O8G0^)@WCDlPewedM^mb zlZ%go^bC86gZJ1|QOI$;7qqHf6 zCoUpN*)9te^P{T(xGDx_-FsiPKQ3@haU}*YXYG<1&jxPGYjYk_Z6ZbGW0xD~H@fbG z=t>WRfr>FS(qPK@jH&HMMdiPKGH|O|^LP|t8eTM$N&9EgFDnKStWaO=X|+=6?H^w_#SN9gtYZ9gp-E~P35pv?OJPEBbF-qO)}lQF690NkF#W~7ns~~A9CAQ2I$Cb`48uOb{lQG9MfgVy1G$#NFey&)Vk-EvD9D- zL$ItqUJx;NRf$D;h42_ptyv0c*7hUvc(4Y!G~3#6=D!PW+O~%o+$BX}aDbY;;*uE9Io$-_Eo%0bZe3L8e2@Y8#}D6dt_6ozf28!^`FkOp zI_2D!=66XBpJc0Bu>99|=C!+jzM-;U&mR3YEwNK_GZG_Mv^Z)QNuiRzVyb{D$<5M) zgkvi}Uu>I^&)&2;%7*lN{TkdXb`4wGRb*8<(DQGf02cUjKM07==j!K4s|~%AWbSwQ zX!c8$(;mkTp+9?NGE60EbCCC$F`i?Z)fAdqELb|PQLIwyfx_#yp`%cye_{AfkVly0 z3$;xIrnzF7IreI&Lh0pLiWaLGNW|Tzz?P!ar0+aU4GTl5fPP zL3jr-J<4$4fWr9*{)iQ~OEgA?53+Wa#n-nu*u?6=f+CEp-dt_(U!bWk-cA>6-b<@;cOrNOY922z6stN@EZTy;Y zY1r{Kmcjj3jf-a@Fe($u=TYIWWx*v=p#H^Fma8GKQwfopAcb=A^i{1ZhAk|bt_ zv(Ca?P>czuJCXB$bogfYn?zv&(66{;-6$jFGX;UPeQd(Rc= z(v5lSYV|4ST(}wJ$`H{;on(S| z(8*3=>wb|p(&TJ7M6Y_N#74L9+zK1LwamdlJ9ZXsNGpaB3l<_|FdT?BxZ0QqUKXx((Gy=L!PT*@Y6s=0p<4t>3b7pwpGAJUY=E20lm}A1}6JO^Q>T)4kE@$!in%pF+aSU*~g!<$Z2jO$nUyPxa+33Z2&YZ7#^G2ASlb?w> z<&TcJ-YEQET)k6}CeaqHTefZ6UAApkm)T|8f7!Ne+paF#wr%V5-Vyi4KIbVPG9n`~ zVy?_J))@1fmTGpt8JpE@buxe9uAcw>(kJyAJL3Z%2TXM#m=zs=r6CZ8D=HfAfj3Es z|1ylYVU@R*)y^V(qHAcZHZe=d777Rd0x1nGPHP}uUdHsklN3sC4uo+^PO%G)x1La} z0Q=A<3rOpAddS$JzBI#nT+1XGA_#g@;6ydqRbj1;40XZ_AUl;a7~-(knnZ4|&o_t} zw@N?{>XaCYB5vHI2RY7za#0335nT;zSW__^dQntmp^6&+p=gFtv+T#NmfZ<0Wfw5u zs-Ko>U|w=T<2;1Jg*nAk=@QdCEt?h7k$|IA2V9IkIZ6|uxaSB84mBT>3N>u~)sl9v z5)oQ0TA)dBGk(0!y;4flxXy@DtS{qlHN|B@Dx56Y#=shH1qkB@7I`UP?O`#?#6jS) zCE{pckA>55U65>un&mgsmB`+ecpH$kOGys&2cavPwAxKv z0`Rso_C+>YMA}25%mRtY^QN6H`R9)!=PlzXBmo$meRS zOG@9%!V0R$KxY!#&;O2eX^oum=l=bE9DNy>BviBjj>>Eg z=`el!<8I0p*|Otfbu9ip9FGss8{e9^$&DRRlFsr+;Nz4Tm;JR!V4L_HYh0|LU z-8{a2>Lv?rcy32H!Z7)y>+B5X1^NCM`mAV6n2L>v_NjA9SS)szb;(vYQ^3f-uw%+Q zhm=zwT_3&;BaP1mLQXMje@C{7$5M3QBs-)CUERDMF%LdZhz#GO-o+}h()!q6tbJ+`NP+v|GoV}8Sh?!z|579B?R2^oWdzRM4wiGwH~9L(L}*qbEQ7!?bnC=s52(t zBlgpd9TUQX>%4_e5r8is=jm$bTn>(nv~Skq&(oEx1g6P{1dXTOTBt9KNPv%YMa-MG zy&LF-wRj_*kn{%Kapq95g1Vx0^Xtpfe(S#%(QzKEb?C5}@RrN`zC7f6Kdb$9PENO3 z_@(2fVOn~P3$;!V-_DqJVqmHVS5DpRi9emUAQPL1Vogz?4j`2{KN5Da%qq)(oT{=d z)a%blF!i%|VPEn7cz^BnQm$7hn4TCn)>C4&Vv{Bm5YhrCu6kl7k%+$OHcNDK`(_C{ zFMkwr`notRsh02oGIsyWh@OvsW<fsN}QX&L1;ETL~NvyjD1S5;6UGE0ITUM>?Puw40LMadKlY_mTF zO>tf+qfuM^6B@2BD_+C7(bCGKeih6lnBw-`Z~+GFs3Q%EA>`|mt`(fjPBigb^u3tt zbgLr(6s|JsVzE+J;&yZU$6cVY>lcVWlGNW7KvEC zhx6v*>!>Z53OdYYb2SMVP3$m=nt!P{4tOv|gkexe@{ivbj5K;15Q7iZ8Ncm4%6pF- zDow;7Yy{U+bQ`By6h4xu;(3qpMwx+`YS$(JR+ZEKu}UOH(exDaJw!#ee}c`S5k0I_ zykZv_w7@nnXx7yP00BUb@Hw|1ejpch=CzcmjpEB+LgucAM3lym24EXlTAHdE(3nJo`OK+yS8(3g0r^|HM$jb+PbK)w_2Rx1eOvCO)e2;{#_ti!#9s6#TDnZXHk1EvEklS|H@87Gc$uhkleAYttv!5qZ-Ls>wLufgT)W ze*R`vx0}|ypv6JE9`javv~a~>?kN)08*yq^u3qx6fvA$Irv`5a$yeq_hdLYeo9NAH zjGDIFFVlAOj{QmB-SUIs^W|k3_i%nOV7f@aix)V<_%0|!>YEQ!5#)@RSnHB4&UJ*; zckvGhpY`16u-<~bu*)Ikb6MO!GpPK=AR;Ji!!?_wY}=CAoawIf5Zm#?d+rdMs7)MC zW@rKrDD(kv=-q#8P2iHE+8q?E5i*C(mED^12JX>0uXiO(IylrjxGBD#QV`t`1Z^gk zZQ$c}vv%M4>?6cK+C;{OM5s;zLBMGv)9;sD(H9N%?zhhckR}3-dv*j&N^1Qstc%eQ zGt{hac3pbSj@=^W3*!}0r8@IU0Kmr!oL>Z}!?;R@6@!f+PYiGiTGgk{x!0P1Xk;Np zADzGa%E?9dPCR`3Q%|k9DEwhq!4^~SuU!RUwUh!w9VVGntdNIiIUASY-qJHKt94Zi zp&0RDfUvHoGtSNerTGLPZs)tLHR2-~GkA(DUd7<7$0s;nfG!j}$F$qz$pG>++>CAT zI|9y3Yabu6TK%M*?N3`@$!R#Yo+|h zit)}k1hz&d2Y=@Q=VWLYzxA)nR^+m1*R`w;xn%hVvsR8Dj(UM89*^zeR0^SUFY`Ww z`qn$<<~+=_`7a#2b=D@r}fD~pXC%; z*r*C2IEzd3zsvdHEq2Z=s=Ioc7hXhjF+w*qSN~yxzSdj;9Cd>W&sxOW$Cz(A4X}D) z2W;l`cRCNmZX+7&fWz`^o{S>%Q5<%RjHwcNB`QN+fo=z5=kmtYF(l^JcwP@zn2$R= zYh?F4x6B~_N1v)mB4DO#_#+f$nfePW_E4t*Z_yJ}e(40*`3u!LoLTMam^=F%CyS*% zq7u@7MUw^ayM;&TAr7>#;oV$QS#6{6BgRn*G~&IDS(ciAZ81zwB2UHEm7O$gBwPj6 zMY%gU{C(_Aam%0?bVj9r zIV;^}1ShfT@YWhpi8=~*ZM(Cxz1c{();UA?W5UP+5&70U#Q}rUK}3cCTc>WY(`Vw< zJIj>pHxdM_3#bYR{<^fjK(8hJ3)5l6m=c(OHFueq8;hH0x1k6>Rw$iX4h94S{13+_ z{Lh8>7gX&8hDg{%1?Z}5a3lH5)XugcN|_S*n1%4b6DvyhS!8L4xN;K=X`-^l9MMwG z_TBE>oDW9WOiJkE+*oQzcum)cB7sqAr<9ANBnsck<%g}K3#P<1WKj=0Nhvc(KK7|L z;D8Cuo)xvl2=;6twNSSynnd+hH|JR<*NdH{?VFV@NHNop1JW}dNh@|^B6SsL91KW4 zB=7@%n3{EkP&$MB$#5e&8$M&`()~mY@%r0r3R*zmFz~T`a{oRs@#9a2QZ`1;{XtW( zJJBD!c;(KDRT#KUz)KYTP92IEk*PQpt}~%OFe+8&jdiLq=zrs8YNwGJKNY{WoR@B4 zmNQQDd{%Pj2KW+M4y4{9S8?oJ-dm$=voia_Dv)F!`$xKD+=Yr)2zV2NDntO&QD+{F~(m&3*ZCJ6XX~0q635Gi(c47H1=b? zMA6_-fMyo`)gyBM)wsA~AIDdPLZxbyES!+hk9;Kr88^}Xf5FxCnfxGXlyAydRu^NR(!3$Jdk7Qhc@~GjE|8tU9iQ zZz(C|I?V8eJq><4+ClnWx|z0<*!lCcw(CW!d_1IM#b99_9iR^BtbHXv+x-(+RxE3Z zFjP+f;;=a)j~Q3F{KNeN`hV}z?{Pr6?-7B3@Dk^CWr4L53HDR~a%uxi3ny1+Ct3Vk z&PMgC)GT83f#xCb0%+PS@M0_CZ=i8|OehdX1lFQxG) zugEYsm^+3cj6c6p@w@T2?nLnfyvq;K0-0%goC$6m1)LEqzds6n?ylu)PRqpL)y}yv zZg`gFqU~apXe+VyZ8QKQV+?m46A)EfFO;6|`}53tX{6TD8kX2>ZtKzxCPU!VLqb#ZwSECx^B`6Ut8qXr1nsu|F>9atU8 zrt#vjepot14(n|m!o8swWUm(Q7{4V?O24<->&#g96P7f_BG{$Z491G&^zJOXZOjbQ zH_RhB(hvuLz(QVWSa1WH+mT2X{S%(`h^}w8F5W4kCNXO)jXh5#0o~CgEQV>H$sUPy zBQS}oJ*%cXOTdP}zr*YOK>zVEOOha*lKZ2>^P|)A6|AF3ri4m`o_<7=lA`LwS>KG8 zTKquKlsiQ_(I^e?!NmUL+&hJtM0 z$A`}+{BW)(M@2X)wlw9B7G3xodC=kMcI`*(i!)_0HgV*E6KNj?DfJYTMXae0WkPsw z_9nw$#|XA06SF6(_GqDCa{WzmXD}I>D&xNWc>Mk5@zE3Dil4SSXt( zL%A@(2c%7#|K)rXILtf==jtm(1To|!)8k!|T7rsuwfG6v_6)0rCSOeCYB|U(~%TI}K zDv{9Y8e70dYpg|hc1)%hzQF%cSP4r?5xWL}zYG=XCa4rTO^q>ggJ&}3S7P-nDasDO ztU&D9ZYkRg!0Ov-DSNZnl2V{LC<>TGyQagA53LCZ8{1ZzdA>~2YDqMbz*pj;Z5Ev~ zJ4q{GS%as|u;Q`Y$l2M3TXJ%g!1u389RQwxSKe7keO8G8{%P(C!t{}))qh9)MJ@*@ z)Xu0vf^A~eTmK1e;~OTfWgp$|XEMukEeab5vj-iAZbTo%uu{7>k$98vBMiA$Y6cZA=Om(NtNIHfRq1QD@bx zi-Gnr@jE|mXGhtnX`FzF65q6>hQ$P^f}*C16s&~-p($wBzn-VF0Gk$IQODr~EM-<; z#RtRCMc!useHo5U_A_A$kF65O-0BzS)_3G1TR@_)L&&0hlAisIo%IPj`bF2vw^yJP zyzNJYk$eE|F8dpq0f~4^+%Gt$05b3>0E6$s9Ah7WwXvN)-^z`uK)Ic7d1439aW^#d zt_3OUyx(H1VGfTx`l373T59E0wc%`>OqErLl=hKbga=!it0GM_NNiELfH!(XRTi6h zLdDdSeCS-kL=);%O(m=f#BxQH?C&x;2?C5A$eyUO$h!O0=bB3giPK59g?-*<>|@kX z{ZndcN)4z^0;p%amb2c~usQ(#U1Thb3RruT$7`#)ecGNqVR~bJ!tE$>k|=C<(>_%# zKJSMS6~FIp*N-gd@eQ)w4kW!~22Uze1>cZxrwmA~~Yta(YSWDX*W!^v4wdbM)i z;3Wzt3zdBvSozg*^w!ec^|8I+^kv>0+yf(qAByX}-$FSqICiQ6X#sCyw!3rdABYz# zZ!F$l_J#*aJxPFu=RM7%v%vXR0gmgy5J;T|XKI1iY5#D~Tvvs=`B$$;v_VATx?1ZW z_Y;ad6%Ssux1-Q`(H_?C-$b3?jwOz*b+JLQ?WlDPi8N`r`PE5~e{F@Gh1|jYj^1`A zSWCsRH{N z@du;a`<-r><67>G>Y2H8-wyNz*Prxvta;y*6n%Q}wu=o$Q33wL=VDd&&W1j3@57Bt z(uVEKkmP%mLgUe6+P4r^eaS=7FB8dxfGw;}t@a0`ELwp~B;eFqJwLb~_pjsO@o~Vm z?$+wdE|#w%Wk4HTjJ0=Ke^JL>h2xuqEWt`7 z_p2*$7w7r=;!}O%yHBNX#J_mx*oE#4cM>7=*tHtzkVnFxE2?XH%tl|uIx!RQ;h#C( zDtX7nJ#K5uJTg{zCYHs1)8%?Yp7GOD;_L`esEk`hZ^ z`EcZYzfXFGejfY{0Wc<$EbuCp8Sh7DymApFS3`uQIf6iF7+@Hcz2Blwx)vpWVmg%+ z&&bG0ZeB7kkYTzWcO=H$I4g`5v4s@F@6sn+2>}L}J7H(ZdF-odZu59-%5@wz7bqt$j?1WPS6fQmhgqnci}cl0x05f zVS22&a}P|lF`UdcMi%HJC%IY{G3IDLSaVlb3HY#CPFC4VQSh(vi?A*40PW^yCXg8HK1H6C` zUEFfGDqk6{u+mFl;HE(gaYShig=gJZg#gi#MX?w=dqSkwCB+yubMzF%O=aNPc3~nb zi~YwOTcJVv7Pi%U{DvH7WfgbeOXvAF$zf3II3{7jD2aJ`2_=&umvz7(o&CZ}ba0B1 zf!FtlzOF`muPG(6lJKTl>CO&9tF8~8} z>|h#qWrz7f)xhGV0q^=dLEuX^HJrnpS^j2r!3d^}N1ph7l9BC4EkyT6g~eG=Kgl_l zg)8ASiwz0^h@$wYE4F+=1USXDP{kKnDXU;*=*^|DK{Wg;LqrhpQ83>J(@>m3pw>$v zG^JAitwN;ml}cH#f2c`k0Vf?en7FPik!!{F}oV( zE1dZ%cp3`!B#;djQZS|11D$1$n4H!k39&*R4PhRUwWjZH$XDUzEcB4zDGF`4>uTqy zU)6UQg?d7y^x_=PkXW>kfxk24L(v%v;?|RliABOOG6a8F!K+94jMYM~;zXMF0%g!doqU!w+>(KTzD{!${`fWHc zP8qP&@UvEr<=_A2ZU{67EM9-gS77Sd>3Zn;B-d_XlQyr7oo{g|cQ(`|>(}smBI;Xm zm|7-+D|hm!WcO&&c2#aO#sSVF>#H-JmeSkTqh*?D87oy%D^y_Py=$iERSBnUYGUH6 z&yK3s#FE~Q+-6WeBCJ$`*SB)59$4lTZM@rEtt_luZ6Ahs*D|uJ zwmN#)v<2JOKj|#}6q?IRsn}a*71_#lyI_<;NVYV@ggPHQ8r~e+I{=><*8GH^n0q7V z$C2-Y6<^6nX#rCws7qF`c;8S*C_m?xh7WI!E)W4Ham&|w_J)w?OI{Orn0IHxvQOhK zV9=(MgXeCDz2wxZpN2ioifLA#OaP9%GNZi#lD-=HPrPNn%XOXXay|kPS3YqJ{c>PN zUZZPIn)4fybju48Oh9y6`~>%9I#p*MF_O3 zu67aQxCA->A&Y4(hXY0LzMpZ|Fb*(^B)M%QMTd9)D@-ZcA;1XC4=EXg)zHXL>a0fO zQjPo0d{W6Y>HKj|C-8S(k!;8o5mDbu#qKcZHKo48^nv;a!?uKIhP1gTE<0pdqDWu{ zXo;ROh6OFk$uy9x_uER*F_CFl?LaCn1s(eaVi2BH7cNvd$0Dqv@$8u6P9RNOnGw=t zTC3yoOeUg%F`!zoH9QGC*_4(Lrknq^Ul%0k-#s3+lKGc<>AA)Z_O@`-fch*ZunDPYWGW&g>vgk>y=*84QSqCPto*>jlV+ zH~ebZ!0c(aZLNsPcZ-k-LNHB!-M)(Nq_X#5*);}CDWFC%sxL~gv~oW^!Rx-nsFEx) zc;30({<+r)Y7xe_C0N7X;9P8$U%?+MSBFv7H}156?6TfZh`^(0==5Pc=o6KHs4xi0 z+XzvpJHx-Ec&)?@*`=pAqZ}Vu8fQZe>G+#mw|+qJoqKD$)0ZKWo*25zAOT;oDoS5u zAUSZi1fa5*q|RJmfBe8~bgd-uYDyd`W#VpV7~~7}s9i=(0{l`zymRAFuD*>j5ZP3H zot%G&gwG`#fO)>@6%u-Jfo!pJ7BDO1(QUgCyxI38_TpREWhu;Dht0xk{2jcBK&#@0 z%W-J&V{rORyM5nS2G4V9h=X!pCrBn^DsiHF*N}x zs54#Qmg-AP{AN0ncFN2l+!sy)^m9UGy;K#0zC;@~wefAXe4%KpRs`p`w;&c7m%{&$ zG{#Q2T_3#E2l`@d%QZaXTW32A_1u{p;!6QxurDtIYlwvz?^8VX^I=t-c1$Q!n6J~} z3cmDEj=z;(`2J;JA^!tva5X%=6^^0W134A*s-e;-9rFW#$hX?gn={KdApGbp%q^PN zTeNbz0VlM6WBiZ0Pt9ou#z{nrqy6t0Wux+)1~AM;%R}H1Fc6R?*nhza|GkrVevbv1 zu-#xn>bjuCI48D zyLTe2UaSs(YWXA5P0jO-5L1ca+~fcd$`rgYtdLDF2gR&ed#$&p;`Dn8g61CC56A{L zkZYiXi94n5ECR#g4qMO>LBy5lIVg47D7EG9zj!;T(4z?`r~KuZWe!eP?uX~pVDgwn zDjjTtCjn!`8`4w2|2(`9;~lJ%){r(`45M^fa_a1)_yj|_e>wDCTb9L4Fc z1J}CIQElTAW*}YRJaM7xRO+EK5)6xOzF#eOV&ah9w$9ngKChxRZZQtv@;tkl=sM zwvoVrAyOl|fDr-NzyH5kBSM^1WF8GsHdu;aw-I^Ttst5k;2L zA(3+OWo2Q8`fwIWbm_}=no%PMb740)<#20@j_80KJ}SCm1ciuT%I=lI<^&$2XZSdj z-miM0_NM7E-HpYn13laFiZtwKadd6eDcKD=B^Z_RJRS(o1_||*mAD(A;|pz5g;N*e5xi`NcDDS)yi1C20-j~1 zJJyKg&?%aRTWpKA2f?mY)l1Czd<)C4o1ufsN?eV5SEiCCZFg+b3fxv>K;@09fe}8A zt{v!v5!bSX`zn`_7>$N>L0@XBS+j3HKN3*-^|e(HE`}sp#61Y53`^vA<+yfFFOK23 z!Ja0&{wpIv+a4uORi}Plvbpu|H@-S_w&k)S6}Wo5t~TyASV+z(;;6@_ACll#QDRot z*w{X?(|*mO_{;Q#94U2%tJJnz1!@`ZObL-)IT7tLsKSM()G$lXbW~RV@vGlwZ63fD z$i4&_WP;G?^Lxzq$2%;N{#;bn^u5=UoqG2}65(f{BF91DdUb{((XX)&HxnXW<9#=w z64gHt##&4b4&>hh!_jbjc<32M>8-IE z))P?%t!LN)RmkEKHn~t%nCbLEjDQG(eB@jT4-~af@=Vm1ZL00p@!MkZqR1zvx?;4D z$du@gE!cb4iSZezJ&L)wnM^8*UlKIW)H#u5ndmX&A<<&YQjQ5-{k3BEaqvTWaR64Q zsC*M0VuJ8K#4N_66tx=Oe(oT&*W`V91Q*>F=?4k+UKRQYh~KJ|!-ZO)z5tQzE;VJ$ z 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 DcxitM From 8dcf8f3c6ca9193a30850c09060b8a7e227000f5 Mon Sep 17 00:00:00 2001 From: Hiroyuki Nakazato Date: Tue, 14 Oct 2025 15:59:06 +0900 Subject: [PATCH 07/17] feat: add E2E test for Switch transpiler with environment variable control Add test_switch_install_with_transpile for full workflow testing including job execution and output verification. Test automatically skips without LAKEBRIDGE_SWITCH_E2E=true environment variable. Refactor existing test to test_switch_install and extract helper functions for DRY implementation. All changes in single file with no CI impact. --- tests/integration/transpile/test_switch.py | 138 +++++++++++++++++---- 1 file changed, 117 insertions(+), 21 deletions(-) diff --git a/tests/integration/transpile/test_switch.py b/tests/integration/transpile/test_switch.py index 49531f460a..708bebc0be 100644 --- a/tests/integration/transpile/test_switch.py +++ b/tests/integration/transpile/test_switch.py @@ -1,5 +1,10 @@ +import os +from dataclasses import dataclass from pathlib import Path +from typing import cast +import pytest +from databricks.labs.blueprint.installation import Installation, RootJsonValue from databricks.labs.blueprint.installer import InstallState from databricks.labs.blueprint.wheels import ProductInfo from databricks.sdk import WorkspaceClient @@ -11,46 +16,128 @@ from databricks.labs.lakebridge.deployment.switch import SwitchDeployment from databricks.labs.lakebridge.transpiler.installers import SwitchInstaller from databricks.labs.lakebridge.transpiler.repository import TranspilerRepository -from databricks.labs.lakebridge.transpiler.switch_runner import SwitchConfig +from databricks.labs.lakebridge.transpiler.switch_runner import SwitchConfig, SwitchRunner -def test_switch_installation(ws: WorkspaceClient, switch_artifact: Path): - """Test Switch installation, job creation, resource persistence, and cleanup.""" +@dataclass +class SwitchSetup: + """Setup results for Switch installation and deployment.""" + + installation: Installation + switch_deployment: SwitchDeployment + job_id: int + + +def test_switch_install(ws: WorkspaceClient, switch_artifact: Path): + """Test Switch install: installation, job deployment, and resource verification.""" + setup = _setup_switch(ws, switch_artifact, "test_catalog", "test_schema", "test_volume") + + try: + install_state = InstallState.from_installation(setup.installation) + _verify_resource_persistence(install_state) + _verify_job_id_retrieval(install_state, setup.job_id) + finally: + _cleanup_switch(setup.switch_deployment, setup.installation) + + +def test_switch_install_with_transpile(ws: WorkspaceClient, switch_artifact: Path): + """Test Switch install with transpile: full workflow including job execution and output verification.""" + if os.getenv("LAKEBRIDGE_SWITCH_E2E") != "true": + pytest.skip("E2E test requires LAKEBRIDGE_SWITCH_E2E=true") + + e2e_catalog = os.getenv("LAKEBRIDGE_SWITCH_E2E_CATALOG", "main") + e2e_schema = os.getenv("LAKEBRIDGE_SWITCH_E2E_SCHEMA", "lakebridge_switch_e2e") + e2e_volume = os.getenv("LAKEBRIDGE_SWITCH_E2E_VOLUME", "switch_volume") + setup = _setup_switch(ws, switch_artifact, e2e_catalog, e2e_schema, e2e_volume) + + try: + current_user = ws.current_user.me().user_name + output_folder = f"/Workspace/Users/{current_user}/switch_e2e_output" + run_results = _run_switch_transpile( + ws, setup.installation, output_folder, e2e_catalog, e2e_schema, e2e_volume, setup.job_id + ) + _verify_transpile_results(run_results, setup.job_id) + _verify_output_notebook(ws, f"{output_folder}/test_ctas_simple.py") + + finally: + _cleanup_switch(setup.switch_deployment, setup.installation) + + +def _setup_switch(ws: WorkspaceClient, switch_artifact: Path, catalog: str, schema: str, volume: str) -> SwitchSetup: + """Setup Switch: local installation, workspace deployment, and job creation.""" context = ApplicationContext(ws) installation = context.installation install_state = InstallState.from_installation(installation) transpiler_repository = TranspilerRepository.user_home() - # Phase 1: Local installation + # Local installation installer = SwitchInstaller(transpiler_repository) - result = installer.install(switch_artifact) - assert result, "Switch local installation failed" + installer.install(switch_artifact) - # Phase 2: Workspace deployment + # Workspace deployment product_info = ProductInfo.from_class(SwitchDeployment) job_deployer = JobDeployment(ws, installation, install_state, product_info) switch_deployment = SwitchDeployment( ws, installation, install_state, product_info, job_deployer, transpiler_repository ) - resources = SwitchResourcesConfig(catalog="test_catalog", schema="test_schema", volume="test_volume") + resources = SwitchResourcesConfig(catalog=catalog, schema=schema, volume=volume) switch_deployment.install(resources) - try: - install_state = InstallState.from_installation(installation) - job_id = _verify_job_creation(ws, install_state) - _verify_resource_persistence(install_state) - _verify_job_id_retrieval(install_state, job_id) - finally: - try: - switch_deployment.uninstall() - installation.remove() - except NotFound: - pass + # Verify job creation + install_state = InstallState.from_installation(installation) + job_id = _get_and_verify_job_id(ws, install_state) + + return SwitchSetup(installation=installation, switch_deployment=switch_deployment, job_id=job_id) + + +def _run_switch_transpile( + ws: WorkspaceClient, + installation: Installation, + output_folder: str, + catalog: str, + schema: str, + volume: str, + job_id: int, +) -> RootJsonValue: + """Run Switch transpile job with test SQL and return results.""" + test_sql_path = ( + Path(__file__).parent.parent.parent / "resources" / "functional" / "snowflake" / "ddl" / "test_ctas_simple.sql" + ) + + runner = SwitchRunner(ws, installation) + return runner.run( + input_source=str(test_sql_path), + output_ws_folder=output_folder, + source_dialect="snowflake", + catalog=catalog, + schema=schema, + volume=volume, + job_id=job_id, + switch_options={}, + wait_for_completion=True, + ) -def _verify_job_creation(ws: WorkspaceClient, install_state: InstallState): - """Verify job creation and registration.""" +def _verify_transpile_results(run_results: RootJsonValue, expected_job_id: int) -> None: + """Verify Switch transpile job execution results.""" + assert isinstance(run_results, list) + assert len(run_results) == 1 + result = cast(dict, run_results[0]) + assert result.get("job_id") == expected_job_id + assert result.get("state") == "TERMINATED" + assert result.get("result_state") == "SUCCESS" + + +def _verify_output_notebook(ws: WorkspaceClient, output_path: str) -> None: + """Verify output notebook creation.""" + notebook_info = ws.workspace.get_status(output_path) + assert notebook_info.object_type is not None + assert notebook_info.object_type.value == "NOTEBOOK" + + +def _get_and_verify_job_id(ws: WorkspaceClient, install_state: InstallState): + """Get job ID and verify job creation and registration.""" assert "Switch" in install_state.jobs job_id = int(install_state.jobs["Switch"]) @@ -92,3 +179,12 @@ def _verify_job_id_retrieval(install_state: InstallState, expected_job_id: int): assert isinstance(job_id_from_config, int) assert job_id_from_config > 0 assert job_id_from_config == expected_job_id + + +def _cleanup_switch(switch_deployment: SwitchDeployment, installation: Installation) -> None: + """Cleanup Switch deployment and installation state.""" + try: + switch_deployment.uninstall() + installation.remove() + except NotFound: + pass From 83678b8a450bf848cc416df7d3dd156b50baa8ae Mon Sep 17 00:00:00 2001 From: Hiroyuki Nakazato Date: Tue, 14 Oct 2025 17:18:37 +0900 Subject: [PATCH 08/17] feat: enhance E2E testing for Switch with resource management and unique suffix generation --- tests/integration/transpile/test_switch.py | 151 +++++++++++++++------ 1 file changed, 112 insertions(+), 39 deletions(-) diff --git a/tests/integration/transpile/test_switch.py b/tests/integration/transpile/test_switch.py index 708bebc0be..0e688e2c33 100644 --- a/tests/integration/transpile/test_switch.py +++ b/tests/integration/transpile/test_switch.py @@ -1,5 +1,8 @@ import os +import random +import string from dataclasses import dataclass +from datetime import datetime from pathlib import Path from typing import cast @@ -14,10 +17,34 @@ from databricks.labs.lakebridge.contexts.application import ApplicationContext from databricks.labs.lakebridge.deployment.job import JobDeployment from databricks.labs.lakebridge.deployment.switch import SwitchDeployment +from databricks.labs.lakebridge.helpers.metastore import CatalogOperations from databricks.labs.lakebridge.transpiler.installers import SwitchInstaller from databricks.labs.lakebridge.transpiler.repository import TranspilerRepository from databricks.labs.lakebridge.transpiler.switch_runner import SwitchConfig, SwitchRunner +E2E_FLAG = "LAKEBRIDGE_SWITCH_E2E" +E2E_CATALOG_ENV = "LAKEBRIDGE_SWITCH_E2E_CATALOG" +E2E_SCHEMA_ENV = "LAKEBRIDGE_SWITCH_E2E_SCHEMA" +E2E_VOLUME_ENV = "LAKEBRIDGE_SWITCH_E2E_VOLUME" +DEFAULT_E2E_CATALOG = "main" +DEFAULT_E2E_SCHEMA_PREFIX = "e2e_lakebridge_switch" +DEFAULT_E2E_VOLUME = "switch_volume" +OUTPUT_NOTEBOOK_NAME = "test_ctas_simple" +TEST_SQL_FILE = ( + Path(__file__).parent.parent.parent / "resources" / "functional" / "snowflake" / "ddl" / "test_ctas_simple.sql" +) + + +@dataclass(frozen=True) +class E2EResources: + """Resolved resources used by the end-to-end transpile test.""" + + catalog: str + schema_prefix: str + schema: str + volume: str + output_folder: str + @dataclass class SwitchSetup: @@ -42,25 +69,58 @@ def test_switch_install(ws: WorkspaceClient, switch_artifact: Path): def test_switch_install_with_transpile(ws: WorkspaceClient, switch_artifact: Path): """Test Switch install with transpile: full workflow including job execution and output verification.""" - if os.getenv("LAKEBRIDGE_SWITCH_E2E") != "true": + if os.getenv(E2E_FLAG) != "true": pytest.skip("E2E test requires LAKEBRIDGE_SWITCH_E2E=true") - e2e_catalog = os.getenv("LAKEBRIDGE_SWITCH_E2E_CATALOG", "main") - e2e_schema = os.getenv("LAKEBRIDGE_SWITCH_E2E_SCHEMA", "lakebridge_switch_e2e") - e2e_volume = os.getenv("LAKEBRIDGE_SWITCH_E2E_VOLUME", "switch_volume") - setup = _setup_switch(ws, switch_artifact, e2e_catalog, e2e_schema, e2e_volume) + resources = _resolve_e2e_resources(ws) + catalog_ops = CatalogOperations(ws) + catalog_ops.create_schema(resources.schema, resources.catalog) + catalog_ops.create_volume(resources.catalog, resources.schema, resources.volume) + + setup = _setup_switch(ws, switch_artifact, resources.catalog, resources.schema, resources.volume) try: - current_user = ws.current_user.me().user_name - output_folder = f"/Workspace/Users/{current_user}/switch_e2e_output" run_results = _run_switch_transpile( - ws, setup.installation, output_folder, e2e_catalog, e2e_schema, e2e_volume, setup.job_id + ws, + setup.installation, + resources.output_folder, + resources.catalog, + resources.schema, + resources.volume, + setup.job_id, ) _verify_transpile_results(run_results, setup.job_id) - _verify_output_notebook(ws, f"{output_folder}/test_ctas_simple.py") - + notebook_path = f"{resources.output_folder}/{OUTPUT_NOTEBOOK_NAME}" + _verify_output_notebook(ws, notebook_path) finally: _cleanup_switch(setup.switch_deployment, setup.installation) + _drop_schema(ws, resources.catalog, resources.schema) + _cleanup_output_folder(ws, resources.output_folder) + + +def _generate_unique_suffix() -> str: + """Generate unique suffix using timestamp + random string for parallel execution safety.""" + time_part = datetime.now().strftime("%Y%m%d%H%M%S") + random_part = "".join(random.choices(string.ascii_lowercase + string.digits, k=4)) + return f"{time_part}_{random_part}" + + +def _resolve_e2e_resources(ws: WorkspaceClient) -> E2EResources: + """Resolve catalog/schema/volume and output folder for the end-to-end test.""" + catalog = os.getenv(E2E_CATALOG_ENV, DEFAULT_E2E_CATALOG) + schema_prefix = os.getenv(E2E_SCHEMA_ENV, DEFAULT_E2E_SCHEMA_PREFIX) + volume = os.getenv(E2E_VOLUME_ENV, DEFAULT_E2E_VOLUME) + unique_suffix = _generate_unique_suffix() + schema = f"{schema_prefix}_{unique_suffix}" + current_user = ws.current_user.me().user_name + output_folder = f"/Workspace/Users/{current_user}/.{schema_prefix}_{unique_suffix}" + return E2EResources( + catalog=catalog, + schema_prefix=schema_prefix, + schema=schema, + volume=volume, + output_folder=output_folder, + ) def _setup_switch(ws: WorkspaceClient, switch_artifact: Path, catalog: str, schema: str, volume: str) -> SwitchSetup: @@ -101,13 +161,9 @@ def _run_switch_transpile( job_id: int, ) -> RootJsonValue: """Run Switch transpile job with test SQL and return results.""" - test_sql_path = ( - Path(__file__).parent.parent.parent / "resources" / "functional" / "snowflake" / "ddl" / "test_ctas_simple.sql" - ) - runner = SwitchRunner(ws, installation) return runner.run( - input_source=str(test_sql_path), + input_source=str(TEST_SQL_FILE), output_ws_folder=output_folder, source_dialect="snowflake", catalog=catalog, @@ -121,54 +177,55 @@ def _run_switch_transpile( def _verify_transpile_results(run_results: RootJsonValue, expected_job_id: int) -> None: """Verify Switch transpile job execution results.""" - assert isinstance(run_results, list) - assert len(run_results) == 1 + assert isinstance(run_results, list), f"Unexpected results type: {type(run_results)}" + assert len(run_results) == 1, f"Expected single run result, got {len(run_results)}" result = cast(dict, run_results[0]) - assert result.get("job_id") == expected_job_id - assert result.get("state") == "TERMINATED" - assert result.get("result_state") == "SUCCESS" + assert result.get("job_id") == expected_job_id, result + assert result.get("state") == "TERMINATED", result + assert result.get("result_state") == "SUCCESS", result def _verify_output_notebook(ws: WorkspaceClient, output_path: str) -> None: """Verify output notebook creation.""" notebook_info = ws.workspace.get_status(output_path) - assert notebook_info.object_type is not None - assert notebook_info.object_type.value == "NOTEBOOK" + assert notebook_info.object_type is not None, f"Notebook not found at {output_path}" + assert notebook_info.object_type.value == "NOTEBOOK", f"Unexpected object type for {output_path}: {notebook_info}" def _get_and_verify_job_id(ws: WorkspaceClient, install_state: InstallState): """Get job ID and verify job creation and registration.""" - assert "Switch" in install_state.jobs + assert "Switch" in install_state.jobs, "Switch job not persisted in install state" job_id = int(install_state.jobs["Switch"]) job = ws.jobs.get(job_id) - assert job is not None - assert job.settings is not None - assert job.settings.name is not None - assert "switch" in job.settings.name.lower() + assert job is not None, f"Job {job_id} could not be retrieved" + assert job.settings is not None, f"Job {job_id} missing settings" + assert job.settings.name is not None, f"Job {job_id} missing name" + assert "switch" in job.settings.name.lower(), f"Unexpected job name: {job.settings.name}" assert job.settings.tasks is not None assert len(job.settings.tasks) > 0 task = job.settings.tasks[0] - assert task.notebook_task is not None - assert "switch" in task.notebook_task.notebook_path.lower() + assert task.notebook_task is not None, "First task is not a notebook task" + assert ( + "switch" in task.notebook_task.notebook_path.lower() + ), f"Unexpected notebook path: {task.notebook_task.notebook_path}" return job_id def _verify_resource_persistence(install_state: InstallState): """Verify resource persistence.""" - assert install_state.switch_resources is not None + assert install_state.switch_resources is not None, "Switch resources missing from install state" resources = install_state.switch_resources - assert "catalog" in resources - assert "schema" in resources - assert "volume" in resources + for field in ("catalog", "schema", "volume"): + assert field in resources, f"Switch resource missing '{field}' entry" switch_config = SwitchConfig(install_state) retrieved_resources = switch_config.get_resources() - assert retrieved_resources["catalog"] == "test_catalog" - assert retrieved_resources["schema"] == "test_schema" - assert retrieved_resources["volume"] == "test_volume" + assert retrieved_resources["catalog"] == "test_catalog", retrieved_resources + assert retrieved_resources["schema"] == "test_schema", retrieved_resources + assert retrieved_resources["volume"] == "test_volume", retrieved_resources def _verify_job_id_retrieval(install_state: InstallState, expected_job_id: int): @@ -176,9 +233,25 @@ def _verify_job_id_retrieval(install_state: InstallState, expected_job_id: int): switch_config = SwitchConfig(install_state) job_id_from_config = switch_config.get_job_id() - assert isinstance(job_id_from_config, int) - assert job_id_from_config > 0 - assert job_id_from_config == expected_job_id + assert isinstance(job_id_from_config, int), f"Invalid job id type: {type(job_id_from_config)}" + assert job_id_from_config > 0, f"Job id should be positive: {job_id_from_config}" + assert job_id_from_config == expected_job_id, f"Job id mismatch: {job_id_from_config} != {expected_job_id}" + + +def _cleanup_output_folder(ws: WorkspaceClient, output_folder: str) -> None: + """Remove generated notebooks from the workspace if they exist.""" + try: + ws.workspace.delete(output_folder, recursive=True) + except NotFound: + pass + + +def _drop_schema(ws: WorkspaceClient, catalog: str, schema: str) -> None: + """Drop the temporary schema used during the test.""" + try: + ws.schemas.delete(f"{catalog}.{schema}", force=True) + except NotFound: + pass def _cleanup_switch(switch_deployment: SwitchDeployment, installation: Installation) -> None: From 7f0eaa4b43c652cc8c605c404f7f812c22715151 Mon Sep 17 00:00:00 2001 From: sundarshankar89 Date: Mon, 27 Oct 2025 17:21:56 +0530 Subject: [PATCH 09/17] Rebased from switch installer integration --- pyproject.toml | 2 +- src/databricks/labs/lakebridge/cli.py | 2 +- src/databricks/labs/lakebridge/config.py | 8 - .../labs/lakebridge/contexts/application.py | 1 - src/databricks/labs/lakebridge/install.py | 40 +-- .../labs/lakebridge/transpiler/installers.py | 15 - tests/integration/transpile/test_switch.py | 263 ------------------ ...ricks_switch_plugin-0.1.2-py3-none-any.whl | Bin 116597 -> 0 bytes tests/unit/deployment/test_installation.py | 2 - tests/unit/test_install.py | 5 +- tests/unit/transpiler/test_installers.py | 38 +-- 11 files changed, 13 insertions(+), 363 deletions(-) delete mode 100644 tests/integration/transpile/test_switch.py delete mode 100644 tests/resources/transpiler_configs/switch/wheel/databricks_switch_plugin-0.1.2-py3-none-any.whl diff --git a/pyproject.toml b/pyproject.toml index 7a102a0125..525ab4d381 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -449,7 +449,7 @@ bad-functions = ["map", "input"] # ignored-parents = # Maximum number of arguments for function / method. -max-args = 13 +max-args = 12 # Maximum number of attributes for a class (see R0902). max-attributes = 13 diff --git a/src/databricks/labs/lakebridge/cli.py b/src/databricks/labs/lakebridge/cli.py index 35f61ba5e2..7428a7fa71 100644 --- a/src/databricks/labs/lakebridge/cli.py +++ b/src/databricks/labs/lakebridge/cli.py @@ -86,7 +86,7 @@ def _remove_warehouse(ws: WorkspaceClient, warehouse_id: str): @lakebridge.command -def transpile( # pylint: disable=too-many-arguments +def transpile( *, w: WorkspaceClient, transpiler_config_path: str | None = None, diff --git a/src/databricks/labs/lakebridge/config.py b/src/databricks/labs/lakebridge/config.py index c3380a23cc..a27e7b13d8 100644 --- a/src/databricks/labs/lakebridge/config.py +++ b/src/databricks/labs/lakebridge/config.py @@ -154,13 +154,6 @@ def prompt_for_value(self, prompts: Prompts) -> JsonValue: raise ValueError(f"Unsupported prompt method: {self.method}") -@dataclass -class SwitchResourcesConfig: - catalog: str - schema: str - volume: str - - @dataclass class TranspileConfig: __file__ = "config.yml" @@ -177,7 +170,6 @@ class TranspileConfig: catalog_name: str = "remorph" schema_name: str = "transpiler" transpiler_options: JsonValue = None - switch_resources: SwitchResourcesConfig | None = None @property def transpiler_config_path_parsed(self) -> Path | None: diff --git a/src/databricks/labs/lakebridge/contexts/application.py b/src/databricks/labs/lakebridge/contexts/application.py index 270717a7bd..b95be98de0 100644 --- a/src/databricks/labs/lakebridge/contexts/application.py +++ b/src/databricks/labs/lakebridge/contexts/application.py @@ -20,7 +20,6 @@ from databricks.labs.lakebridge.deployment.recon import TableDeployment, JobDeployment, ReconDeployment from databricks.labs.lakebridge.deployment.switch import SwitchDeployment from databricks.labs.lakebridge.helpers.metastore import CatalogOperations -from databricks.labs.lakebridge.transpiler.repository import TranspilerRepository logger = logging.getLogger(__name__) diff --git a/src/databricks/labs/lakebridge/install.py b/src/databricks/labs/lakebridge/install.py index a7692788cd..cb47a8bf46 100644 --- a/src/databricks/labs/lakebridge/install.py +++ b/src/databricks/labs/lakebridge/install.py @@ -21,7 +21,6 @@ LakebridgeConfiguration, ReconcileMetadataConfig, TranspileConfig, - SwitchResourcesConfig, ) from databricks.labs.lakebridge.contexts.application import ApplicationContext from databricks.labs.lakebridge.deployment.configurator import ResourceConfigurator @@ -30,7 +29,6 @@ from databricks.labs.lakebridge.transpiler.installers import ( BladebridgeInstaller, MorpheusInstaller, - SwitchInstaller, TranspilerInstaller, ) from databricks.labs.lakebridge.transpiler.repository import TranspilerRepository @@ -42,7 +40,7 @@ class WorkspaceInstaller: # TODO: Temporary suppression, is_interactive is pending removal. - def __init__( # pylint: disable=too-many-arguments + def __init__( self, ws: WorkspaceClient, prompts: Prompts, @@ -59,7 +57,6 @@ def __init__( # pylint: disable=too-many-arguments transpiler_installers: Sequence[Callable[[TranspilerRepository], TranspilerInstaller]] = ( BladebridgeInstaller, MorpheusInstaller, - SwitchInstaller, ), ): self._ws = ws @@ -84,12 +81,7 @@ def __init__( # pylint: disable=too-many-arguments @property def _transpiler_installers(self) -> Set[TranspilerInstaller]: - factories = self._transpiler_installer_factories - if not self._include_llm: - if SwitchInstaller in factories: - logger.info("Skipping Switch installation (LLM transpiler not requested)") - factories = tuple(f for f in factories if f != SwitchInstaller) - return frozenset(factory(self._transpiler_repository) for factory in factories) + return frozenset(factory(self._transpiler_repository) for factory in self._transpiler_installer_factories) def run( self, @@ -104,7 +96,7 @@ def run( for transpiler_installer in self._transpiler_installers: transpiler_installer.install() if not config: - config = self.configure(module, self._include_llm) + config = self.configure(module) if self._is_testing(): return config self._ws_installation.install(config) @@ -141,7 +133,7 @@ def _install_artifact(self, artifact: str) -> None: else: logger.fatal(f"Cannot install unsupported artifact: {artifact}") - def configure(self, module: str, include_llm: bool = False) -> LakebridgeConfiguration: + def configure(self, module: str) -> LakebridgeConfiguration: match module: case "transpile": logger.info("Configuring lakebridge `transpile`.") @@ -154,7 +146,7 @@ def configure(self, module: str, include_llm: bool = False) -> LakebridgeConfigu case "all": logger.info("Configuring lakebridge `transpile` and `reconcile`.") return LakebridgeConfiguration( - self._configure_transpile(include_llm), + self._configure_transpile(), self._configure_reconcile(), include_switch=self._include_llm, ) @@ -164,7 +156,7 @@ def configure(self, module: str, include_llm: bool = False) -> LakebridgeConfigu def _is_testing(self): return self._product_info.product_name() != "lakebridge" - def _configure_transpile(self, include_llm: bool = False) -> TranspileConfig | None: + def _configure_transpile(self) -> TranspileConfig | None: try: config = self._installation.load(TranspileConfig) logger.info("Lakebridge `transpile` is already installed on this workspace.") @@ -185,12 +177,12 @@ def _configure_transpile(self, include_llm: bool = False) -> TranspileConfig | N logger.warning("Installation is not interactive, skipping configuration of transpilers.") return None - config = self._configure_new_transpile_installation(include_llm) + config = self._configure_new_transpile_installation() logger.info("Finished configuring lakebridge `transpile`.") return config - def _configure_new_transpile_installation(self, include_llm: bool = False) -> TranspileConfig: - default_config = self._prompt_for_new_transpile_installation(include_llm) + def _configure_new_transpile_installation(self) -> TranspileConfig: + default_config = self._prompt_for_new_transpile_installation() runtime_config = None catalog_name = "remorph" schema_name = "transpiler" @@ -293,20 +285,14 @@ def _prompt_for_new_transpile_installation(self) -> TranspileConfig: "Would you like to validate the syntax and semantics of the transpiled queries?" ) - switch_resources = None - if include_llm: - switch_resources = self._prompt_for_switch_resources() - return TranspileConfig( transpiler_config_path=str(transpiler_config_path) if transpiler_config_path is not None else None, transpiler_options=transpiler_options, source_dialect=source_dialect, skip_validation=(not run_validation), - include_llm=include_llm, input_source=input_source, output_folder=output_folder, error_file_path=error_file_path, - switch_resources=switch_resources, ) def _get_transpiler_config(self, transpiler_names: list[str]) -> tuple[str, Path] | None: @@ -333,14 +319,6 @@ def _prompt_for_transpiler_options(self, transpiler_name: str, source_dialect: s # - There is no way to express 'provide a value later'. return {option.flag: option.prompt_for_value(self._prompts) for option in config_options} - def _prompt_for_switch_resources(self) -> SwitchResourcesConfig: - logger.info("Configuring Switch resources (catalog, schema, volume)...") - catalog = self._resource_configurator.prompt_for_catalog_setup() - schema = self._resource_configurator.prompt_for_schema_setup(catalog, "switch") - volume = self._resource_configurator.prompt_for_volume_setup(catalog, schema, "switch_volume") - self._has_necessary_access(catalog, schema, volume) - return SwitchResourcesConfig(catalog=catalog, schema=schema, volume=volume) - def _configure_catalog(self) -> str: return self._resource_configurator.prompt_for_catalog_setup() diff --git a/src/databricks/labs/lakebridge/transpiler/installers.py b/src/databricks/labs/lakebridge/transpiler/installers.py index 574e856241..edc59fc6e5 100644 --- a/src/databricks/labs/lakebridge/transpiler/installers.py +++ b/src/databricks/labs/lakebridge/transpiler/installers.py @@ -564,18 +564,3 @@ def _parse_java_version(cls, version: str) -> tuple[int, int, int, int] | None: update = int(match["update"] or 0) patch = int(match["patch"] or 0) return feature, interim, update, patch - - -class SwitchInstaller(TranspilerInstaller): - @property - def name(self) -> str: - return "Switch" - - def can_install(self, artifact: Path) -> bool: - return "databricks_switch_plugin" in artifact.name and artifact.suffix == ".whl" - - def install(self, artifact: Path | None = None) -> bool: - local_name = "switch" - pypi_name = "databricks-switch-plugin" - wheel_installer = WheelInstaller(self._transpiler_repository, local_name, pypi_name, artifact) - return wheel_installer.install() is not None diff --git a/tests/integration/transpile/test_switch.py b/tests/integration/transpile/test_switch.py deleted file mode 100644 index 0e688e2c33..0000000000 --- a/tests/integration/transpile/test_switch.py +++ /dev/null @@ -1,263 +0,0 @@ -import os -import random -import string -from dataclasses import dataclass -from datetime import datetime -from pathlib import Path -from typing import cast - -import pytest -from databricks.labs.blueprint.installation import Installation, RootJsonValue -from databricks.labs.blueprint.installer import InstallState -from databricks.labs.blueprint.wheels import ProductInfo -from databricks.sdk import WorkspaceClient -from databricks.sdk.errors import NotFound - -from databricks.labs.lakebridge.config import SwitchResourcesConfig -from databricks.labs.lakebridge.contexts.application import ApplicationContext -from databricks.labs.lakebridge.deployment.job import JobDeployment -from databricks.labs.lakebridge.deployment.switch import SwitchDeployment -from databricks.labs.lakebridge.helpers.metastore import CatalogOperations -from databricks.labs.lakebridge.transpiler.installers import SwitchInstaller -from databricks.labs.lakebridge.transpiler.repository import TranspilerRepository -from databricks.labs.lakebridge.transpiler.switch_runner import SwitchConfig, SwitchRunner - -E2E_FLAG = "LAKEBRIDGE_SWITCH_E2E" -E2E_CATALOG_ENV = "LAKEBRIDGE_SWITCH_E2E_CATALOG" -E2E_SCHEMA_ENV = "LAKEBRIDGE_SWITCH_E2E_SCHEMA" -E2E_VOLUME_ENV = "LAKEBRIDGE_SWITCH_E2E_VOLUME" -DEFAULT_E2E_CATALOG = "main" -DEFAULT_E2E_SCHEMA_PREFIX = "e2e_lakebridge_switch" -DEFAULT_E2E_VOLUME = "switch_volume" -OUTPUT_NOTEBOOK_NAME = "test_ctas_simple" -TEST_SQL_FILE = ( - Path(__file__).parent.parent.parent / "resources" / "functional" / "snowflake" / "ddl" / "test_ctas_simple.sql" -) - - -@dataclass(frozen=True) -class E2EResources: - """Resolved resources used by the end-to-end transpile test.""" - - catalog: str - schema_prefix: str - schema: str - volume: str - output_folder: str - - -@dataclass -class SwitchSetup: - """Setup results for Switch installation and deployment.""" - - installation: Installation - switch_deployment: SwitchDeployment - job_id: int - - -def test_switch_install(ws: WorkspaceClient, switch_artifact: Path): - """Test Switch install: installation, job deployment, and resource verification.""" - setup = _setup_switch(ws, switch_artifact, "test_catalog", "test_schema", "test_volume") - - try: - install_state = InstallState.from_installation(setup.installation) - _verify_resource_persistence(install_state) - _verify_job_id_retrieval(install_state, setup.job_id) - finally: - _cleanup_switch(setup.switch_deployment, setup.installation) - - -def test_switch_install_with_transpile(ws: WorkspaceClient, switch_artifact: Path): - """Test Switch install with transpile: full workflow including job execution and output verification.""" - if os.getenv(E2E_FLAG) != "true": - pytest.skip("E2E test requires LAKEBRIDGE_SWITCH_E2E=true") - - resources = _resolve_e2e_resources(ws) - catalog_ops = CatalogOperations(ws) - catalog_ops.create_schema(resources.schema, resources.catalog) - catalog_ops.create_volume(resources.catalog, resources.schema, resources.volume) - - setup = _setup_switch(ws, switch_artifact, resources.catalog, resources.schema, resources.volume) - - try: - run_results = _run_switch_transpile( - ws, - setup.installation, - resources.output_folder, - resources.catalog, - resources.schema, - resources.volume, - setup.job_id, - ) - _verify_transpile_results(run_results, setup.job_id) - notebook_path = f"{resources.output_folder}/{OUTPUT_NOTEBOOK_NAME}" - _verify_output_notebook(ws, notebook_path) - finally: - _cleanup_switch(setup.switch_deployment, setup.installation) - _drop_schema(ws, resources.catalog, resources.schema) - _cleanup_output_folder(ws, resources.output_folder) - - -def _generate_unique_suffix() -> str: - """Generate unique suffix using timestamp + random string for parallel execution safety.""" - time_part = datetime.now().strftime("%Y%m%d%H%M%S") - random_part = "".join(random.choices(string.ascii_lowercase + string.digits, k=4)) - return f"{time_part}_{random_part}" - - -def _resolve_e2e_resources(ws: WorkspaceClient) -> E2EResources: - """Resolve catalog/schema/volume and output folder for the end-to-end test.""" - catalog = os.getenv(E2E_CATALOG_ENV, DEFAULT_E2E_CATALOG) - schema_prefix = os.getenv(E2E_SCHEMA_ENV, DEFAULT_E2E_SCHEMA_PREFIX) - volume = os.getenv(E2E_VOLUME_ENV, DEFAULT_E2E_VOLUME) - unique_suffix = _generate_unique_suffix() - schema = f"{schema_prefix}_{unique_suffix}" - current_user = ws.current_user.me().user_name - output_folder = f"/Workspace/Users/{current_user}/.{schema_prefix}_{unique_suffix}" - return E2EResources( - catalog=catalog, - schema_prefix=schema_prefix, - schema=schema, - volume=volume, - output_folder=output_folder, - ) - - -def _setup_switch(ws: WorkspaceClient, switch_artifact: Path, catalog: str, schema: str, volume: str) -> SwitchSetup: - """Setup Switch: local installation, workspace deployment, and job creation.""" - context = ApplicationContext(ws) - installation = context.installation - install_state = InstallState.from_installation(installation) - transpiler_repository = TranspilerRepository.user_home() - - # Local installation - installer = SwitchInstaller(transpiler_repository) - installer.install(switch_artifact) - - # Workspace deployment - product_info = ProductInfo.from_class(SwitchDeployment) - job_deployer = JobDeployment(ws, installation, install_state, product_info) - switch_deployment = SwitchDeployment( - ws, installation, install_state, product_info, job_deployer, transpiler_repository - ) - - resources = SwitchResourcesConfig(catalog=catalog, schema=schema, volume=volume) - switch_deployment.install(resources) - - # Verify job creation - install_state = InstallState.from_installation(installation) - job_id = _get_and_verify_job_id(ws, install_state) - - return SwitchSetup(installation=installation, switch_deployment=switch_deployment, job_id=job_id) - - -def _run_switch_transpile( - ws: WorkspaceClient, - installation: Installation, - output_folder: str, - catalog: str, - schema: str, - volume: str, - job_id: int, -) -> RootJsonValue: - """Run Switch transpile job with test SQL and return results.""" - runner = SwitchRunner(ws, installation) - return runner.run( - input_source=str(TEST_SQL_FILE), - output_ws_folder=output_folder, - source_dialect="snowflake", - catalog=catalog, - schema=schema, - volume=volume, - job_id=job_id, - switch_options={}, - wait_for_completion=True, - ) - - -def _verify_transpile_results(run_results: RootJsonValue, expected_job_id: int) -> None: - """Verify Switch transpile job execution results.""" - assert isinstance(run_results, list), f"Unexpected results type: {type(run_results)}" - assert len(run_results) == 1, f"Expected single run result, got {len(run_results)}" - result = cast(dict, run_results[0]) - assert result.get("job_id") == expected_job_id, result - assert result.get("state") == "TERMINATED", result - assert result.get("result_state") == "SUCCESS", result - - -def _verify_output_notebook(ws: WorkspaceClient, output_path: str) -> None: - """Verify output notebook creation.""" - notebook_info = ws.workspace.get_status(output_path) - assert notebook_info.object_type is not None, f"Notebook not found at {output_path}" - assert notebook_info.object_type.value == "NOTEBOOK", f"Unexpected object type for {output_path}: {notebook_info}" - - -def _get_and_verify_job_id(ws: WorkspaceClient, install_state: InstallState): - """Get job ID and verify job creation and registration.""" - assert "Switch" in install_state.jobs, "Switch job not persisted in install state" - job_id = int(install_state.jobs["Switch"]) - - job = ws.jobs.get(job_id) - assert job is not None, f"Job {job_id} could not be retrieved" - assert job.settings is not None, f"Job {job_id} missing settings" - assert job.settings.name is not None, f"Job {job_id} missing name" - assert "switch" in job.settings.name.lower(), f"Unexpected job name: {job.settings.name}" - - assert job.settings.tasks is not None - assert len(job.settings.tasks) > 0 - task = job.settings.tasks[0] - assert task.notebook_task is not None, "First task is not a notebook task" - assert ( - "switch" in task.notebook_task.notebook_path.lower() - ), f"Unexpected notebook path: {task.notebook_task.notebook_path}" - return job_id - - -def _verify_resource_persistence(install_state: InstallState): - """Verify resource persistence.""" - assert install_state.switch_resources is not None, "Switch resources missing from install state" - resources = install_state.switch_resources - for field in ("catalog", "schema", "volume"): - assert field in resources, f"Switch resource missing '{field}' entry" - - switch_config = SwitchConfig(install_state) - retrieved_resources = switch_config.get_resources() - - assert retrieved_resources["catalog"] == "test_catalog", retrieved_resources - assert retrieved_resources["schema"] == "test_schema", retrieved_resources - assert retrieved_resources["volume"] == "test_volume", retrieved_resources - - -def _verify_job_id_retrieval(install_state: InstallState, expected_job_id: int): - """Verify job ID retrieval.""" - switch_config = SwitchConfig(install_state) - job_id_from_config = switch_config.get_job_id() - - assert isinstance(job_id_from_config, int), f"Invalid job id type: {type(job_id_from_config)}" - assert job_id_from_config > 0, f"Job id should be positive: {job_id_from_config}" - assert job_id_from_config == expected_job_id, f"Job id mismatch: {job_id_from_config} != {expected_job_id}" - - -def _cleanup_output_folder(ws: WorkspaceClient, output_folder: str) -> None: - """Remove generated notebooks from the workspace if they exist.""" - try: - ws.workspace.delete(output_folder, recursive=True) - except NotFound: - pass - - -def _drop_schema(ws: WorkspaceClient, catalog: str, schema: str) -> None: - """Drop the temporary schema used during the test.""" - try: - ws.schemas.delete(f"{catalog}.{schema}", force=True) - except NotFound: - pass - - -def _cleanup_switch(switch_deployment: SwitchDeployment, installation: Installation) -> None: - """Cleanup Switch deployment and installation state.""" - try: - switch_deployment.uninstall() - installation.remove() - except NotFound: - pass diff --git a/tests/resources/transpiler_configs/switch/wheel/databricks_switch_plugin-0.1.2-py3-none-any.whl b/tests/resources/transpiler_configs/switch/wheel/databricks_switch_plugin-0.1.2-py3-none-any.whl deleted file mode 100644 index 90de13f372d157e02342efb12450fc3d377184f3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 116597 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%0ATg9R>M9cQ(Q= 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 R8by#fFwcZr01N{_{|CNp=S~0s diff --git a/tests/unit/deployment/test_installation.py b/tests/unit/deployment/test_installation.py index b3d50cb055..039e4a412b 100644 --- a/tests/unit/deployment/test_installation.py +++ b/tests/unit/deployment/test_installation.py @@ -1,4 +1,3 @@ -import logging from unittest.mock import create_autospec import pytest @@ -17,7 +16,6 @@ ReconcileConfig, DatabaseConfig, ReconcileMetadataConfig, - SwitchResourcesConfig, ) from databricks.labs.lakebridge.deployment.installation import WorkspaceInstallation from databricks.labs.lakebridge.deployment.recon import ReconDeployment diff --git a/tests/unit/test_install.py b/tests/unit/test_install.py index 5b3c1640a5..a5ebb54aef 100644 --- a/tests/unit/test_install.py +++ b/tests/unit/test_install.py @@ -1,5 +1,5 @@ import logging -from collections.abc import Callable, Generator, Sequence, Set +from collections.abc import Callable, Generator, Sequence from pathlib import Path from unittest.mock import create_autospec, patch @@ -24,9 +24,6 @@ from databricks.labs.lakebridge.install import WorkspaceInstaller from databricks.labs.lakebridge.reconcile.constants import ReconSourceType, ReconReportType from databricks.labs.lakebridge.transpiler.installers import ( - BladebridgeInstaller, - MorpheusInstaller, - SwitchInstaller, TranspilerInstaller, ) from databricks.labs.lakebridge.transpiler.repository import TranspilerRepository diff --git a/tests/unit/transpiler/test_installers.py b/tests/unit/transpiler/test_installers.py index f93d3ca260..5724787484 100644 --- a/tests/unit/transpiler/test_installers.py +++ b/tests/unit/transpiler/test_installers.py @@ -6,12 +6,7 @@ import pytest -from databricks.labs.lakebridge.transpiler.installers import ( - ArtifactInstaller, - MorpheusInstaller, - SwitchInstaller, -) -from databricks.labs.lakebridge.transpiler.repository import TranspilerRepository +from databricks.labs.lakebridge.transpiler.installers import ArtifactInstaller, MorpheusInstaller def test_store_product_state(tmp_path) -> None: @@ -115,34 +110,3 @@ def test_java_version_parse_missing() -> None: version_output = "Nothing in here that looks like a version." parsed = FriendOfMorpheusInstaller.parse_java_version(version_output) assert parsed is None - - -class TestSwitchInstaller: - """Test suite for SwitchInstaller.""" - - @pytest.fixture - def installer(self, tmp_path: Path) -> SwitchInstaller: - """Create a SwitchInstaller instance for testing.""" - repository = TranspilerRepository(tmp_path) - return SwitchInstaller(repository) - - def test_name(self, installer: SwitchInstaller) -> None: - """Verify the installer name is correct.""" - assert installer.name == "Switch" - - @pytest.mark.parametrize( - ("filename", "expected"), - ( - # Valid Switch wheel files - pytest.param("databricks_switch_plugin-0.1.0-py3-none-any.whl", True, id="valid_version"), - pytest.param("databricks_switch_plugin-1.2.3-py3-none-any.whl", True, id="valid_multi_digit"), - pytest.param("databricks_switch_plugin-0.1.0rc1-py3-none-any.whl", True, id="valid_rc_version"), - # Invalid files - pytest.param("databricks_bb_plugin-0.1.0-py3-none-any.whl", False, id="wrong_package"), - pytest.param("some_other_package-0.1.0-py3-none-any.whl", False, id="other_package"), - pytest.param("databricks_switch_plugin-0.1.0.jar", False, id="wrong_extension"), - ), - ) - def test_can_install(self, filename: str, expected: bool, installer: SwitchInstaller) -> None: - """Verify can_install works for valid and invalid files.""" - assert installer.can_install(Path(filename)) == expected From 0e22abe177136bd2805d558bcc94f44d25b59d8a Mon Sep 17 00:00:00 2001 From: sundarshankar89 Date: Mon, 27 Oct 2025 17:26:08 +0530 Subject: [PATCH 10/17] Rebased from switch installer integration --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 525ab4d381..7a102a0125 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -449,7 +449,7 @@ bad-functions = ["map", "input"] # ignored-parents = # Maximum number of arguments for function / method. -max-args = 12 +max-args = 13 # Maximum number of attributes for a class (see R0902). max-attributes = 13 From 43cc0f5877b30bad160bf61a23025d3160b60862 Mon Sep 17 00:00:00 2001 From: sundarshankar89 Date: Mon, 27 Oct 2025 18:57:28 +0530 Subject: [PATCH 11/17] Intermediate check in --- src/databricks/labs/lakebridge/cli.py | 272 +++----------- .../lakebridge/transpiler/switch_runner.py | 75 +--- tests/unit/test_cli_transpile.py | 111 ------ tests/unit/transpiler/test_switch_runner.py | 346 ------------------ 4 files changed, 72 insertions(+), 732 deletions(-) diff --git a/src/databricks/labs/lakebridge/cli.py b/src/databricks/labs/lakebridge/cli.py index 7428a7fa71..a6539bd2f3 100644 --- a/src/databricks/labs/lakebridge/cli.py +++ b/src/databricks/labs/lakebridge/cli.py @@ -17,7 +17,6 @@ from databricks.labs.blueprint.cli import App from databricks.labs.blueprint.entrypoint import get_logger, is_in_debug from databricks.labs.blueprint.installation import RootJsonValue, JsonObject, JsonValue -from databricks.labs.blueprint.installer import InstallState from databricks.labs.blueprint.tui import Prompts @@ -34,10 +33,10 @@ from databricks.labs.lakebridge.reconcile.recon_config import RECONCILE_OPERATION_NAME, AGG_RECONCILE_OPERATION_NAME from databricks.labs.lakebridge.transpiler.describe import TranspilersDescription from databricks.labs.lakebridge.transpiler.execute import transpile as do_transpile -from databricks.labs.lakebridge.transpiler.lsp.lsp_engine import LSPConfig, LSPEngine +from databricks.labs.lakebridge.transpiler.lsp.lsp_engine import LSPEngine from databricks.labs.lakebridge.transpiler.repository import TranspilerRepository from databricks.labs.lakebridge.transpiler.sqlglot.sqlglot_engine import SqlglotEngine -from databricks.labs.lakebridge.transpiler.switch_runner import SwitchConfig, SwitchRunner +from databricks.labs.lakebridge.transpiler.switch_runner import SwitchRunner from databricks.labs.lakebridge.transpiler.transpile_engine import TranspileEngine from databricks.labs.lakebridge.transpiler.transpile_status import ErrorSeverity @@ -228,6 +227,10 @@ def _validate_transpiler_config_path(transpiler_config_path: str, msg: str) -> N def use_transpiler_config_path(self, transpiler_config_path: str | None) -> None: if transpiler_config_path is not None: logger.debug(f"Setting transpiler_config_path to: {transpiler_config_path!r}") + # Switch is installed inside "/Users/<>/.lakebridge/transpilers/Switch/lsp/config.yml + if Path(transpiler_config_path).parent.parent.name == "Switch": + msg = "Switch transpiler is not supported through `transpile` run `llm-transpile` instead." + raise_validation_exception(msg) self._validate_transpiler_config_path( transpiler_config_path, f"Invalid path for '--transpiler-config-path', does not exist: {transpiler_config_path}", @@ -635,228 +638,67 @@ def _override_workspace_client_config(ctx: ApplicationContext, overrides: dict[s def llm_transpile( *, w: WorkspaceClient, - input_source: str | None = None, - output_ws_folder: str | None = None, - source_dialect: str | None = None, - transpiler_repository: TranspilerRepository = TranspilerRepository.user_home(), + input_source: str, + output_ws_folder: str, + source_dialect: str, ) -> None: """Transpile source code to Databricks using LLM Transpiler (Switch)""" ctx = ApplicationContext(w) - ctx.add_user_agent_extra("cmd", "llm-transpile") + ctx.add_user_agent_extra("cmd", "transpile-switch") user = ctx.current_user logger.debug(f"User: {user}") - checker = _LLMTranspileConfigChecker(ctx.transpile_config, ctx.prompts, ctx.install_state, transpiler_repository) - checker.use_input_source(input_source) - checker.use_output_ws_folder(output_ws_folder) - checker.use_source_dialect(source_dialect) - params = checker.check() - - result = _llm_transpile(ctx, params) - print(json.dumps(result)) - - -class _LLMTranspileConfigChecker: - """Helper class for 'llm-transpile' command configuration validation""" - - _transpile_config: TranspileConfig | None - _prompts: Prompts - _install_state: InstallState - _transpiler_repository: TranspilerRepository - _input_source: str | None = None - _output_ws_folder: str | None = None - _source_dialect: str | None = None - - def __init__( - self, - transpile_config: TranspileConfig | None, - prompts: Prompts, - install_state: InstallState, - transpiler_repository: TranspilerRepository, - ): - self._transpile_config = transpile_config - self._prompts = prompts - self._install_state = install_state - self._transpiler_repository = transpiler_repository - - @staticmethod - def _validate_input_source_path(input_source: str, msg: str) -> None: - """Validate the input source: it must be a path that exists.""" - if not Path(input_source).exists(): - raise_validation_exception(msg) - - def use_input_source(self, input_source: str | None) -> None: - if input_source is not None: - logger.debug(f"Setting input_source to: {input_source!r}") - self._validate_input_source_path(input_source, f"Invalid path for '--input-source': {input_source}") - self._input_source = input_source - - def _prompt_input_source(self) -> None: - default_input = None - if self._transpile_config and self._transpile_config.input_source: - default_input = self._transpile_config.input_source - - if default_input: - prompt_text = f"Enter input source path (press for default: {default_input})" - prompted = self._prompts.question(prompt_text).strip() - self._input_source = prompted if prompted else default_input - else: - prompted = self._prompts.question("Enter input source path (directory or file)").strip() - self._input_source = prompted - - logger.debug(f"Setting input_source to: {self._input_source!r}") - self._validate_input_source_path(self._input_source, f"Invalid input source: {self._input_source}") - - def _check_input_source(self) -> None: - if self._input_source is None: - self._prompt_input_source() - - def use_output_ws_folder(self, output_ws_folder: str | None) -> None: - if output_ws_folder is not None: - logger.debug(f"Setting output_ws_folder to: {output_ws_folder!r}") - self._validate_output_ws_folder_path( - output_ws_folder, f"Invalid path for '--output-ws-folder': {output_ws_folder}" - ) - self._output_ws_folder = output_ws_folder - - @staticmethod - def _validate_output_ws_folder_path(output_ws_folder: str, msg: str) -> None: - """Validate output folder is a Workspace path.""" - if not output_ws_folder.startswith("/Workspace/"): - raise_validation_exception(f"{msg}. Must start with /Workspace/") - - def _prompt_output_ws_folder(self) -> None: - prompted_output_ws_folder = self._prompts.question( - "Enter output folder path (Databricks Workspace path starting with /Workspace/)" - ).strip() - logger.debug(f"Setting output_ws_folder to: {prompted_output_ws_folder!r}") - self._validate_output_ws_folder_path( - prompted_output_ws_folder, f"Invalid output folder: {prompted_output_ws_folder}" + job_list = ctx.install_state.jobs + if "Switch" not in job_list: + raise RuntimeError( + "Switch Job ID not found. " + "Please run 'databricks labs lakebridge install-transpile --include-llm-transpiler true' first." + ) + logger.debug("Switch job ID found in InstallState") + job_id = int(job_list["Switch"]) + + # Upload File to Volume + transpile_config = ctx.transpile_config + if transpile_config is None or transpile_config.transpiler_options is None: + raise RuntimeError( + "Transpile configuration config.yml not found in workspace." + "Please run 'databricks labs lakebridge install-transpile --include-llm-transpiler true' first." ) - self._output_ws_folder = prompted_output_ws_folder - - def _check_output_ws_folder(self) -> None: - if self._output_ws_folder is None: - self._prompt_output_ws_folder() - - def use_source_dialect(self, source_dialect: str | None) -> None: - if source_dialect is not None: - logger.debug(f"Setting source_dialect to: {source_dialect!r}") - self._source_dialect = source_dialect - - def _prompt_source_dialect(self) -> None: - """Prompt for source dialect from Switch dialects.""" - available_dialects = self._get_switch_dialects() - - if not available_dialects: - raise_validation_exception( - "No Switch dialects available. " - "Install with: databricks labs lakebridge install-transpile --include-llm-transpiler" - ) - - logger.debug(f"Available dialects: {available_dialects!r}") - source_dialect = self._prompts.choice("Select the source dialect:", list(sorted(available_dialects))) - - self._source_dialect = source_dialect - - def _check_source_dialect(self) -> None: - """Validate and prompt for source dialect if not provided.""" - available_dialects = self._get_switch_dialects() - - if self._source_dialect is None: - self._prompt_source_dialect() - elif self._source_dialect not in available_dialects: - supported = ", ".join(sorted(available_dialects)) - raise_validation_exception(f"Invalid source-dialect: '{self._source_dialect}'. " f"Available: {supported}") - - def _get_switch_dialects(self) -> set[str]: - """Get Switch dialects from config.yml using LSPConfig.""" - config_path = self._transpiler_repository.transpiler_config_path("Switch") - if not config_path.exists(): - return set() - - try: - lsp_config = LSPConfig.load(config_path) - return set(lsp_config.remorph.dialects) - except (OSError, ValueError) as e: - logger.warning(f"Failed to load Switch dialects: {e}") - return set() - - def _get_switch_options_with_defaults(self) -> dict[str, str]: - """Get default values for Switch options from config.yml.""" - config_path = self._transpiler_repository.transpiler_config_path("Switch") - if not config_path.exists(): - return {} - - try: - lsp_config = LSPConfig.load(config_path) - except (OSError, ValueError) as e: - logger.warning(f"Failed to load Switch options: {e}") - return {} - - options_all = lsp_config.options_for_dialect("all") - result = {} - for option in options_all: - if option.default and option.default != "": - result[option.flag] = option.default - - logger.debug(f"Loaded {len(result)} Switch options with defaults from config.yml") - return result - - def _validate_switch_options(self, options: dict[str, str]) -> None: - """Validate options against config.yml choices.""" - config_path = self._transpiler_repository.transpiler_config_path("Switch") - if not config_path.exists(): - return - - try: - lsp_config = LSPConfig.load(config_path) - except (OSError, ValueError) as e: - logger.warning(f"Failed to validate Switch options: {e}") - return - - options_all = lsp_config.options_for_dialect("all") - for option in options_all: - if option.flag in options and option.choices: - value = options[option.flag] - if value not in option.choices: - raise_validation_exception( - f"Invalid value for '{option.flag}': {value!r}. " f"Must be one of: {', '.join(option.choices)}" - ) - - def check(self) -> dict: - """Validate all parameters and return configuration dict.""" - logger.debug("Checking llm-transpile configuration") - - self._check_input_source() - self._check_output_ws_folder() - self._check_source_dialect() - - switch_options = self._get_switch_options_with_defaults() - self._validate_switch_options(switch_options) - - wait_for_completion = str(switch_options.pop("wait_for_completion", "false")).lower() == "true" - - return { - "input_source": self._input_source, - "output_ws_folder": self._output_ws_folder, - "source_dialect": self._source_dialect, - "switch_options": switch_options, - "wait_for_completion": wait_for_completion, - } - -def _llm_transpile(ctx: ApplicationContext, params: dict) -> RootJsonValue: - """Execute LLM transpilation via Switch job.""" - config = SwitchConfig(ctx.install_state) - resources = config.get_resources() - job_id = config.get_job_id() + transpile_options = transpile_config.transpiler_options + if not isinstance(transpile_options, Mapping): + raise RuntimeError( + "Invalid transpile configuration: transpiler_options must be a mapping. " + "Please run 'databricks labs lakebridge install-transpile --include-llm-transpiler true' first." + ) + catalog = transpile_options.get("catalog", None) + schema = transpile_options.get("schema", None) + volume = transpile_options.get("volume", None) + + if catalog or schema or volume is None: + raise RuntimeError( + "Invalid transpile configuration: transpiler_options must be a mapping. " + "Please run 'databricks labs lakebridge install-transpile --include-llm-transpiler true' first." + ) + assert isinstance(catalog, str) + assert isinstance(schema, str) + assert isinstance(volume, str) - runner = SwitchRunner(ctx.workspace_client, ctx.installation) + try: + job_runner = SwitchRunner(ctx.workspace_client, ctx.installation) + volume_input_path = job_runner.upload_to_volume( + local_path=Path(input_source), catalog=catalog, schema=schema, volume=volume + ) - return runner.run( - catalog=resources["catalog"], schema=resources["schema"], volume=resources["volume"], job_id=job_id, **params - ) + response = job_runner.run( + volume_input_path=volume_input_path, + output_ws_folder=output_ws_folder, + source_tech=source_dialect, + job_id=job_id, + ) + print(json.dumps(response)) + except Exception as ex: + raise RuntimeError(ex) from ex @lakebridge.command diff --git a/src/databricks/labs/lakebridge/transpiler/switch_runner.py b/src/databricks/labs/lakebridge/transpiler/switch_runner.py index b6afdbb673..1fbdbf3c2d 100644 --- a/src/databricks/labs/lakebridge/transpiler/switch_runner.py +++ b/src/databricks/labs/lakebridge/transpiler/switch_runner.py @@ -7,46 +7,11 @@ from pathlib import Path from databricks.labs.blueprint.installation import Installation, RootJsonValue -from databricks.labs.blueprint.installer import InstallState from databricks.sdk import WorkspaceClient logger = logging.getLogger(__name__) -class SwitchConfig: - """Helper to load Switch configuration from InstallState.""" - - def __init__(self, install_state: InstallState): - self._install_state = install_state - - def get_resources(self) -> dict[str, str]: - """Get catalog, schema, volume from switch_resources.""" - resources = self._install_state.switch_resources - - if not resources or not all(k in resources for k in ("catalog", "schema", "volume")): - raise SystemExit( - "Switch resources not configured. " - "Please run 'databricks labs lakebridge install-transpile --include-llm-transpiler true' first." - ) - - return { - "catalog": resources["catalog"], - "schema": resources["schema"], - "volume": resources["volume"], - } - - def get_job_id(self) -> int: - """Get Switch job ID from InstallState.""" - if "Switch" in self._install_state.jobs: - logger.debug("Switch job ID found in InstallState") - return int(self._install_state.jobs["Switch"]) - - raise SystemExit( - "Switch Job ID not found. " - "Please run 'databricks labs lakebridge install-transpile --include-llm-transpiler true' first." - ) - - class SwitchRunner: """Runner for Switch LLM transpilation jobs.""" @@ -60,32 +25,24 @@ def __init__( def run( self, - input_source: str, + volume_input_path: str, output_ws_folder: str, - source_dialect: str, - catalog: str, - schema: str, - volume: str, + source_tech: str, job_id: int, - switch_options: dict[str, str], wait_for_completion: bool = False, ) -> RootJsonValue: """Upload local files to Volume and trigger Switch job.""" - volume_input_path = self._upload_to_volume(Path(input_source), catalog, schema, volume) job_params = self._build_job_parameters( - volume_input_path=volume_input_path, - output_ws_folder=output_ws_folder, - catalog=catalog, - schema=schema, - source_dialect=source_dialect, - switch_options=switch_options, + input_dir=volume_input_path, + output_dir=output_ws_folder, + source_tech=source_tech, ) logger.info(f"Triggering Switch job with job_id: {job_id}") return self._run_job(job_id, job_params, wait_for_completion) - def _upload_to_volume( + def upload_to_volume( self, local_path: Path, catalog: str, @@ -129,20 +86,18 @@ def _upload_to_volume( def _build_job_parameters( self, - volume_input_path: str, - output_ws_folder: str, - catalog: str, - schema: str, - source_dialect: str, - switch_options: dict[str, str], + input_dir: str, + output_dir: str, + source_tech: str, + switch_options: dict | None = None, ) -> dict[str, str]: """Build Switch job parameters.""" + if switch_options is None: + switch_options = {} return { - "input_dir": volume_input_path, - "output_dir": output_ws_folder, - "result_catalog": catalog, - "result_schema": schema, - "builtin_prompt": source_dialect, + "input_dir": input_dir, + "output_dir": output_dir, + "source_tech": source_tech, **switch_options, } diff --git a/tests/unit/test_cli_transpile.py b/tests/unit/test_cli_transpile.py index d60e11095d..300818b57c 100644 --- a/tests/unit/test_cli_transpile.py +++ b/tests/unit/test_cli_transpile.py @@ -10,7 +10,6 @@ from databricks.labs.lakebridge import cli -from databricks.labs.blueprint.installer import InstallState from databricks.labs.blueprint.tui import MockPrompts from databricks.labs.lakebridge.config import TranspileConfig from databricks.sdk import WorkspaceClient @@ -546,113 +545,3 @@ def test_describe_transpile(mock_cli_transpile_no_config, transpiler_repository: } ], } - - -class TestLLMTranspile: - """Test suite for llm-transpile command.""" - - @pytest.fixture - def switch_transpiler_repository(self, tmp_path: Path) -> TranspilerRepository: - """Create TranspilerRepository with Switch config.""" - switch_config = { - "remorph": { - "version": 1, - "name": "Switch", - "dialects": ["snowflake", "teradata", "oracle"], - "command_line": ["/usr/bin/true"], - }, - "options": {"all": []}, - } - config_path = tmp_path / "remorph-transpilers" / "Switch" / "lib" / "config.yml" - config_path.parent.mkdir(parents=True) - with config_path.open("w") as f: - yaml.dump(switch_config, f) - return TranspilerRepository(tmp_path) - - def test_with_valid_inputs( - self, empty_input_source: Path, switch_transpiler_repository: TranspilerRepository - ) -> None: - """Test llm-transpile with valid parameters.""" - ws = create_autospec(WorkspaceClient) - mock_runner_run = MagicMock(return_value=[{"job_id": 123, "run_id": 456, "run_url": "http://test"}]) - - with ( - patch("databricks.labs.lakebridge.cli.ApplicationContext") as mock_ctx, - patch( - "databricks.labs.lakebridge.cli._llm_transpile", return_value=mock_runner_run.return_value - ) as mock_llm, - ): - mock_ctx.return_value.transpile_config = None - mock_ctx.return_value.prompts = MockPrompts({}) - mock_ctx.return_value.install_state = create_autospec(InstallState) - - cli.llm_transpile( - w=ws, - input_source=str(empty_input_source), - output_ws_folder="/Workspace/Users/test/output", - source_dialect="snowflake", - transpiler_repository=switch_transpiler_repository, - ) - - # Verify _llm_transpile was called with correct parameters - mock_llm.assert_called_once() - call_args = mock_llm.call_args - ctx_arg = call_args[0][0] - params_arg = call_args[0][1] - - # Verify context - assert ctx_arg == mock_ctx.return_value - - # Verify parameters - assert params_arg["input_source"] == str(empty_input_source) - assert params_arg["output_ws_folder"] == "/Workspace/Users/test/output" - assert params_arg["source_dialect"] == "snowflake" - assert "switch_options" in params_arg - assert "wait_for_completion" in params_arg - - @pytest.mark.parametrize( - ("input_source_type", "output_ws_folder", "source_dialect", "expected_error"), - ( - pytest.param( - "invalid", "/Workspace/Users/test/output", "snowflake", "Invalid path", id="invalid_input_source" - ), - pytest.param( - "valid", "/invalid/path", "snowflake", "Must start with /Workspace/", id="invalid_output_folder" - ), - pytest.param( - "valid", - "/Workspace/Users/test/output", - "invalid_dialect", - "Invalid source-dialect", - id="invalid_dialect", - ), - ), - ) - def test_with_invalid_params( - self, - empty_input_source: Path, - switch_transpiler_repository: TranspilerRepository, - input_source_type: str, - output_ws_folder: str, - source_dialect: str, - expected_error: str, - ) -> None: - """Test llm-transpile with invalid parameters.""" - ws = create_autospec(WorkspaceClient) - input_source = "/invalid/path/does/not/exist" if input_source_type == "invalid" else str(empty_input_source) - - with ( - patch("databricks.labs.lakebridge.cli.ApplicationContext") as mock_ctx, - pytest.raises(ValueError, match=expected_error), - ): - mock_ctx.return_value.transpile_config = None - mock_ctx.return_value.prompts = MockPrompts({}) - mock_ctx.return_value.install_state = create_autospec(InstallState) - - cli.llm_transpile( - w=ws, - input_source=input_source, - output_ws_folder=output_ws_folder, - source_dialect=source_dialect, - transpiler_repository=switch_transpiler_repository, - ) diff --git a/tests/unit/transpiler/test_switch_runner.py b/tests/unit/transpiler/test_switch_runner.py index 40099cdc91..e69de29bb2 100644 --- a/tests/unit/transpiler/test_switch_runner.py +++ b/tests/unit/transpiler/test_switch_runner.py @@ -1,346 +0,0 @@ -from pathlib import Path -from unittest.mock import Mock, create_autospec - -import pytest - -from databricks.labs.blueprint.installer import InstallState -from databricks.labs.lakebridge.transpiler.switch_runner import SwitchConfig, SwitchRunner - - -class FriendOfSwitchRunner(SwitchRunner): - """Friend class to access protected methods for testing.""" - - def upload_to_volume(self, local_path: Path, catalog: str, schema: str, volume: str) -> str: - return self._upload_to_volume(local_path, catalog, schema, volume) - - def build_job_parameters( - self, - volume_input_path: str, - output_ws_folder: str, - catalog: str, - schema: str, - source_dialect: str, - switch_options: dict[str, str], - ) -> dict[str, str]: - return self._build_job_parameters( - volume_input_path, output_ws_folder, catalog, schema, source_dialect, switch_options - ) - - -class TestSwitchConfig: - """Test suite for SwitchConfig.""" - - @pytest.fixture - def install_state_with_switch(self) -> InstallState: - """Create InstallState with Switch configured.""" - state = create_autospec(InstallState) - state.switch_resources = { - "catalog": "test_catalog", - "schema": "test_schema", - "volume": "test_volume", - } - state.jobs = {"Switch": "12345"} - return state - - @pytest.fixture - def install_state_without_switch(self) -> InstallState: - """Create InstallState without Switch configured.""" - state = create_autospec(InstallState) - state.switch_resources = None - state.jobs = {} - return state - - def test_get_resources_success(self, install_state_with_switch: InstallState) -> None: - """Verify successful resource retrieval from InstallState.""" - config = SwitchConfig(install_state_with_switch) - resources = config.get_resources() - - assert resources == { - "catalog": "test_catalog", - "schema": "test_schema", - "volume": "test_volume", - } - - @pytest.mark.parametrize( - ("switch_resources", "error_msg_fragment"), - ( - pytest.param(None, "Switch resources not configured", id="resources_none"), - pytest.param({"catalog": "cat"}, "Switch resources not configured", id="missing_schema_volume"), - pytest.param({"catalog": "cat", "schema": "sch"}, "Switch resources not configured", id="missing_volume"), - ), - ) - def test_get_resources_not_configured(self, switch_resources: dict | None, error_msg_fragment: str) -> None: - """Test error when switch_resources missing or incomplete.""" - state = create_autospec(InstallState) - state.switch_resources = switch_resources - - config = SwitchConfig(state) - - with pytest.raises(SystemExit, match=error_msg_fragment): - config.get_resources() - - @pytest.mark.parametrize( - ("jobs", "should_succeed"), - ( - pytest.param({"Switch": "12345"}, True, id="job_exists"), - pytest.param({"Switch": 67890}, True, id="job_exists_int"), - pytest.param({}, False, id="job_missing"), - pytest.param({"OtherJob": "99999"}, False, id="other_job_only"), - ), - ) - def test_get_job_id(self, jobs: dict, should_succeed: bool) -> None: - """Test job ID retrieval from InstallState.""" - state = create_autospec(InstallState) - state.jobs = jobs - - config = SwitchConfig(state) - - if should_succeed: - job_id = config.get_job_id() - assert isinstance(job_id, int) - assert job_id == int(jobs["Switch"]) - else: - with pytest.raises(SystemExit, match="Switch Job ID not found"): - config.get_job_id() - - -class TestSwitchRunner: - """Test suite for SwitchRunner.""" - - @pytest.fixture - def mock_ws(self) -> Mock: - """Create mock WorkspaceClient.""" - ws = Mock() - ws.config.host = "https://test.databricks.com" - ws.files.upload = Mock() - ws.jobs.run_now = Mock() - ws.jobs.run_now_and_wait = Mock() - return ws - - @pytest.fixture - def mock_installation(self) -> Mock: - """Create mock Installation.""" - return Mock() - - @pytest.fixture - def runner(self, mock_ws: Mock, mock_installation: Mock) -> FriendOfSwitchRunner: - """Create SwitchRunner instance.""" - return FriendOfSwitchRunner(mock_ws, mock_installation) - - @pytest.mark.parametrize( - "is_file", - ( - pytest.param(True, id="single_file"), - pytest.param(False, id="directory"), - ), - ) - def test_upload_to_volume(self, runner: FriendOfSwitchRunner, mock_ws: Mock, tmp_path: Path, is_file: bool) -> None: - """Test file/directory upload to Volume.""" - # Setup test files - if is_file: - test_file = tmp_path / "test.sql" - test_file.write_text("SELECT 1;") - local_path = test_file - expected_calls = 1 - else: - test_dir = tmp_path / "queries" - test_dir.mkdir() - (test_dir / "query1.sql").write_text("SELECT 1;") - (test_dir / "query2.sql").write_text("SELECT 2;") - subdir = test_dir / "subdir" - subdir.mkdir() - (subdir / "query3.sql").write_text("SELECT 3;") - local_path = test_dir - expected_calls = 3 - - # Execute upload - volume_path = runner.upload_to_volume( - local_path=local_path, catalog="test_cat", schema="test_sch", volume="test_vol" - ) - - # Verify results - assert volume_path.startswith("/Volumes/test_cat/test_sch/test_vol/input_") - assert mock_ws.files.upload.call_count == expected_calls - - def test_upload_unique_paths(self, runner: FriendOfSwitchRunner, tmp_path: Path) -> None: - """Verify UUID usage prevents path collisions.""" - test_file = tmp_path / "test.sql" - test_file.write_text("SELECT 1;") - - # Generate two upload paths - path1 = runner.upload_to_volume(test_file, "cat", "sch", "vol") - path2 = runner.upload_to_volume(test_file, "cat", "sch", "vol") - - # Paths should be different due to UUID - assert path1 != path2 - assert path1.startswith("/Volumes/cat/sch/vol/input_") - assert path2.startswith("/Volumes/cat/sch/vol/input_") - - def test_upload_preserves_hierarchy(self, runner: FriendOfSwitchRunner, mock_ws: Mock, tmp_path: Path) -> None: - """Verify subdirectory structure is maintained.""" - # Create nested directory structure - root = tmp_path / "root" - root.mkdir() - (root / "file1.sql").write_text("SELECT 1;") - subdir = root / "sub" / "deep" - subdir.mkdir(parents=True) - (subdir / "file2.sql").write_text("SELECT 2;") - - # Upload directory - runner.upload_to_volume(root, "cat", "sch", "vol") - - # Verify hierarchy is preserved - calls = [call[1]["file_path"] for call in mock_ws.files.upload.call_args_list] - assert any("file1.sql" in path and "/sub/" not in path for path in calls) - assert any("file2.sql" in path and "/sub/deep/" in path for path in calls) - - @pytest.mark.parametrize( - ("switch_options", "expected_extra_keys"), - ( - pytest.param({}, 0, id="no_options"), - pytest.param({"endpoint_name": "claude", "concurrency": "4", "log_level": "DEBUG"}, 3, id="with_options"), - ), - ) - def test_build_job_parameters( - self, runner: FriendOfSwitchRunner, switch_options: dict, expected_extra_keys: int - ) -> None: - """Test job parameter construction.""" - params = runner.build_job_parameters( - volume_input_path="/Volumes/cat/sch/vol/input_123", - output_ws_folder="/Workspace/Users/test/output", - catalog="test_cat", - schema="test_sch", - source_dialect="snowflake", - switch_options=switch_options, - ) - - # Verify required parameters - assert params["input_dir"] == "/Volumes/cat/sch/vol/input_123" - assert params["output_dir"] == "/Workspace/Users/test/output" - assert params["result_catalog"] == "test_cat" - assert params["result_schema"] == "test_sch" - assert params["builtin_prompt"] == "snowflake" - - # Verify options are included - assert len(params) == 5 + expected_extra_keys - for key, value in switch_options.items(): - assert params[key] == value - - @pytest.mark.parametrize( - "wait_for_completion", - ( - pytest.param(False, id="async_execution"), - pytest.param(True, id="sync_execution"), - ), - ) - def test_run_job_execution( - self, runner: FriendOfSwitchRunner, mock_ws: Mock, tmp_path: Path, wait_for_completion: bool - ) -> None: - """Test async/sync job execution based on wait_for_completion flag.""" - # Setup test file - test_file = tmp_path / "test.sql" - test_file.write_text("SELECT 1;") - - # Mock job execution - mock_run = Mock() - mock_run.run_id = 99999 - if wait_for_completion: - mock_run.state.life_cycle_state.value = "TERMINATED" - mock_run.state.result_state.value = "SUCCESS" - mock_ws.jobs.run_now_and_wait.return_value = mock_run - else: - mock_ws.jobs.run_now.return_value = mock_run - - # Execute run - result = runner.run( - input_source=str(test_file), - output_ws_folder="/Workspace/output", - source_dialect="snowflake", - catalog="cat", - schema="sch", - volume="vol", - job_id=12345, - switch_options={"log_level": "DEBUG"}, - wait_for_completion=wait_for_completion, - ) - - # Verify correct method called with proper job_parameters - expected_params = { - "result_catalog": "cat", - "result_schema": "sch", - "builtin_prompt": "snowflake", - "log_level": "DEBUG", - } - - if wait_for_completion: - call_args = mock_ws.jobs.run_now_and_wait.call_args - mock_ws.jobs.run_now.assert_not_called() - else: - call_args = mock_ws.jobs.run_now.call_args - mock_ws.jobs.run_now_and_wait.assert_not_called() - - # Verify job_id and job_parameters - assert call_args[0][0] == 12345 # job_id - actual_params = call_args[1]["job_parameters"] - assert actual_params["result_catalog"] == expected_params["result_catalog"] - assert actual_params["result_schema"] == expected_params["result_schema"] - assert actual_params["builtin_prompt"] == expected_params["builtin_prompt"] - assert actual_params["log_level"] == expected_params["log_level"] - assert "input_dir" in actual_params - assert "output_dir" in actual_params - - # Verify result structure - assert isinstance(result, list) - assert len(result) == 1 - first_item = result[0] - assert isinstance(first_item, dict) - - if wait_for_completion: - assert first_item["state"] == "TERMINATED" - assert first_item["result_state"] == "SUCCESS" - else: - assert "state" not in first_item - assert "result_state" not in first_item - - # Verify common result fields - assert first_item["job_id"] == 12345 - assert first_item["run_id"] == 99999 - # Verify run_url format - assert first_item["run_url"] == "https://test.databricks.com/jobs/12345/runs/99999" - - @pytest.mark.parametrize( - "wait_for_completion", - ( - pytest.param(False, id="async_missing_run_id"), - pytest.param(True, id="sync_missing_run_id"), - ), - ) - def test_run_job_execution_with_missing_run_id( - self, runner: FriendOfSwitchRunner, mock_ws: Mock, tmp_path: Path, wait_for_completion: bool - ) -> None: - """Test SystemExit when run_id is missing.""" - # Setup test file - test_file = tmp_path / "test.sql" - test_file.write_text("SELECT 1;") - - # Mock job execution with missing run_id - mock_run = Mock() - mock_run.run_id = None # Simulate missing run_id - if wait_for_completion: - mock_ws.jobs.run_now_and_wait.return_value = mock_run - else: - mock_ws.jobs.run_now.return_value = mock_run - - # Execute and expect SystemExit - with pytest.raises(SystemExit, match="Job 12345 execution failed"): - runner.run( - input_source=str(test_file), - output_ws_folder="/Workspace/output", - source_dialect="snowflake", - catalog="cat", - schema="sch", - volume="vol", - job_id=12345, - switch_options={}, - wait_for_completion=wait_for_completion, - ) From bb7c3d62e4ddaf0d5b9e76b1550bb745c249fc92 Mon Sep 17 00:00:00 2001 From: sundarshankar89 Date: Mon, 27 Oct 2025 19:20:20 +0530 Subject: [PATCH 12/17] Intermediate check in --- src/databricks/labs/lakebridge/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/databricks/labs/lakebridge/cli.py b/src/databricks/labs/lakebridge/cli.py index a6539bd2f3..b1b04b0a68 100644 --- a/src/databricks/labs/lakebridge/cli.py +++ b/src/databricks/labs/lakebridge/cli.py @@ -666,7 +666,7 @@ def llm_transpile( ) transpile_options = transpile_config.transpiler_options - if not isinstance(transpile_options, Mapping): + if not isinstance(transpile_options, JsonValue): raise RuntimeError( "Invalid transpile configuration: transpiler_options must be a mapping. " "Please run 'databricks labs lakebridge install-transpile --include-llm-transpiler true' first." From bd706387315e84249eb7af1e7f04c713e6834873 Mon Sep 17 00:00:00 2001 From: sundarshankar89 Date: Mon, 27 Oct 2025 19:29:50 +0530 Subject: [PATCH 13/17] Intermediate check in --- src/databricks/labs/lakebridge/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/databricks/labs/lakebridge/cli.py b/src/databricks/labs/lakebridge/cli.py index b1b04b0a68..a6539bd2f3 100644 --- a/src/databricks/labs/lakebridge/cli.py +++ b/src/databricks/labs/lakebridge/cli.py @@ -666,7 +666,7 @@ def llm_transpile( ) transpile_options = transpile_config.transpiler_options - if not isinstance(transpile_options, JsonValue): + if not isinstance(transpile_options, Mapping): raise RuntimeError( "Invalid transpile configuration: transpiler_options must be a mapping. " "Please run 'databricks labs lakebridge install-transpile --include-llm-transpiler true' first." From 0eb1570eb8bf3e2284a4877b28d2ddd0e38f09fe Mon Sep 17 00:00:00 2001 From: sundarshankar89 Date: Mon, 27 Oct 2025 19:56:16 +0530 Subject: [PATCH 14/17] Intermediate check in --- src/databricks/labs/lakebridge/cli.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/databricks/labs/lakebridge/cli.py b/src/databricks/labs/lakebridge/cli.py index a6539bd2f3..620f97396a 100644 --- a/src/databricks/labs/lakebridge/cli.py +++ b/src/databricks/labs/lakebridge/cli.py @@ -666,6 +666,7 @@ def llm_transpile( ) transpile_options = transpile_config.transpiler_options + logger.debug(f"Transpiler options: {transpile_options}") if not isinstance(transpile_options, Mapping): raise RuntimeError( "Invalid transpile configuration: transpiler_options must be a mapping. " @@ -675,9 +676,9 @@ def llm_transpile( schema = transpile_options.get("schema", None) volume = transpile_options.get("volume", None) - if catalog or schema or volume is None: + if catalog is None or schema is None or volume is None: raise RuntimeError( - "Invalid transpile configuration: transpiler_options must be a mapping. " + "Invalid transpile configuration: catalog, schema or volume is missing. " "Please run 'databricks labs lakebridge install-transpile --include-llm-transpiler true' first." ) assert isinstance(catalog, str) From eb46f244968568b1e333f12d7c4aa166e8739c68 Mon Sep 17 00:00:00 2001 From: sundarshankar89 Date: Tue, 28 Oct 2025 15:14:33 +0530 Subject: [PATCH 15/17] initial tests --- src/databricks/labs/lakebridge/cli.py | 7 ++- tests/unit/test_cli_llm_transpile.py | 88 +++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 tests/unit/test_cli_llm_transpile.py diff --git a/src/databricks/labs/lakebridge/cli.py b/src/databricks/labs/lakebridge/cli.py index 992549006c..57a7306052 100644 --- a/src/databricks/labs/lakebridge/cli.py +++ b/src/databricks/labs/lakebridge/cli.py @@ -649,9 +649,12 @@ def llm_transpile( input_source: str, output_ws_folder: str, source_dialect: str, + ctx: ApplicationContext | None = None, ) -> None: """Transpile source code to Databricks using LLM Transpiler (Switch)""" - ctx = ApplicationContext(w) + if ctx is None: + ctx = ApplicationContext(w) + del w ctx.add_user_agent_extra("cmd", "transpile-switch") user = ctx.current_user logger.debug(f"User: {user}") @@ -705,7 +708,7 @@ def llm_transpile( source_tech=source_dialect, job_id=job_id, ) - print(json.dumps(response)) + json.dump(response, sys.stdout, indent=2) except Exception as ex: raise RuntimeError(ex) from ex diff --git a/tests/unit/test_cli_llm_transpile.py b/tests/unit/test_cli_llm_transpile.py new file mode 100644 index 0000000000..2f6a8a5155 --- /dev/null +++ b/tests/unit/test_cli_llm_transpile.py @@ -0,0 +1,88 @@ +import json +from pathlib import Path +from unittest.mock import create_autospec +from typing import cast + +import pytest + +from databricks.labs.blueprint.installation import MockInstallation, RootJsonValue +from databricks.labs.lakebridge import cli +from databricks.labs.lakebridge.contexts.application import ApplicationContext +from databricks.sdk import WorkspaceClient + +_JOB_ID = 1234567890 +_RUN_ID = 123456789 + + +def create_switch_workspace_client_mock() -> WorkspaceClient: + ws = create_autospec(spec=WorkspaceClient, instance=True) + + ws.config.host = 'https://workspace.databricks.com' + ws.files.upload.return_value = None + ws.jobs.run_now.return_value.run_id = _RUN_ID + ws.jobs.run_now_and_wait_result.return_value.run_id = _RUN_ID + + return ws + + +@pytest.fixture +def mock_installation_with_switch() -> MockInstallation: + """MockInstallation with Switch configuration state.""" + state: dict[str, RootJsonValue] = { + "config.yml": { + "version": 3, + "transpiler_config_path": str(Path.home() / ".lakebridge" / "Switch" / "lsp" / "config.yml"), + "transpiler_options": { + "catalog": "test_catalog", + "schema": "test_schema", + "volume": "test_volume", + "foundation_model": "databricks-claude-sonnet-4-5", + "transpiler_name": "Switch", + }, + "source_dialect": "mssql", + "input_source": "input_sql", + "output_folder": "output_folder", + "sdk_config": None, + "skip_validation": False, + "catalog_name": "catalog", + "schema_name": "schema", + }, + "state.json": {"resources": {"jobs": {"Switch": f"{_JOB_ID}"}}, "version": 1}, + } + return MockInstallation(cast(dict[str, RootJsonValue], state)) + + +def test_llm_transpile_success( + mock_installation_with_switch: MockInstallation, + tmp_path: Path, + capsys, +) -> None: + """Test successful LLM transpile execution.""" + input_source = tmp_path / "input.sql" + input_source.write_text("SELECT * FROM table1;") + output_folder = "/Workspace/Users/test/output" + + # Use a dedicated WorkspaceClient mock tailored for SwitchRunner + mock_ws = create_switch_workspace_client_mock() + + ctx = ApplicationContext(mock_ws) + ctx.replace(installation=mock_installation_with_switch) + ctx.replace(add_user_agent_extra=lambda w, *args, **kwargs: w) + + cli.llm_transpile( + w=mock_ws, + input_source=str(input_source), + output_ws_folder=output_folder, + source_dialect="mssql", + ctx=ctx, + ) + + (out, _) = capsys.readouterr() + result = json.loads(out) + assert [ + { + "job_id": _JOB_ID, + "run_id": _RUN_ID, + "run_url": f"https://workspace.databricks.com/jobs/{_JOB_ID}/runs/{_RUN_ID}", + } + ] == result From 1e10b6052347ee56d78195e07b68e601e6fe41ef Mon Sep 17 00:00:00 2001 From: sundarshankar89 Date: Thu, 30 Oct 2025 11:13:21 +0530 Subject: [PATCH 16/17] added flag to fail if users use regular transpile after installing switch --- src/databricks/labs/lakebridge/cli.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/databricks/labs/lakebridge/cli.py b/src/databricks/labs/lakebridge/cli.py index 57a7306052..ffc8e98e8a 100644 --- a/src/databricks/labs/lakebridge/cli.py +++ b/src/databricks/labs/lakebridge/cli.py @@ -235,16 +235,20 @@ def _validate_transpiler_config_path(transpiler_config_path: str, msg: str) -> N def use_transpiler_config_path(self, transpiler_config_path: str | None) -> None: if transpiler_config_path is not None: logger.debug(f"Setting transpiler_config_path to: {transpiler_config_path!r}") - # Switch is installed inside "/Users/<>/.lakebridge/transpilers/Switch/lsp/config.yml - if Path(transpiler_config_path).parent.parent.name == "Switch": - msg = "Switch transpiler is not supported through `transpile` run `llm-transpile` instead." - raise_validation_exception(msg) self._validate_transpiler_config_path( transpiler_config_path, f"Invalid path for '--transpiler-config-path', does not exist: {transpiler_config_path}", ) self._config = dataclasses.replace(self._config, transpiler_config_path=transpiler_config_path) + # Switch is installed inside "/Users/<>/.lakebridge/transpilers/Switch/lsp/config.yml + if ( + self._config.transpiler_config_path is not None + and Path(self._config.transpiler_config_path).parent.parent.name == "Switch" + ): + msg = "Switch transpiler is not supported through `transpile` run `llm-transpile` instead." + raise RuntimeError(msg) + def use_source_dialect(self, source_dialect: str | None) -> None: if source_dialect is not None: # Defer validation: depends on the transpiler config path, we'll deal with this later. From 7a336f9a913e9655183c4cf8716ae5bf6a5d0483 Mon Sep 17 00:00:00 2001 From: sundarshankar89 Date: Fri, 31 Oct 2025 16:44:44 +0530 Subject: [PATCH 17/17] added additional tests improved cuj --- src/databricks/labs/lakebridge/cli.py | 61 +++++++++++++- tests/unit/test_cli_llm_transpile.py | 111 +++++++++++++++++++++++++- 2 files changed, 166 insertions(+), 6 deletions(-) diff --git a/src/databricks/labs/lakebridge/cli.py b/src/databricks/labs/lakebridge/cli.py index ffc8e98e8a..54d3bc5363 100644 --- a/src/databricks/labs/lakebridge/cli.py +++ b/src/databricks/labs/lakebridge/cli.py @@ -40,6 +40,7 @@ from databricks.labs.lakebridge.transpiler.transpile_engine import TranspileEngine from databricks.labs.lakebridge.transpiler.transpile_status import ErrorSeverity +from databricks.labs.switch.lsp import get_switch_dialects # Subclass to allow controlled access to protected methods. @@ -646,13 +647,56 @@ def _override_workspace_client_config(ctx: ApplicationContext, overrides: dict[s ctx.connect_config.cluster_id = cluster_id +def _validate_llm_transpile_args( + input_source: str | None, + output_ws_folder: str | None, + source_dialect: str | None, + transpile_config: TranspileConfig | None, + prompts: Prompts, +) -> tuple[str, str, str]: + + if input_source is None: + input_source = transpile_config.input_source if transpile_config else None + if output_ws_folder is None: + output_ws_folder = transpile_config.output_folder if transpile_config else None + if source_dialect is None: + source_dialect = transpile_config.source_dialect if transpile_config else None + + _switch_dialects = get_switch_dialects() + + # Validate presence after attempting to source from config + if not input_source: + input_source = prompts.question("Enter input SQL path") + if not output_ws_folder: + output_ws_folder = prompts.question("Enter output workspace folder must start with /Workspace/") + if not source_dialect: + source_dialect = prompts.choice("Select the source dialect", sorted(_switch_dialects)) + + # Validate input_source path exists (local path) + if not Path(input_source).exists(): + raise_validation_exception(f"Invalid path for '--input-source': Path '{input_source}' does not exist.") + + # Validate output_ws_folder is a workspace path + if not str(output_ws_folder).startswith("/Workspace/"): + raise_validation_exception( + f"Invalid value for '--output-ws-folder': workspace output path must start with /Workspace/. Got: {output_ws_folder!r}" + ) + + if source_dialect not in _switch_dialects: + raise_validation_exception( + f"Invalid value for '--source-dialect': {source_dialect!r} must be one of: {', '.join(sorted(_switch_dialects))}" + ) + + return input_source, output_ws_folder, source_dialect + + @lakebridge.command def llm_transpile( *, w: WorkspaceClient, - input_source: str, - output_ws_folder: str, - source_dialect: str, + input_source: str | None = None, + output_ws_folder: str | None = None, + source_dialect: str | None = None, ctx: ApplicationContext | None = None, ) -> None: """Transpile source code to Databricks using LLM Transpiler (Switch)""" @@ -663,6 +707,17 @@ def llm_transpile( user = ctx.current_user logger.debug(f"User: {user}") + prompts = ctx.prompts + transpile_config = ctx.transpile_config + # If CLI args are missing, try to read them from config.yml + input_source, output_ws_folder, source_dialect = _validate_llm_transpile_args( + input_source, + output_ws_folder, + source_dialect, + transpile_config, + prompts, + ) + job_list = ctx.install_state.jobs if "Switch" not in job_list: raise RuntimeError( diff --git a/tests/unit/test_cli_llm_transpile.py b/tests/unit/test_cli_llm_transpile.py index 2f6a8a5155..4e3ee97cf6 100644 --- a/tests/unit/test_cli_llm_transpile.py +++ b/tests/unit/test_cli_llm_transpile.py @@ -6,12 +6,26 @@ import pytest from databricks.labs.blueprint.installation import MockInstallation, RootJsonValue +from databricks.labs.blueprint.tui import MockPrompts +from databricks.labs.switch.lsp import get_switch_dialects + from databricks.labs.lakebridge import cli from databricks.labs.lakebridge.contexts.application import ApplicationContext from databricks.sdk import WorkspaceClient _JOB_ID = 1234567890 _RUN_ID = 123456789 +_switch_dialects = get_switch_dialects() + + +def make_mock_prompts(input_path: str, output_folder: str, source_dialect: str = "mssql") -> MockPrompts: + return MockPrompts( + { + r"Enter input SQL path": str(input_path), + r"Enter output workspace folder must start with /Workspace/": output_folder, + r"Select the source dialect": str(sorted(_switch_dialects).index(source_dialect)), + } + ) def create_switch_workspace_client_mock() -> WorkspaceClient: @@ -39,9 +53,9 @@ def mock_installation_with_switch() -> MockInstallation: "foundation_model": "databricks-claude-sonnet-4-5", "transpiler_name": "Switch", }, - "source_dialect": "mssql", - "input_source": "input_sql", - "output_folder": "output_folder", + "source_dialect": None, + "input_source": None, + "output_folder": None, "sdk_config": None, "skip_validation": False, "catalog_name": "catalog", @@ -86,3 +100,94 @@ def test_llm_transpile_success( "run_url": f"https://workspace.databricks.com/jobs/{_JOB_ID}/runs/{_RUN_ID}", } ] == result + + +def test_llm_transpile_without_parms( + mock_installation_with_switch: MockInstallation, + tmp_path: Path, + capsys, +) -> None: + """Test LLM transpile execution without parameters, relying on config file.""" + input_source = tmp_path / "input.sql" + input_source.write_text("SELECT * FROM table1;") + output_folder = "/Workspace/Users/test/output" + + mock_prompts = make_mock_prompts(str(input_source), output_folder, "mssql") + + # Use a dedicated WorkspaceClient mock tailored for SwitchRunner + mock_ws = create_switch_workspace_client_mock() + + ctx = ApplicationContext(mock_ws) + + ctx.replace( + installation=mock_installation_with_switch, + add_user_agent_extra=lambda w, *args, **kwargs: w, + prompts=mock_prompts, + ) + + cli.llm_transpile(w=mock_ws, ctx=ctx) + + (out, _) = capsys.readouterr() + result = json.loads(out) + assert [ + { + "job_id": _JOB_ID, + "run_id": _RUN_ID, + "run_url": f"https://workspace.databricks.com/jobs/{_JOB_ID}/runs/{_RUN_ID}", + } + ] == result + + +def test_llm_transpile_with_incorrect_output_parms( + mock_installation_with_switch: MockInstallation, + tmp_path: Path, +) -> None: + + input_source = tmp_path / "input.sql" + input_source.write_text("SELECT * FROM table1;") + output_folder = "/Users/test/output" + + mock_prompts = make_mock_prompts(str(input_source), output_folder, "mssql") + + # Use a dedicated WorkspaceClient mock tailored for SwitchRunner + mock_ws = create_switch_workspace_client_mock() + + ctx = ApplicationContext(mock_ws) + + ctx.replace( + installation=mock_installation_with_switch, + add_user_agent_extra=lambda w, *args, **kwargs: w, + prompts=mock_prompts, + ) + + error_msg = "Invalid value for '--output-ws-folder': workspace output path must start with /Workspace/. Got: '/Users/test/output'" + with pytest.raises(ValueError, match=rf"{error_msg}"): + cli.llm_transpile(w=mock_ws, output_ws_folder=output_folder, ctx=ctx) + + +def test_llm_transpile_with_incorrect_dialect( + mock_installation_with_switch: MockInstallation, + tmp_path: Path, +) -> None: + + input_source = tmp_path / "input.sql" + input_source.write_text("SELECT * FROM table1;") + output_folder = "/Workspace/Users/test/output" + + # passing this but will override below to mimic incorrect dialect scenario + mock_prompts = make_mock_prompts(str(input_source), output_folder, "mssql") + + # Use a dedicated WorkspaceClient mock tailored for SwitchRunner + mock_ws = create_switch_workspace_client_mock() + + ctx = ApplicationContext(mock_ws) + + ctx.replace( + installation=mock_installation_with_switch, + add_user_agent_extra=lambda w, *args, **kwargs: w, + prompts=mock_prompts, + ) + + error_msg = "Invalid value for '--source-dialect': 'agent_sql' must be one of: airflow, mssql, mysql, netezza, oracle, postgresql, redshift, snowflake, synapse, teradata" + with pytest.raises(ValueError, match=rf"{error_msg}"): + cli.llm_transpile(w=mock_ws, source_dialect="agent_sql", ctx=ctx)