snowflake-cli-labs 3.0.0rc0__py3-none-any.whl → 3.0.0rc2__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.
Files changed (66) hide show
  1. snowflake/cli/__about__.py +1 -1
  2. snowflake/cli/_app/cli_app.py +10 -1
  3. snowflake/cli/_app/snow_connector.py +91 -37
  4. snowflake/cli/_app/telemetry.py +8 -4
  5. snowflake/cli/_app/version_check.py +74 -0
  6. snowflake/cli/_plugins/connection/commands.py +3 -2
  7. snowflake/cli/_plugins/git/commands.py +55 -14
  8. snowflake/cli/_plugins/git/manager.py +14 -6
  9. snowflake/cli/_plugins/nativeapp/codegen/compiler.py +18 -2
  10. snowflake/cli/_plugins/nativeapp/codegen/setup/native_app_setup_processor.py +123 -42
  11. snowflake/cli/_plugins/nativeapp/codegen/setup/setup_driver.py.source +5 -2
  12. snowflake/cli/_plugins/nativeapp/codegen/snowpark/python_processor.py +6 -11
  13. snowflake/cli/_plugins/nativeapp/codegen/templates/templates_processor.py +111 -0
  14. snowflake/cli/_plugins/nativeapp/exceptions.py +3 -3
  15. snowflake/cli/_plugins/nativeapp/manager.py +74 -144
  16. snowflake/cli/_plugins/nativeapp/project_model.py +2 -9
  17. snowflake/cli/_plugins/nativeapp/run_processor.py +56 -260
  18. snowflake/cli/_plugins/nativeapp/same_account_install_method.py +74 -0
  19. snowflake/cli/_plugins/nativeapp/teardown_processor.py +17 -246
  20. snowflake/cli/_plugins/nativeapp/v2_conversions/v2_to_v1_decorator.py +91 -17
  21. snowflake/cli/_plugins/snowpark/commands.py +5 -65
  22. snowflake/cli/_plugins/snowpark/common.py +17 -1
  23. snowflake/cli/_plugins/snowpark/models.py +2 -1
  24. snowflake/cli/_plugins/snowpark/package/anaconda_packages.py +1 -35
  25. snowflake/cli/_plugins/sql/commands.py +1 -2
  26. snowflake/cli/_plugins/stage/commands.py +2 -2
  27. snowflake/cli/_plugins/stage/manager.py +46 -15
  28. snowflake/cli/_plugins/streamlit/commands.py +4 -63
  29. snowflake/cli/_plugins/streamlit/manager.py +13 -0
  30. snowflake/cli/_plugins/workspace/action_context.py +7 -0
  31. snowflake/cli/_plugins/workspace/commands.py +145 -32
  32. snowflake/cli/_plugins/workspace/manager.py +21 -4
  33. snowflake/cli/api/cli_global_context.py +136 -313
  34. snowflake/cli/api/commands/decorators.py +1 -1
  35. snowflake/cli/api/commands/flags.py +106 -102
  36. snowflake/cli/api/commands/snow_typer.py +15 -6
  37. snowflake/cli/api/config.py +18 -5
  38. snowflake/cli/api/connections.py +214 -0
  39. snowflake/cli/api/console/abc.py +4 -2
  40. snowflake/cli/api/constants.py +11 -0
  41. snowflake/cli/api/entities/application_entity.py +687 -2
  42. snowflake/cli/api/entities/application_package_entity.py +407 -9
  43. snowflake/cli/api/entities/common.py +7 -2
  44. snowflake/cli/api/entities/utils.py +80 -20
  45. snowflake/cli/api/exceptions.py +12 -2
  46. snowflake/cli/api/feature_flags.py +0 -2
  47. snowflake/cli/api/identifiers.py +3 -0
  48. snowflake/cli/api/project/definition.py +35 -1
  49. snowflake/cli/api/project/definition_conversion.py +352 -0
  50. snowflake/cli/api/project/schemas/entities/application_package_entity_model.py +17 -0
  51. snowflake/cli/api/project/schemas/entities/common.py +0 -12
  52. snowflake/cli/api/project/schemas/identifier_model.py +2 -2
  53. snowflake/cli/api/project/schemas/project_definition.py +102 -43
  54. snowflake/cli/api/rendering/jinja.py +2 -16
  55. snowflake/cli/api/rendering/project_definition_templates.py +5 -1
  56. snowflake/cli/api/rendering/sql_templates.py +14 -4
  57. snowflake/cli/api/secure_path.py +13 -18
  58. snowflake/cli/api/secure_utils.py +90 -1
  59. snowflake/cli/api/sql_execution.py +13 -0
  60. snowflake/cli/api/utils/definition_rendering.py +7 -7
  61. {snowflake_cli_labs-3.0.0rc0.dist-info → snowflake_cli_labs-3.0.0rc2.dist-info}/METADATA +9 -9
  62. {snowflake_cli_labs-3.0.0rc0.dist-info → snowflake_cli_labs-3.0.0rc2.dist-info}/RECORD +65 -61
  63. snowflake/cli/api/commands/typer_pre_execute.py +0 -26
  64. {snowflake_cli_labs-3.0.0rc0.dist-info → snowflake_cli_labs-3.0.0rc2.dist-info}/WHEEL +0 -0
  65. {snowflake_cli_labs-3.0.0rc0.dist-info → snowflake_cli_labs-3.0.0rc2.dist-info}/entry_points.txt +0 -0
  66. {snowflake_cli_labs-3.0.0rc0.dist-info → snowflake_cli_labs-3.0.0rc2.dist-info}/licenses/LICENSE +0 -0
