snowflake-cli-labs 3.0.0rc1__py3-none-any.whl → 3.0.0rc3__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.
- snowflake/cli/__about__.py +1 -1
- snowflake/cli/_app/cli_app.py +10 -1
- snowflake/cli/_app/commands_registration/builtin_plugins.py +2 -0
- snowflake/cli/_app/secret.py +9 -0
- snowflake/cli/_app/snow_connector.py +110 -51
- snowflake/cli/_app/telemetry.py +8 -4
- snowflake/cli/_app/version_check.py +74 -0
- snowflake/cli/_plugins/git/commands.py +55 -14
- snowflake/cli/_plugins/git/manager.py +53 -7
- snowflake/cli/_plugins/helpers/commands.py +57 -0
- snowflake/cli/{api/commands/typer_pre_execute.py → _plugins/helpers/plugin_spec.py} +14 -10
- snowflake/cli/_plugins/nativeapp/application_entity.py +651 -0
- snowflake/cli/{api/project/schemas/entities → _plugins/nativeapp}/application_entity_model.py +2 -2
- snowflake/cli/_plugins/nativeapp/application_package_entity.py +1107 -0
- snowflake/cli/{api/project/schemas/entities → _plugins/nativeapp}/application_package_entity_model.py +3 -3
- snowflake/cli/_plugins/nativeapp/artifacts.py +10 -9
- snowflake/cli/_plugins/nativeapp/bundle_context.py +1 -1
- snowflake/cli/_plugins/nativeapp/codegen/artifact_processor.py +1 -1
- snowflake/cli/_plugins/nativeapp/codegen/compiler.py +1 -1
- snowflake/cli/_plugins/nativeapp/codegen/setup/native_app_setup_processor.py +1 -1
- snowflake/cli/_plugins/nativeapp/codegen/snowpark/extension_function_utils.py +1 -1
- snowflake/cli/_plugins/nativeapp/codegen/snowpark/models.py +1 -1
- snowflake/cli/_plugins/nativeapp/codegen/snowpark/python_processor.py +3 -6
- snowflake/cli/_plugins/nativeapp/codegen/templates/templates_processor.py +50 -32
- snowflake/cli/_plugins/nativeapp/commands.py +84 -16
- snowflake/cli/_plugins/nativeapp/exceptions.py +0 -9
- snowflake/cli/_plugins/nativeapp/manager.py +56 -92
- snowflake/cli/_plugins/nativeapp/policy.py +3 -0
- snowflake/cli/_plugins/nativeapp/project_model.py +2 -2
- snowflake/cli/_plugins/nativeapp/run_processor.py +65 -272
- snowflake/cli/_plugins/nativeapp/same_account_install_method.py +70 -0
- snowflake/cli/_plugins/nativeapp/teardown_processor.py +11 -154
- snowflake/cli/_plugins/nativeapp/v2_conversions/v2_to_v1_decorator.py +150 -40
- snowflake/cli/_plugins/nativeapp/version/commands.py +6 -24
- snowflake/cli/_plugins/nativeapp/version/version_processor.py +35 -235
- snowflake/cli/_plugins/snowpark/commands.py +5 -5
- snowflake/cli/_plugins/snowpark/common.py +4 -4
- snowflake/cli/_plugins/snowpark/models.py +2 -1
- snowflake/cli/{api/entities → _plugins/snowpark}/snowpark_entity.py +2 -2
- snowflake/cli/{api/project/schemas/entities/snowpark_entity.py → _plugins/snowpark/snowpark_entity_model.py} +3 -6
- snowflake/cli/_plugins/snowpark/snowpark_project_paths.py +1 -1
- snowflake/cli/_plugins/stage/manager.py +9 -4
- snowflake/cli/_plugins/streamlit/commands.py +4 -4
- snowflake/cli/_plugins/streamlit/manager.py +17 -4
- snowflake/cli/{api/entities → _plugins/streamlit}/streamlit_entity.py +2 -2
- snowflake/cli/{api/project/schemas/entities → _plugins/streamlit}/streamlit_entity_model.py +5 -12
- snowflake/cli/_plugins/workspace/action_context.py +2 -1
- snowflake/cli/_plugins/workspace/commands.py +127 -48
- snowflake/cli/_plugins/workspace/manager.py +1 -0
- snowflake/cli/_plugins/workspace/plugin_spec.py +1 -1
- snowflake/cli/api/cli_global_context.py +136 -313
- snowflake/cli/api/commands/flags.py +76 -91
- snowflake/cli/api/commands/snow_typer.py +7 -5
- snowflake/cli/api/config.py +1 -1
- snowflake/cli/api/connections.py +214 -0
- snowflake/cli/api/console/abc.py +4 -2
- snowflake/cli/api/entities/common.py +4 -0
- snowflake/cli/api/entities/utils.py +41 -31
- snowflake/cli/api/errno.py +1 -0
- snowflake/cli/api/identifiers.py +7 -3
- snowflake/cli/api/project/definition.py +11 -0
- snowflake/cli/api/project/definition_conversion.py +175 -16
- snowflake/cli/api/project/schemas/entities/common.py +15 -14
- snowflake/cli/api/project/schemas/entities/entities.py +13 -10
- snowflake/cli/api/project/schemas/project_definition.py +107 -45
- snowflake/cli/api/project/schemas/v1/__init__.py +0 -0
- snowflake/cli/api/project/schemas/{identifier_model.py → v1/identifier_model.py} +0 -7
- snowflake/cli/api/project/schemas/v1/native_app/__init__.py +0 -0
- snowflake/cli/api/project/schemas/{native_app → v1/native_app}/native_app.py +4 -4
- snowflake/cli/api/project/schemas/v1/snowpark/__init__.py +0 -0
- snowflake/cli/api/project/schemas/{snowpark → v1/snowpark}/callable.py +2 -2
- snowflake/cli/api/project/schemas/{snowpark → v1/snowpark}/snowpark.py +2 -2
- snowflake/cli/api/project/schemas/v1/streamlit/__init__.py +0 -0
- snowflake/cli/api/project/schemas/{streamlit → v1/streamlit}/streamlit.py +2 -1
- snowflake/cli/api/rendering/project_definition_templates.py +4 -0
- snowflake/cli/api/rendering/sql_templates.py +7 -0
- snowflake/cli/api/sql_execution.py +6 -15
- snowflake/cli/api/utils/definition_rendering.py +3 -1
- {snowflake_cli_labs-3.0.0rc1.dist-info → snowflake_cli_labs-3.0.0rc3.dist-info}/METADATA +9 -9
- {snowflake_cli_labs-3.0.0rc1.dist-info → snowflake_cli_labs-3.0.0rc3.dist-info}/RECORD +88 -81
- snowflake/cli/api/entities/application_entity.py +0 -12
- snowflake/cli/api/entities/application_package_entity.py +0 -553
- snowflake/cli/api/project/schemas/snowpark/__init__.py +0 -13
- snowflake/cli/api/project/schemas/streamlit/__init__.py +0 -13
- /snowflake/cli/{api/project/schemas/native_app → _plugins/helpers}/__init__.py +0 -0
- /snowflake/cli/api/project/schemas/{native_app → v1/native_app}/application.py +0 -0
- /snowflake/cli/api/project/schemas/{native_app → v1/native_app}/package.py +0 -0
- /snowflake/cli/api/project/schemas/{native_app → v1/native_app}/path_mapping.py +0 -0
- /snowflake/cli/api/project/schemas/{snowpark → v1/snowpark}/argument.py +0 -0
- {snowflake_cli_labs-3.0.0rc1.dist-info → snowflake_cli_labs-3.0.0rc3.dist-info}/WHEEL +0 -0
- {snowflake_cli_labs-3.0.0rc1.dist-info → snowflake_cli_labs-3.0.0rc3.dist-info}/entry_points.txt +0 -0
- {snowflake_cli_labs-3.0.0rc1.dist-info → snowflake_cli_labs-3.0.0rc3.dist-info}/licenses/LICENSE +0 -0
|
@@ -16,17 +16,17 @@ from __future__ import annotations
|
|
|
16
16
|
|
|
17
17
|
from pathlib import Path
|
|
18
18
|
from textwrap import dedent
|
|
19
|
-
from typing import Dict,
|
|
19
|
+
from typing import Dict, Optional
|
|
20
20
|
|
|
21
21
|
import typer
|
|
22
|
-
from click import
|
|
22
|
+
from click import ClickException
|
|
23
|
+
from snowflake.cli._plugins.nativeapp.application_package_entity import (
|
|
24
|
+
ApplicationPackageEntity,
|
|
25
|
+
)
|
|
23
26
|
from snowflake.cli._plugins.nativeapp.artifacts import (
|
|
24
|
-
BundleMap,
|
|
25
27
|
find_version_info_in_manifest_file,
|
|
26
28
|
)
|
|
27
|
-
from snowflake.cli._plugins.nativeapp.constants import VERSION_COL
|
|
28
29
|
from snowflake.cli._plugins.nativeapp.exceptions import (
|
|
29
|
-
ApplicationPackageAlreadyExistsError,
|
|
30
30
|
ApplicationPackageDoesNotExistError,
|
|
31
31
|
)
|
|
32
32
|
from snowflake.cli._plugins.nativeapp.manager import (
|
|
@@ -36,245 +36,50 @@ from snowflake.cli._plugins.nativeapp.manager import (
|
|
|
36
36
|
from snowflake.cli._plugins.nativeapp.policy import PolicyBase
|
|
37
37
|
from snowflake.cli._plugins.nativeapp.run_processor import NativeAppRunProcessor
|
|
38
38
|
from snowflake.cli.api.console import cli_console as cc
|
|
39
|
-
from snowflake.cli.api.
|
|
40
|
-
from snowflake.cli.api.
|
|
41
|
-
from snowflake.cli.api.project.schemas.native_app.native_app import NativeApp
|
|
42
|
-
from snowflake.cli.api.project.util import to_identifier, unquote_identifier
|
|
43
|
-
from snowflake.cli.api.utils.cursor import (
|
|
44
|
-
find_all_rows,
|
|
45
|
-
)
|
|
39
|
+
from snowflake.cli.api.project.schemas.v1.native_app.native_app import NativeApp
|
|
40
|
+
from snowflake.cli.api.project.util import to_identifier
|
|
46
41
|
from snowflake.connector import ProgrammingError
|
|
47
|
-
from snowflake.connector.cursor import DictCursor
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def check_index_changes_in_git_repo(
|
|
51
|
-
project_root: Path, policy: PolicyBase, is_interactive: bool
|
|
52
|
-
) -> None:
|
|
53
|
-
"""
|
|
54
|
-
Checks if the project root, i.e. the native apps project is a git repository. If it is a git repository,
|
|
55
|
-
it also checks if there any local changes to the directory that may not be on the application package stage.
|
|
56
|
-
"""
|
|
57
|
-
from git import Repo
|
|
58
|
-
from git.exc import InvalidGitRepositoryError
|
|
59
|
-
|
|
60
|
-
try:
|
|
61
|
-
repo = Repo(project_root, search_parent_directories=True)
|
|
62
|
-
assert repo.git_dir is not None
|
|
63
|
-
|
|
64
|
-
# Check if the repo has any changes, including untracked files
|
|
65
|
-
if repo.is_dirty(untracked_files=True):
|
|
66
|
-
cc.warning(
|
|
67
|
-
"Changes detected in the git repository. "
|
|
68
|
-
"(Rerun your command with --skip-git-check flag to ignore this check)"
|
|
69
|
-
)
|
|
70
|
-
repo.git.execute(["git", "status"])
|
|
71
|
-
|
|
72
|
-
user_prompt = (
|
|
73
|
-
"You have local changes in this repository that are not part of a previous commit. "
|
|
74
|
-
"Do you still want to continue?"
|
|
75
|
-
)
|
|
76
|
-
if not policy.should_proceed(user_prompt):
|
|
77
|
-
if is_interactive:
|
|
78
|
-
cc.message("Not creating a new version.")
|
|
79
|
-
raise typer.Exit(0)
|
|
80
|
-
else:
|
|
81
|
-
cc.message(
|
|
82
|
-
"Cannot create a new version non-interactively without --force."
|
|
83
|
-
)
|
|
84
|
-
raise typer.Exit(1)
|
|
85
|
-
|
|
86
|
-
except InvalidGitRepositoryError:
|
|
87
|
-
pass # not a git repository, which is acceptable
|
|
88
42
|
|
|
89
43
|
|
|
90
44
|
class NativeAppVersionCreateProcessor(NativeAppRunProcessor):
|
|
91
45
|
def __init__(self, project_definition: Dict, project_root: Path):
|
|
92
46
|
super().__init__(project_definition, project_root)
|
|
93
47
|
|
|
94
|
-
def get_existing_release_directive_info_for_version(
|
|
95
|
-
self, version: str
|
|
96
|
-
) -> List[dict]:
|
|
97
|
-
"""
|
|
98
|
-
Get all existing release directives, if present, set on the version defined in an application package.
|
|
99
|
-
It executes a 'show release directives in application package' query and returns the filtered results, if they exist.
|
|
100
|
-
"""
|
|
101
|
-
with self.use_role(self.package_role):
|
|
102
|
-
show_obj_query = (
|
|
103
|
-
f"show release directives in application package {self.package_name}"
|
|
104
|
-
)
|
|
105
|
-
show_obj_cursor = self._execute_query(
|
|
106
|
-
show_obj_query, cursor_class=DictCursor
|
|
107
|
-
)
|
|
108
|
-
|
|
109
|
-
if show_obj_cursor.rowcount is None:
|
|
110
|
-
raise SnowflakeSQLExecutionError(show_obj_query)
|
|
111
|
-
|
|
112
|
-
show_obj_rows = find_all_rows(
|
|
113
|
-
show_obj_cursor,
|
|
114
|
-
lambda row: row[VERSION_COL] == unquote_identifier(version),
|
|
115
|
-
)
|
|
116
|
-
|
|
117
|
-
return show_obj_rows
|
|
118
|
-
|
|
119
|
-
def add_new_version(self, version: str) -> None:
|
|
120
|
-
"""
|
|
121
|
-
Defines a new version in an existing application package.
|
|
122
|
-
"""
|
|
123
|
-
# Make the version a valid identifier, adding quotes if necessary
|
|
124
|
-
version = to_identifier(version)
|
|
125
|
-
with self.use_role(self.package_role):
|
|
126
|
-
cc.step(
|
|
127
|
-
f"Defining a new version {version} in application package {self.package_name}"
|
|
128
|
-
)
|
|
129
|
-
add_version_query = dedent(
|
|
130
|
-
f"""\
|
|
131
|
-
alter application package {self.package_name}
|
|
132
|
-
add version {version}
|
|
133
|
-
using @{self.stage_fqn}
|
|
134
|
-
"""
|
|
135
|
-
)
|
|
136
|
-
self._execute_query(add_version_query, cursor_class=DictCursor)
|
|
137
|
-
cc.message(
|
|
138
|
-
f"Version {version} created for application package {self.package_name}."
|
|
139
|
-
)
|
|
140
|
-
|
|
141
|
-
def add_new_patch_to_version(self, version: str, patch: Optional[int] = None):
|
|
142
|
-
"""
|
|
143
|
-
Add a new patch, optionally a custom one, to an existing version in an application package.
|
|
144
|
-
"""
|
|
145
|
-
# Make the version a valid identifier, adding quotes if necessary
|
|
146
|
-
version = to_identifier(version)
|
|
147
|
-
with self.use_role(self.package_role):
|
|
148
|
-
cc.step(
|
|
149
|
-
f"Adding new patch to version {version} defined in application package {self.package_name}"
|
|
150
|
-
)
|
|
151
|
-
add_version_query = dedent(
|
|
152
|
-
f"""\
|
|
153
|
-
alter application package {self.package_name}
|
|
154
|
-
add patch {patch if patch else ""} for version {version}
|
|
155
|
-
using @{self.stage_fqn}
|
|
156
|
-
"""
|
|
157
|
-
)
|
|
158
|
-
result_cursor = self._execute_query(
|
|
159
|
-
add_version_query, cursor_class=DictCursor
|
|
160
|
-
)
|
|
161
|
-
|
|
162
|
-
show_row = result_cursor.fetchall()[0]
|
|
163
|
-
new_patch = show_row["patch"]
|
|
164
|
-
cc.message(
|
|
165
|
-
f"Patch {new_patch} created for version {version} defined in application package {self.package_name}."
|
|
166
|
-
)
|
|
167
|
-
|
|
168
48
|
def process(
|
|
169
49
|
self,
|
|
170
|
-
bundle_map: BundleMap,
|
|
171
50
|
version: Optional[str],
|
|
172
51
|
patch: Optional[int],
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
52
|
+
force: bool,
|
|
53
|
+
interactive: bool,
|
|
54
|
+
skip_git_check: bool,
|
|
176
55
|
*args,
|
|
177
56
|
**kwargs,
|
|
178
57
|
):
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
)
|
|
204
|
-
except ApplicationPackageDoesNotExistError as app_err:
|
|
205
|
-
raise BadOptionUsage(
|
|
206
|
-
option_name="patch",
|
|
207
|
-
message=f"Cannot create a custom patch when application package {self.package_name} does not exist. Try again without using --patch.",
|
|
208
|
-
)
|
|
209
|
-
|
|
210
|
-
if git_policy.should_proceed():
|
|
211
|
-
check_index_changes_in_git_repo(
|
|
212
|
-
project_root=self.project_root,
|
|
213
|
-
policy=policy,
|
|
214
|
-
is_interactive=is_interactive,
|
|
215
|
-
)
|
|
216
|
-
|
|
217
|
-
# TODO: consider using self.deploy() instead
|
|
218
|
-
|
|
219
|
-
try:
|
|
220
|
-
self.create_app_package()
|
|
221
|
-
except ApplicationPackageAlreadyExistsError as e:
|
|
222
|
-
cc.warning(e.message)
|
|
223
|
-
if not policy.should_proceed("Proceed with using this package?"):
|
|
224
|
-
raise typer.Abort() from e
|
|
225
|
-
|
|
226
|
-
with self.use_role(self.package_role):
|
|
227
|
-
# Now that the application package exists, create shared data
|
|
228
|
-
self._apply_package_scripts()
|
|
229
|
-
|
|
230
|
-
# Upload files from deploy root local folder to the above stage
|
|
231
|
-
self.sync_deploy_root_with_stage(
|
|
232
|
-
bundle_map=bundle_map,
|
|
233
|
-
role=self.package_role,
|
|
234
|
-
prune=True,
|
|
235
|
-
recursive=True,
|
|
236
|
-
stage_fqn=self.stage_fqn,
|
|
237
|
-
)
|
|
238
|
-
with self.use_package_warehouse():
|
|
239
|
-
self.execute_package_post_deploy_hooks()
|
|
240
|
-
|
|
241
|
-
# Warn if the version exists in a release directive(s)
|
|
242
|
-
existing_release_directives = (
|
|
243
|
-
self.get_existing_release_directive_info_for_version(version)
|
|
58
|
+
return ApplicationPackageEntity.version_create(
|
|
59
|
+
console=cc,
|
|
60
|
+
project_root=self.project_root,
|
|
61
|
+
deploy_root=self.deploy_root,
|
|
62
|
+
bundle_root=self.bundle_root,
|
|
63
|
+
generated_root=self.generated_root,
|
|
64
|
+
artifacts=self.artifacts,
|
|
65
|
+
package_name=self.package_name,
|
|
66
|
+
package_role=self.package_role,
|
|
67
|
+
package_distribution=self.package_distribution,
|
|
68
|
+
prune=True,
|
|
69
|
+
recursive=True,
|
|
70
|
+
paths=None,
|
|
71
|
+
print_diff=True,
|
|
72
|
+
validate=True,
|
|
73
|
+
stage_fqn=self.stage_fqn,
|
|
74
|
+
package_warehouse=self.package_warehouse,
|
|
75
|
+
post_deploy_hooks=self.package_post_deploy_hooks,
|
|
76
|
+
package_scripts=self.package_scripts,
|
|
77
|
+
version=version,
|
|
78
|
+
patch=patch,
|
|
79
|
+
force=force,
|
|
80
|
+
interactive=interactive,
|
|
81
|
+
skip_git_check=skip_git_check,
|
|
244
82
|
)
|
|
245
|
-
if existing_release_directives:
|
|
246
|
-
release_directive_names = ", ".join(
|
|
247
|
-
row["name"] for row in existing_release_directives
|
|
248
|
-
)
|
|
249
|
-
cc.warning(
|
|
250
|
-
dedent(
|
|
251
|
-
f"""\
|
|
252
|
-
Version {version} already defined in application package {self.package_name} and in release directive(s): {release_directive_names}.
|
|
253
|
-
"""
|
|
254
|
-
)
|
|
255
|
-
)
|
|
256
|
-
|
|
257
|
-
user_prompt = (
|
|
258
|
-
f"Are you sure you want to create a new patch for version {version} in application "
|
|
259
|
-
f"package {self.package_name}? Once added, this operation cannot be undone."
|
|
260
|
-
)
|
|
261
|
-
if not policy.should_proceed(user_prompt):
|
|
262
|
-
if is_interactive:
|
|
263
|
-
cc.message("Not creating a new patch.")
|
|
264
|
-
raise typer.Exit(0)
|
|
265
|
-
else:
|
|
266
|
-
cc.message(
|
|
267
|
-
"Cannot create a new patch non-interactively without --force."
|
|
268
|
-
)
|
|
269
|
-
raise typer.Exit(1)
|
|
270
|
-
|
|
271
|
-
# Define a new version in the application package
|
|
272
|
-
if not self.get_existing_version_info(version):
|
|
273
|
-
self.add_new_version(version=version)
|
|
274
|
-
return # A new version created automatically has patch 0, we do not need to further increment the patch.
|
|
275
|
-
|
|
276
|
-
# Add a new patch to an existing (old) version
|
|
277
|
-
self.add_new_patch_to_version(version=version, patch=patch)
|
|
278
83
|
|
|
279
84
|
|
|
280
85
|
class NativeAppVersionDropProcessor(NativeAppManager, NativeAppCommandProcessor):
|
|
@@ -295,12 +100,7 @@ class NativeAppVersionDropProcessor(NativeAppManager, NativeAppCommandProcessor)
|
|
|
295
100
|
|
|
296
101
|
# 1. Check for existing an existing application package
|
|
297
102
|
show_obj_row = self.get_existing_app_pkg_info()
|
|
298
|
-
if show_obj_row:
|
|
299
|
-
# Check for the right owner role
|
|
300
|
-
ensure_correct_owner(
|
|
301
|
-
row=show_obj_row, role=self.package_role, obj_name=self.package_name
|
|
302
|
-
)
|
|
303
|
-
else:
|
|
103
|
+
if not show_obj_row:
|
|
304
104
|
raise ApplicationPackageDoesNotExistError(self.package_name)
|
|
305
105
|
|
|
306
106
|
# 2. Check distribution of the existing application package
|
|
@@ -46,6 +46,10 @@ from snowflake.cli._plugins.snowpark.package.anaconda_packages import (
|
|
|
46
46
|
AnacondaPackagesManager,
|
|
47
47
|
)
|
|
48
48
|
from snowflake.cli._plugins.snowpark.package.commands import app as package_app
|
|
49
|
+
from snowflake.cli._plugins.snowpark.snowpark_entity_model import (
|
|
50
|
+
FunctionEntityModel,
|
|
51
|
+
ProcedureEntityModel,
|
|
52
|
+
)
|
|
49
53
|
from snowflake.cli._plugins.snowpark.snowpark_project_paths import (
|
|
50
54
|
SnowparkProjectPaths,
|
|
51
55
|
)
|
|
@@ -87,10 +91,6 @@ from snowflake.cli.api.output.types import (
|
|
|
87
91
|
from snowflake.cli.api.project.definition_conversion import (
|
|
88
92
|
convert_project_definition_to_v2,
|
|
89
93
|
)
|
|
90
|
-
from snowflake.cli.api.project.schemas.entities.snowpark_entity import (
|
|
91
|
-
FunctionEntityModel,
|
|
92
|
-
ProcedureEntityModel,
|
|
93
|
-
)
|
|
94
94
|
from snowflake.cli.api.project.schemas.project_definition import (
|
|
95
95
|
ProjectDefinition,
|
|
96
96
|
ProjectDefinitionV2,
|
|
@@ -446,5 +446,5 @@ def describe(
|
|
|
446
446
|
def _get_v2_project_definition(cli_context) -> ProjectDefinitionV2:
|
|
447
447
|
pd = cli_context.project_definition
|
|
448
448
|
if not pd.meets_version_requirement("2"):
|
|
449
|
-
pd = convert_project_definition_to_v2(pd)
|
|
449
|
+
pd = convert_project_definition_to_v2(cli_context.project_root, pd)
|
|
450
450
|
return pd
|
|
@@ -21,6 +21,10 @@ from typing import Dict, List, Set
|
|
|
21
21
|
|
|
22
22
|
from click import UsageError
|
|
23
23
|
from snowflake.cli._plugins.snowpark.models import Requirement
|
|
24
|
+
from snowflake.cli._plugins.snowpark.snowpark_entity_model import (
|
|
25
|
+
ProcedureEntityModel,
|
|
26
|
+
SnowparkEntityModel,
|
|
27
|
+
)
|
|
24
28
|
from snowflake.cli._plugins.snowpark.snowpark_project_paths import Artefact
|
|
25
29
|
from snowflake.cli.api.console import cli_console
|
|
26
30
|
from snowflake.cli.api.constants import (
|
|
@@ -30,10 +34,6 @@ from snowflake.cli.api.constants import (
|
|
|
30
34
|
PROJECT_TEMPLATE_VARIABLE_OPENING,
|
|
31
35
|
ObjectType,
|
|
32
36
|
)
|
|
33
|
-
from snowflake.cli.api.project.schemas.entities.snowpark_entity import (
|
|
34
|
-
ProcedureEntityModel,
|
|
35
|
-
SnowparkEntityModel,
|
|
36
|
-
)
|
|
37
37
|
from snowflake.cli.api.sql_execution import SqlExecutionMixin
|
|
38
38
|
from snowflake.connector.cursor import SnowflakeCursor
|
|
39
39
|
|
|
@@ -121,13 +121,14 @@ class WheelMetadata:
|
|
|
121
121
|
if line.startswith(dep_keyword)
|
|
122
122
|
]
|
|
123
123
|
name = cls._get_name_from_wheel_filename(wheel_path.name)
|
|
124
|
+
|
|
124
125
|
return cls(name=name, wheel_path=wheel_path, dependencies=dependencies)
|
|
125
126
|
|
|
126
127
|
@staticmethod
|
|
127
128
|
def _get_name_from_wheel_filename(wheel_filename: str) -> str:
|
|
128
129
|
# wheel filename is in format {name}-{version}[-{extra info}]
|
|
129
130
|
# https://peps.python.org/pep-0491/#file-name-convention
|
|
130
|
-
return wheel_filename.split("-")[0]
|
|
131
|
+
return wheel_filename.split("-")[0].lower()
|
|
131
132
|
|
|
132
133
|
@staticmethod
|
|
133
134
|
def to_wheel_name_format(package_name: str) -> str:
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
from typing import Generic, TypeVar
|
|
2
2
|
|
|
3
|
-
from snowflake.cli.
|
|
4
|
-
from snowflake.cli.api.project.schemas.entities.snowpark_entity import (
|
|
3
|
+
from snowflake.cli._plugins.snowpark.snowpark_entity_model import (
|
|
5
4
|
FunctionEntityModel,
|
|
6
5
|
ProcedureEntityModel,
|
|
7
6
|
)
|
|
7
|
+
from snowflake.cli.api.entities.common import EntityBase
|
|
8
8
|
|
|
9
9
|
T = TypeVar("T")
|
|
10
10
|
|
|
@@ -22,12 +22,13 @@ from snowflake.cli.api.identifiers import FQN
|
|
|
22
22
|
from snowflake.cli.api.project.schemas.entities.common import (
|
|
23
23
|
EntityModelBase,
|
|
24
24
|
ExternalAccessBaseModel,
|
|
25
|
+
ImportsBaseModel,
|
|
25
26
|
)
|
|
26
|
-
from snowflake.cli.api.project.schemas.snowpark.argument import Argument
|
|
27
27
|
from snowflake.cli.api.project.schemas.updatable_model import (
|
|
28
28
|
DiscriminatorField,
|
|
29
29
|
UpdatableModel,
|
|
30
30
|
)
|
|
31
|
+
from snowflake.cli.api.project.schemas.v1.snowpark.argument import Argument
|
|
31
32
|
|
|
32
33
|
|
|
33
34
|
class PathMapping(UpdatableModel):
|
|
@@ -43,7 +44,7 @@ class PathMapping(UpdatableModel):
|
|
|
43
44
|
)
|
|
44
45
|
|
|
45
46
|
|
|
46
|
-
class SnowparkEntityModel(EntityModelBase, ExternalAccessBaseModel):
|
|
47
|
+
class SnowparkEntityModel(EntityModelBase, ExternalAccessBaseModel, ImportsBaseModel):
|
|
47
48
|
handler: str = Field(
|
|
48
49
|
title="Function’s or procedure’s implementation of the object inside source module",
|
|
49
50
|
examples=["functions.hello_function"],
|
|
@@ -57,10 +58,6 @@ class SnowparkEntityModel(EntityModelBase, ExternalAccessBaseModel):
|
|
|
57
58
|
runtime: Optional[Union[str, float]] = Field(
|
|
58
59
|
title="Python version to use when executing ", default=None
|
|
59
60
|
)
|
|
60
|
-
imports: Optional[List[str]] = Field(
|
|
61
|
-
title="Stage and path to previously uploaded files you want to import",
|
|
62
|
-
default=[],
|
|
63
|
-
)
|
|
64
61
|
stage: str = Field(title="Stage in which artifacts will be stored")
|
|
65
62
|
artifacts: List[Union[PathMapping, str]] = Field(title="List of required sources")
|
|
66
63
|
|
|
@@ -16,11 +16,11 @@ from __future__ import annotations
|
|
|
16
16
|
from dataclasses import dataclass
|
|
17
17
|
from pathlib import Path, PurePosixPath
|
|
18
18
|
|
|
19
|
+
from snowflake.cli._plugins.snowpark.snowpark_entity_model import PathMapping
|
|
19
20
|
from snowflake.cli._plugins.snowpark.zipper import zip_dir
|
|
20
21
|
from snowflake.cli.api.console import cli_console
|
|
21
22
|
from snowflake.cli.api.constants import DEPLOYMENT_STAGE
|
|
22
23
|
from snowflake.cli.api.identifiers import FQN
|
|
23
|
-
from snowflake.cli.api.project.schemas.entities.snowpark_entity import PathMapping
|
|
24
24
|
from snowflake.cli.api.secure_path import SecurePath
|
|
25
25
|
|
|
26
26
|
|
|
@@ -57,6 +57,9 @@ EXECUTE_SUPPORTED_FILES_FORMATS = (
|
|
|
57
57
|
".py",
|
|
58
58
|
) # tuple to preserve order but it's a set
|
|
59
59
|
|
|
60
|
+
# Replace magic numbers with constants
|
|
61
|
+
OMIT_FIRST = slice(1, None)
|
|
62
|
+
|
|
60
63
|
|
|
61
64
|
@dataclass
|
|
62
65
|
class StagePathParts:
|
|
@@ -67,7 +70,7 @@ class StagePathParts:
|
|
|
67
70
|
|
|
68
71
|
@classmethod
|
|
69
72
|
def get_directory(cls, stage_path: str) -> str:
|
|
70
|
-
return "/".join(Path(stage_path).parts[
|
|
73
|
+
return "/".join(Path(stage_path).parts[OMIT_FIRST])
|
|
71
74
|
|
|
72
75
|
@property
|
|
73
76
|
def path(self) -> str:
|
|
@@ -119,7 +122,9 @@ class DefaultStagePathParts(StagePathParts):
|
|
|
119
122
|
self.directory = self.get_directory(stage_path)
|
|
120
123
|
self.stage = StageManager.get_stage_from_path(stage_path)
|
|
121
124
|
stage_name = self.stage.split(".")[-1]
|
|
122
|
-
stage_name =
|
|
125
|
+
stage_name = (
|
|
126
|
+
stage_name[OMIT_FIRST] if stage_name.startswith("@") else stage_name
|
|
127
|
+
)
|
|
123
128
|
self.stage_name = stage_name
|
|
124
129
|
self.is_directory = True if stage_path.endswith("/") else False
|
|
125
130
|
|
|
@@ -133,7 +138,7 @@ class DefaultStagePathParts(StagePathParts):
|
|
|
133
138
|
|
|
134
139
|
def replace_stage_prefix(self, file_path: str) -> str:
|
|
135
140
|
stage = Path(self.stage).parts[0]
|
|
136
|
-
file_path_without_prefix = Path(file_path).parts[
|
|
141
|
+
file_path_without_prefix = Path(file_path).parts[OMIT_FIRST]
|
|
137
142
|
return f"{stage}/{'/'.join(file_path_without_prefix)}"
|
|
138
143
|
|
|
139
144
|
def add_stage_prefix(self, file_path: str) -> str:
|
|
@@ -461,7 +466,7 @@ class StageManager(SqlExecutionMixin):
|
|
|
461
466
|
on_error: OnErrorType,
|
|
462
467
|
) -> Dict:
|
|
463
468
|
try:
|
|
464
|
-
query = f"execute immediate from {file_stage_path}"
|
|
469
|
+
query = f"execute immediate from {self.quote_stage_name(file_stage_path)}"
|
|
465
470
|
if variables:
|
|
466
471
|
query += variables
|
|
467
472
|
self._execute_query(query)
|
|
@@ -26,6 +26,9 @@ from snowflake.cli._plugins.object.command_aliases import (
|
|
|
26
26
|
scope_option,
|
|
27
27
|
)
|
|
28
28
|
from snowflake.cli._plugins.streamlit.manager import StreamlitManager
|
|
29
|
+
from snowflake.cli._plugins.streamlit.streamlit_entity_model import (
|
|
30
|
+
StreamlitEntityModel,
|
|
31
|
+
)
|
|
29
32
|
from snowflake.cli.api.cli_global_context import get_cli_context
|
|
30
33
|
from snowflake.cli.api.commands.decorators import (
|
|
31
34
|
with_experimental_behaviour,
|
|
@@ -49,9 +52,6 @@ from snowflake.cli.api.output.types import (
|
|
|
49
52
|
from snowflake.cli.api.project.definition_conversion import (
|
|
50
53
|
convert_project_definition_to_v2,
|
|
51
54
|
)
|
|
52
|
-
from snowflake.cli.api.project.schemas.entities.streamlit_entity_model import (
|
|
53
|
-
StreamlitEntityModel,
|
|
54
|
-
)
|
|
55
55
|
|
|
56
56
|
app = SnowTyperFactory(
|
|
57
57
|
name="streamlit",
|
|
@@ -138,7 +138,7 @@ def streamlit_deploy(
|
|
|
138
138
|
raise NoProjectDefinitionError(
|
|
139
139
|
project_type="streamlit", project_root=cli_context.project_root
|
|
140
140
|
)
|
|
141
|
-
pd = convert_project_definition_to_v2(pd)
|
|
141
|
+
pd = convert_project_definition_to_v2(cli_context.project_root, pd)
|
|
142
142
|
|
|
143
143
|
streamlits: Dict[str, StreamlitEntityModel] = pd.get_entities_by_type(
|
|
144
144
|
entity_type="streamlit"
|
|
@@ -18,21 +18,23 @@ import logging
|
|
|
18
18
|
from pathlib import Path
|
|
19
19
|
from typing import List, Optional
|
|
20
20
|
|
|
21
|
+
from click import ClickException
|
|
21
22
|
from snowflake.cli._plugins.connection.util import (
|
|
22
23
|
MissingConnectionAccountError,
|
|
23
24
|
MissingConnectionRegionError,
|
|
24
25
|
make_snowsight_url,
|
|
25
26
|
)
|
|
27
|
+
from snowflake.cli._plugins.object.manager import ObjectManager
|
|
26
28
|
from snowflake.cli._plugins.stage.manager import StageManager
|
|
29
|
+
from snowflake.cli._plugins.streamlit.streamlit_entity_model import (
|
|
30
|
+
StreamlitEntityModel,
|
|
31
|
+
)
|
|
27
32
|
from snowflake.cli.api.commands.experimental_behaviour import (
|
|
28
33
|
experimental_behaviour_enabled,
|
|
29
34
|
)
|
|
30
35
|
from snowflake.cli.api.console import cli_console
|
|
31
36
|
from snowflake.cli.api.feature_flags import FeatureFlag
|
|
32
37
|
from snowflake.cli.api.identifiers import FQN
|
|
33
|
-
from snowflake.cli.api.project.schemas.entities.streamlit_entity_model import (
|
|
34
|
-
StreamlitEntityModel,
|
|
35
|
-
)
|
|
36
38
|
from snowflake.cli.api.sql_execution import SqlExecutionMixin
|
|
37
39
|
from snowflake.connector.cursor import SnowflakeCursor
|
|
38
40
|
from snowflake.connector.errors import ProgrammingError
|
|
@@ -96,12 +98,16 @@ class StreamlitManager(SqlExecutionMixin):
|
|
|
96
98
|
query.append(f"ROOT_LOCATION = '{from_stage_name}'")
|
|
97
99
|
|
|
98
100
|
query.append(f"MAIN_FILE = '{streamlit.main_file}'")
|
|
99
|
-
|
|
101
|
+
if streamlit.imports:
|
|
102
|
+
query.append(streamlit.get_imports_sql())
|
|
100
103
|
if streamlit.query_warehouse:
|
|
101
104
|
query.append(f"QUERY_WAREHOUSE = {streamlit.query_warehouse}")
|
|
102
105
|
if streamlit.title:
|
|
103
106
|
query.append(f"TITLE = '{streamlit.title}'")
|
|
104
107
|
|
|
108
|
+
if streamlit.comment:
|
|
109
|
+
query.append(f"COMMENT = '{streamlit.comment}'")
|
|
110
|
+
|
|
105
111
|
if streamlit.external_access_integrations:
|
|
106
112
|
query.append(streamlit.get_external_access_integrations_sql())
|
|
107
113
|
|
|
@@ -112,6 +118,13 @@ class StreamlitManager(SqlExecutionMixin):
|
|
|
112
118
|
|
|
113
119
|
def deploy(self, streamlit: StreamlitEntityModel, replace: bool = False):
|
|
114
120
|
streamlit_id = streamlit.fqn.using_connection(self._conn)
|
|
121
|
+
if (
|
|
122
|
+
ObjectManager().object_exists(object_type="streamlit", fqn=streamlit_id)
|
|
123
|
+
and not replace
|
|
124
|
+
):
|
|
125
|
+
raise ClickException(
|
|
126
|
+
f"Streamlit {streamlit.fqn} already exist. If you want to replace it use --replace flag."
|
|
127
|
+
)
|
|
115
128
|
|
|
116
129
|
# for backwards compatibility - quoted stage path might be case-sensitive
|
|
117
130
|
# https://docs.snowflake.com/en/sql-reference/identifiers-syntax#double-quoted-identifiers
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
from snowflake.cli.
|
|
2
|
-
from snowflake.cli.api.project.schemas.entities.streamlit_entity_model import (
|
|
1
|
+
from snowflake.cli._plugins.streamlit.streamlit_entity_model import (
|
|
3
2
|
StreamlitEntityModel,
|
|
4
3
|
)
|
|
4
|
+
from snowflake.cli.api.entities.common import EntityBase
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class StreamlitEntity(EntityBase[StreamlitEntityModel]):
|
|
@@ -20,17 +20,19 @@ from pydantic import Field, model_validator
|
|
|
20
20
|
from snowflake.cli.api.project.schemas.entities.common import (
|
|
21
21
|
EntityModelBase,
|
|
22
22
|
ExternalAccessBaseModel,
|
|
23
|
+
ImportsBaseModel,
|
|
23
24
|
)
|
|
24
25
|
from snowflake.cli.api.project.schemas.updatable_model import (
|
|
25
26
|
DiscriminatorField,
|
|
26
27
|
)
|
|
27
28
|
|
|
28
29
|
|
|
29
|
-
class StreamlitEntityModel(EntityModelBase, ExternalAccessBaseModel):
|
|
30
|
+
class StreamlitEntityModel(EntityModelBase, ExternalAccessBaseModel, ImportsBaseModel):
|
|
30
31
|
type: Literal["streamlit"] = DiscriminatorField() # noqa: A003
|
|
31
32
|
title: Optional[str] = Field(
|
|
32
33
|
title="Human-readable title for the Streamlit dashboard", default=None
|
|
33
34
|
)
|
|
35
|
+
comment: Optional[str] = Field(title="Comment for the Streamlit app", default=None)
|
|
34
36
|
query_warehouse: str = Field(
|
|
35
37
|
title="Snowflake warehouse to host the app", default=None
|
|
36
38
|
)
|
|
@@ -48,23 +50,14 @@ class StreamlitEntityModel(EntityModelBase, ExternalAccessBaseModel):
|
|
|
48
50
|
default=None,
|
|
49
51
|
)
|
|
50
52
|
|
|
51
|
-
@model_validator(mode="after")
|
|
52
|
-
def main_file_must_be_in_artifacts(self):
|
|
53
|
-
if not self.artifacts:
|
|
54
|
-
return self
|
|
55
|
-
|
|
56
|
-
if Path(self.main_file) not in self.artifacts:
|
|
57
|
-
raise ValueError(
|
|
58
|
-
f"Specified main file {self.main_file} is not included in artifacts."
|
|
59
|
-
)
|
|
60
|
-
return self
|
|
61
|
-
|
|
62
53
|
@model_validator(mode="after")
|
|
63
54
|
def artifacts_must_exists(self):
|
|
64
55
|
if not self.artifacts:
|
|
65
56
|
return self
|
|
66
57
|
|
|
67
58
|
for artifact in self.artifacts:
|
|
59
|
+
if "*" in artifact.name:
|
|
60
|
+
continue
|
|
68
61
|
if not artifact.exists():
|
|
69
62
|
raise ValueError(
|
|
70
63
|
f"Specified artifact {artifact} does not exist locally."
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
from pathlib import Path
|
|
3
|
-
from typing import Optional
|
|
3
|
+
from typing import Callable, Optional
|
|
4
4
|
|
|
5
5
|
from snowflake.cli.api.console.abc import AbstractConsole
|
|
6
6
|
|
|
@@ -15,3 +15,4 @@ class ActionContext:
|
|
|
15
15
|
project_root: Path
|
|
16
16
|
default_role: str
|
|
17
17
|
default_warehouse: Optional[str]
|
|
18
|
+
get_entity: Callable
|