snowflake-cli 3.11.0__py3-none-any.whl → 3.13.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. snowflake/cli/__about__.py +1 -1
  2. snowflake/cli/_app/cli_app.py +43 -1
  3. snowflake/cli/_app/commands_registration/builtin_plugins.py +1 -1
  4. snowflake/cli/_app/commands_registration/command_plugins_loader.py +14 -1
  5. snowflake/cli/_app/printing.py +153 -19
  6. snowflake/cli/_app/telemetry.py +25 -10
  7. snowflake/cli/_plugins/auth/__init__.py +0 -2
  8. snowflake/cli/_plugins/connection/commands.py +1 -78
  9. snowflake/cli/_plugins/dbt/commands.py +44 -19
  10. snowflake/cli/_plugins/dbt/constants.py +1 -1
  11. snowflake/cli/_plugins/dbt/manager.py +252 -47
  12. snowflake/cli/_plugins/dcm/commands.py +65 -90
  13. snowflake/cli/_plugins/dcm/manager.py +137 -50
  14. snowflake/cli/_plugins/logs/commands.py +7 -0
  15. snowflake/cli/_plugins/logs/manager.py +21 -1
  16. snowflake/cli/_plugins/nativeapp/entities/application_package.py +4 -1
  17. snowflake/cli/_plugins/nativeapp/sf_sql_facade.py +3 -1
  18. snowflake/cli/_plugins/object/manager.py +1 -0
  19. snowflake/cli/_plugins/snowpark/common.py +1 -0
  20. snowflake/cli/_plugins/snowpark/package/anaconda_packages.py +29 -5
  21. snowflake/cli/_plugins/snowpark/package_utils.py +44 -3
  22. snowflake/cli/_plugins/spcs/services/commands.py +19 -1
  23. snowflake/cli/_plugins/spcs/services/manager.py +17 -4
  24. snowflake/cli/_plugins/spcs/services/service_entity_model.py +5 -0
  25. snowflake/cli/_plugins/sql/lexer/types.py +1 -0
  26. snowflake/cli/_plugins/sql/repl.py +100 -26
  27. snowflake/cli/_plugins/sql/repl_commands.py +607 -0
  28. snowflake/cli/_plugins/sql/statement_reader.py +44 -20
  29. snowflake/cli/_plugins/streamlit/streamlit_entity.py +28 -2
  30. snowflake/cli/_plugins/streamlit/streamlit_entity_model.py +24 -4
  31. snowflake/cli/api/artifacts/bundle_map.py +32 -2
  32. snowflake/cli/api/artifacts/regex_resolver.py +54 -0
  33. snowflake/cli/api/artifacts/upload.py +5 -1
  34. snowflake/cli/api/artifacts/utils.py +12 -1
  35. snowflake/cli/api/cli_global_context.py +7 -0
  36. snowflake/cli/api/commands/decorators.py +7 -0
  37. snowflake/cli/api/commands/flags.py +24 -1
  38. snowflake/cli/api/console/abc.py +13 -2
  39. snowflake/cli/api/console/console.py +20 -0
  40. snowflake/cli/api/constants.py +9 -0
  41. snowflake/cli/api/entities/utils.py +10 -6
  42. snowflake/cli/api/feature_flags.py +3 -2
  43. snowflake/cli/api/identifiers.py +18 -1
  44. snowflake/cli/api/project/schemas/entities/entities.py +0 -6
  45. snowflake/cli/api/rendering/sql_templates.py +2 -0
  46. {snowflake_cli-3.11.0.dist-info → snowflake_cli-3.13.0.dist-info}/METADATA +7 -7
  47. {snowflake_cli-3.11.0.dist-info → snowflake_cli-3.13.0.dist-info}/RECORD +51 -54
  48. snowflake/cli/_plugins/auth/keypair/__init__.py +0 -0
  49. snowflake/cli/_plugins/auth/keypair/commands.py +0 -153
  50. snowflake/cli/_plugins/auth/keypair/manager.py +0 -331
  51. snowflake/cli/_plugins/dcm/dcm_project_entity_model.py +0 -59
  52. snowflake/cli/_plugins/sql/snowsql_commands.py +0 -331
  53. /snowflake/cli/_plugins/auth/{keypair/plugin_spec.py → plugin_spec.py} +0 -0
  54. {snowflake_cli-3.11.0.dist-info → snowflake_cli-3.13.0.dist-info}/WHEEL +0 -0
  55. {snowflake_cli-3.11.0.dist-info → snowflake_cli-3.13.0.dist-info}/entry_points.txt +0 -0
  56. {snowflake_cli-3.11.0.dist-info → snowflake_cli-3.13.0.dist-info}/licenses/LICENSE +0 -0
