snowflake-cli 3.5.0__py3-none-any.whl → 3.7.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 (64) hide show
  1. snowflake/cli/__about__.py +13 -1
  2. snowflake/cli/_app/commands_registration/builtin_plugins.py +4 -0
  3. snowflake/cli/_app/loggers.py +2 -2
  4. snowflake/cli/_app/snow_connector.py +7 -6
  5. snowflake/cli/_app/telemetry.py +3 -15
  6. snowflake/cli/_app/version_check.py +4 -4
  7. snowflake/cli/_plugins/auth/__init__.py +11 -0
  8. snowflake/cli/_plugins/auth/keypair/__init__.py +0 -0
  9. snowflake/cli/_plugins/auth/keypair/commands.py +151 -0
  10. snowflake/cli/_plugins/auth/keypair/manager.py +331 -0
  11. snowflake/cli/_plugins/auth/keypair/plugin_spec.py +30 -0
  12. snowflake/cli/_plugins/connection/commands.py +78 -1
  13. snowflake/cli/_plugins/helpers/commands.py +25 -1
  14. snowflake/cli/_plugins/helpers/snowsl_vars_reader.py +133 -0
  15. snowflake/cli/_plugins/init/commands.py +9 -6
  16. snowflake/cli/_plugins/logs/__init__.py +0 -0
  17. snowflake/cli/_plugins/logs/commands.py +105 -0
  18. snowflake/cli/_plugins/logs/manager.py +107 -0
  19. snowflake/cli/_plugins/logs/plugin_spec.py +16 -0
  20. snowflake/cli/_plugins/logs/utils.py +60 -0
  21. snowflake/cli/_plugins/nativeapp/entities/application.py +4 -1
  22. snowflake/cli/_plugins/nativeapp/sf_sql_facade.py +33 -6
  23. snowflake/cli/_plugins/notebook/commands.py +3 -0
  24. snowflake/cli/_plugins/notebook/notebook_entity.py +16 -27
  25. snowflake/cli/_plugins/object/command_aliases.py +3 -1
  26. snowflake/cli/_plugins/object/manager.py +4 -2
  27. snowflake/cli/_plugins/project/commands.py +89 -48
  28. snowflake/cli/_plugins/project/manager.py +57 -23
  29. snowflake/cli/_plugins/project/project_entity_model.py +22 -3
  30. snowflake/cli/_plugins/snowpark/commands.py +15 -2
  31. snowflake/cli/_plugins/spcs/compute_pool/commands.py +17 -5
  32. snowflake/cli/_plugins/sql/manager.py +43 -52
  33. snowflake/cli/_plugins/sql/source_reader.py +230 -0
  34. snowflake/cli/_plugins/stage/manager.py +25 -12
  35. snowflake/cli/_plugins/streamlit/commands.py +3 -0
  36. snowflake/cli/_plugins/streamlit/manager.py +19 -15
  37. snowflake/cli/api/artifacts/upload.py +30 -34
  38. snowflake/cli/api/artifacts/utils.py +8 -6
  39. snowflake/cli/api/cli_global_context.py +7 -2
  40. snowflake/cli/api/commands/decorators.py +11 -2
  41. snowflake/cli/api/commands/flags.py +35 -4
  42. snowflake/cli/api/commands/snow_typer.py +20 -2
  43. snowflake/cli/api/config.py +5 -3
  44. snowflake/cli/api/constants.py +2 -0
  45. snowflake/cli/api/entities/utils.py +29 -16
  46. snowflake/cli/api/errno.py +1 -0
  47. snowflake/cli/api/exceptions.py +75 -27
  48. snowflake/cli/api/feature_flags.py +1 -0
  49. snowflake/cli/api/identifiers.py +2 -0
  50. snowflake/cli/api/plugins/plugin_config.py +2 -2
  51. snowflake/cli/api/project/schemas/template.py +3 -3
  52. snowflake/cli/api/rendering/project_templates.py +3 -3
  53. snowflake/cli/api/rendering/sql_templates.py +2 -2
  54. snowflake/cli/api/rest_api.py +2 -3
  55. snowflake/cli/{_app → api}/secret.py +4 -1
  56. snowflake/cli/api/secure_path.py +16 -4
  57. snowflake/cli/api/sql_execution.py +8 -4
  58. snowflake/cli/api/utils/definition_rendering.py +14 -8
  59. snowflake/cli/api/utils/templating_functions.py +4 -4
  60. {snowflake_cli-3.5.0.dist-info → snowflake_cli-3.7.0.dist-info}/METADATA +11 -11
  61. {snowflake_cli-3.5.0.dist-info → snowflake_cli-3.7.0.dist-info}/RECORD +64 -52
  62. {snowflake_cli-3.5.0.dist-info → snowflake_cli-3.7.0.dist-info}/WHEEL +0 -0
  63. {snowflake_cli-3.5.0.dist-info → snowflake_cli-3.7.0.dist-info}/entry_points.txt +0 -0
  64. {snowflake_cli-3.5.0.dist-info → snowflake_cli-3.7.0.dist-info}/licenses/LICENSE +0 -0
