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.
Files changed (77) hide show
  1. snowflake/cli/__about__.py +1 -1
  2. snowflake/cli/_app/__main__.py +2 -2
  3. snowflake/cli/_app/cli_app.py +224 -192
  4. snowflake/cli/_app/commands_registration/commands_registration_with_callbacks.py +1 -27
  5. snowflake/cli/_plugins/cortex/commands.py +2 -4
  6. snowflake/cli/_plugins/git/manager.py +1 -1
  7. snowflake/cli/_plugins/nativeapp/artifacts.py +6 -624
  8. snowflake/cli/_plugins/nativeapp/bundle_context.py +1 -1
  9. snowflake/cli/_plugins/nativeapp/codegen/artifact_processor.py +1 -1
  10. snowflake/cli/_plugins/nativeapp/codegen/compiler.py +1 -3
  11. snowflake/cli/_plugins/nativeapp/codegen/setup/native_app_setup_processor.py +2 -2
  12. snowflake/cli/_plugins/nativeapp/codegen/snowpark/python_processor.py +2 -2
  13. snowflake/cli/_plugins/nativeapp/codegen/templates/templates_processor.py +2 -2
  14. snowflake/cli/_plugins/nativeapp/commands.py +21 -19
  15. snowflake/cli/_plugins/nativeapp/entities/application.py +16 -19
  16. snowflake/cli/_plugins/nativeapp/entities/application_package.py +142 -55
  17. snowflake/cli/_plugins/nativeapp/release_channel/commands.py +37 -3
  18. snowflake/cli/_plugins/nativeapp/release_directive/commands.py +80 -2
  19. snowflake/cli/_plugins/nativeapp/sf_sql_facade.py +224 -44
  20. snowflake/cli/_plugins/nativeapp/v2_conversions/compat.py +2 -2
  21. snowflake/cli/_plugins/nativeapp/version/commands.py +1 -1
  22. snowflake/cli/_plugins/notebook/commands.py +55 -2
  23. snowflake/cli/_plugins/notebook/exceptions.py +1 -1
  24. snowflake/cli/_plugins/notebook/manager.py +3 -3
  25. snowflake/cli/_plugins/notebook/notebook_entity.py +120 -0
  26. snowflake/cli/_plugins/notebook/notebook_entity_model.py +42 -0
  27. snowflake/cli/_plugins/notebook/notebook_project_paths.py +15 -0
  28. snowflake/cli/_plugins/notebook/types.py +3 -0
  29. snowflake/cli/_plugins/snowpark/commands.py +48 -30
  30. snowflake/cli/_plugins/snowpark/common.py +47 -2
  31. snowflake/cli/_plugins/snowpark/snowpark_entity.py +38 -25
  32. snowflake/cli/_plugins/snowpark/snowpark_entity_model.py +18 -30
  33. snowflake/cli/_plugins/snowpark/snowpark_project_paths.py +156 -23
  34. snowflake/cli/_plugins/snowpark/zipper.py +33 -1
  35. snowflake/cli/_plugins/spcs/services/commands.py +0 -3
  36. snowflake/cli/_plugins/stage/commands.py +2 -1
  37. snowflake/cli/_plugins/stage/diff.py +60 -39
  38. snowflake/cli/_plugins/stage/manager.py +24 -11
  39. snowflake/cli/_plugins/stage/utils.py +1 -1
  40. snowflake/cli/_plugins/streamlit/commands.py +10 -1
  41. snowflake/cli/_plugins/streamlit/manager.py +62 -21
  42. snowflake/cli/_plugins/streamlit/streamlit_entity.py +20 -41
  43. snowflake/cli/_plugins/streamlit/streamlit_entity_model.py +14 -24
  44. snowflake/cli/_plugins/streamlit/streamlit_project_paths.py +30 -0
  45. snowflake/cli/_plugins/workspace/commands.py +3 -3
  46. snowflake/cli/_plugins/workspace/manager.py +1 -1
  47. snowflake/cli/api/artifacts/__init__.py +13 -0
  48. snowflake/cli/api/artifacts/bundle_map.py +500 -0
  49. snowflake/cli/api/artifacts/common.py +78 -0
  50. snowflake/cli/api/artifacts/utils.py +82 -0
  51. snowflake/cli/api/cli_global_context.py +14 -1
  52. snowflake/cli/api/commands/flags.py +10 -4
  53. snowflake/cli/api/commands/utils.py +28 -2
  54. snowflake/cli/api/constants.py +1 -0
  55. snowflake/cli/api/entities/common.py +14 -32
  56. snowflake/cli/api/entities/resolver.py +160 -0
  57. snowflake/cli/api/entities/utils.py +56 -15
  58. snowflake/cli/api/errno.py +3 -0
  59. snowflake/cli/api/feature_flags.py +1 -2
  60. snowflake/cli/api/project/definition_conversion.py +3 -2
  61. snowflake/cli/api/project/project_paths.py +28 -0
  62. snowflake/cli/api/project/schemas/entities/common.py +130 -1
  63. snowflake/cli/api/project/schemas/entities/entities.py +4 -0
  64. snowflake/cli/api/project/schemas/project_definition.py +27 -0
  65. snowflake/cli/api/project/schemas/updatable_model.py +2 -2
  66. snowflake/cli/api/project/schemas/v1/native_app/native_app.py +5 -7
  67. snowflake/cli/api/secure_path.py +6 -0
  68. snowflake/cli/api/sql_execution.py +5 -1
  69. snowflake/cli/api/stage_path.py +7 -2
  70. snowflake/cli/api/utils/graph.py +3 -0
  71. snowflake/cli/api/utils/path_utils.py +24 -0
  72. {snowflake_cli-3.3.0.dist-info → snowflake_cli-3.4.1.dist-info}/METADATA +8 -9
  73. {snowflake_cli-3.3.0.dist-info → snowflake_cli-3.4.1.dist-info}/RECORD +76 -67
  74. snowflake/cli/api/project/schemas/v1/native_app/path_mapping.py +0 -65
  75. {snowflake_cli-3.3.0.dist-info → snowflake_cli-3.4.1.dist-info}/WHEEL +0 -0
  76. {snowflake_cli-3.3.0.dist-info → snowflake_cli-3.4.1.dist-info}/entry_points.txt +0 -0
  77. {snowflake_cli-3.3.0.dist-info → snowflake_cli-3.4.1.dist-info}/licenses/LICENSE +0 -0