@@ -16,7 +16,7 @@ from __future__ import annotations
16
16
 
17
17
  from enum import Enum, unique
18
18
 
19
- VERSION = "3.11.0"
19
+ VERSION = "3.13.0"
20
20
 
21
21
 
22
22
  @unique
@@ -14,6 +14,7 @@
14
14
 
15
15
  from __future__ import annotations
16
16
 
17
+ import inspect
17
18
  import logging
18
19
  import os
19
20
  import platform
@@ -47,6 +48,20 @@ from snowflake.connector.config_manager import CONFIG_MANAGER
47
48
 
48
49
  log = logging.getLogger(__name__)
49
50
 
51
+ INTERNAL_CLI_FLAGS = {
52
+ "custom_help",
53
+ "version",
54
+ "docs",
55
+ "structure",
56
+ "info",
57
+ "configuration_file",
58
+ "pycharm_debug_library_path",
59
+ "pycharm_debug_server_host",
60
+ "pycharm_debug_server_port",
61
+ "disable_external_command_plugins",
62
+ "commands_registration",
63
+ }
64
+
50
65
 
51
66
  def _do_not_execute_on_completion(callback):
52
67
  def enriched_callback(value):
@@ -256,7 +271,6 @@ class CliAppFactory:
256
271
  "--commands-registration",
257
272
  help="Commands registration",
258
273
  hidden=True,
259
- is_eager=True,
260
274
  callback=self._commands_registration_callback(),
261
275
  ),
262
276
  ) -> None:
@@ -271,8 +285,36 @@ class CliAppFactory:
271
285
  pycharm_debug_server_port=pycharm_debug_server_port,
272
286
  )
273
287
 
288
+ self._validate_internal_flags_excluded_from_telemetry(default)
289
+
274
290
  self._app = app
275
291
  return app
276
292
 
293
+ @staticmethod
294
+ def _validate_internal_flags_excluded_from_telemetry(callback_function):
295
+ """
296
+ We have not been interested in collecting telemetry data about root
297
+ command flags (most of which are internal flags). This method validates
298
+ that all new flags should be added to INTERNAL_CLI_FLAGS and thus
299
+ excluded from telemetry as well.
300
+ """
301
+ sig = inspect.signature(callback_function)
302
+ actual_params = {name for name in sig.parameters.keys() if name != "ctx"}
303
+ if actual_params != INTERNAL_CLI_FLAGS:
304
+ missing = actual_params - INTERNAL_CLI_FLAGS
305
+ extra = INTERNAL_CLI_FLAGS - actual_params
306
+ error_parts = []
307
+ if missing:
308
+ error_parts.append(
309
+ f"Parameters in default() but not in INTERNAL_CLI_FLAGS: {missing}"
310
+ )
311
+ if extra:
312
+ error_parts.append(
313
+ f"Flags in INTERNAL_CLI_FLAGS but not in default(): {extra}"
314
+ )
315
+ raise AssertionError(
316
+ "INTERNAL_CLI_FLAGS mismatch! " + ". ".join(error_parts)
317
+ )
318
+
277
319
  def get_click_context(self):
278
320
  return self._click_context
@@ -12,7 +12,7 @@
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.auth.keypair import plugin_spec as auth_plugin_spec
15
+ from snowflake.cli._plugins.auth import plugin_spec as auth_plugin_spec
16
16
  from snowflake.cli._plugins.connection import plugin_spec as connection_plugin_spec
17
17
  from snowflake.cli._plugins.cortex import plugin_spec as cortex_plugin_spec
18
18
  from snowflake.cli._plugins.dbt import plugin_spec as dbt_plugin_spec
@@ -69,7 +69,20 @@ class CommandPluginsLoader:
69
69
  )
70
70
 
71
71
  def load_all_registered_plugins(self) -> List[LoadedCommandPlugin]:
