snowflake-cli-labs 2.8.0rc1__py3-none-any.whl → 3.0.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.
Files changed (251) hide show
  1. snowflake/cli/__about__.py +1 -1
  2. snowflake/cli/{app → _app}/__main__.py +1 -1
  3. snowflake/cli/{app → _app}/cli_app.py +22 -13
  4. snowflake/cli/{app → _app}/commands_registration/builtin_plugins.py +15 -19
  5. snowflake/cli/{app → _app}/commands_registration/command_plugins_loader.py +9 -9
  6. snowflake/cli/{app → _app}/commands_registration/commands_registration_with_callbacks.py +4 -4
  7. snowflake/cli/{app → _app}/commands_registration/exception_logging.py +2 -2
  8. snowflake/cli/{app → _app}/commands_registration/typer_registration.py +2 -2
  9. snowflake/cli/{app → _app}/dev/docs/commands_docs_generator.py +30 -12
  10. snowflake/cli/{app → _app}/dev/docs/generator.py +3 -3
  11. snowflake/cli/{app → _app}/dev/docs/project_definition_docs_generator.py +4 -4
  12. snowflake/cli/{app → _app}/dev/docs/templates/usage.rst.jinja2 +14 -4
  13. snowflake/cli/{app → _app}/main_typer.py +2 -2
  14. snowflake/cli/{app → _app}/printing.py +2 -2
  15. snowflake/cli/_app/secret.py +9 -0
  16. snowflake/cli/{app → _app}/snow_connector.py +127 -61
  17. snowflake/cli/{app → _app}/telemetry.py +38 -7
  18. snowflake/cli/_app/version_check.py +74 -0
  19. snowflake/cli/{plugins → _plugins}/connection/commands.py +34 -11
  20. snowflake/cli/_plugins/connection/plugin_spec.py +30 -0
  21. snowflake/cli/{plugins → _plugins}/connection/util.py +16 -0
  22. snowflake/cli/{plugins → _plugins}/cortex/commands.py +54 -49
  23. snowflake/cli/{plugins → _plugins}/cortex/constants.py +1 -1
  24. snowflake/cli/{plugins → _plugins}/cortex/manager.py +5 -5
  25. snowflake/cli/{plugins → _plugins}/cortex/plugin_spec.py +1 -1
  26. snowflake/cli/{plugins → _plugins}/git/commands.py +79 -26
  27. snowflake/cli/{plugins → _plugins}/git/manager.py +72 -17
  28. snowflake/cli/{plugins → _plugins}/git/plugin_spec.py +1 -1
  29. snowflake/cli/_plugins/helpers/commands.py +90 -0
  30. snowflake/cli/{plugins/notebook → _plugins/helpers}/plugin_spec.py +1 -1
  31. snowflake/cli/{plugins → _plugins}/init/commands.py +10 -6
  32. snowflake/cli/{plugins → _plugins}/init/plugin_spec.py +1 -1
  33. snowflake/cli/{plugins → _plugins}/nativeapp/artifacts.py +24 -9
  34. snowflake/cli/_plugins/nativeapp/bundle_context.py +31 -0
  35. snowflake/cli/{plugins → _plugins}/nativeapp/codegen/artifact_processor.py +4 -4
  36. snowflake/cli/{plugins → _plugins}/nativeapp/codegen/compiler.py +37 -18
  37. snowflake/cli/_plugins/nativeapp/codegen/setup/native_app_setup_processor.py +249 -0
  38. snowflake/cli/{plugins → _plugins}/nativeapp/codegen/setup/setup_driver.py.source +5 -2
  39. snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/extension_function_utils.py +5 -5
  40. snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/models.py +1 -1
  41. snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/python_processor.py +29 -34
  42. snowflake/cli/_plugins/nativeapp/codegen/templates/templates_processor.py +114 -0
  43. snowflake/cli/{plugins → _plugins}/nativeapp/commands.py +252 -132
  44. snowflake/cli/{plugins → _plugins}/nativeapp/common_flags.py +1 -1
  45. snowflake/cli/_plugins/nativeapp/entities/application.py +878 -0
  46. snowflake/cli/_plugins/nativeapp/entities/application_package.py +1392 -0
  47. snowflake/cli/{plugins → _plugins}/nativeapp/exceptions.py +3 -12
  48. snowflake/cli/_plugins/nativeapp/manager.py +415 -0
  49. snowflake/cli/{plugins/connection → _plugins/nativeapp}/plugin_spec.py +1 -1
  50. snowflake/cli/{plugins → _plugins}/nativeapp/policy.py +3 -0
  51. snowflake/cli/{plugins → _plugins}/nativeapp/project_model.py +36 -20
  52. snowflake/cli/_plugins/nativeapp/run_processor.py +184 -0
  53. snowflake/cli/_plugins/nativeapp/same_account_install_method.py +70 -0
  54. snowflake/cli/_plugins/nativeapp/teardown_processor.py +70 -0
  55. snowflake/cli/_plugins/nativeapp/v2_conversions/v2_to_v1_decorator.py +262 -0
  56. snowflake/cli/{plugins → _plugins}/nativeapp/version/commands.py +20 -49
  57. snowflake/cli/_plugins/nativeapp/version/version_processor.py +98 -0
  58. snowflake/cli/{plugins → _plugins}/notebook/commands.py +8 -6
  59. snowflake/cli/{plugins → _plugins}/notebook/manager.py +14 -14
  60. snowflake/cli/{plugins/nativeapp → _plugins/notebook}/plugin_spec.py +1 -1
  61. snowflake/cli/{plugins → _plugins}/notebook/types.py +0 -1
  62. snowflake/cli/{plugins → _plugins}/object/command_aliases.py +6 -5
  63. snowflake/cli/{plugins → _plugins}/object/commands.py +16 -10
  64. snowflake/cli/{plugins → _plugins}/object/manager.py +43 -21
  65. snowflake/cli/{plugins → _plugins}/object/plugin_spec.py +1 -1
  66. snowflake/cli/_plugins/snowpark/commands.py +450 -0
  67. snowflake/cli/_plugins/snowpark/common.py +268 -0
  68. snowflake/cli/{plugins → _plugins}/snowpark/models.py +2 -8
  69. snowflake/cli/{plugins → _plugins}/snowpark/package/anaconda_packages.py +2 -36
  70. snowflake/cli/{plugins → _plugins}/snowpark/package/commands.py +13 -74
  71. snowflake/cli/{plugins → _plugins}/snowpark/package/manager.py +4 -3
  72. snowflake/cli/{plugins → _plugins}/snowpark/package_utils.py +5 -5
  73. snowflake/cli/_plugins/snowpark/plugin_spec.py +30 -0
  74. snowflake/cli/_plugins/snowpark/snowpark_entity.py +29 -0
  75. snowflake/cli/_plugins/snowpark/snowpark_entity_model.py +173 -0
  76. snowflake/cli/_plugins/snowpark/snowpark_project_paths.py +109 -0
  77. snowflake/cli/{plugins → _plugins}/snowpark/snowpark_shared.py +0 -36
  78. snowflake/cli/{plugins → _plugins}/snowpark/zipper.py +16 -8
  79. snowflake/cli/{plugins → _plugins}/spcs/__init__.py +5 -7
  80. snowflake/cli/{plugins → _plugins}/spcs/compute_pool/commands.py +29 -28
  81. snowflake/cli/{plugins → _plugins}/spcs/compute_pool/manager.py +3 -3
  82. snowflake/cli/{plugins → _plugins}/spcs/image_registry/commands.py +3 -3
  83. snowflake/cli/{plugins → _plugins}/spcs/image_repository/commands.py +25 -19
  84. snowflake/cli/{plugins → _plugins}/spcs/image_repository/manager.py +1 -1
  85. snowflake/cli/{plugins → _plugins}/spcs/plugin_spec.py +1 -1
  86. snowflake/cli/{plugins → _plugins}/spcs/services/commands.py +66 -32
  87. snowflake/cli/{plugins → _plugins}/spcs/services/manager.py +43 -5
  88. snowflake/cli/{plugins → _plugins}/sql/commands.py +20 -17
  89. snowflake/cli/{plugins → _plugins}/sql/manager.py +1 -1
  90. snowflake/cli/{plugins → _plugins}/sql/plugin_spec.py +1 -1
  91. snowflake/cli/{plugins → _plugins}/stage/commands.py +20 -17
  92. snowflake/cli/{plugins → _plugins}/stage/diff.py +1 -47
  93. snowflake/cli/{plugins → _plugins}/stage/manager.py +62 -24
  94. snowflake/cli/{plugins → _plugins}/stage/plugin_spec.py +1 -1
  95. snowflake/cli/_plugins/stage/utils.py +54 -0
  96. snowflake/cli/{plugins → _plugins}/streamlit/commands.py +71 -62
  97. snowflake/cli/{plugins → _plugins}/streamlit/manager.py +68 -70
  98. snowflake/cli/_plugins/streamlit/plugin_spec.py +30 -0
  99. snowflake/cli/_plugins/streamlit/streamlit_entity.py +12 -0
  100. snowflake/cli/_plugins/streamlit/streamlit_entity_model.py +66 -0
  101. snowflake/cli/_plugins/workspace/action_context.py +18 -0
  102. snowflake/cli/_plugins/workspace/commands.py +306 -0
  103. snowflake/cli/_plugins/workspace/manager.py +74 -0
  104. snowflake/cli/_plugins/workspace/plugin_spec.py +30 -0
  105. snowflake/cli/api/cli_global_context.py +152 -295
  106. snowflake/cli/api/commands/common.py +25 -0
  107. snowflake/cli/api/commands/decorators.py +19 -4
  108. snowflake/cli/api/commands/experimental_behaviour.py +2 -3
  109. snowflake/cli/api/commands/flags.py +143 -222
  110. snowflake/cli/api/commands/overrideable_parameter.py +143 -0
  111. snowflake/cli/api/commands/snow_typer.py +21 -11
  112. snowflake/cli/api/commands/utils.py +18 -0
  113. snowflake/cli/api/config.py +44 -12
  114. snowflake/cli/api/connections.py +216 -0
  115. snowflake/cli/api/console/abc.py +8 -3
  116. snowflake/cli/api/constants.py +11 -0
  117. snowflake/cli/api/entities/common.py +56 -0
  118. snowflake/cli/api/entities/utils.py +370 -0
  119. snowflake/cli/api/errno.py +1 -0
  120. snowflake/cli/api/exceptions.py +31 -5
  121. snowflake/cli/api/feature_flags.py +0 -1
  122. snowflake/cli/api/identifiers.py +45 -9
  123. snowflake/cli/api/metrics.py +92 -0
  124. snowflake/cli/api/project/definition.py +48 -6
  125. snowflake/cli/api/project/definition_conversion.py +400 -0
  126. snowflake/cli/api/project/definition_manager.py +16 -5
  127. snowflake/cli/api/project/project_verification.py +3 -3
  128. snowflake/cli/api/project/schemas/entities/common.py +91 -16
  129. snowflake/cli/api/project/schemas/entities/entities.py +37 -6
  130. snowflake/cli/api/project/schemas/project_definition.py +180 -49
  131. snowflake/cli/api/project/schemas/updatable_model.py +11 -3
  132. snowflake/cli/api/project/schemas/v1/__init__.py +0 -0
  133. snowflake/cli/api/project/schemas/{identifier_model.py → v1/identifier_model.py} +3 -1
  134. snowflake/cli/api/project/schemas/v1/native_app/__init__.py +0 -0
  135. snowflake/cli/api/project/schemas/{native_app → v1/native_app}/application.py +8 -9
  136. snowflake/cli/api/project/schemas/{native_app → v1/native_app}/native_app.py +4 -4
  137. snowflake/cli/api/project/schemas/{native_app → v1/native_app}/package.py +7 -1
  138. snowflake/cli/api/project/schemas/v1/snowpark/__init__.py +0 -0
  139. snowflake/cli/api/project/schemas/{snowpark → v1/snowpark}/callable.py +2 -2
  140. snowflake/cli/api/project/schemas/{snowpark → v1/snowpark}/snowpark.py +2 -2
  141. snowflake/cli/api/project/schemas/v1/streamlit/__init__.py +0 -0
  142. snowflake/cli/api/project/schemas/{streamlit → v1/streamlit}/streamlit.py +2 -1
  143. snowflake/cli/api/project/util.py +23 -6
  144. snowflake/cli/api/rendering/jinja.py +14 -8
  145. snowflake/cli/api/rendering/project_definition_templates.py +5 -1
  146. snowflake/cli/api/rendering/sql_templates.py +56 -11
  147. snowflake/cli/api/rest_api.py +11 -5
  148. snowflake/cli/api/secure_path.py +16 -18
  149. snowflake/cli/api/secure_utils.py +90 -1
  150. snowflake/cli/api/sql_execution.py +47 -27
  151. snowflake/cli/api/utils/definition_rendering.py +45 -13
  152. {snowflake_cli_labs-2.8.0rc1.dist-info → snowflake_cli_labs-3.0.0.dist-info}/METADATA +20 -18
  153. snowflake_cli_labs-3.0.0.dist-info/RECORD +242 -0
  154. snowflake_cli_labs-3.0.0.dist-info/entry_points.txt +2 -0
  155. snowflake/cli/api/commands/project_initialisation.py +0 -65
  156. snowflake/cli/api/commands/typer_pre_execute.py +0 -26
  157. snowflake/cli/api/project/schemas/entities/application_entity.py +0 -44
  158. snowflake/cli/api/project/schemas/entities/application_package_entity.py +0 -66
  159. snowflake/cli/app/build_and_push.sh +0 -8
  160. snowflake/cli/plugins/nativeapp/codegen/setup/native_app_setup_processor.py +0 -172
  161. snowflake/cli/plugins/nativeapp/init.py +0 -345
  162. snowflake/cli/plugins/nativeapp/manager.py +0 -823
  163. snowflake/cli/plugins/nativeapp/run_processor.py +0 -389
  164. snowflake/cli/plugins/nativeapp/teardown_processor.py +0 -301
  165. snowflake/cli/plugins/nativeapp/v2_conversions/v2_to_v1_decorator.py +0 -135
  166. snowflake/cli/plugins/nativeapp/version/version_processor.py +0 -362
  167. snowflake/cli/plugins/object_stage_deprecated/__init__.py +0 -15
  168. snowflake/cli/plugins/object_stage_deprecated/commands.py +0 -122
  169. snowflake/cli/plugins/object_stage_deprecated/plugin_spec.py +0 -32
  170. snowflake/cli/plugins/snowpark/commands.py +0 -548
  171. snowflake/cli/plugins/snowpark/common.py +0 -307
  172. snowflake/cli/plugins/snowpark/manager.py +0 -109
  173. snowflake/cli/plugins/snowpark/plugin_spec.py +0 -30
  174. snowflake/cli/plugins/snowpark/snowpark_package_paths.py +0 -65
  175. snowflake/cli/plugins/spcs/jobs/commands.py +0 -78
  176. snowflake/cli/plugins/spcs/jobs/manager.py +0 -53
  177. snowflake/cli/plugins/streamlit/__init__.py +0 -13
  178. snowflake/cli/plugins/streamlit/plugin_spec.py +0 -30
  179. snowflake/cli/plugins/workspace/__init__.py +0 -13
  180. snowflake/cli/plugins/workspace/commands.py +0 -35
  181. snowflake/cli/plugins/workspace/plugin_spec.py +0 -30
  182. snowflake/cli/templates/default_snowpark/.gitignore +0 -4
  183. snowflake/cli/templates/default_snowpark/app/common.py +0 -2
  184. snowflake/cli/templates/default_snowpark/app/functions.py +0 -15
  185. snowflake/cli/templates/default_snowpark/app/procedures.py +0 -22
  186. snowflake/cli/templates/default_snowpark/requirements.txt +0 -1
  187. snowflake/cli/templates/default_snowpark/snowflake.yml +0 -23
  188. snowflake/cli/templates/default_streamlit/.gitignore +0 -4
  189. snowflake/cli/templates/default_streamlit/common/hello.py +0 -2
  190. snowflake/cli/templates/default_streamlit/environment.yml +0 -6
  191. snowflake/cli/templates/default_streamlit/pages/my_page.py +0 -3
  192. snowflake/cli/templates/default_streamlit/snowflake.yml +0 -10
  193. snowflake/cli/templates/default_streamlit/streamlit_app.py +0 -4
  194. snowflake_cli_labs-2.8.0rc1.dist-info/RECORD +0 -240
  195. snowflake_cli_labs-2.8.0rc1.dist-info/entry_points.txt +0 -2
  196. /snowflake/cli/{app → _app}/__init__.py +0 -0
  197. /snowflake/cli/{api/project/schemas/native_app → _app/api_impl}/__init__.py +0 -0
  198. /snowflake/cli/{api/project/schemas/snowpark → _app/api_impl/plugin}/__init__.py +0 -0
  199. /snowflake/cli/{app → _app}/api_impl/plugin/plugin_config_provider_impl.py +0 -0
  200. /snowflake/cli/{app → _app}/commands_registration/__init__.py +0 -0
  201. /snowflake/cli/{app → _app}/commands_registration/threadsafe.py +0 -0
  202. /snowflake/cli/{app → _app}/constants.py +0 -0
  203. /snowflake/cli/{api/project/schemas/streamlit → _app/dev}/__init__.py +0 -0
  204. /snowflake/cli/{app → _app}/dev/commands_structure.py +0 -0
  205. /snowflake/cli/{app/api_impl → _app/dev/docs}/__init__.py +0 -0
  206. /snowflake/cli/{app → _app}/dev/docs/project_definition_generate_json_schema.py +0 -0
  207. /snowflake/cli/{app → _app}/dev/docs/template_utils.py +0 -0
  208. /snowflake/cli/{app → _app}/dev/docs/templates/definition_description.rst.jinja2 +0 -0
  209. /snowflake/cli/{app → _app}/dev/docs/templates/overview.rst.jinja2 +0 -0
  210. /snowflake/cli/{app → _app}/dev/pycharm_remote_debug.py +0 -0
  211. /snowflake/cli/{app → _app}/loggers.py +0 -0
  212. /snowflake/cli/{app/api_impl/plugin → _plugins}/__init__.py +0 -0
  213. /snowflake/cli/{app/dev → _plugins/connection}/__init__.py +0 -0
  214. /snowflake/cli/{app/dev/docs → _plugins/cortex}/__init__.py +0 -0
  215. /snowflake/cli/{plugins → _plugins}/cortex/types.py +0 -0
  216. /snowflake/cli/{plugins → _plugins/git}/__init__.py +0 -0
  217. /snowflake/cli/{plugins/connection → _plugins/helpers}/__init__.py +0 -0
  218. /snowflake/cli/{plugins/cortex → _plugins/init}/__init__.py +0 -0
  219. /snowflake/cli/{plugins/git → _plugins/nativeapp}/__init__.py +0 -0
  220. /snowflake/cli/{plugins/init → _plugins/nativeapp/codegen}/__init__.py +0 -0
  221. /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/sandbox.py +0 -0
  222. /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/callback_source.py.jinja +0 -0
  223. /snowflake/cli/{plugins → _plugins}/nativeapp/constants.py +0 -0
  224. /snowflake/cli/{templates/default_snowpark/app → _plugins/nativeapp/entities}/__init__.py +0 -0
  225. /snowflake/cli/{plugins → _plugins}/nativeapp/feature_flags.py +0 -0
  226. /snowflake/cli/{plugins → _plugins}/nativeapp/utils.py +0 -0
  227. /snowflake/cli/{plugins/nativeapp → _plugins/nativeapp/version}/__init__.py +0 -0
  228. /snowflake/cli/{plugins/nativeapp/codegen → _plugins/notebook}/__init__.py +0 -0
  229. /snowflake/cli/{plugins → _plugins}/notebook/exceptions.py +0 -0
  230. /snowflake/cli/{plugins/nativeapp/version → _plugins/object}/__init__.py +0 -0
  231. /snowflake/cli/{plugins → _plugins}/object/common.py +0 -0
  232. /snowflake/cli/{plugins/notebook → _plugins/snowpark}/__init__.py +0 -0
  233. /snowflake/cli/{plugins/object → _plugins/snowpark/package}/__init__.py +0 -0
  234. /snowflake/cli/{plugins → _plugins}/snowpark/package/utils.py +0 -0
  235. /snowflake/cli/{plugins → _plugins}/spcs/common.py +0 -0
  236. /snowflake/cli/{plugins/snowpark → _plugins/spcs/compute_pool}/__init__.py +0 -0
  237. /snowflake/cli/{plugins/snowpark/package → _plugins/spcs/image_registry}/__init__.py +0 -0
  238. /snowflake/cli/{plugins → _plugins}/spcs/image_registry/manager.py +0 -0
  239. /snowflake/cli/{plugins/spcs/compute_pool → _plugins/spcs/image_repository}/__init__.py +0 -0
  240. /snowflake/cli/{plugins/spcs/image_registry → _plugins/spcs/services}/__init__.py +0 -0
  241. /snowflake/cli/{plugins/spcs/image_repository → _plugins/sql}/__init__.py +0 -0
  242. /snowflake/cli/{plugins → _plugins}/sql/snowsql_templating.py +0 -0
  243. /snowflake/cli/{plugins/spcs/jobs → _plugins/stage}/__init__.py +0 -0
  244. /snowflake/cli/{plugins → _plugins}/stage/md5.py +0 -0
  245. /snowflake/cli/{plugins/spcs/services → _plugins/streamlit}/__init__.py +0 -0
  246. /snowflake/cli/{plugins/sql → _plugins/workspace}/__init__.py +0 -0
  247. /snowflake/cli/{plugins/stage → api/project/schemas/entities}/__init__.py +0 -0
  248. /snowflake/cli/api/project/schemas/{native_app → v1/native_app}/path_mapping.py +0 -0
  249. /snowflake/cli/api/project/schemas/{snowpark → v1/snowpark}/argument.py +0 -0
  250. {snowflake_cli_labs-2.8.0rc1.dist-info → snowflake_cli_labs-3.0.0.dist-info}/WHEEL +0 -0
  251. {snowflake_cli_labs-2.8.0rc1.dist-info → snowflake_cli_labs-3.0.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,56 @@
1
+ from enum import Enum
2
+ from typing import Generic, Type, TypeVar, get_args
3
+
4
+ from snowflake.cli._plugins.workspace.action_context import ActionContext
5
+ from snowflake.cli.api.sql_execution import SqlExecutor
6
+
7
+
8
+ class EntityActions(str, Enum):
9
+ BUNDLE = "action_bundle"
10
+ DEPLOY = "action_deploy"
11
+ DROP = "action_drop"
12
+ VALIDATE = "action_validate"
13
+
14
+ VERSION_LIST = "action_version_list"
15
+ VERSION_CREATE = "action_version_create"
16
+ VERSION_DROP = "action_version_drop"
17
+
18
+
19
+ T = TypeVar("T")
20
+
21
+
22
+ class EntityBase(Generic[T]):
23
+ """
24
+ Base class for the fully-featured entity classes.
25
+ """
26
+
27
+ def __init__(self, entity_model: T):
28
+ self._entity_model = entity_model
29
+
30
+ @classmethod
31
+ def get_entity_model_type(cls) -> Type[T]:
32
+ """
33
+ Returns the generic model class specified in each entity class.
34
+
35
+ For example, calling ApplicationEntity.get_entity_model_type() will return the ApplicationEntityModel class.
36
+ """
37
+ return get_args(cls.__orig_bases__[0])[0] # type: ignore[attr-defined]
38
+
39
+ def supports(self, action: EntityActions) -> bool:
40
+ """
41
+ Checks whether this entity supports the given action. An entity is considered to support an action if it implements a method with the action name.
42
+ """
43
+ return callable(getattr(self, action, None))
44
+
45
+ def perform(
46
+ self, action: EntityActions, action_ctx: ActionContext, *args, **kwargs
47
+ ):
48
+ """
49
+ Performs the requested action.
50
+ """
51
+ return getattr(self, action)(action_ctx, *args, **kwargs)
52
+
53
+
54
+ def get_sql_executor() -> SqlExecutor:
55
+ """Returns an SQL Executor that uses the connection from the current CLI context"""
56
+ return SqlExecutor()
@@ -0,0 +1,370 @@
1
+ import os
2
+ from pathlib import Path
3
+ from textwrap import dedent
4
+ from typing import Any, List, NoReturn, Optional
5
+
6
+ import jinja2
7
+ from click import ClickException
8
+ from snowflake.cli._plugins.nativeapp.artifacts import (
9
+ BundleMap,
10
+ resolve_without_follow,
11
+ )
12
+ from snowflake.cli._plugins.nativeapp.exceptions import (
13
+ InvalidTemplateInFileError,
14
+ MissingScriptError,
15
+ )
16
+ from snowflake.cli._plugins.nativeapp.utils import verify_exists, verify_no_directories
17
+ from snowflake.cli._plugins.stage.diff import (
18
+ DiffResult,
19
+ StagePath,
20
+ compute_stage_diff,
21
+ preserve_from_diff,
22
+ sync_local_diff_with_stage,
23
+ to_stage_path,
24
+ )
25
+ from snowflake.cli._plugins.stage.utils import print_diff_to_console
26
+ from snowflake.cli.api.cli_global_context import get_cli_context
27
+ from snowflake.cli.api.console.abc import AbstractConsole
28
+ from snowflake.cli.api.entities.common import get_sql_executor
29
+ from snowflake.cli.api.errno import (
30
+ DOES_NOT_EXIST_OR_CANNOT_BE_PERFORMED,
31
+ NO_WAREHOUSE_SELECTED_IN_SESSION,
32
+ )
33
+ from snowflake.cli.api.exceptions import SnowflakeSQLExecutionError
34
+ from snowflake.cli.api.metrics import CLICounterField
35
+ from snowflake.cli.api.project.schemas.entities.common import PostDeployHook
36
+ from snowflake.cli.api.rendering.sql_templates import (
37
+ choose_sql_jinja_env_based_on_template_syntax,
38
+ )
39
+ from snowflake.cli.api.secure_path import UNLIMITED, SecurePath
40
+ from snowflake.connector import ProgrammingError
41
+ from snowflake.connector.cursor import SnowflakeCursor
42
+
43
+
44
+ def generic_sql_error_handler(
45
+ err: ProgrammingError, role: Optional[str] = None, warehouse: Optional[str] = None
46
+ ) -> NoReturn:
47
+ # Potential refactor: If moving away from Python 3.8 and 3.9 to >= 3.10, use match ... case
48
+ if err.errno == DOES_NOT_EXIST_OR_CANNOT_BE_PERFORMED:
49
+ raise ProgrammingError(
50
+ msg=dedent(
51
+ f"""\
52
+ Received error message '{err.msg}' while executing SQL statement.
53
+ '{role}' may not have access to warehouse '{warehouse}'.
54
+ Please grant usage privilege on warehouse to this role.
55
+ """
56
+ ),
57
+ errno=err.errno,
58
+ )
59
+ elif err.errno == NO_WAREHOUSE_SELECTED_IN_SESSION:
60
+ raise ProgrammingError(
61
+ msg=dedent(
62
+ f"""\
63
+ Received error message '{err.msg}' while executing SQL statement.
64
+ Please provide a warehouse for the active session role in your project definition file, config.toml file, or via command line.
65
+ """
66
+ ),
67
+ errno=err.errno,
68
+ )
69
+ elif "does not exist or not authorized" in err.msg:
70
+ raise ProgrammingError(
71
+ msg=dedent(
72
+ f"""\
73
+ Received error message '{err.msg}' while executing SQL statement.
74
+ Please check the name of the resource you are trying to query or the permissions of the role you are using to run the query.
75
+ """
76
+ )
77
+ )
78
+ raise err
79
+
80
+
81
+ def _get_stage_paths_to_sync(
82
+ local_paths_to_sync: List[Path], deploy_root: Path
83
+ ) -> List[StagePath]:
84
+ """
85
+ Takes a list of paths (files and directories), returning a list of all files recursively relative to the deploy root.
86
+ """
87
+
88
+ stage_paths = []
89
+ for path in local_paths_to_sync:
90
+ if path.is_dir():
91
+ for current_dir, _dirs, files in os.walk(path):
92
+ for file in files:
93
+ deploy_path = Path(current_dir, file).relative_to(deploy_root)
94
+ stage_paths.append(to_stage_path(deploy_path))
95
+ else:
96
+ stage_paths.append(to_stage_path(path.relative_to(deploy_root)))
97
+ return stage_paths
98
+
99
+
100
+ def sync_deploy_root_with_stage(
101
+ console: AbstractConsole,
102
+ deploy_root: Path,
103
+ package_name: str,
104
+ stage_schema: str,
105
+ bundle_map: BundleMap,
106
+ role: str,
107
+ prune: bool,
108
+ recursive: bool,
109
+ stage_fqn: str,
110
+ local_paths_to_sync: List[Path] | None = None,
111
+ print_diff: bool = True,
112
+ ) -> DiffResult:
113
+ """
114
+ Ensures that the files on our remote stage match the artifacts we have in
115
+ the local filesystem.
116
+
117
+ Args:
118
+ bundle_map (BundleMap): The artifact mapping computed by the `build_bundle` function.
119
+ role (str): The name of the role to use for queries and commands.
120
+ prune (bool): Whether to prune artifacts from the stage that don't exist locally.
121
+ recursive (bool): Whether to traverse directories recursively.
122
+ stage_fqn (str): The name of the stage to diff against and upload to.
123
+ local_paths_to_sync (List[Path], optional): List of local paths to sync. Defaults to None to sync all
124
+ local paths. Note that providing an empty list here is equivalent to None.
125
+ print_diff (bool): Whether to print the diff between the local files and the remote stage. Defaults to True
126
+
127
+ Returns:
128
+ A `DiffResult` instance describing the changes that were performed.
129
+ """
130
+
131
+ sql_executor = get_sql_executor()
132
+ # Does a stage already exist within the application package, or we need to create one?
133
+ # Using "if not exists" should take care of either case.
134
+ console.step(
135
+ f"Checking if stage {stage_fqn} exists, or creating a new one if none exists."
136
+ )
137
+ with sql_executor.use_role(role):
138
+ sql_executor.execute_query(
139
+ f"create schema if not exists {package_name}.{stage_schema}"
140
+ )
141
+ sql_executor.execute_query(
142
+ f"""
143
+ create stage if not exists {stage_fqn}
144
+ encryption = (TYPE = 'SNOWFLAKE_SSE')
145
+ DIRECTORY = (ENABLE = TRUE)"""
146
+ )
147
+
148
+ # Perform a diff operation and display results to the user for informational purposes
149
+ if print_diff:
150
+ console.step(
151
+ "Performing a diff between the Snowflake stage and your local deploy_root ('%s') directory."
152
+ % deploy_root.resolve()
153
+ )
154
+ diff: DiffResult = compute_stage_diff(deploy_root, stage_fqn)
155
+
156
+ if local_paths_to_sync:
157
+ # Deploying specific files/directories
158
+ resolved_paths_to_sync = [
159
+ resolve_without_follow(p) for p in local_paths_to_sync
160
+ ]
161
+ if not recursive:
162
+ verify_no_directories(resolved_paths_to_sync)
163
+
164
+ deploy_paths_to_sync = []
165
+ for resolved_path in resolved_paths_to_sync:
166
+ verify_exists(resolved_path)
167
+ deploy_paths = bundle_map.to_deploy_paths(resolved_path)
168
+ if not deploy_paths:
169
+ if resolved_path.is_dir() and recursive:
170
+ # No direct artifact mapping found for this path. Check to see
171
+ # if there are subpaths of this directory that are matches. We
172
+ # loop over sources because it's likely a much smaller list
173
+ # than the project directory.
174
+ for src in bundle_map.all_sources(absolute=True):
175
+ if resolved_path in src.parents:
176
+ # There is a source that contains this path, get its dest path(s)
177
+ deploy_paths.extend(bundle_map.to_deploy_paths(src))
178
+
179
+ if not deploy_paths:
180
+ raise ClickException(f"No artifact found for {resolved_path}")
181
+ deploy_paths_to_sync.extend(deploy_paths)
182
+
183
+ stage_paths_to_sync = _get_stage_paths_to_sync(
184
+ deploy_paths_to_sync, resolve_without_follow(deploy_root)
185
+ )
186
+ diff = preserve_from_diff(diff, stage_paths_to_sync)
187
+ else:
188
+ # Full deploy
189
+ if not recursive:
190
+ verify_no_directories(deploy_root.resolve().iterdir())
191
+
192
+ if not prune:
193
+ files_not_removed = [str(path) for path in diff.only_on_stage]
194
+ diff.only_on_stage = []
195
+
196
+ if len(files_not_removed) > 0:
197
+ files_not_removed_str = "\n".join(files_not_removed)
198
+ console.warning(
199
+ f"The following files exist only on the stage:\n{files_not_removed_str}\n\nUse the --prune flag to delete them from the stage."
200
+ )
201
+
202
+ if print_diff:
203
+ print_diff_to_console(diff, bundle_map)
204
+
205
+ # Upload diff-ed files to application package stage
206
+ if diff.has_changes():
207
+ console.step(
208
+ "Updating the Snowflake stage from your local %s directory."
209
+ % deploy_root.resolve(),
210
+ )
211
+ sync_local_diff_with_stage(
212
+ role=role,
213
+ deploy_root_path=deploy_root,
214
+ diff_result=diff,
215
+ stage_fqn=stage_fqn,
216
+ )
217
+ return diff
218
+
219
+
220
+ def _execute_sql_script(
221
+ script_content: str,
222
+ database_name: Optional[str] = None,
223
+ ) -> None:
224
+ """
225
+ Executing the provided SQL script content.
226
+ This assumes that a relevant warehouse is already active.
227
+ If database_name is passed in, it will be used first.
228
+ """
229
+ try:
230
+ sql_executor = get_sql_executor()
231
+ if database_name is not None:
232
+ sql_executor.execute_query(f"use database {database_name}")
233
+ sql_executor.execute_queries(script_content)
234
+ except ProgrammingError as err:
235
+ generic_sql_error_handler(err)
236
+
237
+
238
+ def execute_post_deploy_hooks(
239
+ console: AbstractConsole,
240
+ project_root: Path,
241
+ post_deploy_hooks: Optional[List[PostDeployHook]],
242
+ deployed_object_type: str,
243
+ database_name: str,
244
+ ) -> None:
245
+ """
246
+ Executes post-deploy hooks for the given object type.
247
+ While executing SQL post deploy hooks, it first switches to the database provided in the input.
248
+ All post deploy scripts templates will first be expanded using the global template context.
249
+ """
250
+ if not post_deploy_hooks:
251
+ return
252
+
253
+ get_cli_context().metrics.set_counter(CLICounterField.POST_DEPLOY_SCRIPTS, 1)
254
+
255
+ with console.phase(f"Executing {deployed_object_type} post-deploy actions"):
256
+ sql_scripts_paths = []
257
+ for hook in post_deploy_hooks:
258
+ if hook.sql_script:
259
+ sql_scripts_paths.append(hook.sql_script)
260
+ else:
261
+ raise ValueError(
262
+ f"Unsupported {deployed_object_type} post-deploy hook type: {hook}"
263
+ )
264
+
265
+ scripts_content_list = render_script_templates(
266
+ project_root,
267
+ get_cli_context().template_context,
268
+ sql_scripts_paths,
269
+ )
270
+
271
+ for index, sql_script_path in enumerate(sql_scripts_paths):
272
+ console.step(f"Executing SQL script: {sql_script_path}")
273
+ _execute_sql_script(
274
+ script_content=scripts_content_list[index],
275
+ database_name=database_name,
276
+ )
277
+
278
+
279
+ def render_script_templates(
280
+ project_root: Path,
281
+ jinja_context: dict[str, Any],
282
+ scripts: List[str],
283
+ override_env: Optional[jinja2.Environment] = None,
284
+ ) -> List[str]:
285
+ """
286
+ Input:
287
+ - project_root: path to project root
288
+ - jinja_context: a dictionary with the jinja context
289
+ - scripts: list of script paths relative to the project root
290
+ - override_env: optional jinja environment to use for rendering,
291
+ if not provided, the environment will be chosen based on the template syntax
292
+ Returns:
293
+ - List of rendered scripts content
294
+ Size of the return list is the same as the size of the input scripts list.
295
+ """
296
+ return [
297
+ render_script_template(project_root, jinja_context, script, override_env)
298
+ for script in scripts
299
+ ]
300
+
301
+
302
+ def render_script_template(
303
+ project_root: Path,
304
+ jinja_context: dict[str, Any],
305
+ script: str,
306
+ override_env: Optional[jinja2.Environment] = None,
307
+ ) -> str:
308
+ script_full_path = SecurePath(project_root) / script
309
+ try:
310
+ template_content = script_full_path.read_text(file_size_limit_mb=UNLIMITED)
311
+ env = override_env or choose_sql_jinja_env_based_on_template_syntax(
312
+ template_content, reference_name=script
313
+ )
314
+ return env.from_string(template_content).render(jinja_context)
315
+
316
+ except FileNotFoundError as e:
317
+ raise MissingScriptError(script) from e
318
+
319
+ except jinja2.TemplateSyntaxError as e:
320
+ raise InvalidTemplateInFileError(script, e, e.lineno) from e
321
+
322
+ except jinja2.UndefinedError as e:
323
+ raise InvalidTemplateInFileError(script, e) from e
324
+
325
+
326
+ def validation_item_to_str(item: dict[str, str | int]):
327
+ s = item["message"]
328
+ if item["errorCode"]:
329
+ s = f"{s} (error code {item['errorCode']})"
330
+ return s
331
+
332
+
333
+ def drop_generic_object(
334
+ console: AbstractConsole,
335
+ object_type: str,
336
+ object_name: str,
337
+ role: str,
338
+ cascade: bool = False,
339
+ ):
340
+ """
341
+ Drop object using the given role.
342
+ """
343
+ sql_executor = get_sql_executor()
344
+ with sql_executor.use_role(role):
345
+ console.step(f"Dropping {object_type} {object_name} now.")
346
+ drop_query = f"drop {object_type} {object_name}"
347
+ if cascade:
348
+ drop_query += " cascade"
349
+ try:
350
+ sql_executor.execute_query(drop_query)
351
+ except:
352
+ raise SnowflakeSQLExecutionError(drop_query)
353
+
354
+ console.message(f"Dropped {object_type} {object_name} successfully.")
355
+
356
+
357
+ def print_messages(
358
+ console: AbstractConsole, create_or_upgrade_cursor: Optional[SnowflakeCursor]
359
+ ):
360
+ """
361
+ Shows messages in the console returned by the CREATE or UPGRADE
362
+ APPLICATION command.
363
+ """
364
+ if not create_or_upgrade_cursor:
365
+ return
366
+
367
+ messages = [row[0] for row in create_or_upgrade_cursor.fetchall()]
368
+ for message in messages:
369
+ console.warning(message)
370
+ console.message("")
@@ -17,6 +17,7 @@ NO_WAREHOUSE_SELECTED_IN_SESSION = 606
17
17
 
18
18
  DOES_NOT_EXIST_OR_NOT_AUTHORIZED = 2003
19
19
  DOES_NOT_EXIST_OR_CANNOT_BE_PERFORMED = 2043
20
+ INSUFFICIENT_PRIVILEGES = 3001
20
21
 
21
22
  # Native Apps
22
23
  CANNOT_UPGRADE_FROM_LOOSE_FILES_TO_VERSION = 93044
@@ -17,8 +17,9 @@ from __future__ import annotations
17
17
  from pathlib import Path
18
18
  from typing import Optional
19
19
 
20
- from click.exceptions import ClickException
20
+ from click.exceptions import ClickException, UsageError
21
21
  from snowflake.cli.api.constants import ObjectType
22
+ from snowflake.connector.compat import IS_WINDOWS
22
23
 
23
24
 
24
25
  class EnvironmentVariableNotFoundError(ClickException):
@@ -101,9 +102,16 @@ class ObjectAlreadyExistsError(ClickException):
101
102
 
102
103
 
103
104
  class NoProjectDefinitionError(ClickException):
104
- def __init__(self, project_type: str, project_file: str):
105
+ def __init__(self, project_type: str, project_root: str | Path):
105
106
  super().__init__(
106
- f"No {project_type} project definition found in {project_file}"
107
+ f"No {project_type} project definition found in {project_root}"
108
+ )
109
+
110
+
111
+ class InvalidProjectDefinitionVersionError(ClickException):
112
+ def __init__(self, expected_version: str, actual_version: str):
113
+ super().__init__(
114
+ f"This command only supports definition version {expected_version}, got {actual_version}."
107
115
  )
108
116
 
109
117
 
@@ -133,9 +141,18 @@ class DirectoryIsNotEmptyError(ClickException):
133
141
 
134
142
  class ConfigFileTooWidePermissionsError(ClickException):
135
143
  def __init__(self, path: Path):
136
- super().__init__(
137
- f'Configuration file {path} has too wide permissions, run `chmod 0600 "{path}"`'
144
+ change_permissons_command = (
145
+ f'icacls "{path}" /deny <USER_ID>:F'
146
+ if IS_WINDOWS
147
+ else f'chmod 0600 "{path}"'
138
148
  )
149
+ msg = f"Configuration file {path} has too wide permissions, run `{change_permissons_command}`."
150
+ if IS_WINDOWS:
151
+ msg += (
152
+ f'\nTo check which users have access to the file run `icacls "{path}"`.'
153
+ "Run the above command for all users except you and administrators."
154
+ )
155
+ super().__init__(msg)
139
156
 
140
157
 
141
158
  class DatabaseNotProvidedError(ClickException):
@@ -162,3 +179,12 @@ class FQNInconsistencyError(ClickException):
162
179
  super().__init__(
163
180
  f"{part.capitalize()} provided but name '{name}' is fully qualified name."
164
181
  )
182
+
183
+
184
+ class IncompatibleParametersError(UsageError):
185
+ def __init__(self, options: list[str]):
186
+ options_with_quotes = [f"'{option}'" for option in options]
187
+ comma_separated_options = ", ".join(options_with_quotes[:-1])
188
+ super().__init__(
189
+ f"Parameters {comma_separated_options} and {options_with_quotes[-1]} are incompatible and cannot be used simultaneously."
190
+ )
@@ -52,4 +52,3 @@ class FeatureFlag(FeatureFlagMixin):
52
52
  ENABLE_STREAMLIT_VERSIONED_STAGE = BooleanFlag(
53
53
  "ENABLE_STREAMLIT_VERSIONED_STAGE", False
54
54
  )
55
- ENABLE_PROJECT_DEFINITION_V2 = BooleanFlag("ENABLE_PROJECT_DEFINITION_V2", False)
@@ -17,9 +17,10 @@ from __future__ import annotations
17
17
  import re
18
18
 
19
19
  from click import ClickException
20
- from snowflake.cli.api.cli_global_context import cli_context
21
20
  from snowflake.cli.api.exceptions import FQNInconsistencyError, FQNNameError
22
- from snowflake.cli.api.project.schemas.identifier_model import ObjectIdentifierBaseModel
21
+ from snowflake.cli.api.project.schemas.v1.identifier_model import (
22
+ ObjectIdentifierBaseModel,
23
+ )
23
24
  from snowflake.cli.api.project.util import VALID_IDENTIFIER_REGEX, identifier_for_url
24
25
 
25
26
 
@@ -35,10 +36,17 @@ class FQN:
35
36
  fqn = FQN.from_string("my_name").set_database("db").set_schema("foo")
36
37
  """
37
38
 
38
- def __init__(self, database: str | None, schema: str | None, name: str):
39
+ def __init__(
40
+ self,
41
+ database: str | None,
42
+ schema: str | None,
43
+ name: str,
44
+ signature: str | None = None,
45
+ ):
39
46
  self._database = database
40
47
  self._schema = schema
41
48
  self._name = name
49
+ self.signature = signature
42
50
 
43
51
  @property
44
52
  def database(self) -> str | None:
@@ -72,6 +80,8 @@ class FQN:
72
80
 
73
81
  @property
74
82
  def sql_identifier(self) -> str:
83
+ if self.signature:
84
+ return f"IDENTIFIER('{self.identifier}'){self.signature}"
75
85
  return f"IDENTIFIER('{self.identifier}')"
76
86
 
77
87
  def __str__(self):
@@ -98,9 +108,13 @@ class FQN:
98
108
  else:
99
109
  database = None
100
110
  schema = result.group("first_qualifier")
101
- if signature := result.group("signature"):
102
- unqualified_name = unqualified_name + signature
103
- return cls(name=unqualified_name, schema=schema, database=database)
111
+
112
+ signature = None
113
+ if result.group("signature"):
114
+ signature = result.group("signature")
115
+ return cls(
116
+ name=unqualified_name, schema=schema, database=database, signature=signature
117
+ )
104
118
 
105
119
  @classmethod
106
120
  def from_stage(cls, stage: str) -> "FQN":
@@ -110,11 +124,11 @@ class FQN:
110
124
  return cls.from_string(name)
111
125
 
112
126
  @classmethod
113
- def from_identifier_model(cls, model: ObjectIdentifierBaseModel) -> "FQN":
127
+ def from_identifier_model_v1(cls, model: ObjectIdentifierBaseModel) -> "FQN":
114
128
  """Create an instance from object model."""
115
129
  if not isinstance(model, ObjectIdentifierBaseModel):
116
130
  raise ClickException(
117
- f"Expected {type(ObjectIdentifierBaseModel)}, got {model}."
131
+ f"Expected {type(ObjectIdentifierBaseModel).__name__}, got {model}."
118
132
  )
119
133
 
120
134
  fqn = cls.from_string(model.name)
@@ -126,6 +140,23 @@ class FQN:
126
140
 
127
141
  return fqn.set_database(model.database).set_schema(model.schema_name)
128
142
 
143
+ @classmethod
144
+ def from_identifier_model_v2(cls, model) -> "FQN":
145
+ """Create an instance from object model."""
146
+ from snowflake.cli.api.project.schemas.entities.common import Identifier
147
+
148
+ if not isinstance(model, Identifier):
149
+ raise ClickException(f"Expected {type(Identifier).__name__}, got {model}.")
150
+
151
+ fqn = cls.from_string(model.name)
152
+
153
+ if fqn.database and model.database:
154
+ raise FQNInconsistencyError("database", model.name)
155
+ if fqn.schema and model.schema_:
156
+ raise FQNInconsistencyError("schema", model.name)
157
+
158
+ return fqn.set_database(model.database).set_schema(model.schema_)
159
+
129
160
  def set_database(self, database: str | None) -> "FQN":
130
161
  if database:
131
162
  self._database = database
@@ -151,4 +182,9 @@ class FQN:
151
182
 
152
183
  def using_context(self) -> "FQN":
153
184
  """Update the instance with database and schema from connection in current cli context."""
154
- return self.using_connection(cli_context.connection)
185
+ from snowflake.cli.api.cli_global_context import get_cli_context
186
+
187
+ return self.using_connection(get_cli_context().connection)
188
+
189
+ def to_dict(self) -> dict:
190
+ return {"name": self.name, "schema": self.schema, "database": self.database}