snowflake-cli 3.12.0__py3-none-any.whl → 3.13.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 (29) hide show
  1. snowflake/cli/__about__.py +1 -1
  2. snowflake/cli/_app/cli_app.py +43 -0
  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/telemetry.py +25 -10
  6. snowflake/cli/_plugins/auth/__init__.py +0 -2
  7. snowflake/cli/_plugins/connection/commands.py +1 -78
  8. snowflake/cli/_plugins/dbt/commands.py +15 -19
  9. snowflake/cli/_plugins/dbt/constants.py +1 -1
  10. snowflake/cli/_plugins/dbt/manager.py +163 -68
  11. snowflake/cli/_plugins/nativeapp/entities/application_package.py +4 -1
  12. snowflake/cli/_plugins/object/manager.py +1 -0
  13. snowflake/cli/_plugins/spcs/services/commands.py +19 -1
  14. snowflake/cli/_plugins/spcs/services/manager.py +12 -0
  15. snowflake/cli/_plugins/spcs/services/service_entity_model.py +5 -0
  16. snowflake/cli/_plugins/streamlit/streamlit_entity.py +28 -2
  17. snowflake/cli/_plugins/streamlit/streamlit_entity_model.py +24 -4
  18. snowflake/cli/api/commands/decorators.py +7 -0
  19. snowflake/cli/api/commands/flags.py +24 -1
  20. snowflake/cli/api/feature_flags.py +3 -3
  21. {snowflake_cli-3.12.0.dist-info → snowflake_cli-3.13.1.dist-info}/METADATA +4 -4
  22. {snowflake_cli-3.12.0.dist-info → snowflake_cli-3.13.1.dist-info}/RECORD +26 -29
  23. snowflake/cli/_plugins/auth/keypair/__init__.py +0 -0
  24. snowflake/cli/_plugins/auth/keypair/commands.py +0 -153
  25. snowflake/cli/_plugins/auth/keypair/manager.py +0 -331
  26. /snowflake/cli/_plugins/auth/{keypair/plugin_spec.py → plugin_spec.py} +0 -0
  27. {snowflake_cli-3.12.0.dist-info → snowflake_cli-3.13.1.dist-info}/WHEEL +0 -0
  28. {snowflake_cli-3.12.0.dist-info → snowflake_cli-3.13.1.dist-info}/entry_points.txt +0 -0
  29. {snowflake_cli-3.12.0.dist-info → snowflake_cli-3.13.1.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.12.0"
19
+ VERSION = "3.13.1"
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):
@@ -270,8 +285,36 @@ class CliAppFactory:
270
285
  pycharm_debug_server_port=pycharm_debug_server_port,
271
286
  )
272
287
 
288
+ self._validate_internal_flags_excluded_from_telemetry(default)
289
+
273
290
  self._app = app
274
291
  return app
275
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
+
276
319
  def get_click_context(self):
277
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
 
@@ -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
- )
@@ -35,7 +35,6 @@ from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
35
35
  from snowflake.cli.api.console.console import cli_console
36
36
  from snowflake.cli.api.constants import ObjectType
37
37
  from snowflake.cli.api.exceptions import CliError
38
- from snowflake.cli.api.feature_flags import FeatureFlag
39
38
  from snowflake.cli.api.identifiers import FQN
40
39
  from snowflake.cli.api.output.types import (
41
40
  CommandResult,
@@ -47,8 +46,6 @@ from snowflake.cli.api.secure_path import SecurePath
47
46
  app = SnowTyperFactory(
48
47
  name="dbt",
49
48
  help="Manages dbt on Snowflake projects.",
50
- is_hidden=FeatureFlag.ENABLE_DBT.is_disabled,
51
- preview=True,
52
49
  )
53
50
  log = logging.getLogger(__name__)
54
51
 
@@ -79,7 +76,7 @@ add_object_command_aliases(
79
76
  help_example='`list --like "my%"` lists all dbt projects that begin with "my"'
80
77
  ),
81
78
  scope_option=scope_option(help_example="`list --in database my_db`"),
82
- ommit_commands=["drop", "create", "describe"],
79
+ ommit_commands=["create"],
83
80
  )
