snowflake-cli 3.3.0__py3-none-any.whl → 3.4.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/_app/__main__.py +2 -2
- snowflake/cli/_app/cli_app.py +224 -192
- snowflake/cli/_app/commands_registration/commands_registration_with_callbacks.py +1 -27
- snowflake/cli/_plugins/cortex/commands.py +2 -4
- snowflake/cli/_plugins/git/manager.py +1 -1
- snowflake/cli/_plugins/nativeapp/artifacts.py +6 -624
- snowflake/cli/_plugins/nativeapp/bundle_context.py +1 -1
- snowflake/cli/_plugins/nativeapp/codegen/artifact_processor.py +1 -1
- snowflake/cli/_plugins/nativeapp/codegen/compiler.py +1 -3
- snowflake/cli/_plugins/nativeapp/codegen/setup/native_app_setup_processor.py +2 -2
- snowflake/cli/_plugins/nativeapp/codegen/snowpark/python_processor.py +2 -2
- snowflake/cli/_plugins/nativeapp/codegen/templates/templates_processor.py +2 -2
- snowflake/cli/_plugins/nativeapp/commands.py +21 -19
- snowflake/cli/_plugins/nativeapp/entities/application.py +16 -19
- snowflake/cli/_plugins/nativeapp/entities/application_package.py +142 -55
- snowflake/cli/_plugins/nativeapp/release_channel/commands.py +37 -3
- snowflake/cli/_plugins/nativeapp/release_directive/commands.py +80 -2
- snowflake/cli/_plugins/nativeapp/sf_sql_facade.py +224 -44
- snowflake/cli/_plugins/nativeapp/v2_conversions/compat.py +2 -2
- snowflake/cli/_plugins/nativeapp/version/commands.py +1 -1
- snowflake/cli/_plugins/notebook/commands.py +55 -2
- snowflake/cli/_plugins/notebook/exceptions.py +1 -1
- snowflake/cli/_plugins/notebook/manager.py +3 -3
- snowflake/cli/_plugins/notebook/notebook_entity.py +120 -0
- snowflake/cli/_plugins/notebook/notebook_entity_model.py +42 -0
- snowflake/cli/_plugins/notebook/notebook_project_paths.py +15 -0
- snowflake/cli/_plugins/notebook/types.py +3 -0
- snowflake/cli/_plugins/snowpark/commands.py +48 -30
- snowflake/cli/_plugins/snowpark/common.py +47 -2
- snowflake/cli/_plugins/snowpark/snowpark_entity.py +38 -25
- snowflake/cli/_plugins/snowpark/snowpark_entity_model.py +18 -30
- snowflake/cli/_plugins/snowpark/snowpark_project_paths.py +156 -23
- snowflake/cli/_plugins/snowpark/zipper.py +33 -1
- snowflake/cli/_plugins/spcs/services/commands.py +0 -3
- snowflake/cli/_plugins/stage/commands.py +2 -1
- snowflake/cli/_plugins/stage/diff.py +60 -39
- snowflake/cli/_plugins/stage/manager.py +24 -11
- snowflake/cli/_plugins/stage/utils.py +1 -1
- snowflake/cli/_plugins/streamlit/commands.py +10 -1
- snowflake/cli/_plugins/streamlit/manager.py +62 -21
- snowflake/cli/_plugins/streamlit/streamlit_entity.py +20 -41
- snowflake/cli/_plugins/streamlit/streamlit_entity_model.py +14 -24
- snowflake/cli/_plugins/streamlit/streamlit_project_paths.py +30 -0
- snowflake/cli/_plugins/workspace/commands.py +3 -3
- snowflake/cli/_plugins/workspace/manager.py +1 -1
- snowflake/cli/api/artifacts/__init__.py +13 -0
- snowflake/cli/api/artifacts/bundle_map.py +500 -0
- snowflake/cli/api/artifacts/common.py +78 -0
- snowflake/cli/api/artifacts/utils.py +82 -0
- snowflake/cli/api/cli_global_context.py +14 -1
- snowflake/cli/api/commands/flags.py +10 -4
- snowflake/cli/api/commands/utils.py +28 -2
- snowflake/cli/api/constants.py +1 -0
- snowflake/cli/api/entities/common.py +14 -32
- snowflake/cli/api/entities/resolver.py +160 -0
- snowflake/cli/api/entities/utils.py +56 -15
- snowflake/cli/api/errno.py +3 -0
- snowflake/cli/api/feature_flags.py +1 -2
- snowflake/cli/api/project/definition_conversion.py +3 -2
- snowflake/cli/api/project/project_paths.py +28 -0
- snowflake/cli/api/project/schemas/entities/common.py +130 -1
- snowflake/cli/api/project/schemas/entities/entities.py +4 -0
- snowflake/cli/api/project/schemas/project_definition.py +27 -0
- snowflake/cli/api/project/schemas/updatable_model.py +2 -2
- snowflake/cli/api/project/schemas/v1/native_app/native_app.py +5 -7
- snowflake/cli/api/secure_path.py +6 -0
- snowflake/cli/api/sql_execution.py +5 -1
- snowflake/cli/api/stage_path.py +7 -2
- snowflake/cli/api/utils/graph.py +3 -0
- snowflake/cli/api/utils/path_utils.py +24 -0
- {snowflake_cli-3.3.0.dist-info → snowflake_cli-3.4.1.dist-info}/METADATA +8 -9
- {snowflake_cli-3.3.0.dist-info → snowflake_cli-3.4.1.dist-info}/RECORD +76 -67
- snowflake/cli/api/project/schemas/v1/native_app/path_mapping.py +0 -65
- {snowflake_cli-3.3.0.dist-info → snowflake_cli-3.4.1.dist-info}/WHEEL +0 -0
- {snowflake_cli-3.3.0.dist-info → snowflake_cli-3.4.1.dist-info}/entry_points.txt +0 -0
- {snowflake_cli-3.3.0.dist-info → snowflake_cli-3.4.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Literal, Optional
|
|
5
|
+
|
|
6
|
+
from pydantic import Field, model_validator
|
|
7
|
+
from snowflake.cli._plugins.notebook.exceptions import NotebookFilePathError
|
|
8
|
+
from snowflake.cli.api.project.schemas.entities.common import (
|
|
9
|
+
EntityModelBaseWithArtifacts,
|
|
10
|
+
)
|
|
11
|
+
from snowflake.cli.api.project.schemas.updatable_model import (
|
|
12
|
+
DiscriminatorField,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class NotebookEntityModel(EntityModelBaseWithArtifacts):
|
|
17
|
+
type: Literal["notebook"] = DiscriminatorField() # noqa: A003
|
|
18
|
+
stage_path: Optional[str] = Field(
|
|
19
|
+
title="Stage directory in which the notebook file will be stored", default=None
|
|
20
|
+
)
|
|
21
|
+
notebook_file: Path = Field(title="Notebook file")
|
|
22
|
+
query_warehouse: str = Field(title="Snowflake warehouse to execute the notebook")
|
|
23
|
+
compute_pool: Optional[str] = Field(
|
|
24
|
+
title="Compute pool to run the notebook in", default=None
|
|
25
|
+
)
|
|
26
|
+
runtime_name: Optional[str] = Field(title="Container Runtime for ML", default=None)
|
|
27
|
+
|
|
28
|
+
@model_validator(mode="after")
|
|
29
|
+
def validate_notebook_file(self):
|
|
30
|
+
if not self.notebook_file.exists():
|
|
31
|
+
raise ValueError(f"Notebook file {self.notebook_file} does not exist")
|
|
32
|
+
if self.notebook_file.suffix.lower() != ".ipynb":
|
|
33
|
+
raise NotebookFilePathError(str(self.notebook_file))
|
|
34
|
+
return self
|
|
35
|
+
|
|
36
|
+
@model_validator(mode="after")
|
|
37
|
+
def validate_container_setup(self):
|
|
38
|
+
if self.compute_pool and not self.runtime_name:
|
|
39
|
+
raise ValueError("compute_pool is specified without runtime_name")
|
|
40
|
+
if self.runtime_name and not self.compute_pool and not self:
|
|
41
|
+
raise ValueError("runtime_name is specified without compute_pool")
|
|
42
|
+
return self
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from snowflake.cli.api.project.project_paths import ProjectPaths, bundle_root
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class NotebookProjectPaths(ProjectPaths):
|
|
9
|
+
"""
|
|
10
|
+
This class allows you to manage files paths related to given project.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
@property
|
|
14
|
+
def bundle_root(self) -> Path:
|
|
15
|
+
return bundle_root(self.project_root, "notebook")
|
|
@@ -39,7 +39,9 @@ from snowflake.cli._plugins.snowpark.common import (
|
|
|
39
39
|
SnowparkEntities,
|
|
40
40
|
SnowparkObject,
|
|
41
41
|
SnowparkObjectManager,
|
|
42
|
-
|
|
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,9 @@ LikeOption = like_option(
|
|
|
125
129
|
@with_project_definition()
|
|
126
130
|
def deploy(
|
|
127
131
|
replace: bool = ReplaceOption(
|
|
128
|
-
help="Replaces procedure or function
|
|
132
|
+
help="Replaces procedure or function if there were changes in the definition."
|
|
129
133
|
),
|
|
134
|
+
force_replace: bool = ForceReplaceOption(),
|
|
130
135
|
**options,
|
|
131
136
|
) -> CommandResult:
|
|
132
137
|
"""
|
|
@@ -156,7 +161,11 @@ def deploy(
|
|
|
156
161
|
with cli_console.phase("Checking remote state"):
|
|
157
162
|
om = ObjectManager()
|
|
158
163
|
_check_if_all_defined_integrations_exists(om, snowpark_entities)
|
|
159
|
-
existing_objects =
|
|
164
|
+
existing_objects = (
|
|
165
|
+
{}
|
|
166
|
+
if force_replace
|
|
167
|
+
else check_for_existing_objects(om, replace, snowpark_entities)
|
|
168
|
+
)
|
|
160
169
|
|
|
161
170
|
with cli_console.phase("Preparing required stages and artifacts"):
|
|
162
171
|
entities_to_imports_map, stages_to_artifact_map = build_artifacts_mappings(
|
|
@@ -189,11 +198,11 @@ def validate_all_artifacts_exists(
|
|
|
189
198
|
project_paths: SnowparkProjectPaths, snowpark_entities: SnowparkEntities
|
|
190
199
|
):
|
|
191
200
|
for key, entity in snowpark_entities.items():
|
|
192
|
-
for
|
|
193
|
-
path = project_paths.
|
|
201
|
+
for artifact in entity.artifacts:
|
|
202
|
+
path = project_paths.get_artifact_dto(artifact).post_build_path
|
|
194
203
|
if not path.exists():
|
|
195
204
|
raise UsageError(
|
|
196
|
-
f"
|
|
205
|
+
f"Artifact {path} required for {entity.type} {key} does not exist."
|
|
197
206
|
)
|
|
198
207
|
|
|
199
208
|
|
|
@@ -213,38 +222,39 @@ def check_for_existing_objects(
|
|
|
213
222
|
|
|
214
223
|
def build_artifacts_mappings(
|
|
215
224
|
project_paths: SnowparkProjectPaths, snowpark_entities: SnowparkEntities
|
|
216
|
-
) -> Tuple[EntityToImportPathsMapping,
|
|
217
|
-
stages_to_artifact_map:
|
|
225
|
+
) -> Tuple[EntityToImportPathsMapping, StageToArtifactMapping]:
|
|
226
|
+
stages_to_artifact_map: StageToArtifactMapping = defaultdict(set)
|
|
218
227
|
entities_to_imports_map: EntityToImportPathsMapping = defaultdict(set)
|
|
219
|
-
for
|
|
228
|
+
for name, entity in snowpark_entities.items():
|
|
220
229
|
stage = entity.stage
|
|
221
230
|
required_artifacts = set()
|
|
222
|
-
for
|
|
223
|
-
|
|
224
|
-
required_artifacts.add(
|
|
225
|
-
entities_to_imports_map[
|
|
231
|
+
for artifact in entity.artifacts:
|
|
232
|
+
artifact_dto = project_paths.get_artifact_dto(artifact)
|
|
233
|
+
required_artifacts.add(artifact_dto)
|
|
234
|
+
entities_to_imports_map[name].add(artifact_dto.import_path(stage))
|
|
226
235
|
stages_to_artifact_map[stage].update(required_artifacts)
|
|
227
236
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
stages_to_artifact_map[stage].add(
|
|
231
|
-
entities_to_imports_map[
|
|
237
|
+
deps_artifact = project_paths.get_dependencies_artifact()
|
|
238
|
+
if deps_artifact.post_build_path.exists():
|
|
239
|
+
stages_to_artifact_map[stage].add(deps_artifact)
|
|
240
|
+
entities_to_imports_map[name].add(deps_artifact.import_path(stage))
|
|
232
241
|
return entities_to_imports_map, stages_to_artifact_map
|
|
233
242
|
|
|
234
243
|
|
|
235
|
-
def create_stages_and_upload_artifacts(stages_to_artifact_map:
|
|
244
|
+
def create_stages_and_upload_artifacts(stages_to_artifact_map: StageToArtifactMapping):
|
|
236
245
|
stage_manager = StageManager()
|
|
237
246
|
for stage, artifacts in stages_to_artifact_map.items():
|
|
238
247
|
cli_console.step(f"Creating (if not exists) stage: {stage}")
|
|
239
248
|
stage = FQN.from_stage(stage).using_context()
|
|
240
249
|
stage_manager.create(fqn=stage, comment="deployments managed by Snowflake CLI")
|
|
241
|
-
for
|
|
250
|
+
for artifact in artifacts:
|
|
251
|
+
post_build_path = artifact.post_build_path
|
|
242
252
|
cli_console.step(
|
|
243
|
-
f"Uploading {
|
|
253
|
+
f"Uploading {post_build_path.name} to {artifact.upload_path(stage)}"
|
|
244
254
|
)
|
|
245
255
|
stage_manager.put(
|
|
246
|
-
local_path=
|
|
247
|
-
stage_path=
|
|
256
|
+
local_path=post_build_path,
|
|
257
|
+
stage_path=artifact.upload_path(stage),
|
|
248
258
|
overwrite=True,
|
|
249
259
|
)
|
|
250
260
|
|
|
@@ -324,6 +334,9 @@ def build(
|
|
|
324
334
|
|
|
325
335
|
anaconda_packages_manager = AnacondaPackagesManager()
|
|
326
336
|
|
|
337
|
+
# Clean up bundle root
|
|
338
|
+
project_paths.remove_up_bundle_root()
|
|
339
|
+
|
|
327
340
|
# Resolve dependencies
|
|
328
341
|
if project_paths.requirements.exists():
|
|
329
342
|
with (
|
|
@@ -362,22 +375,27 @@ def build(
|
|
|
362
375
|
)
|
|
363
376
|
|
|
364
377
|
if any(temp_deps_dir.path.iterdir()):
|
|
365
|
-
|
|
378
|
+
dep_artifact = project_paths.get_dependencies_artifact()
|
|
379
|
+
cli_console.step(f"Creating {dep_artifact.path.name}")
|
|
366
380
|
zip_dir(
|
|
367
381
|
source=temp_deps_dir.path,
|
|
368
|
-
dest_zip=
|
|
382
|
+
dest_zip=dep_artifact.post_build_path,
|
|
369
383
|
)
|
|
370
384
|
else:
|
|
371
385
|
cli_console.step(f"No external dependencies.")
|
|
372
386
|
|
|
373
387
|
artifacts = set()
|
|
374
|
-
for entity in get_snowpark_entities(pd).values():
|
|
375
|
-
artifacts.update(entity.artifacts)
|
|
376
|
-
|
|
377
388
|
with cli_console.phase("Preparing artifacts for source code"):
|
|
378
|
-
for
|
|
379
|
-
|
|
380
|
-
|
|
389
|
+
for entity in get_snowpark_entities(pd).values():
|
|
390
|
+
artifacts.update(
|
|
391
|
+
map_path_mapping_to_artifact(project_paths, entity.artifacts)
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
if FeatureFlag.ENABLE_SNOWPARK_GLOB_SUPPORT.is_enabled():
|
|
395
|
+
zip_and_copy_artifacts_to_deploy(artifacts, project_paths.bundle_root)
|
|
396
|
+
else:
|
|
397
|
+
for artifact in artifacts:
|
|
398
|
+
artifact.build()
|
|
381
399
|
|
|
382
400
|
return MessageResult(f"Build done.")
|
|
383
401
|
|
|
@@ -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
|
|
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
|
-
|
|
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
|
|
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,
|
|
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.
|
|
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
|
|
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 =
|
|
105
|
-
output_dir.
|
|
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
|
-
#
|
|
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
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
35
|
-
|
|
36
|
-
|
|
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
|
|
69
|
-
if
|
|
70
|
-
|
|
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=
|
|
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)
|