snowflake-cli-labs 2.8.0rc0__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 (220) 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/errors.py +16 -1
  113. snowflake/cli/api/project/project_verification.py +3 -3
  114. snowflake/cli/api/project/schemas/entities/{application_entity.py → application_entity_model.py} +21 -9
  115. snowflake/cli/api/project/schemas/entities/{application_package_entity.py → application_package_entity_model.py} +26 -15
  116. snowflake/cli/api/project/schemas/entities/common.py +80 -6
  117. snowflake/cli/api/project/schemas/entities/entities.py +38 -8
  118. snowflake/cli/api/project/schemas/entities/snowpark_entity.py +176 -0
  119. snowflake/cli/api/project/schemas/entities/streamlit_entity_model.py +73 -0
  120. snowflake/cli/api/project/schemas/identifier_model.py +10 -1
  121. snowflake/cli/api/project/schemas/native_app/application.py +8 -9
  122. snowflake/cli/api/project/schemas/native_app/package.py +7 -1
  123. snowflake/cli/api/project/schemas/project_definition.py +97 -23
  124. snowflake/cli/api/project/schemas/updatable_model.py +11 -3
  125. snowflake/cli/api/project/util.py +23 -6
  126. snowflake/cli/api/rendering/jinja.py +28 -8
  127. snowflake/cli/api/rendering/sql_templates.py +41 -12
  128. snowflake/cli/api/secure_path.py +3 -0
  129. snowflake/cli/api/sql_execution.py +35 -19
  130. snowflake/cli/api/utils/definition_rendering.py +14 -2
  131. {snowflake_cli_labs-2.8.0rc0.dist-info → snowflake_cli_labs-3.0.0rc0.dist-info}/METADATA +12 -12
  132. snowflake_cli_labs-3.0.0rc0.dist-info/RECORD +234 -0
  133. snowflake_cli_labs-3.0.0rc0.dist-info/entry_points.txt +2 -0
  134. snowflake/cli/api/commands/project_initialisation.py +0 -65
  135. snowflake/cli/app/build_and_push.sh +0 -8
  136. snowflake/cli/plugins/nativeapp/manager.py +0 -819
  137. snowflake/cli/plugins/object_stage_deprecated/__init__.py +0 -15
  138. snowflake/cli/plugins/object_stage_deprecated/commands.py +0 -122
  139. snowflake/cli/plugins/object_stage_deprecated/plugin_spec.py +0 -32
  140. snowflake/cli/plugins/snowpark/commands.py +0 -548
  141. snowflake/cli/plugins/snowpark/common.py +0 -307
  142. snowflake/cli/plugins/snowpark/manager.py +0 -109
  143. snowflake/cli/plugins/snowpark/plugin_spec.py +0 -30
  144. snowflake/cli/plugins/snowpark/snowpark_package_paths.py +0 -65
  145. snowflake/cli/plugins/spcs/jobs/commands.py +0 -78
  146. snowflake/cli/plugins/spcs/jobs/manager.py +0 -53
  147. snowflake/cli/plugins/streamlit/commands.py +0 -186
  148. snowflake/cli/plugins/streamlit/plugin_spec.py +0 -30
  149. snowflake/cli/plugins/workspace/commands.py +0 -35
  150. snowflake/cli/templates/default_snowpark/.gitignore +0 -4
  151. snowflake/cli/templates/default_snowpark/app/__init__.py +0 -0
  152. snowflake/cli/templates/default_snowpark/app/common.py +0 -2
  153. snowflake/cli/templates/default_snowpark/app/functions.py +0 -15
  154. snowflake/cli/templates/default_snowpark/app/procedures.py +0 -22
  155. snowflake/cli/templates/default_snowpark/requirements.txt +0 -1
  156. snowflake/cli/templates/default_snowpark/snowflake.yml +0 -23
  157. snowflake/cli/templates/default_streamlit/.gitignore +0 -4
  158. snowflake/cli/templates/default_streamlit/common/hello.py +0 -2
  159. snowflake/cli/templates/default_streamlit/environment.yml +0 -6
  160. snowflake/cli/templates/default_streamlit/pages/my_page.py +0 -3
  161. snowflake/cli/templates/default_streamlit/snowflake.yml +0 -10
  162. snowflake/cli/templates/default_streamlit/streamlit_app.py +0 -4
  163. snowflake_cli_labs-2.8.0rc0.dist-info/RECORD +0 -240
  164. snowflake_cli_labs-2.8.0rc0.dist-info/entry_points.txt +0 -2
  165. /snowflake/cli/{app → _app}/__init__.py +0 -0
  166. /snowflake/cli/{app → _app}/api_impl/__init__.py +0 -0
  167. /snowflake/cli/{app → _app}/api_impl/plugin/__init__.py +0 -0
  168. /snowflake/cli/{app → _app}/api_impl/plugin/plugin_config_provider_impl.py +0 -0
  169. /snowflake/cli/{app → _app}/commands_registration/__init__.py +0 -0
  170. /snowflake/cli/{app → _app}/commands_registration/threadsafe.py +0 -0
  171. /snowflake/cli/{app → _app}/constants.py +0 -0
  172. /snowflake/cli/{app → _app}/dev/__init__.py +0 -0
  173. /snowflake/cli/{app → _app}/dev/commands_structure.py +0 -0
  174. /snowflake/cli/{app → _app}/dev/docs/__init__.py +0 -0
  175. /snowflake/cli/{app → _app}/dev/docs/project_definition_generate_json_schema.py +0 -0
  176. /snowflake/cli/{app → _app}/dev/docs/template_utils.py +0 -0
  177. /snowflake/cli/{app → _app}/dev/docs/templates/definition_description.rst.jinja2 +0 -0
  178. /snowflake/cli/{app → _app}/dev/docs/templates/overview.rst.jinja2 +0 -0
  179. /snowflake/cli/{app → _app}/dev/pycharm_remote_debug.py +0 -0
  180. /snowflake/cli/{app → _app}/loggers.py +0 -0
  181. /snowflake/cli/{plugins → _plugins}/__init__.py +0 -0
  182. /snowflake/cli/{plugins → _plugins}/connection/__init__.py +0 -0
  183. /snowflake/cli/{plugins → _plugins}/cortex/__init__.py +0 -0
  184. /snowflake/cli/{plugins → _plugins}/cortex/types.py +0 -0
  185. /snowflake/cli/{plugins → _plugins}/git/__init__.py +0 -0
  186. /snowflake/cli/{plugins → _plugins}/init/__init__.py +0 -0
  187. /snowflake/cli/{plugins → _plugins}/nativeapp/__init__.py +0 -0
  188. /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/__init__.py +0 -0
  189. /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/sandbox.py +0 -0
  190. /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/setup/setup_driver.py.source +0 -0
  191. /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/callback_source.py.jinja +0 -0
  192. /snowflake/cli/{plugins → _plugins}/nativeapp/codegen/snowpark/models.py +0 -0
  193. /snowflake/cli/{plugins → _plugins}/nativeapp/constants.py +0 -0
  194. /snowflake/cli/{plugins → _plugins}/nativeapp/exceptions.py +0 -0
  195. /snowflake/cli/{plugins → _plugins}/nativeapp/feature_flags.py +0 -0
  196. /snowflake/cli/{plugins → _plugins}/nativeapp/policy.py +0 -0
  197. /snowflake/cli/{plugins → _plugins}/nativeapp/utils.py +0 -0
  198. /snowflake/cli/{plugins → _plugins}/nativeapp/version/__init__.py +0 -0
  199. /snowflake/cli/{plugins → _plugins}/notebook/__init__.py +0 -0
  200. /snowflake/cli/{plugins → _plugins}/notebook/exceptions.py +0 -0
  201. /snowflake/cli/{plugins → _plugins}/object/__init__.py +0 -0
  202. /snowflake/cli/{plugins → _plugins}/object/common.py +0 -0
  203. /snowflake/cli/{plugins → _plugins}/snowpark/__init__.py +0 -0
  204. /snowflake/cli/{plugins → _plugins}/snowpark/package/__init__.py +0 -0
  205. /snowflake/cli/{plugins → _plugins}/snowpark/package/utils.py +0 -0
  206. /snowflake/cli/{plugins → _plugins}/spcs/common.py +0 -0
  207. /snowflake/cli/{plugins → _plugins}/spcs/compute_pool/__init__.py +0 -0
  208. /snowflake/cli/{plugins → _plugins}/spcs/image_registry/__init__.py +0 -0
  209. /snowflake/cli/{plugins → _plugins}/spcs/image_registry/manager.py +0 -0
  210. /snowflake/cli/{plugins → _plugins}/spcs/image_repository/__init__.py +0 -0
  211. /snowflake/cli/{plugins/spcs/jobs → _plugins/spcs/services}/__init__.py +0 -0
  212. /snowflake/cli/{plugins/spcs/services → _plugins/sql}/__init__.py +0 -0
  213. /snowflake/cli/{plugins → _plugins}/sql/snowsql_templating.py +0 -0
  214. /snowflake/cli/{plugins/sql → _plugins/stage}/__init__.py +0 -0
  215. /snowflake/cli/{plugins → _plugins}/stage/md5.py +0 -0
  216. /snowflake/cli/{plugins/stage → _plugins/streamlit}/__init__.py +0 -0
  217. /snowflake/cli/{plugins/streamlit → _plugins/workspace}/__init__.py +0 -0
  218. /snowflake/cli/{plugins/workspace → api/project/schemas/entities}/__init__.py +0 -0
  219. {snowflake_cli_labs-2.8.0rc0.dist-info → snowflake_cli_labs-3.0.0rc0.dist-info}/WHEEL +0 -0
  220. {snowflake_cli_labs-2.8.0rc0.dist-info → snowflake_cli_labs-3.0.0rc0.dist-info}/licenses/LICENSE +0 -0