@@ -15,7 +15,7 @@
15
15
  from __future__ import annotations
16
16
 
17
17
  import logging
18
- from pathlib import Path
18
+ from pathlib import PurePosixPath
19
19
  from typing import List, Optional
20
20
 
21
21
  from click import ClickException
@@ -29,12 +29,18 @@ from snowflake.cli._plugins.stage.manager import StageManager
29
29
  from snowflake.cli._plugins.streamlit.streamlit_entity_model import (
30
30
  StreamlitEntityModel,
31
31
  )
32
+ from snowflake.cli._plugins.streamlit.streamlit_project_paths import (
33
+ StreamlitProjectPaths,
34
+ )
35
+ from snowflake.cli.api.artifacts.bundle_map import BundleMap
36
+ from snowflake.cli.api.artifacts.utils import symlink_or_copy
32
37
  from snowflake.cli.api.commands.experimental_behaviour import (
33
38
  experimental_behaviour_enabled,
34
39
  )
35
40
  from snowflake.cli.api.console import cli_console
36
41
  from snowflake.cli.api.feature_flags import FeatureFlag
37
42
  from snowflake.cli.api.identifiers import FQN
43
+ from snowflake.cli.api.project.schemas.entities.common import PathMapping
38
44
  from snowflake.cli.api.sql_execution import SqlExecutionMixin
39
45
  from snowflake.connector.cursor import SnowflakeCursor
40
46
  from snowflake.connector.errors import ProgrammingError
