From 4b30958c23a2f58681a4ce7c5bdd8951ee46c920 Mon Sep 17 00:00:00 2001 From: Tomasz Urbaszek Date: Mon, 26 Aug 2024 09:10:03 +0200 Subject: [PATCH] Refactor snowpark commands to be entity-centric (#1483) --- .../cli/_plugins/snowpark/commands.py | 124 ++-------- src/snowflake/cli/_plugins/snowpark/common.py | 217 ++++++++++-------- .../cli/_plugins/snowpark/manager.py | 111 --------- .../api/project/schemas/entities/common.py | 4 +- tests/snowpark/test_common.py | 78 +++++-- tests/snowpark/test_function.py | 4 +- tests/snowpark/test_procedure.py | 4 +- tests/streamlit/test_streamlit_manager.py | 2 +- 8 files changed, 203 insertions(+), 341 deletions(-) delete mode 100644 src/snowflake/cli/_plugins/snowpark/manager.py diff --git a/src/snowflake/cli/_plugins/snowpark/commands.py b/src/snowflake/cli/_plugins/snowpark/commands.py index 009bd7b0d4..8d7c3b5ca2 100644 --- a/src/snowflake/cli/_plugins/snowpark/commands.py +++ b/src/snowflake/cli/_plugins/snowpark/commands.py @@ -16,8 +16,7 @@ import logging from collections import defaultdict -from enum import Enum -from typing import Dict, List, Optional, Set, Tuple +from typing import Dict, Optional, Set, Tuple import typer from click import ClickException, UsageError @@ -36,16 +35,18 @@ from snowflake.cli._plugins.object.manager import ObjectManager from snowflake.cli._plugins.snowpark import package_utils from snowflake.cli._plugins.snowpark.common import ( - check_if_replace_is_required, + EntityToImportPathsMapping, + SnowparkEntities, + SnowparkObject, + SnowparkObjectManager, + StageToArtefactMapping, ) -from snowflake.cli._plugins.snowpark.manager import FunctionManager, ProcedureManager from snowflake.cli._plugins.snowpark.package.anaconda_packages import ( AnacondaPackages, AnacondaPackagesManager, ) from snowflake.cli._plugins.snowpark.package.commands import app as package_app from snowflake.cli._plugins.snowpark.snowpark_project_paths import ( - Artefact, SnowparkProjectPaths, ) from snowflake.cli._plugins.snowpark.snowpark_shared import ( @@ -72,7 +73,6 @@ from snowflake.cli.api.console import cli_console from snowflake.cli.api.constants import ( DEFAULT_SIZE_LIMIT_MB, - ObjectType, ) from snowflake.cli.api.exceptions import ( NoProjectDefinitionError, @@ -88,7 +88,6 @@ from snowflake.cli.api.project.schemas.entities.snowpark_entity import ( FunctionEntityModel, ProcedureEntityModel, - SnowparkEntityModel, ) from snowflake.cli.api.project.schemas.project_definition import ( ProjectDefinition, @@ -124,11 +123,6 @@ ) -SnowparkEntities = Dict[str, SnowparkEntityModel] -StageToArtefactMapping = Dict[str, set[Artefact]] -EntityToImportPathsMapping = Dict[str, set[str]] - - @app.command("deploy", requires_connection=True) @with_project_definition() def deploy( @@ -179,13 +173,13 @@ def deploy( # Create snowpark entities with cli_console.phase("Creating Snowpark entities"): + snowpark_manager = SnowparkObjectManager() snowflake_dependencies = _read_snowflake_requirements_file( project_paths.snowflake_requirements ) deploy_status = [] for entity in snowpark_entities.values(): - cli_console.step(f"Creating {entity.type} {entity.fqn}") - operation_result = _deploy_single_object( + operation_result = snowpark_manager.deploy_entity( entity=entity, existing_objects=existing_objects, snowflake_dependencies=snowflake_dependencies, @@ -280,7 +274,7 @@ def _find_existing_objects( def _check_if_all_defined_integrations_exists( om: ObjectManager, - snowpark_entities: Dict[str, FunctionEntityModel | ProcedureEntityModel], + snowpark_entities: SnowparkEntities, ): existing_integrations = { i["name"].lower() @@ -306,72 +300,6 @@ def _check_if_all_defined_integrations_exists( ) -def _deploy_single_object( - entity: SnowparkEntityModel, - existing_objects: Dict[str, SnowflakeCursor], - snowflake_dependencies: List[str], - entities_to_artifact_map: EntityToImportPathsMapping, -): - object_type = entity.get_type() - is_procedure = isinstance(entity, ProcedureEntityModel) - - handler = entity.handler - returns = entity.returns - imports = entity.imports - external_access_integrations = entity.external_access_integrations - runtime_ver = entity.runtime - execute_as_caller = None - if is_procedure: - execute_as_caller = entity.execute_as_caller - replace_object = False - - object_exists = entity.entity_id in existing_objects - if object_exists: - replace_object = check_if_replace_is_required( - object_type=object_type, - current_state=existing_objects[entity.entity_id], - handler=handler, - return_type=returns, - snowflake_dependencies=snowflake_dependencies, - external_access_integrations=external_access_integrations, - imports=imports, - stage_artifact_files=entities_to_artifact_map[entity.entity_id], - runtime_ver=runtime_ver, - execute_as_caller=execute_as_caller, - ) - - if object_exists and not replace_object: - return { - "object": entity.udf_sproc_identifier.identifier_with_arg_names_types_defaults, - "type": str(object_type), - "status": "packages updated", - } - - create_or_replace_kwargs = { - "identifier": entity.udf_sproc_identifier, - "handler": handler, - "return_type": returns, - "artifact_files": entities_to_artifact_map[entity.entity_id], - "packages": snowflake_dependencies, - "runtime": entity.runtime, - "external_access_integrations": entity.external_access_integrations, - "secrets": entity.secrets, - "imports": imports, - } - if is_procedure: - create_or_replace_kwargs["execute_as_caller"] = entity.execute_as_caller - - manager = ProcedureManager() if is_procedure else FunctionManager() - manager.create_or_replace(**create_or_replace_kwargs) - - status = "created" if not object_exists else "definition updated" - return { - "object": entity.udf_sproc_identifier.identifier_with_arg_names_types_defaults, - "type": str(object_type), - "status": status, - } - - def _read_snowflake_requirements_file(file_path: SecurePath): if not file_path.exists(): return [] @@ -470,46 +398,24 @@ def get_snowpark_entities( return snowpark_entities -class _SnowparkObject(Enum): - """This clas is used only for Snowpark execute where choice is limited.""" - - PROCEDURE = str(ObjectType.PROCEDURE) - FUNCTION = str(ObjectType.FUNCTION) - - -def _execute_object_method( - method_name: str, - object_type: _SnowparkObject, - **kwargs, -): - if object_type == _SnowparkObject.PROCEDURE: - manager = ProcedureManager() - elif object_type == _SnowparkObject.FUNCTION: - manager = FunctionManager() - else: - raise ClickException(f"Unknown object type: {object_type}") - - return getattr(manager, method_name)(**kwargs) - - @app.command("execute", requires_connection=True) def execute( - object_type: _SnowparkObject = ObjectTypeArgument, + object_type: SnowparkObject = ObjectTypeArgument, execution_identifier: str = execution_identifier_argument( "procedure/function", "hello(1, 'world')" ), **options, ) -> CommandResult: """Executes a procedure or function in a specified environment.""" - cursor = _execute_object_method( - "execute", object_type=object_type, execution_identifier=execution_identifier + cursor = SnowparkObjectManager().execute( + execution_identifier=execution_identifier, object_type=object_type ) return SingleQueryResult(cursor) @app.command("list", requires_connection=True) def list_( - object_type: _SnowparkObject = ObjectTypeArgument, + object_type: SnowparkObject = ObjectTypeArgument, like: str = LikeOption, scope: Tuple[str, str] = scope_option( help_example="`list function --in database my_db`" @@ -522,7 +428,7 @@ def list_( @app.command("drop", requires_connection=True) def drop( - object_type: _SnowparkObject = ObjectTypeArgument, + object_type: SnowparkObject = ObjectTypeArgument, identifier: FQN = IdentifierArgument, **options, ): @@ -532,7 +438,7 @@ def drop( @app.command("describe", requires_connection=True) def describe( - object_type: _SnowparkObject = ObjectTypeArgument, + object_type: SnowparkObject = ObjectTypeArgument, identifier: FQN = IdentifierArgument, **options, ): diff --git a/src/snowflake/cli/_plugins/snowpark/common.py b/src/snowflake/cli/_plugins/snowpark/common.py index 256631502c..48d258c6ac 100644 --- a/src/snowflake/cli/_plugins/snowpark/common.py +++ b/src/snowflake/cli/_plugins/snowpark/common.py @@ -14,46 +14,128 @@ from __future__ import annotations +import logging import re -from typing import Dict, List, Optional, Set +from enum import Enum +from typing import Dict, List, Set +from click import UsageError from snowflake.cli._plugins.snowpark.models import Requirement -from snowflake.cli._plugins.snowpark.package_utils import ( - generate_deploy_stage_name, -) +from snowflake.cli._plugins.snowpark.snowpark_project_paths import Artefact +from snowflake.cli.api.console import cli_console from snowflake.cli.api.constants import ObjectType from snowflake.cli.api.project.schemas.entities.snowpark_entity import ( - UdfSprocIdentifier, + ProcedureEntityModel, + SnowparkEntityModel, ) from snowflake.cli.api.sql_execution import SqlExecutionMixin from snowflake.connector.cursor import SnowflakeCursor +log = logging.getLogger(__name__) + +SnowparkEntities = Dict[str, SnowparkEntityModel] +StageToArtefactMapping = Dict[str, set[Artefact]] +EntityToImportPathsMapping = Dict[str, set[str]] + DEFAULT_RUNTIME = "3.10" -def check_if_replace_is_required( - object_type: str, +class SnowparkObject(Enum): + """This clas is used only for Snowpark execute where choice is limited.""" + + PROCEDURE = str(ObjectType.PROCEDURE) + FUNCTION = str(ObjectType.FUNCTION) + + +class SnowparkObjectManager(SqlExecutionMixin): + def execute( + self, execution_identifier: str, object_type: SnowparkObject + ) -> SnowflakeCursor: + if object_type == SnowparkObject.FUNCTION: + return self._execute_query(f"select {execution_identifier}") + if object_type == SnowparkObject.PROCEDURE: + return self._execute_query(f"call {execution_identifier}") + raise UsageError(f"Unknown object type: {object_type}.") + + def create_or_replace( + self, + entity: SnowparkEntityModel, + artifact_files: set[str], + snowflake_dependencies: list[str], + ) -> str: + entity.imports.extend(artifact_files) + imports = [f"'{x}'" for x in entity.imports] + packages_list = ",".join(f"'{p}'" for p in snowflake_dependencies) + + object_type = entity.get_type() + + query = [ + f"create or replace {object_type} {entity.udf_sproc_identifier.identifier_for_sql}", + f"copy grants", + f"returns {entity.returns}", + "language python", + f"runtime_version={entity.runtime or DEFAULT_RUNTIME}", + f"imports=({', '.join(imports)})", + f"handler='{entity.handler}'", + f"packages=({packages_list})", + ] + + if entity.external_access_integrations: + query.append(entity.get_external_access_integrations_sql()) + + if entity.secrets: + query.append(entity.get_secrets_sql()) + + if isinstance(entity, ProcedureEntityModel) and entity.execute_as_caller: + query.append("execute as caller") + + return self._execute_query("\n".join(query)) + + def deploy_entity( + self, + entity: SnowparkEntityModel, + existing_objects: Dict[str, SnowflakeCursor], + snowflake_dependencies: List[str], + entities_to_artifact_map: EntityToImportPathsMapping, + ): + cli_console.step(f"Creating {entity.type} {entity.fqn}") + object_exists = entity.entity_id in existing_objects + replace_object = False + if object_exists: + replace_object = _check_if_replace_is_required( + entity=entity, + current_state=existing_objects[entity.entity_id], + snowflake_dependencies=snowflake_dependencies, + stage_artifact_files=entities_to_artifact_map[entity.entity_id], + ) + + state = { + "object": entity.udf_sproc_identifier.identifier_with_arg_names_types_defaults, + "type": entity.get_type(), + } + if object_exists and not replace_object: + return {**state, "status": "packages updated"} + + self.create_or_replace( + entity=entity, + artifact_files=entities_to_artifact_map[entity.entity_id], + snowflake_dependencies=snowflake_dependencies, + ) + return { + **state, + "status": "created" if not object_exists else "definition updated", + } + + +def _check_if_replace_is_required( + entity: SnowparkEntityModel, current_state, - handler: str, - return_type: str, snowflake_dependencies: List[str], - external_access_integrations: List[str], - imports: List[str], stage_artifact_files: set[str], - runtime_ver: Optional[str] = None, - execute_as_caller: Optional[bool] = None, ) -> bool: - import logging - - log = logging.getLogger(__name__) + object_type = entity.get_type() resource_json = _convert_resource_details_to_dict(current_state) old_dependencies = resource_json["packages"] - log.info( - "Found %d defined Anaconda packages in deployed %s...", - len(old_dependencies), - object_type, - ) - log.info("Checking if app configuration has changed...") if _snowflake_dependencies_differ(old_dependencies, snowflake_dependencies): log.info( @@ -61,7 +143,7 @@ def check_if_replace_is_required( ) return True - if set(external_access_integrations) != set( + if set(entity.external_access_integrations) != set( resource_json.get("external_access_integrations", []) ): log.info( @@ -71,33 +153,33 @@ def check_if_replace_is_required( return True if ( - resource_json["handler"].lower() != handler.lower() + resource_json["handler"].lower() != entity.handler.lower() or _sql_to_python_return_type_mapper(resource_json["returns"]).lower() - != return_type.lower() + != entity.returns.lower() ): log.info( "Return type or handler types do not match. Replacing the %s.", object_type ) return True - if _compare_imports(resource_json, imports, stage_artifact_files): + if _compare_imports(resource_json, entity.imports, stage_artifact_files): log.info("Imports do not match. Replacing the %s", object_type) return True - if runtime_ver is not None and runtime_ver != resource_json.get( + if entity.runtime is not None and entity.runtime != resource_json.get( "runtime_version", "RUNTIME_NOT_SET" ): log.info("Runtime versions do not match. Replacing the %s", object_type) return True - if execute_as_caller is not None and ( - resource_json.get("execute as", "OWNER") - != ("CALLER" if execute_as_caller else "OWNER") - ): - log.info( - "Execute as caller settings do not match. Replacing the %s", object_type - ) - return True + if isinstance(entity, ProcedureEntityModel): + if resource_json.get("execute as", "OWNER") != ( + "CALLER" if entity.execute_as_caller else "OWNER" + ): + log.info( + "Execute as caller settings do not match. Replacing the %s", object_type + ) + return True return False @@ -148,71 +230,6 @@ def _sql_to_python_return_type_mapper(resource_return_type: str) -> str: return mapping.get(resource_return_type.lower(), resource_return_type.lower()) -class SnowparkObjectManager(SqlExecutionMixin): - @property - def _object_type(self) -> ObjectType: - raise NotImplementedError() - - @property - def _object_execute(self): - raise NotImplementedError() - - def create(self, *args, **kwargs) -> SnowflakeCursor: - raise NotImplementedError() - - def execute(self, execution_identifier: str) -> SnowflakeCursor: - return self._execute_query(f"{self._object_execute} {execution_identifier}") - - @staticmethod - def artifact_stage_path(identifier: str): - return generate_deploy_stage_name(identifier).lower() - - def create_query( - self, - identifier: UdfSprocIdentifier, - return_type: str, - handler: str, - artifact_files: set[str], - packages: List[str], - imports: List[str], - external_access_integrations: Optional[List[str]] = None, - secrets: Optional[Dict[str, str]] = None, - runtime: Optional[str] = None, - execute_as_caller: bool = False, - ) -> str: - imports.extend(artifact_files) - imports = [f"'{x}'" for x in imports] - packages_list = ",".join(f"'{p}'" for p in packages) - - query = [ - f"create or replace {self._object_type.value.sf_name} {identifier.identifier_for_sql}", - f"copy grants", - f"returns {return_type}", - "language python", - f"runtime_version={runtime or DEFAULT_RUNTIME}", - f"imports=({', '.join(imports)})", - f"handler='{handler}'", - f"packages=({packages_list})", - ] - - if external_access_integrations: - external_access_integration_name = ",".join( - f"{e}" for e in external_access_integrations - ) - query.append( - f"external_access_integrations=({external_access_integration_name})" - ) - - if secrets: - secret_name = ",".join(f"'{k}'={v}" for k, v in secrets.items()) - query.append(f"secrets=({secret_name})") - - if execute_as_caller: - query.append("execute as caller") - - return "\n".join(query) - - def _compare_imports( resource_json: dict, imports: List[str], artifact_files: set[str] ) -> bool: diff --git a/src/snowflake/cli/_plugins/snowpark/manager.py b/src/snowflake/cli/_plugins/snowpark/manager.py deleted file mode 100644 index 1bbbf16106..0000000000 --- a/src/snowflake/cli/_plugins/snowpark/manager.py +++ /dev/null @@ -1,111 +0,0 @@ -# Copyright (c) 2024 Snowflake Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from __future__ import annotations - -import logging -from typing import Dict, List, Optional - -from snowflake.cli._plugins.snowpark.common import ( - SnowparkObjectManager, -) -from snowflake.cli.api.constants import ObjectType -from snowflake.cli.api.project.schemas.entities.snowpark_entity import ( - UdfSprocIdentifier, -) -from snowflake.connector.cursor import SnowflakeCursor - -log = logging.getLogger(__name__) - - -class FunctionManager(SnowparkObjectManager): - @property - def _object_type(self): - return ObjectType.FUNCTION - - @property - def _object_execute(self): - return "select" - - def create_or_replace( - self, - identifier: UdfSprocIdentifier, - return_type: str, - handler: str, - artifact_files: set[str], - packages: List[str], - imports: List[str], - external_access_integrations: Optional[List[str]] = None, - secrets: Optional[Dict[str, str]] = None, - runtime: Optional[str] = None, - ) -> SnowflakeCursor: - log.debug( - "Creating function %s using @%s", - identifier.identifier_with_arg_names_types_defaults, - artifact_files, - ) - query = self.create_query( - identifier, - return_type, - handler, - artifact_files, - packages, - imports, - external_access_integrations, - secrets, - runtime, - ) - return self._execute_query(query) - - -class ProcedureManager(SnowparkObjectManager): - @property - def _object_type(self): - return ObjectType.PROCEDURE - - @property - def _object_execute(self): - return "call" - - def create_or_replace( - self, - identifier: UdfSprocIdentifier, - return_type: str, - handler: str, - artifact_files: set[str], - packages: List[str], - imports: List[str], - external_access_integrations: Optional[List[str]] = None, - secrets: Optional[Dict[str, str]] = None, - runtime: Optional[str] = None, - execute_as_caller: bool = False, - ) -> SnowflakeCursor: - log.debug( - "Creating procedure %s using @%s", - identifier.identifier_with_arg_names_types_defaults, - artifact_files, - ) - query = self.create_query( - identifier, - return_type, - handler, - artifact_files, - packages, - imports, - external_access_integrations, - secrets, - runtime, - execute_as_caller, - ) - return self._execute_query(query) diff --git a/src/snowflake/cli/api/project/schemas/entities/common.py b/src/snowflake/cli/api/project/schemas/entities/common.py index 46922588c1..4868f1f396 100644 --- a/src/snowflake/cli/api/project/schemas/entities/common.py +++ b/src/snowflake/cli/api/project/schemas/entities/common.py @@ -135,5 +135,5 @@ def get_external_access_integrations_sql(self) -> str | None: def get_secrets_sql(self) -> str | None: if not self.secrets: return None - secrets = ", ".join(f"'{key}' = {value}" for key, value in self.secrets.items()) - return f"secrets = ({secrets})" + secrets = ", ".join(f"'{key}'={value}" for key, value in self.secrets.items()) + return f"secrets=({secrets})" diff --git a/tests/snowpark/test_common.py b/tests/snowpark/test_common.py index f381ee7fe7..d9ecf65cb3 100644 --- a/tests/snowpark/test_common.py +++ b/tests/snowpark/test_common.py @@ -18,10 +18,13 @@ import pytest from snowflake.cli._plugins.snowpark.common import ( + _check_if_replace_is_required, _convert_resource_details_to_dict, _snowflake_dependencies_differ, _sql_to_python_return_type_mapper, - check_if_replace_is_required, +) +from snowflake.cli.api.project.schemas.entities.snowpark_entity import ( + ProcedureEntityModel, ) @@ -79,11 +82,51 @@ def test_sql_to_python_return_type_mapper(argument: Tuple[str, str]): [ ({}, False), ({"handler": "app.another_procedure"}, True), - ({"return_type": "variant"}, True), - ({"snowflake_dependencies": ["snowflake-snowpark-python", "pandas"]}, True), + ({"returns": "variant"}, True), ({"external_access_integrations": ["My_Integration"]}, True), ({"imports": ["@FOO.BAR.BAZ/some_project/some_package.zip"]}, True), ({"imports": ["@FOO.BAR.BAZ/my_snowpark_project/app.zip"]}, False), + ({"runtime": "3.9"}, True), + ({"execute_as_caller": False}, True), + ], +) +def test_check_if_replace_is_required_entity_changes( + mock_procedure_description, arguments, expected +): + entity_spec = { + "type": "procedure", + "handler": "app.hello_procedure", + "signature": "(NAME VARCHAR)", + "artifacts": [], + "stage": "foo", + "returns": "string", + "external_access_integrations": [], + "imports": [], + "runtime": "3.10", + "execute_as_caller": True, + } + entity_spec.update(arguments) + + entity = ProcedureEntityModel(**entity_spec) + + assert ( + _check_if_replace_is_required( + entity=entity, + current_state=mock_procedure_description, + snowflake_dependencies=[ + "snowflake-snowpark-python", + "pytest<9.0.0,>=7.0.0", + ], + stage_artifact_files={"@FOO.BAR.BAZ/my_snowpark_project/app.zip"}, + ) + == expected + ) + + +@pytest.mark.parametrize( + "arguments, expected", + [ + ({"snowflake_dependencies": ["snowflake-snowpark-python", "pandas"]}, True), ( { "stage_artifact_files": [ @@ -92,26 +135,33 @@ def test_sql_to_python_return_type_mapper(argument: Tuple[str, str]): }, True, ), - ({"runtime_ver": "3.9"}, True), - ({"execute_as_caller": False}, True), ], ) -def test_check_if_replace_is_required(mock_procedure_description, arguments, expected): - replace_arguments = { +def test_check_if_replace_is_required_file_changes( + mock_procedure_description, arguments, expected +): + entity_spec = { + "type": "procedure", "handler": "app.hello_procedure", - "return_type": "string", - "snowflake_dependencies": ["snowflake-snowpark-python", "pytest<9.0.0,>=7.0.0"], + "signature": "(NAME VARCHAR)", + "artifacts": [], + "stage": "foo", + "returns": "string", "external_access_integrations": [], "imports": [], - "stage_artifact_files": ["@FOO.BAR.BAZ/my_snowpark_project/app.zip"], - "runtime_ver": "3.10", + "runtime": "3.10", "execute_as_caller": True, } - replace_arguments.update(arguments) + entity = ProcedureEntityModel(**entity_spec) + kwargs = { + "snowflake_dependencies": ["snowflake-snowpark-python", "pytest<9.0.0,>=7.0.0"], + "stage_artifact_files": {"@FOO.BAR.BAZ/my_snowpark_project/app.zip"}, + } + kwargs.update(arguments) assert ( - check_if_replace_is_required( - "procedure", mock_procedure_description, **replace_arguments + _check_if_replace_is_required( + entity=entity, current_state=mock_procedure_description, **kwargs ) == expected ) diff --git a/tests/snowpark/test_function.py b/tests/snowpark/test_function.py index 1f3ce17de3..dab8b3483d 100644 --- a/tests/snowpark/test_function.py +++ b/tests/snowpark/test_function.py @@ -113,8 +113,8 @@ def test_deploy_function_with_external_access( imports=('@MockDatabase.MockSchema.dev_deployment/my_snowpark_project/app.py') handler='app.func1_handler' packages=() - external_access_integrations=(external_1,external_2) - secrets=('cred'=cred_name,'other'=other_name) + external_access_integrations=(external_1, external_2) + secrets=('cred'=cred_name, 'other'=other_name) """ ).strip(), ] diff --git a/tests/snowpark/test_procedure.py b/tests/snowpark/test_procedure.py index 97f7a960d2..275b464f23 100644 --- a/tests/snowpark/test_procedure.py +++ b/tests/snowpark/test_procedure.py @@ -158,8 +158,8 @@ def test_deploy_procedure_with_external_access( imports=('@MockDatabase.MockSchema.dev_deployment/my_snowpark_project/app.py') handler='app.hello' packages=() - external_access_integrations=(external_1,external_2) - secrets=('cred'=cred_name,'other'=other_name) + external_access_integrations=(external_1, external_2) + secrets=('cred'=cred_name, 'other'=other_name) """ ).strip(), ] diff --git a/tests/streamlit/test_streamlit_manager.py b/tests/streamlit/test_streamlit_manager.py index dd17f3d426..9cf4e01509 100644 --- a/tests/streamlit/test_streamlit_manager.py +++ b/tests/streamlit/test_streamlit_manager.py @@ -80,6 +80,6 @@ def test_deploy_streamlit_with_api_integrations( QUERY_WAREHOUSE = My_WH TITLE = 'MyStreamlit' external_access_integrations=(MY_INTERGATION, OTHER) - secrets = ('my_secret' = SecretOfTheSecrets, 'other' = other_secret)""" + secrets=('my_secret'=SecretOfTheSecrets, 'other'=other_secret)""" ) )