snowflake-cli 3.4.1__py3-none-any.whl → 3.5.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 (53) hide show
  1. snowflake/cli/__about__.py +1 -1
  2. snowflake/cli/_app/cli_app.py +1 -10
  3. snowflake/cli/_app/commands_registration/builtin_plugins.py +5 -1
  4. snowflake/cli/_app/commands_registration/command_plugins_loader.py +3 -1
  5. snowflake/cli/_app/commands_registration/commands_registration_with_callbacks.py +3 -3
  6. snowflake/cli/_app/printing.py +2 -2
  7. snowflake/cli/_plugins/connection/commands.py +2 -4
  8. snowflake/cli/_plugins/helpers/commands.py +3 -4
  9. snowflake/cli/_plugins/notebook/commands.py +3 -4
  10. snowflake/cli/_plugins/plugin/commands.py +79 -0
  11. snowflake/cli/_plugins/plugin/manager.py +74 -0
  12. snowflake/cli/_plugins/plugin/plugin_spec.py +30 -0
  13. snowflake/cli/_plugins/project/__init__.py +0 -0
  14. snowflake/cli/_plugins/project/commands.py +157 -0
  15. snowflake/cli/{_app/api_impl/plugin/__init__.py → _plugins/project/feature_flags.py} +9 -0
  16. snowflake/cli/_plugins/project/manager.py +76 -0
  17. snowflake/cli/_plugins/project/plugin_spec.py +30 -0
  18. snowflake/cli/_plugins/project/project_entity_model.py +40 -0
  19. snowflake/cli/_plugins/snowpark/commands.py +2 -1
  20. snowflake/cli/_plugins/spcs/compute_pool/commands.py +53 -5
  21. snowflake/cli/_plugins/spcs/compute_pool/compute_pool_entity.py +8 -0
  22. snowflake/cli/_plugins/spcs/compute_pool/compute_pool_entity_model.py +37 -0
  23. snowflake/cli/_plugins/spcs/compute_pool/manager.py +45 -0
  24. snowflake/cli/_plugins/spcs/image_repository/commands.py +29 -0
  25. snowflake/cli/_plugins/spcs/image_repository/image_repository_entity.py +8 -0
  26. snowflake/cli/_plugins/spcs/image_repository/image_repository_entity_model.py +8 -0
  27. snowflake/cli/_plugins/spcs/image_repository/manager.py +1 -1
  28. snowflake/cli/_plugins/spcs/services/commands.py +53 -0
  29. snowflake/cli/_plugins/spcs/services/manager.py +114 -0
  30. snowflake/cli/_plugins/spcs/services/service_entity.py +6 -0
  31. snowflake/cli/_plugins/spcs/services/service_entity_model.py +45 -0
  32. snowflake/cli/_plugins/spcs/services/service_project_paths.py +15 -0
  33. snowflake/cli/_plugins/stage/manager.py +2 -2
  34. snowflake/cli/_plugins/streamlit/commands.py +9 -24
  35. snowflake/cli/_plugins/streamlit/manager.py +5 -36
  36. snowflake/cli/api/artifacts/upload.py +51 -0
  37. snowflake/cli/api/commands/flags.py +24 -9
  38. snowflake/cli/api/commands/snow_typer.py +12 -0
  39. snowflake/cli/api/commands/utils.py +2 -0
  40. snowflake/cli/api/config.py +15 -10
  41. snowflake/cli/api/exceptions.py +8 -1
  42. snowflake/cli/api/feature_flags.py +1 -0
  43. snowflake/cli/api/plugins/plugin_config.py +43 -4
  44. snowflake/cli/api/project/definition_helper.py +31 -0
  45. snowflake/cli/api/project/schemas/entities/entities.py +26 -0
  46. {snowflake_cli-3.4.1.dist-info → snowflake_cli-3.5.0.dist-info}/METADATA +9 -9
  47. {snowflake_cli-3.4.1.dist-info → snowflake_cli-3.5.0.dist-info}/RECORD +51 -36
  48. snowflake/cli/_app/api_impl/plugin/plugin_config_provider_impl.py +0 -66
  49. snowflake/cli/api/__init__.py +0 -48
  50. /snowflake/cli/{_app/api_impl → _plugins/plugin}/__init__.py +0 -0
  51. {snowflake_cli-3.4.1.dist-info → snowflake_cli-3.5.0.dist-info}/WHEEL +0 -0
  52. {snowflake_cli-3.4.1.dist-info → snowflake_cli-3.5.0.dist-info}/entry_points.txt +0 -0
  53. {snowflake_cli-3.4.1.dist-info → snowflake_cli-3.5.0.dist-info}/licenses/LICENSE +0 -0
