snowflake-cli-labs 2.4.0rc1__py3-none-any.whl → 2.5.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 (204) hide show
  1. snowflake/cli/__about__.py +15 -1
  2. snowflake/cli/__init__.py +13 -0
  3. snowflake/cli/api/__init__.py +14 -0
  4. snowflake/cli/api/cli_global_context.py +26 -0
  5. snowflake/cli/api/commands/__init__.py +13 -0
  6. snowflake/cli/api/commands/alias.py +14 -0
  7. snowflake/cli/api/commands/decorators.py +14 -0
  8. snowflake/cli/api/commands/experimental_behaviour.py +14 -0
  9. snowflake/cli/api/commands/flags.py +21 -1
  10. snowflake/cli/api/commands/project_initialisation.py +19 -2
  11. snowflake/cli/api/commands/snow_typer.py +90 -1
  12. snowflake/cli/api/config.py +14 -0
  13. snowflake/cli/api/console/__init__.py +14 -0
  14. snowflake/cli/api/console/abc.py +14 -0
  15. snowflake/cli/api/console/console.py +14 -0
  16. snowflake/cli/api/console/enum.py +14 -0
  17. snowflake/cli/api/constants.py +14 -0
  18. snowflake/cli/api/exceptions.py +22 -0
  19. snowflake/cli/api/feature_flags.py +14 -1
  20. snowflake/cli/api/identifiers.py +16 -2
  21. snowflake/cli/api/output/__init__.py +13 -0
  22. snowflake/cli/api/output/formats.py +14 -0
  23. snowflake/cli/api/output/types.py +14 -0
  24. snowflake/cli/api/plugins/__init__.py +13 -0
  25. snowflake/cli/api/plugins/command/__init__.py +14 -0
  26. snowflake/cli/api/plugins/command/plugin_hook_specs.py +14 -0
  27. snowflake/cli/api/plugins/plugin_config.py +14 -0
  28. snowflake/cli/api/project/__init__.py +13 -0
  29. snowflake/cli/api/project/definition.py +54 -8
  30. snowflake/cli/api/project/definition_manager.py +28 -2
  31. snowflake/cli/api/project/errors.py +14 -0
  32. snowflake/cli/api/project/schemas/__init__.py +13 -0
  33. snowflake/cli/api/project/schemas/identifier_model.py +14 -0
  34. snowflake/cli/api/project/schemas/native_app/__init__.py +13 -0
  35. snowflake/cli/api/project/schemas/native_app/application.py +14 -0
  36. snowflake/cli/api/project/schemas/native_app/native_app.py +35 -0
  37. snowflake/cli/api/project/schemas/native_app/package.py +14 -0
  38. snowflake/cli/api/project/schemas/native_app/path_mapping.py +15 -1
  39. snowflake/cli/api/project/schemas/project_definition.py +14 -0
  40. snowflake/cli/api/project/schemas/snowpark/__init__.py +13 -0
  41. snowflake/cli/api/project/schemas/snowpark/argument.py +14 -0
  42. snowflake/cli/api/project/schemas/snowpark/callable.py +14 -0
  43. snowflake/cli/api/project/schemas/snowpark/snowpark.py +14 -0
  44. snowflake/cli/api/project/schemas/streamlit/__init__.py +13 -0
  45. snowflake/cli/api/project/schemas/streamlit/streamlit.py +14 -0
  46. snowflake/cli/api/project/schemas/updatable_model.py +14 -0
  47. snowflake/cli/api/project/util.py +32 -3
  48. snowflake/cli/api/secure_path.py +59 -7
  49. snowflake/cli/api/secure_utils.py +14 -0
  50. snowflake/cli/api/sql_execution.py +14 -0
  51. snowflake/cli/api/utils/__init__.py +13 -0
  52. snowflake/cli/api/utils/cursor.py +14 -0
  53. snowflake/cli/api/utils/definition_rendering.py +271 -0
  54. snowflake/cli/api/utils/dict_utils.py +73 -0
  55. snowflake/cli/api/utils/error_handling.py +14 -0
  56. snowflake/cli/api/utils/graph.py +97 -0
  57. snowflake/cli/api/utils/models.py +32 -0
  58. snowflake/cli/api/utils/naming_utils.py +13 -0
  59. snowflake/cli/api/utils/path_utils.py +14 -0
  60. snowflake/cli/api/utils/rendering.py +21 -152
  61. snowflake/cli/api/utils/types.py +18 -1
  62. snowflake/cli/app/__init__.py +14 -0
  63. snowflake/cli/app/__main__.py +14 -0
  64. snowflake/cli/app/api_impl/__init__.py +13 -0
  65. snowflake/cli/app/api_impl/plugin/__init__.py +13 -0
  66. snowflake/cli/app/api_impl/plugin/plugin_config_provider_impl.py +14 -0
  67. snowflake/cli/app/cli_app.py +14 -0
  68. snowflake/cli/app/commands_registration/__init__.py +14 -0
  69. snowflake/cli/app/commands_registration/builtin_plugins.py +14 -0
  70. snowflake/cli/app/commands_registration/command_plugins_loader.py +14 -0
  71. snowflake/cli/app/commands_registration/commands_registration_with_callbacks.py +14 -0
  72. snowflake/cli/app/commands_registration/exception_logging.py +14 -0
  73. snowflake/cli/app/commands_registration/threadsafe.py +14 -0
  74. snowflake/cli/app/commands_registration/typer_registration.py +14 -0
  75. snowflake/cli/app/constants.py +14 -1
  76. snowflake/cli/app/dev/__init__.py +13 -0
  77. snowflake/cli/app/dev/commands_structure.py +14 -0
  78. snowflake/cli/app/dev/docs/__init__.py +13 -0
  79. snowflake/cli/app/dev/docs/generator.py +14 -0
  80. snowflake/cli/app/dev/pycharm_remote_debug.py +14 -0
  81. snowflake/cli/app/loggers.py +14 -0
  82. snowflake/cli/app/main_typer.py +14 -0
  83. snowflake/cli/app/printing.py +17 -0
  84. snowflake/cli/app/snow_connector.py +17 -4
  85. snowflake/cli/app/telemetry.py +14 -0
  86. snowflake/cli/plugins/__init__.py +13 -0
  87. snowflake/cli/plugins/connection/__init__.py +13 -0
  88. snowflake/cli/plugins/connection/commands.py +27 -2
  89. snowflake/cli/plugins/connection/plugin_spec.py +15 -1
  90. snowflake/cli/plugins/connection/util.py +21 -1
  91. snowflake/cli/plugins/cortex/__init__.py +13 -0
  92. snowflake/cli/plugins/cortex/commands.py +16 -2
  93. snowflake/cli/plugins/cortex/constants.py +14 -0
  94. snowflake/cli/plugins/cortex/manager.py +14 -0
  95. snowflake/cli/plugins/cortex/plugin_spec.py +15 -1
  96. snowflake/cli/plugins/cortex/types.py +14 -0
  97. snowflake/cli/plugins/git/__init__.py +13 -0
  98. snowflake/cli/plugins/git/commands.py +16 -2
  99. snowflake/cli/plugins/git/manager.py +14 -0
  100. snowflake/cli/plugins/git/plugin_spec.py +15 -1
  101. snowflake/cli/plugins/nativeapp/__init__.py +13 -0
  102. snowflake/cli/plugins/nativeapp/artifacts.py +202 -98
  103. snowflake/cli/plugins/nativeapp/codegen/__init__.py +13 -0
  104. snowflake/cli/plugins/nativeapp/codegen/artifact_processor.py +14 -0
  105. snowflake/cli/plugins/nativeapp/codegen/compiler.py +14 -0
  106. snowflake/cli/plugins/nativeapp/codegen/sandbox.py +14 -0
  107. snowflake/cli/plugins/nativeapp/codegen/snowpark/callback_source.py.jinja +5 -5
  108. snowflake/cli/plugins/nativeapp/codegen/snowpark/extension_function_utils.py +34 -13
  109. snowflake/cli/plugins/nativeapp/codegen/snowpark/models.py +14 -0
  110. snowflake/cli/plugins/nativeapp/codegen/snowpark/python_processor.py +52 -13
  111. snowflake/cli/plugins/nativeapp/commands.py +63 -10
  112. snowflake/cli/plugins/nativeapp/common_flags.py +20 -0
  113. snowflake/cli/plugins/nativeapp/constants.py +15 -0
  114. snowflake/cli/plugins/nativeapp/exceptions.py +23 -2
  115. snowflake/cli/plugins/nativeapp/init.py +14 -0
  116. snowflake/cli/plugins/nativeapp/manager.py +118 -25
  117. snowflake/cli/plugins/nativeapp/plugin_spec.py +15 -1
  118. snowflake/cli/plugins/nativeapp/policy.py +14 -0
  119. snowflake/cli/plugins/nativeapp/run_processor.py +22 -3
  120. snowflake/cli/plugins/nativeapp/teardown_processor.py +14 -0
  121. snowflake/cli/plugins/nativeapp/utils.py +14 -0
  122. snowflake/cli/plugins/nativeapp/version/__init__.py +13 -0
  123. snowflake/cli/plugins/nativeapp/version/commands.py +19 -4
  124. snowflake/cli/plugins/nativeapp/version/version_processor.py +26 -4
  125. snowflake/cli/plugins/notebook/__init__.py +13 -0
  126. snowflake/cli/plugins/notebook/commands.py +16 -4
  127. snowflake/cli/plugins/notebook/exceptions.py +14 -0
  128. snowflake/cli/plugins/notebook/manager.py +14 -0
  129. snowflake/cli/plugins/notebook/plugin_spec.py +15 -1
  130. snowflake/cli/plugins/notebook/types.py +14 -0
  131. snowflake/cli/plugins/object/__init__.py +13 -11
  132. snowflake/cli/plugins/object/command_aliases.py +20 -6
  133. snowflake/cli/plugins/object/commands.py +16 -2
  134. snowflake/cli/plugins/object/common.py +14 -0
  135. snowflake/cli/plugins/object/manager.py +14 -0
  136. snowflake/cli/plugins/object/plugin_spec.py +16 -2
  137. snowflake/cli/plugins/object_stage_deprecated/__init__.py +14 -0
  138. snowflake/cli/plugins/object_stage_deprecated/commands.py +16 -2
  139. snowflake/cli/plugins/object_stage_deprecated/plugin_spec.py +16 -4
  140. snowflake/cli/plugins/snowpark/__init__.py +13 -4
  141. snowflake/cli/plugins/snowpark/commands.py +18 -2
  142. snowflake/cli/plugins/snowpark/common.py +15 -0
  143. snowflake/cli/plugins/snowpark/manager.py +14 -0
  144. snowflake/cli/plugins/snowpark/models.py +15 -1
  145. snowflake/cli/plugins/snowpark/package/__init__.py +13 -0
  146. snowflake/cli/plugins/snowpark/package/anaconda_packages.py +14 -0
  147. snowflake/cli/plugins/snowpark/package/commands.py +16 -2
  148. snowflake/cli/plugins/snowpark/package/manager.py +14 -0
  149. snowflake/cli/plugins/snowpark/package/utils.py +14 -0
  150. snowflake/cli/plugins/snowpark/package_utils.py +15 -1
  151. snowflake/cli/plugins/snowpark/plugin_spec.py +16 -2
  152. snowflake/cli/plugins/snowpark/snowpark_package_paths.py +14 -0
  153. snowflake/cli/plugins/snowpark/snowpark_shared.py +14 -0
  154. snowflake/cli/plugins/snowpark/zipper.py +20 -3
  155. snowflake/cli/plugins/spcs/__init__.py +16 -2
  156. snowflake/cli/plugins/spcs/common.py +14 -0
  157. snowflake/cli/plugins/spcs/compute_pool/__init__.py +13 -0
  158. snowflake/cli/plugins/spcs/compute_pool/commands.py +16 -2
  159. snowflake/cli/plugins/spcs/compute_pool/manager.py +14 -0
  160. snowflake/cli/plugins/spcs/image_registry/__init__.py +13 -0
  161. snowflake/cli/plugins/spcs/image_registry/commands.py +16 -2
  162. snowflake/cli/plugins/spcs/image_registry/manager.py +16 -0
  163. snowflake/cli/plugins/spcs/image_repository/__init__.py +13 -0
  164. snowflake/cli/plugins/spcs/image_repository/commands.py +16 -2
  165. snowflake/cli/plugins/spcs/image_repository/manager.py +14 -0
  166. snowflake/cli/plugins/spcs/jobs/__init__.py +13 -0
  167. snowflake/cli/plugins/spcs/jobs/commands.py +17 -3
  168. snowflake/cli/plugins/spcs/jobs/manager.py +14 -0
  169. snowflake/cli/plugins/spcs/plugin_spec.py +15 -1
  170. snowflake/cli/plugins/spcs/services/__init__.py +13 -0
  171. snowflake/cli/plugins/spcs/services/commands.py +16 -2
  172. snowflake/cli/plugins/spcs/services/manager.py +14 -0
  173. snowflake/cli/plugins/sql/__init__.py +13 -0
  174. snowflake/cli/plugins/sql/commands.py +16 -2
  175. snowflake/cli/plugins/sql/manager.py +14 -0
  176. snowflake/cli/plugins/sql/plugin_spec.py +15 -1
  177. snowflake/cli/plugins/sql/snowsql_templating.py +14 -0
  178. snowflake/cli/plugins/stage/__init__.py +13 -0
  179. snowflake/cli/plugins/stage/commands.py +17 -3
  180. snowflake/cli/plugins/stage/diff.py +14 -0
  181. snowflake/cli/plugins/stage/manager.py +16 -5
  182. snowflake/cli/plugins/stage/plugin_spec.py +16 -2
  183. snowflake/cli/plugins/streamlit/__init__.py +13 -0
  184. snowflake/cli/plugins/streamlit/commands.py +16 -2
  185. snowflake/cli/plugins/streamlit/manager.py +14 -0
  186. snowflake/cli/plugins/streamlit/plugin_spec.py +15 -1
  187. snowflake/cli/templates/default_snowpark/app/__init__.py +13 -0
  188. snowflake/cli/templates/default_snowpark/app/common.py +15 -0
  189. snowflake/cli/templates/default_snowpark/app/functions.py +14 -0
  190. snowflake/cli/templates/default_snowpark/app/procedures.py +14 -0
  191. snowflake/cli/templates/default_snowpark/snowflake.yml +1 -1
  192. snowflake/cli/templates/default_streamlit/common/hello.py +15 -0
  193. snowflake/cli/templates/default_streamlit/pages/my_page.py +14 -0
  194. snowflake/cli/templates/default_streamlit/snowflake.yml +1 -1
  195. snowflake/cli/templates/default_streamlit/streamlit_app.py +14 -0
  196. {snowflake_cli_labs-2.4.0rc1.dist-info → snowflake_cli_labs-2.5.0.dist-info}/METADATA +23 -7
  197. snowflake_cli_labs-2.5.0.dist-info/RECORD +206 -0
  198. snowflake/cli/plugins/nativeapp/feature_flags.py +0 -10
  199. snowflake/cli/templates/environment.yml.jinja +0 -5
  200. snowflake/cli/templates/streamlit_app_launcher.py.jinja +0 -19
  201. snowflake_cli_labs-2.4.0rc1.dist-info/RECORD +0 -206
  202. {snowflake_cli_labs-2.4.0rc1.dist-info → snowflake_cli_labs-2.5.0.dist-info}/WHEEL +0 -0
  203. {snowflake_cli_labs-2.4.0rc1.dist-info → snowflake_cli_labs-2.5.0.dist-info}/entry_points.txt +0 -0
  204. {snowflake_cli_labs-2.4.0rc1.dist-info → snowflake_cli_labs-2.5.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,3 +1,17 @@
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
+
1
15
  from __future__ import annotations
2
16
 
3
17
  import json
@@ -43,7 +57,10 @@ from snowflake.cli.plugins.stage.diff import to_stage_path
43
57
  DEFAULT_TIMEOUT = 30
44
58
  TEMPLATE_PATH = Path(__file__).parent / "callback_source.py.jinja"
45
59
  SNOWPARK_LIB_NAME = "snowflake-snowpark-python"
46
- SNOWPARK_LIB_REGEX = rf"'{SNOWPARK_LIB_NAME}\s*((<|<=|!=|==|>=|>|~=|===)\s*[a-zA-Z0-9_.*+!-]+)?'" # support PEP 508, even though not all of it is supported in Snowflake yet
60
+ SNOWPARK_LIB_REGEX = re.compile(
61
+ # support PEP 508, even though not all of it is supported in Snowflake yet
62
+ rf"'{SNOWPARK_LIB_NAME}\s*((<|<=|!=|==|>=|>|~=|===)\s*[a-zA-Z0-9_.*+!-]+)?'"
63
+ )
47
64
  STAGE_PREFIX = "@"
48
65
 
49
66
 
@@ -194,6 +211,7 @@ class SnowparkAnnotationProcessor(ArtifactProcessor):
194
211
  py_file=py_file,
195
212
  )
