snowflake-cli-labs 3.0.0rc4__py3-none-any.whl → 3.0.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 (244) hide show
  1. README.md +21 -0
  2. {snowflake_cli_labs-3.0.0rc4.dist-info → snowflake_cli_labs-3.0.1.dist-info}/METADATA +6 -96
  3. snowflake_cli_labs-3.0.1.dist-info/RECORD +5 -0
  4. snowflake/cli/__about__.py +0 -17
  5. snowflake/cli/__init__.py +0 -13
  6. snowflake/cli/_app/__init__.py +0 -22
  7. snowflake/cli/_app/__main__.py +0 -31
  8. snowflake/cli/_app/api_impl/__init__.py +0 -13
  9. snowflake/cli/_app/api_impl/plugin/__init__.py +0 -13
  10. snowflake/cli/_app/api_impl/plugin/plugin_config_provider_impl.py +0 -66
  11. snowflake/cli/_app/cli_app.py +0 -252
  12. snowflake/cli/_app/commands_registration/__init__.py +0 -33
  13. snowflake/cli/_app/commands_registration/builtin_plugins.py +0 -50
  14. snowflake/cli/_app/commands_registration/command_plugins_loader.py +0 -169
  15. snowflake/cli/_app/commands_registration/commands_registration_with_callbacks.py +0 -105
  16. snowflake/cli/_app/commands_registration/exception_logging.py +0 -26
  17. snowflake/cli/_app/commands_registration/threadsafe.py +0 -48
  18. snowflake/cli/_app/commands_registration/typer_registration.py +0 -153
  19. snowflake/cli/_app/constants.py +0 -19
  20. snowflake/cli/_app/dev/__init__.py +0 -13
  21. snowflake/cli/_app/dev/commands_structure.py +0 -48
  22. snowflake/cli/_app/dev/docs/__init__.py +0 -13
  23. snowflake/cli/_app/dev/docs/commands_docs_generator.py +0 -118
  24. snowflake/cli/_app/dev/docs/generator.py +0 -35
  25. snowflake/cli/_app/dev/docs/project_definition_docs_generator.py +0 -58
  26. snowflake/cli/_app/dev/docs/project_definition_generate_json_schema.py +0 -227
  27. snowflake/cli/_app/dev/docs/template_utils.py +0 -23
  28. snowflake/cli/_app/dev/docs/templates/definition_description.rst.jinja2 +0 -38
  29. snowflake/cli/_app/dev/docs/templates/overview.rst.jinja2 +0 -9
  30. snowflake/cli/_app/dev/docs/templates/usage.rst.jinja2 +0 -67
  31. snowflake/cli/_app/dev/pycharm_remote_debug.py +0 -46
  32. snowflake/cli/_app/loggers.py +0 -199
  33. snowflake/cli/_app/main_typer.py +0 -62
  34. snowflake/cli/_app/printing.py +0 -181
  35. snowflake/cli/_app/secret.py +0 -9
  36. snowflake/cli/_app/snow_connector.py +0 -309
  37. snowflake/cli/_app/telemetry.py +0 -220
  38. snowflake/cli/_app/version_check.py +0 -74
  39. snowflake/cli/_plugins/__init__.py +0 -13
  40. snowflake/cli/_plugins/connection/__init__.py +0 -13
  41. snowflake/cli/_plugins/connection/commands.py +0 -353
  42. snowflake/cli/_plugins/connection/plugin_spec.py +0 -30
  43. snowflake/cli/_plugins/connection/util.py +0 -195
  44. snowflake/cli/_plugins/cortex/__init__.py +0 -13
  45. snowflake/cli/_plugins/cortex/commands.py +0 -332
  46. snowflake/cli/_plugins/cortex/constants.py +0 -17
  47. snowflake/cli/_plugins/cortex/manager.py +0 -189
  48. snowflake/cli/_plugins/cortex/plugin_spec.py +0 -30
  49. snowflake/cli/_plugins/cortex/types.py +0 -22
  50. snowflake/cli/_plugins/git/__init__.py +0 -13
  51. snowflake/cli/_plugins/git/commands.py +0 -358
  52. snowflake/cli/_plugins/git/manager.py +0 -151
  53. snowflake/cli/_plugins/git/plugin_spec.py +0 -30
  54. snowflake/cli/_plugins/helpers/__init__.py +0 -13
  55. snowflake/cli/_plugins/helpers/commands.py +0 -61
  56. snowflake/cli/_plugins/helpers/plugin_spec.py +0 -30
  57. snowflake/cli/_plugins/init/__init__.py +0 -13
  58. snowflake/cli/_plugins/init/commands.py +0 -248
  59. snowflake/cli/_plugins/init/plugin_spec.py +0 -30
  60. snowflake/cli/_plugins/nativeapp/__init__.py +0 -13
  61. snowflake/cli/_plugins/nativeapp/artifacts.py +0 -757
  62. snowflake/cli/_plugins/nativeapp/bundle_context.py +0 -31
  63. snowflake/cli/_plugins/nativeapp/codegen/__init__.py +0 -13
  64. snowflake/cli/_plugins/nativeapp/codegen/artifact_processor.py +0 -91
  65. snowflake/cli/_plugins/nativeapp/codegen/compiler.py +0 -149
  66. snowflake/cli/_plugins/nativeapp/codegen/sandbox.py +0 -306
  67. snowflake/cli/_plugins/nativeapp/codegen/setup/native_app_setup_processor.py +0 -249
  68. snowflake/cli/_plugins/nativeapp/codegen/setup/setup_driver.py.source +0 -59
  69. snowflake/cli/_plugins/nativeapp/codegen/snowpark/callback_source.py.jinja +0 -181
  70. snowflake/cli/_plugins/nativeapp/codegen/snowpark/extension_function_utils.py +0 -217
  71. snowflake/cli/_plugins/nativeapp/codegen/snowpark/models.py +0 -61
  72. snowflake/cli/_plugins/nativeapp/codegen/snowpark/python_processor.py +0 -523
  73. snowflake/cli/_plugins/nativeapp/codegen/templates/templates_processor.py +0 -114
  74. snowflake/cli/_plugins/nativeapp/commands.py +0 -559
  75. snowflake/cli/_plugins/nativeapp/common_flags.py +0 -44
  76. snowflake/cli/_plugins/nativeapp/constants.py +0 -27
  77. snowflake/cli/_plugins/nativeapp/entities/__init__.py +0 -0
  78. snowflake/cli/_plugins/nativeapp/entities/application.py +0 -878
  79. snowflake/cli/_plugins/nativeapp/entities/application_package.py +0 -1392
  80. snowflake/cli/_plugins/nativeapp/exceptions.py +0 -113
  81. snowflake/cli/_plugins/nativeapp/feature_flags.py +0 -24
  82. snowflake/cli/_plugins/nativeapp/manager.py +0 -415
  83. snowflake/cli/_plugins/nativeapp/plugin_spec.py +0 -30
  84. snowflake/cli/_plugins/nativeapp/policy.py +0 -53
  85. snowflake/cli/_plugins/nativeapp/project_model.py +0 -211
  86. snowflake/cli/_plugins/nativeapp/run_processor.py +0 -184
  87. snowflake/cli/_plugins/nativeapp/same_account_install_method.py +0 -70
  88. snowflake/cli/_plugins/nativeapp/teardown_processor.py +0 -70
  89. snowflake/cli/_plugins/nativeapp/utils.py +0 -98
  90. snowflake/cli/_plugins/nativeapp/v2_conversions/v2_to_v1_decorator.py +0 -262
  91. snowflake/cli/_plugins/nativeapp/version/__init__.py +0 -13
  92. snowflake/cli/_plugins/nativeapp/version/commands.py +0 -141
  93. snowflake/cli/_plugins/nativeapp/version/version_processor.py +0 -98
  94. snowflake/cli/_plugins/notebook/__init__.py +0 -13
  95. snowflake/cli/_plugins/notebook/commands.py +0 -86
  96. snowflake/cli/_plugins/notebook/exceptions.py +0 -20
  97. snowflake/cli/_plugins/notebook/manager.py +0 -71
  98. snowflake/cli/_plugins/notebook/plugin_spec.py +0 -30
  99. snowflake/cli/_plugins/notebook/types.py +0 -15
  100. snowflake/cli/_plugins/object/__init__.py +0 -13
  101. snowflake/cli/_plugins/object/command_aliases.py +0 -95
  102. snowflake/cli/_plugins/object/commands.py +0 -180
  103. snowflake/cli/_plugins/object/common.py +0 -85
  104. snowflake/cli/_plugins/object/manager.py +0 -118
  105. snowflake/cli/_plugins/object/plugin_spec.py +0 -30
  106. snowflake/cli/_plugins/snowpark/__init__.py +0 -13
  107. snowflake/cli/_plugins/snowpark/commands.py +0 -450
  108. snowflake/cli/_plugins/snowpark/common.py +0 -268
  109. snowflake/cli/_plugins/snowpark/models.py +0 -150
  110. snowflake/cli/_plugins/snowpark/package/__init__.py +0 -13
  111. snowflake/cli/_plugins/snowpark/package/anaconda_packages.py +0 -199
  112. snowflake/cli/_plugins/snowpark/package/commands.py +0 -195
  113. snowflake/cli/_plugins/snowpark/package/manager.py +0 -44
  114. snowflake/cli/_plugins/snowpark/package/utils.py +0 -26
  115. snowflake/cli/_plugins/snowpark/package_utils.py +0 -354
  116. snowflake/cli/_plugins/snowpark/plugin_spec.py +0 -30
  117. snowflake/cli/_plugins/snowpark/snowpark_entity.py +0 -29
  118. snowflake/cli/_plugins/snowpark/snowpark_entity_model.py +0 -173
  119. snowflake/cli/_plugins/snowpark/snowpark_project_paths.py +0 -109
  120. snowflake/cli/_plugins/snowpark/snowpark_shared.py +0 -59
  121. snowflake/cli/_plugins/snowpark/zipper.py +0 -89
  122. snowflake/cli/_plugins/spcs/__init__.py +0 -33
  123. snowflake/cli/_plugins/spcs/common.py +0 -99
  124. snowflake/cli/_plugins/spcs/compute_pool/__init__.py +0 -13
  125. snowflake/cli/_plugins/spcs/compute_pool/commands.py +0 -241
  126. snowflake/cli/_plugins/spcs/compute_pool/manager.py +0 -121
  127. snowflake/cli/_plugins/spcs/image_registry/__init__.py +0 -13
  128. snowflake/cli/_plugins/spcs/image_registry/commands.py +0 -65
  129. snowflake/cli/_plugins/spcs/image_registry/manager.py +0 -105
  130. snowflake/cli/_plugins/spcs/image_repository/__init__.py +0 -13
  131. snowflake/cli/_plugins/spcs/image_repository/commands.py +0 -202
  132. snowflake/cli/_plugins/spcs/image_repository/manager.py +0 -84
  133. snowflake/cli/_plugins/spcs/plugin_spec.py +0 -30
  134. snowflake/cli/_plugins/spcs/services/__init__.py +0 -13
  135. snowflake/cli/_plugins/spcs/services/commands.py +0 -345
  136. snowflake/cli/_plugins/spcs/services/manager.py +0 -208
  137. snowflake/cli/_plugins/sql/__init__.py +0 -13
  138. snowflake/cli/_plugins/sql/commands.py +0 -86
  139. snowflake/cli/_plugins/sql/manager.py +0 -92
  140. snowflake/cli/_plugins/sql/plugin_spec.py +0 -30
  141. snowflake/cli/_plugins/sql/snowsql_templating.py +0 -28
  142. snowflake/cli/_plugins/stage/__init__.py +0 -13
  143. snowflake/cli/_plugins/stage/commands.py +0 -264
  144. snowflake/cli/_plugins/stage/diff.py +0 -280
  145. snowflake/cli/_plugins/stage/manager.py +0 -582
  146. snowflake/cli/_plugins/stage/md5.py +0 -160
  147. snowflake/cli/_plugins/stage/plugin_spec.py +0 -30
  148. snowflake/cli/_plugins/stage/utils.py +0 -54
  149. snowflake/cli/_plugins/streamlit/__init__.py +0 -13
  150. snowflake/cli/_plugins/streamlit/commands.py +0 -195
  151. snowflake/cli/_plugins/streamlit/manager.py +0 -220
  152. snowflake/cli/_plugins/streamlit/plugin_spec.py +0 -30
  153. snowflake/cli/_plugins/streamlit/streamlit_entity.py +0 -12
  154. snowflake/cli/_plugins/streamlit/streamlit_entity_model.py +0 -66
  155. snowflake/cli/_plugins/workspace/__init__.py +0 -13
  156. snowflake/cli/_plugins/workspace/action_context.py +0 -18
  157. snowflake/cli/_plugins/workspace/commands.py +0 -306
  158. snowflake/cli/_plugins/workspace/manager.py +0 -74
  159. snowflake/cli/_plugins/workspace/plugin_spec.py +0 -30
  160. snowflake/cli/api/__init__.py +0 -48
  161. snowflake/cli/api/cli_global_context.py +0 -247
  162. snowflake/cli/api/commands/__init__.py +0 -13
  163. snowflake/cli/api/commands/alias.py +0 -23
  164. snowflake/cli/api/commands/common.py +0 -25
  165. snowflake/cli/api/commands/decorators.py +0 -369
  166. snowflake/cli/api/commands/execution_metadata.py +0 -40
  167. snowflake/cli/api/commands/experimental_behaviour.py +0 -18
  168. snowflake/cli/api/commands/flags.py +0 -561
  169. snowflake/cli/api/commands/overrideable_parameter.py +0 -143
  170. snowflake/cli/api/commands/snow_typer.py +0 -247
  171. snowflake/cli/api/commands/utils.py +0 -18
  172. snowflake/cli/api/config.py +0 -380
  173. snowflake/cli/api/connections.py +0 -216
  174. snowflake/cli/api/console/__init__.py +0 -17
  175. snowflake/cli/api/console/abc.py +0 -94
  176. snowflake/cli/api/console/console.py +0 -134
  177. snowflake/cli/api/console/enum.py +0 -17
  178. snowflake/cli/api/constants.py +0 -90
  179. snowflake/cli/api/entities/common.py +0 -56
  180. snowflake/cli/api/entities/utils.py +0 -370
  181. snowflake/cli/api/errno.py +0 -28
  182. snowflake/cli/api/exceptions.py +0 -190
  183. snowflake/cli/api/feature_flags.py +0 -54
  184. snowflake/cli/api/identifiers.py +0 -190
  185. snowflake/cli/api/metrics.py +0 -92
  186. snowflake/cli/api/output/__init__.py +0 -13
  187. snowflake/cli/api/output/formats.py +0 -20
  188. snowflake/cli/api/output/types.py +0 -118
  189. snowflake/cli/api/plugins/__init__.py +0 -13
  190. snowflake/cli/api/plugins/command/__init__.py +0 -72
  191. snowflake/cli/api/plugins/command/plugin_hook_specs.py +0 -21
  192. snowflake/cli/api/plugins/plugin_config.py +0 -32
  193. snowflake/cli/api/project/__init__.py +0 -13
  194. snowflake/cli/api/project/definition.py +0 -126
  195. snowflake/cli/api/project/definition_conversion.py +0 -395
  196. snowflake/cli/api/project/definition_manager.py +0 -145
  197. snowflake/cli/api/project/errors.py +0 -56
  198. snowflake/cli/api/project/project_verification.py +0 -23
  199. snowflake/cli/api/project/schemas/__init__.py +0 -13
  200. snowflake/cli/api/project/schemas/entities/__init__.py +0 -13
  201. snowflake/cli/api/project/schemas/entities/common.py +0 -153
  202. snowflake/cli/api/project/schemas/entities/entities.py +0 -61
  203. snowflake/cli/api/project/schemas/project_definition.py +0 -330
  204. snowflake/cli/api/project/schemas/template.py +0 -77
  205. snowflake/cli/api/project/schemas/updatable_model.py +0 -202
  206. snowflake/cli/api/project/schemas/v1/__init__.py +0 -0
  207. snowflake/cli/api/project/schemas/v1/identifier_model.py +0 -51
  208. snowflake/cli/api/project/schemas/v1/native_app/__init__.py +0 -0
  209. snowflake/cli/api/project/schemas/v1/native_app/application.py +0 -61
  210. snowflake/cli/api/project/schemas/v1/native_app/native_app.py +0 -93
  211. snowflake/cli/api/project/schemas/v1/native_app/package.py +0 -84
  212. snowflake/cli/api/project/schemas/v1/native_app/path_mapping.py +0 -65
  213. snowflake/cli/api/project/schemas/v1/snowpark/__init__.py +0 -0
  214. snowflake/cli/api/project/schemas/v1/snowpark/argument.py +0 -28
  215. snowflake/cli/api/project/schemas/v1/snowpark/callable.py +0 -69
  216. snowflake/cli/api/project/schemas/v1/snowpark/snowpark.py +0 -36
  217. snowflake/cli/api/project/schemas/v1/streamlit/__init__.py +0 -0
  218. snowflake/cli/api/project/schemas/v1/streamlit/streamlit.py +0 -47
  219. snowflake/cli/api/project/util.py +0 -278
  220. snowflake/cli/api/rendering/__init__.py +0 -13
  221. snowflake/cli/api/rendering/jinja.py +0 -118
  222. snowflake/cli/api/rendering/project_definition_templates.py +0 -43
  223. snowflake/cli/api/rendering/project_templates.py +0 -98
  224. snowflake/cli/api/rendering/sql_templates.py +0 -105
  225. snowflake/cli/api/rest_api.py +0 -178
  226. snowflake/cli/api/sanitizers.py +0 -43
  227. snowflake/cli/api/secure_path.py +0 -360
  228. snowflake/cli/api/secure_utils.py +0 -118
  229. snowflake/cli/api/sql_execution.py +0 -280
  230. snowflake/cli/api/utils/__init__.py +0 -13
  231. snowflake/cli/api/utils/cursor.py +0 -34
  232. snowflake/cli/api/utils/definition_rendering.py +0 -415
  233. snowflake/cli/api/utils/dict_utils.py +0 -73
  234. snowflake/cli/api/utils/error_handling.py +0 -23
  235. snowflake/cli/api/utils/graph.py +0 -97
  236. snowflake/cli/api/utils/models.py +0 -63
  237. snowflake/cli/api/utils/naming_utils.py +0 -13
  238. snowflake/cli/api/utils/path_utils.py +0 -36
  239. snowflake/cli/api/utils/templating_functions.py +0 -144
  240. snowflake/cli/api/utils/types.py +0 -35
  241. snowflake_cli_labs-3.0.0rc4.dist-info/RECORD +0 -242
  242. snowflake_cli_labs-3.0.0rc4.dist-info/entry_points.txt +0 -2
  243. {snowflake_cli_labs-3.0.0rc4.dist-info → snowflake_cli_labs-3.0.1.dist-info}/WHEEL +0 -0
  244. {snowflake_cli_labs-3.0.0rc4.dist-info → snowflake_cli_labs-3.0.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,757 +0,0 @@
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
-
15
- from __future__ import annotations
16
-
17
- import itertools
18
- import os
19
- from pathlib import Path
20
- from textwrap import dedent
21
- from typing import Any, Callable, Dict, Iterable, Iterator, List, Optional, Tuple, Union
22
-
23
- from click.exceptions import ClickException
24
- from snowflake.cli.api.constants import DEFAULT_SIZE_LIMIT_MB
25
- from snowflake.cli.api.project.schemas.v1.native_app.path_mapping import PathMapping
26
- from snowflake.cli.api.project.util import to_identifier
27
- from snowflake.cli.api.secure_path import SecurePath
28
- from yaml import safe_load
29
-
30
-
31
- class DeployRootError(ClickException):
32
- """
33
- The deploy root was incorrectly specified.
34
- """
35
-
36
- def __init__(self, msg: str):
37
- super().__init__(msg)
38
-
39
-
40
- class ArtifactError(ClickException):
41
- """
42
- Could not parse source or destination artifact.
43
- """
44
-
45
- def __init__(self, msg: str):
46
- super().__init__(msg)
47
-
48
-
49
- class SourceNotFoundError(ClickException):
50
- """
51
- No match was found for the specified source in the project directory
52
- """
53
-
54
- def __init__(self, src: Union[str, Path]):
55
- super().__init__(f"{dedent(str(self.__doc__))}: {src}".strip())
56
-
57
-
58
- class TooManyFilesError(ClickException):
59
- """
60
- Multiple file or directories were mapped to one output destination.
61
- """
62
-
63
- dest_path: Path
64
-
65
- def __init__(self, dest_path: Path):
66
- super().__init__(
67
- f"{dedent(str(self.__doc__))}\ndestination = {dest_path}".strip()
68
- )
69
- self.dest_path = dest_path
70
-
71
-
72
- class NotInDeployRootError(ClickException):
73
- """
74
- The specified destination path is outside of the deploy root, or
75
- would entirely replace it. This can happen when a relative path
76
- with ".." is provided, or when "." is used as the destination
77
- (use "./" instead to copy into the deploy root).
78
- """
79
-
80
- dest_path: Union[str, Path]
81
- deploy_root: Path
82
- src_path: Optional[Union[str, Path]]
83
-
84
- def __init__(
85
- self,
86
- *,
87
- dest_path: Union[Path, str],
88
- deploy_root: Path,
89
- src_path: Optional[Union[str, Path]] = None,
90
- ):
91
- message = dedent(str(self.__doc__))
92
- message += f"\ndestination = {dest_path}"
93
- message += f"\ndeploy root = {deploy_root}"
94
- if src_path is not None:
95
- message += f"""\nsource = {src_path}"""
96
- super().__init__(message.strip())
97
- self.dest_path = dest_path
98
- self.deploy_root = deploy_root
99
- self.src_path = src_path
100
-
101
-
102
- ArtifactPredicate = Callable[[Path, Path], bool]
103
-
104
-
105
- class _ArtifactPathMap:
106
- """
107
- A specialized version of an ordered multimap used to keep track of artifact
108
- source-destination mappings. The mapping is bidirectional, so it can be queried
109
- by source or destination paths. All paths manipulated by this class must be in
110
- relative, canonical form (relative to the project or deploy roots, as appropriate).
111
- """
112
-
113
- def __init__(self, project_root: Path):
114
- self._project_root = project_root
115
-
116
- # All (src,dest) pairs in inserting order, for iterating
117
- self.__src_dest_pairs: List[Tuple[Path, Path]] = []
118
- # built-in dict instances are ordered as of Python 3.7
119
- self.__src_to_dest: Dict[Path, List[Path]] = {}
120
- self.__dest_to_src: Dict[Path, Optional[Path]] = {}
121
-
122
- # This dictionary accumulates keys for each directory or file to be created in
123
- # the deploy root for any artifact mapping rule being processed. This includes
124
- # children of directories that are copied to the deploy root. Having this
125
- # information available is critical to detect possible clashes between rules.
126
- self._dest_is_dir: Dict[Path, bool] = {}
127
-
128
- def put(self, src: Path, dest: Path, dest_is_dir: bool) -> None:
129
- """
130
- Adds a new source-destination mapping pair to this map, if necessary. Note that
131
- this is internal logic that assumes that src-dest pairs have already been preprocessed
132
- by the enclosing BundleMap (for example, only file -> file and
133
- directory -> directory mappings are possible here due to the preprocessing step).
134
-
135
- Arguments:
136
- src {Path} -- the source path, in canonical form.
137
- dest {Path} -- the destination path, in canonical form.
138
- dest_is_dir {bool} -- whether the destination path is a directory.
139
- """
140
- # Both paths should be in canonical form
141
- assert not src.is_absolute()
142
- assert not dest.is_absolute()
143
-
144
- absolute_src = self._project_root / src
145
-
146
- current_source = self.__dest_to_src.get(dest)
147
- src_is_dir = absolute_src.is_dir()
148
- if dest_is_dir:
149
- assert src_is_dir # file -> directory is not possible here given how rules are processed
150
-
151
- # directory -> directory
152
- # Check that dest is currently unmapped
153
- current_is_dir = self._dest_is_dir.get(dest, False)
154
- if current_is_dir:
155
- # mapping to an existing directory is not allowed
156
- raise TooManyFilesError(dest)
157
- else:
158
- # file -> file
159
- # Check that there is no previous mapping for the same file.
160
- if current_source is not None and current_source != src:
161
- # There is already a different source mapping to this destination
162
- raise TooManyFilesError(dest)
163
-
164
- if src_is_dir:
165
- # mark all subdirectories of this source as directories so that we can
166
- # detect accidental clobbering
167
- for (root, _, files) in os.walk(absolute_src, followlinks=True):
168
- canonical_subdir = Path(root).relative_to(absolute_src)
169
- canonical_dest_subdir = dest / canonical_subdir
170
- self._update_dest_is_dir(canonical_dest_subdir, is_dir=True)
171
- for f in files:
172
- self._update_dest_is_dir(canonical_dest_subdir / f, is_dir=False)
173
-
174
- # make sure we check for dest_is_dir consistency regardless of whether the
175
- # insertion happened. This update can fail, so we need to do it first to
176
- # avoid applying partial updates to the underlying data storage.
177
- self._update_dest_is_dir(dest, dest_is_dir)
178
-
179
- dests = self.__src_to_dest.setdefault(src, [])
180
- if dest not in dests:
181
- dests.append(dest)
182
- self.__dest_to_src[dest] = src
183
- self.__src_dest_pairs.append((src, dest))
184
-
185
- def get_source(self, dest: Path) -> Optional[Path]:
186
- """
187
- Returns the source path associated with the provided destination path, if any.
188
- """
189
- return self.__dest_to_src.get(dest)
190
-
191
- def get_destinations(self, src: Path) -> Iterable[Path]:
192
- """
193
- Returns all destination paths associated with the provided source path, in insertion order.
194
- """
195
- return self.__src_to_dest.get(src, [])
196
-
197
- def all_sources(self) -> Iterable[Path]:
198
- """
199
- Returns all source paths associated with this map, in insertion order.
200
- """
201
- return self.__src_to_dest.keys()
202
-
203
- def is_empty(self) -> bool:
204
- """
205
- Returns True if this map has no source-destination mappings.
206
- """
207
- return len(self.__src_dest_pairs) == 0
208
-
209
- def __iter__(self) -> Iterator[Tuple[Path, Path]]:
210
- """
211
- Returns all (source, destination) pairs known to this map, in insertion order.
212
- """
213
- return iter(self.__src_dest_pairs)
214
-
215
- def _update_dest_is_dir(self, dest: Path, is_dir: bool) -> None:
216
- """
217
- Recursively marks seen destination paths as either files or folders, raising an error if any inconsistencies
218
- from previous invocations of this method are encountered.
219
-
220
- Arguments:
221
- dest {Path} -- the destination path, in canonical form.
222
- is_dir {bool} -- whether the destination path is a directory.
223
- """
224
- assert not dest.is_absolute() # dest must be in canonical relative form
225
-
226
- current_is_dir = self._dest_is_dir.get(dest, None)
227
- if current_is_dir is not None and current_is_dir != is_dir:
228
- raise ArtifactError(
229
- "Conflicting type for destination path: {canonical_dest}"
230
- )
231
-
232
- parent = dest.parent
233
- if parent != dest:
234
- self._update_dest_is_dir(parent, True)
235
-
236
- self._dest_is_dir[dest] = is_dir
237
-
238
-
239
- class BundleMap:
240
- """
241
- Computes the mapping between project directory artifacts (aka source artifacts) to their deploy root location
242
- (aka destination artifact). This information is primarily used when bundling a native applications project.
243
- """
244
-
245
- def __init__(self, *, project_root: Path, deploy_root: Path):
246
- self._project_root: Path = resolve_without_follow(project_root)
247
- self._deploy_root: Path = resolve_without_follow(deploy_root)
248
- self._artifact_map = _ArtifactPathMap(project_root=self._project_root)
249
-
250
- def is_empty(self) -> bool:
251
- return self._artifact_map.is_empty()
252
-
253
- def deploy_root(self) -> Path:
254
- return self._deploy_root
255
-
256
- def project_root(self) -> Path:
257
- return self._project_root
258
-
259
- def _add(self, src: Path, dest: Path, map_as_child: bool) -> None:
260
- """
261
- Adds the specified artifact mapping rule to this map.
262
-
263
- Arguments:
264
- src {Path} -- the source path
265
- dest {Path} -- the destination path
266
- map_as_child {bool} -- when True, the source will be added as a child of the specified destination.
267
- """
268
- absolute_src = self._absolute_src(src)
269
- absolute_dest = self._absolute_dest(dest, src_path=src)
270
- dest_is_dir = absolute_src.is_dir() or map_as_child
271
-
272
- # Check for the special case of './' as a target ('.' is not allowed)
273
- if absolute_dest == self._deploy_root and not map_as_child:
274
- raise NotInDeployRootError(
275
- dest_path=dest, deploy_root=self._deploy_root, src_path=src
276
- )
277
-
278
- if self._deploy_root in absolute_src.parents:
279
- # ignore this item since it's in the deploy root. This can happen if the bundle map is created
280
- # after the bundle step and a project is using rules that are not sufficiently constrained.
281
- # Since the bundle step starts with deleting the deploy root, we wouldn't normally encounter this situation.
282
- return
283
-
284
- canonical_src = self._canonical_src(src)
285
- canonical_dest = self._canonical_dest(dest)
286
-
287
- if map_as_child:
288
- # Make sure the destination is a child of the original, since this was requested
289
- canonical_dest = canonical_dest / canonical_src.name
290
- dest_is_dir = absolute_src.is_dir()
291
-
292
- self._artifact_map.put(
293
- src=canonical_src, dest=canonical_dest, dest_is_dir=dest_is_dir
294
- )
295
-
296
- def _add_mapping(self, src: str, dest: Optional[str] = None):
297
- """
298
- Adds the specified artifact rule to this instance. The source should be relative to the project directory. It
299
- is interpreted as a file, directory or glob pattern. If the destination path is not specified, each source match
300
- is mapped to an identical path in the deploy root.
301
- """
302
- match_found = False
303
-
304
- src_path = Path(src)
305
- if src_path.is_absolute():
306
- raise ArtifactError("Source path must be a relative path")
307
-
308
- for resolved_src in self._project_root.glob(src):
309
- match_found = True
310
-
311
- if dest:
312
- dest_stem = dest.rstrip("/")
313
- if not dest_stem:
314
- # handle '/' as the destination as a special case. This is because specifying only '/' as a
315
- # a destination looks like '.' once all forwards slashes are stripped. If we don't handle it
316
- # specially here, `dest: /` would incorrectly be allowed.
317
- raise NotInDeployRootError(
318
- dest_path=dest,
319
- deploy_root=self._deploy_root,
320
- src_path=resolved_src,
321
- )
322
- dest_path = Path(dest.rstrip("/"))
323
- if dest_path.is_absolute():
324
- raise ArtifactError("Destination path must be a relative path")
325
- self._add(resolved_src, dest_path, specifies_directory(dest))
326
- else:
327
- self._add(
328
- resolved_src,
329
- resolved_src.relative_to(self._project_root),
330
- False,
331
- )
332
-
333
- if not match_found:
334
- raise SourceNotFoundError(src)
335
-
336
- def add(self, mapping: PathMapping) -> None:
337
- """
338
- Adds an artifact mapping rule to this instance.
339
- """
340
- self._add_mapping(mapping.src, mapping.dest)
341
-
342
- def _expand_artifact_mapping(
343
- self,
344
- src: Path,
345
- dest: Path,
346
- absolute: bool = False,
347
- expand_directories: bool = False,
348
- predicate: ArtifactPredicate = lambda src, dest: True,
349
- ) -> Iterator[Tuple[Path, Path]]:
350
- """
351
- Expands the specified source-destination mapping according to the provided options.
352
- The original mapping is yielded, followed by any expanded mappings derived from
353
- it.
354
-
355
- Arguments:
356
- src {Path} -- the source path
357
- dest {Path} -- the destination path
358
- absolute {bool} -- when True, all mappings will be yielded as absolute paths
359
- expand_directories {bool} -- when True, child mappings are yielded if the source path is a directory.
360
- predicate {ArtifactPredicate} -- when specified, only mappings satisfying this predicate will be yielded.
361
- """
362
- canonical_src = self._canonical_src(src)
363
- canonical_dest = self._canonical_dest(dest)
364
-
365
- absolute_src = self._absolute_src(canonical_src)
366
- absolute_dest = self._absolute_dest(canonical_dest)
367
- src_for_output = self._to_output_src(absolute_src, absolute)
368
- dest_for_output = self._to_output_dest(absolute_dest, absolute)
369
-
370
- if predicate(src_for_output, dest_for_output):
371
- yield src_for_output, dest_for_output
372
-
373
- if absolute_src.is_dir() and expand_directories:
374
- # both src and dest are directories, and expanding directories was requested. Traverse src, and map each
375
- # file to the dest directory
376
- for (root, subdirs, files) in os.walk(absolute_src, followlinks=True):
377
- relative_root = Path(root).relative_to(absolute_src)
378
- for name in itertools.chain(subdirs, files):
379
- src_file_for_output = src_for_output / relative_root / name
380
- dest_file_for_output = dest_for_output / relative_root / name
381
- if predicate(src_file_for_output, dest_file_for_output):
382
- yield src_file_for_output, dest_file_for_output
383
-
384
- def all_mappings(
385
- self,
386
- absolute: bool = False,
387
- expand_directories: bool = False,
388
- predicate: ArtifactPredicate = lambda src, dest: True,
389
- ) -> Iterator[Tuple[Path, Path]]:
390
- """
391
- Yields a (src, dest) pair for each deployed artifact in the project. Each pair corresponds to a single file
392
- in the project. Source directories are resolved as needed to resolve their contents.
393
-
394
- Arguments:
395
- self: this instance
396
- absolute (bool): Specifies whether the yielded paths should be joined with the project or deploy roots,
397
- as appropriate.
398
- expand_directories (bool): Specifies whether directory to directory mappings should be expanded to
399
- resolve their contained files.
400
- predicate (PathPredicate): If provided, the predicate is invoked with both the source path and the
401
- destination path as arguments. Only pairs selected by the predicate are returned.
402
-
403
- Returns:
404
- An iterator over all matching deployed artifacts.
405
- """
406
- for src, dest in self._artifact_map:
407
- for deployed_src, deployed_dest in self._expand_artifact_mapping(
408
- src,
409
- dest,
410
- absolute=absolute,
411
- expand_directories=expand_directories,
412
- predicate=predicate,
413
- ):
414
- yield deployed_src, deployed_dest
415
-
416
- def to_deploy_paths(self, src: Path) -> List[Path]:
417
- """
418
- Converts a source path to its corresponding deploy root path. If the input path is relative to the project root,
419
- paths relative to the deploy root are returned. If the input path is absolute, absolute paths are returned.
420
-
421
- Note that the provided source path must be part of a mapping. If the source path is not part of any mapping,
422
- an empty list is returned. For example, if `app/*` is specified as the source of a mapping,
423
- `to_deploy_paths(Path("app"))` will not yield any result.
424
-
425
- Arguments:
426
- src {Path} -- the source path within the project root, in canonical or absolute form.
427
-
428
- Returns:
429
- The deploy root paths for the given source path, or an empty list if no such path exists.
430
- """
431
- is_absolute = src.is_absolute()
432
-
433
- try:
434
- absolute_src = self._absolute_src(src)
435
- if not absolute_src.exists():
436
- return []
437
- canonical_src = self._canonical_src(absolute_src)
438
- except ArtifactError:
439
- # No mapping is possible for this src path
440
- return []
441
-
442
- output_destinations: List[Path] = []
443
-
444
- # 1. Check for exact rule matches for this path
445
- canonical_dests = self._artifact_map.get_destinations(canonical_src)
446
- if canonical_dests:
447
- for d in canonical_dests:
448
- output_destinations.append(self._to_output_dest(d, is_absolute))
449
-
450
- # 2. Check for any matches to parent directories for this path that would
451
- # cause this path to be part of the recursive copy
452
- canonical_parent = canonical_src.parent
453
- canonical_parent_dests = self.to_deploy_paths(canonical_parent)
454
- if canonical_parent_dests:
455
- canonical_child = canonical_src.relative_to(canonical_parent)
456
- for d in canonical_parent_dests:
457
- output_destinations.append(
458
- self._to_output_dest(d / canonical_child, is_absolute)
459
- )
460
-
461
- return output_destinations
462
-
463
- def all_sources(self, absolute: bool = False) -> Iterator[Path]:
464
- """
465
- Yields each registered artifact source in the project.
466
-
467
- Arguments:
468
- self: this instance
469
- absolute (bool): Specifies whether the yielded paths should be joined with the absolute project root.
470
- Returns:
471
- An iterator over all artifact mapping source paths.
472
- """
473
- for src in self._artifact_map.all_sources():
474
- yield self._to_output_src(src, absolute)
475
-
476
- def to_project_path(self, dest: Path) -> Optional[Path]:
477
- """
478
- Converts a deploy root path to its corresponding project source path. If the input path is relative to the
479
- deploy root, a path relative to the project root is returned. If the input path is absolute, an absolute path is
480
- returned.
481
-
482
- Arguments:
483
- dest {Path} -- the destination path within the deploy root, in canonical or absolute form.
484
-
485
- Returns:
486
- The project root path for the given deploy root path, or None if no such path exists.
487
- """
488
- is_absolute = dest.is_absolute()
489
- try:
490
- canonical_dest = self._canonical_dest(dest)
491
- except NotInDeployRootError:
492
- # No mapping possible for the dest path
493
- return None
494
-
495
- # 1. Look for an exact rule matching this path. If we find any, then
496
- # stop searching. This is because each destination path can only originate
497
- # from a single source (however, one source can be copied to multiple destinations).
498
- canonical_src = self._artifact_map.get_source(canonical_dest)
499
- if canonical_src is not None:
500
- return self._to_output_src(canonical_src, is_absolute)
501
-
502
- # 2. No exact match was found, look for a match for parent directories of this
503
- # path, recursively. Stop when a match is found
504
- canonical_parent = canonical_dest.parent
505
- if canonical_parent == canonical_dest:
506
- return None
507
- canonical_parent_src = self.to_project_path(canonical_parent)
508
- if canonical_parent_src is not None:
509
- canonical_child = canonical_dest.relative_to(canonical_parent)
510
- canonical_child_candidate = canonical_parent_src / canonical_child
511
- if self._absolute_src(canonical_child_candidate).exists():
512
- return self._to_output_src(canonical_child_candidate, is_absolute)
513
-
514
- # No mapping for this destination path
515
- return None
516
-
517
- def _absolute_src(self, src: Path) -> Path:
518
- if src.is_absolute():
519
- resolved_src = resolve_without_follow(src)
520
- else:
521
- resolved_src = resolve_without_follow(self._project_root / src)
522
- if self._project_root not in resolved_src.parents:
523
- raise ArtifactError(
524
- f"Source is not in the project root: {src}, root={self._project_root}"
525
- )
526
- return resolved_src
527
-
528
- def _absolute_dest(self, dest: Path, src_path: Optional[Path] = None) -> Path:
529
- if dest.is_absolute():
530
- resolved_dest = resolve_without_follow(dest)
531
- else:
532
- resolved_dest = resolve_without_follow(self._deploy_root / dest)
533
- if (
534
- self._deploy_root != resolved_dest
535
- and self._deploy_root not in resolved_dest.parents
536
- ):
537
- raise NotInDeployRootError(
538
- dest_path=dest, deploy_root=self._deploy_root, src_path=src_path
539
- )
540
-
541
- return resolved_dest
542
-
543
- def _canonical_src(self, src: Path) -> Path:
544
- """
545
- Returns the canonical version of a source path, relative to the project root.
546
- """
547
- absolute_src = self._absolute_src(src)
548
- return absolute_src.relative_to(self._project_root)
549
-
550
- def _canonical_dest(self, dest: Path) -> Path:
551
- """
552
- Returns the canonical version of a destination path, relative to the deploy root.
553
- """
554
- absolute_dest = self._absolute_dest(dest)
555
- return absolute_dest.relative_to(self._deploy_root)
556
-
557
- def _to_output_dest(self, dest: Path, absolute: bool) -> Path:
558
- return self._absolute_dest(dest) if absolute else self._canonical_dest(dest)
559
-
560
- def _to_output_src(self, src: Path, absolute: bool) -> Path:
561
- return self._absolute_src(src) if absolute else self._canonical_src(src)
562
-
563
-
564
- def specifies_directory(s: str) -> bool:
565
- """
566
- Does the path (as seen from the project definition) refer to
567
- a directory? For destination paths, we enforce the usage of a
568
- trailing forward slash (/). Note that we use the forward slash
569
- even on Windows so that snowflake.yml can be shared between OSes.
570
-
571
- This means that to put a file in the root of the stage, we need
572
- to specify "./" as its destination, or omit it (but only if the
573
- file already lives in the project root).
574
- """
575
- return s.endswith("/")
576
-
577
-
578
- def delete(path: Path) -> None:
579
- """
580
- Obliterates whatever is at the given path, or is a no-op if the
581
- given path does not represent a file or directory that exists.
582
- """
583
- spath = SecurePath(path)
584
- if spath.path.is_file():
585
- spath.unlink() # remove the file
586
- elif spath.path.is_dir():
587
- spath.rmdir(recursive=True) # remove dir and all contains
588
-
589
-
590
- def symlink_or_copy(src: Path, dst: Path, deploy_root: Path) -> None:
591
- """
592
- Symlinks files from src to dst. If the src contains parent directories, then copies the empty directory shell to the deploy root.
593
- The directory hierarchy above dst is created if any of those directories do not exist.
594
- """
595
- ssrc = SecurePath(src)
596
- sdst = SecurePath(dst)
597
- sdst.parent.mkdir(parents=True, exist_ok=True)
598
-
599
- # Verify that the mapping isn't accidentally trying to create a file in the project source through symlinks.
600
- # We need to ensure we're resolving symlinks for this check to be effective.
601
- # We are unlikely to hit this if calling the function through bundle map, keeping it here for other future use cases outside bundle.
602
- resolved_dst = dst.resolve()
603
- resolved_deploy_root = deploy_root.resolve()
604
- dst_is_deploy_root = resolved_deploy_root == resolved_dst
605
- if (not dst_is_deploy_root) and (resolved_deploy_root not in resolved_dst.parents):
606
- raise NotInDeployRootError(dest_path=dst, deploy_root=deploy_root, src_path=src)
607
-
608
- absolute_src = resolve_without_follow(src)
609
- if absolute_src.is_file():
610
- delete(dst)
611
- try:
612
- os.symlink(absolute_src, dst)
613
- except OSError:
614
- ssrc.copy(dst)
615
- else:
616
- # 1. Create a new directory in the deploy root
617
- dst.mkdir(exist_ok=True)
618
- # 2. For all children of src, create their counterparts in dst now that it exists
619
- for root, _, files in sorted(os.walk(absolute_src, followlinks=True)):
620
- relative_root = Path(root).relative_to(absolute_src)
621
- absolute_root_in_deploy = Path(dst, relative_root)
622
- absolute_root_in_deploy.mkdir(parents=True, exist_ok=True)
623
- for file in sorted(files):
624
- absolute_file_in_project = Path(absolute_src, relative_root, file)
625
- absolute_file_in_deploy = Path(absolute_root_in_deploy, file)
626
- symlink_or_copy(
627
- src=absolute_file_in_project,
628
- dst=absolute_file_in_deploy,
629
- deploy_root=deploy_root,
630
- )
631
-
632
-
633
- def resolve_without_follow(path: Path) -> Path:
634
- """
635
- Resolves a Path to an absolute version of itself, without following
636
- symlinks like Path.resolve() does.
637
- """
638
- return Path(os.path.abspath(path))
639
-
640
-
641
- def build_bundle(
642
- project_root: Path,
643
- deploy_root: Path,
644
- artifacts: List[PathMapping],
645
- ) -> BundleMap:
646
- """
647
- Prepares a local folder (deploy_root) with configured app artifacts.
648
- This folder can then be uploaded to a stage.
649
- Returns a map of the copied source files, pointing to where they were copied.
650
- """
651
- resolved_root = deploy_root.resolve()
652
- if resolved_root.exists() and not resolved_root.is_dir():
653
- raise DeployRootError(
654
- f"Deploy root {resolved_root} exists, but is not a directory!"
655
- )
656
-
657
- if project_root.resolve() not in resolved_root.parents:
658
- raise DeployRootError(
659
- f"Deploy root {resolved_root} is not a descendent of the project directory!"
660
- )
661
-
662
- # users may have removed files or entire artifact mappings from their project
663
- # definition since the last time we bundled; we need to clear the deploy root first
664
- if resolved_root.exists():
665
- delete(resolved_root)
666
-
667
- bundle_map = BundleMap(project_root=project_root, deploy_root=deploy_root)
668
- for artifact in artifacts:
669
- bundle_map.add(artifact)
670
-
671
- if bundle_map.is_empty():
672
- raise ArtifactError(
673
- "No artifacts mapping found in project definition, nothing to do."
674
- )
675
-
676
- for (absolute_src, absolute_dest) in bundle_map.all_mappings(
677
- absolute=True, expand_directories=False
678
- ):
679
- symlink_or_copy(absolute_src, absolute_dest, deploy_root=deploy_root)
680
-
681
- return bundle_map
682
-
683
-
684
- def find_manifest_file(deploy_root: Path) -> Path:
685
- """
686
- Find manifest.yml file, if available, in the deploy_root of the Snowflake Native App project.
687
- """
688
- resolved_root = deploy_root.resolve()
689
- for root, _, files in os.walk(resolved_root):
690
- for file in files:
691
- if file.lower() == "manifest.yml":
692
- return Path(os.path.join(root, file))
693
-
694
- raise ClickException(
695
- "Required manifest.yml file not found in the deploy root of the Snowflake Native App project."
696
- )
697
-
698
-
699
- def find_and_read_manifest_file(deploy_root: Path) -> Dict[str, Any]:
700
- """
701
- Finds the manifest file in the deploy root of the project, and reads the contents and returns them
702
- as a dictionary.
703
- """
704
- manifest_file = find_manifest_file(deploy_root=deploy_root)
705
- with SecurePath(manifest_file).open(
706
- "r", read_file_limit_mb=DEFAULT_SIZE_LIMIT_MB
707
- ) as file:
708
- manifest_content = safe_load(file.read())
709
- return manifest_content
710
-
711
-
712
- def find_setup_script_file(deploy_root: Path) -> Path:
713
- """
714
- Find the setup script file, if available, in the deploy_root of the Snowflake Native App project.
715
- """
716
- artifacts = "artifacts"
717
- setup_script = "setup_script"
718
-
719
- manifest_content = find_and_read_manifest_file(deploy_root=deploy_root)
720
-
721
- if (artifacts in manifest_content) and (
722
- setup_script in manifest_content[artifacts]
723
- ):
724
- setup_script_rel_path = manifest_content[artifacts][setup_script]
725
- file_name = Path(deploy_root / setup_script_rel_path)
726
- if file_name.is_file():
727
- return file_name
728
- else:
729
- raise ClickException(f"Could not find setup script file at {file_name}.")
730
- else:
731
- raise ClickException(
732
- "Manifest.yml file must contain an artifacts section to specify the location of the setup script."
733
- )
734
-
735
-
736
- def find_version_info_in_manifest_file(
737
- deploy_root: Path,
738
- ) -> Tuple[Optional[str], Optional[int]]:
739
- """
740
- Find version and patch, if available, in the manifest.yml file.
741
- """
742
- name_field = "name"
743
- patch_field = "patch"
744
-
745
- manifest_content = find_and_read_manifest_file(deploy_root=deploy_root)
746
-
747
- version_name: Optional[str] = None
748
- patch_number: Optional[int] = None
749
-
750
- version_info = manifest_content.get("version", None)
751
- if version_info:
752
- if name_field in version_info:
753
- version_name = to_identifier(str(version_info[name_field]))
754
- if patch_field in version_info:
755
- patch_number = int(version_info[patch_field])
756
-
757
- return version_name, patch_number