snowflake-cli-labs 2.8.0rc1__py3-none-any.whl → 3.0.0rc0__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 (219) 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 +6 -6
  16. snowflake/cli/{app → _app}/telemetry.py +4 -5
  17. snowflake/cli/{plugins → _plugins}/connection/commands.py +22 -5
  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 +6 -5
  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 +16 -18
  33. snowflake/cli/{plugins → _plugins}/nativeapp/codegen/setup/native_app_setup_processor.py +24 -28
  34. snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/extension_function_utils.py +4 -4
  35. snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/python_processor.py +20 -24
  36. snowflake/cli/{plugins → _plugins}/nativeapp/commands.py +171 -42
  37. snowflake/cli/{plugins → _plugins}/nativeapp/common_flags.py +1 -1
  38. snowflake/cli/{plugins → _plugins}/nativeapp/init.py +1 -1
  39. snowflake/cli/_plugins/nativeapp/manager.py +601 -0
  40. snowflake/cli/{plugins/connection → _plugins/nativeapp}/plugin_spec.py +1 -1
  41. snowflake/cli/{plugins → _plugins}/nativeapp/project_model.py +34 -11
  42. snowflake/cli/{plugins → _plugins}/nativeapp/run_processor.py +25 -23
  43. snowflake/cli/{plugins → _plugins}/nativeapp/teardown_processor.py +8 -8
  44. snowflake/cli/{plugins → _plugins}/nativeapp/v2_conversions/v2_to_v1_decorator.py +47 -28
  45. snowflake/cli/{plugins → _plugins}/nativeapp/version/commands.py +15 -12
  46. snowflake/cli/{plugins → _plugins}/nativeapp/version/version_processor.py +22 -20
  47. snowflake/cli/{plugins → _plugins}/notebook/commands.py +8 -6
  48. snowflake/cli/{plugins → _plugins}/notebook/manager.py +14 -14
  49. snowflake/cli/{plugins → _plugins}/notebook/plugin_spec.py +1 -1
  50. snowflake/cli/{plugins → _plugins}/notebook/types.py +0 -1
  51. snowflake/cli/{plugins → _plugins}/object/command_aliases.py +6 -5
  52. snowflake/cli/{plugins → _plugins}/object/commands.py +16 -10
  53. snowflake/cli/{plugins → _plugins}/object/manager.py +7 -6
  54. snowflake/cli/{plugins → _plugins}/object/plugin_spec.py +1 -1
  55. snowflake/cli/_plugins/snowpark/commands.py +510 -0
  56. snowflake/cli/_plugins/snowpark/common.py +252 -0
  57. snowflake/cli/{plugins → _plugins}/snowpark/models.py +0 -7
  58. snowflake/cli/{plugins → _plugins}/snowpark/package/anaconda_packages.py +1 -1
  59. snowflake/cli/{plugins → _plugins}/snowpark/package/commands.py +13 -74
  60. snowflake/cli/{plugins → _plugins}/snowpark/package/manager.py +4 -3
  61. snowflake/cli/{plugins → _plugins}/snowpark/package_utils.py +5 -5
  62. snowflake/cli/{plugins/nativeapp → _plugins/snowpark}/plugin_spec.py +1 -1
  63. snowflake/cli/_plugins/snowpark/snowpark_project_paths.py +109 -0
  64. snowflake/cli/{plugins → _plugins}/snowpark/snowpark_shared.py +0 -36
  65. snowflake/cli/{plugins → _plugins}/snowpark/zipper.py +16 -8
  66. snowflake/cli/{plugins → _plugins}/spcs/__init__.py +5 -7
  67. snowflake/cli/{plugins → _plugins}/spcs/compute_pool/commands.py +29 -28
  68. snowflake/cli/{plugins → _plugins}/spcs/compute_pool/manager.py +3 -3
  69. snowflake/cli/{plugins → _plugins}/spcs/image_registry/commands.py +3 -3
  70. snowflake/cli/{plugins → _plugins}/spcs/image_repository/commands.py +25 -19
  71. snowflake/cli/{plugins → _plugins}/spcs/image_repository/manager.py +1 -1
  72. snowflake/cli/{plugins → _plugins}/spcs/plugin_spec.py +1 -1
  73. snowflake/cli/{plugins → _plugins}/spcs/services/commands.py +66 -32
  74. snowflake/cli/{plugins → _plugins}/spcs/services/manager.py +43 -5
  75. snowflake/cli/{plugins → _plugins}/sql/commands.py +19 -15
  76. snowflake/cli/{plugins → _plugins}/sql/manager.py +1 -1
  77. snowflake/cli/{plugins → _plugins}/sql/plugin_spec.py +1 -1
  78. snowflake/cli/{plugins → _plugins}/stage/commands.py +20 -17
  79. snowflake/cli/{plugins → _plugins}/stage/diff.py +1 -47
  80. snowflake/cli/{plugins → _plugins}/stage/manager.py +8 -6
  81. snowflake/cli/{plugins → _plugins}/stage/plugin_spec.py +1 -1
  82. snowflake/cli/_plugins/stage/utils.py +54 -0
  83. snowflake/cli/_plugins/streamlit/commands.py +242 -0
  84. snowflake/cli/{plugins → _plugins}/streamlit/manager.py +47 -70
  85. snowflake/cli/_plugins/streamlit/plugin_spec.py +30 -0
  86. snowflake/cli/_plugins/workspace/action_context.py +11 -0
  87. snowflake/cli/_plugins/workspace/commands.py +113 -0
  88. snowflake/cli/_plugins/workspace/manager.py +57 -0
  89. snowflake/cli/{plugins → _plugins}/workspace/plugin_spec.py +1 -1
  90. snowflake/cli/api/cli_global_context.py +34 -7
  91. snowflake/cli/api/commands/common.py +25 -0
  92. snowflake/cli/api/commands/decorators.py +4 -3
  93. snowflake/cli/api/commands/experimental_behaviour.py +2 -3
  94. snowflake/cli/api/commands/flags.py +73 -174
  95. snowflake/cli/api/commands/overrideable_parameter.py +143 -0
  96. snowflake/cli/api/commands/snow_typer.py +5 -4
  97. snowflake/cli/api/commands/typer_pre_execute.py +3 -3
  98. snowflake/cli/api/commands/utils.py +18 -0
  99. snowflake/cli/api/config.py +1 -1
  100. snowflake/cli/api/console/abc.py +5 -2
  101. snowflake/cli/api/entities/application_entity.py +12 -0
  102. snowflake/cli/api/entities/application_package_entity.py +260 -0
  103. snowflake/cli/api/entities/common.py +47 -0
  104. snowflake/cli/api/entities/snowpark_entity.py +29 -0
  105. snowflake/cli/api/entities/streamlit_entity.py +12 -0
  106. snowflake/cli/api/entities/utils.py +321 -0
  107. snowflake/cli/api/exceptions.py +19 -3
  108. snowflake/cli/api/feature_flags.py +2 -1
  109. snowflake/cli/api/identifiers.py +41 -9
  110. snowflake/cli/api/project/definition.py +13 -5
  111. snowflake/cli/api/project/definition_manager.py +12 -1
  112. snowflake/cli/api/project/project_verification.py +3 -3
  113. snowflake/cli/api/project/schemas/entities/{application_entity.py → application_entity_model.py} +21 -9
  114. snowflake/cli/api/project/schemas/entities/{application_package_entity.py → application_package_entity_model.py} +26 -15
  115. snowflake/cli/api/project/schemas/entities/common.py +80 -6
  116. snowflake/cli/api/project/schemas/entities/entities.py +38 -8
  117. snowflake/cli/api/project/schemas/entities/snowpark_entity.py +176 -0
  118. snowflake/cli/api/project/schemas/entities/streamlit_entity_model.py +73 -0
  119. snowflake/cli/api/project/schemas/identifier_model.py +10 -1
  120. snowflake/cli/api/project/schemas/native_app/application.py +8 -9
  121. snowflake/cli/api/project/schemas/native_app/package.py +7 -1
  122. snowflake/cli/api/project/schemas/project_definition.py +97 -23
  123. snowflake/cli/api/project/schemas/updatable_model.py +11 -3
  124. snowflake/cli/api/project/util.py +23 -6
  125. snowflake/cli/api/rendering/jinja.py +28 -8
  126. snowflake/cli/api/rendering/sql_templates.py +41 -12
  127. snowflake/cli/api/secure_path.py +3 -0
  128. snowflake/cli/api/sql_execution.py +35 -19
  129. snowflake/cli/api/utils/definition_rendering.py +14 -2
  130. {snowflake_cli_labs-2.8.0rc1.dist-info → snowflake_cli_labs-3.0.0rc0.dist-info}/METADATA +12 -12
  131. snowflake_cli_labs-3.0.0rc0.dist-info/RECORD +234 -0
  132. snowflake_cli_labs-3.0.0rc0.dist-info/entry_points.txt +2 -0
  133. snowflake/cli/api/commands/project_initialisation.py +0 -65
  134. snowflake/cli/app/build_and_push.sh +0 -8
  135. snowflake/cli/plugins/nativeapp/manager.py +0 -823
  136. snowflake/cli/plugins/object_stage_deprecated/__init__.py +0 -15
  137. snowflake/cli/plugins/object_stage_deprecated/commands.py +0 -122
  138. snowflake/cli/plugins/object_stage_deprecated/plugin_spec.py +0 -32
  139. snowflake/cli/plugins/snowpark/commands.py +0 -548
  140. snowflake/cli/plugins/snowpark/common.py +0 -307
  141. snowflake/cli/plugins/snowpark/manager.py +0 -109
  142. snowflake/cli/plugins/snowpark/plugin_spec.py +0 -30
  143. snowflake/cli/plugins/snowpark/snowpark_package_paths.py +0 -65
  144. snowflake/cli/plugins/spcs/jobs/commands.py +0 -78
  145. snowflake/cli/plugins/spcs/jobs/manager.py +0 -53
  146. snowflake/cli/plugins/streamlit/commands.py +0 -186
  147. snowflake/cli/plugins/streamlit/plugin_spec.py +0 -30
  148. snowflake/cli/plugins/workspace/commands.py +0 -35
  149. snowflake/cli/templates/default_snowpark/.gitignore +0 -4
  150. snowflake/cli/templates/default_snowpark/app/__init__.py +0 -0
  151. snowflake/cli/templates/default_snowpark/app/common.py +0 -2
  152. snowflake/cli/templates/default_snowpark/app/functions.py +0 -15
  153. snowflake/cli/templates/default_snowpark/app/procedures.py +0 -22
  154. snowflake/cli/templates/default_snowpark/requirements.txt +0 -1
  155. snowflake/cli/templates/default_snowpark/snowflake.yml +0 -23
  156. snowflake/cli/templates/default_streamlit/.gitignore +0 -4
  157. snowflake/cli/templates/default_streamlit/common/hello.py +0 -2
  158. snowflake/cli/templates/default_streamlit/environment.yml +0 -6
  159. snowflake/cli/templates/default_streamlit/pages/my_page.py +0 -3
  160. snowflake/cli/templates/default_streamlit/snowflake.yml +0 -10
  161. snowflake/cli/templates/default_streamlit/streamlit_app.py +0 -4
  162. snowflake_cli_labs-2.8.0rc1.dist-info/RECORD +0 -240
  163. snowflake_cli_labs-2.8.0rc1.dist-info/entry_points.txt +0 -2
  164. /snowflake/cli/{app → _app}/__init__.py +0 -0
  165. /snowflake/cli/{app → _app}/api_impl/__init__.py +0 -0
  166. /snowflake/cli/{app → _app}/api_impl/plugin/__init__.py +0 -0
  167. /snowflake/cli/{app → _app}/api_impl/plugin/plugin_config_provider_impl.py +0 -0
  168. /snowflake/cli/{app → _app}/commands_registration/__init__.py +0 -0
  169. /snowflake/cli/{app → _app}/commands_registration/threadsafe.py +0 -0
  170. /snowflake/cli/{app → _app}/constants.py +0 -0
  171. /snowflake/cli/{app → _app}/dev/__init__.py +0 -0
  172. /snowflake/cli/{app → _app}/dev/commands_structure.py +0 -0
  173. /snowflake/cli/{app → _app}/dev/docs/__init__.py +0 -0
  174. /snowflake/cli/{app → _app}/dev/docs/project_definition_generate_json_schema.py +0 -0
  175. /snowflake/cli/{app → _app}/dev/docs/template_utils.py +0 -0
  176. /snowflake/cli/{app → _app}/dev/docs/templates/definition_description.rst.jinja2 +0 -0
  177. /snowflake/cli/{app → _app}/dev/docs/templates/overview.rst.jinja2 +0 -0
  178. /snowflake/cli/{app → _app}/dev/pycharm_remote_debug.py +0 -0
  179. /snowflake/cli/{app → _app}/loggers.py +0 -0
  180. /snowflake/cli/{plugins → _plugins}/__init__.py +0 -0
  181. /snowflake/cli/{plugins → _plugins}/connection/__init__.py +0 -0
  182. /snowflake/cli/{plugins → _plugins}/cortex/__init__.py +0 -0
  183. /snowflake/cli/{plugins → _plugins}/cortex/types.py +0 -0
  184. /snowflake/cli/{plugins → _plugins}/git/__init__.py +0 -0
  185. /snowflake/cli/{plugins → _plugins}/init/__init__.py +0 -0
  186. /snowflake/cli/{plugins → _plugins}/nativeapp/__init__.py +0 -0
  187. /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/__init__.py +0 -0
  188. /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/sandbox.py +0 -0
  189. /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/setup/setup_driver.py.source +0 -0
  190. /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/callback_source.py.jinja +0 -0
  191. /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/models.py +0 -0
  192. /snowflake/cli/{plugins → _plugins}/nativeapp/constants.py +0 -0
  193. /snowflake/cli/{plugins → _plugins}/nativeapp/exceptions.py +0 -0
  194. /snowflake/cli/{plugins → _plugins}/nativeapp/feature_flags.py +0 -0
  195. /snowflake/cli/{plugins → _plugins}/nativeapp/policy.py +0 -0
  196. /snowflake/cli/{plugins → _plugins}/nativeapp/utils.py +0 -0
  197. /snowflake/cli/{plugins → _plugins}/nativeapp/version/__init__.py +0 -0
  198. /snowflake/cli/{plugins → _plugins}/notebook/__init__.py +0 -0
  199. /snowflake/cli/{plugins → _plugins}/notebook/exceptions.py +0 -0
  200. /snowflake/cli/{plugins → _plugins}/object/__init__.py +0 -0
  201. /snowflake/cli/{plugins → _plugins}/object/common.py +0 -0
  202. /snowflake/cli/{plugins → _plugins}/snowpark/__init__.py +0 -0
  203. /snowflake/cli/{plugins → _plugins}/snowpark/package/__init__.py +0 -0
  204. /snowflake/cli/{plugins → _plugins}/snowpark/package/utils.py +0 -0
  205. /snowflake/cli/{plugins → _plugins}/spcs/common.py +0 -0
  206. /snowflake/cli/{plugins → _plugins}/spcs/compute_pool/__init__.py +0 -0
  207. /snowflake/cli/{plugins → _plugins}/spcs/image_registry/__init__.py +0 -0
  208. /snowflake/cli/{plugins → _plugins}/spcs/image_registry/manager.py +0 -0
  209. /snowflake/cli/{plugins → _plugins}/spcs/image_repository/__init__.py +0 -0
  210. /snowflake/cli/{plugins/spcs/jobs → _plugins/spcs/services}/__init__.py +0 -0
  211. /snowflake/cli/{plugins/spcs/services → _plugins/sql}/__init__.py +0 -0
  212. /snowflake/cli/{plugins → _plugins}/sql/snowsql_templating.py +0 -0
  213. /snowflake/cli/{plugins/sql → _plugins/stage}/__init__.py +0 -0
  214. /snowflake/cli/{plugins → _plugins}/stage/md5.py +0 -0
  215. /snowflake/cli/{plugins/stage → _plugins/streamlit}/__init__.py +0 -0
  216. /snowflake/cli/{plugins/streamlit → _plugins/workspace}/__init__.py +0 -0
  217. /snowflake/cli/{plugins/workspace → api/project/schemas/entities}/__init__.py +0 -0
  218. {snowflake_cli_labs-2.8.0rc1.dist-info → snowflake_cli_labs-3.0.0rc0.dist-info}/WHEEL +0 -0
  219. {snowflake_cli_labs-2.8.0rc1.dist-info → snowflake_cli_labs-3.0.0rc0.dist-info}/licenses/LICENSE +0 -0
