snowflake-cli 2.8.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.
Files changed (240) hide show
  1. snowflake/cli/__about__.py +17 -0
  2. snowflake/cli/__init__.py +13 -0
  3. snowflake/cli/api/__init__.py +48 -0
  4. snowflake/cli/api/cli_global_context.py +390 -0
  5. snowflake/cli/api/commands/__init__.py +13 -0
  6. snowflake/cli/api/commands/alias.py +23 -0
  7. snowflake/cli/api/commands/decorators.py +354 -0
  8. snowflake/cli/api/commands/execution_metadata.py +40 -0
  9. snowflake/cli/api/commands/experimental_behaviour.py +19 -0
  10. snowflake/cli/api/commands/flags.py +662 -0
  11. snowflake/cli/api/commands/project_initialisation.py +65 -0
  12. snowflake/cli/api/commands/snow_typer.py +237 -0
  13. snowflake/cli/api/commands/typer_pre_execute.py +26 -0
  14. snowflake/cli/api/config.py +348 -0
  15. snowflake/cli/api/console/__init__.py +17 -0
  16. snowflake/cli/api/console/abc.py +89 -0
  17. snowflake/cli/api/console/console.py +134 -0
  18. snowflake/cli/api/console/enum.py +17 -0
  19. snowflake/cli/api/constants.py +79 -0
  20. snowflake/cli/api/errno.py +27 -0
  21. snowflake/cli/api/exceptions.py +164 -0
  22. snowflake/cli/api/feature_flags.py +55 -0
  23. snowflake/cli/api/identifiers.py +167 -0
  24. snowflake/cli/api/output/__init__.py +13 -0
  25. snowflake/cli/api/output/formats.py +20 -0
  26. snowflake/cli/api/output/types.py +118 -0
  27. snowflake/cli/api/plugins/__init__.py +13 -0
  28. snowflake/cli/api/plugins/command/__init__.py +72 -0
  29. snowflake/cli/api/plugins/command/plugin_hook_specs.py +21 -0
  30. snowflake/cli/api/plugins/plugin_config.py +32 -0
  31. snowflake/cli/api/project/__init__.py +13 -0
  32. snowflake/cli/api/project/definition.py +84 -0
  33. snowflake/cli/api/project/definition_manager.py +134 -0
  34. snowflake/cli/api/project/errors.py +56 -0
  35. snowflake/cli/api/project/project_verification.py +23 -0
  36. snowflake/cli/api/project/schemas/__init__.py +13 -0
  37. snowflake/cli/api/project/schemas/entities/application_entity.py +44 -0
  38. snowflake/cli/api/project/schemas/entities/application_package_entity.py +66 -0
  39. snowflake/cli/api/project/schemas/entities/common.py +78 -0
  40. snowflake/cli/api/project/schemas/entities/entities.py +30 -0
  41. snowflake/cli/api/project/schemas/identifier_model.py +49 -0
  42. snowflake/cli/api/project/schemas/native_app/__init__.py +13 -0
  43. snowflake/cli/api/project/schemas/native_app/application.py +62 -0
  44. snowflake/cli/api/project/schemas/native_app/native_app.py +93 -0
  45. snowflake/cli/api/project/schemas/native_app/package.py +78 -0
  46. snowflake/cli/api/project/schemas/native_app/path_mapping.py +65 -0
  47. snowflake/cli/api/project/schemas/project_definition.py +199 -0
  48. snowflake/cli/api/project/schemas/snowpark/__init__.py +13 -0
  49. snowflake/cli/api/project/schemas/snowpark/argument.py +28 -0
  50. snowflake/cli/api/project/schemas/snowpark/callable.py +69 -0
  51. snowflake/cli/api/project/schemas/snowpark/snowpark.py +36 -0
  52. snowflake/cli/api/project/schemas/streamlit/__init__.py +13 -0
  53. snowflake/cli/api/project/schemas/streamlit/streamlit.py +46 -0
  54. snowflake/cli/api/project/schemas/template.py +77 -0
  55. snowflake/cli/api/project/schemas/updatable_model.py +194 -0
  56. snowflake/cli/api/project/util.py +261 -0
  57. snowflake/cli/api/rendering/__init__.py +13 -0
  58. snowflake/cli/api/rendering/jinja.py +112 -0
  59. snowflake/cli/api/rendering/project_definition_templates.py +39 -0
  60. snowflake/cli/api/rendering/project_templates.py +98 -0
  61. snowflake/cli/api/rendering/sql_templates.py +60 -0
  62. snowflake/cli/api/rest_api.py +172 -0
  63. snowflake/cli/api/sanitizers.py +43 -0
  64. snowflake/cli/api/secure_path.py +362 -0
  65. snowflake/cli/api/secure_utils.py +29 -0
  66. snowflake/cli/api/sql_execution.py +260 -0
  67. snowflake/cli/api/utils/__init__.py +13 -0
  68. snowflake/cli/api/utils/cursor.py +34 -0
  69. snowflake/cli/api/utils/definition_rendering.py +383 -0
  70. snowflake/cli/api/utils/dict_utils.py +73 -0
  71. snowflake/cli/api/utils/error_handling.py +23 -0
  72. snowflake/cli/api/utils/graph.py +97 -0
  73. snowflake/cli/api/utils/models.py +63 -0
  74. snowflake/cli/api/utils/naming_utils.py +13 -0
  75. snowflake/cli/api/utils/path_utils.py +36 -0
  76. snowflake/cli/api/utils/templating_functions.py +144 -0
  77. snowflake/cli/api/utils/types.py +35 -0
  78. snowflake/cli/app/__init__.py +22 -0
  79. snowflake/cli/app/__main__.py +31 -0
  80. snowflake/cli/app/api_impl/__init__.py +13 -0
  81. snowflake/cli/app/api_impl/plugin/__init__.py +13 -0
  82. snowflake/cli/app/api_impl/plugin/plugin_config_provider_impl.py +66 -0
  83. snowflake/cli/app/build_and_push.sh +8 -0
  84. snowflake/cli/app/cli_app.py +243 -0
  85. snowflake/cli/app/commands_registration/__init__.py +33 -0
  86. snowflake/cli/app/commands_registration/builtin_plugins.py +54 -0
  87. snowflake/cli/app/commands_registration/command_plugins_loader.py +169 -0
  88. snowflake/cli/app/commands_registration/commands_registration_with_callbacks.py +105 -0
  89. snowflake/cli/app/commands_registration/exception_logging.py +26 -0
  90. snowflake/cli/app/commands_registration/threadsafe.py +48 -0
  91. snowflake/cli/app/commands_registration/typer_registration.py +153 -0
  92. snowflake/cli/app/constants.py +19 -0
  93. snowflake/cli/app/dev/__init__.py +13 -0
  94. snowflake/cli/app/dev/commands_structure.py +48 -0
  95. snowflake/cli/app/dev/docs/__init__.py +13 -0
  96. snowflake/cli/app/dev/docs/commands_docs_generator.py +100 -0
  97. snowflake/cli/app/dev/docs/generator.py +35 -0
  98. snowflake/cli/app/dev/docs/project_definition_docs_generator.py +58 -0
  99. snowflake/cli/app/dev/docs/project_definition_generate_json_schema.py +227 -0
  100. snowflake/cli/app/dev/docs/template_utils.py +23 -0
  101. snowflake/cli/app/dev/docs/templates/definition_description.rst.jinja2 +38 -0
  102. snowflake/cli/app/dev/docs/templates/overview.rst.jinja2 +9 -0
  103. snowflake/cli/app/dev/docs/templates/usage.rst.jinja2 +57 -0
  104. snowflake/cli/app/dev/pycharm_remote_debug.py +46 -0
  105. snowflake/cli/app/loggers.py +199 -0
  106. snowflake/cli/app/main_typer.py +62 -0
  107. snowflake/cli/app/printing.py +181 -0
  108. snowflake/cli/app/snow_connector.py +243 -0
  109. snowflake/cli/app/telemetry.py +189 -0
  110. snowflake/cli/plugins/__init__.py +13 -0
  111. snowflake/cli/plugins/connection/__init__.py +13 -0
  112. snowflake/cli/plugins/connection/commands.py +330 -0
  113. snowflake/cli/plugins/connection/plugin_spec.py +30 -0
  114. snowflake/cli/plugins/connection/util.py +179 -0
  115. snowflake/cli/plugins/cortex/__init__.py +13 -0
  116. snowflake/cli/plugins/cortex/commands.py +327 -0
  117. snowflake/cli/plugins/cortex/constants.py +17 -0
  118. snowflake/cli/plugins/cortex/manager.py +189 -0
  119. snowflake/cli/plugins/cortex/plugin_spec.py +30 -0
  120. snowflake/cli/plugins/cortex/types.py +22 -0
  121. snowflake/cli/plugins/git/__init__.py +13 -0
  122. snowflake/cli/plugins/git/commands.py +354 -0
  123. snowflake/cli/plugins/git/manager.py +105 -0
  124. snowflake/cli/plugins/git/plugin_spec.py +30 -0
  125. snowflake/cli/plugins/init/__init__.py +13 -0
  126. snowflake/cli/plugins/init/commands.py +248 -0
  127. snowflake/cli/plugins/init/plugin_spec.py +30 -0
  128. snowflake/cli/plugins/nativeapp/__init__.py +13 -0
  129. snowflake/cli/plugins/nativeapp/artifacts.py +742 -0
  130. snowflake/cli/plugins/nativeapp/codegen/__init__.py +13 -0
  131. snowflake/cli/plugins/nativeapp/codegen/artifact_processor.py +91 -0
  132. snowflake/cli/plugins/nativeapp/codegen/compiler.py +130 -0
  133. snowflake/cli/plugins/nativeapp/codegen/sandbox.py +306 -0
  134. snowflake/cli/plugins/nativeapp/codegen/setup/native_app_setup_processor.py +172 -0
  135. snowflake/cli/plugins/nativeapp/codegen/setup/setup_driver.py.source +56 -0
  136. snowflake/cli/plugins/nativeapp/codegen/snowpark/callback_source.py.jinja +181 -0
  137. snowflake/cli/plugins/nativeapp/codegen/snowpark/extension_function_utils.py +217 -0
  138. snowflake/cli/plugins/nativeapp/codegen/snowpark/models.py +61 -0
  139. snowflake/cli/plugins/nativeapp/codegen/snowpark/python_processor.py +528 -0
  140. snowflake/cli/plugins/nativeapp/commands.py +439 -0
  141. snowflake/cli/plugins/nativeapp/common_flags.py +44 -0
  142. snowflake/cli/plugins/nativeapp/constants.py +27 -0
  143. snowflake/cli/plugins/nativeapp/exceptions.py +122 -0
  144. snowflake/cli/plugins/nativeapp/feature_flags.py +24 -0
  145. snowflake/cli/plugins/nativeapp/init.py +345 -0
  146. snowflake/cli/plugins/nativeapp/manager.py +823 -0
  147. snowflake/cli/plugins/nativeapp/plugin_spec.py +30 -0
  148. snowflake/cli/plugins/nativeapp/policy.py +50 -0
  149. snowflake/cli/plugins/nativeapp/project_model.py +195 -0
  150. snowflake/cli/plugins/nativeapp/run_processor.py +389 -0
  151. snowflake/cli/plugins/nativeapp/teardown_processor.py +301 -0
  152. snowflake/cli/plugins/nativeapp/utils.py +98 -0
  153. snowflake/cli/plugins/nativeapp/v2_conversions/v2_to_v1_decorator.py +135 -0
  154. snowflake/cli/plugins/nativeapp/version/__init__.py +13 -0
  155. snowflake/cli/plugins/nativeapp/version/commands.py +170 -0
  156. snowflake/cli/plugins/nativeapp/version/version_processor.py +362 -0
  157. snowflake/cli/plugins/notebook/__init__.py +13 -0
  158. snowflake/cli/plugins/notebook/commands.py +85 -0
  159. snowflake/cli/plugins/notebook/exceptions.py +20 -0
  160. snowflake/cli/plugins/notebook/manager.py +71 -0
  161. snowflake/cli/plugins/notebook/plugin_spec.py +30 -0
  162. snowflake/cli/plugins/notebook/types.py +15 -0
  163. snowflake/cli/plugins/object/__init__.py +13 -0
  164. snowflake/cli/plugins/object/command_aliases.py +95 -0
  165. snowflake/cli/plugins/object/commands.py +181 -0
  166. snowflake/cli/plugins/object/common.py +85 -0
  167. snowflake/cli/plugins/object/manager.py +97 -0
  168. snowflake/cli/plugins/object/plugin_spec.py +30 -0
  169. snowflake/cli/plugins/object_stage_deprecated/__init__.py +15 -0
  170. snowflake/cli/plugins/object_stage_deprecated/commands.py +122 -0
  171. snowflake/cli/plugins/object_stage_deprecated/plugin_spec.py +32 -0
  172. snowflake/cli/plugins/snowpark/__init__.py +13 -0
  173. snowflake/cli/plugins/snowpark/commands.py +546 -0
  174. snowflake/cli/plugins/snowpark/common.py +307 -0
  175. snowflake/cli/plugins/snowpark/manager.py +109 -0
  176. snowflake/cli/plugins/snowpark/models.py +157 -0
  177. snowflake/cli/plugins/snowpark/package/__init__.py +13 -0
  178. snowflake/cli/plugins/snowpark/package/anaconda_packages.py +233 -0
  179. snowflake/cli/plugins/snowpark/package/commands.py +256 -0
  180. snowflake/cli/plugins/snowpark/package/manager.py +44 -0
  181. snowflake/cli/plugins/snowpark/package/utils.py +26 -0
  182. snowflake/cli/plugins/snowpark/package_utils.py +354 -0
  183. snowflake/cli/plugins/snowpark/plugin_spec.py +30 -0
  184. snowflake/cli/plugins/snowpark/snowpark_package_paths.py +65 -0
  185. snowflake/cli/plugins/snowpark/snowpark_shared.py +95 -0
  186. snowflake/cli/plugins/snowpark/zipper.py +81 -0
  187. snowflake/cli/plugins/spcs/__init__.py +35 -0
  188. snowflake/cli/plugins/spcs/common.py +99 -0
  189. snowflake/cli/plugins/spcs/compute_pool/__init__.py +13 -0
  190. snowflake/cli/plugins/spcs/compute_pool/commands.py +241 -0
  191. snowflake/cli/plugins/spcs/compute_pool/manager.py +121 -0
  192. snowflake/cli/plugins/spcs/image_registry/__init__.py +13 -0
  193. snowflake/cli/plugins/spcs/image_registry/commands.py +65 -0
  194. snowflake/cli/plugins/spcs/image_registry/manager.py +105 -0
  195. snowflake/cli/plugins/spcs/image_repository/__init__.py +13 -0
  196. snowflake/cli/plugins/spcs/image_repository/commands.py +202 -0
  197. snowflake/cli/plugins/spcs/image_repository/manager.py +84 -0
  198. snowflake/cli/plugins/spcs/jobs/__init__.py +13 -0
  199. snowflake/cli/plugins/spcs/jobs/commands.py +78 -0
  200. snowflake/cli/plugins/spcs/jobs/manager.py +53 -0
  201. snowflake/cli/plugins/spcs/plugin_spec.py +30 -0
  202. snowflake/cli/plugins/spcs/services/__init__.py +13 -0
  203. snowflake/cli/plugins/spcs/services/commands.py +312 -0
  204. snowflake/cli/plugins/spcs/services/manager.py +170 -0
  205. snowflake/cli/plugins/sql/__init__.py +13 -0
  206. snowflake/cli/plugins/sql/commands.py +83 -0
  207. snowflake/cli/plugins/sql/manager.py +92 -0
  208. snowflake/cli/plugins/sql/plugin_spec.py +30 -0
  209. snowflake/cli/plugins/sql/snowsql_templating.py +28 -0
  210. snowflake/cli/plugins/stage/__init__.py +13 -0
  211. snowflake/cli/plugins/stage/commands.py +263 -0
  212. snowflake/cli/plugins/stage/diff.py +326 -0
  213. snowflake/cli/plugins/stage/manager.py +577 -0
  214. snowflake/cli/plugins/stage/md5.py +160 -0
  215. snowflake/cli/plugins/stage/plugin_spec.py +30 -0
  216. snowflake/cli/plugins/streamlit/__init__.py +13 -0
  217. snowflake/cli/plugins/streamlit/commands.py +179 -0
  218. snowflake/cli/plugins/streamlit/manager.py +222 -0
  219. snowflake/cli/plugins/streamlit/plugin_spec.py +30 -0
  220. snowflake/cli/plugins/workspace/__init__.py +13 -0
  221. snowflake/cli/plugins/workspace/commands.py +35 -0
  222. snowflake/cli/plugins/workspace/plugin_spec.py +30 -0
  223. snowflake/cli/templates/default_snowpark/.gitignore +4 -0
  224. snowflake/cli/templates/default_snowpark/app/__init__.py +0 -0
  225. snowflake/cli/templates/default_snowpark/app/common.py +2 -0
  226. snowflake/cli/templates/default_snowpark/app/functions.py +15 -0
  227. snowflake/cli/templates/default_snowpark/app/procedures.py +22 -0
  228. snowflake/cli/templates/default_snowpark/requirements.txt +1 -0
  229. snowflake/cli/templates/default_snowpark/snowflake.yml +23 -0
  230. snowflake/cli/templates/default_streamlit/.gitignore +4 -0
  231. snowflake/cli/templates/default_streamlit/common/hello.py +2 -0
  232. snowflake/cli/templates/default_streamlit/environment.yml +6 -0
  233. snowflake/cli/templates/default_streamlit/pages/my_page.py +3 -0
  234. snowflake/cli/templates/default_streamlit/snowflake.yml +10 -0
  235. snowflake/cli/templates/default_streamlit/streamlit_app.py +4 -0
  236. snowflake_cli-2.8.2.dist-info/METADATA +325 -0
  237. snowflake_cli-2.8.2.dist-info/RECORD +240 -0
  238. snowflake_cli-2.8.2.dist-info/WHEEL +4 -0
  239. snowflake_cli-2.8.2.dist-info/entry_points.txt +2 -0
  240. snowflake_cli-2.8.2.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,301 @@
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
+ from pathlib import Path
18
+ from textwrap import dedent
19
+ from typing import Dict, Optional
20
+
21
+ import typer
22
+ from snowflake.cli.api.console import cli_console as cc
23
+ from snowflake.cli.api.errno import APPLICATION_NO_LONGER_AVAILABLE
24
+ from snowflake.cli.api.exceptions import SnowflakeSQLExecutionError
25
+ from snowflake.cli.plugins.nativeapp.constants import (
26
+ ALLOWED_SPECIAL_COMMENTS,
27
+ COMMENT_COL,
28
+ EXTERNAL_DISTRIBUTION,
29
+ INTERNAL_DISTRIBUTION,
30
+ OWNER_COL,
31
+ )
32
+ from snowflake.cli.plugins.nativeapp.exceptions import (
33
+ CouldNotDropApplicationPackageWithVersions,
34
+ )
35
+ from snowflake.cli.plugins.nativeapp.manager import (
36
+ NativeAppCommandProcessor,
37
+ NativeAppManager,
38
+ ensure_correct_owner,
39
+ )
40
+ from snowflake.cli.plugins.nativeapp.utils import (
41
+ needs_confirmation,
42
+ )
43
+ from snowflake.connector import ProgrammingError
44
+ from snowflake.connector.cursor import DictCursor
45
+
46
+
47
+ class NativeAppTeardownProcessor(NativeAppManager, NativeAppCommandProcessor):
48
+ def __init__(self, project_definition: Dict, project_root: Path):
49
+ super().__init__(project_definition, project_root)
50
+
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
+ def drop_application(
70
+ self, auto_yes: bool, interactive: bool = False, cascade: Optional[bool] = None
71
+ ):
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,
196
+ cascade=cascade,
197
+ )
198
+ return # The application object was successfully dropped, therefore exit gracefully
199
+
200
+ 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,
283
+ )
284
+ return # The application package was successfully dropped, therefore exit gracefully
285
+
286
+ def process(
287
+ self,
288
+ interactive: bool,
289
+ force_drop: bool = False,
290
+ cascade: Optional[bool] = None,
291
+ *args,
292
+ **kwargs,
293
+ ):
294
+
295
+ # Drop the application object
296
+ self.drop_application(
297
+ auto_yes=force_drop, interactive=interactive, cascade=cascade
298
+ )
299
+
300
+ # Drop the application package
301
+ self.drop_package(auto_yes=force_drop)
@@ -0,0 +1,98 @@
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 os
18
+ from pathlib import Path
19
+ from sys import stdin, stdout
20
+ from typing import Iterable, Optional, Union
21
+
22
+ from click import ClickException
23
+
24
+
25
+ def needs_confirmation(needs_confirm: bool, auto_yes: bool) -> bool:
26
+ return needs_confirm and not auto_yes
27
+
28
+
29
+ def is_tty_interactive():
30
+ return stdin.isatty() and stdout.isatty()
31
+
32
+
33
+ def get_first_paragraph_from_markdown_file(file_path: Path) -> Optional[str]:
34
+ """
35
+ Reads a Markdown file at the given file path and finds the first paragraph
36
+
37
+ Parameters:
38
+ file_path (Path): Path to Markdown file
39
+
40
+ Returns:
41
+ Optional[str]: the first paragraph as a string, or None
42
+ if no paragraph could be found
43
+
44
+ Raises:
45
+ FileNotFoundError: if file_path to Markdown file does not exist
46
+ """
47
+ if not file_path.exists():
48
+ raise FileNotFoundError(file_path)
49
+
50
+ with open(file_path, "r") as markdown_file:
51
+ paragraph_text = None
52
+
53
+ for line in markdown_file:
54
+ stripped_line = line.strip()
55
+ if not stripped_line.startswith("#") and stripped_line:
56
+ paragraph_text = stripped_line
57
+ break
58
+
59
+ return paragraph_text
60
+
61
+
62
+ def shallow_git_clone(url: Union[str, os.PathLike], to_path: Union[str, os.PathLike]):
63
+ """
64
+ Performs a shallow clone of the repository at the provided url to the path specified
65
+
66
+ Parameters:
67
+ url (str | PathLike): Valid git url.
68
+ to_path (str | PathLike): Path to which the repository should be cloned to.
69
+
70
+ Returns:
71
+ Repo: the repository that was cloned
72
+ """
73
+ from git import Repo
74
+
75
+ # Clone the repository in the directory with options.
76
+ repo = Repo.clone_from(
77
+ url=url,
78
+ to_path=to_path,
79
+ filter=["tree:0"],
80
+ depth=1,
81
+ )
82
+ # Close repo to avoid issues with permissions on Windows
83
+ repo.close()
84
+
85
+ return repo
86
+
87
+
88
+ def verify_no_directories(paths_to_sync: Iterable[Path]):
89
+ for path in paths_to_sync:
90
+ if path.is_dir():
91
+ raise ClickException(
92
+ f"{path} is a directory. Add the -r flag to deploy directories." #
93
+ )
94
+
95
+
96
+ def verify_exists(path: Path):
97
+ if not path.exists():
98
+ raise ClickException(f"The following path does not exist: {path}")
@@ -0,0 +1,135 @@
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
+ from functools import wraps
18
+ from pathlib import Path
19
+ from typing import Any, Dict, Optional, Union
20
+
21
+ from click import ClickException
22
+ from snowflake.cli.api.cli_global_context import cli_context, cli_context_manager
23
+ from snowflake.cli.api.project.schemas.entities.application_entity import (
24
+ ApplicationEntity,
25
+ )
26
+ from snowflake.cli.api.project.schemas.entities.application_package_entity import (
27
+ ApplicationPackageEntity,
28
+ )
29
+ from snowflake.cli.api.project.schemas.native_app.path_mapping import PathMapping
30
+ from snowflake.cli.api.project.schemas.project_definition import (
31
+ DefinitionV11,
32
+ DefinitionV20,
33
+ )
34
+ from snowflake.cli.api.utils.definition_rendering import render_definition_template
35
+
36
+
37
+ def _convert_v2_artifact_to_v1_dict(
38
+ v2_artifact: Union[PathMapping, Path]
39
+ ) -> Union[Dict, str]:
40
+ if isinstance(v2_artifact, PathMapping):
41
+ return {
42
+ "src": v2_artifact.src,
43
+ "dest": v2_artifact.dest,
44
+ "processors": v2_artifact.processors,
45
+ }
46
+ return str(v2_artifact)
47
+
48
+
49
+ def _pdf_v2_to_v1(v2_definition: DefinitionV20) -> DefinitionV11:
50
+ pdfv1: Dict[str, Any] = {"definition_version": "1.1", "native_app": {}}
51
+
52
+ app_package_definition: ApplicationPackageEntity = None
53
+ app_definition: Optional[ApplicationEntity] = None
54
+
55
+ for key, entity in v2_definition.entities.items():
56
+ if entity.get_type() == ApplicationPackageEntity.get_type():
57
+ if app_package_definition:
58
+ raise ClickException(
59
+ "More than one application package entity exists in the project definition file."
60
+ )
61
+ app_package_definition = entity
62
+ elif entity.get_type() == ApplicationEntity.get_type():
63
+ if app_definition:
64
+ raise ClickException(
65
+ "More than one application entity exists in the project definition file."
66
+ )
67
+ app_definition = entity
68
+ if not app_package_definition:
69
+ raise ClickException(
70
+ "Could not find an application package entity in the project definition file."
71
+ )
72
+
73
+ # NativeApp
74
+ if app_definition and app_definition.name:
75
+ pdfv1["native_app"]["name"] = app_definition.name
76
+ else:
77
+ pdfv1["native_app"]["name"] = app_package_definition.name.split("_pkg_")[0]
78
+ pdfv1["native_app"]["artifacts"] = [
79
+ _convert_v2_artifact_to_v1_dict(a) for a in app_package_definition.artifacts
80
+ ]
81
+ pdfv1["native_app"]["source_stage"] = app_package_definition.stage
82
+ pdfv1["native_app"]["bundle_root"] = str(app_package_definition.bundle_root)
83
+ pdfv1["native_app"]["generated_root"] = str(app_package_definition.generated_root)
84
+ pdfv1["native_app"]["deploy_root"] = str(app_package_definition.deploy_root)
85
+
86
+ # Package
87
+ pdfv1["native_app"]["package"] = {}
88
+ pdfv1["native_app"]["package"]["name"] = app_package_definition.name
89
+ if app_package_definition.distribution:
90
+ pdfv1["native_app"]["package"][
91
+ "distribution"
92
+ ] = app_package_definition.distribution
93
+ if app_package_definition.meta and app_package_definition.meta.post_deploy:
94
+ pdfv1["native_app"]["package"][
95
+ "post_deploy"
96
+ ] = app_package_definition.meta.post_deploy
97
+
98
+ # Application
99
+ if app_definition:
100
+ pdfv1["native_app"]["application"] = {}
101
+ pdfv1["native_app"]["application"]["name"] = app_definition.name
102
+ if app_definition.meta and app_definition.meta.role:
103
+ pdfv1["native_app"]["application"]["role"] = app_definition.meta.role
104
+ if app_definition.meta and app_definition.meta.post_deploy:
105
+ pdfv1["native_app"]["application"][
106
+ "post_deploy"
107
+ ] = app_definition.meta.post_deploy
108
+
109
+ result = render_definition_template(pdfv1, {})
110
+ # Override the definition object in global context
111
+ return result.project_definition
112
+
113
+
114
+ def nativeapp_definition_v2_to_v1(func):
115
+ """
116
+ A command decorator that attempts to automatically convert a native app project from
117
+ definition v2 to v1.1. Assumes with_project_definition() has already been called.
118
+ The definition object in CliGlobalContext will be replaced with the converted object.
119
+ Exactly one application package entity type is expected, and up to one application
120
+ entity type is expected.
121
+ """
122
+
123
+ @wraps(func)
124
+ def wrapper(*args, **kwargs):
125
+ original_pdf: DefinitionV20 = cli_context.project_definition
126
+ if not original_pdf:
127
+ raise ValueError(
128
+ "Project definition could not be found. The nativeapp_definition_v2_to_v1 command decorator assumes with_project_definition() was called before it."
129
+ )
130
+ if original_pdf.definition_version == "2":
131
+ pdfv1 = _pdf_v2_to_v1(original_pdf)
132
+ cli_context_manager.set_project_definition(pdfv1)
133
+ return func(*args, **kwargs)
134
+
135
+ return wrapper
@@ -0,0 +1,13 @@
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.