@@ -32,6 +32,7 @@ from snowflake.cli.api.connections import ConnectionContext
32
32
  from snowflake.cli.api.console import cli_console
33
33
  from snowflake.cli.api.identifiers import FQN
34
34
  from snowflake.cli.api.output.formats import OutputFormat
35
+ from snowflake.cli.api.secret import SecretType
35
36
  from snowflake.cli.api.stage_path import StagePath
36
37
 
37
38
  DEFAULT_CONTEXT_SETTINGS = {"help_option_names": ["--help", "-h"]}
@@ -214,7 +215,7 @@ MasterTokenOption = typer.Option(
214
215
  TokenFilePathOption = typer.Option(
215
216
  None,
216
217
  "--token-file-path",
217
- help="Path to file with an OAuth token that should be used when connecting to Snowflake",
218
+ help="Path to file with an OAuth token to use when connecting to Snowflake.",
218
219
  callback=_connection_callback("token_file_path"),
219
220
  show_default=False,
220
221
  rich_help_panel=_CONNECTION_SECTION,
@@ -324,7 +325,7 @@ def _diag_log_allowlist_path_callback(path: str):
324
325
  DiagAllowlistPathOption: Path = typer.Option(
325
326
  None,
326
327
  "--diag-allowlist-path",
327
- help="Path to a JSON file containing allowlist parameters.",
328
+ help="Path to a JSON file that contains allowlist parameters.",
328
329
  callback=_diag_log_allowlist_path_callback,
329
330
  show_default=False,
330
331
  rich_help_panel=_CONNECTION_SECTION,
@@ -370,6 +371,17 @@ DebugOption = typer.Option(
370
371
  rich_help_panel=_CLI_BEHAVIOUR,
371
372
  )
372
373
 
374
+ EnhancedExitCodesOption = typer.Option(
375
+ False,
376
+ "--enhanced-exit-codes",
377
+ help="Differentiate exit error codes based on failure type.",
378
+ callback=_context_callback("enhanced_exit_codes"),
379
+ is_flag=True,
380
+ rich_help_panel=_CLI_BEHAVIOUR,
381
+ is_eager=True,
382
+ envvar="SNOWFLAKE_ENHANCED_EXIT_CODES",
383
+ )
384
+
373
385
 
374
386
  # If IfExistsOption, IfNotExistsOption, or ReplaceOption are used with names other than those in CREATE_MODE_OPTION_NAMES,
375
387
  # you must also override mutually_exclusive if you want to retain the validation that at most one of these flags is
@@ -411,9 +423,19 @@ OnErrorOption = typer.Option(
411
423
 
412
424
  NoInteractiveOption = typer.Option(False, "--no-interactive", help="Disable prompting.")
413
425
 
426
+ PruneOption = OverrideableOption(
427
+ False,
428
+ "--prune",
429
+ help=f"Delete files that exist in the stage, but not in the local filesystem.",
430
+ show_default=False,
431
+ )
432
+
414
433
 
415
- def entity_argument(entity_type: str) -> typer.Argument:
416
- return typer.Argument(None, help=f"ID of {entity_type} entity.")
434
+ def entity_argument(entity_type: str, required=False) -> typer.Argument:
435
+ _help = f"ID of {entity_type} entity."
436
+ if not required:
437
+ return typer.Argument(None, help=_help)
438
+ return typer.Argument(..., help=_help, show_default=False)
417
439
 
418
440
 
419
441
  def variables_option(description: str):
@@ -501,6 +523,15 @@ class IdentifierStagePathType(click.ParamType):
501
523
  return StagePath.from_stage_str(value)
502
524
 
503
525
 
526
+ class SecretTypeParser(click.ParamType):
527
+ name = "TEXT"
528
+
529
+ def convert(self, value, param, ctx):
530
+ if not isinstance(value, SecretType):
531
+ return SecretType(value)
532
+ return value
533
+
534
+
504
535
  def identifier_argument(
505
536
  sf_object: str,
506
537
  example: str,
@@ -31,7 +31,13 @@ from snowflake.cli.api.commands.execution_metadata import (
31
31
  ExecutionStatus,
32
32
  )
33
33
  from snowflake.cli.api.commands.flags import DEFAULT_CONTEXT_SETTINGS
34
- from snowflake.cli.api.exceptions import CommandReturnTypeError
34
+ from snowflake.cli.api.exceptions import (
35
+ BaseCliError,
36
+ CliArgumentError,
37
+ CliError,
38
+ CliSqlError,
39
+ CommandReturnTypeError,
40
+ )
35
41
  from snowflake.cli.api.output.types import CommandResult
36
42
  from snowflake.cli.api.sanitizers import sanitize_for_terminal
37
43
  from snowflake.cli.api.sql_execution import SqlExecutionMixin
@@ -168,8 +174,20 @@ class SnowTyper(typer.Typer):
168
174
 
169
175
  log.debug("Executing command exception callback")
170
176
  log_command_execution_error(exception, execution)
177
+ exception = SnowTyper._cli_base_exception_migration_dispatcher(exception)
178
+ return exception
179
+
180
+ @staticmethod
181
+ def _cli_base_exception_migration_dispatcher(exception):
182
+ """Handler used for dispatching exception until migration completed."""
183
+ if isinstance(
184
+ exception, (BaseCliError, CliError, CliArgumentError, CliSqlError)
185
+ ):
186
+ return exception
187
+
171
188
  if isinstance(exception, DatabaseError):
172
- return ClickException(exception.msg)
189
+ return CliSqlError(exception.msg)
190
+
173
191
  return exception
174
192
 
175
193
  @staticmethod
@@ -26,7 +26,7 @@ import tomlkit
26
26
  from click import ClickException
27
27
  from snowflake.cli.api.exceptions import (
28
28
  ConfigFileTooWidePermissionsError,
29
- MissingConfiguration,
29
+ MissingConfigurationError,
30
30
  UnsupportedConfigSectionTypeError,
31
31
  )
32
32
  from snowflake.cli.api.secure_path import SecurePath
@@ -252,7 +252,9 @@ def get_connection_dict(connection_name: str) -> dict:
252
252
  try:
253
253
  return get_config_section(CONNECTIONS_SECTION, connection_name)
254
254
  except KeyError:
255
- raise MissingConfiguration(f"Connection {connection_name} is not configured")
255
+ raise MissingConfigurationError(
256
+ f"Connection {connection_name} is not configured"
257
+ )
256
258
 
257
259
 
258
260
  def get_default_connection_name() -> str:
@@ -262,7 +264,7 @@ def get_default_connection_name() -> str:
262
264
  def get_default_connection_dict() -> dict:
263
265
  def_connection_name = get_default_connection_name()
264
266
  if not connection_exists(def_connection_name):
265
- raise MissingConfiguration(
267
+ raise MissingConfigurationError(
266
268
  f"Couldn't find connection for default connection `{def_connection_name}`. "
267
269
  f"Specify connection name or configure default connection."
268
270
  )
@@ -47,6 +47,7 @@ class ObjectType(Enum):
47
47
  NETWORK_RULE = ObjectNames("network-rule", "network rule", "network rules")
48
48
  NOTEBOOK = ObjectNames("notebook", "notebook", "notebooks")
49
49
  PROCEDURE = ObjectNames("procedure", "procedure", "procedures")
50
+ PROJECT = ObjectNames("project", "project", "projects")
50
51
  ROLE = ObjectNames("role", "role", "roles")
51
52
  SCHEMA = ObjectNames("schema", "schema", "schemas")
52
53
  SERVICE = ObjectNames("service", "service", "services")
@@ -77,6 +78,7 @@ OBJECT_TO_NAMES = {o.value.cli_name: o.value for o in ObjectType}
77
78
  UNSUPPORTED_OBJECTS = {
78
79
  ObjectType.APPLICATION.value.cli_name,
79
80
  ObjectType.APPLICATION_PACKAGE.value.cli_name,
81
+ ObjectType.PROJECT.value.cli_name,
80
82
  }
81
83
  SUPPORTED_OBJECTS = sorted(OBJECT_TO_NAMES.keys() - UNSUPPORTED_OBJECTS)
82
84
 
@@ -19,7 +19,10 @@ from snowflake.cli._plugins.stage.diff import (
19
19
  sync_local_diff_with_stage,
20
20
  to_stage_path,
21
21
  )
22
- from snowflake.cli._plugins.stage.manager import DefaultStagePathParts
22
+ from snowflake.cli._plugins.stage.manager import (
23
+ StageManager,
24
+ StagePathParts,
25
+ )
23
26
  from snowflake.cli._plugins.stage.utils import print_diff_to_console
24
27
  from snowflake.cli.api.artifacts.bundle_map import BundleMap
25
28
  from snowflake.cli.api.cli_global_context import get_cli_context, span
@@ -33,6 +36,7 @@ from snowflake.cli.api.exceptions import (
33
36
  NoWarehouseSelectedInSessionError,
34
37
  SnowflakeSQLExecutionError,
35
38
  )
39
+ from snowflake.cli.api.identifiers import FQN
36
40
  from snowflake.cli.api.metrics import CLICounterField
37
41
  from snowflake.cli.api.project.schemas.entities.common import PostDeployHook
38
42
  from snowflake.cli.api.rendering.sql_templates import (
@@ -79,12 +83,12 @@ def _get_stage_paths_to_sync(
79
83
  def sync_deploy_root_with_stage(
80
84
  console: AbstractConsole,
81
85
  deploy_root: Path,
82
- package_name: str,
83
86
  bundle_map: BundleMap,
84
- role: str,
85
87
  prune: bool,
86
88
  recursive: bool,
87
- stage_path: DefaultStagePathParts,
89
+ stage_path: StagePathParts,
90
+ role: str | None = None,
91
+ package_name: str | None = None,
88
92
  local_paths_to_sync: List[Path] | None = None,
89
93
  print_diff: bool = True,
90
94
  ) -> DiffResult:
@@ -99,6 +103,8 @@ def sync_deploy_root_with_stage(
99
103
  recursive (bool): Whether to traverse directories recursively.
100
104
  stage_path (DefaultStagePathParts): stage path object.
101
105
 
106
+ package_name (str): supported for Native App compatibility. Should be None out of Native App context.
107
+
102
108
  local_paths_to_sync (List[Path], optional): List of local paths to sync. Defaults to None to sync all
103
109
  local paths. Note that providing an empty list here is equivalent to None.
104
110
  print_diff (bool): Whether to print the diff between the local files and the remote stage. Defaults to True
@@ -106,17 +112,24 @@ def sync_deploy_root_with_stage(
106
112
  Returns:
107
113
  A `DiffResult` instance describing the changes that were performed.
108
114
  """
109
- sql_facade = get_snowflake_facade()
110
- schema = stage_path.schema
111
- stage_fqn = stage_path.stage
112
- # Does a stage already exist within the application package, or we need to create one?
113
- # Using "if not exists" should take care of either case.
114
- console.step(
115
- f"Checking if stage {stage_fqn} exists, or creating a new one if none exists."
116
- )
117
- if not sql_facade.stage_exists(stage_fqn):
118
- sql_facade.create_schema(schema, database=package_name)
119
- sql_facade.create_stage(stage_fqn)
115
+ if not package_name:
116
+ # ensure stage exists
117
+ stage_fqn = FQN.from_stage(stage_path.stage)
118
+ console.step(f"Creating stage {stage_fqn} if not exists.")
119
+ StageManager().create(fqn=stage_fqn)
120
+ else:
121
+ # ensure stage exists - nativeapp behavior
122
+ sql_facade = get_snowflake_facade()
123
+ schema = stage_path.schema
124
+ stage_fqn = stage_path.stage
125
+ # Does a stage already exist within the application package, or we need to create one?
126
+ # Using "if not exists" should take care of either case.
127
+ console.step(
128
+ f"Checking if stage {stage_fqn} exists, or creating a new one if none exists."
129
+ )
130
+ if not sql_facade.stage_exists(stage_fqn):
131
+ sql_facade.create_schema(schema, database=package_name)
132
+ sql_facade.create_stage(stage_fqn)
120
133
 
121
134
  # Perform a diff operation and display results to the user for informational purposes
122
135
  if print_diff:
@@ -178,7 +191,7 @@ def sync_deploy_root_with_stage(
178
191
  if print_diff:
179
192
  print_diff_to_console(diff, bundle_map)
180
193
 
181
- # Upload diff-ed files to application package stage
194
+ # Upload diff-ed files to the stage
182
195
  if diff.has_changes():
183
196
  console.step(
184
197
  "Updating the Snowflake stage from your local %s directory."
@@ -78,6 +78,7 @@ MAX_VERSIONS_IN_RELEASE_CHANNEL_REACHED = 512004
78
78
  MAX_UNBOUND_VERSIONS_REACHED = 512023
79
79
  CANNOT_DEREGISTER_VERSION_ASSOCIATED_WITH_CHANNEL = 512021
80
80
  TARGET_ACCOUNT_USED_BY_OTHER_RELEASE_DIRECTIVE = 93091
81
+ CANNOT_SET_DEBUG_MODE_WITH_MANIFEST_VERSION = 93362
81
82
 
82
83
 
83
84
  ERR_JAVASCRIPT_EXECUTION = 100132
@@ -22,46 +22,87 @@ from snowflake.cli.api.constants import ObjectType
22
22
  from snowflake.connector.compat import IS_WINDOWS
23
23
 
24
24
 
25
- class EnvironmentVariableNotFoundError(ClickException):
25
+ class BaseCliError(ClickException):
26
+ """Base Cli Exception.
27
+
28
+ 0 Everything ran smoothly.
29
+ 1 Something went wrong with the client.
30
+ 2 Something went wrong with command line arguments.
31
+ 3 Cli could not connect to server.
32
+ 4 Cli could not communicate properly with server.
33
+ 5 The enhanced_exit_codes parameter was set and Cli exited because of error.
34
+ """
35
+
36
+ def __init__(self, *args, **kwargs):
37
+ from snowflake.cli.api.cli_global_context import get_cli_context
38
+
39
+ if not get_cli_context().enhanced_exit_codes:
40
+ self.exit_code = kwargs.pop("exit_code", 1)
41
+ super().__init__(*args, **kwargs)
42
+
43
+
44
+ class CliError(BaseCliError):
45
+ """Generic Cli Error - to be used in favour of ClickException."""
46
+
47
+ exit_code = 1
48
+
49
+
50
+ class CliArgumentError(BaseCliError):
51
+ exit_code = 2
52
+
53
+
54
+ class CliConnectionError(BaseCliError):
55
+ exit_code = 3
56
+
57
+
58
+ class CliCommunicationError(BaseCliError):
59
+ exit_code = 4
60
+
61
+
62
+ class CliSqlError(BaseCliError):
63
+ exit_code = 5
64
+
65
+
66
+ class EnvironmentVariableNotFoundError(CliError):
26
67
  def __init__(self, env_variable_name: str):
27
68
  super().__init__(f"Environment variable {env_variable_name} not found")
28
69
 
29
70
 
30
- class MissingConfiguration(ClickException):
71
+ class MissingConfigurationError(CliError):
31
72
  pass
32
73
 
33
74
 
34
- class CycleDetectedError(ClickException):
75
+ class CycleDetectedError(CliError):
35
76
  pass
36
77
 
37
78
 
38
- class InvalidTemplate(ClickException):
79
+ class InvalidTemplateError(CliError):
39
80
  pass
40
81
 
41
82
 
42
- class InvalidConnectionConfiguration(ClickException):
83
+ class InvalidConnectionConfigurationError(CliError):
43
84
  def format_message(self):
44
85
  return f"Invalid connection configuration. {self.message}"
45
86
 
46
87
 
47
- class InvalidLogsConfiguration(ClickException):
88
+ class InvalidLogsConfigurationError(CliError):
48
89
  def format_message(self):
49
90
  return f"Invalid logs configuration. {self.message}"
50
91
 
51
92
 
52
- class InvalidPluginConfiguration(ClickException):
93
+ class InvalidPluginConfigurationError(CliError):
53
94
  def format_message(self):
54
95
  return f"Invalid plugin configuration. {self.message}"
55
96
 
56
97
 
57
- class PluginNotInstalledError(ClickException):
98
+ class PluginNotInstalledError(CliError):
58
99
  def __init__(self, plugin_name, installed_plugins: List[str]):
59
100
  super().__init__(
60
101
  f"Plugin {plugin_name} is not installed. Available plugins: {', '.join(installed_plugins)}."
61
102
  )
62
103
 
63
104
 
64
- class SnowflakeConnectionError(ClickException):
105
+ class SnowflakeConnectionError(CliError):
65
106
  def __init__(self, snowflake_err: Exception):
66
107
  super().__init__(f"Could not connect to Snowflake. Reason: {snowflake_err}")
67
108
 
@@ -71,17 +112,17 @@ class UnsupportedConfigSectionTypeError(Exception):
71
112
  super().__init__(f"Unsupported configuration section type {section_type}")
72
113
 
73
114
 
74
- class OutputDataTypeError(ClickException):
115
+ class OutputDataTypeError(CliError):
75
116
  def __init__(self, got_type: type, expected_type: type):
76
117
  super().__init__(f"Got {got_type} type but expected {expected_type}")
77
118
 
78
119
 
79
- class CommandReturnTypeError(ClickException):
120
+ class CommandReturnTypeError(CliError):
80
121
  def __init__(self, got_type: type):
81
122
  super().__init__(f"Commands have to return OutputData type, but got {got_type}")
82
123
 
83
124
 
84
- class SnowflakeSQLExecutionError(ClickException):
125
+ class SnowflakeSQLExecutionError(CliError):
85
126
  """
86
127
  Could not successfully execute the Snowflake SQL statements.
87
128
  """
@@ -95,7 +136,7 @@ class SnowflakeSQLExecutionError(ClickException):
95
136
  )
96
137
 
97
138
 
98
- class ObjectAlreadyExistsError(ClickException):
139
+ class ObjectAlreadyExistsError(CliError):
99
140
  def __init__(
100
141
  self,
101
142
  object_type: ObjectType,
@@ -108,45 +149,45 @@ class ObjectAlreadyExistsError(ClickException):
108
149
  super().__init__(msg)
109
150
 
110
151
 
111
- class NoProjectDefinitionError(ClickException):
152
+ class NoProjectDefinitionError(CliError):
112
153
  def __init__(self, project_type: str, project_root: str | Path):
113
154
  super().__init__(
114
155
  f"No {project_type} project definition found in {project_root}"
115
156
  )
116
157
 
117
158
 
118
- class InvalidProjectDefinitionVersionError(ClickException):
159
+ class InvalidProjectDefinitionVersionError(CliError):
119
160
  def __init__(self, expected_version: str, actual_version: str):
120
161
  super().__init__(
121
162
  f"This command only supports definition version {expected_version}, got {actual_version}."
122
163
  )
123
164
 
124
165
 
125
- class InvalidSchemaError(ClickException):
166
+ class InvalidSchemaError(CliError):
126
167
  def __init__(self, schema: str):
127
168
  super().__init__(f"Invalid schema {schema}")
128
169
 
129
170
 
130
- class SecretsWithoutExternalAccessIntegrationError(ClickException):
171
+ class SecretsWithoutExternalAccessIntegrationError(CliError):
131
172
  def __init__(self, object_name: str):
132
173
  super().__init__(
133
174
  f"{object_name} defined with secrets but without external integration."
134
175
  )
135
176
 
136
177
 
137
- class FileTooLargeError(ClickException):
178
+ class FileTooLargeError(CliError):
138
179
  def __init__(self, path: Path, size_limit_in_kb: int):
139
180
  super().__init__(
140
181
  f"File {path} is too large (size limit: {size_limit_in_kb} KB)"
141
182
  )
142
183
 
143
184
 
144
- class DirectoryIsNotEmptyError(ClickException):
185
+ class DirectoryIsNotEmptyError(CliError):
145
186
  def __init__(self, path: Path):
146
187
  super().__init__(f"Directory '{path}' is not empty")
147
188
 
148
189
 
149
- class ConfigFileTooWidePermissionsError(ClickException):
190
+ class ConfigFileTooWidePermissionsError(CliError):
150
191
  def __init__(self, path: Path):
151
192
  change_permissons_command = (
152
193
  f'icacls "{path}" /deny <USER_ID>:F'
@@ -162,26 +203,26 @@ class ConfigFileTooWidePermissionsError(ClickException):
162
203
  super().__init__(msg)
163
204
 
164
205
 
165
- class DatabaseNotProvidedError(ClickException):
206
+ class DatabaseNotProvidedError(CliError):
166
207
  def __init__(self):
167
208
  super().__init__(
168
209
  "Database not specified. Please update connection to add `database` parameter, or re-run command using `--database` option. Use `snow connection list` to list existing connections."
169
210
  )
170
211
 
171
212
 
172
- class SchemaNotProvidedError(ClickException):
213
+ class SchemaNotProvidedError(CliError):
173
214
  def __init__(self):
174
215
  super().__init__(
175
216
  "Schema not specified. Please update connection to add `schema` parameter, or re-run command using `--schema` option. Use `snow connection list` to list existing connections."
176
217
  )
177
218
 
178
219
 
179
- class FQNNameError(ClickException):
220
+ class FQNNameError(CliError):
180
221
  def __init__(self, name: str):
181
222
  super().__init__(f"Specified name '{name}' is not valid name.")
182
223
 
183
224
 
184
- class FQNInconsistencyError(ClickException):
225
+ class FQNInconsistencyError(CliError):
185
226
  def __init__(self, part: str, name: str):
186
227
  super().__init__(
187
228
  f"{part.capitalize()} provided but name '{name}' is fully qualified name."
@@ -206,7 +247,7 @@ class UnmetParametersError(UsageError):
206
247
  )
207
248
 
208
249
 
209
- class NoWarehouseSelectedInSessionError(ClickException):
250
+ class NoWarehouseSelectedInSessionError(CliError):
210
251
  def __init__(self, msg: str):
211
252
  super().__init__(
212
253
  "Received the following error message while executing SQL statement:\n"
@@ -215,7 +256,7 @@ class NoWarehouseSelectedInSessionError(ClickException):
215
256
  )
216
257
 
217
258
 
218
- class DoesNotExistOrUnauthorizedError(ClickException):
259
+ class DoesNotExistOrUnauthorizedError(CliError):
219
260
  def __init__(self, msg: str):
220
261
  super().__init__(
221
262
  "Received the following error message while executing SQL statement:\n"
@@ -224,7 +265,7 @@ class DoesNotExistOrUnauthorizedError(ClickException):
224
265
  )
225
266
 
226
267
 
227
- class CouldNotUseObjectError(ClickException):
268
+ class CouldNotUseObjectError(CliError):
228
269
  def __init__(self, object_type: ObjectType, name: str):
229
270
  super().__init__(
230
271
  f"Could not use {object_type} {name}. Object does not exist, or operation cannot be performed."
@@ -236,3 +277,10 @@ class ShowSpecificObjectMultipleRowsError(RuntimeError):
236
277
  super().__init__(
237
278
  f"Received multiple rows from result of SQL statement: {show_obj_query}. Usage of 'show_specific_object' may not be properly scoped."
238
279
  )
280
+
281
+
282
+ class CouldNotSetKeyPairError(CliError):
283
+ def __init__(self):
284
+ super().__init__(
285
+ "The public key is set already. Use the rotate command instead."
286
+ )
@@ -68,3 +68,4 @@ class FeatureFlag(FeatureFlagMixin):
68
68
  )
69
69
  ENABLE_SNOWPARK_GLOB_SUPPORT = BooleanFlag("ENABLE_SNOWPARK_GLOB_SUPPORT", False)
70
70
  ENABLE_SPCS_SERVICE_EVENTS = BooleanFlag("ENABLE_SPCS_SERVICE_EVENTS", False)
71
+ ENABLE_AUTH_KEYPAIR = BooleanFlag("ENABLE_AUTH_KEYPAIR", False)
@@ -122,6 +122,8 @@ class FQN:
122
122
  name = stage
123
123
  if stage.startswith("@"):
124
124
  name = stage[1:]
125
+ if stage.startswith("snow://"):
126
+ name = stage[7:]
125
127
  if stage.startswith("~"):
126
128
  return cls(name="~", database=None, schema=None)
127
129
  return cls.from_string(name)
@@ -25,7 +25,7 @@ from snowflake.cli.api.config import (
25
25
  get_config_value,
26
26
  get_plugins_config,
27
27
  )
28
- from snowflake.cli.api.exceptions import InvalidPluginConfiguration
28
+ from snowflake.cli.api.exceptions import InvalidPluginConfigurationError
29
29
 
30
30
 
31
31
  @dataclass
@@ -66,6 +66,6 @@ class PluginConfigProvider:
66
66
 
67
67
  def _assert_value_is_bool(value, *, value_name: str, plugin_name: str) -> None:
68
68
  if type(value) is not bool:
69
- raise InvalidPluginConfiguration(
69
+ raise InvalidPluginConfigurationError(
70
70
  f'[{plugin_name}]: "{value_name}" must be a boolean'
71
71
  )
@@ -19,7 +19,7 @@ from typing import Any, List, Literal, Optional, Union
19
19
  import typer
20
20
  from click import ClickException
21
21
  from pydantic import BaseModel, Field
22
- from snowflake.cli.api.exceptions import InvalidTemplate
22
+ from snowflake.cli.api.exceptions import InvalidTemplateError
23
23
  from snowflake.cli.api.secure_path import SecurePath
24
24
 
25
25
 
@@ -68,10 +68,10 @@ class Template(BaseModel):
68
68
  for path_in_template in self.files_to_render:
69
69
  full_path = template_root / path_in_template
70
70
  if not full_path.exists():
71
- raise InvalidTemplate(
71
+ raise InvalidTemplateError(
72
72
  f"[files_to_render] contains not-existing file: {path_in_template}"
73
73
  )
74
74
  if full_path.is_dir():
75
- raise InvalidTemplate(
75
+ raise InvalidTemplateError(
76
76
  f"[files_to_render] contains a dictionary: {path_in_template}"
77
77
  )
@@ -24,7 +24,7 @@ from jinja2 import (
24
24
  UndefinedError,
25
25
  loaders,
26
26
  )
27
- from snowflake.cli.api.exceptions import InvalidTemplate
27
+ from snowflake.cli.api.exceptions import InvalidTemplateError
28
28
  from snowflake.cli.api.rendering.jinja import IgnoreAttrEnvironment, env_bootstrap
29
29
  from snowflake.cli.api.secure_path import SecurePath
30
30
 
@@ -90,9 +90,9 @@ def render_template_files(
90
90
  full_path = template_root / path
91
91
  full_path.write_text(rendered_result)
92
92
  except TemplateSyntaxError as err:
93
- raise InvalidTemplate(
93
+ raise InvalidTemplateError(
94
94
  f"Invalid template syntax in line {err.lineno} of file {path}:\n"
95
95
  f"{err.message}"
96
96
  )
97
97
  except UndefinedError as err:
98
- raise InvalidTemplate(err.message)
98
+ raise InvalidTemplateError(err.message)
@@ -20,7 +20,7 @@ from click import ClickException
20
20
  from jinja2 import Environment, StrictUndefined, loaders, meta
21
21
  from snowflake.cli.api.cli_global_context import get_cli_context
22
22
  from snowflake.cli.api.console.console import cli_console
23
- from snowflake.cli.api.exceptions import InvalidTemplate
23
+ from snowflake.cli.api.exceptions import InvalidTemplateError
24
24
  from snowflake.cli.api.metrics import CLICounterField
25
25
  from snowflake.cli.api.rendering.jinja import (
26
26
  CONTEXT_KEY,
@@ -72,7 +72,7 @@ def choose_sql_jinja_env_based_on_template_syntax(
72
72
  has_new_syntax = _does_template_have_env_syntax(new_syntax_env, template_content)
73
73
  reference_name_str = f" in {reference_name}" if reference_name else ""
74
74
  if has_old_syntax and has_new_syntax:
75
- raise InvalidTemplate(
75
+ raise InvalidTemplateError(
76
76
  f"The SQL query{reference_name_str} mixes {_OLD_SQL_TEMPLATE_START} ... {_OLD_SQL_TEMPLATE_END} syntax"
77
77
  f" and {_SQL_TEMPLATE_START} ... {_SQL_TEMPLATE_END} syntax."
78
78
  )
@@ -155,9 +155,8 @@ class RestApi:
155
155
  raise SchemaNotDefinedException(
156
156
  "Schema not defined in connection. Please try again with `--schema` flag."
157
157
  )
158
- # temporarily disable this check due to an issue on server side: SNOW-1747450
159
- # if not self._schema_exists(db_name=db, schema_name=schema):
160
- # raise SchemaNotExistsException(f"Schema '{schema}' does not exist.")
158
+ if not self._schema_exists(db_name=db, schema_name=schema):
159
+ raise SchemaNotExistsException(f"Schema '{schema}' does not exist.")
161
160
  if self.get_endpoint_exists(
162
161
  url := f"{SF_REST_API_URL_PREFIX}/databases/{self.conn.database}/schemas/{self.conn.schema}/{plural_object_type}/"
163
162
  ):
@@ -5,5 +5,8 @@ class SecretType:
5
5
  def __repr__(self):
6
6
  return "SecretType(***)"
7
7
 
8
- def __str___(self):
8
+ def __str__(self):
9
9
  return "***"
10
+
11
+ def __bool__(self):
12
+ return self.value is not None and self.value != ""