84
81
 
85
82
 
@@ -105,42 +102,43 @@ def deploy_dbt(
105
102
  ),
106
103
  default_target: Optional[str] = DefaultTargetOption(
107
104
  help="Default target for the dbt project. Mutually exclusive with --unset-default-target.",
108
- hidden=FeatureFlag.ENABLE_DBT_GA_FEATURES.is_disabled(),
109
105
  ),
110
106
  unset_default_target: Optional[bool] = UnsetDefaultTargetOption(
111
107
  help="Unset the default target for the dbt project. Mutually exclusive with --default-target.",
112
- hidden=FeatureFlag.ENABLE_DBT_GA_FEATURES.is_disabled(),
113
108
  ),
114
109
  external_access_integrations: Optional[list[str]] = typer.Option(
115
110
  None,
116
111
  "--external-access-integration",
117
112
  show_default=False,
118
113
  help="External access integration to be used by the dbt object.",
119
- hidden=FeatureFlag.ENABLE_DBT_GA_FEATURES.is_disabled(),
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
120
  ),
121
121
  **options,
122
122
  ) -> CommandResult:
123
123
  """
124
- Copy dbt files and either recreate dbt on Snowflake if `--force` flag is
125
- provided; or create a new one if it doesn't exist; or update files and
126
- create a new version if it exists.
127
- """
128
- if FeatureFlag.ENABLE_DBT_GA_FEATURES.is_disabled():
129
- default_target = None
130
- unset_default_target = False
131
- external_access_integrations = None
124
+ Upload local dbt project files and create or update a DBT project object on Snowflake.
132
125
 
126
+ Examples:
127
+ snow dbt deploy PROJECT
128
+ snow dbt deploy PROJECT --source=/Users/jdoe/project --force
129
+ """
133
130
  project_path = SecurePath(source) if source is not None else SecurePath.cwd()
134
131
  profiles_dir_path = SecurePath(profiles_dir) if profiles_dir else project_path
135
132
  return QueryResult(
136
133
  DBTManager().deploy(
137
134
  name,
138
- project_path.resolve(),
139
- profiles_dir_path.resolve(),
135
+ path=project_path.resolve(),
136
+ profiles_path=profiles_dir_path.resolve(),
140
137
  force=force,
141
138
  default_target=default_target,
142
139
  unset_default_target=unset_default_target,
143
140
  external_access_integrations=external_access_integrations,
141
+ install_local_deps=install_local_deps,
144
142
  )
145
143
  )
146
144
 
@@ -150,7 +148,6 @@ dbt_execute_app = SnowTyperFactory(
150
148
  help="Execute a dbt command on Snowflake. Subcommand name and all "
151
149
  "parameters following it will be passed over to dbt.",
152
150
  subcommand_metavar="DBT_COMMAND",
153
- preview=True,
154
151
  )
155
152
  app.add_typer(dbt_execute_app)
156
153
 
@@ -177,7 +174,6 @@ for cmd in DBT_COMMANDS:
177
174
  context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
178
175
  help=f"Execute {cmd} command on Snowflake. Command name and all parameters following it will be passed over to dbt.",
179
176
  add_help_option=False,
180
- preview=True,
181
177
  )
182
178
  def _dbt_execute(
183
179
  ctx: typer.Context,
@@ -22,6 +22,7 @@ DBT_COMMANDS = [
22
22
  "deps",
23
23
  "list",
24
24
  "parse",
25
+ "retry",
25
26
  "run",
26
27
  "run-operation",
27
28
  "seed",
@@ -36,6 +37,5 @@ UNSUPPORTED_COMMANDS = [
36
37
  "debug",
37
38
  "docs",
38
39
  "init",
39
- "retry",
40
40
  "source",
41
41
  ]