snowflake-cli-labs 2.8.0rc1__py3-none-any.whl → 3.0.0rc1__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 (224) 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 +12 -12
  4. snowflake/cli/{app → _app}/commands_registration/builtin_plugins.py +13 -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 → _app}/snow_connector.py +24 -17
  16. snowflake/cli/{app → _app}/telemetry.py +4 -5
  17. snowflake/cli/{plugins → _plugins}/connection/commands.py +25 -7
  18. snowflake/cli/_plugins/connection/plugin_spec.py +30 -0
  19. snowflake/cli/{plugins → _plugins}/connection/util.py +16 -0
  20. snowflake/cli/{plugins → _plugins}/cortex/commands.py +54 -49
  21. snowflake/cli/{plugins → _plugins}/cortex/constants.py +1 -1
  22. snowflake/cli/{plugins → _plugins}/cortex/manager.py +5 -5
  23. snowflake/cli/{plugins → _plugins}/cortex/plugin_spec.py +1 -1
  24. snowflake/cli/{plugins → _plugins}/git/commands.py +32 -20
  25. snowflake/cli/{plugins → _plugins}/git/manager.py +20 -11
  26. snowflake/cli/{plugins → _plugins}/git/plugin_spec.py +1 -1
  27. snowflake/cli/{plugins → _plugins}/init/commands.py +10 -6
  28. snowflake/cli/{plugins → _plugins}/init/plugin_spec.py +1 -1
  29. snowflake/cli/{plugins → _plugins}/nativeapp/artifacts.py +14 -0
  30. snowflake/cli/_plugins/nativeapp/bundle_context.py +31 -0
  31. snowflake/cli/{plugins → _plugins}/nativeapp/codegen/artifact_processor.py +3 -3
  32. snowflake/cli/{plugins → _plugins}/nativeapp/codegen/compiler.py +32 -18
  33. snowflake/cli/_plugins/nativeapp/codegen/setup/native_app_setup_processor.py +249 -0
  34. snowflake/cli/{plugins → _plugins}/nativeapp/codegen/setup/setup_driver.py.source +5 -2
  35. snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/extension_function_utils.py +4 -4
  36. snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/python_processor.py +23 -29
  37. snowflake/cli/_plugins/nativeapp/codegen/templates/templates_processor.py +93 -0
  38. snowflake/cli/{plugins → _plugins}/nativeapp/commands.py +171 -42
  39. snowflake/cli/{plugins → _plugins}/nativeapp/common_flags.py +1 -1
  40. snowflake/cli/{plugins → _plugins}/nativeapp/exceptions.py +3 -3
  41. snowflake/cli/{plugins → _plugins}/nativeapp/init.py +1 -1
  42. snowflake/cli/_plugins/nativeapp/manager.py +572 -0
  43. snowflake/cli/{plugins/connection → _plugins/nativeapp}/plugin_spec.py +1 -1
  44. snowflake/cli/{plugins → _plugins}/nativeapp/project_model.py +35 -19
  45. snowflake/cli/{plugins → _plugins}/nativeapp/run_processor.py +25 -23
  46. snowflake/cli/{plugins → _plugins}/nativeapp/teardown_processor.py +24 -110
  47. snowflake/cli/{plugins → _plugins}/nativeapp/v2_conversions/v2_to_v1_decorator.py +47 -28
  48. snowflake/cli/{plugins → _plugins}/nativeapp/version/commands.py +15 -12
  49. snowflake/cli/{plugins → _plugins}/nativeapp/version/version_processor.py +22 -20
  50. snowflake/cli/{plugins → _plugins}/notebook/commands.py +8 -6
  51. snowflake/cli/{plugins → _plugins}/notebook/manager.py +14 -14
  52. snowflake/cli/{plugins → _plugins}/notebook/plugin_spec.py +1 -1
  53. snowflake/cli/{plugins → _plugins}/notebook/types.py +0 -1
  54. snowflake/cli/{plugins → _plugins}/object/command_aliases.py +6 -5
  55. snowflake/cli/{plugins → _plugins}/object/commands.py +16 -10
  56. snowflake/cli/{plugins → _plugins}/object/manager.py +7 -6
  57. snowflake/cli/{plugins → _plugins}/object/plugin_spec.py +1 -1
  58. snowflake/cli/_plugins/snowpark/commands.py +450 -0
  59. snowflake/cli/_plugins/snowpark/common.py +268 -0
  60. snowflake/cli/{plugins → _plugins}/snowpark/models.py +0 -7
  61. snowflake/cli/{plugins → _plugins}/snowpark/package/anaconda_packages.py +2 -36
  62. snowflake/cli/{plugins → _plugins}/snowpark/package/commands.py +13 -74
  63. snowflake/cli/{plugins → _plugins}/snowpark/package/manager.py +4 -3
  64. snowflake/cli/{plugins → _plugins}/snowpark/package_utils.py +5 -5
  65. snowflake/cli/{plugins/nativeapp → _plugins/snowpark}/plugin_spec.py +1 -1
  66. snowflake/cli/_plugins/snowpark/snowpark_project_paths.py +109 -0
  67. snowflake/cli/{plugins → _plugins}/snowpark/snowpark_shared.py +0 -36
  68. snowflake/cli/{plugins → _plugins}/snowpark/zipper.py +16 -8
  69. snowflake/cli/{plugins → _plugins}/spcs/__init__.py +5 -7
  70. snowflake/cli/{plugins → _plugins}/spcs/compute_pool/commands.py +29 -28
  71. snowflake/cli/{plugins → _plugins}/spcs/compute_pool/manager.py +3 -3
  72. snowflake/cli/{plugins → _plugins}/spcs/image_registry/commands.py +3 -3
  73. snowflake/cli/{plugins → _plugins}/spcs/image_repository/commands.py +25 -19
  74. snowflake/cli/{plugins → _plugins}/spcs/image_repository/manager.py +1 -1
  75. snowflake/cli/{plugins → _plugins}/spcs/plugin_spec.py +1 -1
  76. snowflake/cli/{plugins → _plugins}/spcs/services/commands.py +66 -32
  77. snowflake/cli/{plugins → _plugins}/spcs/services/manager.py +43 -5
  78. snowflake/cli/{plugins → _plugins}/sql/commands.py +20 -17
  79. snowflake/cli/{plugins → _plugins}/sql/manager.py +1 -1
  80. snowflake/cli/{plugins → _plugins}/sql/plugin_spec.py +1 -1
  81. snowflake/cli/{plugins → _plugins}/stage/commands.py +20 -17
  82. snowflake/cli/{plugins → _plugins}/stage/diff.py +1 -47
  83. snowflake/cli/{plugins → _plugins}/stage/manager.py +54 -21
  84. snowflake/cli/{plugins → _plugins}/stage/plugin_spec.py +1 -1
  85. snowflake/cli/_plugins/stage/utils.py +54 -0
  86. snowflake/cli/{plugins → _plugins}/streamlit/commands.py +59 -62
  87. snowflake/cli/{plugins → _plugins}/streamlit/manager.py +51 -70
  88. snowflake/cli/_plugins/streamlit/plugin_spec.py +30 -0
  89. snowflake/cli/_plugins/workspace/action_context.py +17 -0
  90. snowflake/cli/_plugins/workspace/commands.py +194 -0
  91. snowflake/cli/_plugins/workspace/manager.py +73 -0
  92. snowflake/cli/{plugins → _plugins}/workspace/plugin_spec.py +1 -1
  93. snowflake/cli/api/cli_global_context.py +40 -13
  94. snowflake/cli/api/commands/common.py +25 -0
  95. snowflake/cli/api/commands/decorators.py +5 -4
  96. snowflake/cli/api/commands/experimental_behaviour.py +2 -3
  97. snowflake/cli/api/commands/flags.py +97 -179
  98. snowflake/cli/api/commands/overrideable_parameter.py +143 -0
  99. snowflake/cli/api/commands/snow_typer.py +14 -6
  100. snowflake/cli/api/commands/typer_pre_execute.py +3 -3
  101. snowflake/cli/api/commands/utils.py +18 -0
  102. snowflake/cli/api/config.py +18 -5
  103. snowflake/cli/api/console/abc.py +5 -2
  104. snowflake/cli/api/constants.py +11 -0
  105. snowflake/cli/api/entities/application_entity.py +12 -0
  106. snowflake/cli/api/entities/application_package_entity.py +553 -0
  107. snowflake/cli/api/entities/common.py +51 -0
  108. snowflake/cli/api/entities/snowpark_entity.py +29 -0
  109. snowflake/cli/api/entities/streamlit_entity.py +12 -0
  110. snowflake/cli/api/entities/utils.py +357 -0
  111. snowflake/cli/api/exceptions.py +31 -5
  112. snowflake/cli/api/feature_flags.py +0 -1
  113. snowflake/cli/api/identifiers.py +41 -9
  114. snowflake/cli/api/project/definition.py +37 -6
  115. snowflake/cli/api/project/definition_conversion.py +194 -0
  116. snowflake/cli/api/project/definition_manager.py +12 -1
  117. snowflake/cli/api/project/project_verification.py +3 -3
  118. snowflake/cli/api/project/schemas/entities/{application_entity.py → application_entity_model.py} +21 -9
  119. snowflake/cli/api/project/schemas/entities/{application_package_entity.py → application_package_entity_model.py} +43 -15
  120. snowflake/cli/api/project/schemas/entities/common.py +80 -6
  121. snowflake/cli/api/project/schemas/entities/entities.py +38 -8
  122. snowflake/cli/api/project/schemas/entities/snowpark_entity.py +176 -0
  123. snowflake/cli/api/project/schemas/entities/streamlit_entity_model.py +73 -0
  124. snowflake/cli/api/project/schemas/identifier_model.py +10 -1
  125. snowflake/cli/api/project/schemas/native_app/application.py +8 -9
  126. snowflake/cli/api/project/schemas/native_app/package.py +7 -1
  127. snowflake/cli/api/project/schemas/project_definition.py +98 -27
  128. snowflake/cli/api/project/schemas/updatable_model.py +11 -3
  129. snowflake/cli/api/project/util.py +23 -6
  130. snowflake/cli/api/rendering/jinja.py +14 -8
  131. snowflake/cli/api/rendering/project_definition_templates.py +1 -1
  132. snowflake/cli/api/rendering/sql_templates.py +43 -11
  133. snowflake/cli/api/secure_path.py +16 -18
  134. snowflake/cli/api/secure_utils.py +90 -1
  135. snowflake/cli/api/sql_execution.py +48 -19
  136. snowflake/cli/api/utils/definition_rendering.py +18 -8
  137. {snowflake_cli_labs-2.8.0rc1.dist-info → snowflake_cli_labs-3.0.0rc1.dist-info}/METADATA +13 -13
  138. snowflake_cli_labs-3.0.0rc1.dist-info/RECORD +236 -0
  139. snowflake_cli_labs-3.0.0rc1.dist-info/entry_points.txt +2 -0
  140. snowflake/cli/api/commands/project_initialisation.py +0 -65
  141. snowflake/cli/app/build_and_push.sh +0 -8
  142. snowflake/cli/plugins/nativeapp/codegen/setup/native_app_setup_processor.py +0 -172
  143. snowflake/cli/plugins/nativeapp/manager.py +0 -823
  144. snowflake/cli/plugins/object_stage_deprecated/__init__.py +0 -15
  145. snowflake/cli/plugins/object_stage_deprecated/commands.py +0 -122
  146. snowflake/cli/plugins/object_stage_deprecated/plugin_spec.py +0 -32
  147. snowflake/cli/plugins/snowpark/commands.py +0 -548
  148. snowflake/cli/plugins/snowpark/common.py +0 -307
  149. snowflake/cli/plugins/snowpark/manager.py +0 -109
  150. snowflake/cli/plugins/snowpark/plugin_spec.py +0 -30
  151. snowflake/cli/plugins/snowpark/snowpark_package_paths.py +0 -65
  152. snowflake/cli/plugins/spcs/jobs/commands.py +0 -78
  153. snowflake/cli/plugins/spcs/jobs/manager.py +0 -53
  154. snowflake/cli/plugins/streamlit/plugin_spec.py +0 -30
  155. snowflake/cli/plugins/workspace/commands.py +0 -35
  156. snowflake/cli/templates/default_snowpark/.gitignore +0 -4
  157. snowflake/cli/templates/default_snowpark/app/__init__.py +0 -0
  158. snowflake/cli/templates/default_snowpark/app/common.py +0 -2
  159. snowflake/cli/templates/default_snowpark/app/functions.py +0 -15
  160. snowflake/cli/templates/default_snowpark/app/procedures.py +0 -22
  161. snowflake/cli/templates/default_snowpark/requirements.txt +0 -1
  162. snowflake/cli/templates/default_snowpark/snowflake.yml +0 -23
  163. snowflake/cli/templates/default_streamlit/.gitignore +0 -4
  164. snowflake/cli/templates/default_streamlit/common/hello.py +0 -2
  165. snowflake/cli/templates/default_streamlit/environment.yml +0 -6
  166. snowflake/cli/templates/default_streamlit/pages/my_page.py +0 -3
  167. snowflake/cli/templates/default_streamlit/snowflake.yml +0 -10
  168. snowflake/cli/templates/default_streamlit/streamlit_app.py +0 -4
  169. snowflake_cli_labs-2.8.0rc1.dist-info/RECORD +0 -240
  170. snowflake_cli_labs-2.8.0rc1.dist-info/entry_points.txt +0 -2
  171. /snowflake/cli/{app → _app}/__init__.py +0 -0
  172. /snowflake/cli/{app → _app}/api_impl/__init__.py +0 -0
  173. /snowflake/cli/{app → _app}/api_impl/plugin/__init__.py +0 -0
  174. /snowflake/cli/{app → _app}/api_impl/plugin/plugin_config_provider_impl.py +0 -0
  175. /snowflake/cli/{app → _app}/commands_registration/__init__.py +0 -0
  176. /snowflake/cli/{app → _app}/commands_registration/threadsafe.py +0 -0
  177. /snowflake/cli/{app → _app}/constants.py +0 -0
  178. /snowflake/cli/{app → _app}/dev/__init__.py +0 -0
  179. /snowflake/cli/{app → _app}/dev/commands_structure.py +0 -0
  180. /snowflake/cli/{app → _app}/dev/docs/__init__.py +0 -0
  181. /snowflake/cli/{app → _app}/dev/docs/project_definition_generate_json_schema.py +0 -0
  182. /snowflake/cli/{app → _app}/dev/docs/template_utils.py +0 -0
  183. /snowflake/cli/{app → _app}/dev/docs/templates/definition_description.rst.jinja2 +0 -0
  184. /snowflake/cli/{app → _app}/dev/docs/templates/overview.rst.jinja2 +0 -0
  185. /snowflake/cli/{app → _app}/dev/pycharm_remote_debug.py +0 -0
  186. /snowflake/cli/{app → _app}/loggers.py +0 -0
  187. /snowflake/cli/{plugins → _plugins}/__init__.py +0 -0
  188. /snowflake/cli/{plugins → _plugins}/connection/__init__.py +0 -0
  189. /snowflake/cli/{plugins → _plugins}/cortex/__init__.py +0 -0
  190. /snowflake/cli/{plugins → _plugins}/cortex/types.py +0 -0
  191. /snowflake/cli/{plugins → _plugins}/git/__init__.py +0 -0
  192. /snowflake/cli/{plugins → _plugins}/init/__init__.py +0 -0
  193. /snowflake/cli/{plugins → _plugins}/nativeapp/__init__.py +0 -0
  194. /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/__init__.py +0 -0
  195. /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/sandbox.py +0 -0
  196. /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/callback_source.py.jinja +0 -0
  197. /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/models.py +0 -0
  198. /snowflake/cli/{plugins → _plugins}/nativeapp/constants.py +0 -0
  199. /snowflake/cli/{plugins → _plugins}/nativeapp/feature_flags.py +0 -0
  200. /snowflake/cli/{plugins → _plugins}/nativeapp/policy.py +0 -0
  201. /snowflake/cli/{plugins → _plugins}/nativeapp/utils.py +0 -0
  202. /snowflake/cli/{plugins → _plugins}/nativeapp/version/__init__.py +0 -0
  203. /snowflake/cli/{plugins → _plugins}/notebook/__init__.py +0 -0
  204. /snowflake/cli/{plugins → _plugins}/notebook/exceptions.py +0 -0
  205. /snowflake/cli/{plugins → _plugins}/object/__init__.py +0 -0
  206. /snowflake/cli/{plugins → _plugins}/object/common.py +0 -0
  207. /snowflake/cli/{plugins → _plugins}/snowpark/__init__.py +0 -0
  208. /snowflake/cli/{plugins → _plugins}/snowpark/package/__init__.py +0 -0
  209. /snowflake/cli/{plugins → _plugins}/snowpark/package/utils.py +0 -0
  210. /snowflake/cli/{plugins → _plugins}/spcs/common.py +0 -0
  211. /snowflake/cli/{plugins → _plugins}/spcs/compute_pool/__init__.py +0 -0
  212. /snowflake/cli/{plugins → _plugins}/spcs/image_registry/__init__.py +0 -0
  213. /snowflake/cli/{plugins → _plugins}/spcs/image_registry/manager.py +0 -0
  214. /snowflake/cli/{plugins → _plugins}/spcs/image_repository/__init__.py +0 -0
  215. /snowflake/cli/{plugins/spcs/jobs → _plugins/spcs/services}/__init__.py +0 -0
  216. /snowflake/cli/{plugins/spcs/services → _plugins/sql}/__init__.py +0 -0
  217. /snowflake/cli/{plugins → _plugins}/sql/snowsql_templating.py +0 -0
  218. /snowflake/cli/{plugins/sql → _plugins/stage}/__init__.py +0 -0
  219. /snowflake/cli/{plugins → _plugins}/stage/md5.py +0 -0
  220. /snowflake/cli/{plugins/stage → _plugins/streamlit}/__init__.py +0 -0
  221. /snowflake/cli/{plugins/streamlit → _plugins/workspace}/__init__.py +0 -0
  222. /snowflake/cli/{plugins/workspace → api/project/schemas/entities}/__init__.py +0 -0
  223. {snowflake_cli_labs-2.8.0rc1.dist-info → snowflake_cli_labs-3.0.0rc1.dist-info}/WHEEL +0 -0
  224. {snowflake_cli_labs-2.8.0rc1.dist-info → snowflake_cli_labs-3.0.0rc1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,572 @@
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 time
18
+ from abc import ABC, abstractmethod
19
+ from contextlib import contextmanager
20
+ from datetime import datetime
21
+ from functools import cached_property
22
+ from pathlib import Path
23
+ from textwrap import dedent
24
+ from typing import Generator, List, Optional, TypedDict
25
+
26
+ from click import ClickException
27
+ from snowflake.cli._plugins.connection.util import make_snowsight_url
28
+ from snowflake.cli._plugins.nativeapp.artifacts import (
29
+ BundleMap,
30
+ build_bundle,
31
+ )
32
+ from snowflake.cli._plugins.nativeapp.codegen.compiler import (
33
+ NativeAppCompiler,
34
+ )
35
+ from snowflake.cli._plugins.nativeapp.constants import (
36
+ NAME_COL,
37
+ )
38
+ from snowflake.cli._plugins.nativeapp.exceptions import (
39
+ NoEventTableForAccount,
40
+ )
41
+ from snowflake.cli._plugins.nativeapp.project_model import (
42
+ NativeAppProjectModel,
43
+ )
44
+ from snowflake.cli._plugins.stage.diff import (
45
+ DiffResult,
46
+ )
47
+ from snowflake.cli.api.console import cli_console as cc
48
+ from snowflake.cli.api.entities.application_package_entity import (
49
+ ApplicationPackageEntity,
50
+ )
51
+ from snowflake.cli.api.entities.utils import (
52
+ execute_post_deploy_hooks,
53
+ generic_sql_error_handler,
54
+ sync_deploy_root_with_stage,
55
+ )
56
+ from snowflake.cli.api.project.schemas.entities.common import PostDeployHook
57
+ from snowflake.cli.api.project.schemas.native_app.native_app import NativeApp
58
+ from snowflake.cli.api.project.schemas.native_app.path_mapping import PathMapping
59
+ from snowflake.cli.api.project.util import (
60
+ identifier_for_url,
61
+ unquote_identifier,
62
+ )
63
+ from snowflake.cli.api.sql_execution import SqlExecutionMixin
64
+ from snowflake.connector import DictCursor, ProgrammingError
65
+
66
+ ApplicationOwnedObject = TypedDict("ApplicationOwnedObject", {"name": str, "type": str})
67
+
68
+
69
+ class NativeAppCommandProcessor(ABC):
70
+ @abstractmethod
71
+ def process(self, *args, **kwargs):
72
+ pass
73
+
74
+
75
+ class NativeAppManager(SqlExecutionMixin):
76
+ """
77
+ Base class with frequently used functionality already implemented and ready to be used by related subclasses.
78
+ """
79
+
80
+ def __init__(self, project_definition: NativeApp, project_root: Path):
81
+ super().__init__()
82
+ self._na_project = NativeAppProjectModel(
83
+ project_definition=project_definition,
84
+ project_root=project_root,
85
+ )
86
+
87
+ @property
88
+ def na_project(self) -> NativeAppProjectModel:
89
+ return self._na_project
90
+
91
+ @property
92
+ def project_root(self) -> Path:
93
+ return self.na_project.project_root
94
+
95
+ @property
96
+ def definition(self) -> NativeApp:
97
+ return self.na_project.definition
98
+
99
+ @property
100
+ def artifacts(self) -> List[PathMapping]:
101
+ return self.na_project.artifacts
102
+
103
+ @property
104
+ def bundle_root(self) -> Path:
105
+ return self.na_project.bundle_root
106
+
107
+ @property
108
+ def deploy_root(self) -> Path:
109
+ return self.na_project.deploy_root
110
+
111
+ @property
112
+ def generated_root(self) -> Path:
113
+ return self.na_project.generated_root
114
+
115
+ @property
116
+ def package_scripts(self) -> List[str]:
117
+ return self.na_project.package_scripts
118
+
119
+ @property
120
+ def stage_fqn(self) -> str:
121
+ return self.na_project.stage_fqn
122
+
123
+ @property
124
+ def scratch_stage_fqn(self) -> str:
125
+ return self.na_project.scratch_stage_fqn
126
+
127
+ @property
128
+ def stage_schema(self) -> Optional[str]:
129
+ return self.na_project.stage_schema
130
+
131
+ @property
132
+ def package_warehouse(self) -> Optional[str]:
133
+ return self.na_project.package_warehouse
134
+
135
+ def use_package_warehouse(self):
136
+ return ApplicationPackageEntity.use_package_warehouse(
137
+ self.package_warehouse,
138
+ )
139
+
140
+ @property
141
+ def application_warehouse(self) -> Optional[str]:
142
+ return self.na_project.application_warehouse
143
+
144
+ @contextmanager
145
+ def use_application_warehouse(self):
146
+ if self.application_warehouse:
147
+ with self.use_warehouse(self.application_warehouse):
148
+ yield
149
+ else:
150
+ raise ClickException(
151
+ dedent(
152
+ f"""\
153
+ Application warehouse cannot be empty.
154
+ Please provide a value for it in your connection information or your project definition file.
155
+ """
156
+ )
157
+ )
158
+
159
+ @property
160
+ def project_identifier(self) -> str:
161
+ return self.na_project.project_identifier
162
+
163
+ @property
164
+ def package_name(self) -> str:
165
+ return self.na_project.package_name
166
+
167
+ @property
168
+ def package_role(self) -> str:
169
+ return self.na_project.package_role
170
+
171
+ @property
172
+ def package_distribution(self) -> str:
173
+ return self.na_project.package_distribution
174
+
175
+ @property
176
+ def app_name(self) -> str:
177
+ return self.na_project.app_name
178
+
179
+ @property
180
+ def app_role(self) -> str:
181
+ return self.na_project.app_role
182
+
183
+ @property
184
+ def app_post_deploy_hooks(self) -> Optional[List[PostDeployHook]]:
185
+ return self.na_project.app_post_deploy_hooks
186
+
187
+ @property
188
+ def package_post_deploy_hooks(self) -> Optional[List[PostDeployHook]]:
189
+ return self.na_project.package_post_deploy_hooks
190
+
191
+ @property
192
+ def debug_mode(self) -> bool:
193
+ return self.na_project.debug_mode
194
+
195
+ @cached_property
196
+ def get_app_pkg_distribution_in_snowflake(self) -> str:
197
+ return ApplicationPackageEntity.get_app_pkg_distribution_in_snowflake(
198
+ self.package_name, self.package_role
199
+ )
200
+
201
+ @cached_property
202
+ def account_event_table(self) -> str:
203
+ query = "show parameters like 'event_table' in account"
204
+ results = self._execute_query(query, cursor_class=DictCursor)
205
+ return next((r["value"] for r in results if r["key"] == "EVENT_TABLE"), "")
206
+
207
+ def verify_project_distribution(
208
+ self, expected_distribution: Optional[str] = None
209
+ ) -> bool:
210
+ return ApplicationPackageEntity.verify_project_distribution(
211
+ console=cc,
212
+ package_name=self.package_name,
213
+ package_role=self.package_role,
214
+ package_distribution=self.package_distribution,
215
+ expected_distribution=expected_distribution,
216
+ )
217
+
218
+ def build_bundle(self) -> BundleMap:
219
+ """
220
+ Populates the local deploy root from artifact sources.
221
+ """
222
+ bundle_map = build_bundle(self.project_root, self.deploy_root, self.artifacts)
223
+ compiler = NativeAppCompiler(self.na_project.get_bundle_context())
224
+ compiler.compile_artifacts()
225
+ return bundle_map
226
+
227
+ def sync_deploy_root_with_stage(
228
+ self,
229
+ bundle_map: BundleMap,
230
+ role: str,
231
+ prune: bool,
232
+ recursive: bool,
233
+ stage_fqn: str,
234
+ local_paths_to_sync: List[Path] | None = None,
235
+ print_diff: bool = True,
236
+ ) -> DiffResult:
237
+ return sync_deploy_root_with_stage(
238
+ console=cc,
239
+ deploy_root=self.deploy_root,
240
+ package_name=self.package_name,
241
+ stage_schema=self.stage_schema,
242
+ bundle_map=bundle_map,
243
+ role=role,
244
+ prune=prune,
245
+ recursive=recursive,
246
+ stage_fqn=stage_fqn,
247
+ local_paths_to_sync=local_paths_to_sync,
248
+ print_diff=print_diff,
249
+ )
250
+
251
+ def get_existing_app_info(self) -> Optional[dict]:
252
+ """
253
+ Check for an existing application object by the same name as in project definition, in account.
254
+ It executes a 'show applications like' query and returns the result as single row, if one exists.
255
+ """
256
+ with self.use_role(self.app_role):
257
+ return self.show_specific_object(
258
+ "applications", self.app_name, name_col=NAME_COL
259
+ )
260
+
261
+ def get_existing_app_pkg_info(self) -> Optional[dict]:
262
+ return ApplicationPackageEntity.get_existing_app_pkg_info(
263
+ package_name=self.package_name,
264
+ package_role=self.package_role,
265
+ )
266
+
267
+ def get_objects_owned_by_application(self) -> List[ApplicationOwnedObject]:
268
+ """
269
+ Returns all application objects owned by this application.
270
+ """
271
+ with self.use_role(self.app_role):
272
+ results = self._execute_query(
273
+ f"show objects owned by application {self.app_name}"
274
+ ).fetchall()
275
+ return [{"name": row[1], "type": row[2]} for row in results]
276
+
277
+ def _application_objects_to_str(
278
+ self, application_objects: list[ApplicationOwnedObject]
279
+ ) -> str:
280
+ """
281
+ Returns a list in an "(Object Type) Object Name" format. Database-level and schema-level object names are fully qualified:
282
+ (COMPUTE_POOL) POOL_NAME
283
+ (DATABASE) DB_NAME
284
+ (SCHEMA) DB_NAME.PUBLIC
285
+ ...
286
+ """
287
+ return "\n".join(
288
+ [self._application_object_to_str(obj) for obj in application_objects]
289
+ )
290
+
291
+ def _application_object_to_str(self, obj: ApplicationOwnedObject) -> str:
292
+ return f"({obj['type']}) {obj['name']}"
293
+
294
+ def get_snowsight_url(self) -> str:
295
+ """Returns the URL that can be used to visit this app via Snowsight."""
296
+ name = identifier_for_url(self.app_name)
297
+ with self.use_application_warehouse():
298
+ return make_snowsight_url(self._conn, f"/#/apps/application/{name}")
299
+
300
+ def create_app_package(self) -> None:
301
+ return ApplicationPackageEntity.create_app_package(
302
+ console=cc,
303
+ package_name=self.package_name,
304
+ package_role=self.package_role,
305
+ package_distribution=self.package_distribution,
306
+ )
307
+
308
+ def _apply_package_scripts(self) -> None:
309
+ return ApplicationPackageEntity.apply_package_scripts(
310
+ console=cc,
311
+ package_scripts=self.package_scripts,
312
+ package_warehouse=self.package_warehouse,
313
+ project_root=self.project_root,
314
+ package_role=self.package_role,
315
+ package_name=self.package_name,
316
+ )
317
+
318
+ def execute_package_post_deploy_hooks(self) -> None:
319
+ execute_post_deploy_hooks(
320
+ console=cc,
321
+ project_root=self.project_root,
322
+ post_deploy_hooks=self.package_post_deploy_hooks,
323
+ deployed_object_type="application package",
324
+ database_name=self.package_name,
325
+ )
326
+
327
+ def execute_app_post_deploy_hooks(self) -> None:
328
+ execute_post_deploy_hooks(
329
+ console=cc,
330
+ project_root=self.project_root,
331
+ post_deploy_hooks=self.app_post_deploy_hooks,
332
+ deployed_object_type="application",
333
+ database_name=self.app_name,
334
+ )
335
+
336
+ def deploy(
337
+ self,
338
+ bundle_map: BundleMap,
339
+ prune: bool,
340
+ recursive: bool,
341
+ stage_fqn: Optional[str] = None,
342
+ local_paths_to_sync: List[Path] | None = None,
343
+ validate: bool = True,
344
+ print_diff: bool = True,
345
+ ) -> DiffResult:
346
+ """app deploy process"""
347
+
348
+ # 1. Create an empty application package, if none exists
349
+ self.create_app_package()
350
+
351
+ with self.use_role(self.package_role):
352
+ # 2. now that the application package exists, create shared data
353
+ self._apply_package_scripts()
354
+
355
+ # 3. Upload files from deploy root local folder to the above stage
356
+ stage_fqn = stage_fqn or self.stage_fqn
357
+ diff = self.sync_deploy_root_with_stage(
358
+ bundle_map=bundle_map,
359
+ role=self.package_role,
360
+ prune=prune,
361
+ recursive=recursive,
362
+ stage_fqn=stage_fqn,
363
+ local_paths_to_sync=local_paths_to_sync,
364
+ print_diff=print_diff,
365
+ )
366
+
367
+ # 4. Execute post-deploy hooks
368
+ with self.use_package_warehouse():
369
+ self.execute_package_post_deploy_hooks()
370
+
371
+ if validate:
372
+ self.validate(use_scratch_stage=False)
373
+
374
+ return diff
375
+
376
+ def deploy_to_scratch_stage_fn(self):
377
+ bundle_map = self.build_bundle()
378
+ self.deploy(
379
+ bundle_map=bundle_map,
380
+ prune=True,
381
+ recursive=True,
382
+ stage_fqn=self.scratch_stage_fqn,
383
+ validate=False,
384
+ print_diff=False,
385
+ )
386
+
387
+ def validate(self, use_scratch_stage: bool = False):
388
+ return ApplicationPackageEntity.validate_setup_script(
389
+ console=cc,
390
+ package_name=self.package_name,
391
+ package_role=self.package_role,
392
+ stage_fqn=self.stage_fqn,
393
+ use_scratch_stage=use_scratch_stage,
394
+ scratch_stage_fqn=self.scratch_stage_fqn,
395
+ deploy_to_scratch_stage_fn=self.deploy_to_scratch_stage_fn,
396
+ )
397
+
398
+ def get_validation_result(self, use_scratch_stage: bool):
399
+ return ApplicationPackageEntity.get_validation_result(
400
+ console=cc,
401
+ package_name=self.package_name,
402
+ package_role=self.package_role,
403
+ stage_fqn=self.stage_fqn,
404
+ use_scratch_stage=use_scratch_stage,
405
+ scratch_stage_fqn=self.scratch_stage_fqn,
406
+ deploy_to_scratch_stage_fn=self.deploy_to_scratch_stage_fn,
407
+ )
408
+
409
+ def get_events( # type: ignore [return]
410
+ self,
411
+ since: str | datetime | None = None,
412
+ until: str | datetime | None = None,
413
+ record_types: list[str] | None = None,
414
+ scopes: list[str] | None = None,
415
+ consumer_org: str = "",
416
+ consumer_account: str = "",
417
+ consumer_app_hash: str = "",
418
+ first: int = -1,
419
+ last: int = -1,
420
+ ) -> list[dict]:
421
+ record_types = record_types or []
422
+ scopes = scopes or []
423
+
424
+ if first >= 0 and last >= 0:
425
+ raise ValueError("first and last cannot be used together")
426
+
427
+ if not self.account_event_table:
428
+ raise NoEventTableForAccount()
429
+
430
+ # resource_attributes uses the unquoted/uppercase app and package name
431
+ app_name = unquote_identifier(self.app_name)
432
+ package_name = unquote_identifier(self.package_name)
433
+ org_name = unquote_identifier(consumer_org)
434
+ account_name = unquote_identifier(consumer_account)
435
+
436
+ # Filter on record attributes
437
+ if consumer_org and consumer_account:
438
+ # Look for events shared from a consumer account
439
+ app_clause = (
440
+ f"resource_attributes:\"snow.application.package.name\" = '{package_name}' "
441
+ f"and resource_attributes:\"snow.application.consumer.organization\" = '{org_name}' "
442
+ f"and resource_attributes:\"snow.application.consumer.name\" = '{account_name}'"
443
+ )
444
+ if consumer_app_hash:
445
+ # If the user has specified a hash of a specific app installation
446
+ # in the consumer account, filter events to that installation only
447
+ app_clause += f" and resource_attributes:\"snow.database.hash\" = '{consumer_app_hash.lower()}'"
448
+ else:
449
+ # Otherwise look for events from an app installed in the same account as the package
450
+ app_clause = f"resource_attributes:\"snow.database.name\" = '{app_name}'"
451
+
452
+ # Filter on event time
453
+ if isinstance(since, datetime):
454
+ since_clause = f"and timestamp >= '{since}'"
455
+ elif isinstance(since, str) and since:
456
+ since_clause = f"and timestamp >= sysdate() - interval '{since}'"
457
+ else:
458
+ since_clause = ""
459
+ if isinstance(until, datetime):
460
+ until_clause = f"and timestamp <= '{until}'"
461
+ elif isinstance(until, str) and until:
462
+ until_clause = f"and timestamp <= sysdate() - interval '{until}'"
463
+ else:
464
+ until_clause = ""
465
+
466
+ # Filter on event type (log, span, span_event)
467
+ type_in_values = ",".join(f"'{v}'" for v in record_types)
468
+ types_clause = (
469
+ f"and record_type in ({type_in_values})" if type_in_values else ""
470
+ )
471
+
472
+ # Filter on event scope (e.g. the logger name)
473
+ scope_in_values = ",".join(f"'{v}'" for v in scopes)
474
+ scopes_clause = (
475
+ f"and scope:name in ({scope_in_values})" if scope_in_values else ""
476
+ )
477
+
478
+ # Limit event count
479
+ first_clause = f"limit {first}" if first >= 0 else ""
480
+ last_clause = f"limit {last}" if last >= 0 else ""
481
+
482
+ query = dedent(
483
+ f"""\
484
+ select * from (
485
+ select timestamp, value::varchar value
486
+ from {self.account_event_table}
487
+ where ({app_clause})
488
+ {since_clause}
489
+ {until_clause}
490
+ {types_clause}
491
+ {scopes_clause}
492
+ order by timestamp desc
493
+ {last_clause}
494
+ ) order by timestamp asc
495
+ {first_clause}
496
+ """
497
+ )
498
+ try:
499
+ return self._execute_query(query, cursor_class=DictCursor).fetchall()
500
+ except ProgrammingError as err:
501
+ generic_sql_error_handler(err)
502
+
503
+ def stream_events(
504
+ self,
505
+ interval_seconds: int,
506
+ since: str | datetime | None = None,
507
+ record_types: list[str] | None = None,
508
+ scopes: list[str] | None = None,
509
+ consumer_org: str = "",
510
+ consumer_account: str = "",
511
+ consumer_app_hash: str = "",
512
+ last: int = -1,
513
+ ) -> Generator[dict, None, None]:
514
+ try:
515
+ events = self.get_events(
516
+ since=since,
517
+ record_types=record_types,
518
+ scopes=scopes,
519
+ consumer_org=consumer_org,
520
+ consumer_account=consumer_account,
521
+ consumer_app_hash=consumer_app_hash,
522
+ last=last,
523
+ )
524
+ yield from events # Yield the initial batch of events
525
+ last_event_time = events[-1]["TIMESTAMP"] if events else None
526
+
527
+ while True: # Then infinite poll for new events
528
+ time.sleep(interval_seconds)
529
+ previous_events = events
530
+ events = self.get_events(
531
+ since=last_event_time,
532
+ record_types=record_types,
533
+ scopes=scopes,
534
+ consumer_org=consumer_org,
535
+ consumer_account=consumer_account,
536
+ consumer_app_hash=consumer_app_hash,
537
+ )
538
+ if not events:
539
+ continue
540
+
541
+ yield from _new_events_only(previous_events, events)
542
+ last_event_time = events[-1]["TIMESTAMP"]
543
+ except KeyboardInterrupt:
544
+ return
545
+
546
+
547
+ def _new_events_only(previous_events: list[dict], new_events: list[dict]) -> list[dict]:
548
+ # The timestamp that overlaps between both sets of events
549
+ overlap_time = new_events[0]["TIMESTAMP"]
550
+
551
+ # Remove all the events from the new result set
552
+ # if they were already printed. We iterate and remove
553
+ # instead of filtering in order to handle duplicates
554
+ # (i.e. if an event is present 3 times in new_events
555
+ # but only once in previous_events, it should still
556
+ # appear twice in new_events at the end
557
+ new_events = new_events.copy()
558
+ for event in reversed(previous_events):
559
+ if event["TIMESTAMP"] < overlap_time:
560
+ break
561
+ # No need to handle ValueError here since we know
562
+ # that events that pass the above if check will
563
+ # either be in both lists or in new_events only
564
+ new_events.remove(event)
565
+ return new_events
566
+
567
+
568
+ def _validation_item_to_str(item: dict[str, str | int]):
569
+ s = item["message"]
570
+ if item["errorCode"]:
571
+ s = f"{s} (error code {item['errorCode']})"
572
+ return s
@@ -12,13 +12,13 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ from snowflake.cli._plugins.nativeapp import commands
15
16
  from snowflake.cli.api.plugins.command import (
16
17
  SNOWCLI_ROOT_COMMAND_PATH,
17
18
  CommandSpec,
18
19
  CommandType,
19
20
  plugin_hook_impl,
20
21
  )
21
- from snowflake.cli.plugins.connection import commands
22
22
 
23
23
 
24
24
  @plugin_hook_impl
@@ -18,27 +18,23 @@ from functools import cached_property
18
18
  from pathlib import Path
19
19
  from typing import List, Optional
20
20
 
21
- from snowflake.cli.api.cli_global_context import cli_context
21
+ from snowflake.cli._plugins.nativeapp.artifacts import resolve_without_follow
22
+ from snowflake.cli._plugins.nativeapp.bundle_context import BundleContext
23
+ from snowflake.cli.api.cli_global_context import get_cli_context
24
+ from snowflake.cli.api.entities.common import get_sql_executor
22
25
  from snowflake.cli.api.project.definition import (
23
26
  default_app_package,
24
27
  default_application,
25
28
  default_role,
26
29
  )
27
- from snowflake.cli.api.project.schemas.native_app.application import (
28
- PostDeployHook,
29
- )
30
+ from snowflake.cli.api.project.schemas.entities.common import PostDeployHook
30
31
  from snowflake.cli.api.project.schemas.native_app.native_app import NativeApp
31
32
  from snowflake.cli.api.project.schemas.native_app.path_mapping import PathMapping
32
- from snowflake.cli.api.project.util import extract_schema, to_identifier
33
- from snowflake.cli.plugins.nativeapp.artifacts import resolve_without_follow
34
- from snowflake.connector import DictCursor
35
-
36
-
37
- def current_role() -> str:
38
- conn = cli_context.connection
39
- *_, cursor = conn.execute_string("select current_role()", cursor_class=DictCursor)
40
- role_result = cursor.fetchone()
41
- return role_result["CURRENT_ROLE()"]
33
+ from snowflake.cli.api.project.util import (
34
+ append_test_resource_suffix,
35
+ extract_schema,
36
+ to_identifier,
37
+ )
42
38
 
43
39
 
44
40
  class NativeAppProjectModel:
@@ -107,6 +103,7 @@ class NativeAppProjectModel:
107
103
  if self.definition.package and self.definition.package.warehouse:
108
104
  return to_identifier(self.definition.package.warehouse)
109
105
  else:
106
+ cli_context = get_cli_context()
110
107
  if cli_context.connection.warehouse:
111
108
  return to_identifier(cli_context.connection.warehouse)
112
109
  return None
@@ -116,6 +113,7 @@ class NativeAppProjectModel:
116
113
  if self.definition.application and self.definition.application.warehouse:
117
114
  return to_identifier(self.definition.application.warehouse)
118
115
  else:
116
+ cli_context = get_cli_context()
119
117
  if cli_context.connection.warehouse:
120
118
  return to_identifier(cli_context.connection.warehouse)
121
119
  return None
@@ -126,12 +124,16 @@ class NativeAppProjectModel:
126
124
  # sometimes strip out double quotes, so we try to get them back here.
127
125
  return to_identifier(self.definition.name)
128
126
 
129
- @cached_property
127
+ @property
130
128
  def package_name(self) -> str:
131
129
  if self.definition.package and self.definition.package.name:
132
130
  return to_identifier(self.definition.package.name)
133
131
  else:
134
- return to_identifier(default_app_package(self.project_identifier))
132
+ # V1.0 PDF doesn't support templating, so if the identifier isn't
133
+ # explicitly specified, the default is generated here,
134
+ # so we have to append the test resource suffix here
135
+ name = default_app_package(self.project_identifier)
136
+ return to_identifier(append_test_resource_suffix(name))
135
137
 
136
138
  @cached_property
137
139
  def package_role(self) -> str:
@@ -147,12 +149,16 @@ class NativeAppProjectModel:
147
149
  else:
148
150
  return "internal"
149
151
 
150
- @cached_property
152
+ @property
151
153
  def app_name(self) -> str:
152
154
  if self.definition.application and self.definition.application.name:
153
155
  return to_identifier(self.definition.application.name)
154
156
  else:
155
- return to_identifier(default_application(self.project_identifier))
157
+ # V1.0 PDF doesn't support templating, so if the identifier isn't
158
+ # explicitly specified, the default is generated here,
159
+ # so we have to append the test resource suffix here
160
+ name = default_application(self.project_identifier)
161
+ return to_identifier(append_test_resource_suffix(name))
156
162
 
157
163
  @cached_property
158
164
  def app_role(self) -> str:
@@ -185,7 +191,7 @@ class NativeAppProjectModel:
185
191
  def _default_role(self) -> str:
186
192
  role = default_role()
187
193
  if role is None:
188
- role = current_role()
194
+ role = get_sql_executor().current_role()
189
195
  return role
190
196
 
191
197
  @cached_property
@@ -193,3 +199,13 @@ class NativeAppProjectModel:
193
199
  if self.definition.application:
194
200
  return self.definition.application.debug
195
201
  return None
202
+
203
+ def get_bundle_context(self) -> BundleContext:
204
+ return BundleContext(
205
+ package_name=self.package_name,
206
+ artifacts=self.artifacts,
207
+ project_root=self.project_root,
208
+ bundle_root=self.bundle_root,
209
+ deploy_root=self.deploy_root,
210
+ generated_root=self.generated_root,
211
+ )