196
213
  collected_sql_files.append(sql_file)
214
+ insert_newline = False
197
215
  for extension_fn in extension_fns:
198
216
  create_stmt = generate_create_sql_ddl_statement(extension_fn)
199
217
  if create_stmt is None:
@@ -206,6 +224,9 @@ class SnowparkAnnotationProcessor(ArtifactProcessor):
206
224
  collected_output.append(grant_statements)
207
225
 
208
226
  with open(sql_file, "a") as file:
227
+ if insert_newline:
228
+ file.write("\n")
229
+ insert_newline = True
209
230
  file.write(
210
231
  f"-- Generated by the Snowflake CLI from {relative_py_file}\n"
211
232
  )
@@ -266,14 +287,12 @@ class SnowparkAnnotationProcessor(ArtifactProcessor):
266
287
  # If user defined their udf as @udf(lambda: x, ...) then extension_fn.handler is <lambda>.
267
288
  extension_fn.handler = f"{py_file.stem}.{extension_fn.handler}"
268
289
 
269
- snowpark_lib_found = False
270
- snowpark_lib_regex = re.compile(SNOWPARK_LIB_REGEX)
271
- for pkg in extension_fn.packages:
272
- if snowpark_lib_regex.fullmatch(ensure_string_literal(pkg.strip())):
273
- snowpark_lib_found = True
274
- break
275
- if not snowpark_lib_found:
276
- extension_fn.packages.append(SNOWPARK_LIB_NAME)
290
+ extension_fn.packages = [
291
+ self._normalize_package_name(pkg) for pkg in extension_fn.packages
292
+ ]
293
+ snowpark_lib_name = ensure_string_literal(SNOWPARK_LIB_NAME)
294
+ if snowpark_lib_name not in extension_fn.packages:
295
+ extension_fn.packages.append(snowpark_lib_name)
277
296
 