72
- for plugin_name, plugin in self._plugin_manager.list_name_plugin():
72
+ all_plugins = list(self._plugin_manager.list_name_plugin())
73
+ builtin_plugin_names = set(get_builtin_plugin_name_to_plugin_spec().keys())
74
+
75
+ def plugin_sort_key(name_plugin_tuple):
76
+ _plugin_name, _ = name_plugin_tuple
77
+ is_builtin = _plugin_name in builtin_plugin_names
78
+ return (
79
+ not is_builtin,
80
+ _plugin_name,
81
+ )
82
+
83
+ sorted_plugins = sorted(all_plugins, key=plugin_sort_key)
84
+
85
+ for plugin_name, plugin in sorted_plugins:
73
86
  self._load_plugin(plugin_name, plugin)
74
87
  return list(self._loaded_plugins.values())
75
88
 
@@ -22,7 +22,7 @@ from decimal import Decimal
22
22
  from json import JSONEncoder
23
23
  from pathlib import Path
24
24
  from textwrap import indent
25
- from typing import TextIO
25
+ from typing import Any, Dict, TextIO
26
26
 
27
27
  from rich import box, get_console
28
28
  from rich import print as rich_print
@@ -61,13 +61,114 @@ class CustomJSONEncoder(JSONEncoder):
61
61
  return list(o.result)
62
62
  if isinstance(o, (date, datetime, time)):
63
63
  return o.isoformat()
64
- if isinstance(o, (Path, Decimal)):
64
+ if isinstance(o, Path):
65
+ return o.as_posix()
66
+ if isinstance(o, Decimal):
65
67
  return str(o)
66
68
  if isinstance(o, bytearray):
67
69
  return o.hex()
68
70
  return super().default(o)
69
71
 
70
72
 
73
+ class StreamingJSONEncoder(JSONEncoder):
74
+ """Streaming JSON encoder that doesn't materialize generators into lists"""
75
+
76
+ def default(self, o):
77
+ if isinstance(o, str):
78
+ return sanitize_for_terminal(o)
79
+ if isinstance(o, (ObjectResult, MessageResult)):
80
+ return o.result
81
+ if isinstance(o, (CollectionResult, MultipleResults)):
82
+ raise TypeError(
83
+ f"CollectionResult should be handled by streaming functions, not encoder"
84
+ )
85
+ if isinstance(o, (date, datetime, time)):
86
+ return o.isoformat()
87
+ if isinstance(o, Path):
88
+ return o.as_posix()
89
+ if isinstance(o, Decimal):
90
+ return str(o)
91
+ if isinstance(o, bytearray):
92
+ return o.hex()
93
+ return super().default(o)
94
+
95
+
96
+ def _print_json_item_with_array_indentation(item: Any, indent: int):
97
+ """Print a JSON item with proper indentation for array context"""
98
+ if indent:
99
+ indented_output = json.dumps(item, cls=StreamingJSONEncoder, indent=indent)
100
+ indented_lines = indented_output.split("\n")
101
+ for i, line in enumerate(indented_lines):
102
+ if i == 0:
103
+ print(" " * indent + line, end="")
104
+ else:
105
+ print("\n" + " " * indent + line, end="")
106
+ else:
107
+ json.dump(item, sys.stdout, cls=StreamingJSONEncoder, separators=(",", ":"))
108
+
109
+
110
+ def _stream_collection_as_json(result: CollectionResult, indent: int = 4):
111
+ """Stream a CollectionResult as a JSON array without loading all data into memory"""
112
+ items = iter(result.result)
113
+ try:
114
+ first_item = next(items)
115
+ except StopIteration:
116
+ print("[]", end="")
117
+ return
118
+
119
+ print("[")
120
+
121
+ _print_json_item_with_array_indentation(first_item, indent)
122
+
123
+ for item in items:
124
+ print(",")
125
+ _print_json_item_with_array_indentation(item, indent)
126
+
127
+ print("\n]", end="")
128
+
129
+
130
+ def _stream_collection_as_csv(result: CollectionResult):
131
+ """Stream a CollectionResult as CSV without loading all data into memory"""
132
+ items = iter(result.result)
133
+ try:
134
+ first_item = next(items)
135
+ except StopIteration:
136
+ return
137
+
138
+ fieldnames = list(first_item.keys())
139
+ if not isinstance(first_item, dict):
140
+ raise TypeError("CSV output requires dictionary items")
141
+
142
+ writer = csv.DictWriter(sys.stdout, fieldnames=fieldnames, lineterminator="\n")
143
+ writer.writeheader()
144
+ _write_csv_row(writer, first_item)
145
+
146
+ for item in items:
147
+ _write_csv_row(writer, item)
148
+
149
+
150
+ def _write_csv_row(writer: csv.DictWriter, row_data: Dict[str, Any]):
151
+ """Write a single CSV row, handling special data types"""
152
+ processed_row = {}
153
+ for key, value in row_data.items():
154
+ if isinstance(value, str):
155
+ processed_row[key] = sanitize_for_terminal(value)
156
+ elif isinstance(value, (date, datetime, time)):
157
+ processed_row[key] = value.isoformat()
158
+ elif isinstance(value, Path):
159
+ processed_row[key] = value.as_posix()
160
+ elif isinstance(value, Decimal):
161
+ processed_row[key] = str(value)
162
+ elif isinstance(value, bytearray):
163
+ processed_row[key] = value.hex()
164
+ elif value is None:
165
+ processed_row[key] = ""
166
+ else:
167
+ processed_row[key] = str(value)
168
+
169
+ writer.writerow(processed_row)
170
+
171
+
71
172
  def _get_format_type() -> OutputFormat:
