snowflake-cli-labs 3.0.0rc5__py3-none-any.whl → 3.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- README.md +21 -0
- {snowflake_cli_labs-3.0.0rc5.dist-info → snowflake_cli_labs-3.0.2.dist-info}/METADATA +6 -96
- snowflake_cli_labs-3.0.2.dist-info/RECORD +5 -0
- snowflake/cli/__about__.py +0 -17
- snowflake/cli/__init__.py +0 -13
- snowflake/cli/_app/__init__.py +0 -22
- snowflake/cli/_app/__main__.py +0 -31
- snowflake/cli/_app/api_impl/__init__.py +0 -13
- snowflake/cli/_app/api_impl/plugin/__init__.py +0 -13
- snowflake/cli/_app/api_impl/plugin/plugin_config_provider_impl.py +0 -66
- snowflake/cli/_app/cli_app.py +0 -252
- snowflake/cli/_app/commands_registration/__init__.py +0 -33
- snowflake/cli/_app/commands_registration/builtin_plugins.py +0 -50
- snowflake/cli/_app/commands_registration/command_plugins_loader.py +0 -169
- snowflake/cli/_app/commands_registration/commands_registration_with_callbacks.py +0 -105
- snowflake/cli/_app/commands_registration/exception_logging.py +0 -26
- snowflake/cli/_app/commands_registration/threadsafe.py +0 -48
- snowflake/cli/_app/commands_registration/typer_registration.py +0 -153
- snowflake/cli/_app/constants.py +0 -19
- snowflake/cli/_app/dev/__init__.py +0 -13
- snowflake/cli/_app/dev/commands_structure.py +0 -48
- snowflake/cli/_app/dev/docs/__init__.py +0 -13
- snowflake/cli/_app/dev/docs/commands_docs_generator.py +0 -118
- snowflake/cli/_app/dev/docs/generator.py +0 -35
- snowflake/cli/_app/dev/docs/project_definition_docs_generator.py +0 -58
- snowflake/cli/_app/dev/docs/project_definition_generate_json_schema.py +0 -227
- snowflake/cli/_app/dev/docs/template_utils.py +0 -23
- snowflake/cli/_app/dev/docs/templates/definition_description.rst.jinja2 +0 -38
- snowflake/cli/_app/dev/docs/templates/overview.rst.jinja2 +0 -9
- snowflake/cli/_app/dev/docs/templates/usage.rst.jinja2 +0 -67
- snowflake/cli/_app/dev/pycharm_remote_debug.py +0 -46
- snowflake/cli/_app/loggers.py +0 -199
- snowflake/cli/_app/main_typer.py +0 -62
- snowflake/cli/_app/printing.py +0 -181
- snowflake/cli/_app/secret.py +0 -9
- snowflake/cli/_app/snow_connector.py +0 -309
- snowflake/cli/_app/telemetry.py +0 -220
- snowflake/cli/_app/version_check.py +0 -74
- snowflake/cli/_plugins/__init__.py +0 -13
- snowflake/cli/_plugins/connection/__init__.py +0 -13
- snowflake/cli/_plugins/connection/commands.py +0 -353
- snowflake/cli/_plugins/connection/plugin_spec.py +0 -30
- snowflake/cli/_plugins/connection/util.py +0 -195
- snowflake/cli/_plugins/cortex/__init__.py +0 -13
- snowflake/cli/_plugins/cortex/commands.py +0 -332
- snowflake/cli/_plugins/cortex/constants.py +0 -17
- snowflake/cli/_plugins/cortex/manager.py +0 -189
- snowflake/cli/_plugins/cortex/plugin_spec.py +0 -30
- snowflake/cli/_plugins/cortex/types.py +0 -22
- snowflake/cli/_plugins/git/__init__.py +0 -13
- snowflake/cli/_plugins/git/commands.py +0 -358
- snowflake/cli/_plugins/git/manager.py +0 -151
- snowflake/cli/_plugins/git/plugin_spec.py +0 -30
- snowflake/cli/_plugins/helpers/__init__.py +0 -13
- snowflake/cli/_plugins/helpers/commands.py +0 -90
- snowflake/cli/_plugins/helpers/plugin_spec.py +0 -30
- snowflake/cli/_plugins/init/__init__.py +0 -13
- snowflake/cli/_plugins/init/commands.py +0 -248
- snowflake/cli/_plugins/init/plugin_spec.py +0 -30
- snowflake/cli/_plugins/nativeapp/__init__.py +0 -13
- snowflake/cli/_plugins/nativeapp/artifacts.py +0 -757
- snowflake/cli/_plugins/nativeapp/bundle_context.py +0 -31
- snowflake/cli/_plugins/nativeapp/codegen/__init__.py +0 -13
- snowflake/cli/_plugins/nativeapp/codegen/artifact_processor.py +0 -91
- snowflake/cli/_plugins/nativeapp/codegen/compiler.py +0 -149
- snowflake/cli/_plugins/nativeapp/codegen/sandbox.py +0 -306
- snowflake/cli/_plugins/nativeapp/codegen/setup/native_app_setup_processor.py +0 -249
- snowflake/cli/_plugins/nativeapp/codegen/setup/setup_driver.py.source +0 -59
- snowflake/cli/_plugins/nativeapp/codegen/snowpark/callback_source.py.jinja +0 -181
- snowflake/cli/_plugins/nativeapp/codegen/snowpark/extension_function_utils.py +0 -217
- snowflake/cli/_plugins/nativeapp/codegen/snowpark/models.py +0 -61
- snowflake/cli/_plugins/nativeapp/codegen/snowpark/python_processor.py +0 -523
- snowflake/cli/_plugins/nativeapp/codegen/templates/templates_processor.py +0 -114
- snowflake/cli/_plugins/nativeapp/commands.py +0 -559
- snowflake/cli/_plugins/nativeapp/common_flags.py +0 -44
- snowflake/cli/_plugins/nativeapp/constants.py +0 -27
- snowflake/cli/_plugins/nativeapp/entities/__init__.py +0 -0
- snowflake/cli/_plugins/nativeapp/entities/application.py +0 -878
- snowflake/cli/_plugins/nativeapp/entities/application_package.py +0 -1392
- snowflake/cli/_plugins/nativeapp/exceptions.py +0 -113
- snowflake/cli/_plugins/nativeapp/feature_flags.py +0 -24
- snowflake/cli/_plugins/nativeapp/manager.py +0 -415
- snowflake/cli/_plugins/nativeapp/plugin_spec.py +0 -30
- snowflake/cli/_plugins/nativeapp/policy.py +0 -53
- snowflake/cli/_plugins/nativeapp/project_model.py +0 -211
- snowflake/cli/_plugins/nativeapp/run_processor.py +0 -184
- snowflake/cli/_plugins/nativeapp/same_account_install_method.py +0 -70
- snowflake/cli/_plugins/nativeapp/teardown_processor.py +0 -70
- snowflake/cli/_plugins/nativeapp/utils.py +0 -98
- snowflake/cli/_plugins/nativeapp/v2_conversions/v2_to_v1_decorator.py +0 -262
- snowflake/cli/_plugins/nativeapp/version/__init__.py +0 -13
- snowflake/cli/_plugins/nativeapp/version/commands.py +0 -141
- snowflake/cli/_plugins/nativeapp/version/version_processor.py +0 -98
- snowflake/cli/_plugins/notebook/__init__.py +0 -13
- snowflake/cli/_plugins/notebook/commands.py +0 -86
- snowflake/cli/_plugins/notebook/exceptions.py +0 -20
- snowflake/cli/_plugins/notebook/manager.py +0 -71
- snowflake/cli/_plugins/notebook/plugin_spec.py +0 -30
- snowflake/cli/_plugins/notebook/types.py +0 -15
- snowflake/cli/_plugins/object/__init__.py +0 -13
- snowflake/cli/_plugins/object/command_aliases.py +0 -95
- snowflake/cli/_plugins/object/commands.py +0 -180
- snowflake/cli/_plugins/object/common.py +0 -85
- snowflake/cli/_plugins/object/manager.py +0 -118
- snowflake/cli/_plugins/object/plugin_spec.py +0 -30
- snowflake/cli/_plugins/snowpark/__init__.py +0 -13
- snowflake/cli/_plugins/snowpark/commands.py +0 -450
- snowflake/cli/_plugins/snowpark/common.py +0 -268
- snowflake/cli/_plugins/snowpark/models.py +0 -150
- snowflake/cli/_plugins/snowpark/package/__init__.py +0 -13
- snowflake/cli/_plugins/snowpark/package/anaconda_packages.py +0 -199
- snowflake/cli/_plugins/snowpark/package/commands.py +0 -195
- snowflake/cli/_plugins/snowpark/package/manager.py +0 -44
- snowflake/cli/_plugins/snowpark/package/utils.py +0 -26
- snowflake/cli/_plugins/snowpark/package_utils.py +0 -354
- snowflake/cli/_plugins/snowpark/plugin_spec.py +0 -30
- snowflake/cli/_plugins/snowpark/snowpark_entity.py +0 -29
- snowflake/cli/_plugins/snowpark/snowpark_entity_model.py +0 -173
- snowflake/cli/_plugins/snowpark/snowpark_project_paths.py +0 -109
- snowflake/cli/_plugins/snowpark/snowpark_shared.py +0 -59
- snowflake/cli/_plugins/snowpark/zipper.py +0 -89
- snowflake/cli/_plugins/spcs/__init__.py +0 -33
- snowflake/cli/_plugins/spcs/common.py +0 -99
- snowflake/cli/_plugins/spcs/compute_pool/__init__.py +0 -13
- snowflake/cli/_plugins/spcs/compute_pool/commands.py +0 -241
- snowflake/cli/_plugins/spcs/compute_pool/manager.py +0 -121
- snowflake/cli/_plugins/spcs/image_registry/__init__.py +0 -13
- snowflake/cli/_plugins/spcs/image_registry/commands.py +0 -65
- snowflake/cli/_plugins/spcs/image_registry/manager.py +0 -105
- snowflake/cli/_plugins/spcs/image_repository/__init__.py +0 -13
- snowflake/cli/_plugins/spcs/image_repository/commands.py +0 -202
- snowflake/cli/_plugins/spcs/image_repository/manager.py +0 -84
- snowflake/cli/_plugins/spcs/plugin_spec.py +0 -30
- snowflake/cli/_plugins/spcs/services/__init__.py +0 -13
- snowflake/cli/_plugins/spcs/services/commands.py +0 -345
- snowflake/cli/_plugins/spcs/services/manager.py +0 -208
- snowflake/cli/_plugins/sql/__init__.py +0 -13
- snowflake/cli/_plugins/sql/commands.py +0 -86
- snowflake/cli/_plugins/sql/manager.py +0 -92
- snowflake/cli/_plugins/sql/plugin_spec.py +0 -30
- snowflake/cli/_plugins/sql/snowsql_templating.py +0 -28
- snowflake/cli/_plugins/stage/__init__.py +0 -13
- snowflake/cli/_plugins/stage/commands.py +0 -264
- snowflake/cli/_plugins/stage/diff.py +0 -280
- snowflake/cli/_plugins/stage/manager.py +0 -582
- snowflake/cli/_plugins/stage/md5.py +0 -160
- snowflake/cli/_plugins/stage/plugin_spec.py +0 -30
- snowflake/cli/_plugins/stage/utils.py +0 -54
- snowflake/cli/_plugins/streamlit/__init__.py +0 -13
- snowflake/cli/_plugins/streamlit/commands.py +0 -195
- snowflake/cli/_plugins/streamlit/manager.py +0 -220
- snowflake/cli/_plugins/streamlit/plugin_spec.py +0 -30
- snowflake/cli/_plugins/streamlit/streamlit_entity.py +0 -12
- snowflake/cli/_plugins/streamlit/streamlit_entity_model.py +0 -66
- snowflake/cli/_plugins/workspace/__init__.py +0 -13
- snowflake/cli/_plugins/workspace/action_context.py +0 -18
- snowflake/cli/_plugins/workspace/commands.py +0 -306
- snowflake/cli/_plugins/workspace/manager.py +0 -74
- snowflake/cli/_plugins/workspace/plugin_spec.py +0 -30
- snowflake/cli/api/__init__.py +0 -48
- snowflake/cli/api/cli_global_context.py +0 -247
- snowflake/cli/api/commands/__init__.py +0 -13
- snowflake/cli/api/commands/alias.py +0 -23
- snowflake/cli/api/commands/common.py +0 -25
- snowflake/cli/api/commands/decorators.py +0 -369
- snowflake/cli/api/commands/execution_metadata.py +0 -40
- snowflake/cli/api/commands/experimental_behaviour.py +0 -18
- snowflake/cli/api/commands/flags.py +0 -561
- snowflake/cli/api/commands/overrideable_parameter.py +0 -143
- snowflake/cli/api/commands/snow_typer.py +0 -247
- snowflake/cli/api/commands/utils.py +0 -18
- snowflake/cli/api/config.py +0 -380
- snowflake/cli/api/connections.py +0 -216
- snowflake/cli/api/console/__init__.py +0 -17
- snowflake/cli/api/console/abc.py +0 -94
- snowflake/cli/api/console/console.py +0 -134
- snowflake/cli/api/console/enum.py +0 -17
- snowflake/cli/api/constants.py +0 -90
- snowflake/cli/api/entities/common.py +0 -56
- snowflake/cli/api/entities/utils.py +0 -370
- snowflake/cli/api/errno.py +0 -28
- snowflake/cli/api/exceptions.py +0 -190
- snowflake/cli/api/feature_flags.py +0 -54
- snowflake/cli/api/identifiers.py +0 -190
- snowflake/cli/api/metrics.py +0 -92
- snowflake/cli/api/output/__init__.py +0 -13
- snowflake/cli/api/output/formats.py +0 -20
- snowflake/cli/api/output/types.py +0 -118
- snowflake/cli/api/plugins/__init__.py +0 -13
- snowflake/cli/api/plugins/command/__init__.py +0 -72
- snowflake/cli/api/plugins/command/plugin_hook_specs.py +0 -21
- snowflake/cli/api/plugins/plugin_config.py +0 -32
- snowflake/cli/api/project/__init__.py +0 -13
- snowflake/cli/api/project/definition.py +0 -126
- snowflake/cli/api/project/definition_conversion.py +0 -400
- snowflake/cli/api/project/definition_manager.py +0 -145
- snowflake/cli/api/project/errors.py +0 -56
- snowflake/cli/api/project/project_verification.py +0 -23
- snowflake/cli/api/project/schemas/__init__.py +0 -13
- snowflake/cli/api/project/schemas/entities/__init__.py +0 -13
- snowflake/cli/api/project/schemas/entities/common.py +0 -153
- snowflake/cli/api/project/schemas/entities/entities.py +0 -61
- snowflake/cli/api/project/schemas/project_definition.py +0 -330
- snowflake/cli/api/project/schemas/template.py +0 -77
- snowflake/cli/api/project/schemas/updatable_model.py +0 -202
- snowflake/cli/api/project/schemas/v1/__init__.py +0 -0
- snowflake/cli/api/project/schemas/v1/identifier_model.py +0 -51
- snowflake/cli/api/project/schemas/v1/native_app/__init__.py +0 -0
- snowflake/cli/api/project/schemas/v1/native_app/application.py +0 -61
- snowflake/cli/api/project/schemas/v1/native_app/native_app.py +0 -93
- snowflake/cli/api/project/schemas/v1/native_app/package.py +0 -84
- snowflake/cli/api/project/schemas/v1/native_app/path_mapping.py +0 -65
- snowflake/cli/api/project/schemas/v1/snowpark/__init__.py +0 -0
- snowflake/cli/api/project/schemas/v1/snowpark/argument.py +0 -28
- snowflake/cli/api/project/schemas/v1/snowpark/callable.py +0 -69
- snowflake/cli/api/project/schemas/v1/snowpark/snowpark.py +0 -36
- snowflake/cli/api/project/schemas/v1/streamlit/__init__.py +0 -0
- snowflake/cli/api/project/schemas/v1/streamlit/streamlit.py +0 -47
- snowflake/cli/api/project/util.py +0 -278
- snowflake/cli/api/rendering/__init__.py +0 -13
- snowflake/cli/api/rendering/jinja.py +0 -118
- snowflake/cli/api/rendering/project_definition_templates.py +0 -43
- snowflake/cli/api/rendering/project_templates.py +0 -98
- snowflake/cli/api/rendering/sql_templates.py +0 -105
- snowflake/cli/api/rest_api.py +0 -178
- snowflake/cli/api/sanitizers.py +0 -43
- snowflake/cli/api/secure_path.py +0 -360
- snowflake/cli/api/secure_utils.py +0 -118
- snowflake/cli/api/sql_execution.py +0 -280
- snowflake/cli/api/utils/__init__.py +0 -13
- snowflake/cli/api/utils/cursor.py +0 -34
- snowflake/cli/api/utils/definition_rendering.py +0 -415
- snowflake/cli/api/utils/dict_utils.py +0 -73
- snowflake/cli/api/utils/error_handling.py +0 -23
- snowflake/cli/api/utils/graph.py +0 -97
- snowflake/cli/api/utils/models.py +0 -63
- snowflake/cli/api/utils/naming_utils.py +0 -13
- snowflake/cli/api/utils/path_utils.py +0 -36
- snowflake/cli/api/utils/templating_functions.py +0 -144
- snowflake/cli/api/utils/types.py +0 -35
- snowflake_cli_labs-3.0.0rc5.dist-info/RECORD +0 -242
- snowflake_cli_labs-3.0.0rc5.dist-info/entry_points.txt +0 -2
- {snowflake_cli_labs-3.0.0rc5.dist-info → snowflake_cli_labs-3.0.2.dist-info}/WHEEL +0 -0
- {snowflake_cli_labs-3.0.0rc5.dist-info → snowflake_cli_labs-3.0.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,582 +0,0 @@
|
|
|
1
|
-
# Copyright (c) 2024 Snowflake Inc.
|
|
2
|
-
#
|
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
# you may not use this file except in compliance with the License.
|
|
5
|
-
# You may obtain a copy of the License at
|
|
6
|
-
#
|
|
7
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
#
|
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
# See the License for the specific language governing permissions and
|
|
13
|
-
# limitations under the License.
|
|
14
|
-
|
|
15
|
-
from __future__ import annotations
|
|
16
|
-
|
|
17
|
-
import fnmatch
|
|
18
|
-
import glob
|
|
19
|
-
import logging
|
|
20
|
-
import re
|
|
21
|
-
import sys
|
|
22
|
-
from contextlib import nullcontext
|
|
23
|
-
from dataclasses import dataclass
|
|
24
|
-
from os import path
|
|
25
|
-
from pathlib import Path
|
|
26
|
-
from textwrap import dedent
|
|
27
|
-
from typing import Dict, List, Optional, Union
|
|
28
|
-
|
|
29
|
-
from click import ClickException
|
|
30
|
-
from snowflake.cli._plugins.snowpark.package_utils import parse_requirements
|
|
31
|
-
from snowflake.cli.api.commands.common import (
|
|
32
|
-
OnErrorType,
|
|
33
|
-
Variable,
|
|
34
|
-
)
|
|
35
|
-
from snowflake.cli.api.commands.utils import parse_key_value_variables
|
|
36
|
-
from snowflake.cli.api.console import cli_console
|
|
37
|
-
from snowflake.cli.api.constants import PYTHON_3_12
|
|
38
|
-
from snowflake.cli.api.identifiers import FQN
|
|
39
|
-
from snowflake.cli.api.project.util import to_string_literal
|
|
40
|
-
from snowflake.cli.api.secure_path import SecurePath
|
|
41
|
-
from snowflake.cli.api.sql_execution import SqlExecutionMixin
|
|
42
|
-
from snowflake.cli.api.utils.path_utils import path_resolver
|
|
43
|
-
from snowflake.connector import DictCursor, ProgrammingError
|
|
44
|
-
from snowflake.connector.cursor import SnowflakeCursor
|
|
45
|
-
|
|
46
|
-
if sys.version_info < PYTHON_3_12:
|
|
47
|
-
# Because Snowpark works only below 3.12 and to use @sproc Session must be imported here.
|
|
48
|
-
from snowflake.snowpark import Session
|
|
49
|
-
|
|
50
|
-
log = logging.getLogger(__name__)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
UNQUOTED_FILE_URI_REGEX = r"[\w/*?\-.=&{}$#[\]\"\\!@%^+:]+"
|
|
54
|
-
USER_STAGE_PREFIX = "@~"
|
|
55
|
-
EXECUTE_SUPPORTED_FILES_FORMATS = (
|
|
56
|
-
".sql",
|
|
57
|
-
".py",
|
|
58
|
-
) # tuple to preserve order but it's a set
|
|
59
|
-
|
|
60
|
-
# Replace magic numbers with constants
|
|
61
|
-
OMIT_FIRST = slice(1, None)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
@dataclass
|
|
65
|
-
class StagePathParts:
|
|
66
|
-
directory: str
|
|
67
|
-
stage: str
|
|
68
|
-
stage_name: str
|
|
69
|
-
is_directory: bool
|
|
70
|
-
|
|
71
|
-
@classmethod
|
|
72
|
-
def get_directory(cls, stage_path: str) -> str:
|
|
73
|
-
return "/".join(Path(stage_path).parts[OMIT_FIRST])
|
|
74
|
-
|
|
75
|
-
@property
|
|
76
|
-
def path(self) -> str:
|
|
77
|
-
raise NotImplementedError
|
|
78
|
-
|
|
79
|
-
@property
|
|
80
|
-
def full_path(self) -> str:
|
|
81
|
-
raise NotImplementedError
|
|
82
|
-
|
|
83
|
-
def replace_stage_prefix(self, file_path: str) -> str:
|
|
84
|
-
raise NotImplementedError
|
|
85
|
-
|
|
86
|
-
def add_stage_prefix(self, file_path: str) -> str:
|
|
87
|
-
raise NotImplementedError
|
|
88
|
-
|
|
89
|
-
def get_directory_from_file_path(self, file_path: str) -> List[str]:
|
|
90
|
-
raise NotImplementedError
|
|
91
|
-
|
|
92
|
-
def get_full_stage_path(self, path: str):
|
|
93
|
-
if prefix := FQN.from_stage(self.stage).prefix:
|
|
94
|
-
return prefix + "." + path
|
|
95
|
-
return path
|
|
96
|
-
|
|
97
|
-
def get_standard_stage_path(self) -> str:
|
|
98
|
-
path = self.path
|
|
99
|
-
return f"@{path}{'/'if self.is_directory and not path.endswith('/') else ''}"
|
|
100
|
-
|
|
101
|
-
def get_standard_stage_directory_path(self) -> str:
|
|
102
|
-
path = self.get_standard_stage_path()
|
|
103
|
-
if not path.endswith("/"):
|
|
104
|
-
return path + "/"
|
|
105
|
-
return path
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
@dataclass
|
|
109
|
-
class DefaultStagePathParts(StagePathParts):
|
|
110
|
-
"""
|
|
111
|
-
For path like @db.schema.stage/dir the values will be:
|
|
112
|
-
directory = dir
|
|
113
|
-
stage = @db.schema.stage
|
|
114
|
-
stage_name = stage
|
|
115
|
-
For `@stage/dir` to
|
|
116
|
-
stage -> @stage
|
|
117
|
-
stage_name -> stage
|
|
118
|
-
directory -> dir
|
|
119
|
-
"""
|
|
120
|
-
|
|
121
|
-
def __init__(self, stage_path: str):
|
|
122
|
-
self.directory = self.get_directory(stage_path)
|
|
123
|
-
self.stage = StageManager.get_stage_from_path(stage_path)
|
|
124
|
-
stage_name = self.stage.split(".")[-1]
|
|
125
|
-
stage_name = (
|
|
126
|
-
stage_name[OMIT_FIRST] if stage_name.startswith("@") else stage_name
|
|
127
|
-
)
|
|
128
|
-
self.stage_name = stage_name
|
|
129
|
-
self.is_directory = True if stage_path.endswith("/") else False
|
|
130
|
-
|
|
131
|
-
@property
|
|
132
|
-
def path(self) -> str:
|
|
133
|
-
return f"{self.stage_name.rstrip('/')}/{self.directory}"
|
|
134
|
-
|
|
135
|
-
@property
|
|
136
|
-
def full_path(self) -> str:
|
|
137
|
-
return f"{self.stage.rstrip('/')}/{self.directory}"
|
|
138
|
-
|
|
139
|
-
def replace_stage_prefix(self, file_path: str) -> str:
|
|
140
|
-
stage = Path(self.stage).parts[0]
|
|
141
|
-
file_path_without_prefix = Path(file_path).parts[OMIT_FIRST]
|
|
142
|
-
return f"{stage}/{'/'.join(file_path_without_prefix)}"
|
|
143
|
-
|
|
144
|
-
def add_stage_prefix(self, file_path: str) -> str:
|
|
145
|
-
stage = self.stage.rstrip("/")
|
|
146
|
-
return f"{stage}/{file_path.lstrip('/')}"
|
|
147
|
-
|
|
148
|
-
def get_directory_from_file_path(self, file_path: str) -> List[str]:
|
|
149
|
-
stage_path_length = len(Path(self.directory).parts)
|
|
150
|
-
return list(Path(file_path).parts[1 + stage_path_length : -1])
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
@dataclass
|
|
154
|
-
class UserStagePathParts(StagePathParts):
|
|
155
|
-
"""
|
|
156
|
-
For path like @db.schema.stage/dir the values will be:
|
|
157
|
-
directory = dir
|
|
158
|
-
stage = @~
|
|
159
|
-
stage_name = @~
|
|
160
|
-
"""
|
|
161
|
-
|
|
162
|
-
def __init__(self, stage_path: str):
|
|
163
|
-
self.directory = self.get_directory(stage_path)
|
|
164
|
-
self.stage = USER_STAGE_PREFIX
|
|
165
|
-
self.stage_name = USER_STAGE_PREFIX
|
|
166
|
-
self.is_directory = True if stage_path.endswith("/") else False
|
|
167
|
-
|
|
168
|
-
@classmethod
|
|
169
|
-
def get_directory(cls, stage_path: str) -> str:
|
|
170
|
-
if Path(stage_path).parts[0] == USER_STAGE_PREFIX:
|
|
171
|
-
return super().get_directory(stage_path)
|
|
172
|
-
return stage_path
|
|
173
|
-
|
|
174
|
-
@property
|
|
175
|
-
def path(self) -> str:
|
|
176
|
-
return f"{self.directory}"
|
|
177
|
-
|
|
178
|
-
@property
|
|
179
|
-
def full_path(self) -> str:
|
|
180
|
-
return f"{self.stage}/{self.directory}"
|
|
181
|
-
|
|
182
|
-
def replace_stage_prefix(self, file_path: str) -> str:
|
|
183
|
-
if Path(file_path).parts[0] == self.stage_name:
|
|
184
|
-
return file_path
|
|
185
|
-
return f"{self.stage}/{file_path}"
|
|
186
|
-
|
|
187
|
-
def add_stage_prefix(self, file_path: str) -> str:
|
|
188
|
-
return f"{self.stage}/{file_path}"
|
|
189
|
-
|
|
190
|
-
def get_directory_from_file_path(self, file_path: str) -> List[str]:
|
|
191
|
-
stage_path_length = len(Path(self.directory).parts)
|
|
192
|
-
return list(Path(file_path).parts[stage_path_length:-1])
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
class StageManager(SqlExecutionMixin):
|
|
196
|
-
def __init__(self):
|
|
197
|
-
super().__init__()
|
|
198
|
-
self._python_exe_procedure = None
|
|
199
|
-
|
|
200
|
-
@staticmethod
|
|
201
|
-
def get_standard_stage_prefix(name: str | FQN) -> str:
|
|
202
|
-
if isinstance(name, FQN):
|
|
203
|
-
name = name.identifier
|
|
204
|
-
# Handle embedded stages
|
|
205
|
-
if name.startswith("snow://") or name.startswith("@"):
|
|
206
|
-
return name
|
|
207
|
-
|
|
208
|
-
return f"@{name}"
|
|
209
|
-
|
|
210
|
-
@staticmethod
|
|
211
|
-
def get_stage_from_path(path: str):
|
|
212
|
-
"""
|
|
213
|
-
Returns stage name from potential path on stage. For example
|
|
214
|
-
db.schema.stage/foo/bar -> db.schema.stage
|
|
215
|
-
"""
|
|
216
|
-
return Path(path).parts[0]
|
|
217
|
-
|
|
218
|
-
@staticmethod
|
|
219
|
-
def quote_stage_name(name: str) -> str:
|
|
220
|
-
if name.startswith("'") and name.endswith("'"):
|
|
221
|
-
return name # already quoted
|
|
222
|
-
|
|
223
|
-
standard_name = StageManager.get_standard_stage_prefix(name)
|
|
224
|
-
if standard_name.startswith("@") and not re.fullmatch(
|
|
225
|
-
r"@([\w./$])+", standard_name
|
|
226
|
-
):
|
|
227
|
-
return to_string_literal(standard_name)
|
|
228
|
-
|
|
229
|
-
return standard_name
|
|
230
|
-
|
|
231
|
-
def _to_uri(self, local_path: str):
|
|
232
|
-
uri = f"file://{local_path}"
|
|
233
|
-
if re.fullmatch(UNQUOTED_FILE_URI_REGEX, uri):
|
|
234
|
-
return uri
|
|
235
|
-
return to_string_literal(uri)
|
|
236
|
-
|
|
237
|
-
def list_files(self, stage_name: str, pattern: str | None = None) -> DictCursor:
|
|
238
|
-
stage_name = self.get_standard_stage_prefix(stage_name)
|
|
239
|
-
query = f"ls {self.quote_stage_name(stage_name)}"
|
|
240
|
-
if pattern is not None:
|
|
241
|
-
query += f" pattern = '{pattern}'"
|
|
242
|
-
return self._execute_query(query, cursor_class=DictCursor)
|
|
243
|
-
|
|
244
|
-
@staticmethod
|
|
245
|
-
def _assure_is_existing_directory(path: Path) -> None:
|
|
246
|
-
spath = SecurePath(path)
|
|
247
|
-
if not spath.exists():
|
|
248
|
-
spath.mkdir(parents=True)
|
|
249
|
-
spath.assert_is_directory()
|
|
250
|
-
|
|
251
|
-
def get(
|
|
252
|
-
self, stage_path: str, dest_path: Path, parallel: int = 4
|
|
253
|
-
) -> SnowflakeCursor:
|
|
254
|
-
stage_path = self.get_standard_stage_prefix(stage_path)
|
|
255
|
-
self._assure_is_existing_directory(dest_path)
|
|
256
|
-
dest_directory = f"{dest_path}/"
|
|
257
|
-
return self._execute_query(
|
|
258
|
-
f"get {self.quote_stage_name(stage_path)} {self._to_uri(dest_directory)} parallel={parallel}"
|
|
259
|
-
)
|
|
260
|
-
|
|
261
|
-
def get_recursive(
|
|
262
|
-
self, stage_path: str, dest_path: Path, parallel: int = 4
|
|
263
|
-
) -> List[SnowflakeCursor]:
|
|
264
|
-
stage_path_parts = self._stage_path_part_factory(stage_path)
|
|
265
|
-
|
|
266
|
-
results = []
|
|
267
|
-
for file_path in self.iter_stage(stage_path):
|
|
268
|
-
dest_directory = dest_path
|
|
269
|
-
for path_part in stage_path_parts.get_directory_from_file_path(file_path):
|
|
270
|
-
dest_directory = dest_directory / path_part
|
|
271
|
-
self._assure_is_existing_directory(dest_directory)
|
|
272
|
-
|
|
273
|
-
result = self._execute_query(
|
|
274
|
-
f"get {self.quote_stage_name(stage_path_parts.replace_stage_prefix(file_path))} {self._to_uri(f'{dest_directory}/')} parallel={parallel}"
|
|
275
|
-
)
|
|
276
|
-
results.append(result)
|
|
277
|
-
|
|
278
|
-
return results
|
|
279
|
-
|
|
280
|
-
def put(
|
|
281
|
-
self,
|
|
282
|
-
local_path: Union[str, Path],
|
|
283
|
-
stage_path: str,
|
|
284
|
-
parallel: int = 4,
|
|
285
|
-
overwrite: bool = False,
|
|
286
|
-
role: Optional[str] = None,
|
|
287
|
-
auto_compress: bool = False,
|
|
288
|
-
) -> SnowflakeCursor:
|
|
289
|
-
"""
|
|
290
|
-
This method will take a file path from the user's system and put it into a Snowflake stage,
|
|
291
|
-
which includes its fully qualified name as well as the path within the stage.
|
|
292
|
-
If provided with a role, then temporarily use this role to perform the operation above,
|
|
293
|
-
and switch back to the original role for the next commands to run.
|
|
294
|
-
"""
|
|
295
|
-
with self.use_role(role) if role else nullcontext():
|
|
296
|
-
stage_path = self.get_standard_stage_prefix(stage_path)
|
|
297
|
-
local_resolved_path = path_resolver(str(local_path))
|
|
298
|
-
log.info("Uploading %s to %s", local_resolved_path, stage_path)
|
|
299
|
-
cursor = self._execute_query(
|
|
300
|
-
f"put {self._to_uri(local_resolved_path)} {self.quote_stage_name(stage_path)} "
|
|
301
|
-
f"auto_compress={str(auto_compress).lower()} parallel={parallel} overwrite={overwrite}"
|
|
302
|
-
)
|
|
303
|
-
return cursor
|
|
304
|
-
|
|
305
|
-
def copy_files(self, source_path: str, destination_path: str) -> SnowflakeCursor:
|
|
306
|
-
source_path_parts = self._stage_path_part_factory(source_path)
|
|
307
|
-
destination_path_parts = self._stage_path_part_factory(destination_path)
|
|
308
|
-
|
|
309
|
-
if isinstance(destination_path_parts, UserStagePathParts):
|
|
310
|
-
raise ClickException(
|
|
311
|
-
"Destination path cannot be a user stage. Please provide a named stage."
|
|
312
|
-
)
|
|
313
|
-
|
|
314
|
-
source = source_path_parts.get_standard_stage_path()
|
|
315
|
-
destination = destination_path_parts.get_standard_stage_directory_path()
|
|
316
|
-
log.info("Copying files from %s to %s", source, destination)
|
|
317
|
-
query = f"copy files into {destination} from {source}"
|
|
318
|
-
return self._execute_query(query)
|
|
319
|
-
|
|
320
|
-
def remove(
|
|
321
|
-
self, stage_name: str, path: str, role: Optional[str] = None
|
|
322
|
-
) -> SnowflakeCursor:
|
|
323
|
-
"""
|
|
324
|
-
This method will take a file path that exists on a Snowflake stage,
|
|
325
|
-
and remove it from the stage.
|
|
326
|
-
If provided with a role, then temporarily use this role to perform the operation above,
|
|
327
|
-
and switch back to the original role for the next commands to run.
|
|
328
|
-
"""
|
|
329
|
-
with self.use_role(role) if role else nullcontext():
|
|
330
|
-
stage_name = self.get_standard_stage_prefix(stage_name)
|
|
331
|
-
path = path if path.startswith("/") else "/" + path
|
|
332
|
-
quoted_stage_name = self.quote_stage_name(f"{stage_name}{path}")
|
|
333
|
-
return self._execute_query(f"remove {quoted_stage_name}")
|
|
334
|
-
|
|
335
|
-
def create(self, fqn: FQN, comment: Optional[str] = None) -> SnowflakeCursor:
|
|
336
|
-
query = f"create stage if not exists {fqn.sql_identifier}"
|
|
337
|
-
if comment:
|
|
338
|
-
query += f" comment='{comment}'"
|
|
339
|
-
return self._execute_query(query)
|
|
340
|
-
|
|
341
|
-
def iter_stage(self, stage_path: str):
|
|
342
|
-
for file in self.list_files(stage_path).fetchall():
|
|
343
|
-
yield file["name"]
|
|
344
|
-
|
|
345
|
-
def execute(
|
|
346
|
-
self,
|
|
347
|
-
stage_path: str,
|
|
348
|
-
on_error: OnErrorType,
|
|
349
|
-
variables: Optional[List[str]] = None,
|
|
350
|
-
):
|
|
351
|
-
stage_path_parts = self._stage_path_part_factory(stage_path)
|
|
352
|
-
all_files_list = self._get_files_list_from_stage(stage_path_parts)
|
|
353
|
-
|
|
354
|
-
all_files_with_stage_name_prefix = [
|
|
355
|
-
stage_path_parts.get_directory(file) for file in all_files_list
|
|
356
|
-
]
|
|
357
|
-
|
|
358
|
-
# filter files from stage if match stage_path pattern
|
|
359
|
-
filtered_file_list = self._filter_files_list(
|
|
360
|
-
stage_path_parts, all_files_with_stage_name_prefix
|
|
361
|
-
)
|
|
362
|
-
|
|
363
|
-
if not filtered_file_list:
|
|
364
|
-
raise ClickException(f"No files matched pattern '{stage_path}'")
|
|
365
|
-
|
|
366
|
-
# sort filtered files in alphabetical order with directories at the end
|
|
367
|
-
sorted_file_path_list = sorted(
|
|
368
|
-
filtered_file_list, key=lambda f: (path.dirname(f), path.basename(f))
|
|
369
|
-
)
|
|
370
|
-
|
|
371
|
-
parsed_variables = parse_key_value_variables(variables)
|
|
372
|
-
sql_variables = self._parse_execute_variables(parsed_variables)
|
|
373
|
-
python_variables = {str(v.key): v.value for v in parsed_variables}
|
|
374
|
-
results = []
|
|
375
|
-
|
|
376
|
-
if any(file.endswith(".py") for file in sorted_file_path_list):
|
|
377
|
-
self._python_exe_procedure = self._bootstrap_snowpark_execution_environment(
|
|
378
|
-
stage_path_parts
|
|
379
|
-
)
|
|
380
|
-
|
|
381
|
-
for file_path in sorted_file_path_list:
|
|
382
|
-
file_stage_path = stage_path_parts.add_stage_prefix(file_path)
|
|
383
|
-
if file_path.endswith(".py"):
|
|
384
|
-
result = self._execute_python(
|
|
385
|
-
file_stage_path=file_stage_path,
|
|
386
|
-
on_error=on_error,
|
|
387
|
-
variables=python_variables,
|
|
388
|
-
)
|
|
389
|
-
else:
|
|
390
|
-
result = self._call_execute_immediate(
|
|
391
|
-
file_stage_path=file_stage_path,
|
|
392
|
-
variables=sql_variables,
|
|
393
|
-
on_error=on_error,
|
|
394
|
-
)
|
|
395
|
-
results.append(result)
|
|
396
|
-
|
|
397
|
-
return results
|
|
398
|
-
|
|
399
|
-
def _get_files_list_from_stage(
|
|
400
|
-
self, stage_path_parts: StagePathParts, pattern: str | None = None
|
|
401
|
-
) -> List[str]:
|
|
402
|
-
files_list_result = self.list_files(
|
|
403
|
-
stage_path_parts.stage, pattern=pattern
|
|
404
|
-
).fetchall()
|
|
405
|
-
|
|
406
|
-
if not files_list_result:
|
|
407
|
-
raise ClickException(f"No files found on stage '{stage_path_parts.stage}'")
|
|
408
|
-
|
|
409
|
-
return [f["name"] for f in files_list_result]
|
|
410
|
-
|
|
411
|
-
def _filter_files_list(
|
|
412
|
-
self, stage_path_parts: StagePathParts, files_on_stage: List[str]
|
|
413
|
-
) -> List[str]:
|
|
414
|
-
if not stage_path_parts.directory:
|
|
415
|
-
return self._filter_supported_files(files_on_stage)
|
|
416
|
-
|
|
417
|
-
stage_path = stage_path_parts.directory
|
|
418
|
-
|
|
419
|
-
# Exact file path was provided if stage_path in file list
|
|
420
|
-
if stage_path in files_on_stage:
|
|
421
|
-
filtered_files = self._filter_supported_files([stage_path])
|
|
422
|
-
if filtered_files:
|
|
423
|
-
return filtered_files
|
|
424
|
-
else:
|
|
425
|
-
raise ClickException(
|
|
426
|
-
f"Invalid file extension, only {', '.join(EXECUTE_SUPPORTED_FILES_FORMATS)} files are allowed."
|
|
427
|
-
)
|
|
428
|
-
# Filter with fnmatch if contains `*` or `?`
|
|
429
|
-
if glob.has_magic(stage_path):
|
|
430
|
-
filtered_files = fnmatch.filter(files_on_stage, stage_path)
|
|
431
|
-
else:
|
|
432
|
-
# Path to directory was provided
|
|
433
|
-
filtered_files = fnmatch.filter(files_on_stage, f"{stage_path}*")
|
|
434
|
-
return self._filter_supported_files(filtered_files)
|
|
435
|
-
|
|
436
|
-
@staticmethod
|
|
437
|
-
def _filter_supported_files(files: List[str]) -> List[str]:
|
|
438
|
-
return [f for f in files if Path(f).suffix in EXECUTE_SUPPORTED_FILES_FORMATS]
|
|
439
|
-
|
|
440
|
-
@staticmethod
|
|
441
|
-
def _parse_execute_variables(variables: List[Variable]) -> Optional[str]:
|
|
442
|
-
if not variables:
|
|
443
|
-
return None
|
|
444
|
-
query_parameters = [f"{v.key}=>{v.value}" for v in variables]
|
|
445
|
-
return f" using ({', '.join(query_parameters)})"
|
|
446
|
-
|
|
447
|
-
@staticmethod
|
|
448
|
-
def _success_result(file: str):
|
|
449
|
-
cli_console.warning(f"SUCCESS - {file}")
|
|
450
|
-
return {"File": file, "Status": "SUCCESS", "Error": None}
|
|
451
|
-
|
|
452
|
-
@staticmethod
|
|
453
|
-
def _error_result(file: str, msg: str):
|
|
454
|
-
cli_console.warning(f"FAILURE - {file}")
|
|
455
|
-
return {"File": file, "Status": "FAILURE", "Error": msg}
|
|
456
|
-
|
|
457
|
-
@staticmethod
|
|
458
|
-
def _handle_execution_exception(on_error: OnErrorType, exception: Exception):
|
|
459
|
-
if on_error == OnErrorType.BREAK:
|
|
460
|
-
raise exception
|
|
461
|
-
|
|
462
|
-
def _call_execute_immediate(
|
|
463
|
-
self,
|
|
464
|
-
file_stage_path: str,
|
|
465
|
-
variables: Optional[str],
|
|
466
|
-
on_error: OnErrorType,
|
|
467
|
-
) -> Dict:
|
|
468
|
-
try:
|
|
469
|
-
query = f"execute immediate from {self.quote_stage_name(file_stage_path)}"
|
|
470
|
-
if variables:
|
|
471
|
-
query += variables
|
|
472
|
-
self._execute_query(query)
|
|
473
|
-
return StageManager._success_result(file=file_stage_path)
|
|
474
|
-
except ProgrammingError as e:
|
|
475
|
-
StageManager._handle_execution_exception(on_error=on_error, exception=e)
|
|
476
|
-
return StageManager._error_result(file=file_stage_path, msg=e.msg)
|
|
477
|
-
|
|
478
|
-
@staticmethod
|
|
479
|
-
def _stage_path_part_factory(stage_path: str) -> StagePathParts:
|
|
480
|
-
stage_path = StageManager.get_standard_stage_prefix(stage_path)
|
|
481
|
-
if stage_path.startswith(USER_STAGE_PREFIX):
|
|
482
|
-
return UserStagePathParts(stage_path)
|
|
483
|
-
return DefaultStagePathParts(stage_path)
|
|
484
|
-
|
|
485
|
-
def _check_for_requirements_file(
|
|
486
|
-
self, stage_path_parts: StagePathParts
|
|
487
|
-
) -> List[str]:
|
|
488
|
-
"""Looks for requirements.txt file on stage."""
|
|
489
|
-
req_files_on_stage = self._get_files_list_from_stage(
|
|
490
|
-
stage_path_parts, pattern=r".*requirements\.txt$"
|
|
491
|
-
)
|
|
492
|
-
if not req_files_on_stage:
|
|
493
|
-
return []
|
|
494
|
-
|
|
495
|
-
# Construct all possible path for requirements file for this context
|
|
496
|
-
# We don't use os.path or pathlib to preserve compatibility on Windows
|
|
497
|
-
req_file_name = "requirements.txt"
|
|
498
|
-
path_parts = stage_path_parts.path.split("/")
|
|
499
|
-
possible_req_files = []
|
|
500
|
-
|
|
501
|
-
while path_parts:
|
|
502
|
-
current_file = "/".join([*path_parts, req_file_name])
|
|
503
|
-
possible_req_files.append(str(current_file))
|
|
504
|
-
path_parts = path_parts[:-1]
|
|
505
|
-
|
|
506
|
-
# Now for every possible path check if the file exists on stage,
|
|
507
|
-
# if yes break, we use the first possible file
|
|
508
|
-
requirements_file = None
|
|
509
|
-
for req_file in possible_req_files:
|
|
510
|
-
if req_file in req_files_on_stage:
|
|
511
|
-
requirements_file = req_file
|
|
512
|
-
break
|
|
513
|
-
|
|
514
|
-
# If we haven't found any matching requirements
|
|
515
|
-
if requirements_file is None:
|
|
516
|
-
return []
|
|
517
|
-
|
|
518
|
-
# req_file at this moment is the first found requirements file
|
|
519
|
-
with SecurePath.temporary_directory() as tmp_dir:
|
|
520
|
-
self.get(
|
|
521
|
-
stage_path_parts.get_full_stage_path(requirements_file), tmp_dir.path
|
|
522
|
-
)
|
|
523
|
-
requirements = parse_requirements(
|
|
524
|
-
requirements_file=tmp_dir / "requirements.txt"
|
|
525
|
-
)
|
|
526
|
-
|
|
527
|
-
return [req.package_name for req in requirements]
|
|
528
|
-
|
|
529
|
-
def _bootstrap_snowpark_execution_environment(
|
|
530
|
-
self, stage_path_parts: StagePathParts
|
|
531
|
-
):
|
|
532
|
-
"""Prepares Snowpark session for executing Python code remotely."""
|
|
533
|
-
if sys.version_info >= PYTHON_3_12:
|
|
534
|
-
raise ClickException(
|
|
535
|
-
f"Executing python files is not supported in Python >= 3.12. Current version: {sys.version}"
|
|
536
|
-
)
|
|
537
|
-
|
|
538
|
-
from snowflake.snowpark.functions import sproc
|
|
539
|
-
|
|
540
|
-
self.snowpark_session.add_packages("snowflake-snowpark-python")
|
|
541
|
-
self.snowpark_session.add_packages("snowflake.core")
|
|
542
|
-
requirements = self._check_for_requirements_file(stage_path_parts)
|
|
543
|
-
self.snowpark_session.add_packages(*requirements)
|
|
544
|
-
|
|
545
|
-
@sproc(is_permanent=False)
|
|
546
|
-
def _python_execution_procedure(
|
|
547
|
-
_: Session, file_path: str, variables: Dict | None = None
|
|
548
|
-
) -> None:
|
|
549
|
-
"""Snowpark session-scoped stored procedure to execute content of provided python file."""
|
|
550
|
-
import json
|
|
551
|
-
|
|
552
|
-
from snowflake.snowpark.files import SnowflakeFile
|
|
553
|
-
|
|
554
|
-
with SnowflakeFile.open(file_path, require_scoped_url=False) as f:
|
|
555
|
-
file_content: str = f.read() # type: ignore
|
|
556
|
-
|
|
557
|
-
wrapper = dedent(
|
|
558
|
-
f"""\
|
|
559
|
-
import os
|
|
560
|
-
os.environ.update({json.dumps(variables)})
|
|
561
|
-
"""
|
|
562
|
-
)
|
|
563
|
-
|
|
564
|
-
exec(wrapper + file_content)
|
|
565
|
-
|
|
566
|
-
return _python_execution_procedure
|
|
567
|
-
|
|
568
|
-
def _execute_python(
|
|
569
|
-
self, file_stage_path: str, on_error: OnErrorType, variables: Dict
|
|
570
|
-
):
|
|
571
|
-
"""
|
|
572
|
-
Executes Python file from stage using a Snowpark temporary procedure.
|
|
573
|
-
Currently, there's no option to pass input to the execution.
|
|
574
|
-
"""
|
|
575
|
-
from snowflake.snowpark.exceptions import SnowparkSQLException
|
|
576
|
-
|
|
577
|
-
try:
|
|
578
|
-
self._python_exe_procedure(self.get_standard_stage_prefix(file_stage_path), variables) # type: ignore
|
|
579
|
-
return StageManager._success_result(file=file_stage_path)
|
|
580
|
-
except SnowparkSQLException as e:
|
|
581
|
-
StageManager._handle_execution_exception(on_error=on_error, exception=e)
|
|
582
|
-
return StageManager._error_result(file=file_stage_path, msg=e.message)
|