@@ -15,273 +15,44 @@
15
15
  from __future__ import annotations
16
16
 
17
17
  from pathlib import Path
18
- from textwrap import dedent
19
18
  from typing import Dict, Optional
20
19
 
21
- import typer
22
- from snowflake.cli._plugins.nativeapp.constants import (
23
- ALLOWED_SPECIAL_COMMENTS,
24
- COMMENT_COL,
25
- EXTERNAL_DISTRIBUTION,
26
- INTERNAL_DISTRIBUTION,
27
- OWNER_COL,
28
- )
29
- from snowflake.cli._plugins.nativeapp.exceptions import (
30
- CouldNotDropApplicationPackageWithVersions,
31
- )
32
20
  from snowflake.cli._plugins.nativeapp.manager import (
33
21
  NativeAppCommandProcessor,
34
22
  NativeAppManager,
35
23
  )
36
- from snowflake.cli._plugins.nativeapp.utils import (
37
- needs_confirmation,
38
- )
39
24
  from snowflake.cli.api.console import cli_console as cc
40
- from snowflake.cli.api.entities.utils import ensure_correct_owner
41
- from snowflake.cli.api.errno import APPLICATION_NO_LONGER_AVAILABLE
42
- from snowflake.cli.api.exceptions import SnowflakeSQLExecutionError
43
- from snowflake.connector import ProgrammingError
44
- from snowflake.connector.cursor import DictCursor
25
+ from snowflake.cli.api.entities.application_entity import (
26
+ ApplicationEntity,
27
+ )
28
+ from snowflake.cli.api.entities.application_package_entity import (
29
+ ApplicationPackageEntity,
30
+ )
45
31
 
46
32
 
47
33
  class NativeAppTeardownProcessor(NativeAppManager, NativeAppCommandProcessor):
48
34
  def __init__(self, project_definition: Dict, project_root: Path):
49
35
  super().__init__(project_definition, project_root)
50
36
 
51
- def drop_generic_object(
52
- self, object_type: str, object_name: str, role: str, cascade: bool = False
53
- ):
54
- """
55
- Drop object using the given role.
56
- """
57
- with self.use_role(role):
58
- cc.step(f"Dropping {object_type} {object_name} now.")
59
- drop_query = f"drop {object_type} {object_name}"
60
- if cascade:
61
- drop_query += " cascade"
62
- try:
63
- self._execute_query(drop_query)
64
- except:
65
- raise SnowflakeSQLExecutionError(drop_query)
66
-
67
- cc.message(f"Dropped {object_type} {object_name} successfully.")
68
-
69
37
  def drop_application(
70
38
  self, auto_yes: bool, interactive: bool = False, cascade: Optional[bool] = None
71
39
  ):