72
173
  output_format = get_cli_context().output_format
73
174
  if output_format:
@@ -110,12 +211,13 @@ def is_structured_format(output_format):
110
211
  def print_structured(
111
212
  result: CommandResult, output_format: OutputFormat = OutputFormat.JSON
112
213
  ):
113
- """Handles outputs like json, yml and other structured and parsable formats."""
214
+ """Handles outputs like json, csv and other structured and parsable formats with streaming."""
114
215
  printed_end_line = False
216
+
115
217
  if isinstance(result, MultipleResults):
116
218
  if output_format == OutputFormat.CSV:
117
219
  for command_result in result.result:
118
- _print_csv_result(command_result)
220
+ _print_csv_result_streaming(command_result)
119
221
  print(flush=True)
120
222
  printed_end_line = True
121
223
  else:
@@ -125,35 +227,67 @@ def print_structured(
125
227
  # instead of joining all the values into a JSON array or CSV entry set
126
228
  for r in result.result:
127
229
  if output_format == OutputFormat.CSV:
128
- _print_csv_result(r.result)
230
+ _print_csv_result_streaming(r)
129
231
  else:
130
- json.dump(r, sys.stdout, cls=CustomJSONEncoder)
232
+ json.dump(r, sys.stdout, cls=StreamingJSONEncoder)
131
233
  print(flush=True)
132
234
  printed_end_line = True
133
235
  else:
134
236
  if output_format == OutputFormat.CSV:
135
- _print_csv_result(result)
237
+ _print_csv_result_streaming(result)
136
238
  printed_end_line = True
137
239
  else:
138
- json.dump(result, sys.stdout, cls=CustomJSONEncoder, indent=4)
240
+ _print_json_result_streaming(result)
241
+
139
242
  # Adds empty line at the end
140
243
  if not printed_end_line:
141
244
  print(flush=True)
142
245
 
143
246
 
144
- def _print_csv_result(result: CommandResult):
145
- data = json.loads(json.dumps(result, cls=CustomJSONEncoder))
247
+ def _print_json_result_streaming(result: CommandResult):
248
+ """Print a single CommandResult as JSON with streaming support"""
249
+ if isinstance(result, CollectionResult):
250
+ _stream_collection_as_json(result, indent=4)
251
+ elif isinstance(result, (ObjectResult, MessageResult)):
252
+ json.dump(result, sys.stdout, cls=StreamingJSONEncoder, indent=4)
253
+ else:
254
+ json.dump(result, sys.stdout, cls=StreamingJSONEncoder, indent=4)
255
+
256
+
257
+ def _print_object_result_as_csv(result: ObjectResult):
258
+ """Print an ObjectResult as a single-row CSV.
259
+
260
+ Converts the object's key-value pairs into a CSV with headers
261
+ from the keys and a single data row from the values.
262
+ """
263
+ data = result.result
146
264
  if isinstance(data, dict):
147
- writer = csv.DictWriter(sys.stdout, [*data], lineterminator="\n")
148
- writer.writeheader()
149
- writer.writerow(data)
150
- elif isinstance(data, list):
151
- if not data:
152
- return
153
- writer = csv.DictWriter(sys.stdout, [*data[0]], lineterminator="\n")
265
+ writer = csv.DictWriter(
266
+ sys.stdout, fieldnames=list(data.keys()), lineterminator="\n"
267
+ )
154
268
  writer.writeheader()
155
- for entry in data:
156
- writer.writerow(entry)
269
+ _write_csv_row(writer, data)
270
+
271
+
272
+ def _print_message_result_as_csv(result: MessageResult):
273
+ """Print a MessageResult as CSV with a single 'message' column.
274
+
275
+ Creates a simple CSV structure with one column named 'message'
276
+ containing the sanitized message text.
277
+ """
278
+ writer = csv.DictWriter(sys.stdout, fieldnames=["message"], lineterminator="\n")
279
+ writer.writeheader()
280
+ writer.writerow({"message": sanitize_for_terminal(result.message)})
281
+
282
+
283
+ def _print_csv_result_streaming(result: CommandResult):
284
+ """Print a single CommandResult as CSV with streaming support"""
285
+ if isinstance(result, CollectionResult):
286
+ _stream_collection_as_csv(result)
287
+ elif isinstance(result, ObjectResult):
288
+ _print_object_result_as_csv(result)
289
+ elif isinstance(result, MessageResult):
290
+ _print_message_result_as_csv(result)
157
291
 
158
292
 
159
293
  def _stream_json(result):
@@ -18,11 +18,12 @@ import os
18
18
  import platform
19
19
  import sys
20
20
  from enum import Enum, unique
21
- from typing import Any, Dict, Union
21
+ from typing import Any, Dict, Optional, Union
22
22
 
23
23
  import click
24
24
  import typer
25
25
  from snowflake.cli import __about__
26
+ from snowflake.cli._app.cli_app import INTERNAL_CLI_FLAGS
26
27
  from snowflake.cli._app.constants import PARAM_APPLICATION_NAME
27
28
  from snowflake.cli.api.cli_global_context import (
28
29
  _CliGlobalContextAccess,
@@ -38,6 +39,7 @@ from snowflake.connector.telemetry import (
38
39
  TelemetryField,
39
40
  )
40
41
  from snowflake.connector.time_util import get_time_millis
42
+ from typer import Context
41
43
 
42
44
 
43
45
  @unique
@@ -140,17 +142,30 @@ def _get_command_metrics() -> TelemetryDict:
140
142
  def _find_command_info() -> TelemetryDict:
141
143
  ctx = click.get_current_context()
142
144
  command_path = ctx.command_path.split(" ")[1:]
145
+
146
+ command_flags = {}
147
+ format_value = None
148
+ current_ctx: Optional[Context] = ctx
149
+ while current_ctx:
150
+ for flag, flag_value in current_ctx.params.items():
151
+ if (
152
+ flag_value
153
+ and flag not in command_flags
154
+ and flag not in INTERNAL_CLI_FLAGS
155
+ ):
156
+ command_flags[flag] = current_ctx.get_parameter_source(flag).name # type: ignore[attr-defined]
157
+ if format_value is None and "format" in current_ctx.params:
158
+ format_value = current_ctx.params["format"]
159
+ current_ctx = current_ctx.parent
160
+
161
+ if format_value is None:
162
+ format_value = OutputFormat.TABLE
163
+
143
164
  return {
144
165
  CLITelemetryField.COMMAND: command_path,
145
166
  CLITelemetryField.COMMAND_GROUP: command_path[0],
146
- CLITelemetryField.COMMAND_FLAGS: {
147
- k: ctx.get_parameter_source(k).name # type: ignore[attr-defined]
148
- for k, v in ctx.params.items()
149
- if v # noqa
150
- },
151
- CLITelemetryField.COMMAND_OUTPUT_TYPE: ctx.params.get(
152
- "format", OutputFormat.TABLE
153
- ).value,
167
+ CLITelemetryField.COMMAND_FLAGS: command_flags,
168
+ CLITelemetryField.COMMAND_OUTPUT_TYPE: format_value.value,
154
169
  CLITelemetryField.PROJECT_DEFINITION_VERSION: str(_get_definition_version()),
155
170
  CLITelemetryField.MODE: _get_cli_running_mode(),
156
171
  }
@@ -227,7 +242,7 @@ class CLITelemetryClient:
227
242
  **telemetry_payload,
228
243
  }
229
244
  # To map Enum to string, so we don't have to use .value every time
230
- return {getattr(k, "value", k): v for k, v in data.items()} # type: ignore[arg-type]
245
+ return {getattr(k, "value", k): v for k, v in data.items()} # type: ignore[arg-type, misc]
231
246
 
232
247
  @property
233
248
  def _telemetry(self):
@@ -1,4 +1,3 @@
1
- from snowflake.cli._plugins.auth.keypair.commands import app as keypair_app
2
1
  from snowflake.cli._plugins.auth.oidc.commands import (
3
2
  app as oidc_app,
4
3
  )
@@ -9,5 +8,4 @@ app = SnowTyperFactory(
9
8
  help="Manages authentication methods.",
10
9
  )
11
10
 
12
- app.add_typer(keypair_app)
13
11
  app.add_typer(oidc_app)
@@ -16,9 +16,8 @@ from __future__ import annotations
16
16
 
17
17
  import logging
18
18
  import os.path
19
- from copy import deepcopy
20
19
  from pathlib import Path
21
- from typing import Dict, Optional, Tuple
20
+ from typing import Optional
22
21
 
23
22
  import typer
24
23
  from click import ( # type: ignore
@@ -29,14 +28,10 @@ from click import ( # type: ignore
29
28
  )
30
29
  from click.core import ParameterSource # type: ignore
31
30
  from snowflake import connector
32
- from snowflake.cli._app.snow_connector import connect_to_snowflake
33
- from snowflake.cli._plugins.auth.keypair.commands import KEY_PAIR_DEFAULT_PATH
34
- from snowflake.cli._plugins.auth.keypair.manager import AuthManager
35
31
  from snowflake.cli._plugins.connection.util import (
36
32
  strip_if_value_present,
37
33
  )
38
34
  from snowflake.cli._plugins.object.manager import ObjectManager
39
- from snowflake.cli.api import exceptions
40
35
  from snowflake.cli.api.cli_global_context import get_cli_context
41
36
  from snowflake.cli.api.commands.flags import (
42
37
  PLAIN_PASSWORD_MSG,
@@ -69,15 +64,12 @@ from snowflake.cli.api.config import (
69
64
  )
70
65
  from snowflake.cli.api.console import cli_console
71
66
  from snowflake.cli.api.constants import ObjectType
72
- from snowflake.cli.api.feature_flags import FeatureFlag
73
67
  from snowflake.cli.api.output.types import (
74
68
  CollectionResult,
75
69
  CommandResult,
76
70
  MessageResult,
77
71
  ObjectResult,
78
72
  )
79
- from snowflake.cli.api.secret import SecretType
80
- from snowflake.cli.api.secure_path import SecurePath
81
73
  from snowflake.connector import ProgrammingError
82
74
  from snowflake.connector.constants import CONNECTIONS_FILE
83
75
 
@@ -302,13 +294,6 @@ def add(
302
294
  if connection_exists(connection_name):
303
295
  raise UsageError(f"Connection {connection_name} already exists")
304
296
 
305
- if FeatureFlag.ENABLE_AUTH_KEYPAIR.is_enabled() and not no_interactive:
306
- connection_options, keypair_error = _extend_add_with_key_pair(
307
- connection_name, connection_options
308
- )
309
- else:
310
- keypair_error = ""
311
-
312
297
  connections_file = add_connection_to_proper_file(
313
298
  connection_name,
314
299
  ConnectionConfig(**connection_options),
@@ -316,12 +301,6 @@ def add(
316
301
  if set_as_default:
317
302
  set_config_value(path=["default_connection_name"], value=connection_name)
318
303
 
319
- if keypair_error:
320
- return MessageResult(
321
- f"Wrote new password-based connection {connection_name} to {connections_file}, "
322
- f"however there were some issues during key pair setup. Review the following error and check 'snow auth keypair' "
323
- f"commands to setup key pair authentication:\n * {keypair_error}"
324
- )
325
304
  return MessageResult(
326
305
  f"Wrote new connection {connection_name} to {connections_file}"
327
306
  )
@@ -459,59 +438,3 @@ def generate_jwt(
459
438
  return MessageResult(token)
460
439
  except (ValueError, TypeError) as err:
461
440
  raise ClickException(str(err))
462
-
463
-
464
- def _extend_add_with_key_pair(
465
- connection_name: str, connection_options: Dict
466
- ) -> Tuple[Dict, str]:
467
- if not _should_extend_with_key_pair(connection_options):
468
- return connection_options, ""
469
-
470
- configure_key_pair = typer.confirm(
471
- "Do you want to configure key pair authentication?",
472
- default=False,
473
- )
474
- if not configure_key_pair:
475
- return connection_options, ""
476
-
477
- key_length = typer.prompt(
478
- "Key length",
479
- default=2048,
480
- show_default=True,
481
- )
482
-
483
- output_path = typer.prompt(
484
- "Output path",
485
- default=KEY_PAIR_DEFAULT_PATH,
486
- show_default=True,
487
- value_proc=lambda value: SecurePath(value),
488
- )
489
- private_key_passphrase = typer.prompt(
490
- "Private key passphrase",
491
- default="",
492
- hide_input=True,
493
- show_default=False,
494
- value_proc=lambda value: SecretType(value),
495
- )
496
- connection = connect_to_snowflake(temporary_connection=True, **connection_options)
497
- try:
498
- connection_options = AuthManager(connection=connection).extend_connection_add(
499
- connection_name=connection_name,
500
- connection_options=deepcopy(connection_options),
501
- key_length=key_length,
502
- output_path=output_path,
503
- private_key_passphrase=private_key_passphrase,
504
- )
505
- except exceptions.CouldNotSetKeyPairError:
506
- return connection_options, "The public key is set already."
507
- except Exception as e:
508
- return connection_options, str(e)
509
- return connection_options, ""
510
-
511
-
512
- def _should_extend_with_key_pair(connection_options: Dict) -> bool:
513
- return (
514
- connection_options.get("password") is not None
515
- and connection_options.get("private_key_file") is None
516
- and connection_options.get("private_key_path") is None
517
- )
@@ -19,7 +19,6 @@ from typing import Optional
19
19
 
20
20
  import typer
21
21
  from click import types
22
- from rich.progress import Progress, SpinnerColumn, TextColumn
23
22
  from snowflake.cli._plugins.dbt.constants import (
24
23
  DBT_COMMANDS,
25
24
  OUTPUT_COLUMN_NAME,
@@ -31,10 +30,11 @@ from snowflake.cli._plugins.object.command_aliases import add_object_command_ali
31
30
  from snowflake.cli._plugins.object.commands import scope_option
32
31
  from snowflake.cli.api.commands.decorators import global_options_with_connection
33
32
  from snowflake.cli.api.commands.flags import identifier_argument, like_option
33
+ from snowflake.cli.api.commands.overrideable_parameter import OverrideableOption
34
34
  from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
35
+ from snowflake.cli.api.console.console import cli_console
35
36
  from snowflake.cli.api.constants import ObjectType
36
37
  from snowflake.cli.api.exceptions import CliError
37
- from snowflake.cli.api.feature_flags import FeatureFlag
38
38
  from snowflake.cli.api.identifiers import FQN
39
39
  from snowflake.cli.api.output.types import (
40
40
  CommandResult,
@@ -46,8 +46,6 @@ from snowflake.cli.api.secure_path import SecurePath
46
46
  app = SnowTyperFactory(
47
47
  name="dbt",
48
48
  help="Manages dbt on Snowflake projects.",
49
- is_hidden=FeatureFlag.ENABLE_DBT.is_disabled,
50
- preview=True,
51
49
  )
52
50
  log = logging.getLogger(__name__)
53
51
 
@@ -59,6 +57,16 @@ DBTNameArgument = identifier_argument(sf_object="DBT Project", example="my_pipel
59
57
  DBTNameOrCommandArgument = identifier_argument(
60
58
  sf_object="DBT Project", example="my_pipeline", click_type=types.StringParamType()
61
59
  )
60
+ DefaultTargetOption = OverrideableOption(
61
+ None,
62
+ "--default-target",
63
+ mutually_exclusive=["unset_default_target"],
64
+ )
65
+ UnsetDefaultTargetOption = OverrideableOption(
66
+ False,
67
+ "--unset-default-target",
68
+ mutually_exclusive=["default_target"],
69
+ )
62
70
 
63
71
  add_object_command_aliases(
64
72
  app=app,
@@ -68,7 +76,7 @@ add_object_command_aliases(
68
76
  help_example='`list --like "my%"` lists all dbt projects that begin with "my"'
69
77
  ),
70
78
  scope_option=scope_option(help_example="`list --in database my_db`"),
71
- ommit_commands=["drop", "create", "describe"],
79
+ ommit_commands=["create"],
72
80
  )
73
81
 
74
82
 
@@ -92,21 +100,45 @@ def deploy_dbt(
92
100
  False,
93
101
  help="Overwrites conflicting files in the project, if any.",
94
102
  ),
103
+ default_target: Optional[str] = DefaultTargetOption(
104
+ help="Default target for the dbt project. Mutually exclusive with --unset-default-target.",
105
+ ),
106
+ unset_default_target: Optional[bool] = UnsetDefaultTargetOption(
107
+ help="Unset the default target for the dbt project. Mutually exclusive with --default-target.",
108
+ ),
109
+ external_access_integrations: Optional[list[str]] = typer.Option(
110
+ None,
111
+ "--external-access-integration",
112
+ show_default=False,
113
+ help="External access integration to be used by the dbt object.",
114
+ ),
115
+ install_local_deps: Optional[bool] = typer.Option(
116
+ False,
117
+ "--install-local-deps",
118
+ show_default=False,
119
+ help="Installs local dependencies from project that don't require external access.",
120
+ ),
95
121
  **options,
96
122
  ) -> CommandResult:
97
123
  """
98
- Copy dbt files and either recreate dbt on Snowflake if `--force` flag is
99
- provided; or create a new one if it doesn't exist; or update files and
100
- create a new version if it exists.
124
+ Upload local dbt project files and create or update a DBT project object on Snowflake.
125
+
126
+ Examples:
127
+ snow dbt deploy PROJECT
128
+ snow dbt deploy PROJECT --source=/Users/jdoe/project --force
101
129
  """
102
130
  project_path = SecurePath(source) if source is not None else SecurePath.cwd()
103
131
  profiles_dir_path = SecurePath(profiles_dir) if profiles_dir else project_path
104
132
  return QueryResult(
105
133
  DBTManager().deploy(
106
134
  name,
107
- project_path.resolve(),
108
- profiles_dir_path.resolve(),
135
+ path=project_path.resolve(),
136
+ profiles_path=profiles_dir_path.resolve(),
109
137
  force=force,
138
+ default_target=default_target,
139
+ unset_default_target=unset_default_target,
140
+ external_access_integrations=external_access_integrations,
141
+ install_local_deps=install_local_deps,
110
142
  )
111
143
  )
112
144
 
@@ -116,7 +148,6 @@ dbt_execute_app = SnowTyperFactory(
116
148
  help="Execute a dbt command on Snowflake. Subcommand name and all "
117
149
  "parameters following it will be passed over to dbt.",
118
150
  subcommand_metavar="DBT_COMMAND",
119
- preview=True,
120
151
  )
121
152
  app.add_typer(dbt_execute_app)
122
153
 
@@ -143,7 +174,6 @@ for cmd in DBT_COMMANDS:
143
174
  context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
144
175
  help=f"Execute {cmd} command on Snowflake. Command name and all parameters following it will be passed over to dbt.",
145
176
  add_help_option=False,
146
- preview=True,
147
177
  )
148
178
  def _dbt_execute(
149
179
  ctx: typer.Context,
@@ -161,13 +191,8 @@ for cmd in DBT_COMMANDS:
161
191
  f"Command submitted. You can check the result with `snow sql -q \"select execution_status from table(information_schema.query_history_by_user()) where query_id in ('{result.sfqid}');\"`"
162
192
  )
163
193
 
164
- with Progress(
165
- SpinnerColumn(),
166
- TextColumn("[progress.description]{task.description}"),
167
- transient=True,
168
- ) as progress:
169
- progress.add_task(description=f"Executing 'dbt {dbt_command}'", total=None)
170
-
194
+ with cli_console.spinner() as spinner:
195
+ spinner.add_task(description=f"Executing 'dbt {dbt_command}'", total=None)
171
196
  result = dbt_manager.execute(*execute_args)
172
197
 
173
198
  try: