snowflake-cli 3.0.2__py3-none-any.whl → 3.1.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 +3 -0
- snowflake/cli/_app/dev/docs/templates/overview.rst.jinja2 +1 -1
- snowflake/cli/_app/dev/docs/templates/usage.rst.jinja2 +2 -2
- snowflake/cli/_app/telemetry.py +69 -4
- snowflake/cli/_plugins/connection/commands.py +40 -2
- snowflake/cli/_plugins/git/commands.py +6 -3
- snowflake/cli/_plugins/git/manager.py +5 -0
- snowflake/cli/_plugins/nativeapp/artifacts.py +13 -3
- snowflake/cli/_plugins/nativeapp/codegen/artifact_processor.py +1 -1
- snowflake/cli/_plugins/nativeapp/codegen/compiler.py +7 -0
- snowflake/cli/_plugins/nativeapp/codegen/sandbox.py +10 -10
- snowflake/cli/_plugins/nativeapp/codegen/setup/native_app_setup_processor.py +2 -2
- snowflake/cli/_plugins/nativeapp/codegen/snowpark/extension_function_utils.py +1 -1
- snowflake/cli/_plugins/nativeapp/codegen/snowpark/python_processor.py +8 -8
- snowflake/cli/_plugins/nativeapp/commands.py +135 -186
- snowflake/cli/_plugins/nativeapp/entities/application.py +176 -24
- snowflake/cli/_plugins/nativeapp/entities/application_package.py +112 -136
- snowflake/cli/_plugins/nativeapp/exceptions.py +12 -0
- snowflake/cli/_plugins/nativeapp/manager.py +3 -26
- snowflake/cli/_plugins/nativeapp/v2_conversions/{v2_to_v1_decorator.py → compat.py} +131 -72
- snowflake/cli/_plugins/nativeapp/version/commands.py +30 -29
- snowflake/cli/_plugins/nativeapp/version/version_processor.py +1 -43
- snowflake/cli/_plugins/snowpark/common.py +60 -18
- snowflake/cli/_plugins/snowpark/package/anaconda_packages.py +2 -2
- snowflake/cli/_plugins/spcs/image_repository/commands.py +4 -37
- snowflake/cli/_plugins/spcs/image_repository/manager.py +4 -1
- snowflake/cli/_plugins/spcs/services/commands.py +36 -4
- snowflake/cli/_plugins/spcs/services/manager.py +36 -4
- snowflake/cli/_plugins/stage/commands.py +8 -3
- snowflake/cli/_plugins/stage/diff.py +16 -16
- snowflake/cli/_plugins/stage/manager.py +164 -73
- snowflake/cli/_plugins/stage/md5.py +1 -1
- snowflake/cli/_plugins/workspace/commands.py +21 -1
- snowflake/cli/_plugins/workspace/context.py +38 -0
- snowflake/cli/_plugins/workspace/manager.py +23 -13
- snowflake/cli/api/cli_global_context.py +3 -3
- snowflake/cli/api/commands/flags.py +23 -7
- snowflake/cli/api/config.py +7 -4
- snowflake/cli/api/connections.py +12 -1
- snowflake/cli/api/entities/common.py +4 -2
- snowflake/cli/api/entities/utils.py +17 -37
- snowflake/cli/api/exceptions.py +32 -0
- snowflake/cli/api/identifiers.py +8 -0
- snowflake/cli/api/project/definition_conversion.py +139 -40
- snowflake/cli/api/project/schemas/entities/common.py +11 -0
- snowflake/cli/api/project/schemas/project_definition.py +30 -25
- snowflake/cli/api/sql_execution.py +5 -7
- snowflake/cli/api/stage_path.py +241 -0
- snowflake/cli/api/utils/definition_rendering.py +3 -5
- {snowflake_cli-3.0.2.dist-info → snowflake_cli-3.1.0.dist-info}/METADATA +11 -11
- {snowflake_cli-3.0.2.dist-info → snowflake_cli-3.1.0.dist-info}/RECORD +55 -55
- snowflake/cli/_plugins/nativeapp/teardown_processor.py +0 -70
- snowflake/cli/_plugins/workspace/action_context.py +0 -18
- {snowflake_cli-3.0.2.dist-info → snowflake_cli-3.1.0.dist-info}/WHEEL +0 -0
- {snowflake_cli-3.0.2.dist-info → snowflake_cli-3.1.0.dist-info}/entry_points.txt +0 -0
- {snowflake_cli-3.0.2.dist-info → snowflake_cli-3.1.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -131,7 +131,7 @@ def create(
|
|
|
131
131
|
external_access_integrations: Optional[List[str]] = typer.Option(
|
|
132
132
|
None,
|
|
133
133
|
"--eai-name",
|
|
134
|
-
help="Identifies
|
|
134
|
+
help="Identifies external access integrations (EAI) that the service can access. This option may be specified multiple times for multiple EAIs.",
|
|
135
135
|
),
|
|
136
136
|
query_warehouse: Optional[str] = QueryWarehouseOption(),
|
|
137
137
|
tags: Optional[List[Tag]] = TagOption(help="Tag for the service."),
|
|
@@ -174,7 +174,7 @@ def execute_job(
|
|
|
174
174
|
external_access_integrations: Optional[List[str]] = typer.Option(
|
|
175
175
|
None,
|
|
176
176
|
"--eai-name",
|
|
177
|
-
help="Identifies
|
|
177
|
+
help="Identifies external access integrations (EAI) that the job service can access. This option may be specified multiple times for multiple EAIs.",
|
|
178
178
|
),
|
|
179
179
|
query_warehouse: Optional[str] = QueryWarehouseOption(),
|
|
180
180
|
comment: Optional[str] = CommentOption(help=_COMMENT_HELP),
|
|
@@ -194,10 +194,10 @@ def execute_job(
|
|
|
194
194
|
return SingleQueryResult(cursor)
|
|
195
195
|
|
|
196
196
|
|
|
197
|
-
@app.command(requires_connection=True)
|
|
197
|
+
@app.command(requires_connection=True, deprecated=True)
|
|
198
198
|
def status(name: FQN = ServiceNameArgument, **options) -> CommandResult:
|
|
199
199
|
"""
|
|
200
|
-
Retrieves the status of a service.
|
|
200
|
+
Retrieves the status of a service. This command is deprecated and will be removed in a future release. Use `describe` instead to get service status and use `list-instances` and `list-containers` to get more detailed information about service instances and containers.
|
|
201
201
|
"""
|
|
202
202
|
cursor = ServiceManager().status(service_name=name.identifier)
|
|
203
203
|
return QueryJsonValueResult(cursor)
|
|
@@ -259,6 +259,32 @@ def list_endpoints(name: FQN = ServiceNameArgument, **options):
|
|
|
259
259
|
return QueryResult(ServiceManager().list_endpoints(service_name=name.identifier))
|
|
260
260
|
|
|
261
261
|
|
|
262
|
+
@app.command("list-instances", requires_connection=True)
|
|
263
|
+
def list_service_instances(name: FQN = ServiceNameArgument, **options) -> CommandResult:
|
|
264
|
+
"""
|
|
265
|
+
Lists all service instances in a service.
|
|
266
|
+
"""
|
|
267
|
+
return QueryResult(ServiceManager().list_instances(service_name=name.identifier))
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
@app.command("list-containers", requires_connection=True)
|
|
271
|
+
def list_service_containers(
|
|
272
|
+
name: FQN = ServiceNameArgument, **options
|
|
273
|
+
) -> CommandResult:
|
|
274
|
+
"""
|
|
275
|
+
Lists all service containers in a service.
|
|
276
|
+
"""
|
|
277
|
+
return QueryResult(ServiceManager().list_containers(service_name=name.identifier))
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
@app.command("list-roles", requires_connection=True)
|
|
281
|
+
def list_service_roles(name: FQN = ServiceNameArgument, **options) -> CommandResult:
|
|
282
|
+
"""
|
|
283
|
+
Lists all service roles in a service.
|
|
284
|
+
"""
|
|
285
|
+
return QueryResult(ServiceManager().list_roles(service_name=name.identifier))
|
|
286
|
+
|
|
287
|
+
|
|
262
288
|
@app.command(requires_connection=True)
|
|
263
289
|
def suspend(name: FQN = ServiceNameArgument, **options) -> CommandResult:
|
|
264
290
|
"""
|
|
@@ -282,6 +308,11 @@ def set_property(
|
|
|
282
308
|
max_instances: Optional[int] = MaxInstancesOption(show_default=False),
|
|
283
309
|
query_warehouse: Optional[str] = QueryWarehouseOption(show_default=False),
|
|
284
310
|
auto_resume: Optional[bool] = AutoResumeOption(default=None, show_default=False),
|
|
311
|
+
external_access_integrations: Optional[List[str]] = typer.Option(
|
|
312
|
+
None,
|
|
313
|
+
"--eai-name",
|
|
314
|
+
help="Identifies external access integrations (EAI) that the service can access. This option may be specified multiple times for multiple EAIs.",
|
|
315
|
+
),
|
|
285
316
|
comment: Optional[str] = CommentOption(help=_COMMENT_HELP, show_default=False),
|
|
286
317
|
**options,
|
|
287
318
|
):
|
|
@@ -294,6 +325,7 @@ def set_property(
|
|
|
294
325
|
max_instances=max_instances,
|
|
295
326
|
query_warehouse=query_warehouse,
|
|
296
327
|
auto_resume=auto_resume,
|
|
328
|
+
external_access_integrations=external_access_integrations,
|
|
297
329
|
comment=comment,
|
|
298
330
|
)
|
|
299
331
|
return SingleQueryResult(cursor)
|
|
@@ -147,6 +147,15 @@ class ServiceManager(SqlExecutionMixin):
|
|
|
147
147
|
def list_endpoints(self, service_name: str) -> SnowflakeCursor:
|
|
148
148
|
return self._execute_query(f"show endpoints in service {service_name}")
|
|
149
149
|
|
|
150
|
+
def list_instances(self, service_name: str) -> SnowflakeCursor:
|
|
151
|
+
return self._execute_query(f"show service instances in service {service_name}")
|
|
152
|
+
|
|
153
|
+
def list_containers(self, service_name: str) -> SnowflakeCursor:
|
|
154
|
+
return self._execute_query(f"show service containers in service {service_name}")
|
|
155
|
+
|
|
156
|
+
def list_roles(self, service_name: str) -> SnowflakeCursor:
|
|
157
|
+
return self._execute_query(f"show roles in service {service_name}")
|
|
158
|
+
|
|
150
159
|
def suspend(self, service_name: str):
|
|
151
160
|
return self._execute_query(f"alter service {service_name} suspend")
|
|
152
161
|
|
|
@@ -160,6 +169,7 @@ class ServiceManager(SqlExecutionMixin):
|
|
|
160
169
|
max_instances: Optional[int],
|
|
161
170
|
query_warehouse: Optional[str],
|
|
162
171
|
auto_resume: Optional[bool],
|
|
172
|
+
external_access_integrations: Optional[List[str]],
|
|
163
173
|
comment: Optional[str],
|
|
164
174
|
):
|
|
165
175
|
property_pairs = [
|
|
@@ -167,6 +177,7 @@ class ServiceManager(SqlExecutionMixin):
|
|
|
167
177
|
("max_instances", max_instances),
|
|
168
178
|
("query_warehouse", query_warehouse),
|
|
169
179
|
("auto_resume", auto_resume),
|
|
180
|
+
("external_access_integrations", external_access_integrations),
|
|
170
181
|
("comment", comment),
|
|
171
182
|
]
|
|
172
183
|
|
|
@@ -175,10 +186,31 @@ class ServiceManager(SqlExecutionMixin):
|
|
|
175
186
|
raise NoPropertiesProvidedError(
|
|
176
187
|
f"No properties specified for service '{service_name}'. Please provide at least one property to set."
|
|
177
188
|
)
|
|
178
|
-
query: List[str] = [f"alter service {service_name} set"]
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
189
|
+
query: List[str] = [f"alter service {service_name} set "]
|
|
190
|
+
|
|
191
|
+
if min_instances is not None:
|
|
192
|
+
query.append(f" min_instances = {min_instances}")
|
|
193
|
+
|
|
194
|
+
if max_instances is not None:
|
|
195
|
+
query.append(f" max_instances = {max_instances}")
|
|
196
|
+
|
|
197
|
+
if query_warehouse is not None:
|
|
198
|
+
query.append(f" query_warehouse = {query_warehouse}")
|
|
199
|
+
|
|
200
|
+
if auto_resume is not None:
|
|
201
|
+
query.append(f" auto_resume = {auto_resume}")
|
|
202
|
+
|
|
203
|
+
if external_access_integrations is not None:
|
|
204
|
+
external_access_integration_list = ",".join(
|
|
205
|
+
f"{e}" for e in external_access_integrations
|
|
206
|
+
)
|
|
207
|
+
query.append(
|
|
208
|
+
f"external_access_integrations = ({external_access_integration_list})"
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
if comment is not None:
|
|
212
|
+
query.append(f" comment = {comment}")
|
|
213
|
+
|
|
182
214
|
return self._execute_query(strip_empty_lines(query))
|
|
183
215
|
|
|
184
216
|
def unset_property(
|
|
@@ -38,6 +38,7 @@ from snowflake.cli.api.commands.flags import (
|
|
|
38
38
|
OnErrorOption,
|
|
39
39
|
PatternOption,
|
|
40
40
|
identifier_stage_argument,
|
|
41
|
+
identifier_stage_path_argument,
|
|
41
42
|
like_option,
|
|
42
43
|
)
|
|
43
44
|
from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
|
|
@@ -60,6 +61,10 @@ app = SnowTyperFactory(
|
|
|
60
61
|
)
|
|
61
62
|
|
|
62
63
|
StageNameArgument = identifier_stage_argument(sf_object="stage", example="@my_stage")
|
|
64
|
+
StagePathArgument = identifier_stage_path_argument(
|
|
65
|
+
sf_object="stage", example="@my_stage/path"
|
|
66
|
+
)
|
|
67
|
+
|
|
63
68
|
|
|
64
69
|
add_object_command_aliases(
|
|
65
70
|
app=app,
|
|
@@ -74,7 +79,7 @@ add_object_command_aliases(
|
|
|
74
79
|
|
|
75
80
|
@app.command("list-files", requires_connection=True)
|
|
76
81
|
def stage_list_files(
|
|
77
|
-
stage_name: str =
|
|
82
|
+
stage_name: str = StagePathArgument, pattern=PatternOption, **options
|
|
78
83
|
) -> CommandResult:
|
|
79
84
|
"""
|
|
80
85
|
Lists the stage contents.
|
|
@@ -208,11 +213,11 @@ def execute(
|
|
|
208
213
|
**options,
|
|
209
214
|
):
|
|
210
215
|
"""
|
|
211
|
-
Execute immediate all files from the stage path. Files can be filtered with glob
|
|
216
|
+
Execute immediate all files from the stage path. Files can be filtered with a glob-like pattern,
|
|
212
217
|
e.g. `@stage/*.sql`, `@stage/dev/*`. Only files with `.sql` extension will be executed.
|
|
213
218
|
"""
|
|
214
219
|
results = StageManager().execute(
|
|
215
|
-
|
|
220
|
+
stage_path_str=stage_path, on_error=on_error, variables=variables
|
|
216
221
|
)
|
|
217
222
|
return CollectionResult(results)
|
|
218
223
|
|
|
@@ -30,7 +30,7 @@ from .md5 import UnknownMD5FormatError, file_matches_md5sum
|
|
|
30
30
|
|
|
31
31
|
log = logging.getLogger(__name__)
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
StagePathType = PurePosixPath # alias PurePosixPath as StagePath for clarity
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
@dataclass
|
|
@@ -39,16 +39,16 @@ class DiffResult:
|
|
|
39
39
|
Each collection is a list of stage paths ('/'-separated, regardless of the platform), relative to the stage root.
|
|
40
40
|
"""
|
|
41
41
|
|
|
42
|
-
identical: List[
|
|
42
|
+
identical: List[StagePathType] = field(default_factory=list)
|
|
43
43
|
"Files with matching md5sums"
|
|
44
44
|
|
|
45
|
-
different: List[
|
|
45
|
+
different: List[StagePathType] = field(default_factory=list)
|
|
46
46
|
"Files that may be different between the stage and the local directory"
|
|
47
47
|
|
|
48
|
-
only_local: List[
|
|
48
|
+
only_local: List[StagePathType] = field(default_factory=list)
|
|
49
49
|
"Files that only exist in the local directory"
|
|
50
50
|
|
|
51
|
-
only_on_stage: List[
|
|
51
|
+
only_on_stage: List[StagePathType] = field(default_factory=list)
|
|
52
52
|
"Files that only exist on the stage"
|
|
53
53
|
|
|
54
54
|
def has_changes(self) -> bool:
|
|
@@ -83,12 +83,12 @@ def enumerate_files(path: Path) -> List[Path]:
|
|
|
83
83
|
return paths
|
|
84
84
|
|
|
85
85
|
|
|
86
|
-
def strip_stage_name(path: str) ->
|
|
86
|
+
def strip_stage_name(path: str) -> StagePathType:
|
|
87
87
|
"""Returns the given stage path without the stage name as the first part."""
|
|
88
|
-
return
|
|
88
|
+
return StagePathType(*path.split("/")[1:])
|
|
89
89
|
|
|
90
90
|
|
|
91
|
-
def build_md5_map(list_stage_cursor: DictCursor) -> Dict[
|
|
91
|
+
def build_md5_map(list_stage_cursor: DictCursor) -> Dict[StagePathType, Optional[str]]:
|
|
92
92
|
"""
|
|
93
93
|
Returns a mapping of relative stage paths to their md5sums.
|
|
94
94
|
"""
|
|
@@ -99,7 +99,7 @@ def build_md5_map(list_stage_cursor: DictCursor) -> Dict[StagePath, Optional[str
|
|
|
99
99
|
|
|
100
100
|
|
|
101
101
|
def preserve_from_diff(
|
|
102
|
-
diff: DiffResult, stage_paths_to_sync: Collection[
|
|
102
|
+
diff: DiffResult, stage_paths_to_sync: Collection[StagePathType]
|
|
103
103
|
) -> DiffResult:
|
|
104
104
|
"""
|
|
105
105
|
Returns a filtered version of the provided diff, keeping only the provided stage paths.
|
|
@@ -163,7 +163,7 @@ def compute_stage_diff(
|
|
|
163
163
|
return result
|
|
164
164
|
|
|
165
165
|
|
|
166
|
-
def get_stage_subpath(stage_path:
|
|
166
|
+
def get_stage_subpath(stage_path: StagePathType) -> str:
|
|
167
167
|
"""
|
|
168
168
|
Returns the parent portion of a stage path, as a string, for inclusion in the fully qualified stage path. Note that
|
|
169
169
|
'.' treated specially here, and so the return value of this call is not a `StagePath` instance.
|
|
@@ -172,21 +172,21 @@ def get_stage_subpath(stage_path: StagePath) -> str:
|
|
|
172
172
|
return "" if parent == "." else parent
|
|
173
173
|
|
|
174
174
|
|
|
175
|
-
def to_stage_path(filename: Path) ->
|
|
175
|
+
def to_stage_path(filename: Path) -> StagePathType:
|
|
176
176
|
"""
|
|
177
177
|
Returns the stage file name, with the path separator suitably transformed if needed.
|
|
178
178
|
"""
|
|
179
|
-
return
|
|
179
|
+
return StagePathType(*filename.parts)
|
|
180
180
|
|
|
181
181
|
|
|
182
|
-
def to_local_path(stage_path:
|
|
182
|
+
def to_local_path(stage_path: StagePathType) -> Path:
|
|
183
183
|
return Path(*stage_path.parts)
|
|
184
184
|
|
|
185
185
|
|
|
186
186
|
def delete_only_on_stage_files(
|
|
187
187
|
stage_manager: StageManager,
|
|
188
188
|
stage_fqn: str,
|
|
189
|
-
only_on_stage: List[
|
|
189
|
+
only_on_stage: List[StagePathType],
|
|
190
190
|
role: Optional[str] = None,
|
|
191
191
|
):
|
|
192
192
|
"""
|
|
@@ -200,7 +200,7 @@ def put_files_on_stage(
|
|
|
200
200
|
stage_manager: StageManager,
|
|
201
201
|
stage_fqn: str,
|
|
202
202
|
deploy_root_path: Path,
|
|
203
|
-
stage_paths: List[
|
|
203
|
+
stage_paths: List[StagePathType],
|
|
204
204
|
role: Optional[str] = None,
|
|
205
205
|
overwrite: bool = False,
|
|
206
206
|
):
|
|
@@ -254,7 +254,7 @@ def sync_local_diff_with_stage(
|
|
|
254
254
|
|
|
255
255
|
|
|
256
256
|
def _to_src_dest_pair(
|
|
257
|
-
stage_path:
|
|
257
|
+
stage_path: StagePathType, bundle_map: Optional[BundleMap]
|
|
258
258
|
) -> Tuple[Optional[str], str]:
|
|
259
259
|
if not bundle_map:
|
|
260
260
|
return None, str(stage_path)
|