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.
Files changed (57) hide show
  1. snowflake/cli/__about__.py +1 -1
  2. snowflake/cli/_app/cli_app.py +3 -0
  3. snowflake/cli/_app/dev/docs/templates/overview.rst.jinja2 +1 -1
  4. snowflake/cli/_app/dev/docs/templates/usage.rst.jinja2 +2 -2
  5. snowflake/cli/_app/telemetry.py +69 -4
  6. snowflake/cli/_plugins/connection/commands.py +40 -2
  7. snowflake/cli/_plugins/git/commands.py +6 -3
  8. snowflake/cli/_plugins/git/manager.py +5 -0
  9. snowflake/cli/_plugins/nativeapp/artifacts.py +13 -3
  10. snowflake/cli/_plugins/nativeapp/codegen/artifact_processor.py +1 -1
  11. snowflake/cli/_plugins/nativeapp/codegen/compiler.py +7 -0
  12. snowflake/cli/_plugins/nativeapp/codegen/sandbox.py +10 -10
  13. snowflake/cli/_plugins/nativeapp/codegen/setup/native_app_setup_processor.py +2 -2
  14. snowflake/cli/_plugins/nativeapp/codegen/snowpark/extension_function_utils.py +1 -1
  15. snowflake/cli/_plugins/nativeapp/codegen/snowpark/python_processor.py +8 -8
  16. snowflake/cli/_plugins/nativeapp/commands.py +135 -186
  17. snowflake/cli/_plugins/nativeapp/entities/application.py +176 -24
  18. snowflake/cli/_plugins/nativeapp/entities/application_package.py +112 -136
  19. snowflake/cli/_plugins/nativeapp/exceptions.py +12 -0
  20. snowflake/cli/_plugins/nativeapp/manager.py +3 -26
  21. snowflake/cli/_plugins/nativeapp/v2_conversions/{v2_to_v1_decorator.py → compat.py} +131 -72
  22. snowflake/cli/_plugins/nativeapp/version/commands.py +30 -29
  23. snowflake/cli/_plugins/nativeapp/version/version_processor.py +1 -43
  24. snowflake/cli/_plugins/snowpark/common.py +60 -18
  25. snowflake/cli/_plugins/snowpark/package/anaconda_packages.py +2 -2
  26. snowflake/cli/_plugins/spcs/image_repository/commands.py +4 -37
  27. snowflake/cli/_plugins/spcs/image_repository/manager.py +4 -1
  28. snowflake/cli/_plugins/spcs/services/commands.py +36 -4
  29. snowflake/cli/_plugins/spcs/services/manager.py +36 -4
  30. snowflake/cli/_plugins/stage/commands.py +8 -3
  31. snowflake/cli/_plugins/stage/diff.py +16 -16
  32. snowflake/cli/_plugins/stage/manager.py +164 -73
  33. snowflake/cli/_plugins/stage/md5.py +1 -1
  34. snowflake/cli/_plugins/workspace/commands.py +21 -1
  35. snowflake/cli/_plugins/workspace/context.py +38 -0
  36. snowflake/cli/_plugins/workspace/manager.py +23 -13
  37. snowflake/cli/api/cli_global_context.py +3 -3
  38. snowflake/cli/api/commands/flags.py +23 -7
  39. snowflake/cli/api/config.py +7 -4
  40. snowflake/cli/api/connections.py +12 -1
  41. snowflake/cli/api/entities/common.py +4 -2
  42. snowflake/cli/api/entities/utils.py +17 -37
  43. snowflake/cli/api/exceptions.py +32 -0
  44. snowflake/cli/api/identifiers.py +8 -0
  45. snowflake/cli/api/project/definition_conversion.py +139 -40
  46. snowflake/cli/api/project/schemas/entities/common.py +11 -0
  47. snowflake/cli/api/project/schemas/project_definition.py +30 -25
  48. snowflake/cli/api/sql_execution.py +5 -7
  49. snowflake/cli/api/stage_path.py +241 -0
  50. snowflake/cli/api/utils/definition_rendering.py +3 -5
  51. {snowflake_cli-3.0.2.dist-info → snowflake_cli-3.1.0.dist-info}/METADATA +11 -11
  52. {snowflake_cli-3.0.2.dist-info → snowflake_cli-3.1.0.dist-info}/RECORD +55 -55
  53. snowflake/cli/_plugins/nativeapp/teardown_processor.py +0 -70
  54. snowflake/cli/_plugins/workspace/action_context.py +0 -18
  55. {snowflake_cli-3.0.2.dist-info → snowflake_cli-3.1.0.dist-info}/WHEEL +0 -0
  56. {snowflake_cli-3.0.2.dist-info → snowflake_cli-3.1.0.dist-info}/entry_points.txt +0 -0
  57. {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 External Access Integrations(EAI) that the service can access. This option may be specified multiple times for multiple EAIs.",
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 External Access Integrations(EAI) that the job service can access. This option may be specified multiple times for multiple EAIs.",
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
- for property_name, value in property_pairs:
180
- if value is not None:
181
- query.append(f"{property_name} = {value}")
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 = StageNameArgument, pattern=PatternOption, **options
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 like pattern,
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
- stage_path=stage_path, on_error=on_error, variables=variables
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
- StagePath = PurePosixPath # alias PurePosixPath as StagePath for clarity
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[StagePath] = field(default_factory=list)
42
+ identical: List[StagePathType] = field(default_factory=list)
43
43
  "Files with matching md5sums"
44
44
 
45
- different: List[StagePath] = field(default_factory=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[StagePath] = field(default_factory=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[StagePath] = field(default_factory=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) -> StagePath:
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 StagePath(*path.split("/")[1:])
88
+ return StagePathType(*path.split("/")[1:])
89
89
 
90
90
 
91
- def build_md5_map(list_stage_cursor: DictCursor) -> Dict[StagePath, Optional[str]]:
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[StagePath]
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: StagePath) -> str:
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) -> StagePath:
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 StagePath(*filename.parts)
179
+ return StagePathType(*filename.parts)
180
180
 
181
181
 
182
- def to_local_path(stage_path: StagePath) -> 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[StagePath],
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[StagePath],
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: StagePath, bundle_map: Optional[BundleMap]
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)