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
@@ -22,7 +22,6 @@ from textwrap import dedent
22
22
  from typing import Generator, Iterable, List, Optional, cast
23
23
 
24
24
  import typer
25
- from click.exceptions import ClickException
26
25
  from snowflake.cli._plugins.nativeapp.common_flags import (
27
26
  ForceOption,
28
27
  InteractiveOption,
@@ -32,19 +31,9 @@ from snowflake.cli._plugins.nativeapp.entities.application import ApplicationEnt
32
31
  from snowflake.cli._plugins.nativeapp.entities.application_package import (
33
32
  ApplicationPackageEntityModel,
34
33
  )
35
- from snowflake.cli._plugins.nativeapp.manager import NativeAppManager
36
- from snowflake.cli._plugins.nativeapp.policy import (
37
- AllowAlwaysPolicy,
38
- AskAlwaysPolicy,
39
- DenyAlwaysPolicy,
40
- )
41
- from snowflake.cli._plugins.nativeapp.run_processor import NativeAppRunProcessor
42
- from snowflake.cli._plugins.nativeapp.teardown_processor import (
43
- NativeAppTeardownProcessor,
44
- )
45
- from snowflake.cli._plugins.nativeapp.v2_conversions.v2_to_v1_decorator import (
34
+ from snowflake.cli._plugins.nativeapp.v2_conversions.compat import (
46
35
  find_entity,
47
- nativeapp_definition_v2_to_v1,
36
+ force_project_definition_v2,
48
37
  )
49
38
  from snowflake.cli._plugins.nativeapp.version.commands import app as versions_app
50
39
  from snowflake.cli._plugins.stage.diff import (
@@ -67,8 +56,6 @@ from snowflake.cli.api.output.types import (
67
56
  ObjectResult,
68
57
  StreamResult,
69
58
  )
70
- from snowflake.cli.api.project.project_verification import assert_project_type
71
- from snowflake.cli.api.project.schemas.project_definition import ProjectDefinitionV1
72
59
  from typing_extensions import Annotated
73
60
 
74
61
  app = SnowTyperFactory(
@@ -80,57 +67,52 @@ app.add_typer(versions_app)
80
67
  log = logging.getLogger(__name__)
81
68
 
82
69
 
83
- @app.command("init", hidden=True)
84
- def app_init(**options):
85
- """
86
- *** Deprecated. Use snow init instead ***
87
-
88
- Initializes a Snowflake Native App project.
89
- """
90
-
91
- raise ClickException("This command has been removed. Use `snow init` instead.")
92
-
93
-
94
70
  @app.command("bundle")
95
71
  @with_project_definition()
96
- @nativeapp_definition_v2_to_v1()
72
+ @force_project_definition_v2()
97
73
  def app_bundle(
98
74
  **options,
99
75
  ) -> CommandResult:
100
76
  """
101
77
  Prepares a local folder with configured app artifacts.
102
78
  """
103
-
104
- assert_project_type("native_app")
105
-
106
79
  cli_context = get_cli_context()
107
- manager = NativeAppManager(
108
- project_definition=cli_context.project_definition.native_app,
80
+ ws = WorkspaceManager(
81
+ project_definition=cli_context.project_definition,
109
82
  project_root=cli_context.project_root,
110
83
  )
111
- manager.build_bundle()
112
- return MessageResult(f"Bundle generated at {manager.deploy_root}")
84
+ package_id = options["package_entity_id"]
85
+ package = cli_context.project_definition.entities[package_id]
86
+ ws.perform_action(
87
+ package_id,
88
+ EntityActions.BUNDLE,
89
+ )
90
+ return MessageResult(f"Bundle generated at {ws.project_root / package.deploy_root}")
113
91
 
114
92
 
115
93
  @app.command("diff", requires_connection=True, hidden=True)
116
94
  @with_project_definition()
117
- @nativeapp_definition_v2_to_v1()
95
+ @force_project_definition_v2()
118
96
  def app_diff(
119
97
  **options,
120
- ) -> CommandResult:
98
+ ) -> CommandResult | None:
121
99
  """
122
100
  Performs a diff between the app's source stage and the local deploy root.
123
101
  """
124
- assert_project_type("native_app")
125
-
126
102
  cli_context = get_cli_context()
127
- manager = NativeAppManager(
128
- project_definition=cli_context.project_definition.native_app,
103
+ ws = WorkspaceManager(
104
+ project_definition=cli_context.project_definition,
129
105
  project_root=cli_context.project_root,
130
106
  )
131
- bundle_map = manager.build_bundle()
107
+ package_id = options["package_entity_id"]
108
+ package = cli_context.project_definition.entities[package_id]
109
+ bundle_map = ws.perform_action(
110
+ package_id,
111
+ EntityActions.BUNDLE,
112
+ )
113
+ stage_fqn = f"{package.fqn.name}.{package.stage}"
132
114
  diff: DiffResult = compute_stage_diff(
133
- local_root=Path(manager.deploy_root), stage_fqn=manager.stage_fqn
115
+ local_root=Path(package.deploy_root), stage_fqn=stage_fqn
134
116
  )
135
117
  if cli_context.output_format == OutputFormat.JSON:
136
118
  return ObjectResult(diff.to_dict())
@@ -141,7 +123,7 @@ def app_diff(
141
123
 
142
124
  @app.command("run", requires_connection=True)
143
125
  @with_project_definition()
144
- @nativeapp_definition_v2_to_v1(app_required=True)
126
+ @force_project_definition_v2(app_required=True)
145
127
  def app_run(
146
128
  version: Optional[str] = typer.Option(
147
129
  None,
@@ -170,42 +152,35 @@ def app_run(
170
152
  Creates an application package in your Snowflake account, uploads code files to its stage,
171
153
  then creates or upgrades an application object from the application package.
172
154
  """
173
-
174
- assert_project_type("native_app")
175
-
176
- is_interactive = False
177
- if force:
178
- policy = AllowAlwaysPolicy()
179
- elif interactive:
180
- is_interactive = True
181
- policy = AskAlwaysPolicy()
182
- else:
183
- policy = DenyAlwaysPolicy()
184
-
185
155
  cli_context = get_cli_context()
186
- processor = NativeAppRunProcessor(
187
- project_definition=cli_context.project_definition.native_app,
156
+ ws = WorkspaceManager(
157
+ project_definition=cli_context.project_definition,
188
158
  project_root=cli_context.project_root,
189
159
  )
190
- bundle_map = processor.build_bundle()
191
- processor.process(
192
- bundle_map=bundle_map,
193
- policy=policy,
160
+ app_id = options["app_entity_id"]
161
+ app_model = cli_context.project_definition.entities[app_id]
162
+ ws.perform_action(
163
+ app_id,
164
+ EntityActions.DEPLOY,
165
+ validate=validate,
194
166
  version=version,
195
167
  patch=patch,
196
168
  from_release_directive=from_release_directive,
197
- is_interactive=is_interactive,
198
- validate=validate,
169
+ prune=True,
170
+ recursive=True,
171
+ paths=[],
172
+ interactive=interactive,
173
+ force=force,
199
174
  )
175
+ app = ws.get_entity(app_id)
200
176
  return MessageResult(
201
- f"Your application object ({processor.app_name}) is now available:\n"
202
- + processor.get_snowsight_url()
177
+ f"Your application object ({app_model.fqn.name}) is now available:\n{app.get_snowsight_url()}"
203
178
  )
204
179
 
205
180
 
206
181
  @app.command("open", requires_connection=True)
207
182
  @with_project_definition()
208
- @nativeapp_definition_v2_to_v1(app_required=True)
183
+ @force_project_definition_v2(app_required=True)
209
184
  def app_open(
210
185
  **options,
211
186
  ) -> CommandResult:
@@ -213,16 +188,15 @@ def app_open(
213
188
  Opens the Snowflake Native App inside of your browser,
214
189
  once it has been installed in your account.
215
190
  """
216
-
217
- assert_project_type("native_app")
218
-
219
191
  cli_context = get_cli_context()
220
- manager = NativeAppManager(
221
- project_definition=cli_context.project_definition.native_app,
192
+ ws = WorkspaceManager(
193
+ project_definition=cli_context.project_definition,
222
194
  project_root=cli_context.project_root,
223
195
  )
224
- if manager.get_existing_app_info():
225
- typer.launch(manager.get_snowsight_url())
196
+ app_id = options["app_entity_id"]
197
+ app = ws.get_entity(app_id)
198
+ if app.get_existing_app_info():
199
+ typer.launch(app.get_snowsight_url())
226
200
  return MessageResult(f"Snowflake Native App opened in browser.")
227
201
  else:
228
202
  return MessageResult(
@@ -232,9 +206,7 @@ def app_open(
232
206
 
233
207
  @app.command("teardown", requires_connection=True)
234
208
  @with_project_definition()
235
- # This command doesn't use @nativeapp_definition_v2_to_v1 because it needs to
236
- # be aware of PDFv2 definitions that have multiple apps created from the same package,
237
- # which all need to be torn down.
209
+ @force_project_definition_v2(single_app_and_package=False)
238
210
  def app_teardown(
239
211
  force: Optional[bool] = ForceOption,
240
212
  cascade: Optional[bool] = typer.Option(
@@ -243,7 +215,7 @@ def app_teardown(
243
215
  show_default=False,
244
216
  ),
245
217
  interactive: bool = InteractiveOption,
246
- # Same as the param auto-added by @nativeapp_definition_v2_to_v1
218
+ # Same as the param auto-added by @force_project_definition_v2 if single_app_and_package were true
247
219
  package_entity_id: Optional[str] = typer.Option(
248
220
  default="",
249
221
  help="The ID of the package entity on which to operate when definition_version is 2 or higher.",
@@ -255,58 +227,49 @@ def app_teardown(
255
227
  """
256
228
  cli_context = get_cli_context()
257
229
  project = cli_context.project_definition
258
- if isinstance(project, ProjectDefinitionV1):
259
- # Old behaviour, not multi-app aware so we can use the old processor
260
- processor = NativeAppTeardownProcessor(
261
- project_definition=cli_context.project_definition.native_app,
262
- project_root=cli_context.project_root,
263
- )
264
- processor.process(interactive, force, cascade)
265
- else:
266
- # New behaviour, multi-app aware so teardown all the apps created from the package
267
-
268
- # Determine the package entity to drop, there must be one
269
- app_package_entity = find_entity(
270
- project,
271
- ApplicationPackageEntityModel,
272
- package_entity_id,
273
- disambiguation_option="--package-entity-id",
274
- required=True,
275
- )
276
- assert app_package_entity is not None # satisfy mypy
277
230
 
278
- # Same implementation as `snow ws drop`
279
- ws = WorkspaceManager(
280
- project_definition=cli_context.project_definition,
281
- project_root=cli_context.project_root,
282
- )
283
- for app_entity in project.get_entities_by_type(
284
- ApplicationEntityModel.get_type()
285
- ).values():
286
- # Drop each app
287
- if app_entity.from_.target == app_package_entity.entity_id:
288
- ws.perform_action(
289
- app_entity.entity_id,
290
- EntityActions.DROP,
291
- force_drop=force,
292
- interactive=interactive,
293
- cascade=cascade,
294
- )
295
- # Then drop the package
296
- ws.perform_action(
297
- app_package_entity.entity_id,
298
- EntityActions.DROP,
299
- force_drop=force,
300
- interactive=interactive,
301
- cascade=cascade,
302
- )
231
+ # Determine the package entity to drop, there must be one
232
+ app_package_entity = find_entity(
233
+ project,
234
+ ApplicationPackageEntityModel,
235
+ package_entity_id,
236
+ disambiguation_option="--package-entity-id",
237
+ required=True,
238
+ )
239
+ assert app_package_entity is not None # satisfy mypy
240
+
241
+ # Same implementation as `snow ws drop`
242
+ ws = WorkspaceManager(
243
+ project_definition=cli_context.project_definition,
244
+ project_root=cli_context.project_root,
245
+ )
246
+ for app_entity in project.get_entities_by_type(
247
+ ApplicationEntityModel.get_type()
248
+ ).values():
249
+ # Drop each app
250
+ if app_entity.from_.target == app_package_entity.entity_id:
251
+ ws.perform_action(
252
+ app_entity.entity_id,
253
+ EntityActions.DROP,
254
+ force_drop=force,
255
+ interactive=interactive,
256
+ cascade=cascade,
257
+ )
258
+ # Then drop the package
259
+ ws.perform_action(
260
+ app_package_entity.entity_id,
261
+ EntityActions.DROP,
262
+ force_drop=force,
263
+ interactive=interactive,
264
+ cascade=cascade,
265
+ )
303
266
 
304
267
  return MessageResult(f"Teardown is now complete.")
305
268
 
306
269
 
307
270
  @app.command("deploy", requires_connection=True)
308
271
  @with_project_definition()
309
- @nativeapp_definition_v2_to_v1()
272
+ @force_project_definition_v2()
310
273
  def app_deploy(
311
274
  prune: Optional[bool] = typer.Option(
312
275
  default=None,
@@ -338,16 +301,6 @@ def app_deploy(
338
301
  Creates an application package in your Snowflake account and syncs the local changes to the stage without creating or updating the application.
339
302
  Running this command with no arguments at all, as in `snow app deploy`, is a shorthand for `snow app deploy --prune --recursive`.
340
303
  """
341
-
342
- assert_project_type("native_app")
343
-
344
- if force:
345
- policy = AllowAlwaysPolicy()
346
- elif interactive:
347
- policy = AskAlwaysPolicy()
348
- else:
349
- policy = DenyAlwaysPolicy()
350
-
351
304
  has_paths = paths is not None and len(paths) > 0
352
305
  if prune is None and recursive is None and not has_paths:
353
306
  prune = True
@@ -357,24 +310,24 @@ def app_deploy(
357
310
  prune = False
358
311
  if recursive is None:
359
312
  recursive = False
360
-
361
313
  if has_paths and prune:
362
314
  raise IncompatibleParametersError(["paths", "--prune"])
363
315
 
364
316
  cli_context = get_cli_context()
365
- manager = NativeAppManager(
366
- project_definition=cli_context.project_definition.native_app,
317
+ ws = WorkspaceManager(
318
+ project_definition=cli_context.project_definition,
367
319
  project_root=cli_context.project_root,
368
320
  )
369
-
370
- bundle_map = manager.build_bundle()
371
- manager.deploy(
372
- bundle_map=bundle_map,
321
+ package_id = options["package_entity_id"]
322
+ ws.perform_action(
323
+ package_id,
324
+ EntityActions.DEPLOY,
373
325
  prune=prune,
374
326
  recursive=recursive,
375
- local_paths_to_sync=paths,
327
+ paths=paths,
376
328
  validate=validate,
377
- policy=policy,
329
+ interactive=interactive,
330
+ force=force,
378
331
  )
379
332
 
380
333
  return MessageResult(
@@ -384,25 +337,29 @@ def app_deploy(
384
337
 
385
338
  @app.command("validate", requires_connection=True)
386
339
  @with_project_definition()
387
- @nativeapp_definition_v2_to_v1()
340
+ @force_project_definition_v2()
388
341
  def app_validate(
389
342
  **options,
390
343
  ):
391
344
  """
392
345
  Validates a deployed Snowflake Native App's setup script.
393
346
  """
394
-
395
- assert_project_type("native_app")
396
-
397
347
  cli_context = get_cli_context()
398
- manager = NativeAppManager(
399
- project_definition=cli_context.project_definition.native_app,
348
+ ws = WorkspaceManager(
349
+ project_definition=cli_context.project_definition,
400
350
  project_root=cli_context.project_root,
401
351
  )
352
+ package_id = options["package_entity_id"]
353
+ package = ws.get_entity(package_id)
402
354
  if cli_context.output_format == OutputFormat.JSON:
403
- return ObjectResult(manager.get_validation_result(use_scratch_stage=True))
355
+ return ObjectResult(package.get_validation_result(use_scratch_stage=True))
404
356
 
405
- manager.validate(use_scratch_stage=True)
357
+ ws.perform_action(
358
+ package_id,
359
+ EntityActions.VALIDATE,
360
+ interactive=False,
361
+ force=True,
362
+ )
406
363
  return MessageResult("Snowflake Native App validation succeeded.")
407
364
 
408
365
 
@@ -419,7 +376,7 @@ DEFAULT_EVENT_FOLLOW_LAST = 20
419
376
 
420
377
  @app.command("events", requires_connection=True)
421
378
  @with_project_definition()
422
- @nativeapp_definition_v2_to_v1(app_required=True)
379
+ @force_project_definition_v2(app_required=True)
423
380
  def app_events(
424
381
  since: str = typer.Option(
425
382
  default="",
@@ -499,48 +456,40 @@ def app_events(
499
456
  if first >= 0:
500
457
  raise IncompatibleParametersError(["--follow", "--first"])
501
458
 
502
- assert_project_type("native_app")
459
+ cli_context = get_cli_context()
460
+ ws = WorkspaceManager(
461
+ project_definition=cli_context.project_definition,
462
+ project_root=cli_context.project_root,
463
+ )
464
+ app_id = options["app_entity_id"]
503
465
 
504
466
  record_type_names = [r.name for r in record_types]
505
- manager = NativeAppManager(
506
- project_definition=get_cli_context().project_definition.native_app,
507
- project_root=get_cli_context().project_root,
467
+
468
+ if follow and last == -1 and not since:
469
+ # If we don't have a value for --last or --since, assume a value
470
+ # for --last so we at least print something before starting the stream
471
+ last = DEFAULT_EVENT_FOLLOW_LAST
472
+ stream: Iterable[CommandResult] = (
473
+ EventResult(event)
474
+ for event in ws.perform_action(
475
+ app_id,
476
+ EntityActions.EVENTS,
477
+ since=since,
478
+ until=until,
479
+ record_types=record_type_names,
480
+ scopes=scopes,
481
+ consumer_org=consumer_org,
482
+ consumer_account=consumer_account,
483
+ consumer_app_hash=consumer_app_hash,
484
+ first=first,
485
+ last=last,
486
+ follow=follow,
487
+ interval_seconds=follow_interval,
488
+ )
508
489
  )
509
490
  if follow:
510
- if last == -1 and not since:
511
- # If we don't have a value for --last or --since, assume a value
512
- # for --last so we at least print something before starting the stream
513
- last = DEFAULT_EVENT_FOLLOW_LAST
514
- stream: Iterable[CommandResult] = (
515
- EventResult(event)
516
- for event in manager.stream_events(
517
- since=since,
518
- last=last,
519
- interval_seconds=follow_interval,
520
- record_types=record_type_names,
521
- scopes=scopes,
522
- consumer_org=consumer_org,
523
- consumer_account=consumer_account,
524
- consumer_app_hash=consumer_app_hash,
525
- )
526
- )
527
491
  # Append a newline at the end to make the CLI output clean when we hit Ctrl-C
528
492
  stream = itertools.chain(stream, [MessageResult("")])
529
- else:
530
- stream = (
531
- EventResult(event)
532
- for event in manager.get_events(
533
- since=since,
534
- until=until,
535
- record_types=record_type_names,
536
- scopes=scopes,
537
- first=first,
538
- last=last,
539
- consumer_org=consumer_org,
540
- consumer_account=consumer_account,
541
- consumer_app_hash=consumer_app_hash,
542
- )
543
- )
544
493
 
545
494
  # Cast the stream to a Generator since that's what StreamResult wants
546
495
  return StreamResult(cast(Generator[CommandResult, None, None], stream))