@@ -54,26 +60,45 @@ class StreamlitManager(SqlExecutionMixin):
54
60
 
55
61
  def _put_streamlit_files(
56
62
  self,
57
- root_location: str,
58
- artifacts: Optional[List[Path]] = None,
63
+ streamlit_project_paths: StreamlitProjectPaths,
64
+ stage_root: str,
65
+ artifacts: Optional[List[PathMapping]] = None,
59
66
  ):
60
- cli_console.step(f"Deploying files to {root_location}")
67
+ cli_console.step(f"Deploying files to {stage_root}")
61
68
  if not artifacts:
62
69
  return
63
70
  stage_manager = StageManager()
64
- for file in artifacts:
65
- if file.is_dir():
66
- if not any(file.iterdir()):
67
- cli_console.warning(f"Skipping empty directory: {file}")
68
- continue
71
+ # We treat the bundle root as deploy root
72
+ bundle_map = BundleMap(
73
+ project_root=streamlit_project_paths.project_root,
74
+ deploy_root=streamlit_project_paths.bundle_root,
75
+ )
76
+ for artifact in artifacts:
77
+ bundle_map.add(PathMapping(src=str(artifact.src), dest=artifact.dest))
69
78
 
79
+ # Clean up bundle root
80
+ streamlit_project_paths.remove_up_bundle_root()
81
+
82
+ for (absolute_src, absolute_dest) in bundle_map.all_mappings(
83
+ absolute=True, expand_directories=True
84
+ ):
85
+ if absolute_src.is_file():
86
+ # We treat the bundle/streamlit root as deploy root
87
+ symlink_or_copy(
88
+ absolute_src,
89
+ absolute_dest,
90
+ deploy_root=streamlit_project_paths.bundle_root,
91
+ )
92
+ # Temporary solution, will be replaced with diff
93
+ stage_path = (
94
+ PurePosixPath(absolute_dest)
95
+ .relative_to(streamlit_project_paths.bundle_root)
96
+ .parent
97
+ )
98
+ full_stage_path = f"{stage_root}/{stage_path}".rstrip("/")
70
99
  stage_manager.put(
71
- f"{file.joinpath('*')}", f"{root_location}/{file}", 4, True
100
+ local_path=absolute_dest, stage_path=full_stage_path, overwrite=True
72
101
  )
73
- elif len(file.parts) > 1:
74
- stage_manager.put(file, f"{root_location}/{file.parent}", 4, True)
75
- else:
76
- stage_manager.put(file, root_location, 4, True)
77
102
 
