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.
- snowflake/cli/__about__.py +1 -1
- snowflake/cli/_app/cli_app.py +1 -10
- snowflake/cli/_app/commands_registration/builtin_plugins.py +5 -1
- snowflake/cli/_app/commands_registration/command_plugins_loader.py +3 -1
- snowflake/cli/_app/commands_registration/commands_registration_with_callbacks.py +3 -3
- snowflake/cli/_app/printing.py +2 -2
- snowflake/cli/_plugins/connection/commands.py +2 -4
- snowflake/cli/_plugins/helpers/commands.py +3 -4
- snowflake/cli/_plugins/notebook/commands.py +3 -4
- snowflake/cli/_plugins/plugin/commands.py +79 -0
- snowflake/cli/_plugins/plugin/manager.py +74 -0
- snowflake/cli/_plugins/plugin/plugin_spec.py +30 -0
- snowflake/cli/_plugins/project/__init__.py +0 -0
- snowflake/cli/_plugins/project/commands.py +157 -0
- snowflake/cli/{_app/api_impl/plugin/__init__.py → _plugins/project/feature_flags.py} +9 -0
- snowflake/cli/_plugins/project/manager.py +76 -0
- snowflake/cli/_plugins/project/plugin_spec.py +30 -0
- snowflake/cli/_plugins/project/project_entity_model.py +40 -0
- snowflake/cli/_plugins/snowpark/commands.py +2 -1
- snowflake/cli/_plugins/spcs/compute_pool/commands.py +53 -5
- snowflake/cli/_plugins/spcs/compute_pool/compute_pool_entity.py +8 -0
- snowflake/cli/_plugins/spcs/compute_pool/compute_pool_entity_model.py +37 -0
- snowflake/cli/_plugins/spcs/compute_pool/manager.py +45 -0
- snowflake/cli/_plugins/spcs/image_repository/commands.py +29 -0
- snowflake/cli/_plugins/spcs/image_repository/image_repository_entity.py +8 -0
- snowflake/cli/_plugins/spcs/image_repository/image_repository_entity_model.py +8 -0
- snowflake/cli/_plugins/spcs/image_repository/manager.py +1 -1
- snowflake/cli/_plugins/spcs/services/commands.py +53 -0
- snowflake/cli/_plugins/spcs/services/manager.py +114 -0
- snowflake/cli/_plugins/spcs/services/service_entity.py +6 -0
- snowflake/cli/_plugins/spcs/services/service_entity_model.py +45 -0
- snowflake/cli/_plugins/spcs/services/service_project_paths.py +15 -0
- snowflake/cli/_plugins/stage/manager.py +2 -2
- snowflake/cli/_plugins/streamlit/commands.py +9 -24
- snowflake/cli/_plugins/streamlit/manager.py +5 -36
- snowflake/cli/api/artifacts/upload.py +51 -0
- snowflake/cli/api/commands/flags.py +24 -9
- snowflake/cli/api/commands/snow_typer.py +12 -0
- snowflake/cli/api/commands/utils.py +2 -0
- snowflake/cli/api/config.py +15 -10
- snowflake/cli/api/exceptions.py +8 -1
- snowflake/cli/api/feature_flags.py +1 -0
- snowflake/cli/api/plugins/plugin_config.py +43 -4
- snowflake/cli/api/project/definition_helper.py +31 -0
- snowflake/cli/api/project/schemas/entities/entities.py +26 -0
- {snowflake_cli-3.4.1.dist-info → snowflake_cli-3.5.0.dist-info}/METADATA +9 -9
- {snowflake_cli-3.4.1.dist-info → snowflake_cli-3.5.0.dist-info}/RECORD +51 -36
- snowflake/cli/_app/api_impl/plugin/plugin_config_provider_impl.py +0 -66
- snowflake/cli/api/__init__.py +0 -48
- /snowflake/cli/{_app/api_impl → _plugins/plugin}/__init__.py +0 -0
- {snowflake_cli-3.4.1.dist-info → snowflake_cli-3.5.0.dist-info}/WHEEL +0 -0
- {snowflake_cli-3.4.1.dist-info → snowflake_cli-3.5.0.dist-info}/entry_points.txt +0 -0
- {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
|
|
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="
|
|
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
|
-
|
|
159
|
-
|
|
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.
|
|
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
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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="
|
|
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 = "<
|
|
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
|
-
|
|
296
|
-
|
|
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="
|
|
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="
|
|
314
|
-
callback=
|
|
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
|
snowflake/cli/api/config.py
CHANGED
|
@@ -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
|
-
|
|
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(
|
|
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
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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:
|
snowflake/cli/api/exceptions.py
CHANGED
|
@@ -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}")
|
|
@@ -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
|
-
|
|
29
|
-
|
|
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
|
-
|
|
32
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
239
|
-
Requires-Dist: factory-boy==3.3.
|
|
240
|
-
Requires-Dist: faker==
|
|
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.
|
|
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
|