@@ -19,15 +19,15 @@ from pathlib import Path
19
19
  from typing import List, Optional
20
20
 
21
21
  import yaml
22
- from snowflake.cli.api.constants import DEFAULT_SIZE_LIMIT_MB, ObjectType
23
- from snowflake.cli.api.secure_path import SecurePath
24
- from snowflake.cli.api.sql_execution import SqlExecutionMixin
25
- from snowflake.cli.plugins.object.common import Tag
26
- from snowflake.cli.plugins.spcs.common import (
22
+ from snowflake.cli._plugins.object.common import Tag
23
+ from snowflake.cli._plugins.spcs.common import (
27
24
  NoPropertiesProvidedError,
28
25
  handle_object_already_exists,
29
26
  strip_empty_lines,
30
27
  )
28
+ from snowflake.cli.api.constants import DEFAULT_SIZE_LIMIT_MB, ObjectType
29
+ from snowflake.cli.api.secure_path import SecurePath
30
+ from snowflake.cli.api.sql_execution import SqlExecutionMixin
31
31
  from snowflake.connector.cursor import SnowflakeCursor
32
32
  from snowflake.connector.errors import ProgrammingError
33
33
 
@@ -85,6 +85,44 @@ class ServiceManager(SqlExecutionMixin):
85
85
  except ProgrammingError as e:
86
86
  handle_object_already_exists(e, ObjectType.SERVICE, service_name)
87
87
 
88
+ def execute_job(
89
+ self,
90
+ job_service_name: str,
91
+ compute_pool: str,
92
+ spec_path: Path,
93
+ external_access_integrations: Optional[List[str]],
94
+ query_warehouse: Optional[str],
95
+ comment: Optional[str],
96
+ ) -> SnowflakeCursor:
97
+ spec = self._read_yaml(spec_path)
98
+ query = f"""\
99
+ EXECUTE JOB SERVICE
100
+ IN COMPUTE POOL {compute_pool}
101
+ FROM SPECIFICATION $$
102
+ {spec}
103
+ $$
104
+ NAME = {job_service_name}
105
+ """.splitlines()
106
+
107
+ if external_access_integrations:
108
+ external_access_integration_list = ",".join(
109
+ f"{e}" for e in external_access_integrations
110
+ )
111
+ query.append(
112
+ f"EXTERNAL_ACCESS_INTEGRATIONS = ({external_access_integration_list})"
113
+ )
114
+
115
+ if query_warehouse:
116
+ query.append(f"QUERY_WAREHOUSE = {query_warehouse}")
117
+
118
+ if comment:
119
+ query.append(f"COMMENT = {comment}")
120
+
121
+ try:
122
+ return self._execute_query(strip_empty_lines(query))
123
+ except ProgrammingError as e:
124
+ handle_object_already_exists(e, ObjectType.SERVICE, job_service_name)
125
+
88
126
  def _read_yaml(self, path: Path) -> str:
89
127
  # TODO(aivanou): Add validation towards schema
90
128
  with SecurePath(path).open("r", read_file_limit_mb=DEFAULT_SIZE_LIMIT_MB) as fh:
@@ -17,43 +17,46 @@ from __future__ import annotations
17
17
  from pathlib import Path
18
18
  from typing import List, Optional
19
19
 
20
- import typer
20
+ from snowflake.cli._plugins.sql.manager import SqlManager
21
21
  from snowflake.cli.api.commands.decorators import with_project_definition
22
22
  from snowflake.cli.api.commands.flags import (
23
- parse_key_value_variables,
24
23
  variables_option,
25
24
  )
25
+ from snowflake.cli.api.commands.overrideable_parameter import OverrideableOption
26
26
  from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
27
+ from snowflake.cli.api.commands.utils import parse_key_value_variables
27
28
  from snowflake.cli.api.output.types import CommandResult, MultipleResults, QueryResult
28
- from snowflake.cli.plugins.sql.manager import SqlManager
29
29
 
30
30
  # simple Typer with defaults because it won't become a command group as it contains only one command
31
31
  app = SnowTyperFactory()
32
32
 
33
+ SOURCE_EXCLUSIVE_OPTIONS_NAMES = ["query", "files", "std_in"]
34
+
35
+ SourceOption = OverrideableOption(
36
+ mutually_exclusive=SOURCE_EXCLUSIVE_OPTIONS_NAMES, show_default=False
37
+ )
38
+
33
39
 
34
40
  @app.command(name="sql", requires_connection=True, no_args_is_help=True)
35
41
  @with_project_definition(is_optional=True)
36
42
  def execute_sql(
37
- query: Optional[str] = typer.Option(
38
- None,
39
- "--query",
40
- "-q",
43
+ query: Optional[str] = SourceOption(
44
+ default=None,
45
+ param_decls=["--query", "-q"],
41
46
  help="Query to execute.",
42
47
  ),
43
- files: Optional[List[Path]] = typer.Option(
44
- None,
45
- "--filename",
46
- "-f",
48
+ files: Optional[List[Path]] = SourceOption(
49
+ default=[],
50
+ param_decls=["--filename", "-f"],
47
51
  exists=True,
48
52
  file_okay=True,
49
53
  dir_okay=False,
50
54
  readable=True,
51
55
  help="File to execute.",
52
56
  ),
53
- std_in: Optional[bool] = typer.Option(
54
- False,
55
- "--stdin",
56
- "-i",
57
+ std_in: Optional[bool] = SourceOption(
58
+ default=False,
59
+ param_decls=["--stdin", "-i"],
57
60
  help="Read the query from standard input. Use it when piping input to this command.",
58
61
  ),
59
62
  data_override: List[str] = variables_option(
@@ -73,6 +76,7 @@ def execute_sql(
73
76
  The command supports variable substitution that happens on client-side. Both &VARIABLE or &{ VARIABLE }
74
77
  syntax are supported.
75
78
  """
79
+
76
80
  data = {}
77
81
  if data_override:
78
82
  data = {v.key: v.value for v in parse_key_value_variables(data_override)}
@@ -22,10 +22,10 @@ from typing import Dict, Iterable, List, Tuple
22
22
 
23
23
  from click import ClickException, UsageError
24
24
  from jinja2 import UndefinedError
25
+ from snowflake.cli._plugins.sql.snowsql_templating import transpile_snowsql_templates
25
26
  from snowflake.cli.api.rendering.sql_templates import snowflake_sql_jinja_render
26
27
  from snowflake.cli.api.secure_path import UNLIMITED, SecurePath
27
28
  from snowflake.cli.api.sql_execution import SqlExecutionMixin, VerboseCursor
28
- from snowflake.cli.plugins.sql.snowsql_templating import transpile_snowsql_templates
29
29
  from snowflake.connector.cursor import SnowflakeCursor
30
30
  from snowflake.connector.util_text import split_statements
31
31
 
@@ -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.sql 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.sql import commands
22
22
 
23
23
 
24
24
  @plugin_hook_impl
@@ -21,16 +21,29 @@ from typing import List, Optional
21
21
 
22
22
  import click
23
23
  import typer
24
- from snowflake.cli.api.cli_global_context import cli_context
24
+ from snowflake.cli._plugins.object.command_aliases import (
25
+ add_object_command_aliases,
26
+ scope_option,
27
+ )
28
+ from snowflake.cli._plugins.stage.diff import (
29
+ DiffResult,
30
+ compute_stage_diff,
31
+ )
32
+ from snowflake.cli._plugins.stage.manager import StageManager
33
+ from snowflake.cli._plugins.stage.utils import print_diff_to_console
34
+ from snowflake.cli.api.cli_global_context import get_cli_context
35
+ from snowflake.cli.api.commands.common import OnErrorType
25
36
  from snowflake.cli.api.commands.flags import (
26
37
  ExecuteVariablesOption,
27
38
  OnErrorOption,
28
39
  PatternOption,
40
+ identifier_argument,
29
41
  like_option,
30
42
  )
31
43
  from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
32
44
  from snowflake.cli.api.console import cli_console
33
45
  from snowflake.cli.api.constants import ObjectType
46
+ from snowflake.cli.api.identifiers import FQN
34
47
  from snowflake.cli.api.output.formats import OutputFormat
35
48
  from snowflake.cli.api.output.types import (
36
49
  CollectionResult,
@@ -40,23 +53,13 @@ from snowflake.cli.api.output.types import (
40
53
  SingleQueryResult,
41
54
  )
42
55
  from snowflake.cli.api.utils.path_utils import is_stage_path
43
- from snowflake.cli.plugins.object.command_aliases import (
44
- add_object_command_aliases,
45
- scope_option,
46
- )
47
- from snowflake.cli.plugins.stage.diff import (
48
- DiffResult,
49
- compute_stage_diff,
50
- print_diff_to_console,
51
- )
52
- from snowflake.cli.plugins.stage.manager import OnErrorType, StageManager
53
56
 
54
57
  app = SnowTyperFactory(
55
58
  name="stage",
56
59
  help="Manages stages.",
57
60
  )
58
61
 
59
- StageNameArgument = typer.Argument(..., help="Name of the stage.", show_default=False)
62
+ StageNameArgument = identifier_argument(sf_object="stage", example="@my_stage")
60
63
 
61
64
  add_object_command_aliases(
62
65
  app=app,
@@ -142,17 +145,17 @@ def copy(
142
145
 
143
146
 
144
147
  @app.command("create", requires_connection=True)
145
- def stage_create(stage_name: str = StageNameArgument, **options) -> CommandResult:
148
+ def stage_create(stage_name: FQN = StageNameArgument, **options) -> CommandResult:
146
149
  """
147
150
  Creates a named stage if it does not already exist.
148
151
  """
149
- cursor = StageManager().create(stage_name=stage_name)
152
+ cursor = StageManager().create(fqn=stage_name)
150
153
  return SingleQueryResult(cursor)
151
154
 
152
155
 
153
156
  @app.command("remove", requires_connection=True)
154
157
  def stage_remove(
155
- stage_name: str = StageNameArgument,
158
+ stage_name: FQN = StageNameArgument,
156
159
  file_name: str = typer.Argument(
157
160
  ...,
158
161
  help="Name of the file to remove.",
@@ -164,7 +167,7 @@ def stage_remove(
164
167
  Removes a file from a stage.
165
168
  """
166
169
 
167
- cursor = StageManager().remove(stage_name=stage_name, path=file_name)
170
+ cursor = StageManager().remove(stage_name=stage_name.identifier, path=file_name)
168
171
  return SingleQueryResult(cursor)
169
172
 
170
173
 
@@ -186,7 +189,7 @@ def stage_diff(
186
189
  diff: DiffResult = compute_stage_diff(
187
190
  local_root=Path(folder_name), stage_fqn=stage_name
188
191
  )
189
- if cli_context.output_format == OutputFormat.JSON:
192
+ if get_cli_context().output_format == OutputFormat.JSON:
190
193
  return ObjectResult(diff.to_dict())
191
194
  else:
192
195
  print_diff_to_console(diff)
@@ -19,11 +19,10 @@ from dataclasses import dataclass, field
19
19
  from pathlib import Path, PurePosixPath
20
20
  from typing import Collection, Dict, List, Optional, Tuple
21
21
 
22
- from snowflake.cli.api.console import cli_console as cc
22
+ from snowflake.cli._plugins.nativeapp.artifacts import BundleMap
23
23
  from snowflake.cli.api.exceptions import (
24
24
  SnowflakeSQLExecutionError,
25
25
  )
26
- from snowflake.cli.plugins.nativeapp.artifacts import BundleMap
27
26
  from snowflake.connector.cursor import DictCursor
28
27
 
29
28
  from .manager import StageManager
@@ -279,48 +278,3 @@ def _to_diff_line(status: str, src: Optional[str], dest: str) -> str:
279
278
  status_prefix = f"[red]{status}[/red]: {padding}"
280
279
 
281
280
  return f"{status_prefix}{src_prefix}{dest}"
282
-
283
-
284
- def print_diff_to_console(
285
- diff: DiffResult,
286
- bundle_map: Optional[BundleMap] = None,
287
- ):
288
- if not diff.has_changes():
289
- cc.message("Your stage is up-to-date with your local deploy root.")
290
- return
291
-
292
- blank_line_needed = False
293
- if diff.only_local or diff.different:
294
- cc.message("Local changes to be deployed:")
295
- messages_to_output = []
296
- for p in diff.different:
297
- src_dest_pair = _to_src_dest_pair(p, bundle_map)
298
- messages_to_output.append(
299
- (
300
- src_dest_pair,
301
- _to_diff_line("modified", src_dest_pair[0], src_dest_pair[1]),
302
- )
303
- )
304
- for p in diff.only_local:
305
- src_dest_pair = _to_src_dest_pair(p, bundle_map)
306
- messages_to_output.append(
307
- (
308
- src_dest_pair,
309
- _to_diff_line("added", src_dest_pair[0], src_dest_pair[1]),
310
- )
311
- )
312
-
313
- with cc.indented():
314
- for key, message in sorted(messages_to_output, key=lambda pair: pair[0]):
315
- cc.message(message)
316
-
317
- blank_line_needed = True
318
-
319
- if diff.only_on_stage:
320
- if blank_line_needed:
321
- cc.message("")
322
- cc.message(f"Deleted paths to be removed from your stage:")
323
- with cc.indented():
324
- for p in sorted(diff.only_on_stage):
325
- diff_line = _to_diff_line("deleted", src=None, dest=str(p))
326
- cc.message(diff_line)
@@ -27,11 +27,12 @@ from textwrap import dedent
27
27
  from typing import Dict, List, Optional, Union
28
28
 
29
29
  from click import ClickException
30
- from snowflake.cli.api.commands.flags import (
30
+ from snowflake.cli._plugins.snowpark.package_utils import parse_requirements
31
+ from snowflake.cli.api.commands.common import (
31
32
  OnErrorType,
32
33
  Variable,
33
- parse_key_value_variables,
34
34
  )
35
+ from snowflake.cli.api.commands.utils import parse_key_value_variables
35
36
  from snowflake.cli.api.console import cli_console
36
37
  from snowflake.cli.api.constants import PYTHON_3_12
37
38
  from snowflake.cli.api.identifiers import FQN
@@ -39,7 +40,6 @@ from snowflake.cli.api.project.util import to_string_literal
39
40
  from snowflake.cli.api.secure_path import SecurePath
40
41
  from snowflake.cli.api.sql_execution import SqlExecutionMixin
41
42
  from snowflake.cli.api.utils.path_utils import path_resolver
42
- from snowflake.cli.plugins.snowpark.package_utils import parse_requirements
43
43
  from snowflake.connector import DictCursor, ProgrammingError
44
44
  from snowflake.connector.cursor import SnowflakeCursor
45
45
 
@@ -168,7 +168,9 @@ class StageManager(SqlExecutionMixin):
168
168
  self._python_exe_procedure = None
169
169
 
170
170
  @staticmethod
171
- def get_standard_stage_prefix(name: str) -> str:
171
+ def get_standard_stage_prefix(name: str | FQN) -> str:
172
+ if isinstance(name, FQN):
173
+ name = name.identifier
172
174
  # Handle embedded stages
173
175
  if name.startswith("snow://") or name.startswith("@"):
174
176
  return name
@@ -300,8 +302,8 @@ class StageManager(SqlExecutionMixin):
300
302
  quoted_stage_name = self.quote_stage_name(f"{stage_name}{path}")
301
303
  return self._execute_query(f"remove {quoted_stage_name}")
302
304
 
303
- def create(self, stage_name: str, comment: Optional[str] = None) -> SnowflakeCursor:
304
- query = f"create stage if not exists {stage_name}"
305
+ def create(self, fqn: FQN, comment: Optional[str] = None) -> SnowflakeCursor:
306
+ query = f"create stage if not exists {fqn.sql_identifier}"
305
307
  if comment:
306
308
  query += f" comment='{comment}'"
307
309
  return self._execute_query(query)
@@ -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.stage 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.stage import commands
22
22
 
23
23
 
24
24
  @plugin_hook_impl
@@ -0,0 +1,54 @@
1
+ from typing import Optional
2
+
3
+ from snowflake.cli._plugins.nativeapp.artifacts import BundleMap
4
+ from snowflake.cli._plugins.stage.diff import (
5
+ DiffResult,
6
+ _to_diff_line,
7
+ _to_src_dest_pair,
8
+ )
9
+ from snowflake.cli.api.console import cli_console as cc
10
+
11
+
12
+ def print_diff_to_console(
13
+ diff: DiffResult,
14
+ bundle_map: Optional[BundleMap] = None,
15
+ ):
16
+ if not diff.has_changes():
17
+ cc.message("Your stage is up-to-date with your local deploy root.")
18
+ return
19
+
20
+ blank_line_needed = False
21
+ if diff.only_local or diff.different:
22
+ cc.message("Local changes to be deployed:")
23
+ messages_to_output = []
24
+ for p in diff.different:
25
+ src_dest_pair = _to_src_dest_pair(p, bundle_map)
26
+ messages_to_output.append(
27
+ (
28
+ src_dest_pair,
29
+ _to_diff_line("modified", src_dest_pair[0], src_dest_pair[1]),
30
+ )
31
+ )
32
+ for p in diff.only_local:
33
+ src_dest_pair = _to_src_dest_pair(p, bundle_map)
34
+ messages_to_output.append(
35
+ (
36
+ src_dest_pair,
37
+ _to_diff_line("added", src_dest_pair[0], src_dest_pair[1]),
38
+ )
39
+ )
40
+
41
+ with cc.indented():
42
+ for key, message in sorted(messages_to_output, key=lambda pair: pair[0]):
43
+ cc.message(message)
44
+
45
+ blank_line_needed = True
46
+
47
+ if diff.only_on_stage:
48
+ if blank_line_needed:
49
+ cc.message("")
50
+ cc.message(f"Deleted paths to be removed from your stage:")
51
+ with cc.indented():
52
+ for p in sorted(diff.only_on_stage):
53
+ diff_line = _to_diff_line("deleted", src=None, dest=str(p))
54
+ cc.message(diff_line)
@@ -0,0 +1,242 @@
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 logging
18
+ from pathlib import Path
19
+ from typing import Dict
20
+
21
+ import click
22
+ import typer
23
+ from click import ClickException, UsageError
24
+ from snowflake.cli._plugins.object.command_aliases import (
25
+ add_object_command_aliases,
26
+ scope_option,
27
+ )
28
+ from snowflake.cli._plugins.streamlit.manager import StreamlitManager
29
+ from snowflake.cli.api.cli_global_context import get_cli_context
30
+ from snowflake.cli.api.commands.decorators import (
31
+ with_experimental_behaviour,
32
+ with_project_definition,
33
+ )
34
+ from snowflake.cli.api.commands.flags import (
35
+ ReplaceOption,
36
+ entity_argument,
37
+ identifier_argument,
38
+ like_option,
39
+ )
40
+ from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
41
+ from snowflake.cli.api.constants import ObjectType
42
+ from snowflake.cli.api.exceptions import NoProjectDefinitionError
43
+ from snowflake.cli.api.identifiers import FQN
44
+ from snowflake.cli.api.output.types import (
45
+ CommandResult,
46
+ MessageResult,
47
+ SingleQueryResult,
48
+ )
49
+ from snowflake.cli.api.project.schemas.entities.streamlit_entity_model import (
50
+ StreamlitEntityModel,
51
+ )
52
+ from snowflake.cli.api.project.schemas.project_definition import (
53
+ ProjectDefinition,
54
+ ProjectDefinitionV2,
55
+ )
56
+
57
+ app = SnowTyperFactory(
58
+ name="streamlit",
59
+ help="Manages a Streamlit app in Snowflake.",
60
+ )
61
+ log = logging.getLogger(__name__)
62
+
63
+ StreamlitNameArgument = identifier_argument(
64
+ sf_object="Streamlit app", example="my_streamlit"
65
+ )
66
+ OpenOption = typer.Option(
67
+ False,
68
+ "--open",
69
+ help="Whether to open the Streamlit app in a browser.",
70
+ is_flag=True,
71
+ )
72
+
73
+
74
+ add_object_command_aliases(
75
+ app=app,
76
+ object_type=ObjectType.STREAMLIT,
77
+ name_argument=StreamlitNameArgument,
78
+ like_option=like_option(
79
+ help_example='`list --like "my%"` lists all streamlit apps that begin with “my”'
80
+ ),
81
+ scope_option=scope_option(help_example="`list --in database my_db`"),
82
+ )
83
+
84
+
85
+ @app.command("share", requires_connection=True)
86
+ def streamlit_share(
87
+ name: FQN = StreamlitNameArgument,
88
+ to_role: str = typer.Argument(
89
+ ...,
90
+ help="Role with which to share the Streamlit app.",
91
+ show_default=False,
92
+ ),
93
+ **options,
94
+ ) -> CommandResult:
95
+ """
96
+ Shares a Streamlit app with another role.
97
+ """
98
+ cursor = StreamlitManager().share(streamlit_name=name, to_role=to_role)
99
+ return SingleQueryResult(cursor)
100
+
101
+
102
+ def _default_file_callback(param_name: str):
103
+ from click.core import ParameterSource # type: ignore
104
+
105
+ def _check_file_exists_if_not_default(ctx: click.Context, value):
106
+ if (
107
+ ctx.get_parameter_source(param_name) != ParameterSource.DEFAULT # type: ignore
108
+ and value
109
+ and not Path(value).exists()
110
+ ):
111
+ raise ClickException(f"Provided file {value} does not exist")
112
+ return Path(value)
113
+
114
+ return _check_file_exists_if_not_default
115
+
116
+
117
+ @app.command("deploy", requires_connection=True)
118
+ @with_project_definition()
119
+ @with_experimental_behaviour()
120
+ def streamlit_deploy(
121
+ replace: bool = ReplaceOption(
122
+ help="Replace the Streamlit app if it already exists."
123
+ ),
124
+ entity_id: str = entity_argument("streamlit"),
125
+ open_: bool = OpenOption,
126
+ **options,
127
+ ) -> CommandResult:
128
+ """
129
+ Deploys a Streamlit app defined in the project definition file (snowflake.yml). By default, the command uploads
130
+ environment.yml and any other pages or folders, if present. If you don’t specify a stage name, the `streamlit`
131
+ stage is used. If the specified stage does not exist, the command creates it. If multiple Streamlits are defined
132
+ in snowflake.yml and no entity_id is provided then command will raise an error.
133
+ """
134
+
135
+ cli_context = get_cli_context()
136
+ pd = cli_context.project_definition
137
+ if not pd.meets_version_requirement("2"):
138
+ if not pd.streamlit:
139
+ raise NoProjectDefinitionError(
140
+ project_type="streamlit", project_root=cli_context.project_root
141
+ )
142
+ pd = migrate_v1_streamlit_to_v2(pd)
143
+
144
+ streamlits: Dict[str, StreamlitEntityModel] = pd.get_entities_by_type(
145
+ entity_type="streamlit"
146
+ )
147
+
148
+ if not streamlits:
149
+ raise NoProjectDefinitionError(
150
+ project_type="streamlit", project_root=cli_context.project_root
151
+ )
152
+
153
+ if entity_id and entity_id not in streamlits:
154
+ raise UsageError(f"No '{entity_id}' entity in project definition file.")
155
+
156
+ if len(streamlits.keys()) == 1:
157
+ entity_id = list(streamlits.keys())[0]
158
+
159
+ if entity_id is None:
160
+ raise UsageError(
161
+ "Multiple Streamlit apps found. Please provide entity id for the operation."
162
+ )
163
+
164
+ # Get first streamlit
165
+ streamlit: StreamlitEntityModel = streamlits[entity_id]
166
+ url = StreamlitManager().deploy(streamlit=streamlit, replace=replace)
167
+
168
+ if open_:
169
+ typer.launch(url)
170
+
171
+ return MessageResult(f"Streamlit successfully deployed and available under {url}")
172
+
173
+
174
+ def migrate_v1_streamlit_to_v2(pd: ProjectDefinition):
175
+ default_env_file = "environment.yml"
176
+ default_pages_dir = "pages"
177
+
178
+ # Process env file
179
+ environment_file = pd.streamlit.env_file
180
+ if environment_file and not Path(environment_file).exists():
181
+ raise ClickException(f"Provided file {environment_file} does not exist")
182
+ elif environment_file is None and Path(default_env_file).exists():
183
+ environment_file = default_env_file
184
+ # Process pages dir
185
+ pages_dir = pd.streamlit.pages_dir
186
+ if pages_dir and not Path(pages_dir).exists():
187
+ raise ClickException(f"Provided file {pages_dir} does not exist")
188
+ elif pages_dir is None and Path(default_pages_dir).exists():
189
+ pages_dir = default_pages_dir
190
+
191
+ # Build V2 definition
192
+ artifacts = [
193
+ pd.streamlit.main_file,
194
+ environment_file,
195
+ pages_dir,
196
+ ]
197
+ artifacts = [a for a in artifacts if a is not None]
198
+ if pd.streamlit.additional_source_files:
199
+ artifacts.extend(pd.streamlit.additional_source_files)
200
+
201
+ identifier = {"name": pd.streamlit.name}
202
+ if pd.streamlit.schema_name:
203
+ identifier["schema"] = pd.streamlit.schema_name
204
+ if pd.streamlit.database:
205
+ identifier["database"] = pd.streamlit.database
206
+
207
+ if pd.streamlit.name.startswith("<%") and pd.streamlit.name.endswith("%>"):
208
+ streamlit_name = "streamlit_entity_1"
209
+ else:
210
+ streamlit_name = pd.streamlit.name
211
+
212
+ data = {
213
+ "definition_version": "2",
214
+ "entities": {
215
+ streamlit_name: {
216
+ "type": "streamlit",
217
+ "identifier": identifier,
218
+ "title": pd.streamlit.title,
219
+ "query_warehouse": pd.streamlit.query_warehouse,
220
+ "main_file": str(pd.streamlit.main_file),
221
+ "pages_dir": str(pd.streamlit.pages_dir),
222
+ "stage": pd.streamlit.stage,
223
+ "artifacts": artifacts,
224
+ }
225
+ },
226
+ }
227
+ if hasattr(pd, "env") and pd.env:
228
+ data["env"] = {k: v for k, v in pd.env.items()}
229
+ return ProjectDefinitionV2(**data)
230
+
231
+
232
+ @app.command("get-url", requires_connection=True)
233
+ def get_url(
234
+ name: FQN = StreamlitNameArgument,
235
+ open_: bool = OpenOption,
236
+ **options,
237
+ ):
238
+ """Returns a URL to the specified Streamlit app"""
239
+ url = StreamlitManager().get_url(streamlit_name=name)
240
+ if open_:
241
+ typer.launch(url)
242
+ return MessageResult(url)