72
- """
73
- Attempts to drop the application object if all validations and user prompts allow so.
74
- """
75
-
76
- needs_confirm = True
77
-
78
- # 1. If existing application is not found, exit gracefully
79
- show_obj_row = self.get_existing_app_info()
80
- if show_obj_row is None:
81
- cc.warning(
82
- f"Role {self.app_role} does not own any application object with the name {self.app_name}, or the application object does not exist."
83
- )
84
- return
85
-
86
- # 2. Check for the right owner
87
- ensure_correct_owner(
88
- row=show_obj_row, role=self.app_role, obj_name=self.app_name
89
- )
90
-
91
- # 3. Check if created by the Snowflake CLI
92
- row_comment = show_obj_row[COMMENT_COL]
93
- if row_comment not in ALLOWED_SPECIAL_COMMENTS and needs_confirmation(
94
- needs_confirm, auto_yes
95
- ):
96
- should_drop_object = typer.confirm(
97
- dedent(
98
- f"""\
99
- Application object {self.app_name} was not created by Snowflake CLI.
100
- Application object details:
101
- Name: {self.app_name}
102
- Created on: {show_obj_row["created_on"]}
103
- Source: {show_obj_row["source"]}
104
- Owner: {show_obj_row[OWNER_COL]}
105
- Comment: {show_obj_row[COMMENT_COL]}
106
- Version: {show_obj_row["version"]}
107
- Patch: {show_obj_row["patch"]}
108
- Are you sure you want to drop it?
109
- """
110
- )
111
- )
112
- if not should_drop_object:
113
- cc.message(f"Did not drop application object {self.app_name}.")
114
- # The user desires to keep the app, therefore we can't proceed since it would
115
- # leave behind an orphan app when we get to dropping the package
116
- raise typer.Abort()
117
-
118
- # 4. Check for application objects owned by the application
119
- # This query will fail if the application package has already been dropped, so handle this case gracefully
120
- has_objects_to_drop = False
121
- message_prefix = ""
122
- cascade_true_message = ""
123
- cascade_false_message = ""
124
- interactive_prompt = ""
125
- non_interactive_abort = ""
126
- try:
127
- if application_objects := self.get_objects_owned_by_application():
128
- has_objects_to_drop = True
129
- message_prefix = (
130
- f"The following objects are owned by application {self.app_name}"
131
- )
132
- cascade_true_message = f"{message_prefix} and will be dropped:"
133
- cascade_false_message = f"{message_prefix} and will NOT be dropped:"
134
- interactive_prompt = "Would you like to drop these objects in addition to the application? [y/n/ABORT]"
135
- non_interactive_abort = "Re-run teardown again with --cascade or --no-cascade to specify whether these objects should be dropped along with the application"
136
- except ProgrammingError as e:
137
- if e.errno != APPLICATION_NO_LONGER_AVAILABLE:
138
- raise
139
- application_objects = []
140
- message_prefix = f"Could not determine which objects are owned by application {self.app_name}"
141
- has_objects_to_drop = True # potentially, but we don't know what they are
142
- cascade_true_message = (
143
- f"{message_prefix}, an unknown number of objects will be dropped."
144
- )
145
- cascade_false_message = f"{message_prefix}, they will NOT be dropped."
146
- interactive_prompt = f"Would you like to drop an unknown set of objects in addition to the application? [y/n/ABORT]"
147
- non_interactive_abort = f"Re-run teardown again with --cascade or --no-cascade to specify whether any objects should be dropped along with the application."
148
-
149
- if has_objects_to_drop:
150
- if cascade is True:
151
- # If the user explicitly passed the --cascade flag
152
- cc.message(cascade_true_message)
153
- with cc.indented():
154
- for obj in application_objects:
155
- cc.message(self._application_object_to_str(obj))
156
- elif cascade is False:
157
- # If the user explicitly passed the --no-cascade flag
158
- cc.message(cascade_false_message)
159
- with cc.indented():
160
- for obj in application_objects:
161
- cc.message(self._application_object_to_str(obj))
162
- elif interactive:
163
- # If the user didn't pass any cascade flag and the session is interactive
164
- cc.message(message_prefix)
165
- with cc.indented():
166
- for obj in application_objects:
167
- cc.message(self._application_object_to_str(obj))
168
- user_response = typer.prompt(
169
- interactive_prompt,
170
- show_default=False,
171
- default="ABORT",
172
- ).lower()
173
- if user_response in ["y", "yes"]:
174
- cascade = True
175
- elif user_response in ["n", "no"]:
176
- cascade = False
177
- else:
178
- raise typer.Abort()
179
- else:
180
- # Else abort since we don't know what to do and can't ask the user
181
- cc.message(message_prefix)
182
- with cc.indented():
183
- for obj in application_objects:
184
- cc.message(self._application_object_to_str(obj))
185
- cc.message(non_interactive_abort)
186
- raise typer.Abort()
187
- elif cascade is None:
188
- # If there's nothing to drop, set cascade to an explicit False value
189
- cascade = False
190
-
191
- # 5. All validations have passed, drop object
192
- self.drop_generic_object(
193
- object_type="application",
194
- object_name=self.app_name,
195
- role=self.app_role,
40
+ return ApplicationEntity.drop(
41
+ console=cc,
42
+ app_name=self.app_name,
43
+ app_role=self.app_role,
44
+ auto_yes=auto_yes,
45
+ interactive=interactive,
196
46
  cascade=cascade,
197
47
  )
198
- return # The application object was successfully dropped, therefore exit gracefully
199
48
 
200
49
  def drop_package(self, auto_yes: bool):
201
- """
202
- Attempts to drop application package unless user specifies otherwise.
203
- """
204
- needs_confirm = True
205
-
206
- # 1. If existing application package is not found, exit gracefully
207
- show_obj_row = self.get_existing_app_pkg_info()
208
- if show_obj_row is None:
209
- cc.warning(
210
- f"Role {self.package_role} does not own any application package with the name {self.package_name}, or the application package does not exist."
211
- )
212
- return
213
-
214
- # 2. Check for the right owner
215
- ensure_correct_owner(
216
- row=show_obj_row, role=self.package_role, obj_name=self.package_name
217
- )
218
-
219
- with self.use_role(self.package_role):
220
- # 3. Check for versions in the application package
221
- show_versions_query = (
222
- f"show versions in application package {self.package_name}"
223
- )
224
- show_versions_cursor = self._execute_query(
225
- show_versions_query, cursor_class=DictCursor
226
- )
227
- if show_versions_cursor.rowcount is None:
228
- raise SnowflakeSQLExecutionError(show_versions_query)
229
-
230
- if show_versions_cursor.rowcount > 0:
231
- # allow dropping a package with versions when --force is set
232
- if not auto_yes:
233
- raise CouldNotDropApplicationPackageWithVersions(
234
- "Drop versions first, or use --force to override."
235
- )
236
-
237
- # 4. Check distribution of the existing application package
238
- actual_distribution = self.get_app_pkg_distribution_in_snowflake
239
- if not self.verify_project_distribution(actual_distribution):
240
- cc.warning(
241
- f"Continuing to execute `snow app teardown` on application package {self.package_name} with distribution '{actual_distribution}'."
242
- )
243
-
244
- # 5. If distribution is internal, check if created by the Snowflake CLI
245
- row_comment = show_obj_row[COMMENT_COL]
246
- if actual_distribution == INTERNAL_DISTRIBUTION:
247
- if row_comment in ALLOWED_SPECIAL_COMMENTS:
248
- needs_confirm = False
249
- else:
250
- if needs_confirmation(needs_confirm, auto_yes):
251
- cc.warning(
252
- f"Application package {self.package_name} was not created by Snowflake CLI."
253
- )
254
- else:
255
- if needs_confirmation(needs_confirm, auto_yes):
256
- cc.warning(
257
- f"Application package {self.package_name} in your Snowflake account has distribution property '{EXTERNAL_DISTRIBUTION}' and could be associated with one or more of your listings on Snowflake Marketplace."
258
- )
259
-
260
- if needs_confirmation(needs_confirm, auto_yes):
261
- should_drop_object = typer.confirm(
262
- dedent(
263
- f"""\
264
- Application package details:
265
- Name: {self.app_name}
266
- Created on: {show_obj_row["created_on"]}
267
- Distribution: {actual_distribution}
268
- Owner: {show_obj_row[OWNER_COL]}
269
- Comment: {show_obj_row[COMMENT_COL]}
270
- Are you sure you want to drop it?
271
- """
272
- )
273
- )
274
- if not should_drop_object:
275
- cc.message(f"Did not drop application package {self.package_name}.")
276
- return # The user desires to keep the application package, therefore exit gracefully
277
-
278
- # All validations have passed, drop object
279
- self.drop_generic_object(
280
- object_type="application package",
281
- object_name=self.package_name,
282
- role=self.package_role,
50
+ return ApplicationPackageEntity.drop(
51
+ console=cc,
52
+ package_name=self.package_name,
53
+ package_role=self.package_role,
54
+ force_drop=auto_yes,
283
55
  )
284
- return # The application package was successfully dropped, therefore exit gracefully
285
56
 
286
57
  def process(
287
58
  self,
@@ -14,14 +14,17 @@
14
14
 
15
15
  from __future__ import annotations
16
16
 
17
+ import inspect
17
18
  from functools import wraps
18
19
  from typing import Any, Dict, Optional, Union
19
20
 
21
+ import typer
20
22
  from click import ClickException
21
23
  from snowflake.cli.api.cli_global_context import (
22
24
  get_cli_context,
23
25
  get_cli_context_manager,
24
26
  )
27
+ from snowflake.cli.api.commands.decorators import _options_decorator_factory
25
28
  from snowflake.cli.api.project.schemas.entities.application_entity_model import (
26
29
  ApplicationEntityModel,
27
30
  )
@@ -48,28 +51,75 @@ def _convert_v2_artifact_to_v1_dict(
48
51
  return v2_artifact
49
52
 
50
53
 
51
- def _pdf_v2_to_v1(v2_definition: DefinitionV20) -> DefinitionV11:
54
+ def _pdf_v2_to_v1(
55
+ v2_definition: DefinitionV20,
56
+ package_entity_id: str = "",
57
+ app_entity_id: str = "",
58
+ ) -> DefinitionV11:
52
59
  pdfv1: Dict[str, Any] = {"definition_version": "1.1", "native_app": {}}
53
60
 
54
61
  app_package_definition: Optional[ApplicationPackageEntityModel] = None
55
62
  app_definition: Optional[ApplicationEntityModel] = None
56
63
 
57
- for key, entity in v2_definition.entities.items():
58
- if entity.get_type() == ApplicationPackageEntityModel.get_type():
59
- if app_package_definition:
60
- raise ClickException(
61
- "More than one application package entity exists in the project definition file."
62
- )
63
- app_package_definition = entity
64
- elif entity.get_type() == ApplicationEntityModel.get_type():
65
- if app_definition:
64
+ # Enumerate all application package and application entities in the project definition
65
+ packages: dict[
66
+ str, ApplicationPackageEntityModel
67
+ ] = v2_definition.get_entities_by_type(ApplicationPackageEntityModel.get_type())
68
+ apps: dict[str, ApplicationEntityModel] = v2_definition.get_entities_by_type(
69
+ ApplicationEntityModel.get_type()
70
+ )
71
+
72
+ # Determine the application entity to convert, there can be zero or one
73
+ if app_entity_id:
74
+ # If the user specified an app entity ID, use that one directly
75
+ app_definition = apps.get(app_entity_id)
76
+ elif len(apps) == 1:
77
+ # Otherwise, if there is only one app entity, fall back to that one
78
+ app_definition = next(iter(apps.values()))
79
+ elif len(apps) > 1:
80
+ # If there are multiple app entities, the user must specify which one to use
81
+ raise ClickException(
82
+ "More than one application entity exists in the project definition file, "
83
+ "specify --app-entity-id to choose which one to operate on."
84
+ )
85
+
86
+ # Infer or verify the package if we have an app entity to convert
87
+ if app_definition:
88
+ target_package = app_definition.from_.target
89
+ if package_entity_id:
90
+ # If the user specified a package entity ID,
91
+ # check that the app entity targets the user-specified package entity
92
+ if target_package != package_entity_id:
66
93
  raise ClickException(
67
- "More than one application entity exists in the project definition file."
94
+ f"The application entity {app_definition.entity_id} does not "
95
+ f"target the application package entity {package_entity_id}. Either"
96
+ f"use --package-entity-id {target_package} to target the correct package entity, "
97
+ f"or omit the --package-entity-id flag to automatically use the package entity "
98
+ f"that the application entity targets."
68
99
  )
69
- app_definition = entity
100
+ elif target_package in packages:
101
+ # If the user didn't target a specific package entity, use the one the app entity targets
102
+ package_entity_id = target_package
103
+
104
+ # Determine the package entity to convert, there must be one
105
+ if package_entity_id:
106
+ # If the user specified a package entity ID (or we inferred one from the app entity), use that one directly
107
+ app_package_definition = packages.get(package_entity_id)
108
+ elif len(packages) == 1:
109
+ # Otherwise, if there is only one package entity, fall back to that one
110
+ app_package_definition = next(iter(packages.values()))
111
+ elif len(packages) > 1:
112
+ # If there are multiple package entities, the user must specify which one to use
113
+ raise ClickException(
114
+ "More than one application package entity exists in the project definition file, "
115
+ "specify --package-entity-id to choose which one to operate on."
116
+ )
117
+
118
+ # If we don't have a package entity to convert, error out since it's not optional
70
119
  if not app_package_definition:
120
+ with_id = f'with ID "{package_entity_id}" ' if package_entity_id else ""
71
121
  raise ClickException(
72
- "Could not find an application package entity in the project definition file."
122
+ f"Could not find an application package entity {with_id}in the project definition file."
73
123
  )
74
124
 
75
125
  # NativeApp
@@ -141,14 +191,38 @@ def nativeapp_definition_v2_to_v1(func):
141
191
 
142
192
  @wraps(func)
143
193
  def wrapper(*args, **kwargs):
144
- original_pdf: DefinitionV20 = get_cli_context().project_definition
194
+ original_pdf: Optional[DefinitionV20] = get_cli_context().project_definition
145
195
  if not original_pdf:
146
196
  raise ValueError(
147
197
  "Project definition could not be found. The nativeapp_definition_v2_to_v1 command decorator assumes with_project_definition() was called before it."
148
198
  )
149
199
  if original_pdf.definition_version == "2":
150
- pdfv1 = _pdf_v2_to_v1(original_pdf)
151
- get_cli_context_manager().set_project_definition(pdfv1)
200
+ package_entity_id = kwargs.get("package_entity_id", "")
201
+ app_entity_id = kwargs.get("app_entity_id", "")
202
+ pdfv1 = _pdf_v2_to_v1(original_pdf, package_entity_id, app_entity_id)
203
+ get_cli_context_manager().override_project_definition = pdfv1
152
204
  return func(*args, **kwargs)
153
205
 
154
- return wrapper
206
+ return _options_decorator_factory(
207
+ wrapper,
208
+ additional_options=[
209
+ inspect.Parameter(
210
+ "package_entity_id",
211
+ inspect.Parameter.KEYWORD_ONLY,
212
+ annotation=Optional[str],
213
+ default=typer.Option(
214
+ default="",
215
+ help="The ID of the package entity on which to operate when definition_version is 2 or higher.",
216
+ ),
217
+ ),
218
+ inspect.Parameter(
219
+ "app_entity_id",
220
+ inspect.Parameter.KEYWORD_ONLY,
221
+ annotation=Optional[str],
222
+ default=typer.Option(
223
+ default="",
224
+ help="The ID of the application entity on which to operate when definition_version is 2 or higher.",
225
+ ),
226
+ ),
227
+ ],
228
+ )
@@ -75,7 +75,6 @@ from snowflake.cli.api.constants import (
75
75
  DEFAULT_SIZE_LIMIT_MB,
76
76
  )
77
77
  from snowflake.cli.api.exceptions import (
78
- NoProjectDefinitionError,
79
78
  SecretsWithoutExternalAccessIntegrationError,
80
79
  )
81
80
  from snowflake.cli.api.identifiers import FQN
@@ -85,6 +84,9 @@ from snowflake.cli.api.output.types import (
85
84
  MessageResult,
86
85
  SingleQueryResult,
87
86
  )
87
+ from snowflake.cli.api.project.definition_conversion import (
88
+ convert_project_definition_to_v2,
89
+ )
88
90
  from snowflake.cli.api.project.schemas.entities.snowpark_entity import (
89
91
  FunctionEntityModel,
90
92
  ProcedureEntityModel,
@@ -93,10 +95,6 @@ from snowflake.cli.api.project.schemas.project_definition import (
93
95
  ProjectDefinition,
94
96
  ProjectDefinitionV2,
95
97
  )
96
- from snowflake.cli.api.project.schemas.snowpark.callable import (
97
- FunctionSchema,
98
- ProcedureSchema,
99
- )
100
98
  from snowflake.cli.api.secure_path import SecurePath
101
99
  from snowflake.connector import DictCursor, ProgrammingError
102
100
  from snowflake.connector.cursor import SnowflakeCursor
@@ -123,7 +121,7 @@ LikeOption = like_option(
123
121
  )
124
122
 
125
123
 
126
- @app.command("deploy", requires_connection=True)
124
+ @app.command("deploy", requires_connection=True, require_warehouse=True)
127
125
  @with_project_definition()
128
126
  def deploy(
129
127
  replace: bool = ReplaceOption(
@@ -445,66 +443,8 @@ def describe(
445
443
  )
446
444
 
447
445
 
448
- def migrate_v1_snowpark_to_v2(pd: ProjectDefinition):
449
- if not pd.snowpark:
450
- raise NoProjectDefinitionError(
451
- project_type="snowpark", project_root=get_cli_context().project_root
452
- )
453
-
454
- artifact_mapping = {"src": pd.snowpark.src}
455
- if pd.snowpark.project_name:
456
- artifact_mapping["dest"] = pd.snowpark.project_name
457
-
458
- snowpark_shared_mixin = "snowpark_shared"
459
- data: dict = {
460
- "definition_version": "2",
461
- "mixins": {
462
- snowpark_shared_mixin: {
463
- "stage": pd.snowpark.stage_name,
464
- "artifacts": [artifact_mapping],
465
- }
466
- },
467
- "entities": {},
468
- }
469
-
470
- for index, entity in enumerate([*pd.snowpark.procedures, *pd.snowpark.functions]):
471
- identifier = {"name": entity.name}
472
- if entity.database is not None:
473
- identifier["database"] = entity.database
474
- if entity.schema_name is not None:
475
- identifier["schema"] = entity.schema_name
476
-
477
- if entity.name.startswith("<%") and entity.name.endswith("%>"):
478
- entity_name = f"snowpark_entity_{index}"
479
- else:
480
- entity_name = entity.name
481
-
482
- v2_entity = {
483
- "type": "function" if isinstance(entity, FunctionSchema) else "procedure",
484
- "stage": pd.snowpark.stage_name,
485
- "handler": entity.handler,
486
- "returns": entity.returns,
487
- "signature": entity.signature,
488
- "runtime": entity.runtime,
489
- "external_access_integrations": entity.external_access_integrations,
490
- "secrets": entity.secrets,
491
- "imports": entity.imports,
492
- "identifier": identifier,
493
- "meta": {"use_mixins": [snowpark_shared_mixin]},
494
- }
495
- if isinstance(entity, ProcedureSchema):
496
- v2_entity["execute_as_caller"] = entity.execute_as_caller
497
-
498
- data["entities"][entity_name] = v2_entity
499
-
500
- if hasattr(pd, "env") and pd.env:
501
- data["env"] = {k: v for k, v in pd.env.items()}
502
-
503
- return ProjectDefinitionV2(**data)
504
-
505
-
506
446
  def _get_v2_project_definition(cli_context) -> ProjectDefinitionV2:
507
447
  pd = cli_context.project_definition
508
448
  if not pd.meets_version_requirement("2"):
509
- pd = migrate_v1_snowpark_to_v2(pd)
449
+ pd = convert_project_definition_to_v2(cli_context.project_root, pd)
510
450
  return pd
@@ -23,7 +23,13 @@ from click import UsageError
23
23
  from snowflake.cli._plugins.snowpark.models import Requirement
24
24
  from snowflake.cli._plugins.snowpark.snowpark_project_paths import Artefact
25
25
  from snowflake.cli.api.console import cli_console
26
- from snowflake.cli.api.constants import ObjectType
26
+ from snowflake.cli.api.constants import (
27
+ INIT_TEMPLATE_VARIABLE_CLOSING,
28
+ INIT_TEMPLATE_VARIABLE_OPENING,
29
+ PROJECT_TEMPLATE_VARIABLE_CLOSING,
30
+ PROJECT_TEMPLATE_VARIABLE_OPENING,
31
+ ObjectType,
32
+ )
27
33
  from snowflake.cli.api.project.schemas.entities.snowpark_entity import (
28
34
  ProcedureEntityModel,
29
35
  SnowparkEntityModel,
@@ -250,3 +256,13 @@ def _compare_imports(
250
256
  }
251
257
 
252
258
  return project_imports != object_imports
259
+
260
+
261
+ def is_name_a_templated_one(name: str) -> bool:
262
+ return (
263
+ PROJECT_TEMPLATE_VARIABLE_OPENING in name
264
+ and PROJECT_TEMPLATE_VARIABLE_CLOSING in name
265
+ ) or (
266
+ INIT_TEMPLATE_VARIABLE_OPENING in name
267
+ and INIT_TEMPLATE_VARIABLE_CLOSING in name
268
+ )
@@ -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:
@@ -18,12 +18,9 @@ import logging
18
18
  from dataclasses import dataclass
19
19
  from typing import Dict, List, Set
20
20
 
21
- import requests
22
- from click import ClickException
23
21
  from packaging.requirements import InvalidRequirement
24
22
  from packaging.requirements import Requirement as PkgRequirement
25
23
  from packaging.version import InvalidVersion, parse
26
- from requests import HTTPError
27
24
  from snowflake.cli._plugins.snowpark.models import Requirement
28
25
  from snowflake.cli.api.exceptions import SnowflakeSQLExecutionError
29
26
  from snowflake.cli.api.secure_path import SecurePath
@@ -170,23 +167,13 @@ class AnacondaPackagesManager(SqlExecutionMixin):
170
167
  "https://repo.anaconda.com/pkgs/snowflake/channeldata.json"
171
168
  )
172
169
 
173
- # TODO in v3.0: Keep only SQL query, remove fallback to JSON with channel's metadata
174
170
  def find_packages_available_in_snowflake_anaconda(self) -> AnacondaPackages:
175
171
  """
176
172
  Finds python packages available in Snowflake to use in functions and stored procedures.
177
173
  It tries to get the list of packages using SQL query
178
174
  but if the try fails then the fallback is to parse JSON containing info about Snowflake's Anaconda channel.
179
175
  """
180
- try:
181
- packages = self._query_snowflake_for_available_packages()
182
- except Exception as ex:
183
- log.warning(
184
- "Cannot fetch available packages information from Snowflake. "
185
- "Please check your connection configuration. "
186
- "Fallback to Anaconda channel metadata."
187
- )
188
- log.debug("Available packages query failure: %s", ex.__str__(), exc_info=ex)
189
- packages = self._get_available_packages_from_anaconda_channel_info()
176
+ packages = self._query_snowflake_for_available_packages()
190
177
  return AnacondaPackages(packages)
191
178
 
192
179
  def _query_snowflake_for_available_packages(self) -> dict[str, AvailablePackage]:
@@ -210,24 +197,3 @@ class AnacondaPackagesManager(SqlExecutionMixin):
210
197
  snowflake_name=package_name, versions={version}
211
198
  )
212
199
  return packages
213
-
214
- def _get_available_packages_from_anaconda_channel_info(
215
- self,
216
- ) -> dict[str, AvailablePackage]:
217
- try:
218
- response = requests.get(self._snowflake_channel_url)
219
- response.raise_for_status()
220
- packages = {}
221
- for key, package in response.json()["packages"].items():
222
- if not (version := package.get("version")):
223
- continue
224
- package_name = package.get("name", key)
225
- standardized_name = Requirement.standardize_name(package_name)
226
- packages[standardized_name] = AvailablePackage(
227
- snowflake_name=package_name, versions={version}
228
- )
229
- return packages
230
- except HTTPError as err:
231
- raise ClickException(
232
- f"Accessing Snowflake Anaconda channel failed. Reason {err}"
233
- )
@@ -73,8 +73,7 @@ def execute_sql(
73
73
  Query to execute can be specified using query option, filename option (all queries from file will be executed)
74
74
  or via stdin by piping output from other command. For example `cat my.sql | snow sql -i`.
75
75
 
76
- The command supports variable substitution that happens on client-side. Both &VARIABLE or &{ VARIABLE }
77
- syntax are supported.
76
+ The command supports variable substitution that happens on client-side.
78
77
  """
79
78
 
80
79
  data = {}