@@ -18,15 +18,16 @@ from typing import List, Optional, Tuple
18
18
 
19
19
  import typer
20
20
  from click import ClickException
21
- from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
22
- from snowflake.cli.api.constants import ObjectType
23
- from snowflake.cli.plugins.object.commands import (
21
+ from snowflake.cli._plugins.object.commands import (
24
22
  ScopeOption,
25
23
  describe,
26
24
  drop,
27
25
  list_,
28
26
  scope_option, # noqa: F401
29
27
  )
28
+ from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
29
+ from snowflake.cli.api.constants import ObjectType
30
+ from snowflake.cli.api.identifiers import FQN
30
31
 
31
32
 
32
33
  def add_object_command_aliases(
@@ -72,7 +73,7 @@ def add_object_command_aliases(
72
73
  if "drop" not in ommit_commands:
73
74
 
74
75
  @app.command("drop", requires_connection=True)
75
- def drop_cmd(name: str = name_argument, **options):
76
+ def drop_cmd(name: FQN = name_argument, **options):
76
77
  return drop(
77
78
  object_type=object_type.value.cli_name,
78
79
  object_name=name,
@@ -84,7 +85,7 @@ def add_object_command_aliases(
84
85
  if "describe" not in ommit_commands:
85
86
 
86
87
  @app.command("describe", requires_connection=True)
87
- def describe_cmd(name: str = name_argument, **options):
88
+ def describe_cmd(name: FQN = name_argument, **options):
88
89
  return describe(
89
90
  object_type=object_type.value.cli_name,
90
91
  object_name=name,
@@ -18,12 +18,18 @@ from typing import List, Optional, Tuple
18
18
 
19
19
  import typer
20
20
  from click import ClickException
21
- from snowflake.cli.api.commands.flags import like_option, parse_key_value_variables
21
+ from snowflake.cli._plugins.object.manager import ObjectManager
22
+ from snowflake.cli.api.commands.flags import (
23
+ IdentifierType,
24
+ like_option,
25
+ )
22
26
  from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
27
+ from snowflake.cli.api.commands.utils import parse_key_value_variables
23
28
  from snowflake.cli.api.constants import SUPPORTED_OBJECTS, VALID_SCOPES
29
+ from snowflake.cli.api.exceptions import IncompatibleParametersError
30
+ from snowflake.cli.api.identifiers import FQN
24
31
  from snowflake.cli.api.output.types import MessageResult, QueryResult
25
32
  from snowflake.cli.api.project.util import is_valid_identifier
26
- from snowflake.cli.plugins.object.manager import ObjectManager
27
33
 
28
34
  app = SnowTyperFactory(
29
35
  name="object",
@@ -31,7 +37,9 @@ app = SnowTyperFactory(
31
37
  )
32
38
 
33
39
 
34
- NameArgument = typer.Argument(help="Name of the object")
40
+ NameArgument = typer.Argument(
41
+ help="Name of the object.", show_default=False, click_type=IdentifierType()
42
+ )
35
43
  ObjectArgument = typer.Argument(
36
44
  help="Type of object. For example table, database, compute-pool.",
37
45
  case_sensitive=False,
@@ -112,8 +120,8 @@ def list_(
112
120
  help=f"Drops Snowflake object of given name and type. {SUPPORTED_TYPES_MSG}",
113
121
  requires_connection=True,
114
122
  )
115
- def drop(object_type: str = ObjectArgument, object_name: str = NameArgument, **options):
116
- return QueryResult(ObjectManager().drop(object_type=object_type, name=object_name))
123
+ def drop(object_type: str = ObjectArgument, object_name: FQN = NameArgument, **options):
124
+ return QueryResult(ObjectManager().drop(object_type=object_type, fqn=object_name))
117
125
 
118
126
 
119
127
  # Image repository is the only supported object that does not have a DESCRIBE command.
@@ -125,10 +133,10 @@ DESCRIBE_SUPPORTED_TYPES_MSG = f"\n\nSupported types: {', '.join(obj for obj in
125
133
  requires_connection=True,
126
134
  )
127
135
  def describe(
128
- object_type: str = ObjectArgument, object_name: str = NameArgument, **options
136
+ object_type: str = ObjectArgument, object_name: FQN = NameArgument, **options
129
137
  ):
130
138
  return QueryResult(
131
- ObjectManager().describe(object_type=object_type, name=object_name)
139
+ ObjectManager().describe(object_type=object_type, fqn=object_name)
132
140
  )
133
141
 
134
142
 
@@ -146,9 +154,7 @@ def create(
146
154
  import json
147
155
 
148
156
  if object_attributes and object_json:
149
- raise ClickException(
150
- "Conflict: both object attributes and JSON definition are provided"
151
- )
157
+ raise IncompatibleParametersError(["object_attributes", "--json"])
152
158
 
153
159
  if object_json:
154
160
  object_data = json.loads(object_json)
@@ -22,6 +22,7 @@ from snowflake.cli.api.constants import (
22
22
  OBJECT_TO_NAMES,
23
23
  ObjectNames,
24
24
  )
25
+ from snowflake.cli.api.identifiers import FQN
25
26
  from snowflake.cli.api.rest_api import RestApi
26
27
  from snowflake.cli.api.sql_execution import SqlExecutionMixin
27
28
  from snowflake.connector import ProgrammingError
@@ -53,22 +54,22 @@ class ObjectManager(SqlExecutionMixin):
53
54
  query += f" in {scope[0].replace('-', ' ')} {scope[1]}"
54
55
  return self._execute_query(query, **kwargs)
55
56
 
56
- def drop(self, *, object_type: str, name: str) -> SnowflakeCursor:
57
+ def drop(self, *, object_type: str, fqn: FQN) -> SnowflakeCursor:
57
58
  object_name = _get_object_names(object_type).sf_name
58
- return self._execute_query(f"drop {object_name} {name}")
59
+ return self._execute_query(f"drop {object_name} {fqn.sql_identifier}")
59
60
 
60
- def describe(self, *, object_type: str, name: str):
61
+ def describe(self, *, object_type: str, fqn: FQN):
61
62
  # Image repository is the only supported object that does not have a DESCRIBE command.
62
63
  if object_type == "image-repository":
63
64
  raise ClickException(
64
65
  f"Describe is currently not supported for object of type image-repository"
65
66
  )
66
67
  object_name = _get_object_names(object_type).sf_name
67
- return self._execute_query(f"describe {object_name} {name}")
68
+ return self._execute_query(f"describe {object_name} {fqn.sql_identifier}")
68
69
 
69
- def object_exists(self, *, object_type: str, name: str):
70
+ def object_exists(self, *, object_type: str, fqn: FQN):
70
71
  try:
71
- self.describe(object_type=object_type, name=name)
72
+ self.describe(object_type=object_type, fqn=fqn)
72
73
  return True
73
74
  except ProgrammingError:
74
75
  return False
@@ -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.object 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.object import commands
22
22
 
23
23
 
24
24
  @plugin_hook_impl
@@ -0,0 +1,510 @@
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 collections import defaultdict
19
+ from typing import Dict, Optional, Set, Tuple
20
+
21
+ import typer
22
+ from click import ClickException, UsageError
23
+ from snowflake.cli._plugins.object.commands import (
24
+ describe as object_describe,
25
+ )
26
+ from snowflake.cli._plugins.object.commands import (
27
+ drop as object_drop,
28
+ )
29
+ from snowflake.cli._plugins.object.commands import (
30
+ list_ as object_list,
31
+ )
32
+ from snowflake.cli._plugins.object.commands import (
33
+ scope_option,
34
+ )
35
+ from snowflake.cli._plugins.object.manager import ObjectManager
36
+ from snowflake.cli._plugins.snowpark import package_utils
37
+ from snowflake.cli._plugins.snowpark.common import (
38
+ EntityToImportPathsMapping,
39
+ SnowparkEntities,
40
+ SnowparkObject,
41
+ SnowparkObjectManager,
42
+ StageToArtefactMapping,
43
+ )
44
+ from snowflake.cli._plugins.snowpark.package.anaconda_packages import (
45
+ AnacondaPackages,
46
+ AnacondaPackagesManager,
47
+ )
48
+ from snowflake.cli._plugins.snowpark.package.commands import app as package_app
49
+ from snowflake.cli._plugins.snowpark.snowpark_project_paths import (
50
+ SnowparkProjectPaths,
51
+ )
52
+ from snowflake.cli._plugins.snowpark.snowpark_shared import (
53
+ AllowSharedLibrariesOption,
54
+ IgnoreAnacondaOption,
55
+ IndexUrlOption,
56
+ SkipVersionCheckOption,
57
+ )
58
+ from snowflake.cli._plugins.snowpark.zipper import zip_dir
59
+ from snowflake.cli._plugins.stage.manager import StageManager
60
+ from snowflake.cli.api.cli_global_context import (
61
+ get_cli_context,
62
+ )
63
+ from snowflake.cli.api.commands.decorators import (
64
+ with_project_definition,
65
+ )
66
+ from snowflake.cli.api.commands.flags import (
67
+ ReplaceOption,
68
+ execution_identifier_argument,
69
+ identifier_argument,
70
+ like_option,
71
+ )
72
+ from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
73
+ from snowflake.cli.api.console import cli_console
74
+ from snowflake.cli.api.constants import (
75
+ DEFAULT_SIZE_LIMIT_MB,
76
+ )
77
+ from snowflake.cli.api.exceptions import (
78
+ NoProjectDefinitionError,
79
+ SecretsWithoutExternalAccessIntegrationError,
80
+ )
81
+ from snowflake.cli.api.identifiers import FQN
82
+ from snowflake.cli.api.output.types import (
83
+ CollectionResult,
84
+ CommandResult,
85
+ MessageResult,
86
+ SingleQueryResult,
87
+ )
88
+ from snowflake.cli.api.project.schemas.entities.snowpark_entity import (
89
+ FunctionEntityModel,
90
+ ProcedureEntityModel,
91
+ )
92
+ from snowflake.cli.api.project.schemas.project_definition import (
93
+ ProjectDefinition,
94
+ ProjectDefinitionV2,
95
+ )
96
+ from snowflake.cli.api.project.schemas.snowpark.callable import (
97
+ FunctionSchema,
98
+ ProcedureSchema,
99
+ )
100
+ from snowflake.cli.api.secure_path import SecurePath
101
+ from snowflake.connector import DictCursor, ProgrammingError
102
+ from snowflake.connector.cursor import SnowflakeCursor
103
+
104
+ log = logging.getLogger(__name__)
105
+
106
+ app = SnowTyperFactory(
107
+ name="snowpark",
108
+ help="Manages procedures and functions.",
109
+ )
110
+ app.add_typer(package_app)
111
+
112
+ ObjectTypeArgument = typer.Argument(
113
+ help="Type of Snowpark object",
114
+ case_sensitive=False,
115
+ show_default=False,
116
+ )
117
+ IdentifierArgument = identifier_argument(
118
+ "function/procedure",
119
+ example="hello(int, string)",
120
+ )
121
+ LikeOption = like_option(
122
+ help_example='`list function --like "my%"` lists all functions that begin with “my”',
123
+ )
124
+
125
+
126
+ @app.command("deploy", requires_connection=True)
127
+ @with_project_definition()
128
+ def deploy(
129
+ replace: bool = ReplaceOption(
130
+ help="Replaces procedure or function, even if no detected changes to metadata"
131
+ ),
132
+ **options,
133
+ ) -> CommandResult:
134
+ """
135
+ Deploys procedures and functions defined in project. Deploying the project alters all objects defined in it.
136
+ By default, if any of the objects exist already the commands will fail unless `--replace` flag is provided.
137
+ Required artifacts are deployed before creating functions or procedures. Dependencies are deployed once to
138
+ every stage specified in definitions.
139
+ """
140
+ cli_context = get_cli_context()
141
+ pd = _get_v2_project_definition(cli_context)
142
+
143
+ snowpark_entities = get_snowpark_entities(pd)
144
+ project_paths = SnowparkProjectPaths(
145
+ project_root=cli_context.project_root,
146
+ )
147
+
148
+ with cli_console.phase("Performing initial validation"):
149
+ if not snowpark_entities:
150
+ raise ClickException(
151
+ "No procedures or functions were specified in the project definition."
152
+ )
153
+ validate_all_artifacts_exists(
154
+ project_paths=project_paths, snowpark_entities=snowpark_entities
155
+ )
156
+
157
+ # Validate current state
158
+ with cli_console.phase("Checking remote state"):
159
+ om = ObjectManager()
160
+ _check_if_all_defined_integrations_exists(om, snowpark_entities)
161
+ existing_objects = check_for_existing_objects(om, replace, snowpark_entities)
162
+
163
+ with cli_console.phase("Preparing required stages and artifacts"):
164
+ entities_to_imports_map, stages_to_artifact_map = build_artifacts_mappings(
165
+ project_paths=project_paths,
166
+ snowpark_entities=snowpark_entities,
167
+ )
168
+
169
+ create_stages_and_upload_artifacts(stages_to_artifact_map)
170
+
171
+ # Create snowpark entities
172
+ with cli_console.phase("Creating Snowpark entities"):
173
+ snowpark_manager = SnowparkObjectManager()
174
+ snowflake_dependencies = _read_snowflake_requirements_file(
175
+ project_paths.snowflake_requirements
176
+ )
177
+ deploy_status = []
178
+ for entity in snowpark_entities.values():
179
+ operation_result = snowpark_manager.deploy_entity(
180
+ entity=entity,
181
+ existing_objects=existing_objects,
182
+ snowflake_dependencies=snowflake_dependencies,
183
+ entities_to_artifact_map=entities_to_imports_map,
184
+ )
185
+ deploy_status.append(operation_result)
186
+
187
+ return CollectionResult(deploy_status)
188
+
189
+
190
+ def validate_all_artifacts_exists(
191
+ project_paths: SnowparkProjectPaths, snowpark_entities: SnowparkEntities
192
+ ):
193
+ for key, entity in snowpark_entities.items():
194
+ for artefact in entity.artifacts:
195
+ path = project_paths.get_artefact_dto(artefact).post_build_path
196
+ if not path.exists():
197
+ raise UsageError(
198
+ f"Artefact {path} required for {entity.type} {key} does not exist."
199
+ )
200
+
201
+
202
+ def check_for_existing_objects(
203
+ om: ObjectManager, replace: bool, snowpark_entities: SnowparkEntities
204
+ ) -> Dict[str, SnowflakeCursor]:
205
+ existing_objects: Dict[str, SnowflakeCursor] = _find_existing_objects(
206
+ snowpark_entities, om
207
+ )
208
+ if existing_objects and not replace:
209
+ existing_entities = [snowpark_entities[e] for e in existing_objects]
210
+ msg = "Following objects already exists. Consider using --replace.\n"
211
+ msg += "\n".join(f"{e.type}: {e.entity_id}" for e in existing_entities)
212
+ raise ClickException(msg)
213
+ return existing_objects
214
+
215
+
216
+ def build_artifacts_mappings(
217
+ project_paths: SnowparkProjectPaths, snowpark_entities: SnowparkEntities
218
+ ) -> Tuple[EntityToImportPathsMapping, StageToArtefactMapping]:
219
+ stages_to_artifact_map: StageToArtefactMapping = defaultdict(set)
220
+ entities_to_imports_map: EntityToImportPathsMapping = defaultdict(set)
221
+ for entity_id, entity in snowpark_entities.items():
222
+ stage = entity.stage
223
+ required_artifacts = set()
224
+ for artefact in entity.artifacts:
225
+ artefact_dto = project_paths.get_artefact_dto(artefact)
226
+ required_artifacts.add(artefact_dto)
227
+ entities_to_imports_map[entity_id].add(artefact_dto.import_path(stage))
228
+ stages_to_artifact_map[stage].update(required_artifacts)
229
+
230
+ if project_paths.dependencies.exists():
231
+ deps_artefact = project_paths.get_dependencies_artefact()
232
+ stages_to_artifact_map[stage].add(deps_artefact)
233
+ entities_to_imports_map[entity_id].add(deps_artefact.import_path(stage))
234
+ return entities_to_imports_map, stages_to_artifact_map
235
+
236
+
237
+ def create_stages_and_upload_artifacts(stages_to_artifact_map: StageToArtefactMapping):
238
+ stage_manager = StageManager()
239
+ for stage, artifacts in stages_to_artifact_map.items():
240
+ cli_console.step(f"Creating (if not exists) stage: {stage}")
241
+ stage = FQN.from_stage(stage).using_context()
242
+ stage_manager.create(fqn=stage, comment="deployments managed by Snowflake CLI")
243
+ for artefact in artifacts:
244
+ cli_console.step(
245
+ f"Uploading {artefact.post_build_path.name} to {artefact.upload_path(stage)}"
246
+ )
247
+ stage_manager.put(
248
+ local_path=artefact.post_build_path,
249
+ stage_path=artefact.upload_path(stage),
250
+ overwrite=True,
251
+ )
252
+
253
+
254
+ def _find_existing_objects(
255
+ objects: SnowparkEntities,
256
+ om: ObjectManager,
257
+ ) -> Dict[str, SnowflakeCursor]:
258
+ existing_objects = {}
259
+ for entity_id, entity in objects.items():
260
+ identifier = entity.udf_sproc_identifier.identifier_with_arg_types
261
+ try:
262
+ current_state = om.describe(
263
+ object_type=entity.type,
264
+ fqn=FQN.from_string(identifier),
265
+ )
266
+ existing_objects[entity_id] = current_state
267
+ except ProgrammingError:
268
+ pass
269
+ return existing_objects
270
+
271
+
272
+ def _check_if_all_defined_integrations_exists(
273
+ om: ObjectManager,
274
+ snowpark_entities: SnowparkEntities,
275
+ ):
276
+ existing_integrations = {
277
+ i["name"].lower()
278
+ for i in om.show(object_type="integration", cursor_class=DictCursor, like=None)
279
+ if i["type"] == "EXTERNAL_ACCESS"
280
+ }
281
+ declared_integration: Set[str] = set()
282
+ for object_definition in snowpark_entities.values():
283
+ external_access_integrations = {
284
+ s.lower() for s in object_definition.external_access_integrations
285
+ }
286
+ secrets = [s.lower() for s in object_definition.secrets]
287
+
288
+ if not external_access_integrations and secrets:
289
+ raise SecretsWithoutExternalAccessIntegrationError(object_definition.fqn)
290
+
291
+ declared_integration = declared_integration | external_access_integrations
292
+
293
+ missing = declared_integration - existing_integrations
294
+ if missing:
295
+ raise ClickException(
296
+ f"Following external access integration does not exists in Snowflake: {', '.join(missing)}"
297
+ )
298
+
299
+
300
+ def _read_snowflake_requirements_file(file_path: SecurePath):
301
+ if not file_path.exists():
302
+ return []
303
+ return file_path.read_text(file_size_limit_mb=DEFAULT_SIZE_LIMIT_MB).splitlines()
304
+
305
+
306
+ @app.command("build", requires_connection=True)
307
+ @with_project_definition()
308
+ def build(
309
+ ignore_anaconda: bool = IgnoreAnacondaOption,
310
+ allow_shared_libraries: bool = AllowSharedLibrariesOption,
311
+ index_url: Optional[str] = IndexUrlOption,
312
+ skip_version_check: bool = SkipVersionCheckOption,
313
+ **options,
314
+ ) -> CommandResult:
315
+ """
316
+ Builds artifacts required for the Snowpark project. The artifacts can be used by `deploy` command.
317
+ For each directory in artifacts a .zip file is created. All non-anaconda dependencies are packaged in
318
+ dependencies.zip file.
319
+ """
320
+ cli_context = get_cli_context()
321
+ pd = _get_v2_project_definition(cli_context)
322
+
323
+ project_paths = SnowparkProjectPaths(
324
+ project_root=cli_context.project_root,
325
+ )
326
+
327
+ anaconda_packages_manager = AnacondaPackagesManager()
328
+
329
+ # Resolve dependencies
330
+ if project_paths.requirements.exists():
331
+ with (
332
+ cli_console.phase("Resolving dependencies from requirements.txt"),
333
+ SecurePath.temporary_directory() as temp_deps_dir,
334
+ ):
335
+ requirements = package_utils.parse_requirements(
336
+ requirements_file=project_paths.requirements,
337
+ )
338
+ anaconda_packages = (
339
+ AnacondaPackages.empty()
340
+ if ignore_anaconda
341
+ else anaconda_packages_manager.find_packages_available_in_snowflake_anaconda()
342
+ )
343
+ download_result = package_utils.download_unavailable_packages(
344
+ requirements=requirements,
345
+ target_dir=temp_deps_dir,
346
+ anaconda_packages=anaconda_packages,
347
+ skip_version_check=skip_version_check,
348
+ pip_index_url=index_url,
349
+ )
350
+ if not download_result.succeeded:
351
+ raise ClickException(download_result.error_message)
352
+
353
+ log.info("Checking to see if packages have shared (.so/.dll) libraries...")
354
+ if package_utils.detect_and_log_shared_libraries(
355
+ download_result.downloaded_packages_details
356
+ ):
357
+ if not allow_shared_libraries:
358
+ raise ClickException(
359
+ "Some packages contain shared (.so/.dll) libraries. "
360
+ "Try again with --allow-shared-libraries."
361
+ )
362
+ if download_result.anaconda_packages:
363
+ anaconda_packages.write_requirements_file_in_snowflake_format( # type: ignore
364
+ file_path=project_paths.snowflake_requirements,
365
+ requirements=download_result.anaconda_packages,
366
+ )
367
+
368
+ if any(temp_deps_dir.path.iterdir()):
369
+ cli_console.step(f"Creating {project_paths.dependencies.name}")
370
+ zip_dir(
371
+ source=temp_deps_dir.path,
372
+ dest_zip=project_paths.dependencies,
373
+ )
374
+ else:
375
+ cli_console.step(f"No external dependencies.")
376
+
377
+ artifacts = set()
378
+ for entity in get_snowpark_entities(pd).values():
379
+ artifacts.update(entity.artifacts)
380
+
381
+ with cli_console.phase("Preparing artifacts for source code"):
382
+ for artefact in artifacts:
383
+ artefact_dto = project_paths.get_artefact_dto(artefact)
384
+ artefact_dto.build()
385
+
386
+ return MessageResult(f"Build done.")
387
+
388
+
389
+ def get_snowpark_entities(
390
+ pd: ProjectDefinition,
391
+ ) -> Dict[str, ProcedureEntityModel | FunctionEntityModel]:
392
+ procedures: Dict[str, ProcedureEntityModel] = pd.get_entities_by_type("procedure")
393
+ functions: Dict[str, FunctionEntityModel] = pd.get_entities_by_type("function")
394
+ snowpark_entities = {**procedures, **functions}
395
+ return snowpark_entities
396
+
397
+
398
+ @app.command("execute", requires_connection=True)
399
+ def execute(
400
+ object_type: SnowparkObject = ObjectTypeArgument,
401
+ execution_identifier: str = execution_identifier_argument(
402
+ "procedure/function", "hello(1, 'world')"
403
+ ),
404
+ **options,
405
+ ) -> CommandResult:
406
+ """Executes a procedure or function in a specified environment."""
407
+ cursor = SnowparkObjectManager().execute(
408
+ execution_identifier=execution_identifier, object_type=object_type
409
+ )
410
+ return SingleQueryResult(cursor)
411
+
412
+
413
+ @app.command("list", requires_connection=True)
414
+ def list_(
415
+ object_type: SnowparkObject = ObjectTypeArgument,
416
+ like: str = LikeOption,
417
+ scope: Tuple[str, str] = scope_option(
418
+ help_example="`list function --in database my_db`"
419
+ ),
420
+ **options,
421
+ ):
422
+ """Lists all available procedures or functions."""
423
+ return object_list(object_type=object_type.value, like=like, scope=scope, **options)
424
+
425
+
426
+ @app.command("drop", requires_connection=True)
427
+ def drop(
428
+ object_type: SnowparkObject = ObjectTypeArgument,
429
+ identifier: FQN = IdentifierArgument,
430
+ **options,
431
+ ):
432
+ """Drop procedure or function."""
433
+ return object_drop(object_type=object_type.value, object_name=identifier, **options)
434
+
435
+
436
+ @app.command("describe", requires_connection=True)
437
+ def describe(
438
+ object_type: SnowparkObject = ObjectTypeArgument,
439
+ identifier: FQN = IdentifierArgument,
440
+ **options,
441
+ ):
442
+ """Provides description of a procedure or function."""
443
+ return object_describe(
444
+ object_type=object_type.value, object_name=identifier, **options
445
+ )
446
+
447
+
448
+ def migrate_v1_snowpark_to_v2(pd: ProjectDefinition):
449
+ if not pd.snowpark:
450
+ raise NoProjectDefinitionError(
451
+ project_type="snowpark", project_root=get_cli_context().project_root
452
+ )
453
+
454
+ artifact_mapping = {"src": pd.snowpark.src}
455
+ if pd.snowpark.project_name:
456
+ artifact_mapping["dest"] = pd.snowpark.project_name
457
+
458
+ snowpark_shared_mixin = "snowpark_shared"
459
+ data: dict = {
460
+ "definition_version": "2",
461
+ "mixins": {
462
+ snowpark_shared_mixin: {
463
+ "stage": pd.snowpark.stage_name,
464
+ "artifacts": [artifact_mapping],
465
+ }
466
+ },
467
+ "entities": {},
468
+ }
469
+
470
+ for index, entity in enumerate([*pd.snowpark.procedures, *pd.snowpark.functions]):
471
+ identifier = {"name": entity.name}
472
+ if entity.database is not None:
473
+ identifier["database"] = entity.database
474
+ if entity.schema_name is not None:
475
+ identifier["schema"] = entity.schema_name
476
+
477
+ if entity.name.startswith("<%") and entity.name.endswith("%>"):
478
+ entity_name = f"snowpark_entity_{index}"
479
+ else:
480
+ entity_name = entity.name
481
+
482
+ v2_entity = {
483
+ "type": "function" if isinstance(entity, FunctionSchema) else "procedure",
484
+ "stage": pd.snowpark.stage_name,
485
+ "handler": entity.handler,
486
+ "returns": entity.returns,
487
+ "signature": entity.signature,
488
+ "runtime": entity.runtime,
489
+ "external_access_integrations": entity.external_access_integrations,
490
+ "secrets": entity.secrets,
491
+ "imports": entity.imports,
492
+ "identifier": identifier,
493
+ "meta": {"use_mixins": [snowpark_shared_mixin]},
494
+ }
495
+ if isinstance(entity, ProcedureSchema):
496
+ v2_entity["execute_as_caller"] = entity.execute_as_caller
497
+
498
+ data["entities"][entity_name] = v2_entity
499
+
500
+ if hasattr(pd, "env") and pd.env:
501
+ data["env"] = {k: v for k, v in pd.env.items()}
502
+
503
+ return ProjectDefinitionV2(**data)
504
+
505
+
506
+ def _get_v2_project_definition(cli_context) -> ProjectDefinitionV2:
507
+ pd = cli_context.project_definition
508
+ if not pd.meets_version_requirement("2"):
509
+ pd = migrate_v1_snowpark_to_v2(pd)
510
+ return pd