snowflake-cli 3.2.2__py3-none-any.whl → 3.4.1__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 (97) hide show
  1. snowflake/cli/__about__.py +1 -1
  2. snowflake/cli/_app/__main__.py +2 -2
  3. snowflake/cli/_app/cli_app.py +224 -192
  4. snowflake/cli/_app/commands_registration/commands_registration_with_callbacks.py +1 -27
  5. snowflake/cli/_app/constants.py +4 -0
  6. snowflake/cli/_app/snow_connector.py +12 -0
  7. snowflake/cli/_app/telemetry.py +10 -3
  8. snowflake/cli/_plugins/connection/util.py +12 -19
  9. snowflake/cli/_plugins/cortex/commands.py +2 -4
  10. snowflake/cli/_plugins/git/manager.py +1 -1
  11. snowflake/cli/_plugins/helpers/commands.py +207 -1
  12. snowflake/cli/_plugins/nativeapp/artifacts.py +16 -628
  13. snowflake/cli/_plugins/nativeapp/bundle_context.py +1 -1
  14. snowflake/cli/_plugins/nativeapp/codegen/artifact_processor.py +1 -1
  15. snowflake/cli/_plugins/nativeapp/codegen/compiler.py +42 -20
  16. snowflake/cli/_plugins/nativeapp/codegen/setup/native_app_setup_processor.py +9 -2
  17. snowflake/cli/_plugins/nativeapp/codegen/snowpark/python_processor.py +6 -3
  18. snowflake/cli/_plugins/nativeapp/codegen/templates/templates_processor.py +44 -34
  19. snowflake/cli/_plugins/nativeapp/commands.py +113 -21
  20. snowflake/cli/_plugins/nativeapp/constants.py +5 -0
  21. snowflake/cli/_plugins/nativeapp/entities/application.py +226 -296
  22. snowflake/cli/_plugins/nativeapp/entities/application_package.py +911 -141
  23. snowflake/cli/_plugins/nativeapp/entities/application_package_child_interface.py +43 -0
  24. snowflake/cli/_plugins/nativeapp/feature_flags.py +5 -1
  25. snowflake/cli/_plugins/nativeapp/release_channel/__init__.py +13 -0
  26. snowflake/cli/_plugins/nativeapp/release_channel/commands.py +246 -0
  27. snowflake/cli/_plugins/nativeapp/release_directive/__init__.py +13 -0
  28. snowflake/cli/_plugins/nativeapp/release_directive/commands.py +243 -0
  29. snowflake/cli/_plugins/nativeapp/same_account_install_method.py +9 -17
  30. snowflake/cli/_plugins/nativeapp/sf_facade_exceptions.py +80 -0
  31. snowflake/cli/_plugins/nativeapp/sf_sql_facade.py +1184 -80
  32. snowflake/cli/_plugins/nativeapp/utils.py +11 -0
  33. snowflake/cli/_plugins/nativeapp/v2_conversions/compat.py +7 -3
  34. snowflake/cli/_plugins/nativeapp/version/commands.py +32 -5
  35. snowflake/cli/_plugins/notebook/commands.py +55 -2
  36. snowflake/cli/_plugins/notebook/exceptions.py +1 -1
  37. snowflake/cli/_plugins/notebook/manager.py +7 -5
  38. snowflake/cli/_plugins/notebook/notebook_entity.py +120 -0
  39. snowflake/cli/_plugins/notebook/notebook_entity_model.py +42 -0
  40. snowflake/cli/_plugins/notebook/notebook_project_paths.py +15 -0
  41. snowflake/cli/_plugins/notebook/types.py +3 -0
  42. snowflake/cli/_plugins/snowpark/commands.py +48 -30
  43. snowflake/cli/_plugins/snowpark/common.py +47 -2
  44. snowflake/cli/_plugins/snowpark/snowpark_entity.py +247 -4
  45. snowflake/cli/_plugins/snowpark/snowpark_entity_model.py +18 -30
  46. snowflake/cli/_plugins/snowpark/snowpark_project_paths.py +156 -23
  47. snowflake/cli/_plugins/snowpark/zipper.py +33 -1
  48. snowflake/cli/_plugins/spcs/common.py +129 -0
  49. snowflake/cli/_plugins/spcs/services/commands.py +131 -14
  50. snowflake/cli/_plugins/spcs/services/manager.py +169 -1
  51. snowflake/cli/_plugins/stage/commands.py +2 -1
  52. snowflake/cli/_plugins/stage/diff.py +60 -39
  53. snowflake/cli/_plugins/stage/manager.py +34 -13
  54. snowflake/cli/_plugins/stage/utils.py +1 -1
  55. snowflake/cli/_plugins/streamlit/commands.py +10 -1
  56. snowflake/cli/_plugins/streamlit/manager.py +70 -22
  57. snowflake/cli/_plugins/streamlit/streamlit_entity.py +131 -1
  58. snowflake/cli/_plugins/streamlit/streamlit_entity_model.py +14 -24
  59. snowflake/cli/_plugins/streamlit/streamlit_project_paths.py +30 -0
  60. snowflake/cli/_plugins/workspace/commands.py +6 -5
  61. snowflake/cli/_plugins/workspace/manager.py +9 -5
  62. snowflake/cli/api/artifacts/__init__.py +13 -0
  63. snowflake/cli/api/artifacts/bundle_map.py +500 -0
  64. snowflake/cli/api/artifacts/common.py +78 -0
  65. snowflake/cli/api/artifacts/utils.py +82 -0
  66. snowflake/cli/api/cli_global_context.py +36 -2
  67. snowflake/cli/api/commands/flags.py +10 -4
  68. snowflake/cli/api/commands/utils.py +28 -2
  69. snowflake/cli/api/config.py +6 -2
  70. snowflake/cli/api/connections.py +12 -1
  71. snowflake/cli/api/constants.py +10 -1
  72. snowflake/cli/api/entities/common.py +81 -14
  73. snowflake/cli/api/entities/resolver.py +160 -0
  74. snowflake/cli/api/entities/utils.py +65 -23
  75. snowflake/cli/api/errno.py +63 -3
  76. snowflake/cli/api/feature_flags.py +19 -4
  77. snowflake/cli/api/metrics.py +21 -27
  78. snowflake/cli/api/project/definition_conversion.py +4 -4
  79. snowflake/cli/api/project/project_paths.py +28 -0
  80. snowflake/cli/api/project/schemas/entities/common.py +130 -1
  81. snowflake/cli/api/project/schemas/entities/entities.py +4 -0
  82. snowflake/cli/api/project/schemas/project_definition.py +54 -6
  83. snowflake/cli/api/project/schemas/updatable_model.py +2 -2
  84. snowflake/cli/api/project/schemas/v1/native_app/native_app.py +5 -7
  85. snowflake/cli/api/project/schemas/v1/streamlit/streamlit.py +1 -1
  86. snowflake/cli/api/project/util.py +45 -0
  87. snowflake/cli/api/secure_path.py +6 -0
  88. snowflake/cli/api/sql_execution.py +5 -1
  89. snowflake/cli/api/stage_path.py +7 -2
  90. snowflake/cli/api/utils/graph.py +3 -0
  91. snowflake/cli/api/utils/path_utils.py +24 -0
  92. {snowflake_cli-3.2.2.dist-info → snowflake_cli-3.4.1.dist-info}/METADATA +14 -15
  93. {snowflake_cli-3.2.2.dist-info → snowflake_cli-3.4.1.dist-info}/RECORD +96 -82
  94. {snowflake_cli-3.2.2.dist-info → snowflake_cli-3.4.1.dist-info}/WHEEL +1 -1
  95. snowflake/cli/api/project/schemas/v1/native_app/path_mapping.py +0 -65
  96. {snowflake_cli-3.2.2.dist-info → snowflake_cli-3.4.1.dist-info}/entry_points.txt +0 -0
  97. {snowflake_cli-3.2.2.dist-info → snowflake_cli-3.4.1.dist-info}/licenses/LICENSE +0 -0
