snowflake-cli 2.8.2__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 (240) hide show
  1. snowflake/cli/__about__.py +17 -0
  2. snowflake/cli/__init__.py +13 -0
  3. snowflake/cli/api/__init__.py +48 -0
  4. snowflake/cli/api/cli_global_context.py +390 -0
  5. snowflake/cli/api/commands/__init__.py +13 -0
  6. snowflake/cli/api/commands/alias.py +23 -0
  7. snowflake/cli/api/commands/decorators.py +354 -0
  8. snowflake/cli/api/commands/execution_metadata.py +40 -0
  9. snowflake/cli/api/commands/experimental_behaviour.py +19 -0
  10. snowflake/cli/api/commands/flags.py +662 -0
  11. snowflake/cli/api/commands/project_initialisation.py +65 -0
  12. snowflake/cli/api/commands/snow_typer.py +237 -0
  13. snowflake/cli/api/commands/typer_pre_execute.py +26 -0
  14. snowflake/cli/api/config.py +348 -0
  15. snowflake/cli/api/console/__init__.py +17 -0
  16. snowflake/cli/api/console/abc.py +89 -0
  17. snowflake/cli/api/console/console.py +134 -0
  18. snowflake/cli/api/console/enum.py +17 -0
  19. snowflake/cli/api/constants.py +79 -0
  20. snowflake/cli/api/errno.py +27 -0
  21. snowflake/cli/api/exceptions.py +164 -0
  22. snowflake/cli/api/feature_flags.py +55 -0
  23. snowflake/cli/api/identifiers.py +167 -0
  24. snowflake/cli/api/output/__init__.py +13 -0
  25. snowflake/cli/api/output/formats.py +20 -0
  26. snowflake/cli/api/output/types.py +118 -0
  27. snowflake/cli/api/plugins/__init__.py +13 -0
  28. snowflake/cli/api/plugins/command/__init__.py +72 -0
  29. snowflake/cli/api/plugins/command/plugin_hook_specs.py +21 -0
  30. snowflake/cli/api/plugins/plugin_config.py +32 -0
  31. snowflake/cli/api/project/__init__.py +13 -0
  32. snowflake/cli/api/project/definition.py +84 -0
  33. snowflake/cli/api/project/definition_manager.py +134 -0
  34. snowflake/cli/api/project/errors.py +56 -0
  35. snowflake/cli/api/project/project_verification.py +23 -0
  36. snowflake/cli/api/project/schemas/__init__.py +13 -0
  37. snowflake/cli/api/project/schemas/entities/application_entity.py +44 -0
  38. snowflake/cli/api/project/schemas/entities/application_package_entity.py +66 -0
  39. snowflake/cli/api/project/schemas/entities/common.py +78 -0
  40. snowflake/cli/api/project/schemas/entities/entities.py +30 -0
  41. snowflake/cli/api/project/schemas/identifier_model.py +49 -0
  42. snowflake/cli/api/project/schemas/native_app/__init__.py +13 -0
  43. snowflake/cli/api/project/schemas/native_app/application.py +62 -0
  44. snowflake/cli/api/project/schemas/native_app/native_app.py +93 -0
  45. snowflake/cli/api/project/schemas/native_app/package.py +78 -0
  46. snowflake/cli/api/project/schemas/native_app/path_mapping.py +65 -0
  47. snowflake/cli/api/project/schemas/project_definition.py +199 -0
  48. snowflake/cli/api/project/schemas/snowpark/__init__.py +13 -0
  49. snowflake/cli/api/project/schemas/snowpark/argument.py +28 -0
  50. snowflake/cli/api/project/schemas/snowpark/callable.py +69 -0
  51. snowflake/cli/api/project/schemas/snowpark/snowpark.py +36 -0
  52. snowflake/cli/api/project/schemas/streamlit/__init__.py +13 -0
  53. snowflake/cli/api/project/schemas/streamlit/streamlit.py +46 -0
  54. snowflake/cli/api/project/schemas/template.py +77 -0
  55. snowflake/cli/api/project/schemas/updatable_model.py +194 -0
  56. snowflake/cli/api/project/util.py +261 -0
  57. snowflake/cli/api/rendering/__init__.py +13 -0
  58. snowflake/cli/api/rendering/jinja.py +112 -0
  59. snowflake/cli/api/rendering/project_definition_templates.py +39 -0
  60. snowflake/cli/api/rendering/project_templates.py +98 -0
  61. snowflake/cli/api/rendering/sql_templates.py +60 -0
  62. snowflake/cli/api/rest_api.py +172 -0
  63. snowflake/cli/api/sanitizers.py +43 -0
  64. snowflake/cli/api/secure_path.py +362 -0
  65. snowflake/cli/api/secure_utils.py +29 -0
  66. snowflake/cli/api/sql_execution.py +260 -0
  67. snowflake/cli/api/utils/__init__.py +13 -0
  68. snowflake/cli/api/utils/cursor.py +34 -0
  69. snowflake/cli/api/utils/definition_rendering.py +383 -0
  70. snowflake/cli/api/utils/dict_utils.py +73 -0
  71. snowflake/cli/api/utils/error_handling.py +23 -0
  72. snowflake/cli/api/utils/graph.py +97 -0
  73. snowflake/cli/api/utils/models.py +63 -0
  74. snowflake/cli/api/utils/naming_utils.py +13 -0
  75. snowflake/cli/api/utils/path_utils.py +36 -0
  76. snowflake/cli/api/utils/templating_functions.py +144 -0
  77. snowflake/cli/api/utils/types.py +35 -0
  78. snowflake/cli/app/__init__.py +22 -0
  79. snowflake/cli/app/__main__.py +31 -0
  80. snowflake/cli/app/api_impl/__init__.py +13 -0
  81. snowflake/cli/app/api_impl/plugin/__init__.py +13 -0
  82. snowflake/cli/app/api_impl/plugin/plugin_config_provider_impl.py +66 -0
  83. snowflake/cli/app/build_and_push.sh +8 -0
  84. snowflake/cli/app/cli_app.py +243 -0
  85. snowflake/cli/app/commands_registration/__init__.py +33 -0
  86. snowflake/cli/app/commands_registration/builtin_plugins.py +54 -0
  87. snowflake/cli/app/commands_registration/command_plugins_loader.py +169 -0
  88. snowflake/cli/app/commands_registration/commands_registration_with_callbacks.py +105 -0
  89. snowflake/cli/app/commands_registration/exception_logging.py +26 -0
  90. snowflake/cli/app/commands_registration/threadsafe.py +48 -0
  91. snowflake/cli/app/commands_registration/typer_registration.py +153 -0
  92. snowflake/cli/app/constants.py +19 -0
  93. snowflake/cli/app/dev/__init__.py +13 -0
  94. snowflake/cli/app/dev/commands_structure.py +48 -0
  95. snowflake/cli/app/dev/docs/__init__.py +13 -0
  96. snowflake/cli/app/dev/docs/commands_docs_generator.py +100 -0
  97. snowflake/cli/app/dev/docs/generator.py +35 -0
  98. snowflake/cli/app/dev/docs/project_definition_docs_generator.py +58 -0
  99. snowflake/cli/app/dev/docs/project_definition_generate_json_schema.py +227 -0
  100. snowflake/cli/app/dev/docs/template_utils.py +23 -0
  101. snowflake/cli/app/dev/docs/templates/definition_description.rst.jinja2 +38 -0
  102. snowflake/cli/app/dev/docs/templates/overview.rst.jinja2 +9 -0
  103. snowflake/cli/app/dev/docs/templates/usage.rst.jinja2 +57 -0
  104. snowflake/cli/app/dev/pycharm_remote_debug.py +46 -0
  105. snowflake/cli/app/loggers.py +199 -0
  106. snowflake/cli/app/main_typer.py +62 -0
  107. snowflake/cli/app/printing.py +181 -0
  108. snowflake/cli/app/snow_connector.py +243 -0
  109. snowflake/cli/app/telemetry.py +189 -0
  110. snowflake/cli/plugins/__init__.py +13 -0
  111. snowflake/cli/plugins/connection/__init__.py +13 -0
  112. snowflake/cli/plugins/connection/commands.py +330 -0
  113. snowflake/cli/plugins/connection/plugin_spec.py +30 -0
  114. snowflake/cli/plugins/connection/util.py +179 -0
  115. snowflake/cli/plugins/cortex/__init__.py +13 -0
  116. snowflake/cli/plugins/cortex/commands.py +327 -0
  117. snowflake/cli/plugins/cortex/constants.py +17 -0
  118. snowflake/cli/plugins/cortex/manager.py +189 -0
  119. snowflake/cli/plugins/cortex/plugin_spec.py +30 -0
  120. snowflake/cli/plugins/cortex/types.py +22 -0
  121. snowflake/cli/plugins/git/__init__.py +13 -0
  122. snowflake/cli/plugins/git/commands.py +354 -0
  123. snowflake/cli/plugins/git/manager.py +105 -0
  124. snowflake/cli/plugins/git/plugin_spec.py +30 -0
  125. snowflake/cli/plugins/init/__init__.py +13 -0
  126. snowflake/cli/plugins/init/commands.py +248 -0
  127. snowflake/cli/plugins/init/plugin_spec.py +30 -0
  128. snowflake/cli/plugins/nativeapp/__init__.py +13 -0
  129. snowflake/cli/plugins/nativeapp/artifacts.py +742 -0
  130. snowflake/cli/plugins/nativeapp/codegen/__init__.py +13 -0
  131. snowflake/cli/plugins/nativeapp/codegen/artifact_processor.py +91 -0
  132. snowflake/cli/plugins/nativeapp/codegen/compiler.py +130 -0
  133. snowflake/cli/plugins/nativeapp/codegen/sandbox.py +306 -0
  134. snowflake/cli/plugins/nativeapp/codegen/setup/native_app_setup_processor.py +172 -0
  135. snowflake/cli/plugins/nativeapp/codegen/setup/setup_driver.py.source +56 -0
  136. snowflake/cli/plugins/nativeapp/codegen/snowpark/callback_source.py.jinja +181 -0
  137. snowflake/cli/plugins/nativeapp/codegen/snowpark/extension_function_utils.py +217 -0
  138. snowflake/cli/plugins/nativeapp/codegen/snowpark/models.py +61 -0
  139. snowflake/cli/plugins/nativeapp/codegen/snowpark/python_processor.py +528 -0
  140. snowflake/cli/plugins/nativeapp/commands.py +439 -0
  141. snowflake/cli/plugins/nativeapp/common_flags.py +44 -0
  142. snowflake/cli/plugins/nativeapp/constants.py +27 -0
  143. snowflake/cli/plugins/nativeapp/exceptions.py +122 -0
  144. snowflake/cli/plugins/nativeapp/feature_flags.py +24 -0
  145. snowflake/cli/plugins/nativeapp/init.py +345 -0
  146. snowflake/cli/plugins/nativeapp/manager.py +823 -0
  147. snowflake/cli/plugins/nativeapp/plugin_spec.py +30 -0
  148. snowflake/cli/plugins/nativeapp/policy.py +50 -0
  149. snowflake/cli/plugins/nativeapp/project_model.py +195 -0
  150. snowflake/cli/plugins/nativeapp/run_processor.py +389 -0
  151. snowflake/cli/plugins/nativeapp/teardown_processor.py +301 -0
  152. snowflake/cli/plugins/nativeapp/utils.py +98 -0
  153. snowflake/cli/plugins/nativeapp/v2_conversions/v2_to_v1_decorator.py +135 -0
  154. snowflake/cli/plugins/nativeapp/version/__init__.py +13 -0
  155. snowflake/cli/plugins/nativeapp/version/commands.py +170 -0
  156. snowflake/cli/plugins/nativeapp/version/version_processor.py +362 -0
  157. snowflake/cli/plugins/notebook/__init__.py +13 -0
  158. snowflake/cli/plugins/notebook/commands.py +85 -0
  159. snowflake/cli/plugins/notebook/exceptions.py +20 -0
  160. snowflake/cli/plugins/notebook/manager.py +71 -0
  161. snowflake/cli/plugins/notebook/plugin_spec.py +30 -0
  162. snowflake/cli/plugins/notebook/types.py +15 -0
  163. snowflake/cli/plugins/object/__init__.py +13 -0
  164. snowflake/cli/plugins/object/command_aliases.py +95 -0
  165. snowflake/cli/plugins/object/commands.py +181 -0
  166. snowflake/cli/plugins/object/common.py +85 -0
  167. snowflake/cli/plugins/object/manager.py +97 -0
  168. snowflake/cli/plugins/object/plugin_spec.py +30 -0
  169. snowflake/cli/plugins/object_stage_deprecated/__init__.py +15 -0
  170. snowflake/cli/plugins/object_stage_deprecated/commands.py +122 -0
  171. snowflake/cli/plugins/object_stage_deprecated/plugin_spec.py +32 -0
  172. snowflake/cli/plugins/snowpark/__init__.py +13 -0
  173. snowflake/cli/plugins/snowpark/commands.py +546 -0
  174. snowflake/cli/plugins/snowpark/common.py +307 -0
  175. snowflake/cli/plugins/snowpark/manager.py +109 -0
  176. snowflake/cli/plugins/snowpark/models.py +157 -0
  177. snowflake/cli/plugins/snowpark/package/__init__.py +13 -0
  178. snowflake/cli/plugins/snowpark/package/anaconda_packages.py +233 -0
  179. snowflake/cli/plugins/snowpark/package/commands.py +256 -0
  180. snowflake/cli/plugins/snowpark/package/manager.py +44 -0
  181. snowflake/cli/plugins/snowpark/package/utils.py +26 -0
  182. snowflake/cli/plugins/snowpark/package_utils.py +354 -0
  183. snowflake/cli/plugins/snowpark/plugin_spec.py +30 -0
  184. snowflake/cli/plugins/snowpark/snowpark_package_paths.py +65 -0
  185. snowflake/cli/plugins/snowpark/snowpark_shared.py +95 -0
  186. snowflake/cli/plugins/snowpark/zipper.py +81 -0
  187. snowflake/cli/plugins/spcs/__init__.py +35 -0
  188. snowflake/cli/plugins/spcs/common.py +99 -0
  189. snowflake/cli/plugins/spcs/compute_pool/__init__.py +13 -0
  190. snowflake/cli/plugins/spcs/compute_pool/commands.py +241 -0
  191. snowflake/cli/plugins/spcs/compute_pool/manager.py +121 -0
  192. snowflake/cli/plugins/spcs/image_registry/__init__.py +13 -0
  193. snowflake/cli/plugins/spcs/image_registry/commands.py +65 -0
  194. snowflake/cli/plugins/spcs/image_registry/manager.py +105 -0
  195. snowflake/cli/plugins/spcs/image_repository/__init__.py +13 -0
  196. snowflake/cli/plugins/spcs/image_repository/commands.py +202 -0
  197. snowflake/cli/plugins/spcs/image_repository/manager.py +84 -0
  198. snowflake/cli/plugins/spcs/jobs/__init__.py +13 -0
  199. snowflake/cli/plugins/spcs/jobs/commands.py +78 -0
  200. snowflake/cli/plugins/spcs/jobs/manager.py +53 -0
  201. snowflake/cli/plugins/spcs/plugin_spec.py +30 -0
  202. snowflake/cli/plugins/spcs/services/__init__.py +13 -0
  203. snowflake/cli/plugins/spcs/services/commands.py +312 -0
  204. snowflake/cli/plugins/spcs/services/manager.py +170 -0
  205. snowflake/cli/plugins/sql/__init__.py +13 -0
  206. snowflake/cli/plugins/sql/commands.py +83 -0
  207. snowflake/cli/plugins/sql/manager.py +92 -0
  208. snowflake/cli/plugins/sql/plugin_spec.py +30 -0
  209. snowflake/cli/plugins/sql/snowsql_templating.py +28 -0
  210. snowflake/cli/plugins/stage/__init__.py +13 -0
  211. snowflake/cli/plugins/stage/commands.py +263 -0
  212. snowflake/cli/plugins/stage/diff.py +326 -0
  213. snowflake/cli/plugins/stage/manager.py +577 -0
  214. snowflake/cli/plugins/stage/md5.py +160 -0
  215. snowflake/cli/plugins/stage/plugin_spec.py +30 -0
  216. snowflake/cli/plugins/streamlit/__init__.py +13 -0
  217. snowflake/cli/plugins/streamlit/commands.py +179 -0
  218. snowflake/cli/plugins/streamlit/manager.py +222 -0
  219. snowflake/cli/plugins/streamlit/plugin_spec.py +30 -0
  220. snowflake/cli/plugins/workspace/__init__.py +13 -0
  221. snowflake/cli/plugins/workspace/commands.py +35 -0
  222. snowflake/cli/plugins/workspace/plugin_spec.py +30 -0
  223. snowflake/cli/templates/default_snowpark/.gitignore +4 -0
  224. snowflake/cli/templates/default_snowpark/app/__init__.py +0 -0
  225. snowflake/cli/templates/default_snowpark/app/common.py +2 -0
  226. snowflake/cli/templates/default_snowpark/app/functions.py +15 -0
  227. snowflake/cli/templates/default_snowpark/app/procedures.py +22 -0
  228. snowflake/cli/templates/default_snowpark/requirements.txt +1 -0
  229. snowflake/cli/templates/default_snowpark/snowflake.yml +23 -0
  230. snowflake/cli/templates/default_streamlit/.gitignore +4 -0
  231. snowflake/cli/templates/default_streamlit/common/hello.py +2 -0
  232. snowflake/cli/templates/default_streamlit/environment.yml +6 -0
  233. snowflake/cli/templates/default_streamlit/pages/my_page.py +3 -0
  234. snowflake/cli/templates/default_streamlit/snowflake.yml +10 -0
  235. snowflake/cli/templates/default_streamlit/streamlit_app.py +4 -0
  236. snowflake_cli-2.8.2.dist-info/METADATA +325 -0
  237. snowflake_cli-2.8.2.dist-info/RECORD +240 -0
  238. snowflake_cli-2.8.2.dist-info/WHEEL +4 -0
  239. snowflake_cli-2.8.2.dist-info/entry_points.txt +2 -0
  240. snowflake_cli-2.8.2.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,330 @@
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
+ import os.path
19
+
20
+ import typer
21
+ from click import ClickException, Context, Parameter # type: ignore
22
+ from click.core import ParameterSource # type: ignore
23
+ from click.types import StringParamType
24
+ from snowflake.cli.api.cli_global_context import cli_context
25
+ from snowflake.cli.api.commands.flags import (
26
+ PLAIN_PASSWORD_MSG,
27
+ )
28
+ from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
29
+ from snowflake.cli.api.config import (
30
+ ConnectionConfig,
31
+ add_connection,
32
+ connection_exists,
33
+ get_all_connections,
34
+ get_connection_dict,
35
+ get_default_connection_name,
36
+ set_config_value,
37
+ )
38
+ from snowflake.cli.api.console import cli_console
39
+ from snowflake.cli.api.constants import ObjectType
40
+ from snowflake.cli.api.output.types import (
41
+ CollectionResult,
42
+ CommandResult,
43
+ MessageResult,
44
+ ObjectResult,
45
+ )
46
+ from snowflake.cli.plugins.object.manager import ObjectManager
47
+ from snowflake.connector import ProgrammingError
48
+ from snowflake.connector.config_manager import CONFIG_MANAGER
49
+
50
+ app = SnowTyperFactory(
51
+ name="connection",
52
+ help="Manages connections to Snowflake.",
53
+ )
54
+ log = logging.getLogger(__name__)
55
+
56
+
57
+ class EmptyInput:
58
+ def __repr__(self):
59
+ return "optional"
60
+
61
+
62
+ class OptionalPrompt(StringParamType):
63
+ def convert(self, value, param, ctx):
64
+ return None if isinstance(value, EmptyInput) else value
65
+
66
+
67
+ def _mask_password(connection_params: dict):
68
+ if "password" in connection_params:
69
+ connection_params["password"] = "****"
70
+ return connection_params
71
+
72
+
73
+ @app.command(name="list")
74
+ def list_connections(**options) -> CommandResult:
75
+ """
76
+ Lists configured connections.
77
+ """
78
+ connections = get_all_connections()
79
+ default_connection = get_default_connection_name()
80
+ result = (
81
+ {
82
+ "connection_name": connection_name,
83
+ "parameters": _mask_password(
84
+ connection_config.to_dict_of_known_non_empty_values()
85
+ ),
86
+ "is_default": connection_name == default_connection,
87
+ }
88
+ for connection_name, connection_config in connections.items()
89
+ )
90
+ return CollectionResult(result)
91
+
92
+
93
+ def require_integer(field_name: str):
94
+ def callback(value: str):
95
+ if value is None:
96
+ return None
97
+ if value.isdigit():
98
+ return value
99
+ raise ClickException(f"Value of {field_name} must be integer")
100
+
101
+ return callback
102
+
103
+
104
+ def _password_callback(ctx: Context, param: Parameter, value: str):
105
+ if value and ctx.get_parameter_source(param.name) == ParameterSource.COMMANDLINE: # type: ignore
106
+ cli_console.warning(PLAIN_PASSWORD_MSG)
107
+
108
+ return value
109
+
110
+
111
+ @app.command()
112
+ def add(
113
+ connection_name: str = typer.Option(
114
+ None,
115
+ "--connection-name",
116
+ "-n",
117
+ prompt="Name for this connection",
118
+ help="Name of the new connection.",
119
+ show_default=False,
120
+ ),
121
+ account: str = typer.Option(
122
+ None,
123
+ "--account",
124
+ "-a",
125
+ "--accountname",
126
+ prompt="Snowflake account name",
127
+ help="Account name to use when authenticating with Snowflake.",
128
+ show_default=False,
129
+ ),
130
+ user: str = typer.Option(
131
+ None,
132
+ "--user",
133
+ "-u",
134
+ "--username",
135
+ prompt="Snowflake username",
136
+ show_default=False,
137
+ help="Username to connect to Snowflake.",
138
+ ),
139
+ password: str = typer.Option(
140
+ EmptyInput(),
141
+ "--password",
142
+ "-p",
143
+ click_type=OptionalPrompt(),
144
+ callback=_password_callback,
145
+ prompt="Snowflake password",
146
+ help="Snowflake password.",
147
+ hide_input=True,
148
+ ),
149
+ role: str = typer.Option(
150
+ EmptyInput(),
151
+ "--role",
152
+ "-r",
153
+ click_type=OptionalPrompt(),
154
+ prompt="Role for the connection",
155
+ help="Role to use on Snowflake.",
156
+ ),
157
+ warehouse: str = typer.Option(
158
+ EmptyInput(),
159
+ "--warehouse",
160
+ "-w",
161
+ click_type=OptionalPrompt(),
162
+ prompt="Warehouse for the connection",
163
+ help="Warehouse to use on Snowflake.",
164
+ ),
165
+ database: str = typer.Option(
166
+ EmptyInput(),
167
+ "--database",
168
+ "-d",
169
+ click_type=OptionalPrompt(),
170
+ prompt="Database for the connection",
171
+ help="Database to use on Snowflake.",
172
+ ),
173
+ schema: str = typer.Option(
174
+ EmptyInput(),
175
+ "--schema",
176
+ "-s",
177
+ click_type=OptionalPrompt(),
178
+ prompt="Schema for the connection",
179
+ help="Schema to use on Snowflake.",
180
+ ),
181
+ host: str = typer.Option(
182
+ EmptyInput(),
183
+ "--host",
184
+ "-h",
185
+ click_type=OptionalPrompt(),
186
+ prompt="Connection host",
187
+ help="Host name the connection attempts to connect to Snowflake.",
188
+ ),
189
+ port: int = typer.Option(
190
+ EmptyInput(),
191
+ "--port",
192
+ "-P",
193
+ click_type=OptionalPrompt(),
194
+ prompt="Connection port",
195
+ help="Port to communicate with on the host.",
196
+ callback=require_integer(field_name="port"),
197
+ ),
198
+ region: str = typer.Option(
199
+ EmptyInput(),
200
+ "--region",
201
+ "-R",
202
+ click_type=OptionalPrompt(),
203
+ prompt="Snowflake region",
204
+ help="Region name if not the default Snowflake deployment.",
205
+ ),
206
+ authenticator: str = typer.Option(
207
+ EmptyInput(),
208
+ "--authenticator",
209
+ "-A",
210
+ click_type=OptionalPrompt(),
211
+ prompt="Authentication method",
212
+ help="Chosen authenticator, if other than password-based",
213
+ ),
214
+ private_key_path: str = typer.Option(
215
+ EmptyInput(),
216
+ "--private-key",
217
+ "-k",
218
+ click_type=OptionalPrompt(),
219
+ prompt="Path to private key file",
220
+ help="Path to file containing private key",
221
+ ),
222
+ token_file_path: str = typer.Option(
223
+ EmptyInput(),
224
+ "--token-file-path",
225
+ "-t",
226
+ click_type=OptionalPrompt(),
227
+ prompt="Path to token file",
228
+ help="Path to file with an OAuth token that should be used when connecting to Snowflake",
229
+ ),
230
+ set_as_default: bool = typer.Option(
231
+ False,
232
+ "--default",
233
+ is_flag=True,
234
+ help="If provided the connection will be configured as default connection.",
235
+ ),
236
+ **options,
237
+ ) -> CommandResult:
238
+ """Adds a connection to configuration file."""
239
+ if connection_exists(connection_name):
240
+ raise ClickException(f"Connection {connection_name} already exists")
241
+
242
+ add_connection(
243
+ connection_name,
244
+ ConnectionConfig(
245
+ account=account,
246
+ user=user,
247
+ password=password,
248
+ host=host,
249
+ region=region,
250
+ port=port,
251
+ database=database,
252
+ schema=schema,
253
+ warehouse=warehouse,
254
+ role=role,
255
+ authenticator=authenticator,
256
+ private_key_path=private_key_path,
257
+ token_file_path=token_file_path,
258
+ ),
259
+ )
260
+ if set_as_default:
261
+ set_config_value(
262
+ section=None, key="default_connection_name", value=connection_name
263
+ )
264
+
265
+ return MessageResult(
266
+ f"Wrote new connection {connection_name} to {CONFIG_MANAGER.file_path}"
267
+ )
268
+
269
+
270
+ @app.command(requires_connection=True)
271
+ def test(
272
+ **options,
273
+ ) -> CommandResult:
274
+ """
275
+ Tests the connection to Snowflake.
276
+ """
277
+
278
+ # Test connection
279
+ conn = cli_context.connection
280
+
281
+ # Test session attributes
282
+ om = ObjectManager()
283
+ try:
284
+ # "use database" operation changes schema to default "public",
285
+ # so to test schema set up by user we need to copy it here:
286
+ schema = conn.schema
287
+
288
+ if conn.role:
289
+ om.use(object_type=ObjectType.ROLE, name=f'"{conn.role}"')
290
+ if conn.database:
291
+ om.use(object_type=ObjectType.DATABASE, name=f'"{conn.database}"')
292
+ if schema:
293
+ om.use(object_type=ObjectType.SCHEMA, name=f'"{schema}"')
294
+ if conn.warehouse:
295
+ om.use(object_type=ObjectType.WAREHOUSE, name=f'"{conn.warehouse}"')
296
+
297
+ except ProgrammingError as err:
298
+ raise ClickException(str(err))
299
+
300
+ conn_ctx = cli_context.connection_context
301
+ result = {
302
+ "Connection name": conn_ctx.connection_name,
303
+ "Status": "OK",
304
+ "Host": conn.host,
305
+ "Account": conn.account,
306
+ "User": conn.user,
307
+ "Role": f'{conn.role or "not set"}',
308
+ "Database": f'{conn.database or "not set"}',
309
+ "Warehouse": f'{conn.warehouse or "not set"}',
310
+ }
311
+
312
+ if conn_ctx.enable_diag:
313
+ result["Diag Report Location"] = os.path.join(
314
+ conn_ctx.diag_log_path, "SnowflakeConnectionTestReport.txt"
315
+ )
316
+
317
+ return ObjectResult(result)
318
+
319
+
320
+ @app.command(requires_connection=False)
321
+ def set_default(
322
+ name: str = typer.Argument(
323
+ help="Name of the connection, as defined in your `config.toml`"
324
+ ),
325
+ **options,
326
+ ):
327
+ """Changes default connection to provided value."""
328
+ get_connection_dict(connection_name=name)
329
+ set_config_value(section=None, key="default_connection_name", value=name)
330
+ return MessageResult(f"Default connection set to: {name}")
@@ -0,0 +1,30 @@
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 snowflake.cli.api.plugins.command import (
16
+ SNOWCLI_ROOT_COMMAND_PATH,
17
+ CommandSpec,
18
+ CommandType,
19
+ plugin_hook_impl,
20
+ )
21
+ from snowflake.cli.plugins.connection import commands
22
+
23
+
24
+ @plugin_hook_impl
25
+ def command_spec():
26
+ return CommandSpec(
27
+ parent_command_path=SNOWCLI_ROOT_COMMAND_PATH,
28
+ command_type=CommandType.COMMAND_GROUP,
29
+ typer_instance=commands.app.create_instance(),
30
+ )
@@ -0,0 +1,179 @@
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 json
18
+ import logging
19
+
20
+ from click.exceptions import ClickException
21
+ from snowflake.connector import SnowflakeConnection
22
+ from snowflake.connector.cursor import DictCursor
23
+
24
+ log = logging.getLogger(__name__)
25
+
26
+ REGIONLESS_QUERY = """
27
+ select value['value'] as REGIONLESS from table(flatten(
28
+ input => parse_json(SYSTEM$BOOTSTRAP_DATA_REQUEST()),
29
+ path => 'clientParamsInfo'
30
+ )) where value['name'] = 'UI_SNOWSIGHT_ENABLE_REGIONLESS_REDIRECT';
31
+ """
32
+
33
+ ALLOWLIST_QUERY = "SELECT SYSTEM$ALLOWLIST()"
34
+ SNOWFLAKE_DEPLOYMENT = "SNOWFLAKE_DEPLOYMENT"
35
+ LOCAL_DEPLOYMENT_REGION: str = "us-west-2"
36
+
37
+
38
+ class MissingConnectionAccountError(ClickException):
39
+ def __init__(self, conn: SnowflakeConnection):
40
+ super().__init__(
41
+ "Could not determine account by system call, configured account name, or configured host. Connection: "
42
+ + repr(conn)
43
+ )
44
+
45
+
46
+ class MissingConnectionRegionError(ClickException):
47
+ def __init__(self, host: str | None):
48
+ super().__init__(
49
+ f"The connection host ({host}) was missing or not in "
50
+ "the expected format "
51
+ "(<account>.<deployment>.snowflakecomputing.com)"
52
+ )
53
+
54
+
55
+ def is_regionless_redirect(conn: SnowflakeConnection) -> bool:
56
+ """
57
+ Determines if the deployment this connection refers to uses
58
+ regionless URLs in Snowsight (/orgname/account) or regional URLs
59
+ (/region/account). If we cannot determine the correct value we
60
+ assume it's regionless, as this is true for most production deployments.
61
+ """
62
+ try:
63
+ *_, cursor = conn.execute_string(REGIONLESS_QUERY, cursor_class=DictCursor)
64
+ return cursor.fetchone()["REGIONLESS"].lower() == "true"
65
+ except:
66
+ log.warning(
67
+ "Cannot determine regionless redirect; assuming True.", exc_info=True
68
+ )
69
+ return True
70
+
71
+
72
+ def get_host_region(host: str) -> str | None:
73
+ """
74
+ Looks for hosts of form
75
+ <account>.[x.y.z].snowflakecomputing.com
76
+ Returns the three-part [region identifier] or None.
77
+ """
78
+ host_parts = host.split(".")
79
+ if host_parts[-1] == "local":
80
+ return LOCAL_DEPLOYMENT_REGION
81
+ elif len(host_parts) == 6:
82
+ return ".".join(host_parts[1:4])
83
+ return None
84
+
85
+
86
+ def guess_regioned_host_from_allowlist(conn: SnowflakeConnection) -> str | None:
87
+ """
88
+ Use SYSTEM$ALLOWLIST to find a regioned host (<account>.x.y.z.snowflakecomputing.com)
89
+ that corresponds to the given Snowflake connection object.
90
+ """
91
+ try:
92
+ *_, cursor = conn.execute_string(ALLOWLIST_QUERY, cursor_class=DictCursor)
93
+ allowlist_tuples = json.loads(cursor.fetchone()["SYSTEM$ALLOWLIST()"])
94
+ for t in allowlist_tuples:
95
+ if t["type"] == SNOWFLAKE_DEPLOYMENT:
96
+ if get_host_region(t["host"]) is not None:
97
+ return t["host"]
98
+ except:
99
+ log.warning(
100
+ "Could not call SYSTEM$ALLOWLIST; returning an empty guess.", exc_info=True
101
+ )
102
+ return None
103
+
104
+
105
+ def get_region(conn: SnowflakeConnection) -> str:
106
+ """
107
+ Get the region of the given connection, or raise MissingConnectionRegionError.
108
+ """
109
+ if conn.host:
110
+ if region := get_host_region(conn.host):
111
+ return region
112
+
113
+ if host := guess_regioned_host_from_allowlist(conn):
114
+ if region := get_host_region(host):
115
+ return region
116
+
117
+ raise MissingConnectionRegionError(host or conn.host)
118
+
119
+
120
+ def get_context(conn: SnowflakeConnection) -> str:
121
+ """
122
+ Determines the first part of the path in a Snowsight URL.
123
+ This could be a region or it could be an organization, depending
124
+ on whether or not the underlying deployment uses regionless URLs.
125
+ """
126
+ if is_regionless_redirect(conn):
127
+ *_, cursor = conn.execute_string(
128
+ f"select system$return_current_org_name()", cursor_class=DictCursor
129
+ )
130
+ return cursor.fetchone()["SYSTEM$RETURN_CURRENT_ORG_NAME()"]
131
+
132
+ return get_region(conn)
133
+
134
+
135
+ def get_account(conn: SnowflakeConnection) -> str:
136
+ """
137
+ Determines the account that this connection refers to.
138
+ """
139
+ try:
140
+ *_, cursor = conn.execute_string(
141
+ f"select current_account_name()", cursor_class=DictCursor
142
+ )
143
+ return cursor.fetchone()["CURRENT_ACCOUNT_NAME()"].lower()
144
+ except Exception as e:
145
+ # try to extract the account from the connection information
146
+ if conn.account:
147
+ return conn.account
148
+
149
+ if conn.host:
150
+ host_parts = conn.host.split(".")
151
+ return host_parts[0]
152
+
153
+ raise MissingConnectionAccountError(conn)
154
+
155
+
156
+ def get_snowsight_host(conn: SnowflakeConnection) -> str:
157
+ try:
158
+ *_, cursor = conn.execute_string(
159
+ f"select system$get_snowsight_host()", cursor_class=DictCursor
160
+ )
161
+ return cursor.fetchone()["SYSTEM$GET_SNOWSIGHT_HOST()"]
162
+ except Exception as e:
163
+ # if we cannot determine the host, assume we're on prod
164
+ return "https://app.snowflake.com"
165
+
166
+
167
+ def make_snowsight_url(conn: SnowflakeConnection, path: str) -> str:
168
+ """
169
+ Returns a URL on the correct Snowsight instance for the connected account.
170
+ The path that is passed in must already be properly URL-encoded, and
171
+ can optionally contain a hash/fragment (e.g. #).
172
+
173
+ See also identifier_for_url.
174
+ """
175
+ snowsight_host = get_snowsight_host(conn)
176
+ deployment = get_context(conn)
177
+ account = get_account(conn)
178
+ path_with_slash = path if path.startswith("/") else f"/{path}"
179
+ return f"{snowsight_host}/{deployment}/{account}{path_with_slash}"
@@ -0,0 +1,13 @@
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.