@@ -16,11 +16,10 @@ from __future__ import annotations
16
16
 
17
17
  import logging
18
18
  from pathlib import Path
19
- from typing import Dict
20
19
 
21
20
  import click
22
21
  import typer
23
- from click import ClickException, UsageError
22
+ from click import ClickException
24
23
  from snowflake.cli._plugins.object.command_aliases import (
25
24
  add_object_command_aliases,
26
25
  scope_option,
@@ -44,6 +43,7 @@ from snowflake.cli.api.commands.flags import (
44
43
  like_option,
45
44
  )
46
45
  from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
46
+ from snowflake.cli.api.commands.utils import get_entity_for_operation
47
47
  from snowflake.cli.api.constants import ObjectType
48
48
  from snowflake.cli.api.exceptions import NoProjectDefinitionError
49
49
  from snowflake.cli.api.identifiers import FQN
@@ -133,7 +133,8 @@ def _default_file_callback(param_name: str):
133
133
  @with_experimental_behaviour()
134
134
  def streamlit_deploy(
135
135
  replace: bool = ReplaceOption(
136
- help="Replace the Streamlit app if it already exists."
136
+ help="Replaces the Streamlit app if it already exists. It only uploads new and overwrites existing files, "
137
+ "but does not remove any files already on the stage."
137
138
  ),
138
139
  entity_id: str = entity_argument("streamlit"),
139
140
  open_: bool = OpenOption,
@@ -155,30 +156,14 @@ def streamlit_deploy(
155
156
  )
156
157
  pd = convert_project_definition_to_v2(cli_context.project_root, pd)
157
158
 
158
- streamlits: Dict[str, StreamlitEntityModel] = pd.get_entities_by_type(
159
- entity_type="streamlit"
159
+ streamlit: StreamlitEntityModel = get_entity_for_operation(
160
+ cli_context=cli_context,
161
+ entity_id=entity_id,
162
+ project_definition=pd,
163
+ entity_type="streamlit",
160
164
  )
161
165
 
162
166
  streamlit_project_paths = StreamlitProjectPaths(cli_context.project_root)
163
-
164
- if not streamlits:
165
- raise NoProjectDefinitionError(
166
- project_type="streamlit", project_root=cli_context.project_root
167
- )
168
-
169
- if entity_id and entity_id not in streamlits:
170
- raise UsageError(f"No '{entity_id}' entity in project definition file.")
171
-
172
- if len(streamlits.keys()) == 1:
173
- entity_id = list(streamlits.keys())[0]
174
-
175
- if entity_id is None:
176
- raise UsageError(
177
- "Multiple Streamlit apps found. Please provide entity id for the operation."
178
- )
179
-
180
- # Get first streamlit
181
- streamlit: StreamlitEntityModel = streamlits[entity_id]
182
167
  url = StreamlitManager().deploy(
183
168
  streamlit=streamlit,
184
169
  streamlit_project_paths=streamlit_project_paths,
@@ -15,7 +15,6 @@
15
15
  from __future__ import annotations
16
16
 
17
17
  import logging
18
- from pathlib import PurePosixPath
19
18
  from typing import List, Optional
20
19
 
21
20
  from click import ClickException
@@ -32,8 +31,7 @@ from snowflake.cli._plugins.streamlit.streamlit_entity_model import (
32
31
  from snowflake.cli._plugins.streamlit.streamlit_project_paths import (
33
32
  StreamlitProjectPaths,
34
33
  )
35
- from snowflake.cli.api.artifacts.bundle_map import BundleMap
36
- from snowflake.cli.api.artifacts.utils import symlink_or_copy
34
+ from snowflake.cli.api.artifacts.upload import put_files
37
35
  from snowflake.cli.api.commands.experimental_behaviour import (
38
36
  experimental_behaviour_enabled,
39
37
  )
@@ -65,40 +63,11 @@ class StreamlitManager(SqlExecutionMixin):
65
63
  artifacts: Optional[List[PathMapping]] = None,
66
64
  ):
67
65
  cli_console.step(f"Deploying files to {stage_root}")
68
- if not artifacts:
69
- return
70
- stage_manager = StageManager()
71
- # We treat the bundle root as deploy root
72
- bundle_map = BundleMap(
73
- project_root=streamlit_project_paths.project_root,
74
- deploy_root=streamlit_project_paths.bundle_root,
66
+ put_files(
67
+ project_paths=streamlit_project_paths,
68
+ stage_root=stage_root,
69
+ artifacts=artifacts,
75
70
  )
76
- for artifact in artifacts:
77
- bundle_map.add(PathMapping(src=str(artifact.src), dest=artifact.dest))
78
-
79
- # Clean up bundle root
80
- streamlit_project_paths.remove_up_bundle_root()
81
-
82
- for (absolute_src, absolute_dest) in bundle_map.all_mappings(
83
- absolute=True, expand_directories=True
84
- ):
85
- if absolute_src.is_file():
86
- # We treat the bundle/streamlit root as deploy root
87
- symlink_or_copy(
88
- absolute_src,
89
- absolute_dest,
90
- deploy_root=streamlit_project_paths.bundle_root,
91
- )
92
- # Temporary solution, will be replaced with diff
93
- stage_path = (
94
- PurePosixPath(absolute_dest)
95
- .relative_to(streamlit_project_paths.bundle_root)
96
- .parent
97
- )
98
- full_stage_path = f"{stage_root}/{stage_path}".rstrip("/")
99
- stage_manager.put(
100
- local_path=absolute_dest, stage_path=full_stage_path, overwrite=True
101
- )
102
71
 
103
72
  def _create_streamlit(
104
73
  self,
@@ -0,0 +1,51 @@
1
+ from pathlib import PurePosixPath
2
+ from typing import List, Optional
3
+
4
+ from snowflake.cli._plugins.stage.manager import StageManager
5
+ from snowflake.cli.api.artifacts.bundle_map import BundleMap
6
+ from snowflake.cli.api.artifacts.utils import symlink_or_copy
7
+ from snowflake.cli.api.console import cli_console
8
+ from snowflake.cli.api.project.project_paths import ProjectPaths
9
+ from snowflake.cli.api.project.schemas.entities.common import PathMapping
10
+
11
+
12
+ def put_files(
13
+ project_paths: ProjectPaths,
14
+ stage_root: str,
15
+ artifacts: Optional[List[PathMapping]] = None,
16
+ ):
17
+ if not artifacts:
18
+ return
19
+ stage_manager = StageManager()
20
+ # We treat the bundle root as deploy root
21
+ bundle_map = BundleMap(
22
+ project_root=project_paths.project_root,
23
+ deploy_root=project_paths.bundle_root,
24
+ )
25
+ for artifact in artifacts:
26
+ bundle_map.add(PathMapping(src=str(artifact.src), dest=artifact.dest))
27
+
28
+ # Clean up bundle root
29
+ project_paths.remove_up_bundle_root()
30
+
31
+ for (absolute_src, absolute_dest) in bundle_map.all_mappings(
32
+ absolute=True, expand_directories=True
33
+ ):
34
+ if absolute_src.is_file():
35
+ # We treat the bundle/streamlit root as deploy root
36
+ symlink_or_copy(
37
+ absolute_src,
38
+ absolute_dest,
39
+ deploy_root=project_paths.bundle_root,
40
+ )
41
+ # Temporary solution, will be replaced with diff
42
+ stage_path = (
43
+ PurePosixPath(absolute_dest)
44
+ .relative_to(project_paths.bundle_root)
45
+ .parent
46
+ )
47
+ full_stage_path = f"{stage_root}/{stage_path}".rstrip("/")
48
+ cli_console.step(f"Uploading {absolute_dest} to {full_stage_path}")
49
+ stage_manager.put(
50
+ local_path=absolute_dest, stage_path=full_stage_path, overwrite=True
51
+ )
@@ -14,7 +14,6 @@
14
14
 
15
15
  from __future__ import annotations
16
16
 
17
- import tempfile
18
17
  from pathlib import Path
19
18
  from typing import Any, Callable, Optional
20
19
 
@@ -277,7 +276,7 @@ MfaPasscodeOption = typer.Option(
277
276
  EnableDiagOption = typer.Option(
278
277
  False,
279
278
  "--enable-diag",
280
- help="Run Python connector diagnostic test",
279
+ help="Whether to generate a connection diagnostic report.",
281
280
  callback=_connection_callback("enable_diag"),
282
281
  show_default=False,
283
282
  is_flag=True,
@@ -286,20 +285,29 @@ EnableDiagOption = typer.Option(
286
285
 
287
286
  # Set default via callback to avoid including tempdir path in generated docs (snow --docs).
288
287
  # Use constant instead of None, as None is removed from telemetry data.
289
- _DIAG_LOG_DEFAULT_VALUE = "<temporary_directory>"
288
+ _DIAG_LOG_DEFAULT_VALUE = "<system_temporary_directory>"
290
289
 
291
290
 
292
291
  def _diag_log_path_callback(path: str):
293
292
  if path == _DIAG_LOG_DEFAULT_VALUE:
293
+ import tempfile
294
+
294
295
  path = tempfile.gettempdir()
295
- get_cli_context_manager().connection_context.diag_log_path = Path(path)
296
- return path
296
+
297
+ absolute_path = Path(path).absolute()
298
+ if not absolute_path.exists():
299
+ # if the path does not exist the report is not generated
300
+ from snowflake.cli.api.secure_path import SecurePath
301
+
302
+ SecurePath(absolute_path).mkdir(parents=True)
303
+
304
+ return _connection_callback("diag_log_path")(absolute_path)
297
305
 
298
306
 
299
307
  DiagLogPathOption: Path = typer.Option(
300
308
  _DIAG_LOG_DEFAULT_VALUE,
301
309
  "--diag-log-path",
302
- help="Diagnostic report path",
310
+ help="Path for the generated report. Defaults to system temporary directory.",
303
311
  callback=_diag_log_path_callback,
304
312
  show_default=False,
305
313
  rich_help_panel=_CONNECTION_SECTION,
@@ -307,11 +315,17 @@ DiagLogPathOption: Path = typer.Option(
307
315
  writable=True,
308
316
  )
309
317
 
318
+
319
+ def _diag_log_allowlist_path_callback(path: str):
320
+ absolute_path = Path(path).absolute() if path else None
321
+ return _connection_callback("diag_allowlist_path")(absolute_path)
322
+
323
+
310
324
  DiagAllowlistPathOption: Path = typer.Option(
311
325
  None,
312
326
  "--diag-allowlist-path",
313
- help="Diagnostic report path to optional allowlist",
314
- callback=_connection_callback("diag_allowlist_path"),
327
+ help="Path to a JSON file containing allowlist parameters.",
328
+ callback=_diag_log_allowlist_path_callback,
315
329
  show_default=False,
316
330
  rich_help_panel=_CONNECTION_SECTION,
317
331
  exists=True,
@@ -492,9 +506,10 @@ def identifier_argument(
492
506
  example: str,
493
507
  click_type: click.ParamType = IdentifierType(),
494
508
  callback: Callable | None = None,
509
+ is_optional: bool = False,
495
510
  ) -> typer.Argument:
496
511
  return typer.Argument(
497
- ...,
512
+ None if is_optional else ...,
498
513
  help=f"Identifier of the {sf_object}; for example: {example}",
499
514
  show_default=False,
500
515
  click_type=click_type,
@@ -19,6 +19,7 @@ import logging
19
19
  from functools import wraps
20
20
  from typing import Any, Callable, Dict, List, Optional, Tuple
21
21
 
22
+ import click
22
23
  import typer
23
24
  from click import ClickException
24
25
  from snowflake.cli.api.commands.decorators import (
@@ -35,10 +36,20 @@ from snowflake.cli.api.output.types import CommandResult
35
36
  from snowflake.cli.api.sanitizers import sanitize_for_terminal
36
37
  from snowflake.cli.api.sql_execution import SqlExecutionMixin
37
38
  from snowflake.connector import DatabaseError
39
+ from typer.core import TyperGroup
38
40
 
39
41
  log = logging.getLogger(__name__)
40
42
 
41
43
 
44
+ class SortedTyperGroup(TyperGroup):
45
+ def list_commands(self, ctx: click.Context) -> List[str]:
46
+ """
47
+ From Typer 0.13.0 help items are in order of definition, this function override that approach.
48
+ Returns a list of subcommand names in the alphabetic order.
49
+ """
50
+ return sorted(self.commands)
51
+
52
+
42
53
  class SnowTyper(typer.Typer):
43
54
  def __init__(self, /, **kwargs):
44
55
  self._sanitize_kwargs(kwargs)
@@ -49,6 +60,7 @@ class SnowTyper(typer.Typer):
49
60
  no_args_is_help=True,
50
61
  add_completion=True,
51
62
  rich_markup_mode="markdown",
63
+ cls=SortedTyperGroup,
52
64
  )
53
65
 
54
66
  @staticmethod
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  from typing import Dict, List, Optional
2
4
 
3
5
  from click import ClickException, UsageError
@@ -20,7 +20,7 @@ import warnings
20
20
  from contextlib import contextmanager
21
21
  from dataclasses import asdict, dataclass, field
22
22
  from pathlib import Path
23
- from typing import Any, Dict, Optional, Union
23
+ from typing import Any, Dict, List, Optional, Union
24
24
 
25
25
  import tomlkit
26
26
  from click import ClickException
@@ -58,6 +58,7 @@ PLUGINS_SECTION = "plugins"
58
58
 
59
59
  LOGS_SECTION_PATH = [CLI_SECTION, LOGS_SECTION]
60
60
  PLUGINS_SECTION_PATH = [CLI_SECTION, PLUGINS_SECTION]
61
+ PLUGIN_ENABLED_KEY = "enabled"
61
62
  FEATURE_FLAGS_SECTION_PATH = [CLI_SECTION, "features"]
62
63
 
63
64
  CONFIG_MANAGER.add_option(
@@ -140,8 +141,7 @@ def add_connection_to_proper_file(name: str, connection_config: ConnectionConfig
140
141
  return CONNECTIONS_FILE
141
142
  else:
142
143
  set_config_value(
143
- section=CONNECTIONS_SECTION,
144
- key=name,
144
+ path=[CONNECTIONS_SECTION, name],
145
145
  value=connection_config.to_dict_of_all_non_empty_values(),
146
146
  )
147
147
  return CONFIG_MANAGER.file_path
@@ -200,14 +200,19 @@ def _initialise_logs_section():
200
200
  conf_file_cache[CLI_SECTION][LOGS_SECTION] = _DEFAULT_LOGS_CONFIG
201
201
 
202
202
 
203
- def set_config_value(section: str | None, key: str, value: Any):
203
+ def set_config_value(path: List[str], value: Any) -> None:
204
+ """Sets value in config.
205
+ For example to set value "val" to key "key" in section [a.b.c], call
206
+ set_config_value(["a", "b", "c", "key"], "val").
207
+ If you want to override a whole section, value should be a dictionary.
208
+ """
204
209
  with _config_file() as conf_file_cache:
205
- if section:
206
- if conf_file_cache.get(section) is None:
207
- conf_file_cache[section] = {}
208
- conf_file_cache[section][key] = value
209
- else:
210
- conf_file_cache[key] = value
210
+ current_config_dict = conf_file_cache
211
+ for key in path[:-1]:
212
+ if key not in current_config_dict:
213
+ current_config_dict[key] = {}
214
+ current_config_dict = current_config_dict[key]
215
+ current_config_dict[path[-1]] = value
211
216
 
212
217
 
213
218
  def get_logs_config() -> dict:
@@ -15,7 +15,7 @@
15
15
  from __future__ import annotations
16
16
 
17
17
  from pathlib import Path
18
- from typing import Optional
18
+ from typing import List, Optional
19
19
 
20
20
  from click.exceptions import ClickException, UsageError
21
21
  from snowflake.cli.api.constants import ObjectType
@@ -54,6 +54,13 @@ class InvalidPluginConfiguration(ClickException):
54
54
  return f"Invalid plugin configuration. {self.message}"
55
55
 
56
56
 
57
+ class PluginNotInstalledError(ClickException):
58
+ def __init__(self, plugin_name, installed_plugins: List[str]):
59
+ super().__init__(
60
+ f"Plugin {plugin_name} is not installed. Available plugins: {', '.join(installed_plugins)}."
61
+ )
62
+
63
+
57
64
  class SnowflakeConnectionError(ClickException):
58
65
  def __init__(self, snowflake_err: Exception):
59
66
  super().__init__(f"Could not connect to Snowflake. Reason: {snowflake_err}")
@@ -67,3 +67,4 @@ class FeatureFlag(FeatureFlagMixin):
67
67
  "ENABLE_SEPARATE_AUTHENTICATION_POLICY_ID", False
68
68
  )
69
69
  ENABLE_SNOWPARK_GLOB_SUPPORT = BooleanFlag("ENABLE_SNOWPARK_GLOB_SUPPORT", False)
70
+ ENABLE_SPCS_SERVICE_EVENTS = BooleanFlag("ENABLE_SPCS_SERVICE_EVENTS", False)
@@ -17,6 +17,16 @@ from __future__ import annotations
17
17
  from dataclasses import dataclass
18
18
  from typing import Any, Dict, List
19
19
 
20
+ from snowflake.cli.api.config import (
21
+ PLUGIN_ENABLED_KEY,
22
+ PLUGINS_SECTION_PATH,
23
+ config_section_exists,
24
+ get_config_section,
25
+ get_config_value,
26
+ get_plugins_config,
27
+ )
28
+ from snowflake.cli.api.exceptions import InvalidPluginConfiguration
29
+
20
30
 
21
31
  @dataclass
22
32
  class PluginConfig:
@@ -25,8 +35,37 @@ class PluginConfig:
25
35
 
26
36
 
27
37
  class PluginConfigProvider:
28
- def get_enabled_plugin_names(self) -> List[str]:
29
- raise NotImplementedError()
38
+ @staticmethod
39
+ def get_enabled_plugin_names() -> List[str]:
40
+ enabled_plugins = []
41
+ for plugin_name, plugin_config_section in get_plugins_config().items():
42
+ enabled = plugin_config_section.get(PLUGIN_ENABLED_KEY, False)
43
+ _assert_value_is_bool(
44
+ enabled, value_name=PLUGIN_ENABLED_KEY, plugin_name=plugin_name
45
+ )
46
+ if enabled:
47
+ enabled_plugins.append(plugin_name)
48
+ return enabled_plugins
49
+
50
+ @staticmethod
51
+ def get_config(plugin_name: str) -> PluginConfig:
52
+ config_path = PLUGINS_SECTION_PATH + [plugin_name]
53
+ plugin_config = PluginConfig(is_plugin_enabled=False, internal_config={})
54
+ plugin_config.is_plugin_enabled = get_config_value(
55
+ *config_path, key=PLUGIN_ENABLED_KEY, default=False
56
+ )
57
+ _assert_value_is_bool(
58
+ plugin_config.is_plugin_enabled,
59
+ value_name=PLUGIN_ENABLED_KEY,
60
+ plugin_name=plugin_name,
61
+ )
62
+ if config_section_exists(*config_path, "config"):
63
+ plugin_config.internal_config = get_config_section(*config_path, "config")
64
+ return plugin_config
65
+
30
66
 
31
- def get_config(self, plugin_name: str) -> PluginConfig:
32
- raise NotImplementedError()
67
+ def _assert_value_is_bool(value, *, value_name: str, plugin_name: str) -> None:
68
+ if type(value) is not bool:
69
+ raise InvalidPluginConfiguration(
70
+ f'[{plugin_name}]: "{value_name}" must be a boolean'
71
+ )
@@ -0,0 +1,31 @@
1
+ from typing import Optional
2
+
3
+ from click import UsageError
4
+ from snowflake.cli.api.cli_global_context import get_cli_context
5
+ from snowflake.cli.api.constants import ObjectType
6
+ from snowflake.cli.api.exceptions import NoProjectDefinitionError
7
+
8
+
9
+ def get_entity_from_project_definition(
10
+ entity_type: ObjectType, entity_id: Optional[str] = None
11
+ ):
12
+ cli_context = get_cli_context()
13
+ pd = cli_context.project_definition
14
+ entities = pd.get_entities_by_type(entity_type=entity_type.value.cli_name)
15
+
16
+ if not entities:
17
+ raise NoProjectDefinitionError(
18
+ project_type=entity_type.value.sf_name,
19
+ project_root=cli_context.project_root,
20
+ )
21
+
22
+ if entity_id and entity_id not in entities:
23
+ raise UsageError(f"No '{entity_id}' entity in project definition file.")
24
+ elif len(entities.keys()) == 1:
25
+ entity_id = list(entities.keys())[0]
26
+
27
+ if entity_id is None:
28
+ raise UsageError(
29
+ f"Multiple {entity_type.value.sf_plural_name} found. Please provide entity id for the operation."
30
+ )
31
+ return entities[entity_id]
@@ -26,6 +26,10 @@ from snowflake.cli._plugins.nativeapp.entities.application_package import (
26
26
  )
27
27
  from snowflake.cli._plugins.notebook.notebook_entity import NotebookEntity
28
28
  from snowflake.cli._plugins.notebook.notebook_entity_model import NotebookEntityModel
29
+ from snowflake.cli._plugins.project.project_entity_model import (
30
+ ProjectEntity,
31
+ ProjectEntityModel,
32
+ )
29
33
  from snowflake.cli._plugins.snowpark.snowpark_entity import (
30
34
  FunctionEntity,
31
35
  ProcedureEntity,
@@ -34,6 +38,20 @@ from snowflake.cli._plugins.snowpark.snowpark_entity_model import (
34
38
  FunctionEntityModel,
35
39
  ProcedureEntityModel,
36
40
  )
41
+ from snowflake.cli._plugins.spcs.compute_pool.compute_pool_entity import (
42
+ ComputePoolEntity,
43
+ )
44
+ from snowflake.cli._plugins.spcs.compute_pool.compute_pool_entity_model import (
45
+ ComputePoolEntityModel,
46
+ )
47
+ from snowflake.cli._plugins.spcs.image_repository.image_repository_entity import (
48
+ ImageRepositoryEntity,
49
+ )
50
+ from snowflake.cli._plugins.spcs.image_repository.image_repository_entity_model import (
51
+ ImageRepositoryEntityModel,
52
+ )
53
+ from snowflake.cli._plugins.spcs.services.service_entity import ServiceEntity
54
+ from snowflake.cli._plugins.spcs.services.service_entity_model import ServiceEntityModel
37
55
  from snowflake.cli._plugins.streamlit.streamlit_entity import StreamlitEntity
38
56
  from snowflake.cli._plugins.streamlit.streamlit_entity_model import (
39
57
  StreamlitEntityModel,
@@ -44,7 +62,11 @@ Entity = Union[
44
62
  ApplicationPackageEntity,
45
63
  StreamlitEntity,
46
64
  ProcedureEntity,
65
+ ProjectEntity,
47
66
  FunctionEntity,
67
+ ComputePoolEntity,
68
+ ImageRepositoryEntity,
69
+ ServiceEntity,
48
70
  NotebookEntity,
49
71
  ]
50
72
  EntityModel = Union[
@@ -53,7 +75,11 @@ EntityModel = Union[
53
75
  StreamlitEntityModel,
54
76
  FunctionEntityModel,
55
77
  ProcedureEntityModel,
78
+ ComputePoolEntityModel,
79
+ ImageRepositoryEntityModel,
80
+ ServiceEntityModel,
56
81
  NotebookEntityModel,
82
+ ProjectEntityModel,
57
83
  ]
58
84
 
59
85
  ALL_ENTITIES: List[Entity] = [*get_args(Entity)]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: snowflake-cli
3
- Version: 3.4.1
3
+ Version: 3.5.0
4
4
  Summary: Snowflake CLI
5
5
  Project-URL: Source code, https://github.com/snowflakedb/snowflake-cli
6
6
  Project-URL: Bug Tracker, https://github.com/snowflakedb/snowflake-cli/issues
@@ -218,30 +218,30 @@ Classifier: Programming Language :: SQL
218
218
  Classifier: Topic :: Database
219
219
  Requires-Python: >=3.10
220
220
  Requires-Dist: gitpython==3.1.44
221
- Requires-Dist: jinja2==3.1.5
221
+ Requires-Dist: jinja2==3.1.6
222
222
  Requires-Dist: packaging
223
223
  Requires-Dist: pip
224
224
  Requires-Dist: pluggy==1.5.0
225
- Requires-Dist: pydantic==2.10.4
225
+ Requires-Dist: pydantic==2.10.6
226
226
  Requires-Dist: pyyaml==6.0.2
227
227
  Requires-Dist: requests==2.32.3
228
228
  Requires-Dist: requirements-parser==0.11.0
229
229
  Requires-Dist: rich==13.9.4
230
230
  Requires-Dist: setuptools==75.8.0
231
- Requires-Dist: snowflake-connector-python[secure-local-storage]==3.13.2
231
+ Requires-Dist: snowflake-connector-python[secure-local-storage]==3.14.0
232
232
  Requires-Dist: snowflake-core==1.0.2; python_version < '3.12'
233
233
  Requires-Dist: snowflake-snowpark-python<1.26.0,>=1.15.0; python_version < '3.12'
234
234
  Requires-Dist: tomlkit==0.13.2
235
- Requires-Dist: typer==0.12.5
235
+ Requires-Dist: typer==0.15.1
236
236
  Requires-Dist: urllib3<2.4,>=1.24.3
237
237
  Provides-Extra: development
238
- Requires-Dist: coverage==7.6.10; extra == 'development'
239
- Requires-Dist: factory-boy==3.3.1; extra == 'development'
240
- Requires-Dist: faker==35.2.0; extra == 'development'
238
+ Requires-Dist: coverage==7.6.12; extra == 'development'
239
+ Requires-Dist: factory-boy==3.3.3; extra == 'development'
240
+ Requires-Dist: faker==36.1.1; extra == 'development'
241
241
  Requires-Dist: pre-commit>=3.5.0; extra == 'development'
242
242
  Requires-Dist: pytest-randomly==3.16.0; extra == 'development'
243
243
  Requires-Dist: pytest==8.3.4; extra == 'development'
244
- Requires-Dist: syrupy==4.8.1; extra == 'development'
244
+ Requires-Dist: syrupy==4.8.2; extra == 'development'
245
245
  Provides-Extra: packaging
246
246
  Requires-Dist: pyinstaller~=6.10; extra == 'packaging'
247
247
  Description-Content-Type: text/markdown