snowflake-cli 3.0.2__py3-none-any.whl → 3.1.0__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 +3 -0
- snowflake/cli/_app/dev/docs/templates/overview.rst.jinja2 +1 -1
- snowflake/cli/_app/dev/docs/templates/usage.rst.jinja2 +2 -2
- snowflake/cli/_app/telemetry.py +69 -4
- snowflake/cli/_plugins/connection/commands.py +40 -2
- snowflake/cli/_plugins/git/commands.py +6 -3
- snowflake/cli/_plugins/git/manager.py +5 -0
- snowflake/cli/_plugins/nativeapp/artifacts.py +13 -3
- snowflake/cli/_plugins/nativeapp/codegen/artifact_processor.py +1 -1
- snowflake/cli/_plugins/nativeapp/codegen/compiler.py +7 -0
- snowflake/cli/_plugins/nativeapp/codegen/sandbox.py +10 -10
- snowflake/cli/_plugins/nativeapp/codegen/setup/native_app_setup_processor.py +2 -2
- snowflake/cli/_plugins/nativeapp/codegen/snowpark/extension_function_utils.py +1 -1
- snowflake/cli/_plugins/nativeapp/codegen/snowpark/python_processor.py +8 -8
- snowflake/cli/_plugins/nativeapp/commands.py +135 -186
- snowflake/cli/_plugins/nativeapp/entities/application.py +176 -24
- snowflake/cli/_plugins/nativeapp/entities/application_package.py +112 -136
- snowflake/cli/_plugins/nativeapp/exceptions.py +12 -0
- snowflake/cli/_plugins/nativeapp/manager.py +3 -26
- snowflake/cli/_plugins/nativeapp/v2_conversions/{v2_to_v1_decorator.py → compat.py} +131 -72
- snowflake/cli/_plugins/nativeapp/version/commands.py +30 -29
- snowflake/cli/_plugins/nativeapp/version/version_processor.py +1 -43
- snowflake/cli/_plugins/snowpark/common.py +60 -18
- snowflake/cli/_plugins/snowpark/package/anaconda_packages.py +2 -2
- snowflake/cli/_plugins/spcs/image_repository/commands.py +4 -37
- snowflake/cli/_plugins/spcs/image_repository/manager.py +4 -1
- snowflake/cli/_plugins/spcs/services/commands.py +36 -4
- snowflake/cli/_plugins/spcs/services/manager.py +36 -4
- snowflake/cli/_plugins/stage/commands.py +8 -3
- snowflake/cli/_plugins/stage/diff.py +16 -16
- snowflake/cli/_plugins/stage/manager.py +164 -73
- snowflake/cli/_plugins/stage/md5.py +1 -1
- snowflake/cli/_plugins/workspace/commands.py +21 -1
- snowflake/cli/_plugins/workspace/context.py +38 -0
- snowflake/cli/_plugins/workspace/manager.py +23 -13
- snowflake/cli/api/cli_global_context.py +3 -3
- snowflake/cli/api/commands/flags.py +23 -7
- snowflake/cli/api/config.py +7 -4
- snowflake/cli/api/connections.py +12 -1
- snowflake/cli/api/entities/common.py +4 -2
- snowflake/cli/api/entities/utils.py +17 -37
- snowflake/cli/api/exceptions.py +32 -0
- snowflake/cli/api/identifiers.py +8 -0
- snowflake/cli/api/project/definition_conversion.py +139 -40
- snowflake/cli/api/project/schemas/entities/common.py +11 -0
- snowflake/cli/api/project/schemas/project_definition.py +30 -25
- snowflake/cli/api/sql_execution.py +5 -7
- snowflake/cli/api/stage_path.py +241 -0
- snowflake/cli/api/utils/definition_rendering.py +3 -5
- {snowflake_cli-3.0.2.dist-info → snowflake_cli-3.1.0.dist-info}/METADATA +11 -11
- {snowflake_cli-3.0.2.dist-info → snowflake_cli-3.1.0.dist-info}/RECORD +55 -55
- snowflake/cli/_plugins/nativeapp/teardown_processor.py +0 -70
- snowflake/cli/_plugins/workspace/action_context.py +0 -18
- {snowflake_cli-3.0.2.dist-info → snowflake_cli-3.1.0.dist-info}/WHEEL +0 -0
- {snowflake_cli-3.0.2.dist-info → snowflake_cli-3.1.0.dist-info}/entry_points.txt +0 -0
- {snowflake_cli-3.0.2.dist-info → snowflake_cli-3.1.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -41,7 +41,7 @@ from snowflake.cli._plugins.nativeapp.same_account_install_method import (
|
|
|
41
41
|
SameAccountInstallMethod,
|
|
42
42
|
)
|
|
43
43
|
from snowflake.cli._plugins.nativeapp.utils import needs_confirmation
|
|
44
|
-
from snowflake.cli._plugins.workspace.
|
|
44
|
+
from snowflake.cli._plugins.workspace.context import ActionContext
|
|
45
45
|
from snowflake.cli.api.cli_global_context import get_cli_context
|
|
46
46
|
from snowflake.cli.api.console.abc import AbstractConsole
|
|
47
47
|
from snowflake.cli.api.entities.common import EntityBase, get_sql_executor
|
|
@@ -53,8 +53,10 @@ from snowflake.cli.api.entities.utils import (
|
|
|
53
53
|
)
|
|
54
54
|
from snowflake.cli.api.errno import (
|
|
55
55
|
APPLICATION_NO_LONGER_AVAILABLE,
|
|
56
|
+
APPLICATION_OWNS_EXTERNAL_OBJECTS,
|
|
56
57
|
CANNOT_UPGRADE_FROM_LOOSE_FILES_TO_VERSION,
|
|
57
58
|
CANNOT_UPGRADE_FROM_VERSION_TO_LOOSE_FILES,
|
|
59
|
+
DOES_NOT_EXIST_OR_NOT_AUTHORIZED,
|
|
58
60
|
NOT_SUPPORTED_ON_DEV_MODE_APPLICATIONS,
|
|
59
61
|
ONLY_SUPPORTED_ON_DEV_MODE_APPLICATIONS,
|
|
60
62
|
)
|
|
@@ -70,6 +72,7 @@ from snowflake.cli.api.project.util import (
|
|
|
70
72
|
append_test_resource_suffix,
|
|
71
73
|
extract_schema,
|
|
72
74
|
identifier_for_url,
|
|
75
|
+
to_identifier,
|
|
73
76
|
unquote_identifier,
|
|
74
77
|
)
|
|
75
78
|
from snowflake.connector import DictCursor, ProgrammingError
|
|
@@ -118,7 +121,7 @@ class ApplicationEntity(EntityBase[ApplicationEntityModel]):
|
|
|
118
121
|
|
|
119
122
|
def action_deploy(
|
|
120
123
|
self,
|
|
121
|
-
|
|
124
|
+
action_ctx: ActionContext,
|
|
122
125
|
from_release_directive: bool,
|
|
123
126
|
prune: bool,
|
|
124
127
|
recursive: bool,
|
|
@@ -133,18 +136,21 @@ class ApplicationEntity(EntityBase[ApplicationEntityModel]):
|
|
|
133
136
|
**kwargs,
|
|
134
137
|
):
|
|
135
138
|
model = self._entity_model
|
|
139
|
+
workspace_ctx = self._workspace_ctx
|
|
136
140
|
app_name = model.fqn.identifier
|
|
137
141
|
debug_mode = model.debug
|
|
138
142
|
if model.meta:
|
|
139
|
-
app_role = model.meta.role or
|
|
140
|
-
app_warehouse = model.meta.warehouse or
|
|
143
|
+
app_role = model.meta.role or workspace_ctx.default_role
|
|
144
|
+
app_warehouse = model.meta.warehouse or workspace_ctx.default_warehouse
|
|
141
145
|
post_deploy_hooks = model.meta.post_deploy
|
|
142
146
|
else:
|
|
143
|
-
app_role =
|
|
144
|
-
app_warehouse =
|
|
147
|
+
app_role = workspace_ctx.default_role
|
|
148
|
+
app_warehouse = workspace_ctx.default_warehouse
|
|
145
149
|
post_deploy_hooks = None
|
|
146
150
|
|
|
147
|
-
package_entity: ApplicationPackageEntity =
|
|
151
|
+
package_entity: ApplicationPackageEntity = action_ctx.get_entity(
|
|
152
|
+
model.from_.target
|
|
153
|
+
)
|
|
148
154
|
package_model: ApplicationPackageEntityModel = (
|
|
149
155
|
package_entity._entity_model # noqa: SLF001
|
|
150
156
|
)
|
|
@@ -152,7 +158,7 @@ class ApplicationEntity(EntityBase[ApplicationEntityModel]):
|
|
|
152
158
|
if package_model.meta and package_model.meta.role:
|
|
153
159
|
package_role = package_model.meta.role
|
|
154
160
|
else:
|
|
155
|
-
package_role =
|
|
161
|
+
package_role = workspace_ctx.default_role
|
|
156
162
|
|
|
157
163
|
if not stage_fqn:
|
|
158
164
|
stage_fqn = f"{package_name}.{package_model.stage}"
|
|
@@ -169,7 +175,7 @@ class ApplicationEntity(EntityBase[ApplicationEntityModel]):
|
|
|
169
175
|
|
|
170
176
|
def deploy_package():
|
|
171
177
|
package_entity.action_deploy(
|
|
172
|
-
|
|
178
|
+
action_ctx=action_ctx,
|
|
173
179
|
prune=True,
|
|
174
180
|
recursive=True,
|
|
175
181
|
paths=[],
|
|
@@ -179,9 +185,19 @@ class ApplicationEntity(EntityBase[ApplicationEntityModel]):
|
|
|
179
185
|
force=force,
|
|
180
186
|
)
|
|
181
187
|
|
|
188
|
+
def drop_application_before_upgrade(cascade: bool = False):
|
|
189
|
+
self.drop_application_before_upgrade(
|
|
190
|
+
console=workspace_ctx.console,
|
|
191
|
+
app_name=app_name,
|
|
192
|
+
app_role=app_role,
|
|
193
|
+
policy=policy,
|
|
194
|
+
is_interactive=is_interactive,
|
|
195
|
+
cascade=cascade,
|
|
196
|
+
)
|
|
197
|
+
|
|
182
198
|
self.deploy(
|
|
183
|
-
console=
|
|
184
|
-
project_root=
|
|
199
|
+
console=workspace_ctx.console,
|
|
200
|
+
project_root=workspace_ctx.project_root,
|
|
185
201
|
app_name=app_name,
|
|
186
202
|
app_role=app_role,
|
|
187
203
|
app_warehouse=app_warehouse,
|
|
@@ -198,11 +214,12 @@ class ApplicationEntity(EntityBase[ApplicationEntityModel]):
|
|
|
198
214
|
patch=patch,
|
|
199
215
|
post_deploy_hooks=post_deploy_hooks,
|
|
200
216
|
deploy_package=deploy_package,
|
|
217
|
+
drop_application_before_upgrade=drop_application_before_upgrade,
|
|
201
218
|
)
|
|
202
219
|
|
|
203
220
|
def action_drop(
|
|
204
221
|
self,
|
|
205
|
-
|
|
222
|
+
action_ctx: ActionContext,
|
|
206
223
|
interactive: bool,
|
|
207
224
|
force_drop: bool = False,
|
|
208
225
|
cascade: Optional[bool] = None,
|
|
@@ -210,13 +227,14 @@ class ApplicationEntity(EntityBase[ApplicationEntityModel]):
|
|
|
210
227
|
**kwargs,
|
|
211
228
|
):
|
|
212
229
|
model = self._entity_model
|
|
230
|
+
workspace_ctx = self._workspace_ctx
|
|
213
231
|
app_name = model.fqn.identifier
|
|
214
232
|
if model.meta and model.meta.role:
|
|
215
233
|
app_role = model.meta.role
|
|
216
234
|
else:
|
|
217
|
-
app_role =
|
|
235
|
+
app_role = workspace_ctx.default_role
|
|
218
236
|
self.drop(
|
|
219
|
-
console=
|
|
237
|
+
console=workspace_ctx.console,
|
|
220
238
|
app_name=app_name,
|
|
221
239
|
app_role=app_role,
|
|
222
240
|
auto_yes=force_drop,
|
|
@@ -224,6 +242,58 @@ class ApplicationEntity(EntityBase[ApplicationEntityModel]):
|
|
|
224
242
|
cascade=cascade,
|
|
225
243
|
)
|
|
226
244
|
|
|
245
|
+
def action_events(
|
|
246
|
+
self,
|
|
247
|
+
action_ctx: ActionContext,
|
|
248
|
+
since: str | datetime | None = None,
|
|
249
|
+
until: str | datetime | None = None,
|
|
250
|
+
record_types: list[str] | None = None,
|
|
251
|
+
scopes: list[str] | None = None,
|
|
252
|
+
consumer_org: str = "",
|
|
253
|
+
consumer_account: str = "",
|
|
254
|
+
consumer_app_hash: str = "",
|
|
255
|
+
first: int = -1,
|
|
256
|
+
last: int = -1,
|
|
257
|
+
follow: bool = False,
|
|
258
|
+
interval_seconds: int = 10,
|
|
259
|
+
*args,
|
|
260
|
+
**kwargs,
|
|
261
|
+
):
|
|
262
|
+
model = self._entity_model
|
|
263
|
+
package_entity: ApplicationPackageEntity = action_ctx.get_entity(
|
|
264
|
+
model.from_.target
|
|
265
|
+
)
|
|
266
|
+
package_model: ApplicationPackageEntityModel = (
|
|
267
|
+
package_entity._entity_model # noqa: SLF001
|
|
268
|
+
)
|
|
269
|
+
if follow:
|
|
270
|
+
return self.stream_events(
|
|
271
|
+
app_name=model.fqn.identifier,
|
|
272
|
+
package_name=package_model.fqn.identifier,
|
|
273
|
+
interval_seconds=interval_seconds,
|
|
274
|
+
since=since,
|
|
275
|
+
record_types=record_types,
|
|
276
|
+
scopes=scopes,
|
|
277
|
+
consumer_org=consumer_org,
|
|
278
|
+
consumer_account=consumer_account,
|
|
279
|
+
consumer_app_hash=consumer_app_hash,
|
|
280
|
+
last=last,
|
|
281
|
+
)
|
|
282
|
+
else:
|
|
283
|
+
return self.get_events(
|
|
284
|
+
app_name=model.fqn.identifier,
|
|
285
|
+
package_name=package_model.fqn.identifier,
|
|
286
|
+
since=since,
|
|
287
|
+
until=until,
|
|
288
|
+
record_types=record_types,
|
|
289
|
+
scopes=scopes,
|
|
290
|
+
consumer_org=consumer_org,
|
|
291
|
+
consumer_account=consumer_account,
|
|
292
|
+
consumer_app_hash=consumer_app_hash,
|
|
293
|
+
first=first,
|
|
294
|
+
last=last,
|
|
295
|
+
)
|
|
296
|
+
|
|
227
297
|
@classmethod
|
|
228
298
|
def drop(
|
|
229
299
|
cls,
|
|
@@ -241,7 +311,7 @@ class ApplicationEntity(EntityBase[ApplicationEntityModel]):
|
|
|
241
311
|
needs_confirm = True
|
|
242
312
|
|
|
243
313
|
# 1. If existing application is not found, exit gracefully
|
|
244
|
-
show_obj_row = cls.
|
|
314
|
+
show_obj_row = cls.get_existing_app_info_static(
|
|
245
315
|
app_name=app_name,
|
|
246
316
|
app_role=app_role,
|
|
247
317
|
)
|
|
@@ -531,7 +601,7 @@ class ApplicationEntity(EntityBase[ApplicationEntityModel]):
|
|
|
531
601
|
with sql_executor.use_warehouse(app_warehouse):
|
|
532
602
|
|
|
533
603
|
# 2. Check for an existing application by the same name
|
|
534
|
-
show_app_row = cls.
|
|
604
|
+
show_app_row = cls.get_existing_app_info_static(
|
|
535
605
|
app_name=app_name,
|
|
536
606
|
app_role=app_role,
|
|
537
607
|
)
|
|
@@ -674,11 +744,15 @@ class ApplicationEntity(EntityBase[ApplicationEntityModel]):
|
|
|
674
744
|
)
|
|
675
745
|
)
|
|
676
746
|
|
|
747
|
+
def get_existing_app_info(self) -> Optional[dict]:
|
|
748
|
+
model = self._entity_model
|
|
749
|
+
ctx = self._workspace_ctx
|
|
750
|
+
role = (model.meta and model.meta.role) or ctx.default_role
|
|
751
|
+
return self.get_existing_app_info_static(model.fqn.name, role)
|
|
752
|
+
|
|
753
|
+
# Temporary static entrypoint until NativeAppManager.get_existing_app_info() is removed
|
|
677
754
|
@staticmethod
|
|
678
|
-
def
|
|
679
|
-
app_name: str,
|
|
680
|
-
app_role: str,
|
|
681
|
-
) -> Optional[dict]:
|
|
755
|
+
def get_existing_app_info_static(app_name: str, app_role: str) -> Optional[dict]:
|
|
682
756
|
"""
|
|
683
757
|
Check for an existing application object by the same name as in project definition, in account.
|
|
684
758
|
It executes a 'show applications like' query and returns the result as single row, if one exists.
|
|
@@ -689,6 +763,66 @@ class ApplicationEntity(EntityBase[ApplicationEntityModel]):
|
|
|
689
763
|
"applications", app_name, name_col=NAME_COL
|
|
690
764
|
)
|
|
691
765
|
|
|
766
|
+
@classmethod
|
|
767
|
+
def drop_application_before_upgrade(
|
|
768
|
+
cls,
|
|
769
|
+
console: AbstractConsole,
|
|
770
|
+
app_name: str,
|
|
771
|
+
app_role: str,
|
|
772
|
+
policy: PolicyBase,
|
|
773
|
+
is_interactive: bool,
|
|
774
|
+
cascade: bool = False,
|
|
775
|
+
):
|
|
776
|
+
if cascade:
|
|
777
|
+
try:
|
|
778
|
+
if application_objects := cls.get_objects_owned_by_application(
|
|
779
|
+
app_name, app_role
|
|
780
|
+
):
|
|
781
|
+
application_objects_str = cls.application_objects_to_str(
|
|
782
|
+
application_objects
|
|
783
|
+
)
|
|
784
|
+
console.message(
|
|
785
|
+
f"The following objects are owned by application {app_name} and need to be dropped:\n{application_objects_str}"
|
|
786
|
+
)
|
|
787
|
+
except ProgrammingError as err:
|
|
788
|
+
if err.errno != APPLICATION_NO_LONGER_AVAILABLE:
|
|
789
|
+
generic_sql_error_handler(err)
|
|
790
|
+
console.warning(
|
|
791
|
+
"The application owns other objects but they could not be determined."
|
|
792
|
+
)
|
|
793
|
+
user_prompt = "Do you want the Snowflake CLI to drop these objects, then drop the existing application object and recreate it?"
|
|
794
|
+
else:
|
|
795
|
+
user_prompt = "Do you want the Snowflake CLI to drop the existing application object and recreate it?"
|
|
796
|
+
|
|
797
|
+
if not policy.should_proceed(user_prompt):
|
|
798
|
+
if is_interactive:
|
|
799
|
+
console.message("Not upgrading the application object.")
|
|
800
|
+
raise typer.Exit(0)
|
|
801
|
+
else:
|
|
802
|
+
console.message(
|
|
803
|
+
"Cannot upgrade the application object non-interactively without --force."
|
|
804
|
+
)
|
|
805
|
+
raise typer.Exit(1)
|
|
806
|
+
try:
|
|
807
|
+
cascade_msg = " (cascade)" if cascade else ""
|
|
808
|
+
console.step(f"Dropping application object {app_name}{cascade_msg}.")
|
|
809
|
+
cascade_sql = " cascade" if cascade else ""
|
|
810
|
+
sql_executor = get_sql_executor()
|
|
811
|
+
sql_executor.execute_query(f"drop application {app_name}{cascade_sql}")
|
|
812
|
+
except ProgrammingError as err:
|
|
813
|
+
if err.errno == APPLICATION_OWNS_EXTERNAL_OBJECTS and not cascade:
|
|
814
|
+
# We need to cascade the deletion, let's try again (only if we didn't try with cascade already)
|
|
815
|
+
return cls.drop_application_before_upgrade(
|
|
816
|
+
console=console,
|
|
817
|
+
app_name=app_name,
|
|
818
|
+
app_role=app_role,
|
|
819
|
+
policy=policy,
|
|
820
|
+
is_interactive=is_interactive,
|
|
821
|
+
cascade=True,
|
|
822
|
+
)
|
|
823
|
+
else:
|
|
824
|
+
generic_sql_error_handler(err)
|
|
825
|
+
|
|
692
826
|
@classmethod
|
|
693
827
|
def get_events(
|
|
694
828
|
cls,
|
|
@@ -704,7 +838,6 @@ class ApplicationEntity(EntityBase[ApplicationEntityModel]):
|
|
|
704
838
|
first: int = -1,
|
|
705
839
|
last: int = -1,
|
|
706
840
|
):
|
|
707
|
-
|
|
708
841
|
record_types = record_types or []
|
|
709
842
|
scopes = scopes or []
|
|
710
843
|
|
|
@@ -712,7 +845,7 @@ class ApplicationEntity(EntityBase[ApplicationEntityModel]):
|
|
|
712
845
|
raise ValueError("first and last cannot be used together")
|
|
713
846
|
|
|
714
847
|
account_event_table = cls.get_account_event_table()
|
|
715
|
-
if not account_event_table:
|
|
848
|
+
if not account_event_table or account_event_table == "NONE":
|
|
716
849
|
raise NoEventTableForAccount()
|
|
717
850
|
|
|
718
851
|
# resource_attributes uses the unquoted/uppercase app and package name
|
|
@@ -787,7 +920,16 @@ class ApplicationEntity(EntityBase[ApplicationEntityModel]):
|
|
|
787
920
|
try:
|
|
788
921
|
return sql_executor.execute_query(query, cursor_class=DictCursor).fetchall()
|
|
789
922
|
except ProgrammingError as err:
|
|
790
|
-
|
|
923
|
+
if err.errno == DOES_NOT_EXIST_OR_NOT_AUTHORIZED:
|
|
924
|
+
raise ClickException(
|
|
925
|
+
dedent(
|
|
926
|
+
f"""\
|
|
927
|
+
Event table '{account_event_table}' does not exist or you are not authorized to perform this operation.
|
|
928
|
+
Please check your EVENT_TABLE parameter to ensure that it is set to a valid event table."""
|
|
929
|
+
)
|
|
930
|
+
) from err
|
|
931
|
+
else:
|
|
932
|
+
generic_sql_error_handler(err)
|
|
791
933
|
|
|
792
934
|
@classmethod
|
|
793
935
|
def stream_events(
|
|
@@ -846,8 +988,18 @@ class ApplicationEntity(EntityBase[ApplicationEntityModel]):
|
|
|
846
988
|
results = sql_executor.execute_query(query, cursor_class=DictCursor)
|
|
847
989
|
return next((r["value"] for r in results if r["key"] == "EVENT_TABLE"), "")
|
|
848
990
|
|
|
991
|
+
def get_snowsight_url(self) -> str:
|
|
992
|
+
"""Returns the URL that can be used to visit this app via Snowsight."""
|
|
993
|
+
model = self._entity_model
|
|
994
|
+
ctx = self._workspace_ctx
|
|
995
|
+
warehouse = (
|
|
996
|
+
model.meta and model.meta.warehouse and to_identifier(model.meta.warehouse)
|
|
997
|
+
) or to_identifier(ctx.default_warehouse)
|
|
998
|
+
return self.get_snowsight_url_static(model.fqn.name, warehouse)
|
|
999
|
+
|
|
1000
|
+
# Temporary static entrypoint until NativeAppManager.get_snowsight_url() is removed
|
|
849
1001
|
@classmethod
|
|
850
|
-
def
|
|
1002
|
+
def get_snowsight_url_static(cls, app_name: str, app_warehouse: str) -> str:
|
|
851
1003
|
"""Returns the URL that can be used to visit this app via Snowsight."""
|
|
852
1004
|
name = identifier_for_url(app_name)
|
|
853
1005
|
with cls.use_application_warehouse(app_warehouse):
|