78
103
  def _create_streamlit(
79
104
  self,
@@ -127,7 +152,12 @@ class StreamlitManager(SqlExecutionMixin):
127
152
 
128
153
  self.execute_query("\n".join(query))
129
154
 
130
- def deploy(self, streamlit: StreamlitEntityModel, replace: bool = False):
155
+ def deploy(
156
+ self,
157
+ streamlit: StreamlitEntityModel,
158
+ streamlit_project_paths: StreamlitProjectPaths,
159
+ replace: bool = False,
160
+ ):
131
161
  streamlit_id = streamlit.fqn.using_connection(self._conn)
132
162
  if (
133
163
  ObjectManager().object_exists(object_type="streamlit", fqn=streamlit_id)
@@ -179,12 +209,13 @@ class StreamlitManager(SqlExecutionMixin):
179
209
  embedded_stage_name = f"snow://streamlit/{stage_path}"
180
210
  if use_versioned_stage:
181
211
  # "LIVE" is the only supported version for now, but this may change later.
182
- root_location = f"{embedded_stage_name}/versions/live"
212
+ stage_root = f"{embedded_stage_name}/versions/live"
183
213
  else:
184
- root_location = f"{embedded_stage_name}/default_checkout"
214
+ stage_root = f"{embedded_stage_name}/default_checkout"
185
215
 
186
216
  self._put_streamlit_files(
187
- root_location,
217
+ streamlit_project_paths,
218
+ stage_root,
188
219
  streamlit.artifacts,
189
220
  )
190
221
  else:
@@ -201,21 +232,31 @@ class StreamlitManager(SqlExecutionMixin):
201
232
  cli_console.step(f"Creating {stage_name} stage")
202
233
  stage_manager.create(fqn=stage_name)
203
234
 
204
- root_location = stage_manager.get_standard_stage_prefix(
235
+ stage_root = stage_manager.get_standard_stage_prefix(
205
236
  f"{stage_name}/{streamlit_name_for_root_location}"
206
237
  )
207
238
 
208
- self._put_streamlit_files(root_location, streamlit.artifacts)
239
+ self._put_streamlit_files(
240
+ streamlit_project_paths, stage_root, streamlit.artifacts
241
+ )
209
242
 
210
243
  self._create_streamlit(
211
244
  streamlit=streamlit,
212
245
  replace=replace,
213
- from_stage_name=root_location,
246
+ from_stage_name=stage_root,
214
247
  experimental=False,
215
248
  )
216
249
 
250
+ self.grant_privileges(streamlit)
251
+
217
252
  return self.get_url(streamlit_name=streamlit_id)
218
253
 
254
+ def grant_privileges(self, entity_model: StreamlitEntityModel):
255
+ if not entity_model.grants:
256
+ return
257
+ for grant in entity_model.grants:
258
+ self.execute_query(grant.get_grant_sql(entity_model))
259
+
219
260
  def get_url(self, streamlit_name: FQN) -> str:
220
261
  try:
221
262
  fqn = streamlit_name.using_connection(self._conn)
@@ -1,16 +1,17 @@
1
- import functools
2
1
  from pathlib import Path
3
2
  from typing import Optional
4
3
 
5
4
  from click import ClickException
6
5
  from snowflake.cli._plugins.connection.util import make_snowsight_url
6
+ from snowflake.cli._plugins.nativeapp.artifacts import build_bundle
7
7
  from snowflake.cli._plugins.nativeapp.feature_flags import FeatureFlag
8
8
  from snowflake.cli._plugins.streamlit.streamlit_entity_model import (
9
9
  StreamlitEntityModel,
10
10
  )
11
11
  from snowflake.cli._plugins.workspace.context import ActionContext
12
- from snowflake.cli.api.entities.common import EntityBase, get_sql_executor
13
- from snowflake.cli.api.secure_path import SecurePath
12
+ from snowflake.cli.api.entities.common import EntityBase
13
+ from snowflake.cli.api.project.project_paths import bundle_root
14
+ from snowflake.cli.api.project.schemas.entities.common import PathMapping
14
15
  from snowflake.connector.cursor import SnowflakeCursor
15
16
 
16
17
 
@@ -32,18 +33,6 @@ class StreamlitEntity(EntityBase[StreamlitEntityModel]):
32
33
  def artifacts(self):
33
34
  return self._entity_model.artifacts
34
35
 
35
- @functools.cached_property
36
- def _sql_executor(self):
37
- return get_sql_executor()
38
-
39
- @functools.cached_property
40
- def _conn(self):
41
- return self._sql_executor._conn # noqa
42
-
43
- @property
44
- def model(self):
45
- return self._entity_model # noqa
46
-
47
36
  def action_bundle(self, action_ctx: ActionContext, *args, **kwargs):
48
37
  return self.bundle()
49
38
 
@@ -51,15 +40,15 @@ class StreamlitEntity(EntityBase[StreamlitEntityModel]):
51
40
  # After adding bundle map- we should use it's mapping here
52
41
  # To copy artifacts to destination on stage.
53
42
 
54
- return self._sql_executor.execute_query(self.get_deploy_sql())
43
+ return self.deploy()
55
44
 
56
45
  def action_drop(self, action_ctx: ActionContext, *args, **kwargs):
57
- return self._sql_executor.execute_query(self.get_drop_sql())
46
+ return self._execute_query(self.get_drop_sql())
58
47
 
59
48
  def action_execute(
60
49
  self, action_ctx: ActionContext, *args, **kwargs
61
50
  ) -> SnowflakeCursor:
62
- return self._sql_executor.execute_query(self.get_execute_sql())
51
+ return self._execute_query(self.get_execute_sql())
63
52
 
64
53
  def action_get_url(
65
54
  self, action_ctx: ActionContext, *args, **kwargs
@@ -70,34 +59,24 @@ class StreamlitEntity(EntityBase[StreamlitEntityModel]):
70
59
  )
71
60
 
72
61
  def bundle(self, output_dir: Optional[Path] = None):
62
+ build_bundle(
63
+ self.root,
64
+ output_dir or bundle_root(self.root, "streamlit"),
65
+ [
66
+ PathMapping(
67
+ src=artifact.src, dest=artifact.dest, processors=artifact.processors
68
+ )
69
+ for artifact in self._entity_model.artifacts
70
+ ],
71
+ )
73
72
 
74
- if not output_dir:
75
- output_dir = self.root / "output" / self._entity_model.stage
76
-
77
- artifacts = self._entity_model.artifacts
78
-
79
- output_dir.mkdir(parents=True, exist_ok=True) # type: ignore
80
-
81
- output_files = []
82
-
83
- # This is far from , but will be replaced by bundlemap mappings.
84
- for file in artifacts:
85
- output_file = output_dir / file.name
86
-
87
- if file.is_file():
88
- SecurePath(file).copy(output_file)
89
- elif file.is_dir():
90
- output_file.mkdir(parents=True, exist_ok=True)
91
- SecurePath(file).copy(output_file, dirs_exist_ok=True)
92
-
93
- output_files.append(output_file)
94
-
95
- return output_files
73
+ def deploy(self, *args, **kwargs):
74
+ return self._execute_query(self.get_deploy_sql())
96
75
 
97
76
  def action_share(
98
77
  self, action_ctx: ActionContext, to_role: str, *args, **kwargs
99
78
  ) -> SnowflakeCursor:
100
- return self._sql_executor.execute_query(self.get_share_sql(to_role))
79
+ return self._execute_query(self.get_share_sql(to_role))
101
80
 
102
81
  def get_deploy_sql(
103
82
  self,
@@ -13,13 +13,14 @@
13
13
  # limitations under the License.
14
14
  from __future__ import annotations
15
15
 
16
- from pathlib import Path
17
- from typing import List, Literal, Optional
16
+ from typing import Literal, Optional
18
17
 
19
- from pydantic import Field, model_validator
18
+ from pydantic import Field
20
19
  from snowflake.cli.api.project.schemas.entities.common import (
21
- EntityModelBase,
20
+ Artifacts,
21
+ EntityModelBaseWithArtifacts,
22
22
  ExternalAccessBaseModel,
23
+ GrantBaseModel,
23
24
  ImportsBaseModel,
24
25
  )
25
26
  from snowflake.cli.api.project.schemas.updatable_model import (
@@ -27,7 +28,12 @@ from snowflake.cli.api.project.schemas.updatable_model import (
27
28
  )
28
29
 
29
30
 
30
- class StreamlitEntityModel(EntityModelBase, ExternalAccessBaseModel, ImportsBaseModel):
31
+ class StreamlitEntityModel(
32
+ EntityModelBaseWithArtifacts,
33
+ ExternalAccessBaseModel,
34
+ ImportsBaseModel,
35
+ GrantBaseModel,
36
+ ):
31
37
  type: Literal["streamlit"] = DiscriminatorField() # noqa: A003
32
38
  title: Optional[str] = Field(
33
39
  title="Human-readable title for the Streamlit dashboard", default=None
@@ -43,24 +49,8 @@ class StreamlitEntityModel(EntityModelBase, ExternalAccessBaseModel, ImportsBase
43
49
  stage: Optional[str] = Field(
44
50
  title="Stage in which the app’s artifacts will be stored", default="streamlit"
45
51
  )
46
- # Possibly can be PathMapping
47
- artifacts: Optional[List[Path]] = Field(
48
- title="List of files which should be deployed. Each file needs to exist locally. "
49
- "Main file needs to be included in the artifacts.",
52
+ # Artifacts were optional, so to avoid BCR, we need to make them optional here as well
53
+ artifacts: Optional[Artifacts] = Field(
54
+ title="List of paths or file source/destination pairs to add to the deploy root",
50
55
  default=None,
51
56
  )
52
-
53
- @model_validator(mode="after")
54
- def artifacts_must_exists(self):
55
- if not self.artifacts:
56
- return self
57
-
58
- for artifact in self.artifacts:
59
- if "*" in artifact.name:
60
- continue
61
- if not artifact.exists():
62
- raise ValueError(
63
- f"Specified artifact {artifact} does not exist locally."
64
- )
65
-
66
- return self
@@ -0,0 +1,30 @@
1
+ # Copyright (c) 2024 Snowflake Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ from __future__ import annotations
15
+
16
+ from dataclasses import dataclass
17
+ from pathlib import Path
18
+
19
+ from snowflake.cli.api.project.project_paths import ProjectPaths, bundle_root
20
+
21
+
22
+ @dataclass
23
+ class StreamlitProjectPaths(ProjectPaths):
24
+ """
25
+ This class allows you to manage files paths related to given project.
26
+ """
27
+
28
+ @property
29
+ def bundle_root(self) -> Path:
30
+ return bundle_root(self.project_root, "streamlit")
@@ -22,17 +22,17 @@ from typing import List, Optional
22
22
 
23
23
  import typer
24
24
  import yaml
25
- from snowflake.cli._plugins.nativeapp.artifacts import BundleMap
26
25
  from snowflake.cli._plugins.nativeapp.common_flags import (
27
26
  ForceOption,
28
27
  InteractiveOption,
29
28
  ValidateOption,
30
29
  )
31
30
  from snowflake.cli._plugins.workspace.manager import WorkspaceManager
31
+ from snowflake.cli.api.artifacts.bundle_map import BundleMap
32
32
  from snowflake.cli.api.cli_global_context import get_cli_context
33
33
  from snowflake.cli.api.commands.decorators import with_project_definition
34
34
  from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
35
- from snowflake.cli.api.entities.common import EntityActions
35
+ from snowflake.cli.api.entities.utils import EntityActions
36
36
  from snowflake.cli.api.exceptions import IncompatibleParametersError
37
37
  from snowflake.cli.api.output.types import CollectionResult, MessageResult
38
38
 
@@ -106,7 +106,7 @@ def deploy(
106
106
  show_default=False,
107
107
  help=dedent(
108
108
  f"""
109
- Paths, relative to the the project root, of files or directories you want to upload to a stage. If a file is
109
+ Paths, relative to the project root, of files or directories you want to upload to a stage. If a file is
110
110
  specified, it must match one of the artifacts src pattern entries in snowflake.yml. If a directory is
111
111
  specified, it will be searched for subfolders or files to deploy based on artifacts src pattern entries. If
112
112
  unspecified, the command syncs all local changes to the stage."""
@@ -5,7 +5,7 @@ from typing import Dict
5
5
  from snowflake.cli._plugins.workspace.context import ActionContext, WorkspaceContext
6
6
  from snowflake.cli.api.cli_global_context import get_cli_context
7
7
  from snowflake.cli.api.console import cli_console as cc
8
- from snowflake.cli.api.entities.common import EntityActions, get_sql_executor
8
+ from snowflake.cli.api.entities.utils import EntityActions, get_sql_executor
9
9
  from snowflake.cli.api.exceptions import InvalidProjectDefinitionVersionError
10
10
  from snowflake.cli.api.project.definition import default_role
11
11
  from snowflake.cli.api.project.schemas.entities.entities import (
@@ -0,0 +1,13 @@
1
+ # Copyright (c) 2024 Snowflake Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.