@@ -14,4 +14,4 @@
14
14
 
15
15
  from __future__ import annotations
16
16
 
17
- VERSION = "3.2.2"
17
+ VERSION = "3.4.1"
@@ -16,11 +16,11 @@ from __future__ import annotations
16
16
 
17
17
  import sys
18
18
 
19
- from snowflake.cli._app.cli_app import app_factory
19
+ from snowflake.cli._app.cli_app import CliAppFactory
20
20
 
21
21
 
22
22
  def main(*args):
23
- app = app_factory()
23
+ app = CliAppFactory().create_or_get_app()
24
24
  app(*args)
25
25
 
26
26
 
@@ -18,13 +18,12 @@ import logging
18
18
  import os
19
19
  import platform
20
20
  import sys
21
- from dataclasses import dataclass
22
21
  from pathlib import Path
23
22
  from typing import Optional
24
23
 
25
24
  import click
26
25
  import typer
27
- from click import Context
26
+ from click import Context as ClickContext
28
27
  from snowflake.cli import __about__
29
28
  from snowflake.cli._app.api_impl.plugin.plugin_config_provider_impl import (
30
29
  PluginConfigProviderImpl,
@@ -44,7 +43,7 @@ from snowflake.cli._app.version_check import (
44
43
  show_new_version_banner_callback,
45
44
  )
46
45
  from snowflake.cli.api import Api, api_provider
47
- from snowflake.cli.api.config import config_init
46
+ from snowflake.cli.api.config import config_init, get_feature_flags_section
48
47
  from snowflake.cli.api.output.formats import OutputFormat
49
48
  from snowflake.cli.api.output.types import CollectionResult
50
49
  from snowflake.cli.api.secure_path import SecurePath
@@ -52,25 +51,6 @@ from snowflake.connector.config_manager import CONFIG_MANAGER
52
51
 
53
52
  log = logging.getLogger(__name__)
54
53
 
55
- _api = Api(plugin_config_provider=PluginConfigProviderImpl())
56
- api_provider.register_api(_api)
57
-
58
- _commands_registration = CommandsRegistrationWithCallbacks(_api.plugin_config_provider)
59
-
60
-
61
- @dataclass
62
- class AppContextHolder:
63
- # needed to access the context from tests
64
- app_context: Optional[Context] = None
65
-
66
-
67
- app_context_holder = AppContextHolder()
68
-
69
-
70
- def _exit_with_cleanup():
71
- _commands_registration.reset_running_instance_registration_state()
72
- raise typer.Exit()
73
-
74
54
 
75
55
  def _do_not_execute_on_completion(callback):
76
56
  def enriched_callback(value):
@@ -81,175 +61,227 @@ def _do_not_execute_on_completion(callback):
81
61
  return enriched_callback
82
62
 
83
63
 
84
- def _commands_registration_callback(value: bool):
85
- if value:
86
- _commands_registration.register_commands_if_ready_and_not_registered_yet()
87
- # required to make the tests working
88
- # because a single test can execute multiple commands using always the same "app" instance
89
- _commands_registration.reset_running_instance_registration_state()
90
- app_context_holder.app_context = click.get_current_context()
91
-
92
-
93
- @_commands_registration.before
94
- def _config_init_callback(configuration_file: Optional[Path]):
95
- config_init(configuration_file)
96
-
97
-
98
- @_commands_registration.before
99
- def _disable_external_command_plugins_callback(value: bool):
100
- if value:
101
- _commands_registration.disable_external_command_plugins()
102
-
103
-
104
- @_do_not_execute_on_completion
105
- @_commands_registration.after
106
- def _docs_callback(value: bool):
107
- if value:
108
- ctx = click.get_current_context()
109
- generate_docs(SecurePath("gen_docs"), ctx.command)
110
- _exit_with_cleanup()
111
-
112
-
113
- @_do_not_execute_on_completion
114
- @_commands_registration.after
115
- def _commands_structure_callback(value: bool):
116
- if value:
117
- ctx = click.get_current_context()
118
- generate_commands_structure(ctx.command).print_node()
119
- _exit_with_cleanup()
120
-
121
-
122
- @_do_not_execute_on_completion
123
- def _version_callback(value: bool):
124
- if value:
125
- print_result(MessageResult(f"Snowflake CLI version: {__about__.VERSION}"))
126
- _exit_with_cleanup()
127
-
128
-
129
- from snowflake.cli.api.config import get_feature_flags_section
130
-
131
-
132
- @_do_not_execute_on_completion
133
- def _info_callback(value: bool):
134
- if value:
135
- result = CollectionResult(
136
- [
137
- {"key": "version", "value": __about__.VERSION},
138
- {
139
- "key": "default_config_file_path",
140
- "value": str(CONFIG_MANAGER.file_path),
141
- },
142
- {"key": "python_version", "value": sys.version},
143
- {"key": "system_info", "value": platform.platform()},
144
- {"key": "feature_flags", "value": get_feature_flags_section()},
145
- {"key": "SNOWFLAKE_HOME", "value": os.getenv("SNOWFLAKE_HOME")},
146
- ],
64
+ class CliAppFactory:
65
+ def __init__(self):
66
+ api = Api(plugin_config_provider=PluginConfigProviderImpl())
67
+ self._api = api
68
+ self._commands_registration = CommandsRegistrationWithCallbacks(
69
+ api.plugin_config_provider
147
70
  )
148
- print_result(result, output_format=OutputFormat.JSON)
149
- _exit_with_cleanup()
150
-
151
-
152
- def app_factory() -> SnowCliMainTyper:
153
- app = SnowCliMainTyper()
154
- new_version_msg = get_new_version_msg()
155
-
156
- @app.callback(
157
- invoke_without_command=True,
158
- epilog=new_version_msg,
159
- result_callback=show_new_version_banner_callback(new_version_msg),
160
- help=f"Snowflake CLI tool for developers [v{__about__.VERSION}]",
161
- )
162
- def default(
163
- ctx: typer.Context,
164
- version: bool = typer.Option(
165
- None,
166
- "--version",
167
- help="Shows version of the Snowflake CLI",
168
- callback=_version_callback,
169
- is_eager=True,
170
- ),
171
- docs: bool = typer.Option(
172
- None,
173
- "--docs",
174
- hidden=True,
175
- help="Generates Snowflake CLI documentation",
176
- callback=_docs_callback,
177
- is_eager=True,
178
- ),
179
- structure: bool = typer.Option(
180
- None,
181
- "--structure",
182
- hidden=True,
183
- help="Prints Snowflake CLI structure of commands",
184
- callback=_commands_structure_callback,
185
- is_eager=True,
186
- ),
187
- info: bool = typer.Option(
188
- None,
189
- "--info",
190
- help="Shows information about the Snowflake CLI",
191
- callback=_info_callback,
192
- ),
193
- configuration_file: Path = typer.Option(
194
- None,
195
- "--config-file",
196
- help="Specifies Snowflake CLI configuration file that should be used",
197
- exists=True,
198
- dir_okay=False,
199
- is_eager=True,
200
- callback=_config_init_callback,
201
- ),
202
- pycharm_debug_library_path: str = typer.Option(
203
- None,
204
- "--pycharm-debug-library-path",
205
- hidden=True,
206
- ),
207
- pycharm_debug_server_host: str = typer.Option(
208
- "localhost",
209
- "--pycharm-debug-server-host",
210
- hidden=True,
211
- ),
212
- pycharm_debug_server_port: int = typer.Option(
213
- 12345,
214
- "--pycharm-debug-server-port",
215
- hidden=True,
216
- ),
217
- disable_external_command_plugins: bool = typer.Option(
218
- None,
219
- "--disable-external-command-plugins",
220
- help="Disable external command plugins",
221
- callback=_disable_external_command_plugins_callback,
222
- is_eager=True,
223
- hidden=True,
224
- ),
225
- # THIS OPTION SHOULD BE THE LAST OPTION IN THE LIST!
226
- # ---
227
- # This is a hidden artificial option used only to guarantee execution of commands registration
228
- # and make this guaranty not dependent on other callbacks.
229
- # Commands registration is invoked as soon as all callbacks
230
- # decorated with "_commands_registration.before" are executed
231
- # but if there are no such callbacks (at the result of possible future changes)
232
- # then we need to invoke commands registration manually.
233
- #
234
- # This option is also responsible for resetting registration state for test purposes.
235
- commands_registration: bool = typer.Option(
236
- True,
237
- "--commands-registration",
238
- help="Commands registration",
239
- hidden=True,
240
- is_eager=True,
241
- callback=_commands_registration_callback,
242
- ),
243
- ) -> None:
244
- """
245
- Snowflake CLI tool for developers.
246
- """
247
- if not ctx.invoked_subcommand:
248
- typer.echo(ctx.get_help())
249
- setup_pycharm_remote_debugger_if_provided(
250
- pycharm_debug_library_path=pycharm_debug_library_path,
251
- pycharm_debug_server_host=pycharm_debug_server_host,
252
- pycharm_debug_server_port=pycharm_debug_server_port,
71
+ api_provider.register_api(api)
72
+ self._app: Optional[SnowCliMainTyper] = None
73
+ self._click_context: Optional[ClickContext] = None
74
+
75
+ def _exit_with_cleanup(self):
76
+ self._commands_registration.reset_running_instance_registration_state()
77
+ raise typer.Exit()
78
+
79
+ def _commands_registration_callback(self):
80
+ def callback(value: bool):
81
+ self._click_context = click.get_current_context()
82
+ if value:
83
+ self._commands_registration.register_commands_from_plugins()
84
+ # required to make the tests working
85
+ # because a single test can execute multiple commands using always the same "app" instance
86
+ self._commands_registration.reset_running_instance_registration_state()
87
+
88
+ return callback
89
+
90
+ @staticmethod
91
+ def _config_init_callback():
92
+ def callback(configuration_file: Optional[Path]):
93
+ config_init(configuration_file)
94
+
95
+ return callback
96
+
97
+ def _disable_external_command_plugins_callback(self):
98
+ def callback(value: bool):
99
+ if value:
100
+ self._commands_registration.disable_external_command_plugins()
101
+
102
+ return callback
103
+
104
+ def _docs_callback(self):
105
+ @_do_not_execute_on_completion
106
+ @self._commands_registration.after
107
+ def callback(value: bool):
108
+ if value:
109
+ ctx = click.get_current_context()
110
+ generate_docs(SecurePath("gen_docs"), ctx.command)
111
+ self._exit_with_cleanup()
112
+
113
+ return callback
114
+
115
+ def _help_callback(self):
116
+ @_do_not_execute_on_completion
117
+ @self._commands_registration.after
118
+ def callback(value: bool):
119
+ if value:
120
+ ctx = click.get_current_context()
121
+ typer.echo(ctx.get_help())
122
+ self._exit_with_cleanup()
123
+
124
+ return callback
125
+
126
+ def _commands_structure_callback(self):
127
+ @_do_not_execute_on_completion
128
+ @self._commands_registration.after
129
+ def callback(value: bool):
130
+ if value:
131
+ ctx = click.get_current_context()
132
+ generate_commands_structure(ctx.command).print_node()
133
+ self._exit_with_cleanup()
134
+
135
+ return callback
136
+
137
+ def _version_callback(self):
138
+ @_do_not_execute_on_completion
139
+ def callback(value: bool):
140
+ if value:
141
+ print_result(
142
+ MessageResult(f"Snowflake CLI version: {__about__.VERSION}")
143
+ )
144
+ self._exit_with_cleanup()
145
+
146
+ return callback
147
+
148
+ def _info_callback(self):
149
+ @_do_not_execute_on_completion
150
+ def callback(value: bool):
151
+ if value:
152
+ result = CollectionResult(
153
+ [
154
+ {"key": "version", "value": __about__.VERSION},
155
+ {
156
+ "key": "default_config_file_path",
157
+ "value": str(CONFIG_MANAGER.file_path),
158
+ },
159
+ {"key": "python_version", "value": sys.version},
160
+ {"key": "system_info", "value": platform.platform()},
161
+ {"key": "feature_flags", "value": get_feature_flags_section()},
162
+ {"key": "SNOWFLAKE_HOME", "value": os.getenv("SNOWFLAKE_HOME")},
163
+ ],
164
+ )
165
+ print_result(result, output_format=OutputFormat.JSON)
166
+ self._exit_with_cleanup()
167
+
168
+ return callback
169
+
170
+ def create_or_get_app(self) -> SnowCliMainTyper:
171
+ if self._app:
172
+ return self._app
173
+
174
+ app = SnowCliMainTyper()
175
+ new_version_msg = get_new_version_msg()
176
+
177
+ @app.callback(
178
+ invoke_without_command=True,
179
+ epilog=new_version_msg,
180
+ result_callback=show_new_version_banner_callback(new_version_msg),
181
+ add_help_option=False, # custom_help option added below
182
+ help=f"Snowflake CLI tool for developers [v{__about__.VERSION}]",
253
183
  )
254
-
255
- return app
184
+ def default(
185
+ ctx: typer.Context,
186
+ # We need a custom help option with _help_callback called after command registration
187
+ # to have all commands visible in the help.
188
+ # This is required since click 8.1.8, when the default help option
189
+ # has started to being executed before our eager options, including command registration.
190
+ custom_help: bool = typer.Option(
191
+ None,
192
+ "--help",
193
+ "-h",
194
+ help="Show this message and exit.",
195
+ callback=self._help_callback,
196
+ is_eager=True,
197
+ ),
198
+ version: bool = typer.Option(
199
+ None,
200
+ "--version",
201
+ help="Shows version of the Snowflake CLI",
202
+ callback=self._version_callback(),
203
+ is_eager=True,
204
+ ),
205
+ docs: bool = typer.Option(
206
+ None,
207
+ "--docs",
208
+ hidden=True,
209
+ help="Generates Snowflake CLI documentation",
210
+ callback=self._docs_callback(),
211
+ is_eager=True,
212
+ ),
213
+ structure: bool = typer.Option(
214
+ None,
215
+ "--structure",
216
+ hidden=True,
217
+ help="Prints Snowflake CLI structure of commands",
218
+ callback=self._commands_structure_callback(),
219
+ is_eager=True,
220
+ ),
221
+ info: bool = typer.Option(
222
+ None,
223
+ "--info",
224
+ help="Shows information about the Snowflake CLI",
225
+ callback=self._info_callback(),
226
+ ),
227
+ configuration_file: Path = typer.Option(
228
+ None,
229
+ "--config-file",
230
+ help="Specifies Snowflake CLI configuration file that should be used",
231
+ exists=True,
232
+ dir_okay=False,
233
+ is_eager=True,
234
+ callback=self._config_init_callback(),
235
+ ),
236
+ pycharm_debug_library_path: str = typer.Option(
237
+ None,
238
+ "--pycharm-debug-library-path",
239
+ hidden=True,
240
+ ),
241
+ pycharm_debug_server_host: str = typer.Option(
242
+ "localhost",
243
+ "--pycharm-debug-server-host",
244
+ hidden=True,
245
+ ),
246
+ pycharm_debug_server_port: int = typer.Option(
247
+ 12345,
248
+ "--pycharm-debug-server-port",
249
+ hidden=True,
250
+ ),
251
+ disable_external_command_plugins: bool = typer.Option(
252
+ None,
253
+ "--disable-external-command-plugins",
254
+ help="Disable external command plugins",
255
+ callback=self._disable_external_command_plugins_callback(),
256
+ is_eager=True,
257
+ hidden=True,
258
+ ),
259
+ # THIS OPTION SHOULD BE THE LAST OPTION IN THE LIST!
260
+ # ---
261
+ # This is a hidden artificial option used only to guarantee execution of commands registration.
262
+ # This option is also responsible for resetting registration state for test purposes.
263
+ commands_registration: bool = typer.Option(
264
+ True,
265
+ "--commands-registration",
266
+ help="Commands registration",
267
+ hidden=True,
268
+ is_eager=True,
269
+ callback=self._commands_registration_callback(),
270
+ ),
271
+ ) -> None:
272
+ """
273
+ Snowflake CLI tool for developers.
274
+ """
275
+ if not ctx.invoked_subcommand:
276
+ typer.echo(ctx.get_help())
277
+ setup_pycharm_remote_debugger_if_provided(
278
+ pycharm_debug_library_path=pycharm_debug_library_path,
279
+ pycharm_debug_server_host=pycharm_debug_server_host,
280
+ pycharm_debug_server_port=pycharm_debug_server_port,
281
+ )
282
+
283
+ self._app = app
284
+ return app
285
+
286
+ def get_click_context(self):
287
+ return self._click_context
@@ -21,7 +21,6 @@ from snowflake.cli._app.commands_registration.command_plugins_loader import (
21
21
  load_builtin_and_external_command_plugins,
22
22
  load_only_builtin_command_plugins,
23
23
  )
24
- from snowflake.cli._app.commands_registration.threadsafe import ThreadsafeCounter
25
24
  from snowflake.cli._app.commands_registration.typer_registration import (
26
25
  register_commands_from_plugins,
27
26
  )
@@ -36,27 +35,13 @@ class CommandRegistrationConfig:
36
35
  class CommandsRegistrationWithCallbacks:
37
36
  def __init__(self, plugin_config_provider: PluginConfigProvider):
38
37
  self._plugin_config_provider = plugin_config_provider
39
- self._counter_of_callbacks_required_before_registration: ThreadsafeCounter = (
40
- ThreadsafeCounter(0)
41
- )
42
- self._counter_of_callbacks_invoked_before_registration: ThreadsafeCounter = (
43
- ThreadsafeCounter(0)
44
- )
45
38
  self._callbacks_after_registration: List[Callable[[], None]] = []
46
39
  self._commands_registration_config: CommandRegistrationConfig = (
47
40
  CommandRegistrationConfig(enable_external_command_plugins=True)
48
41
  )
49
42
  self._commands_already_registered: bool = False
50
43
 
51
- def register_commands_if_ready_and_not_registered_yet(self):
52
- all_required_callbacks_executed = (
53
- self._counter_of_callbacks_required_before_registration.value
54
- == self._counter_of_callbacks_invoked_before_registration.value
55
- )
56
- if all_required_callbacks_executed and not self._commands_already_registered:
57
- self._register_commands_from_plugins()
58
-
59
- def _register_commands_from_plugins(self) -> None:
44
+ def register_commands_from_plugins(self) -> None:
60
45
  if self._commands_registration_config.enable_external_command_plugins:
61
46
  self._register_builtin_and_enabled_external_plugin_commands()
62
47
  else:
@@ -83,15 +68,6 @@ class CommandsRegistrationWithCallbacks:
83
68
  def disable_external_command_plugins(self):
84
69
  self._commands_registration_config.enable_external_command_plugins = False
85
70
 
86
- def before(self, callback):
87
- def enriched_callback(value):
88
- self._counter_of_callbacks_invoked_before_registration.increment()
89
- callback(value)
90
- self.register_commands_if_ready_and_not_registered_yet()
91
-
92
- self._counter_of_callbacks_required_before_registration.increment()
93
- return enriched_callback
94
-
95
71
  def after(self, callback):
96
72
  def delayed_callback(value):
97
73
  self._callbacks_after_registration.append(lambda: callback(value))
@@ -99,7 +75,5 @@ class CommandsRegistrationWithCallbacks:
99
75
  return delayed_callback
100
76
 
101
77
  def reset_running_instance_registration_state(self):
102
- self._commands_already_registered = False
103
- self._counter_of_callbacks_invoked_before_registration.set(0)
104
78
  self._callbacks_after_registration.clear()
105
79
  self._commands_registration_config.enable_external_command_plugins = True
@@ -17,3 +17,7 @@ from __future__ import annotations
17
17
  from typing import Literal
18
18
 
19
19
  PARAM_APPLICATION_NAME: Literal["snowcli"] = "snowcli"
20
+
21
+ # This is also defined on server side. Changing this parameter would require
22
+ # a change in https://github.com/snowflakedb/snowflake
23
+ INTERNAL_APPLICATION_NAME: Literal["SNOWFLAKE_CLI"] = "SNOWFLAKE_CLI"
@@ -21,7 +21,9 @@ from typing import Dict, Optional
21
21
 
22
22
  import snowflake.connector
23
23
  from click.exceptions import ClickException
24
+ from snowflake.cli.__about__ import VERSION
24
25
  from snowflake.cli._app.constants import (
26
+ INTERNAL_APPLICATION_NAME,
25
27
  PARAM_APPLICATION_NAME,
26
28
  )
27
29
  from snowflake.cli._app.secret import SecretType
@@ -35,6 +37,7 @@ from snowflake.cli.api.exceptions import (
35
37
  InvalidConnectionConfiguration,
36
38
  SnowflakeConnectionError,
37
39
  )
40
+ from snowflake.cli.api.feature_flags import FeatureFlag
38
41
  from snowflake.cli.api.secure_path import SecurePath
39
42
  from snowflake.connector import SnowflakeConnection
40
43
  from snowflake.connector.errors import DatabaseError, ForbiddenError
@@ -150,6 +153,8 @@ def connect_to_snowflake(
150
153
 
151
154
  _update_connection_application_name(connection_parameters)
152
155
 
156
+ _update_internal_application_info(connection_parameters)
157
+
153
158
  try:
154
159
  # Whatever output is generated when creating connection,
155
160
  # we don't want it in our output. This is particularly important
@@ -238,6 +243,13 @@ def _update_connection_application_name(connection_parameters: Dict):
238
243
  connection_parameters.update(connection_application_params)
239
244
 
240
245
 
246
+ def _update_internal_application_info(connection_parameters: Dict):
247
+ """Update internal application data if ENABLE_SEPARATE_AUTHENTICATION_POLICY_ID is enabled."""
248
+ if FeatureFlag.ENABLE_SEPARATE_AUTHENTICATION_POLICY_ID.is_enabled():
249
+ connection_parameters["internal_application_name"] = INTERNAL_APPLICATION_NAME
250
+ connection_parameters["internal_application_version"] = VERSION
251
+
252
+
241
253
  def _load_pem_from_file(private_key_file: str) -> SecretType:
242
254
  with SecurePath(private_key_file).open(
243
255
  "rb", read_file_limit_mb=DEFAULT_SIZE_LIMIT_MB
@@ -67,6 +67,10 @@ class CLITelemetryField(Enum):
67
67
  CONFIG_FEATURE_FLAGS = "config_feature_flags"
68
68
  # Metrics
69
69
  COUNTERS = "counters"
70
+ SPANS = "spans"
71
+ COMPLETED_SPANS = "completed_spans"
72
+ NUM_SPANS_PAST_DEPTH_LIMIT = "num_spans_past_depth_limit"
73
+ NUM_SPANS_PAST_TOTAL_LIMIT = "num_spans_past_total_limit"
70
74
  # Information
71
75
  EVENT = "event"
72
76
  ERROR_MSG = "error_msg"
@@ -129,9 +133,12 @@ def _get_command_metrics() -> TelemetryDict:
129
133
  cli_context = get_cli_context()
130
134
 
131
135
  return {
132
- CLITelemetryField.COUNTERS: {
133
- **cli_context.metrics.counters,
134
- }
136
+ CLITelemetryField.COUNTERS: cli_context.metrics.counters,
137
+ CLITelemetryField.SPANS: {
138
+ CLITelemetryField.COMPLETED_SPANS.value: cli_context.metrics.completed_spans,
139
+ CLITelemetryField.NUM_SPANS_PAST_DEPTH_LIMIT.value: cli_context.metrics.num_spans_past_depth_limit,
140
+ CLITelemetryField.NUM_SPANS_PAST_TOTAL_LIMIT.value: cli_context.metrics.num_spans_past_total_limit,
141
+ },
135
142
  }
136
143
 
137
144
 
@@ -19,7 +19,6 @@ import logging
19
19
  import os
20
20
  from enum import Enum
21
21
  from functools import lru_cache
22
- from textwrap import dedent
23
22
  from typing import Any, Dict, Optional
24
23
 
25
24
  from click.exceptions import ClickException
@@ -57,11 +56,12 @@ class UIParameter(Enum):
57
56
  NA_ENFORCE_MANDATORY_FILTERS = (
58
57
  "ENFORCE_MANDATORY_FILTERS_FOR_SAME_ACCOUNT_INSTALLATION"
59
58
  )
59
+ NA_FEATURE_RELEASE_CHANNELS = "FEATURE_RELEASE_CHANNELS"
60
60
 
61
61
 
62
62
  def get_ui_parameter(
63
63
  conn: SnowflakeConnection, parameter: UIParameter, default: Any
64
- ) -> str:
64
+ ) -> Any:
65
65
  """
66
66
  Returns the value of a single UI parameter.
67
67
  If the parameter is not found, the default value is returned.
@@ -77,21 +77,19 @@ def get_ui_parameters(conn: SnowflakeConnection) -> Dict[UIParameter, Any]:
77
77
  Returns the UI parameters from the SYSTEM$BOOTSTRAP_DATA_REQUEST function
78
78
  """
79
79
 
80
- parameters_to_fetch = sorted([param.value for param in UIParameter])
80
+ parameters_to_fetch = [param.value for param in UIParameter]
81
81
 
82
- query = dedent(
83
- f"""
84
- select value['value']::string as PARAM_VALUE, value['name']::string as PARAM_NAME from table(flatten(
85
- input => parse_json(SYSTEM$BOOTSTRAP_DATA_REQUEST()),
86
- path => 'clientParamsInfo'
87
- )) where value['name'] in ('{"', '".join(parameters_to_fetch)}');
88
- """
89
- )
82
+ # Parsing of the Json and the filtering is happening here in Snowflake CLI
83
+ # in order to avoid requiring a warehouse in Snowflake
84
+ query = "call system$bootstrap_data_request('CLIENT_PARAMS_INFO')"
85
+ *_, cursor = conn.execute_string(query)
90
86
 
91
- *_, cursor = conn.execute_string(query, cursor_class=DictCursor)
87
+ json_map = json.loads(cursor.fetchone()[0])
92
88
 
93
89
  return {
94
- UIParameter(row["PARAM_NAME"]): row["PARAM_VALUE"] for row in cursor.fetchall()
90
+ UIParameter(row["name"]): row["value"]
91
+ for row in json_map["clientParamsInfo"]
92
+ if row["name"] in parameters_to_fetch
95
93
  }
96
94
 
97
95
 
@@ -103,12 +101,7 @@ def is_regionless_redirect(conn: SnowflakeConnection) -> bool:
103
101
  assume it's regionless, as this is true for most production deployments.
104
102
  """
105
103
  try:
106
- return (
107
- get_ui_parameter(
108
- conn, UIParameter.NA_ENABLE_REGIONLESS_REDIRECT, "true"
109
- ).lower()
110
- == "true"
111
- )
104
+ return get_ui_parameter(conn, UIParameter.NA_ENABLE_REGIONLESS_REDIRECT, True)
112
105
  except:
113
106
  log.warning(
114
107
  "Cannot determine regionless redirect; assuming True.", exc_info=True