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
|
@@ -15,9 +15,10 @@
|
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
17
|
from abc import ABC
|
|
18
|
-
from typing import Dict, Generic, List, Optional, TypeVar, Union
|
|
18
|
+
from typing import Any, Dict, Generic, List, Optional, TypeVar, Union
|
|
19
19
|
|
|
20
20
|
from pydantic import Field, PrivateAttr, field_validator
|
|
21
|
+
from pydantic_core.core_schema import ValidationInfo
|
|
21
22
|
from snowflake.cli.api.identifiers import FQN
|
|
22
23
|
from snowflake.cli.api.project.schemas.updatable_model import (
|
|
23
24
|
IdentifierField,
|
|
@@ -61,6 +62,15 @@ class MetaField(UpdatableModel):
|
|
|
61
62
|
default=None,
|
|
62
63
|
)
|
|
63
64
|
|
|
65
|
+
depends_on: Optional[List[str]] = Field(
|
|
66
|
+
title="Entities that need to be deployed before this one", default_factory=list
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
action_arguments: Optional[Dict[str, Dict[str, Union[int, bool, str]]]] = Field(
|
|
70
|
+
title="Arguments that will be used, when this entity is called as a dependency of other entity",
|
|
71
|
+
default_factory=dict,
|
|
72
|
+
)
|
|
73
|
+
|
|
64
74
|
@field_validator("use_mixins", mode="before")
|
|
65
75
|
@classmethod
|
|
66
76
|
def ensure_use_mixins_is_a_list(
|
|
@@ -70,6 +80,35 @@ class MetaField(UpdatableModel):
|
|
|
70
80
|
return [mixins]
|
|
71
81
|
return mixins
|
|
72
82
|
|
|
83
|
+
@field_validator("action_arguments", mode="before")
|
|
84
|
+
@classmethod
|
|
85
|
+
def arguments_validator(cls, arguments: Dict, info: ValidationInfo) -> Dict:
|
|
86
|
+
duplicated_run = (
|
|
87
|
+
info.context.get("is_duplicated_run", False) if info.context else False
|
|
88
|
+
)
|
|
89
|
+
if not duplicated_run:
|
|
90
|
+
for argument_dict in arguments.values():
|
|
91
|
+
for k, v in argument_dict.items():
|
|
92
|
+
argument_dict[k] = cls._cast_value(v)
|
|
93
|
+
|
|
94
|
+
return arguments
|
|
95
|
+
|
|
96
|
+
@staticmethod
|
|
97
|
+
def _cast_value(value: str) -> Union[int, bool, str]:
|
|
98
|
+
if value.lower() in ["true", "false"]:
|
|
99
|
+
return value.lower() == "true"
|
|
100
|
+
|
|
101
|
+
try:
|
|
102
|
+
return int(value)
|
|
103
|
+
except ValueError:
|
|
104
|
+
return value
|
|
105
|
+
|
|
106
|
+
def __eq__(self, other):
|
|
107
|
+
return self.entity_id == other.entity_id
|
|
108
|
+
|
|
109
|
+
def __hash__(self):
|
|
110
|
+
return hash(self.entity_id)
|
|
111
|
+
|
|
73
112
|
|
|
74
113
|
class Identifier(UpdatableModel):
|
|
75
114
|
name: str = Field(title="Entity name")
|
|
@@ -141,6 +180,23 @@ class ImportsBaseModel:
|
|
|
141
180
|
return f"IMPORTS = ({imports})"
|
|
142
181
|
|
|
143
182
|
|
|
183
|
+
class Grant(UpdatableModel):
|
|
184
|
+
privilege: str = Field(title="Required privileges")
|
|
185
|
+
role: str = Field(title="Role to which the privileges will be granted")
|
|
186
|
+
|
|
187
|
+
def get_grant_sql(self, entity_model: EntityModelBase) -> str:
|
|
188
|
+
return f"GRANT {self.privilege} ON {entity_model.get_type().upper()} {entity_model.fqn.sql_identifier} TO ROLE {self.role}"
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
class GrantBaseModel(UpdatableModel):
|
|
192
|
+
grants: Optional[List[Grant]] = Field(title="List of grants", default=None)
|
|
193
|
+
|
|
194
|
+
def get_grant_sqls(self) -> list[str]:
|
|
195
|
+
return (
|
|
196
|
+
[grant.get_grant_sql(self) for grant in self.grants] if self.grants else []
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
|
|
144
200
|
class ExternalAccessBaseModel:
|
|
145
201
|
external_access_integrations: Optional[List[str]] = Field(
|
|
146
202
|
title="Names of external access integrations needed for this entity to access external networks",
|
|
@@ -162,3 +218,76 @@ class ExternalAccessBaseModel:
|
|
|
162
218
|
return None
|
|
163
219
|
secrets = ", ".join(f"'{key}'={value}" for key, value in self.secrets.items())
|
|
164
220
|
return f"secrets=({secrets})"
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
class ProcessorMapping(UpdatableModel):
|
|
224
|
+
name: str = Field(
|
|
225
|
+
title="Name of a processor to invoke on a collection of artifacts."
|
|
226
|
+
)
|
|
227
|
+
properties: Optional[Dict[str, Any]] = Field(
|
|
228
|
+
title="A set of key-value pairs used to configure the output of the processor. Consult a specific processor's documentation for more details on the supported properties.",
|
|
229
|
+
default=None,
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
class PathMapping(UpdatableModel):
|
|
234
|
+
src: str = Field(
|
|
235
|
+
title="Source path or glob pattern (relative to project root)", default=None
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
dest: Optional[str] = Field(
|
|
239
|
+
title="Destination path on stage",
|
|
240
|
+
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.",
|
|
241
|
+
default=None,
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
processors: Optional[List[Union[str, ProcessorMapping]]] = Field(
|
|
245
|
+
title="List of processors to apply to matching source files during bundling.",
|
|
246
|
+
default=[],
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
@field_validator("processors")
|
|
250
|
+
@classmethod
|
|
251
|
+
def transform_processors(
|
|
252
|
+
cls, input_values: Optional[List[Union[str, Dict, ProcessorMapping]]]
|
|
253
|
+
) -> List[ProcessorMapping]:
|
|
254
|
+
if input_values is None:
|
|
255
|
+
return []
|
|
256
|
+
|
|
257
|
+
transformed_processors: List[ProcessorMapping] = []
|
|
258
|
+
for input_processor in input_values:
|
|
259
|
+
if isinstance(input_processor, str):
|
|
260
|
+
transformed_processors.append(ProcessorMapping(name=input_processor))
|
|
261
|
+
elif isinstance(input_processor, Dict):
|
|
262
|
+
transformed_processors.append(ProcessorMapping(**input_processor))
|
|
263
|
+
else:
|
|
264
|
+
transformed_processors.append(input_processor)
|
|
265
|
+
return transformed_processors
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
Artifacts = List[Union[PathMapping, str]]
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
class EntityModelBaseWithArtifacts(EntityModelBase):
|
|
272
|
+
artifacts: Artifacts = Field(
|
|
273
|
+
title="List of paths or file source/destination pairs to add to the deploy root",
|
|
274
|
+
)
|
|
275
|
+
deploy_root: Optional[str] = Field(
|
|
276
|
+
title="Folder at the root of your project where the build step copies the artifacts",
|
|
277
|
+
default="output/deploy/",
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
@field_validator("artifacts")
|
|
281
|
+
@classmethod
|
|
282
|
+
def transform_artifacts(cls, orig_artifacts: Artifacts) -> List[PathMapping]:
|
|
283
|
+
transformed_artifacts: List[PathMapping] = []
|
|
284
|
+
if orig_artifacts is None:
|
|
285
|
+
return transformed_artifacts
|
|
286
|
+
|
|
287
|
+
for artifact in orig_artifacts:
|
|
288
|
+
if isinstance(artifact, PathMapping):
|
|
289
|
+
transformed_artifacts.append(artifact)
|
|
290
|
+
else:
|
|
291
|
+
transformed_artifacts.append(PathMapping(src=artifact))
|
|
292
|
+
|
|
293
|
+
return transformed_artifacts
|
|
@@ -24,6 +24,8 @@ from snowflake.cli._plugins.nativeapp.entities.application_package import (
|
|
|
24
24
|
ApplicationPackageEntity,
|
|
25
25
|
ApplicationPackageEntityModel,
|
|
26
26
|
)
|
|
27
|
+
from snowflake.cli._plugins.notebook.notebook_entity import NotebookEntity
|
|
28
|
+
from snowflake.cli._plugins.notebook.notebook_entity_model import NotebookEntityModel
|
|
27
29
|
from snowflake.cli._plugins.snowpark.snowpark_entity import (
|
|
28
30
|
FunctionEntity,
|
|
29
31
|
ProcedureEntity,
|
|
@@ -43,6 +45,7 @@ Entity = Union[
|
|
|
43
45
|
StreamlitEntity,
|
|
44
46
|
ProcedureEntity,
|
|
45
47
|
FunctionEntity,
|
|
48
|
+
NotebookEntity,
|
|
46
49
|
]
|
|
47
50
|
EntityModel = Union[
|
|
48
51
|
ApplicationEntityModel,
|
|
@@ -50,6 +53,7 @@ EntityModel = Union[
|
|
|
50
53
|
StreamlitEntityModel,
|
|
51
54
|
FunctionEntityModel,
|
|
52
55
|
ProcedureEntityModel,
|
|
56
|
+
NotebookEntityModel,
|
|
53
57
|
]
|
|
54
58
|
|
|
55
59
|
ALL_ENTITIES: List[Entity] = [*get_args(Entity)]
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
|
+
from collections import defaultdict
|
|
17
18
|
from dataclasses import dataclass
|
|
18
19
|
from types import UnionType
|
|
19
20
|
from typing import Any, Dict, List, Optional, Union, get_args, get_origin
|
|
@@ -262,6 +263,21 @@ class DefinitionV20(_ProjectDefinitionBase):
|
|
|
262
263
|
data = cls._merge_data(data, entity)
|
|
263
264
|
return data
|
|
264
265
|
|
|
266
|
+
@model_validator(mode="after")
|
|
267
|
+
def validate_dependencies(self):
|
|
268
|
+
"""
|
|
269
|
+
Checks if entities listed in depends_on section exist in the project
|
|
270
|
+
"""
|
|
271
|
+
missing_dependencies = defaultdict(list)
|
|
272
|
+
for entity_id, entity in self.entities.items():
|
|
273
|
+
if entity.meta:
|
|
274
|
+
for dependency in entity.meta.depends_on:
|
|
275
|
+
if dependency not in self.entities:
|
|
276
|
+
missing_dependencies[entity_id].append(dependency)
|
|
277
|
+
|
|
278
|
+
if missing_dependencies:
|
|
279
|
+
raise ValueError(_get_missing_dependencies_message(missing_dependencies))
|
|
280
|
+
|
|
265
281
|
@classmethod
|
|
266
282
|
def _merge_data(
|
|
267
283
|
cls,
|
|
@@ -354,3 +370,14 @@ def _unique_extend(list_a: List, list_b: List) -> List:
|
|
|
354
370
|
if all(item != x for x in list_a):
|
|
355
371
|
new_list.append(item)
|
|
356
372
|
return new_list
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
def _get_missing_dependencies_message(
|
|
376
|
+
missing_dependencies: Dict[str, List[str]]
|
|
377
|
+
) -> str:
|
|
378
|
+
missing_dependencies_message = []
|
|
379
|
+
for entity_id, dependencies in missing_dependencies.items():
|
|
380
|
+
missing_dependencies_message.append(
|
|
381
|
+
f"\n Entity {entity_id} depends on non-existing entities: {', '.join(dependencies)}"
|
|
382
|
+
)
|
|
383
|
+
return "".join(missing_dependencies_message)
|
|
@@ -122,10 +122,10 @@ class UpdatableModel(BaseModel):
|
|
|
122
122
|
class_dict = class_.__dict__
|
|
123
123
|
field_annotations.update(class_dict.get("__annotations__", {}))
|
|
124
124
|
|
|
125
|
-
if "model_fields" in class_dict:
|
|
125
|
+
if "model_fields" in class_dict and class_.model_fields:
|
|
126
126
|
# This means the class dict has already been processed by Pydantic
|
|
127
127
|
# All fields should properly be populated in model_fields
|
|
128
|
-
field_values.update(
|
|
128
|
+
field_values.update(class_.model_fields)
|
|
129
129
|
else:
|
|
130
130
|
# If Pydantic did not process this class yet, get the values from class_dict directly
|
|
131
131
|
field_values.update(class_dict)
|
|
@@ -15,16 +15,16 @@
|
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
17
|
import re
|
|
18
|
-
from typing import List, Optional
|
|
18
|
+
from typing import List, Optional
|
|
19
19
|
|
|
20
20
|
from pydantic import Field, field_validator
|
|
21
|
+
from snowflake.cli.api.project.schemas.entities.common import Artifacts, PathMapping
|
|
21
22
|
from snowflake.cli.api.project.schemas.updatable_model import UpdatableModel
|
|
22
23
|
from snowflake.cli.api.project.schemas.v1.native_app.application import (
|
|
23
24
|
Application,
|
|
24
25
|
ApplicationV11,
|
|
25
26
|
)
|
|
26
27
|
from snowflake.cli.api.project.schemas.v1.native_app.package import Package, PackageV11
|
|
27
|
-
from snowflake.cli.api.project.schemas.v1.native_app.path_mapping import PathMapping
|
|
28
28
|
from snowflake.cli.api.project.util import (
|
|
29
29
|
SCHEMA_AND_NAME,
|
|
30
30
|
)
|
|
@@ -34,7 +34,7 @@ class NativeApp(UpdatableModel):
|
|
|
34
34
|
name: str = Field(
|
|
35
35
|
title="Project identifier",
|
|
36
36
|
)
|
|
37
|
-
artifacts:
|
|
37
|
+
artifacts: Artifacts = Field(
|
|
38
38
|
title="List of file source and destination pairs to add to the deploy root",
|
|
39
39
|
)
|
|
40
40
|
bundle_root: Optional[str] = Field(
|
|
@@ -69,10 +69,8 @@ class NativeApp(UpdatableModel):
|
|
|
69
69
|
|
|
70
70
|
@field_validator("artifacts")
|
|
71
71
|
@classmethod
|
|
72
|
-
def transform_artifacts(
|
|
73
|
-
|
|
74
|
-
) -> List[PathMapping]:
|
|
75
|
-
transformed_artifacts = []
|
|
72
|
+
def transform_artifacts(cls, orig_artifacts: Artifacts) -> List[PathMapping]:
|
|
73
|
+
transformed_artifacts: List[PathMapping] = []
|
|
76
74
|
if orig_artifacts is None:
|
|
77
75
|
return transformed_artifacts
|
|
78
76
|
|
snowflake/cli/api/secure_path.py
CHANGED
|
@@ -104,6 +104,12 @@ class SecurePath:
|
|
|
104
104
|
"""
|
|
105
105
|
return self._path.is_file()
|
|
106
106
|
|
|
107
|
+
def glob(self, pattern: str):
|
|
108
|
+
"""
|
|
109
|
+
Return a generator yielding Path objects that match the given pattern.
|
|
110
|
+
"""
|
|
111
|
+
return self._path.glob(pattern)
|
|
112
|
+
|
|
107
113
|
@property
|
|
108
114
|
def name(self) -> str:
|
|
109
115
|
"""A string representing the final path component."""
|
|
@@ -92,7 +92,11 @@ class BaseSqlExecutor:
|
|
|
92
92
|
|
|
93
93
|
def execute_queries(self, queries: str, **kwargs):
|
|
94
94
|
"""Executes multiple SQL queries (passed as one string) and returns the results as a list"""
|
|
95
|
-
|
|
95
|
+
|
|
96
|
+
# Without remove_comments=True, connectors might throw an error if there is a comment at the end of the file
|
|
97
|
+
return list(
|
|
98
|
+
self._execute_string(dedent(queries), remove_comments=True, **kwargs)
|
|
99
|
+
)
|
|
96
100
|
|
|
97
101
|
|
|
98
102
|
class SqlExecutor(BaseSqlExecutor):
|
snowflake/cli/api/stage_path.py
CHANGED
|
@@ -41,6 +41,10 @@ class StagePath:
|
|
|
41
41
|
def stage(self) -> str:
|
|
42
42
|
return self._stage_name
|
|
43
43
|
|
|
44
|
+
@property
|
|
45
|
+
def stage_fqn(self) -> FQN:
|
|
46
|
+
return FQN.from_stage(self.stage)
|
|
47
|
+
|
|
44
48
|
@property
|
|
45
49
|
def path(self) -> PurePosixPath:
|
|
46
50
|
return self._path
|
|
@@ -156,10 +160,11 @@ class StagePath:
|
|
|
156
160
|
def joinpath(self, path: str | Path) -> StagePath:
|
|
157
161
|
if self.is_file():
|
|
158
162
|
raise ValueError("Cannot join path to a file")
|
|
159
|
-
|
|
163
|
+
if isinstance(path, Path):
|
|
164
|
+
path = str(PurePosixPath(path))
|
|
160
165
|
return StagePath(
|
|
161
166
|
stage_name=self._stage_name,
|
|
162
|
-
path=PurePosixPath(self._path) /
|
|
167
|
+
path=PurePosixPath(self._path) / path.lstrip("/"),
|
|
163
168
|
git_ref=self._git_ref,
|
|
164
169
|
)
|
|
165
170
|
|
snowflake/cli/api/utils/graph.py
CHANGED
|
@@ -51,6 +51,9 @@ class Graph(Generic[T]):
|
|
|
51
51
|
def get_all_nodes(self) -> set[Node[T]]:
|
|
52
52
|
return set(self._graph_nodes_map.values())
|
|
53
53
|
|
|
54
|
+
def contains_node(self, key: str) -> bool:
|
|
55
|
+
return self.__contains__(key)
|
|
56
|
+
|
|
54
57
|
def add(self, node: Node[T]) -> None:
|
|
55
58
|
if node.key in self._graph_nodes_map:
|
|
56
59
|
raise KeyError(f"Node key {node.key} already exists")
|
|
@@ -14,7 +14,11 @@
|
|
|
14
14
|
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
|
+
import os
|
|
17
18
|
import sys
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
from snowflake.cli.api.secure_path import SecurePath
|
|
18
22
|
|
|
19
23
|
BUFFER_SIZE = 4096
|
|
20
24
|
|
|
@@ -34,3 +38,23 @@ def path_resolver(path_to_file: str) -> str:
|
|
|
34
38
|
|
|
35
39
|
def is_stage_path(path: str) -> bool:
|
|
36
40
|
return path.startswith("@") or path.startswith("snow://")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def delete(path: Path) -> None:
|
|
44
|
+
"""
|
|
45
|
+
Obliterates whatever is at the given path, or is a no-op if the
|
|
46
|
+
given path does not represent a file or directory that exists.
|
|
47
|
+
"""
|
|
48
|
+
spath = SecurePath(path)
|
|
49
|
+
if spath.path.is_file():
|
|
50
|
+
spath.unlink() # remove the file
|
|
51
|
+
elif spath.path.is_dir():
|
|
52
|
+
spath.rmdir(recursive=True) # remove dir and all contains
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def resolve_without_follow(path: Path) -> Path:
|
|
56
|
+
"""
|
|
57
|
+
Resolves a Path to an absolute version of itself, without following
|
|
58
|
+
symlinks like Path.resolve() does.
|
|
59
|
+
"""
|
|
60
|
+
return Path(os.path.abspath(path))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: snowflake-cli
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.4.1
|
|
4
4
|
Summary: Snowflake CLI
|
|
5
5
|
Project-URL: Source code, https://github.com/snowflakedb/snowflake-cli
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/snowflakedb/snowflake-cli/issues
|
|
@@ -217,32 +217,31 @@ Classifier: Programming Language :: Python :: 3 :: Only
|
|
|
217
217
|
Classifier: Programming Language :: SQL
|
|
218
218
|
Classifier: Topic :: Database
|
|
219
219
|
Requires-Python: >=3.10
|
|
220
|
-
Requires-Dist:
|
|
221
|
-
Requires-Dist: gitpython==3.1.43
|
|
220
|
+
Requires-Dist: gitpython==3.1.44
|
|
222
221
|
Requires-Dist: jinja2==3.1.5
|
|
223
222
|
Requires-Dist: packaging
|
|
224
223
|
Requires-Dist: pip
|
|
225
224
|
Requires-Dist: pluggy==1.5.0
|
|
226
|
-
Requires-Dist: pydantic==2.
|
|
225
|
+
Requires-Dist: pydantic==2.10.4
|
|
227
226
|
Requires-Dist: pyyaml==6.0.2
|
|
228
227
|
Requires-Dist: requests==2.32.3
|
|
229
228
|
Requires-Dist: requirements-parser==0.11.0
|
|
230
229
|
Requires-Dist: rich==13.9.4
|
|
231
|
-
Requires-Dist: setuptools==75.
|
|
232
|
-
Requires-Dist: snowflake-connector-python[secure-local-storage]==3.
|
|
230
|
+
Requires-Dist: setuptools==75.8.0
|
|
231
|
+
Requires-Dist: snowflake-connector-python[secure-local-storage]==3.13.2
|
|
233
232
|
Requires-Dist: snowflake-core==1.0.2; python_version < '3.12'
|
|
234
233
|
Requires-Dist: snowflake-snowpark-python<1.26.0,>=1.15.0; python_version < '3.12'
|
|
235
234
|
Requires-Dist: tomlkit==0.13.2
|
|
236
235
|
Requires-Dist: typer==0.12.5
|
|
237
|
-
Requires-Dist: urllib3<2.
|
|
236
|
+
Requires-Dist: urllib3<2.4,>=1.24.3
|
|
238
237
|
Provides-Extra: development
|
|
239
238
|
Requires-Dist: coverage==7.6.10; extra == 'development'
|
|
240
239
|
Requires-Dist: factory-boy==3.3.1; extra == 'development'
|
|
241
|
-
Requires-Dist: faker==
|
|
240
|
+
Requires-Dist: faker==35.2.0; extra == 'development'
|
|
242
241
|
Requires-Dist: pre-commit>=3.5.0; extra == 'development'
|
|
243
242
|
Requires-Dist: pytest-randomly==3.16.0; extra == 'development'
|
|
244
243
|
Requires-Dist: pytest==8.3.4; extra == 'development'
|
|
245
|
-
Requires-Dist: syrupy==4.8.
|
|
244
|
+
Requires-Dist: syrupy==4.8.1; extra == 'development'
|
|
246
245
|
Provides-Extra: packaging
|
|
247
246
|
Requires-Dist: pyinstaller~=6.10; extra == 'packaging'
|
|
248
247
|
Description-Content-Type: text/markdown
|