snowflake-cli 3.3.0__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.
Files changed (112) hide show
  1. snowflake/cli/__about__.py +1 -1
  2. snowflake/cli/_app/__main__.py +2 -2
  3. snowflake/cli/_app/cli_app.py +220 -197
  4. snowflake/cli/_app/commands_registration/builtin_plugins.py +5 -1
  5. snowflake/cli/_app/commands_registration/command_plugins_loader.py +3 -1
  6. snowflake/cli/_app/commands_registration/commands_registration_with_callbacks.py +4 -30
  7. snowflake/cli/_app/printing.py +2 -2
  8. snowflake/cli/_plugins/connection/commands.py +2 -4
  9. snowflake/cli/_plugins/cortex/commands.py +2 -4
  10. snowflake/cli/_plugins/git/manager.py +1 -1
  11. snowflake/cli/_plugins/helpers/commands.py +3 -4
  12. snowflake/cli/_plugins/nativeapp/artifacts.py +6 -624
  13. snowflake/cli/_plugins/nativeapp/bundle_context.py +1 -1
  14. snowflake/cli/_plugins/nativeapp/codegen/artifact_processor.py +1 -1
  15. snowflake/cli/_plugins/nativeapp/codegen/compiler.py +1 -3
  16. snowflake/cli/_plugins/nativeapp/codegen/setup/native_app_setup_processor.py +2 -2
  17. snowflake/cli/_plugins/nativeapp/codegen/snowpark/python_processor.py +2 -2
  18. snowflake/cli/_plugins/nativeapp/codegen/templates/templates_processor.py +2 -2
  19. snowflake/cli/_plugins/nativeapp/commands.py +21 -19
  20. snowflake/cli/_plugins/nativeapp/entities/application.py +16 -19
  21. snowflake/cli/_plugins/nativeapp/entities/application_package.py +142 -55
  22. snowflake/cli/_plugins/nativeapp/release_channel/commands.py +37 -3
  23. snowflake/cli/_plugins/nativeapp/release_directive/commands.py +80 -2
  24. snowflake/cli/_plugins/nativeapp/sf_sql_facade.py +224 -44
  25. snowflake/cli/_plugins/nativeapp/v2_conversions/compat.py +2 -2
  26. snowflake/cli/_plugins/nativeapp/version/commands.py +1 -1
  27. snowflake/cli/_plugins/notebook/commands.py +54 -2
  28. snowflake/cli/_plugins/notebook/exceptions.py +1 -1
  29. snowflake/cli/_plugins/notebook/manager.py +3 -3
  30. snowflake/cli/_plugins/notebook/notebook_entity.py +120 -0
  31. snowflake/cli/_plugins/notebook/notebook_entity_model.py +42 -0
  32. snowflake/cli/_plugins/notebook/notebook_project_paths.py +15 -0
  33. snowflake/cli/_plugins/notebook/types.py +3 -0
  34. snowflake/cli/_plugins/plugin/commands.py +79 -0
  35. snowflake/cli/_plugins/plugin/manager.py +74 -0
  36. snowflake/cli/_plugins/plugin/plugin_spec.py +30 -0
  37. snowflake/cli/_plugins/project/__init__.py +0 -0
  38. snowflake/cli/_plugins/project/commands.py +157 -0
  39. snowflake/cli/_plugins/project/feature_flags.py +22 -0
  40. snowflake/cli/_plugins/project/manager.py +76 -0
  41. snowflake/cli/_plugins/project/plugin_spec.py +30 -0
  42. snowflake/cli/_plugins/project/project_entity_model.py +40 -0
  43. snowflake/cli/_plugins/snowpark/commands.py +49 -30
  44. snowflake/cli/_plugins/snowpark/common.py +47 -2
  45. snowflake/cli/_plugins/snowpark/snowpark_entity.py +38 -25
  46. snowflake/cli/_plugins/snowpark/snowpark_entity_model.py +18 -30
  47. snowflake/cli/_plugins/snowpark/snowpark_project_paths.py +156 -23
  48. snowflake/cli/_plugins/snowpark/zipper.py +33 -1
  49. snowflake/cli/_plugins/spcs/compute_pool/commands.py +53 -5
  50. snowflake/cli/_plugins/spcs/compute_pool/compute_pool_entity.py +8 -0
  51. snowflake/cli/_plugins/spcs/compute_pool/compute_pool_entity_model.py +37 -0
  52. snowflake/cli/_plugins/spcs/compute_pool/manager.py +45 -0
  53. snowflake/cli/_plugins/spcs/image_repository/commands.py +29 -0
  54. snowflake/cli/_plugins/spcs/image_repository/image_repository_entity.py +8 -0
  55. snowflake/cli/_plugins/spcs/image_repository/image_repository_entity_model.py +8 -0
  56. snowflake/cli/_plugins/spcs/image_repository/manager.py +1 -1
  57. snowflake/cli/_plugins/spcs/services/commands.py +51 -1
  58. snowflake/cli/_plugins/spcs/services/manager.py +114 -0
  59. snowflake/cli/_plugins/spcs/services/service_entity.py +6 -0
  60. snowflake/cli/_plugins/spcs/services/service_entity_model.py +45 -0
  61. snowflake/cli/_plugins/spcs/services/service_project_paths.py +15 -0
  62. snowflake/cli/_plugins/stage/commands.py +2 -1
  63. snowflake/cli/_plugins/stage/diff.py +60 -39
  64. snowflake/cli/_plugins/stage/manager.py +26 -13
  65. snowflake/cli/_plugins/stage/utils.py +1 -1
  66. snowflake/cli/_plugins/streamlit/commands.py +18 -24
  67. snowflake/cli/_plugins/streamlit/manager.py +37 -27
  68. snowflake/cli/_plugins/streamlit/streamlit_entity.py +20 -41
  69. snowflake/cli/_plugins/streamlit/streamlit_entity_model.py +14 -24
  70. snowflake/cli/_plugins/streamlit/streamlit_project_paths.py +30 -0
  71. snowflake/cli/_plugins/workspace/commands.py +3 -3
  72. snowflake/cli/_plugins/workspace/manager.py +1 -1
  73. snowflake/cli/api/artifacts/bundle_map.py +500 -0
  74. snowflake/cli/api/artifacts/common.py +78 -0
  75. snowflake/cli/api/artifacts/upload.py +51 -0
  76. snowflake/cli/api/artifacts/utils.py +82 -0
  77. snowflake/cli/api/cli_global_context.py +14 -1
  78. snowflake/cli/api/commands/flags.py +34 -13
  79. snowflake/cli/api/commands/snow_typer.py +12 -0
  80. snowflake/cli/api/commands/utils.py +30 -2
  81. snowflake/cli/api/config.py +15 -10
  82. snowflake/cli/api/constants.py +1 -0
  83. snowflake/cli/api/entities/common.py +14 -32
  84. snowflake/cli/api/entities/resolver.py +160 -0
  85. snowflake/cli/api/entities/utils.py +56 -15
  86. snowflake/cli/api/errno.py +3 -0
  87. snowflake/cli/api/exceptions.py +8 -1
  88. snowflake/cli/api/feature_flags.py +1 -1
  89. snowflake/cli/api/plugins/plugin_config.py +43 -4
  90. snowflake/cli/api/project/definition_conversion.py +3 -2
  91. snowflake/cli/api/project/definition_helper.py +31 -0
  92. snowflake/cli/api/project/project_paths.py +28 -0
  93. snowflake/cli/api/project/schemas/entities/common.py +130 -1
  94. snowflake/cli/api/project/schemas/entities/entities.py +30 -0
  95. snowflake/cli/api/project/schemas/project_definition.py +27 -0
  96. snowflake/cli/api/project/schemas/updatable_model.py +2 -2
  97. snowflake/cli/api/project/schemas/v1/native_app/native_app.py +5 -7
  98. snowflake/cli/api/secure_path.py +6 -0
  99. snowflake/cli/api/sql_execution.py +5 -1
  100. snowflake/cli/api/stage_path.py +7 -2
  101. snowflake/cli/api/utils/graph.py +3 -0
  102. snowflake/cli/api/utils/path_utils.py +24 -0
  103. {snowflake_cli-3.3.0.dist-info → snowflake_cli-3.5.0.dist-info}/METADATA +12 -13
  104. {snowflake_cli-3.3.0.dist-info → snowflake_cli-3.5.0.dist-info}/RECORD +109 -85
  105. snowflake/cli/_app/api_impl/plugin/plugin_config_provider_impl.py +0 -66
  106. snowflake/cli/api/__init__.py +0 -48
  107. snowflake/cli/api/project/schemas/v1/native_app/path_mapping.py +0 -65
  108. /snowflake/cli/{_app/api_impl → _plugins/plugin}/__init__.py +0 -0
  109. /snowflake/cli/{_app/api_impl/plugin → api/artifacts}/__init__.py +0 -0
  110. {snowflake_cli-3.3.0.dist-info → snowflake_cli-3.5.0.dist-info}/WHEEL +0 -0
  111. {snowflake_cli-3.3.0.dist-info → snowflake_cli-3.5.0.dist-info}/entry_points.txt +0 -0
  112. {snowflake_cli-3.3.0.dist-info → snowflake_cli-3.5.0.dist-info}/licenses/LICENSE +0 -0