278
297
  if extension_fn.imports is None:
279
298
  extension_fn.imports = []
@@ -283,6 +302,23 @@ class SnowparkAnnotationProcessor(ArtifactProcessor):
283
302
  deploy_root=deploy_root,
284
303
  )
285
304
 
305
+ def _normalize_package_name(self, pkg: str) -> str:
306
+ """
307
+ Returns a normalized version of the provided package name, as a Snowflake SQL string literal. Since the
308
+ Snowpark library can sometimes add a spurious version to its own package name, we strip this here too so
309
+ that the native application does not accidentally rely on stale packages once the snowpark library is updated
310
+ in the cloud.
311
+
312
+ Args:
313
+ pkg (str): The package name to normalize.
314
+ Returns:
315
+ A normalized version of the package name, as a Snowflake SQL string literal.
316
+ """
317
+ normalized_package_name = ensure_string_literal(pkg.strip())
318
+ if SNOWPARK_LIB_REGEX.fullmatch(normalized_package_name):
319
+ return ensure_string_literal(SNOWPARK_LIB_NAME)
320
+ return normalized_package_name
321
+
286
322
  def collect_extension_functions(
287
323
  self, bundle_map: BundleMap, processor_mapping: Optional[ProcessorMapping]
288
324
  ) -> Dict[Path, List[NativeAppExtensionFunction]]:
@@ -311,8 +347,6 @@ class SnowparkAnnotationProcessor(ArtifactProcessor):
311
347
  )
312
348
 
313
349
  if collected_extension_function_json is None:
314
- cc.warning(f"Error processing extension functions in {src_file}")
315
- cc.warning("Skipping generating code of all objects from this file.")
316
350
  continue
317
351
 
318
352
  collected_extension_functions = []
@@ -429,15 +463,20 @@ def generate_grant_sql_ddl_statements(
429
463
 
430
464
  if not extension_fn.application_roles:
431
465
  cc.warning(
432
- "Skipping generation of 'GRANT USAGE ON ...' SQL statement for this object due to lack of application roles."
466
+ f"Skipping generation of 'GRANT USAGE ON ...' SQL statement for {extension_fn.function_type.upper()} {extension_fn.handler} due to lack of application roles."
433
467
  )
434
468
  return None
435
469
 
436
470
  grant_sql_statements = []
471
+ object_type = (
472
+ "PROCEDURE"
473
+ if extension_fn.function_type == ExtensionFunctionTypeEnum.PROCEDURE
474
+ else "FUNCTION"
475
+ )
437
476
  for app_role in extension_fn.application_roles:
438
477
  grant_sql_statement = dedent(
439
478
  f"""\
440
- GRANT USAGE ON {get_sql_object_type(extension_fn)} {get_qualified_object_name(extension_fn)}({get_function_type_signature_for_grant(extension_fn)})
479
+ GRANT USAGE ON {object_type} {get_qualified_object_name(extension_fn)}({get_function_type_signature_for_grant(extension_fn)})
441
480
  TO APPLICATION ROLE {app_role};
442
481
  """
443
482
  ).strip()
@@ -1,3 +1,17 @@
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
+
1
15
  from __future__ import annotations
2
16
 
3
17
  import logging
@@ -9,14 +23,20 @@ from snowflake.cli.api.cli_global_context import cli_context
9
23
  from snowflake.cli.api.commands.decorators import (
10
24
  with_project_definition,
11
25
  )
12
- from snowflake.cli.api.commands.snow_typer import SnowTyper
26
+ from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
27
+ from snowflake.cli.api.output.formats import OutputFormat
13
28
  from snowflake.cli.api.output.types import (
14
29
  CollectionResult,
15
30
  CommandResult,
16
31
  MessageResult,
32
+ ObjectResult,
17
33
  )
18
34
  from snowflake.cli.api.secure_path import SecurePath
19
- from snowflake.cli.plugins.nativeapp.common_flags import ForceOption, InteractiveOption
35
+ from snowflake.cli.plugins.nativeapp.common_flags import (
36
+ ForceOption,
37
+ InteractiveOption,
38
+ ValidateOption,
39
+ )
20
40
  from snowflake.cli.plugins.nativeapp.init import (
21
41
  OFFICIAL_TEMPLATES_GITHUB_URL,
22
42
  nativeapp_init,
@@ -37,7 +57,7 @@ from snowflake.cli.plugins.nativeapp.utils import (
37
57
  )
38
58
  from snowflake.cli.plugins.nativeapp.version.commands import app as versions_app
39
59
 
40
- app = SnowTyper(
60
+ app = SnowTyperFactory(
41
61
  name="app",
42
62
  help="Manages a Snowflake Native App",
43
63
  )
@@ -68,7 +88,7 @@ def app_init(
68
88
  ),
69
89
  template: str = typer.Option(
70
90
  None,
71
- help="A specific template name within the template repo to use as template for the Native Apps project. Example: Default is basic if `--template-repo` is https://github.com/snowflakedb/native-apps-templates.git, and None if any other --template-repo is specified.",
91
+ help="A specific template name within the template repo to use as template for the Snowflake Native App project. Example: Default is basic if `--template-repo` is https://github.com/snowflakedb/native-apps-templates.git, and None if any other --template-repo is specified.",
72
92
  ),
73
93
  **options,
74
94
  ) -> CommandResult:
@@ -89,6 +109,8 @@ def app_list_templates(**options) -> CommandResult:
89
109
  Prints information regarding the official templates that can be used with snow app init.
90
110
  """
91
111
  with SecurePath.temporary_directory() as temp_path:
112
+ from git import rmtree as git_rmtree
113
+
92
114
  repo = shallow_git_clone(OFFICIAL_TEMPLATES_GITHUB_URL, temp_path.path)
93
115
 
94
116
  # Mark a directory as a template if a project definition jinja template is inside
@@ -113,10 +135,14 @@ def app_list_templates(**options) -> CommandResult:
113
135
  )
114
136
  )
115
137
 
138
+ # proactively clean up here to avoid permission issues on Windows
139
+ repo.close()
140
+ git_rmtree(temp_path.path)
141
+
116
142
  return CollectionResult(result)
117
143
 
118
144
 
119
- @app.command("bundle", hidden=True)
145
+ @app.command("bundle")
120
146
  @with_project_definition("native_app")
121
147
  def app_bundle(
122
148
  **options,
@@ -140,7 +166,7 @@ def app_run(
140
166
  help=f"""The version defined in an existing application package from which you want to create an application object.
141
167
  The application object and application package names are determined from the project definition file.""",
142
168
  ),
143
- patch: Optional[str] = typer.Option(
169
+ patch: Optional[int] = typer.Option(
144
170
  None,
145
171
  "--patch",
146
172
  help=f"""The patch number under the given `--version` defined in an existing application package that should be used to create an application object.
@@ -155,6 +181,7 @@ def app_run(
155
181
  ),
156
182
  interactive: bool = InteractiveOption,
157
183
  force: Optional[bool] = ForceOption,
184
+ validate: bool = ValidateOption,
158
185
  **options,
159
186
  ) -> CommandResult:
160
187
  """
@@ -175,13 +202,15 @@ def app_run(
175
202
  project_definition=cli_context.project_definition,
176
203
  project_root=cli_context.project_root,
177
204
  )
178
- processor.build_bundle()
205
+ bundle_map = processor.build_bundle()
179
206
  processor.process(
207
+ bundle_map=bundle_map,
180
208
  policy=policy,
181
209
  version=version,
182
210
  patch=patch,
183
211
  from_release_directive=from_release_directive,
184
212
  is_interactive=is_interactive,
213
+ validate=validate,
185
214
  )
186
215
  return MessageResult(
187
216
  f"Your application object ({processor.app_name}) is now available:\n"
@@ -243,7 +272,7 @@ def app_deploy(
243
272
  ),
244
273
  recursive: Optional[bool] = typer.Option(
245
274
  None,
246
- "--recursive",
275
+ "--recursive/--no-recursive",
247
276
  "-r",
248
277
  help=f"""Whether to traverse and deploy files from subdirectories. If set, the command deploys all files and subdirectories; otherwise, only files in the current directory are deployed.""",
249
278
  ),
@@ -252,6 +281,7 @@ def app_deploy(
252
281
  show_default=False,
253
282
  help=f"""Paths, relative to the the project root, of files you want to upload to a stage. The paths must match one of the artifacts src pattern entries in snowflake.yml. If unspecified, the command syncs all local changes to the stage.""",
254
283
  ),
284
+ validate: bool = ValidateOption,
255
285
  **options,
256
286
  ) -> CommandResult:
257
287
  """
@@ -273,9 +303,32 @@ def app_deploy(
273
303
  project_root=cli_context.project_root,
274
304
  )
275
305
 
276
- mapped_files = manager.build_bundle()
277
- manager.deploy(prune, recursive, files, mapped_files)
306
+ bundle_map = manager.build_bundle()
307
+ manager.deploy(
308
+ bundle_map=bundle_map,
309
+ prune=prune,
310
+ recursive=recursive,
311
+ local_paths_to_sync=files,
312
+ validate=validate,
313
+ )
278
314
 
279
315
  return MessageResult(
280
316
  f"Deployed successfully. Application package and stage are up-to-date."
281
317
  )
318
+
319
+
320
+ @app.command("validate", requires_connection=True)
321
+ @with_project_definition("native_app")
322
+ def app_validate(**options):
323
+ """
324
+ Validates a deployed Snowflake Native App's setup script.
325
+ """
326
+ manager = NativeAppManager(
327
+ project_definition=cli_context.project_definition,
328
+ project_root=cli_context.project_root,
329
+ )
330
+ if cli_context.output_format == OutputFormat.JSON:
331
+ return ObjectResult(manager.get_validation_result(use_scratch_stage=True))
332
+
333
+ manager.validate(use_scratch_stage=True)
334
+ return MessageResult("Snowflake Native App validation succeeded.")
@@ -1,3 +1,17 @@
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
+
1
15
  import typer
2
16
  from snowflake.cli.plugins.nativeapp.utils import is_tty_interactive
3
17
 
@@ -22,3 +36,9 @@ ForceOption = typer.Option(
22
36
  You should enable this option if interactive mode is not specified and if you want perform potentially destructive actions. Defaults to unset.""",
23
37
  is_flag=True,
24
38
  )
39
+ ValidateOption = typer.Option(
40
+ True,
41
+ "--validate/--no-validate",
42
+ help="""When enabled, this option triggers validation of a deployed Snowflake Native App's setup script SQL""",
43
+ is_flag=True,
44
+ )
@@ -1,3 +1,17 @@
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
+
1
15
  SPECIAL_COMMENT_OLD = "GENERATED_BY_SNOWCLI"
2
16
  SPECIAL_COMMENT = "GENERATED_BY_SNOWFLAKECLI"
3
17
  ALLOWED_SPECIAL_COMMENTS = {SPECIAL_COMMENT, SPECIAL_COMMENT_OLD}
@@ -12,5 +26,6 @@ PATCH_COL = "patch"
12
26
  INTERNAL_DISTRIBUTION = "internal"
13
27
  EXTERNAL_DISTRIBUTION = "external"
14
28
 
29
+ ERROR_MESSAGE_2003 = "does not exist or not authorized"
15
30
  ERROR_MESSAGE_2043 = "Object does not exist, or operation cannot be performed."
16
31
  ERROR_MESSAGE_606 = "No active warehouse selected in the current session."
@@ -1,3 +1,17 @@
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
+
1
15
  from textwrap import dedent
2
16
 
3
17
  import jinja2
@@ -14,11 +28,11 @@ class ApplicationPackageAlreadyExistsError(ClickException):
14
28
 
15
29
 
16
30
  class ApplicationPackageDoesNotExistError(ClickException):
17
- """An application package of the specified name does not exist in the Snowflake account."""
31
+ """An application package of the specified name does not exist in the Snowflake account or the current role isn't authorized."""
18
32
 
19
33
  def __init__(self, name: str):
20
34
  super().__init__(
21
- f"Application Package {name} does not exist in the Snowflake account."
35
+ f"Application Package {name} does not exist in the Snowflake account or not authorized."
22
36
  )
23
37
 
24
38
 
@@ -74,3 +88,10 @@ class CouldNotDropApplicationPackageWithVersions(ClickException):
74
88
  """
75
89
  )
76
90
  )
91
+
92
+
93
+ class SetupScriptFailedValidation(ClickException):
94
+ """Snowflake Native App setup script failed validation."""
95
+
96
+ def __init__(self):
97
+ super().__init__(self.__doc__)
@@ -1,3 +1,17 @@
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
+
1
15
  from __future__ import annotations
2
16
 
3
17
  import logging