snowflake-cli-labs 2.8.0rc1__py3-none-any.whl → 2.8.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.
- snowflake/cli/__about__.py +1 -1
- snowflake/cli/api/commands/flags.py +26 -4
- snowflake/cli/api/identifiers.py +17 -4
- snowflake/cli/api/sql_execution.py +4 -4
- snowflake/cli/plugins/git/commands.py +68 -19
- snowflake/cli/plugins/git/manager.py +19 -10
- snowflake/cli/plugins/init/commands.py +8 -4
- snowflake/cli/plugins/notebook/commands.py +6 -5
- snowflake/cli/plugins/notebook/manager.py +10 -10
- snowflake/cli/plugins/notebook/types.py +0 -1
- snowflake/cli/plugins/object/command_aliases.py +3 -2
- snowflake/cli/plugins/object/commands.py +13 -6
- snowflake/cli/plugins/object/manager.py +7 -6
- snowflake/cli/plugins/snowpark/commands.py +4 -6
- snowflake/cli/plugins/snowpark/models.py +2 -1
- snowflake/cli/plugins/snowpark/package/manager.py +2 -1
- snowflake/cli/plugins/spcs/compute_pool/commands.py +21 -20
- snowflake/cli/plugins/spcs/image_repository/commands.py +19 -13
- snowflake/cli/plugins/spcs/services/commands.py +23 -22
- snowflake/cli/plugins/stage/commands.py +7 -5
- snowflake/cli/plugins/stage/manager.py +51 -18
- snowflake/cli/plugins/streamlit/commands.py +7 -14
- snowflake/cli/plugins/streamlit/manager.py +1 -1
- {snowflake_cli_labs-2.8.0rc1.dist-info → snowflake_cli_labs-2.8.1.dist-info}/METADATA +1 -1
- {snowflake_cli_labs-2.8.0rc1.dist-info → snowflake_cli_labs-2.8.1.dist-info}/RECORD +28 -28
- {snowflake_cli_labs-2.8.0rc1.dist-info → snowflake_cli_labs-2.8.1.dist-info}/WHEEL +0 -0
- {snowflake_cli_labs-2.8.0rc1.dist-info → snowflake_cli_labs-2.8.1.dist-info}/entry_points.txt +0 -0
- {snowflake_cli_labs-2.8.0rc1.dist-info → snowflake_cli_labs-2.8.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -173,9 +173,7 @@ def deploy(
|
|
|
173
173
|
stage_name = snowpark.stage_name
|
|
174
174
|
stage_manager = StageManager()
|
|
175
175
|
stage_name = FQN.from_string(stage_name).using_context()
|
|
176
|
-
stage_manager.create(
|
|
177
|
-
stage_name=stage_name, comment="deployments managed by Snowflake CLI"
|
|
178
|
-
)
|
|
176
|
+
stage_manager.create(fqn=stage_name, comment="deployments managed by Snowflake CLI")
|
|
179
177
|
|
|
180
178
|
snowflake_dependencies = _read_snowflake_requrements_file(
|
|
181
179
|
paths.snowflake_requirements_file
|
|
@@ -251,7 +249,7 @@ def _find_existing_objects(
|
|
|
251
249
|
try:
|
|
252
250
|
current_state = om.describe(
|
|
253
251
|
object_type=object_type.value.sf_name,
|
|
254
|
-
|
|
252
|
+
fqn=FQN.from_string(identifier),
|
|
255
253
|
)
|
|
256
254
|
existing_objects[identifier] = current_state
|
|
257
255
|
except ProgrammingError:
|
|
@@ -528,7 +526,7 @@ def list_(
|
|
|
528
526
|
@app.command("drop", requires_connection=True)
|
|
529
527
|
def drop(
|
|
530
528
|
object_type: _SnowparkObject = ObjectTypeArgument,
|
|
531
|
-
identifier:
|
|
529
|
+
identifier: FQN = IdentifierArgument,
|
|
532
530
|
**options,
|
|
533
531
|
):
|
|
534
532
|
"""Drop procedure or function."""
|
|
@@ -538,7 +536,7 @@ def drop(
|
|
|
538
536
|
@app.command("describe", requires_connection=True)
|
|
539
537
|
def describe(
|
|
540
538
|
object_type: _SnowparkObject = ObjectTypeArgument,
|
|
541
|
-
identifier:
|
|
539
|
+
identifier: FQN = IdentifierArgument,
|
|
542
540
|
**options,
|
|
543
541
|
):
|
|
544
542
|
"""Provides description of a procedure or function."""
|
|
@@ -128,13 +128,14 @@ class WheelMetadata:
|
|
|
128
128
|
if line.startswith(dep_keyword)
|
|
129
129
|
]
|
|
130
130
|
name = cls._get_name_from_wheel_filename(wheel_path.name)
|
|
131
|
+
|
|
131
132
|
return cls(name=name, wheel_path=wheel_path, dependencies=dependencies)
|
|
132
133
|
|
|
133
134
|
@staticmethod
|
|
134
135
|
def _get_name_from_wheel_filename(wheel_filename: str) -> str:
|
|
135
136
|
# wheel filename is in format {name}-{version}[-{extra info}]
|
|
136
137
|
# https://peps.python.org/pep-0491/#file-name-convention
|
|
137
|
-
return wheel_filename.split("-")[0]
|
|
138
|
+
return wheel_filename.split("-")[0].lower()
|
|
138
139
|
|
|
139
140
|
@staticmethod
|
|
140
141
|
def to_wheel_name_format(package_name: str) -> str:
|
|
@@ -17,6 +17,7 @@ from __future__ import annotations
|
|
|
17
17
|
import logging
|
|
18
18
|
from pathlib import Path
|
|
19
19
|
|
|
20
|
+
from snowflake.cli.api.identifiers import FQN
|
|
20
21
|
from snowflake.cli.api.secure_path import SecurePath
|
|
21
22
|
from snowflake.cli.plugins.snowpark.package.utils import prepare_app_zip
|
|
22
23
|
from snowflake.cli.plugins.stage.manager import StageManager
|
|
@@ -30,7 +31,7 @@ def upload(file: Path, stage: str, overwrite: bool):
|
|
|
30
31
|
temp_app_zip_path = prepare_app_zip(SecurePath(file), temp_dir)
|
|
31
32
|
sm = StageManager()
|
|
32
33
|
|
|
33
|
-
sm.create(sm.get_stage_from_path(stage))
|
|
34
|
+
sm.create(FQN.from_string(sm.get_stage_from_path(stage)))
|
|
34
35
|
put_response = sm.put(
|
|
35
36
|
temp_app_zip_path.path, stage, overwrite=overwrite
|
|
36
37
|
).fetchone()
|
|
@@ -21,10 +21,12 @@ from click import ClickException
|
|
|
21
21
|
from snowflake.cli.api.commands.flags import (
|
|
22
22
|
IfNotExistsOption,
|
|
23
23
|
OverrideableOption,
|
|
24
|
+
identifier_argument,
|
|
24
25
|
like_option,
|
|
25
26
|
)
|
|
26
27
|
from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
|
|
27
28
|
from snowflake.cli.api.constants import ObjectType
|
|
29
|
+
from snowflake.cli.api.identifiers import FQN
|
|
28
30
|
from snowflake.cli.api.output.types import CommandResult, SingleQueryResult
|
|
29
31
|
from snowflake.cli.api.project.util import is_valid_object_name
|
|
30
32
|
from snowflake.cli.plugins.object.command_aliases import (
|
|
@@ -43,22 +45,21 @@ app = SnowTyperFactory(
|
|
|
43
45
|
)
|
|
44
46
|
|
|
45
47
|
|
|
46
|
-
def _compute_pool_name_callback(name:
|
|
48
|
+
def _compute_pool_name_callback(name: FQN) -> FQN:
|
|
47
49
|
"""
|
|
48
50
|
Verifies that compute pool name is a single valid identifier.
|
|
49
51
|
"""
|
|
50
|
-
if not is_valid_object_name(name, max_depth=0, allow_quoted=False):
|
|
52
|
+
if not is_valid_object_name(name.identifier, max_depth=0, allow_quoted=False):
|
|
51
53
|
raise ClickException(
|
|
52
54
|
f"'{name}' is not a valid compute pool name. Note that compute pool names must be unquoted identifiers."
|
|
53
55
|
)
|
|
54
56
|
return name
|
|
55
57
|
|
|
56
58
|
|
|
57
|
-
ComputePoolNameArgument =
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
ComputePoolNameArgument = identifier_argument(
|
|
60
|
+
sf_object="compute pool",
|
|
61
|
+
example="my_compute_pool",
|
|
60
62
|
callback=_compute_pool_name_callback,
|
|
61
|
-
show_default=False,
|
|
62
63
|
)
|
|
63
64
|
|
|
64
65
|
|
|
@@ -106,7 +107,7 @@ add_object_command_aliases(
|
|
|
106
107
|
|
|
107
108
|
@app.command(requires_connection=True)
|
|
108
109
|
def create(
|
|
109
|
-
name:
|
|
110
|
+
name: FQN = ComputePoolNameArgument,
|
|
110
111
|
instance_family: str = typer.Option(
|
|
111
112
|
...,
|
|
112
113
|
"--family",
|
|
@@ -131,7 +132,7 @@ def create(
|
|
|
131
132
|
"""
|
|
132
133
|
max_nodes = validate_and_set_instances(min_nodes, max_nodes, "nodes")
|
|
133
134
|
cursor = ComputePoolManager().create(
|
|
134
|
-
pool_name=name,
|
|
135
|
+
pool_name=name.identifier,
|
|
135
136
|
min_nodes=min_nodes,
|
|
136
137
|
max_nodes=max_nodes,
|
|
137
138
|
instance_family=instance_family,
|
|
@@ -145,33 +146,33 @@ def create(
|
|
|
145
146
|
|
|
146
147
|
|
|
147
148
|
@app.command("stop-all", requires_connection=True)
|
|
148
|
-
def stop_all(name:
|
|
149
|
+
def stop_all(name: FQN = ComputePoolNameArgument, **options) -> CommandResult:
|
|
149
150
|
"""
|
|
150
151
|
Deletes all services running on the compute pool.
|
|
151
152
|
"""
|
|
152
|
-
cursor = ComputePoolManager().stop(pool_name=name)
|
|
153
|
+
cursor = ComputePoolManager().stop(pool_name=name.identifier)
|
|
153
154
|
return SingleQueryResult(cursor)
|
|
154
155
|
|
|
155
156
|
|
|
156
157
|
@app.command(requires_connection=True)
|
|
157
|
-
def suspend(name:
|
|
158
|
+
def suspend(name: FQN = ComputePoolNameArgument, **options) -> CommandResult:
|
|
158
159
|
"""
|
|
159
160
|
Suspends the compute pool by suspending all currently running services and then releasing compute pool nodes.
|
|
160
161
|
"""
|
|
161
|
-
return SingleQueryResult(ComputePoolManager().suspend(name))
|
|
162
|
+
return SingleQueryResult(ComputePoolManager().suspend(name.identifier))
|
|
162
163
|
|
|
163
164
|
|
|
164
165
|
@app.command(requires_connection=True)
|
|
165
|
-
def resume(name:
|
|
166
|
+
def resume(name: FQN = ComputePoolNameArgument, **options) -> CommandResult:
|
|
166
167
|
"""
|
|
167
168
|
Resumes the compute pool from a SUSPENDED state.
|
|
168
169
|
"""
|
|
169
|
-
return SingleQueryResult(ComputePoolManager().resume(name))
|
|
170
|
+
return SingleQueryResult(ComputePoolManager().resume(name.identifier))
|
|
170
171
|
|
|
171
172
|
|
|
172
173
|
@app.command("set", requires_connection=True)
|
|
173
174
|
def set_property(
|
|
174
|
-
name:
|
|
175
|
+
name: FQN = ComputePoolNameArgument,
|
|
175
176
|
min_nodes: Optional[int] = MinNodesOption(default=None, show_default=False),
|
|
176
177
|
max_nodes: Optional[int] = MaxNodesOption(show_default=False),
|
|
177
178
|
auto_resume: Optional[bool] = AutoResumeOption(default=None, show_default=False),
|
|
@@ -187,7 +188,7 @@ def set_property(
|
|
|
187
188
|
Sets one or more properties for the compute pool.
|
|
188
189
|
"""
|
|
189
190
|
cursor = ComputePoolManager().set_property(
|
|
190
|
-
pool_name=name,
|
|
191
|
+
pool_name=name.identifier,
|
|
191
192
|
min_nodes=min_nodes,
|
|
192
193
|
max_nodes=max_nodes,
|
|
193
194
|
auto_resume=auto_resume,
|
|
@@ -199,7 +200,7 @@ def set_property(
|
|
|
199
200
|
|
|
200
201
|
@app.command("unset", requires_connection=True)
|
|
201
202
|
def unset_property(
|
|
202
|
-
name:
|
|
203
|
+
name: FQN = ComputePoolNameArgument,
|
|
203
204
|
auto_resume: bool = AutoResumeOption(
|
|
204
205
|
default=False,
|
|
205
206
|
param_decls=["--auto-resume"],
|
|
@@ -223,7 +224,7 @@ def unset_property(
|
|
|
223
224
|
Resets one or more properties for the compute pool to their default value(s).
|
|
224
225
|
"""
|
|
225
226
|
cursor = ComputePoolManager().unset_property(
|
|
226
|
-
pool_name=name,
|
|
227
|
+
pool_name=name.identifier,
|
|
227
228
|
auto_resume=auto_resume,
|
|
228
229
|
auto_suspend_secs=auto_suspend_secs,
|
|
229
230
|
comment=comment,
|
|
@@ -232,9 +233,9 @@ def unset_property(
|
|
|
232
233
|
|
|
233
234
|
|
|
234
235
|
@app.command(requires_connection=True)
|
|
235
|
-
def status(pool_name:
|
|
236
|
+
def status(pool_name: FQN = ComputePoolNameArgument, **options) -> CommandResult:
|
|
236
237
|
"""
|
|
237
238
|
Retrieves the status of a compute pool along with a relevant message, if one exists.
|
|
238
239
|
"""
|
|
239
|
-
cursor = ComputePoolManager().status(pool_name=pool_name)
|
|
240
|
+
cursor = ComputePoolManager().status(pool_name=pool_name.identifier)
|
|
240
241
|
return SingleQueryResult(cursor)
|
|
@@ -23,11 +23,13 @@ from click import ClickException
|
|
|
23
23
|
from snowflake.cli.api.commands.flags import (
|
|
24
24
|
IfNotExistsOption,
|
|
25
25
|
ReplaceOption,
|
|
26
|
+
identifier_argument,
|
|
26
27
|
like_option,
|
|
27
28
|
)
|
|
28
29
|
from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
|
|
29
30
|
from snowflake.cli.api.console import cli_console
|
|
30
31
|
from snowflake.cli.api.constants import ObjectType
|
|
32
|
+
from snowflake.cli.api.identifiers import FQN
|
|
31
33
|
from snowflake.cli.api.output.types import (
|
|
32
34
|
CollectionResult,
|
|
33
35
|
MessageResult,
|
|
@@ -48,18 +50,18 @@ app = SnowTyperFactory(
|
|
|
48
50
|
)
|
|
49
51
|
|
|
50
52
|
|
|
51
|
-
def _repo_name_callback(name:
|
|
52
|
-
if not is_valid_object_name(name, max_depth=2, allow_quoted=False):
|
|
53
|
+
def _repo_name_callback(name: FQN):
|
|
54
|
+
if not is_valid_object_name(name.identifier, max_depth=2, allow_quoted=False):
|
|
53
55
|
raise ClickException(
|
|
54
56
|
f"'{name}' is not a valid image repository name. Note that image repository names must be unquoted identifiers. The same constraint also applies to database and schema names where you create an image repository."
|
|
55
57
|
)
|
|
56
58
|
return name
|
|
57
59
|
|
|
58
60
|
|
|
59
|
-
REPO_NAME_ARGUMENT =
|
|
60
|
-
|
|
61
|
+
REPO_NAME_ARGUMENT = identifier_argument(
|
|
62
|
+
sf_object="image repository",
|
|
63
|
+
example="my_repository",
|
|
61
64
|
callback=_repo_name_callback,
|
|
62
|
-
show_default=False,
|
|
63
65
|
)
|
|
64
66
|
|
|
65
67
|
add_object_command_aliases(
|
|
@@ -76,7 +78,7 @@ add_object_command_aliases(
|
|
|
76
78
|
|
|
77
79
|
@app.command(requires_connection=True)
|
|
78
80
|
def create(
|
|
79
|
-
name:
|
|
81
|
+
name: FQN = REPO_NAME_ARGUMENT,
|
|
80
82
|
replace: bool = ReplaceOption(),
|
|
81
83
|
if_not_exists: bool = IfNotExistsOption(),
|
|
82
84
|
**options,
|
|
@@ -86,21 +88,21 @@ def create(
|
|
|
86
88
|
"""
|
|
87
89
|
return SingleQueryResult(
|
|
88
90
|
ImageRepositoryManager().create(
|
|
89
|
-
name=name, replace=replace, if_not_exists=if_not_exists
|
|
91
|
+
name=name.identifier, replace=replace, if_not_exists=if_not_exists
|
|
90
92
|
)
|
|
91
93
|
)
|
|
92
94
|
|
|
93
95
|
|
|
94
96
|
@app.command("list-images", requires_connection=True)
|
|
95
97
|
def list_images(
|
|
96
|
-
name:
|
|
98
|
+
name: FQN = REPO_NAME_ARGUMENT,
|
|
97
99
|
**options,
|
|
98
100
|
) -> CollectionResult:
|
|
99
101
|
"""Lists images in the given repository."""
|
|
100
102
|
repository_manager = ImageRepositoryManager()
|
|
101
103
|
database = repository_manager.get_database()
|
|
102
104
|
schema = repository_manager.get_schema()
|
|
103
|
-
url = repository_manager.get_repository_url(name)
|
|
105
|
+
url = repository_manager.get_repository_url(name.identifier)
|
|
104
106
|
api_url = repository_manager.get_repository_api_url(url)
|
|
105
107
|
bearer_login = RegistryManager().login_to_registry(api_url)
|
|
106
108
|
repos = []
|
|
@@ -136,7 +138,7 @@ def list_images(
|
|
|
136
138
|
|
|
137
139
|
@app.command("list-tags", requires_connection=True)
|
|
138
140
|
def list_tags(
|
|
139
|
-
name:
|
|
141
|
+
name: FQN = REPO_NAME_ARGUMENT,
|
|
140
142
|
image_name: str = typer.Option(
|
|
141
143
|
...,
|
|
142
144
|
"--image-name",
|
|
@@ -150,7 +152,7 @@ def list_tags(
|
|
|
150
152
|
"""Lists tags for the given image in a repository."""
|
|
151
153
|
|
|
152
154
|
repository_manager = ImageRepositoryManager()
|
|
153
|
-
url = repository_manager.get_repository_url(name)
|
|
155
|
+
url = repository_manager.get_repository_url(name.identifier)
|
|
154
156
|
api_url = repository_manager.get_repository_api_url(url)
|
|
155
157
|
bearer_login = RegistryManager().login_to_registry(api_url)
|
|
156
158
|
|
|
@@ -187,10 +189,14 @@ def list_tags(
|
|
|
187
189
|
|
|
188
190
|
@app.command("url", requires_connection=True)
|
|
189
191
|
def repo_url(
|
|
190
|
-
name:
|
|
192
|
+
name: FQN = REPO_NAME_ARGUMENT,
|
|
191
193
|
**options,
|
|
192
194
|
):
|
|
193
195
|
"""Returns the URL for the given repository."""
|
|
194
196
|
return MessageResult(
|
|
195
|
-
(
|
|
197
|
+
(
|
|
198
|
+
ImageRepositoryManager().get_repository_url(
|
|
199
|
+
repo_name=name.identifier, with_scheme=False
|
|
200
|
+
)
|
|
201
|
+
)
|
|
196
202
|
)
|
|
@@ -23,10 +23,12 @@ from click import ClickException
|
|
|
23
23
|
from snowflake.cli.api.commands.flags import (
|
|
24
24
|
IfNotExistsOption,
|
|
25
25
|
OverrideableOption,
|
|
26
|
+
identifier_argument,
|
|
26
27
|
like_option,
|
|
27
28
|
)
|
|
28
29
|
from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
|
|
29
30
|
from snowflake.cli.api.constants import ObjectType
|
|
31
|
+
from snowflake.cli.api.identifiers import FQN
|
|
30
32
|
from snowflake.cli.api.output.types import (
|
|
31
33
|
CommandResult,
|
|
32
34
|
QueryJsonValueResult,
|
|
@@ -52,19 +54,18 @@ app = SnowTyperFactory(
|
|
|
52
54
|
)
|
|
53
55
|
|
|
54
56
|
|
|
55
|
-
def _service_name_callback(name:
|
|
56
|
-
if not is_valid_object_name(name, max_depth=2, allow_quoted=False):
|
|
57
|
+
def _service_name_callback(name: FQN) -> FQN:
|
|
58
|
+
if not is_valid_object_name(name.identifier, max_depth=2, allow_quoted=False):
|
|
57
59
|
raise ClickException(
|
|
58
60
|
f"'{name}' is not a valid service name. Note service names must be unquoted identifiers. The same constraint also applies to database and schema names where you create a service."
|
|
59
61
|
)
|
|
60
62
|
return name
|
|
61
63
|
|
|
62
64
|
|
|
63
|
-
ServiceNameArgument =
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
ServiceNameArgument = identifier_argument(
|
|
66
|
+
sf_object="service pool",
|
|
67
|
+
example="my_service",
|
|
66
68
|
callback=_service_name_callback,
|
|
67
|
-
show_default=False,
|
|
68
69
|
)
|
|
69
70
|
|
|
70
71
|
SpecPathOption = typer.Option(
|
|
@@ -116,7 +117,7 @@ add_object_command_aliases(
|
|
|
116
117
|
|
|
117
118
|
@app.command(requires_connection=True)
|
|
118
119
|
def create(
|
|
119
|
-
name:
|
|
120
|
+
name: FQN = ServiceNameArgument,
|
|
120
121
|
compute_pool: str = typer.Option(
|
|
121
122
|
...,
|
|
122
123
|
"--compute-pool",
|
|
@@ -145,7 +146,7 @@ def create(
|
|
|
145
146
|
min_instances, max_instances, "instances"
|
|
146
147
|
)
|
|
147
148
|
cursor = ServiceManager().create(
|
|
148
|
-
service_name=name,
|
|
149
|
+
service_name=name.identifier,
|
|
149
150
|
min_instances=min_instances,
|
|
150
151
|
max_instances=max_instances,
|
|
151
152
|
compute_pool=compute_pool,
|
|
@@ -161,17 +162,17 @@ def create(
|
|
|
161
162
|
|
|
162
163
|
|
|
163
164
|
@app.command(requires_connection=True)
|
|
164
|
-
def status(name:
|
|
165
|
+
def status(name: FQN = ServiceNameArgument, **options) -> CommandResult:
|
|
165
166
|
"""
|
|
166
167
|
Retrieves the status of a service.
|
|
167
168
|
"""
|
|
168
|
-
cursor = ServiceManager().status(service_name=name)
|
|
169
|
+
cursor = ServiceManager().status(service_name=name.identifier)
|
|
169
170
|
return QueryJsonValueResult(cursor)
|
|
170
171
|
|
|
171
172
|
|
|
172
173
|
@app.command(requires_connection=True)
|
|
173
174
|
def logs(
|
|
174
|
-
name:
|
|
175
|
+
name: FQN = ServiceNameArgument,
|
|
175
176
|
container_name: str = typer.Option(
|
|
176
177
|
...,
|
|
177
178
|
"--container-name",
|
|
@@ -193,7 +194,7 @@ def logs(
|
|
|
193
194
|
Retrieves local logs from a service container.
|
|
194
195
|
"""
|
|
195
196
|
results = ServiceManager().logs(
|
|
196
|
-
service_name=name,
|
|
197
|
+
service_name=name.identifier,
|
|
197
198
|
instance_id=instance_id,
|
|
198
199
|
container_name=container_name,
|
|
199
200
|
num_lines=num_lines,
|
|
@@ -205,7 +206,7 @@ def logs(
|
|
|
205
206
|
|
|
206
207
|
@app.command(requires_connection=True)
|
|
207
208
|
def upgrade(
|
|
208
|
-
name:
|
|
209
|
+
name: FQN = ServiceNameArgument,
|
|
209
210
|
spec_path: Path = SpecPathOption,
|
|
210
211
|
**options,
|
|
211
212
|
):
|
|
@@ -213,20 +214,20 @@ def upgrade(
|
|
|
213
214
|
Updates an existing service with a new specification file.
|
|
214
215
|
"""
|
|
215
216
|
return SingleQueryResult(
|
|
216
|
-
ServiceManager().upgrade_spec(service_name=name, spec_path=spec_path)
|
|
217
|
+
ServiceManager().upgrade_spec(service_name=name.identifier, spec_path=spec_path)
|
|
217
218
|
)
|
|
218
219
|
|
|
219
220
|
|
|
220
221
|
@app.command("list-endpoints", requires_connection=True)
|
|
221
|
-
def list_endpoints(name:
|
|
222
|
+
def list_endpoints(name: FQN = ServiceNameArgument, **options):
|
|
222
223
|
"""
|
|
223
224
|
Lists the endpoints in a service.
|
|
224
225
|
"""
|
|
225
|
-
return QueryResult(ServiceManager().list_endpoints(service_name=name))
|
|
226
|
+
return QueryResult(ServiceManager().list_endpoints(service_name=name.identifier))
|
|
226
227
|
|
|
227
228
|
|
|
228
229
|
@app.command(requires_connection=True)
|
|
229
|
-
def suspend(name:
|
|
230
|
+
def suspend(name: FQN = ServiceNameArgument, **options) -> CommandResult:
|
|
230
231
|
"""
|
|
231
232
|
Suspends the service, shutting down and deleting all its containers.
|
|
232
233
|
"""
|
|
@@ -234,7 +235,7 @@ def suspend(name: str = ServiceNameArgument, **options) -> CommandResult:
|
|
|
234
235
|
|
|
235
236
|
|
|
236
237
|
@app.command(requires_connection=True)
|
|
237
|
-
def resume(name:
|
|
238
|
+
def resume(name: FQN = ServiceNameArgument, **options) -> CommandResult:
|
|
238
239
|
"""
|
|
239
240
|
Resumes the service from a SUSPENDED state.
|
|
240
241
|
"""
|
|
@@ -243,7 +244,7 @@ def resume(name: str = ServiceNameArgument, **options) -> CommandResult:
|
|
|
243
244
|
|
|
244
245
|
@app.command("set", requires_connection=True)
|
|
245
246
|
def set_property(
|
|
246
|
-
name:
|
|
247
|
+
name: FQN = ServiceNameArgument,
|
|
247
248
|
min_instances: Optional[int] = MinInstancesOption(default=None, show_default=False),
|
|
248
249
|
max_instances: Optional[int] = MaxInstancesOption(show_default=False),
|
|
249
250
|
query_warehouse: Optional[str] = QueryWarehouseOption(show_default=False),
|
|
@@ -255,7 +256,7 @@ def set_property(
|
|
|
255
256
|
Sets one or more properties for the service.
|
|
256
257
|
"""
|
|
257
258
|
cursor = ServiceManager().set_property(
|
|
258
|
-
service_name=name,
|
|
259
|
+
service_name=name.identifier,
|
|
259
260
|
min_instances=min_instances,
|
|
260
261
|
max_instances=max_instances,
|
|
261
262
|
query_warehouse=query_warehouse,
|
|
@@ -267,7 +268,7 @@ def set_property(
|
|
|
267
268
|
|
|
268
269
|
@app.command("unset", requires_connection=True)
|
|
269
270
|
def unset_property(
|
|
270
|
-
name:
|
|
271
|
+
name: FQN = ServiceNameArgument,
|
|
271
272
|
min_instances: bool = MinInstancesOption(
|
|
272
273
|
default=False,
|
|
273
274
|
help=f"Reset the MIN_INSTANCES property - {_MIN_INSTANCES_HELP}",
|
|
@@ -301,7 +302,7 @@ def unset_property(
|
|
|
301
302
|
Resets one or more properties for the service to their default value(s).
|
|
302
303
|
"""
|
|
303
304
|
cursor = ServiceManager().unset_property(
|
|
304
|
-
service_name=name,
|
|
305
|
+
service_name=name.identifier,
|
|
305
306
|
min_instances=min_instances,
|
|
306
307
|
max_instances=max_instances,
|
|
307
308
|
query_warehouse=query_warehouse,
|
|
@@ -26,11 +26,13 @@ from snowflake.cli.api.commands.flags import (
|
|
|
26
26
|
ExecuteVariablesOption,
|
|
27
27
|
OnErrorOption,
|
|
28
28
|
PatternOption,
|
|
29
|
+
identifier_argument,
|
|
29
30
|
like_option,
|
|
30
31
|
)
|
|
31
32
|
from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
|
|
32
33
|
from snowflake.cli.api.console import cli_console
|
|
33
34
|
from snowflake.cli.api.constants import ObjectType
|
|
35
|
+
from snowflake.cli.api.identifiers import FQN
|
|
34
36
|
from snowflake.cli.api.output.formats import OutputFormat
|
|
35
37
|
from snowflake.cli.api.output.types import (
|
|
36
38
|
CollectionResult,
|
|
@@ -56,7 +58,7 @@ app = SnowTyperFactory(
|
|
|
56
58
|
help="Manages stages.",
|
|
57
59
|
)
|
|
58
60
|
|
|
59
|
-
StageNameArgument =
|
|
61
|
+
StageNameArgument = identifier_argument(sf_object="stage", example="@my_stage")
|
|
60
62
|
|
|
61
63
|
add_object_command_aliases(
|
|
62
64
|
app=app,
|
|
@@ -142,17 +144,17 @@ def copy(
|
|
|
142
144
|
|
|
143
145
|
|
|
144
146
|
@app.command("create", requires_connection=True)
|
|
145
|
-
def stage_create(stage_name:
|
|
147
|
+
def stage_create(stage_name: FQN = StageNameArgument, **options) -> CommandResult:
|
|
146
148
|
"""
|
|
147
149
|
Creates a named stage if it does not already exist.
|
|
148
150
|
"""
|
|
149
|
-
cursor = StageManager().create(
|
|
151
|
+
cursor = StageManager().create(fqn=stage_name)
|
|
150
152
|
return SingleQueryResult(cursor)
|
|
151
153
|
|
|
152
154
|
|
|
153
155
|
@app.command("remove", requires_connection=True)
|
|
154
156
|
def stage_remove(
|
|
155
|
-
stage_name:
|
|
157
|
+
stage_name: FQN = StageNameArgument,
|
|
156
158
|
file_name: str = typer.Argument(
|
|
157
159
|
...,
|
|
158
160
|
help="Name of the file to remove.",
|
|
@@ -164,7 +166,7 @@ def stage_remove(
|
|
|
164
166
|
Removes a file from a stage.
|
|
165
167
|
"""
|
|
166
168
|
|
|
167
|
-
cursor = StageManager().remove(stage_name=stage_name, path=file_name)
|
|
169
|
+
cursor = StageManager().remove(stage_name=stage_name.identifier, path=file_name)
|
|
168
170
|
return SingleQueryResult(cursor)
|
|
169
171
|
|
|
170
172
|
|
|
@@ -65,14 +65,21 @@ class StagePathParts:
|
|
|
65
65
|
stage_name: str
|
|
66
66
|
is_directory: bool
|
|
67
67
|
|
|
68
|
-
@
|
|
69
|
-
def get_directory(stage_path: str) -> str:
|
|
68
|
+
@classmethod
|
|
69
|
+
def get_directory(cls, stage_path: str) -> str:
|
|
70
70
|
return "/".join(Path(stage_path).parts[1:])
|
|
71
71
|
|
|
72
72
|
@property
|
|
73
73
|
def path(self) -> str:
|
|
74
74
|
raise NotImplementedError
|
|
75
75
|
|
|
76
|
+
@property
|
|
77
|
+
def full_path(self) -> str:
|
|
78
|
+
raise NotImplementedError
|
|
79
|
+
|
|
80
|
+
def replace_stage_prefix(self, file_path: str) -> str:
|
|
81
|
+
raise NotImplementedError
|
|
82
|
+
|
|
76
83
|
def add_stage_prefix(self, file_path: str) -> str:
|
|
77
84
|
raise NotImplementedError
|
|
78
85
|
|
|
@@ -112,24 +119,27 @@ class DefaultStagePathParts(StagePathParts):
|
|
|
112
119
|
self.directory = self.get_directory(stage_path)
|
|
113
120
|
self.stage = StageManager.get_stage_from_path(stage_path)
|
|
114
121
|
stage_name = self.stage.split(".")[-1]
|
|
115
|
-
if stage_name.startswith("@")
|
|
116
|
-
stage_name = stage_name[1:]
|
|
122
|
+
stage_name = stage_name[1:] if stage_name.startswith("@") else stage_name
|
|
117
123
|
self.stage_name = stage_name
|
|
118
124
|
self.is_directory = True if stage_path.endswith("/") else False
|
|
119
125
|
|
|
120
126
|
@property
|
|
121
127
|
def path(self) -> str:
|
|
122
|
-
return (
|
|
123
|
-
f"{self.stage_name}{self.directory}"
|
|
124
|
-
if self.stage_name.endswith("/")
|
|
125
|
-
else f"{self.stage_name}/{self.directory}"
|
|
126
|
-
)
|
|
128
|
+
return f"{self.stage_name.rstrip('/')}/{self.directory}"
|
|
127
129
|
|
|
128
|
-
|
|
130
|
+
@property
|
|
131
|
+
def full_path(self) -> str:
|
|
132
|
+
return f"{self.stage.rstrip('/')}/{self.directory}"
|
|
133
|
+
|
|
134
|
+
def replace_stage_prefix(self, file_path: str) -> str:
|
|
129
135
|
stage = Path(self.stage).parts[0]
|
|
130
136
|
file_path_without_prefix = Path(file_path).parts[1:]
|
|
131
137
|
return f"{stage}/{'/'.join(file_path_without_prefix)}"
|
|
132
138
|
|
|
139
|
+
def add_stage_prefix(self, file_path: str) -> str:
|
|
140
|
+
stage = self.stage.rstrip("/")
|
|
141
|
+
return f"{stage}/{file_path.lstrip('/')}"
|
|
142
|
+
|
|
133
143
|
def get_directory_from_file_path(self, file_path: str) -> List[str]:
|
|
134
144
|
stage_path_length = len(Path(self.directory).parts)
|
|
135
145
|
return list(Path(file_path).parts[1 + stage_path_length : -1])
|
|
@@ -146,14 +156,29 @@ class UserStagePathParts(StagePathParts):
|
|
|
146
156
|
|
|
147
157
|
def __init__(self, stage_path: str):
|
|
148
158
|
self.directory = self.get_directory(stage_path)
|
|
149
|
-
self.stage =
|
|
150
|
-
self.stage_name =
|
|
159
|
+
self.stage = USER_STAGE_PREFIX
|
|
160
|
+
self.stage_name = USER_STAGE_PREFIX
|
|
151
161
|
self.is_directory = True if stage_path.endswith("/") else False
|
|
152
162
|
|
|
163
|
+
@classmethod
|
|
164
|
+
def get_directory(cls, stage_path: str) -> str:
|
|
165
|
+
if Path(stage_path).parts[0] == USER_STAGE_PREFIX:
|
|
166
|
+
return super().get_directory(stage_path)
|
|
167
|
+
return stage_path
|
|
168
|
+
|
|
153
169
|
@property
|
|
154
170
|
def path(self) -> str:
|
|
155
171
|
return f"{self.directory}"
|
|
156
172
|
|
|
173
|
+
@property
|
|
174
|
+
def full_path(self) -> str:
|
|
175
|
+
return f"{self.stage}/{self.directory}"
|
|
176
|
+
|
|
177
|
+
def replace_stage_prefix(self, file_path: str) -> str:
|
|
178
|
+
if Path(file_path).parts[0] == self.stage_name:
|
|
179
|
+
return file_path
|
|
180
|
+
return f"{self.stage}/{file_path}"
|
|
181
|
+
|
|
157
182
|
def add_stage_prefix(self, file_path: str) -> str:
|
|
158
183
|
return f"{self.stage}/{file_path}"
|
|
159
184
|
|
|
@@ -168,7 +193,9 @@ class StageManager(SqlExecutionMixin):
|
|
|
168
193
|
self._python_exe_procedure = None
|
|
169
194
|
|
|
170
195
|
@staticmethod
|
|
171
|
-
def get_standard_stage_prefix(name: str) -> str:
|
|
196
|
+
def get_standard_stage_prefix(name: str | FQN) -> str:
|
|
197
|
+
if isinstance(name, FQN):
|
|
198
|
+
name = name.identifier
|
|
172
199
|
# Handle embedded stages
|
|
173
200
|
if name.startswith("snow://") or name.startswith("@"):
|
|
174
201
|
return name
|
|
@@ -239,7 +266,7 @@ class StageManager(SqlExecutionMixin):
|
|
|
239
266
|
self._assure_is_existing_directory(dest_directory)
|
|
240
267
|
|
|
241
268
|
result = self._execute_query(
|
|
242
|
-
f"get {self.quote_stage_name(stage_path_parts.
|
|
269
|
+
f"get {self.quote_stage_name(stage_path_parts.replace_stage_prefix(file_path))} {self._to_uri(f'{dest_directory}/')} parallel={parallel}"
|
|
243
270
|
)
|
|
244
271
|
results.append(result)
|
|
245
272
|
|
|
@@ -300,8 +327,8 @@ class StageManager(SqlExecutionMixin):
|
|
|
300
327
|
quoted_stage_name = self.quote_stage_name(f"{stage_name}{path}")
|
|
301
328
|
return self._execute_query(f"remove {quoted_stage_name}")
|
|
302
329
|
|
|
303
|
-
def create(self,
|
|
304
|
-
query = f"create stage if not exists {
|
|
330
|
+
def create(self, fqn: FQN, comment: Optional[str] = None) -> SnowflakeCursor:
|
|
331
|
+
query = f"create stage if not exists {fqn.sql_identifier}"
|
|
305
332
|
if comment:
|
|
306
333
|
query += f" comment='{comment}'"
|
|
307
334
|
return self._execute_query(query)
|
|
@@ -319,8 +346,14 @@ class StageManager(SqlExecutionMixin):
|
|
|
319
346
|
stage_path_parts = self._stage_path_part_factory(stage_path)
|
|
320
347
|
all_files_list = self._get_files_list_from_stage(stage_path_parts)
|
|
321
348
|
|
|
349
|
+
all_files_with_stage_name_prefix = [
|
|
350
|
+
stage_path_parts.get_directory(file) for file in all_files_list
|
|
351
|
+
]
|
|
352
|
+
|
|
322
353
|
# filter files from stage if match stage_path pattern
|
|
323
|
-
filtered_file_list = self._filter_files_list(
|
|
354
|
+
filtered_file_list = self._filter_files_list(
|
|
355
|
+
stage_path_parts, all_files_with_stage_name_prefix
|
|
356
|
+
)
|
|
324
357
|
|
|
325
358
|
if not filtered_file_list:
|
|
326
359
|
raise ClickException(f"No files matched pattern '{stage_path}'")
|
|
@@ -376,7 +409,7 @@ class StageManager(SqlExecutionMixin):
|
|
|
376
409
|
if not stage_path_parts.directory:
|
|
377
410
|
return self._filter_supported_files(files_on_stage)
|
|
378
411
|
|
|
379
|
-
stage_path = stage_path_parts.
|
|
412
|
+
stage_path = stage_path_parts.directory
|
|
380
413
|
|
|
381
414
|
# Exact file path was provided if stage_path in file list
|
|
382
415
|
if stage_path in files_on_stage:
|