@@ -39,7 +39,9 @@ from snowflake.cli._plugins.snowpark.common import (
39
39
  SnowparkEntities,
40
40
  SnowparkObject,
41
41
  SnowparkObjectManager,
42
- StageToArtefactMapping,
42
+ StageToArtifactMapping,
43
+ map_path_mapping_to_artifact,
44
+ zip_and_copy_artifacts_to_deploy,
43
45
  )
44
46
  from snowflake.cli._plugins.snowpark.package.anaconda_packages import (
45
47
  AnacondaPackages,
@@ -68,6 +70,7 @@ from snowflake.cli.api.commands.decorators import (
68
70
  with_project_definition,
69
71
  )
70
72
  from snowflake.cli.api.commands.flags import (
73
+ ForceReplaceOption,
71
74
  ReplaceOption,
72
75
  execution_identifier_argument,
73
76
  identifier_argument,
@@ -81,6 +84,7 @@ from snowflake.cli.api.constants import (
81
84
  from snowflake.cli.api.exceptions import (
82
85
  SecretsWithoutExternalAccessIntegrationError,
83
86
  )
87
+ from snowflake.cli.api.feature_flags import FeatureFlag
84
88
  from snowflake.cli.api.identifiers import FQN
85
89
  from snowflake.cli.api.output.types import (
86
90
  CollectionResult,
@@ -125,8 +129,10 @@ LikeOption = like_option(
125
129
  @with_project_definition()
126
130
  def deploy(
127
131
  replace: bool = ReplaceOption(
128
- help="Replaces procedure or function, even if no detected changes to metadata"
132
+ help="Replaces procedure or function if there were changes in the definition. It only uploads new and "
133
+ "overwrites existing files, but does not remove any files already on the stage."
129
134
  ),
135
+ force_replace: bool = ForceReplaceOption(),
130
136
  **options,
131
137
  ) -> CommandResult:
132
138
  """
@@ -156,7 +162,11 @@ def deploy(
156
162
  with cli_console.phase("Checking remote state"):
157
163
  om = ObjectManager()
158
164
  _check_if_all_defined_integrations_exists(om, snowpark_entities)
159
- existing_objects = check_for_existing_objects(om, replace, snowpark_entities)
165
+ existing_objects = (
166
+ {}
167
+ if force_replace
168
+ else check_for_existing_objects(om, replace, snowpark_entities)
169
+ )
160
170
 
161
171
  with cli_console.phase("Preparing required stages and artifacts"):
162
172
  entities_to_imports_map, stages_to_artifact_map = build_artifacts_mappings(
@@ -189,11 +199,11 @@ def validate_all_artifacts_exists(
189
199
  project_paths: SnowparkProjectPaths, snowpark_entities: SnowparkEntities
190
200
  ):
191
201
  for key, entity in snowpark_entities.items():
192
- for artefact in entity.artifacts:
193
- path = project_paths.get_artefact_dto(artefact).post_build_path
202
+ for artifact in entity.artifacts:
203
+ path = project_paths.get_artifact_dto(artifact).post_build_path
194
204
  if not path.exists():
195
205
  raise UsageError(
196
- f"Artefact {path} required for {entity.type} {key} does not exist."
206
+ f"Artifact {path} required for {entity.type} {key} does not exist."
197
207
  )
198
208
 
199
209
 
@@ -213,38 +223,39 @@ def check_for_existing_objects(
213
223
 
214
224
  def build_artifacts_mappings(
215
225
  project_paths: SnowparkProjectPaths, snowpark_entities: SnowparkEntities
216
- ) -> Tuple[EntityToImportPathsMapping, StageToArtefactMapping]:
217
- stages_to_artifact_map: StageToArtefactMapping = defaultdict(set)
226
+ ) -> Tuple[EntityToImportPathsMapping, StageToArtifactMapping]:
227
+ stages_to_artifact_map: StageToArtifactMapping = defaultdict(set)
218
228
  entities_to_imports_map: EntityToImportPathsMapping = defaultdict(set)
219
- for entity_id, entity in snowpark_entities.items():
229
+ for name, entity in snowpark_entities.items():
220
230
  stage = entity.stage
221
231
  required_artifacts = set()
222
- for artefact in entity.artifacts:
223
- artefact_dto = project_paths.get_artefact_dto(artefact)
224
- required_artifacts.add(artefact_dto)
225
- entities_to_imports_map[entity_id].add(artefact_dto.import_path(stage))
232
+ for artifact in entity.artifacts:
233
+ artifact_dto = project_paths.get_artifact_dto(artifact)
234
+ required_artifacts.add(artifact_dto)
235
+ entities_to_imports_map[name].add(artifact_dto.import_path(stage))
226
236
  stages_to_artifact_map[stage].update(required_artifacts)
227
237
 
228
- if project_paths.dependencies.exists():
229
- deps_artefact = project_paths.get_dependencies_artefact()
230
- stages_to_artifact_map[stage].add(deps_artefact)
231
- entities_to_imports_map[entity_id].add(deps_artefact.import_path(stage))
238
+ deps_artifact = project_paths.get_dependencies_artifact()
239
+ if deps_artifact.post_build_path.exists():
240
+ stages_to_artifact_map[stage].add(deps_artifact)
241
+ entities_to_imports_map[name].add(deps_artifact.import_path(stage))
232
242
  return entities_to_imports_map, stages_to_artifact_map
233
243
 
234
244
 
235
- def create_stages_and_upload_artifacts(stages_to_artifact_map: StageToArtefactMapping):
245
+ def create_stages_and_upload_artifacts(stages_to_artifact_map: StageToArtifactMapping):
236
246
  stage_manager = StageManager()
237
247
  for stage, artifacts in stages_to_artifact_map.items():
238
248
  cli_console.step(f"Creating (if not exists) stage: {stage}")
239
249
  stage = FQN.from_stage(stage).using_context()
240
250
  stage_manager.create(fqn=stage, comment="deployments managed by Snowflake CLI")
241
- for artefact in artifacts:
251
+ for artifact in artifacts:
252
+ post_build_path = artifact.post_build_path
242
253
  cli_console.step(
243
- f"Uploading {artefact.post_build_path.name} to {artefact.upload_path(stage)}"
254
+ f"Uploading {post_build_path.name} to {artifact.upload_path(stage)}"
244
255
  )
245
256
  stage_manager.put(
246
- local_path=artefact.post_build_path,
247
- stage_path=artefact.upload_path(stage),
257
+ local_path=post_build_path,
258
+ stage_path=artifact.upload_path(stage),
248
259
  overwrite=True,
249
260
  )
250
261
 
@@ -324,6 +335,9 @@ def build(
324
335
 
325
336
  anaconda_packages_manager = AnacondaPackagesManager()
326
337
 
338
+ # Clean up bundle root
339
+ project_paths.remove_up_bundle_root()
340
+
327
341
  # Resolve dependencies
328
342
  if project_paths.requirements.exists():
329
343
  with (
@@ -362,22 +376,27 @@ def build(
362
376
  )
363
377
 
364
378
  if any(temp_deps_dir.path.iterdir()):
365
- cli_console.step(f"Creating {project_paths.dependencies.name}")
379
+ dep_artifact = project_paths.get_dependencies_artifact()
380
+ cli_console.step(f"Creating {dep_artifact.path.name}")
366
381
  zip_dir(
367
382
  source=temp_deps_dir.path,
368
- dest_zip=project_paths.dependencies,
383
+ dest_zip=dep_artifact.post_build_path,
369
384
  )
370
385
  else:
371
386
  cli_console.step(f"No external dependencies.")
372
387
 
373
388
  artifacts = set()
374
- for entity in get_snowpark_entities(pd).values():
375
- artifacts.update(entity.artifacts)
376
-
377
389
  with cli_console.phase("Preparing artifacts for source code"):
378
- for artefact in artifacts:
379
- artefact_dto = project_paths.get_artefact_dto(artefact)
380
- artefact_dto.build()
390
+ for entity in get_snowpark_entities(pd).values():
391
+ artifacts.update(
392
+ map_path_mapping_to_artifact(project_paths, entity.artifacts)
393
+ )
394
+
395
+ if FeatureFlag.ENABLE_SNOWPARK_GLOB_SUPPORT.is_enabled():
396
+ zip_and_copy_artifacts_to_deploy(artifacts, project_paths.bundle_root)
397
+ else:
398
+ for artifact in artifacts:
399
+ artifact.build()
381
400
 
382
401
  return MessageResult(f"Build done.")
383
402
 
@@ -17,6 +17,7 @@ from __future__ import annotations
17
17
  import logging
18
18
  import re
19
19
  from enum import Enum
20
+ from pathlib import Path
20
21
  from typing import Dict, List, Set
21
22
 
22
23
  from click import UsageError
@@ -25,7 +26,13 @@ from snowflake.cli._plugins.snowpark.snowpark_entity_model import (
25
26
  ProcedureEntityModel,
26
27
  SnowparkEntityModel,
27
28
  )
28
- from snowflake.cli._plugins.snowpark.snowpark_project_paths import Artefact
29
+ from snowflake.cli._plugins.snowpark.snowpark_project_paths import (
30
+ Artifact,
31
+ SnowparkProjectPaths,
32
+ )
33
+ from snowflake.cli._plugins.snowpark.zipper import zip_dir_using_bundle_map
34
+ from snowflake.cli.api.artifacts.bundle_map import BundleMap
35
+ from snowflake.cli.api.artifacts.utils import symlink_or_copy
29
36
  from snowflake.cli.api.console import cli_console
30
37
  from snowflake.cli.api.constants import (
31
38
  INIT_TEMPLATE_VARIABLE_CLOSING,
@@ -34,13 +41,14 @@ from snowflake.cli.api.constants import (
34
41
  PROJECT_TEMPLATE_VARIABLE_OPENING,
35
42
  ObjectType,
36
43
  )
44
+ from snowflake.cli.api.project.schemas.entities.common import PathMapping
37
45
  from snowflake.cli.api.sql_execution import SqlExecutionMixin
38
46
  from snowflake.connector.cursor import SnowflakeCursor
39
47
 
40
48
  log = logging.getLogger(__name__)
41
49
 
42
50
  SnowparkEntities = Dict[str, SnowparkEntityModel]
43
- StageToArtefactMapping = Dict[str, set[Artefact]]
51
+ StageToArtifactMapping = Dict[str, set[Artifact]]
44
52
  EntityToImportPathsMapping = Dict[str, set[str]]
45
53
 
46
54
  DEFAULT_RUNTIME = "3.10"
@@ -214,6 +222,43 @@ def _snowflake_dependencies_differ(
214
222
  return _standardize(old_dependencies) != _standardize(new_dependencies)
215
223
 
216
224
 
225
+ def map_path_mapping_to_artifact(
226
+ project_paths: SnowparkProjectPaths, artifacts: List[PathMapping]
227
+ ) -> List[Artifact]:
228
+ return [project_paths.get_artifact_dto(artifact) for artifact in artifacts]
229
+
230
+
231
+ def zip_and_copy_artifacts_to_deploy(
232
+ artifacts: Set[Artifact] | List[Artifact], bundle_root: Path
233
+ ) -> List[Path]:
234
+ copied_files = []
235
+ for artifact in artifacts:
236
+ bundle_map = BundleMap(
237
+ project_root=artifact.project_root,
238
+ deploy_root=bundle_root,
239
+ )
240
+ bundle_map.add(PathMapping(src=str(artifact.path), dest=artifact.dest))
241
+
242
+ if artifact.path.is_file():
243
+ for (absolute_src, absolute_dest) in bundle_map.all_mappings(
244
+ absolute=True, expand_directories=False
245
+ ):
246
+ symlink_or_copy(
247
+ absolute_src,
248
+ absolute_dest,
249
+ deploy_root=bundle_map.deploy_root(),
250
+ )
251
+ copied_files.append(absolute_dest)
252
+ else:
253
+ post_build_path = artifact.post_build_path
254
+ zip_dir_using_bundle_map(
255
+ bundle_map=bundle_map,
256
+ dest_zip=post_build_path,
257
+ )
258
+ copied_files.append(post_build_path)
259
+ return copied_files
260
+
261
+
217
262
  def same_type(sf_type: str, local_type: str) -> bool:
218
263
  sf_type, local_type = sf_type.upper(), local_type.upper()
219
264
 
@@ -5,7 +5,11 @@ from typing import Generic, List, Optional, TypeVar
5
5
  from click import ClickException
6
6
  from snowflake.cli._plugins.nativeapp.feature_flags import FeatureFlag
7
7
  from snowflake.cli._plugins.snowpark import package_utils
8
- from snowflake.cli._plugins.snowpark.common import DEFAULT_RUNTIME
8
+ from snowflake.cli._plugins.snowpark.common import (
9
+ DEFAULT_RUNTIME,
10
+ map_path_mapping_to_artifact,
11
+ zip_and_copy_artifacts_to_deploy,
12
+ )
9
13
  from snowflake.cli._plugins.snowpark.package.anaconda_packages import (
10
14
  AnacondaPackages,
11
15
  AnacondaPackagesManager,
@@ -17,6 +21,7 @@ from snowflake.cli._plugins.snowpark.snowpark_entity_model import (
17
21
  FunctionEntityModel,
18
22
  ProcedureEntityModel,
19
23
  )
24
+ from snowflake.cli._plugins.snowpark.snowpark_project_paths import SnowparkProjectPaths
20
25
  from snowflake.cli._plugins.snowpark.zipper import zip_dir
21
26
  from snowflake.cli._plugins.workspace.context import ActionContext
22
27
  from snowflake.cli.api.entities.common import EntityBase
@@ -44,27 +49,31 @@ class SnowparkEntity(EntityBase[Generic[T]]):
44
49
  def action_bundle(
45
50
  self,
46
51
  action_ctx: ActionContext,
47
- output_dir: Path | None,
48
52
  ignore_anaconda: bool,
49
53
  skip_version_check: bool,
54
+ output_dir: Path | None = None,
50
55
  index_url: str | None = None,
51
56
  allow_shared_libraries: bool = False,
52
57
  *args,
53
58
  **kwargs,
54
59
  ) -> List[Path]:
55
60
  return self.bundle(
56
- output_dir,
57
61
  ignore_anaconda,
58
62
  skip_version_check,
63
+ output_dir,
59
64
  index_url,
60
65
  allow_shared_libraries,
61
66
  )
62
67
 
63
68
  def action_deploy(
64
- self, action_ctx: ActionContext, mode: CreateMode, *args, **kwargs
69
+ self,
70
+ action_ctx: ActionContext,
71
+ mode: CreateMode = CreateMode.create,
72
+ *args,
73
+ **kwargs,
65
74
  ):
66
75
  # TODO: After introducing bundle map, we should introduce file copying part here
67
- return self._execute_query(self.get_deploy_sql(mode))
76
+ return self.deploy(mode, *args, **kwargs)
68
77
 
69
78
  def action_drop(self, action_ctx: ActionContext, *args, **kwargs):
70
79
  return self._execute_query(self.get_drop_sql())
@@ -83,9 +92,9 @@ class SnowparkEntity(EntityBase[Generic[T]]):
83
92
 
84
93
  def bundle(
85
94
  self,
86
- output_dir: Path | None,
87
95
  ignore_anaconda: bool,
88
96
  skip_version_check: bool,
97
+ output_dir: Path | None = None,
89
98
  index_url: str | None = None,
90
99
  allow_shared_libraries: bool = False,
91
100
  ) -> List[Path]:
@@ -93,18 +102,20 @@ class SnowparkEntity(EntityBase[Generic[T]]):
93
102
  Bundles the entity artifacts and dependencies into a directory.
94
103
  Parameters:
95
104
  output_dir: The directory to output the bundled artifacts to. Defaults to output dir in project root
96
- ignore_anaconda: If True, ignores anaconda chceck and tries to download all packages using pip
105
+ ignore_anaconda: If True, ignores anaconda check and tries to download all packages using pip
97
106
  skip_version_check: If True, skips version check when downloading packages
98
107
  index_url: The index URL to use when downloading packages, if none set - default pip index is used (in most cases- Pypi)
99
108
  allow_shared_libraries: If not set to True, using dependency with .so/.dll files will raise an exception
100
109
  Returns:
101
110
  """
102
111
  # 0 Create a directory for the entity
112
+ project_paths = SnowparkProjectPaths(
113
+ project_root=self.root.absolute(),
114
+ )
103
115
  if not output_dir:
104
- output_dir = self.root / "output" / "bundle" / "snowpark"
105
- output_dir.mkdir(parents=True, exist_ok=True) # type: ignore
106
-
107
- output_files = []
116
+ output_dir = project_paths.bundle_root
117
+ if not output_dir.exists(): # type: ignore[union-attr]
118
+ SecurePath(output_dir).mkdir(parents=True)
108
119
 
109
120
  # 1 Check if requirements exits
110
121
  if (self.root / "requirements.txt").exists():
@@ -118,21 +129,20 @@ class SnowparkEntity(EntityBase[Generic[T]]):
118
129
  allow_shared_libraries=allow_shared_libraries,
119
130
  )
120
131
 
121
- # 3 get the artifacts list
122
- artifacts = self.model.artifacts
123
-
124
- for artifact in artifacts:
125
- output_file = output_dir / artifact.dest / artifact.src.name
126
-
127
- if artifact.src.is_file():
128
- output_file.mkdir(parents=True, exist_ok=True)
129
- SecurePath(artifact.src).copy(output_file)
130
- elif artifact.is_dir():
131
- output_file.mkdir(parents=True, exist_ok=True)
132
+ # 2 get the artifacts list
133
+ artifacts = map_path_mapping_to_artifact(project_paths, self.model.artifacts)
134
+ from snowflake.cli.api.feature_flags import FeatureFlag
132
135
 
133
- output_files.append(output_file)
134
-
135
- return output_files
136
+ if FeatureFlag.ENABLE_SNOWPARK_GLOB_SUPPORT.is_enabled():
137
+ return zip_and_copy_artifacts_to_deploy(
138
+ artifacts, project_paths.bundle_root
139
+ )
140
+ else:
141
+ copied_files = []
142
+ for artifact in artifacts:
143
+ artifact.build()
144
+ copied_files.append(artifact.post_build_path)
145
+ return copied_files
136
146
 
137
147
  def check_if_exists(
138
148
  self, action_ctx: ActionContext
@@ -143,6 +153,9 @@ class SnowparkEntity(EntityBase[Generic[T]]):
143
153
  except ProgrammingError:
144
154
  return False
145
155
 
156
+ def deploy(self, mode: CreateMode = CreateMode.create, *args, **kwargs):
157
+ return self._execute_query(self.get_deploy_sql(mode))
158
+
146
159
  def get_deploy_sql(self, mode: CreateMode):
147
160
  query = [
148
161
  f"{mode.value} {self.model.type.upper()} {self.identifier}",
@@ -14,37 +14,27 @@
14
14
 
15
15
  from __future__ import annotations
16
16
 
17
- from pathlib import Path
17
+ import glob
18
18
  from typing import List, Literal, Optional, Union
19
19
 
20
20
  from pydantic import Field, field_validator
21
+ from snowflake.cli.api.feature_flags import FeatureFlag
21
22
  from snowflake.cli.api.identifiers import FQN
22
23
  from snowflake.cli.api.project.schemas.entities.common import (
23
- EntityModelBase,
24
+ EntityModelBaseWithArtifacts,
24
25
  ExternalAccessBaseModel,
25
26
  ImportsBaseModel,
27
+ PathMapping,
26
28
  )
27
29
  from snowflake.cli.api.project.schemas.updatable_model import (
28
30
  DiscriminatorField,
29
- UpdatableModel,
30
31
  )
31
32
  from snowflake.cli.api.project.schemas.v1.snowpark.argument import Argument
32
33
 
33
34
 
34
- class PathMapping(UpdatableModel):
35
- class Config:
36
- frozen = True
37
-
38
- src: Path = Field(title="Source path (relative to project root)", default=None)
39
-
40
- dest: Optional[str] = Field(
41
- title="Destination path on stage",
42
- description="Paths are relative to stage root; paths ending with a slash indicate that the destination is a directory which source files should be copied into.",
43
- default=None,
44
- )
45
-
46
-
47
- class SnowparkEntityModel(EntityModelBase, ExternalAccessBaseModel, ImportsBaseModel):
35
+ class SnowparkEntityModel(
36
+ EntityModelBaseWithArtifacts, ExternalAccessBaseModel, ImportsBaseModel
37
+ ):
48
38
  handler: str = Field(
49
39
  title="Function’s or procedure’s implementation of the object inside source module",
50
40
  examples=["functions.hello_function"],
@@ -59,17 +49,23 @@ class SnowparkEntityModel(EntityModelBase, ExternalAccessBaseModel, ImportsBaseM
59
49
  title="Python version to use when executing ", default=None
60
50
  )
61
51
  stage: str = Field(title="Stage in which artifacts will be stored")
62
- artifacts: List[Union[PathMapping, str]] = Field(title="List of required sources")
63
52
 
64
53
  @field_validator("artifacts")
65
54
  @classmethod
66
55
  def _convert_artifacts(cls, artifacts: Union[dict, str]):
67
56
  _artifacts = []
68
- for artefact in artifacts:
69
- if isinstance(artefact, PathMapping):
70
- _artifacts.append(artefact)
57
+ for artifact in artifacts:
58
+ if (
59
+ (isinstance(artifact, str) and glob.has_magic(artifact))
60
+ or (isinstance(artifact, PathMapping) and glob.has_magic(artifact.src))
61
+ ) and FeatureFlag.ENABLE_SNOWPARK_GLOB_SUPPORT.is_disabled():
62
+ raise ValueError(
63
+ "If you want to use glob patterns in artifacts, you need to enable the Snowpark new build feature flag (enable_snowpark_glob_support=true)"
64
+ )
65
+ if isinstance(artifact, PathMapping):
66
+ _artifacts.append(artifact)
71
67
  else:
72
- _artifacts.append(PathMapping(src=artefact))
68
+ _artifacts.append(PathMapping(src=artifact))
73
69
  return _artifacts
74
70
 
75
71
  @field_validator("runtime")
@@ -79,14 +75,6 @@ class SnowparkEntityModel(EntityModelBase, ExternalAccessBaseModel, ImportsBaseM
79
75
  return str(runtime_input)
80
76
  return runtime_input
81
77
 
82
- @field_validator("artifacts")
83
- @classmethod
84
- def validate_artifacts(cls, artifacts: List[Path]) -> List[Path]:
85
- for artefact in artifacts:
86
- if "*" in str(artefact):
87
- raise ValueError("Glob patterns not supported for Snowpark artifacts.")
88
- return artifacts
89
-
90
78
  @property
91
79
  def udf_sproc_identifier(self) -> UdfSprocIdentifier:
92
80
  return UdfSprocIdentifier.from_definition(self)