snowflake-cli-labs 3.0.0rc5__py3-none-any.whl → 3.0.2__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.0rc5.dist-info → snowflake_cli_labs-3.0.2.dist-info}/METADATA +6 -96
  3. snowflake_cli_labs-3.0.2.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 -90
  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 -400
  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.0rc5.dist-info/RECORD +0 -242
  242. snowflake_cli_labs-3.0.0rc5.dist-info/entry_points.txt +0 -2
  243. {snowflake_cli_labs-3.0.0rc5.dist-info → snowflake_cli_labs-3.0.2.dist-info}/WHEEL +0 -0
  244. {snowflake_cli_labs-3.0.0rc5.dist-info → snowflake_cli_labs-3.0.2.dist-info}/licenses/LICENSE +0 -0
@@ -1,160 +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 hashlib
18
- import logging
19
- import math
20
- import os.path
21
- import re
22
- from pathlib import Path
23
- from typing import List, Tuple
24
-
25
- from click.exceptions import ClickException
26
- from snowflake.cli.api.secure_path import UNLIMITED, SecurePath
27
- from snowflake.connector.constants import S3_CHUNK_SIZE, S3_MAX_PARTS, S3_MIN_PART_SIZE
28
-
29
- ONE_MEGABYTE = 1024**2
30
- READ_BUFFER_BYTES = 64 * 1024
31
- MD5SUM_REGEX = r"^[A-Fa-f0-9]{32}$"
32
- MULTIPART_MD5SUM_REGEX = r"^([A-Fa-f0-9]{32})-(\d+)$"
33
-
34
- log = logging.getLogger(__name__)
35
-
36
-
37
- class UnknownMD5FormatError(ClickException):
38
- def __init__(self, md5: str):
39
- super().__init__(f"Unknown md5 format: {md5}")
40
-
41
-
42
- def is_md5sum(checksum: str) -> bool:
43
- """
44
- Could the provided hexadecimal checksum represent a valid md5sum?
45
- """
46
- return re.match(MD5SUM_REGEX, checksum) is not None
47
-
48
-
49
- def parse_multipart_md5sum(checksum: str) -> Tuple[str, int] | None:
50
- """
51
- Does this represent a multi-part md5sum (i.e. "<md5>-<n>")?
52
- If so, returns the tuple (md5, n), otherwise None.
53
- """
54
- multipart_md5 = re.match(MULTIPART_MD5SUM_REGEX, checksum)
55
- if multipart_md5:
56
- return (multipart_md5.group(1), int(multipart_md5.group(2)))
57
- return None
58
-
59
-
60
- def compute_md5sum(file: Path, chunk_size: int | None = None) -> str:
61
- """
62
- Returns a hexadecimal checksum for the file located at the given path.
63
- If chunk_size is given, computes a multi-part md5sum.
64
- """
65
- if not file.is_file():
66
- raise ValueError(
67
- "The provided file does not exist or not a (symlink to a) regular file"
68
- )
69
-
70
- # If the stage uses SNOWFLAKE_FULL encryption, this will fail to provide
71
- # a matching md5sum, even when the underlying file is the same, as we do
72
- # not have access to the encrypted file under checksum.
73
-
74
- file_size = os.path.getsize(file)
75
- if file_size == 0:
76
- # simple md5 with no content
77
- return hashlib.md5().hexdigest()
78
-
79
- with SecurePath(file).open("rb", read_file_limit_mb=UNLIMITED) as f:
80
- md5s: List[hashlib._Hash] = [] # noqa: SLF001
81
- hasher = hashlib.md5()
82
-
83
- remains = file_size
84
- remains_in_chunk: int = min(chunk_size, remains) if chunk_size else remains
85
- while remains > 0:
86
- sz = min(READ_BUFFER_BYTES, remains_in_chunk)
87
- buf = f.read(sz)
88
- hasher.update(buf)
89
- remains_in_chunk -= sz
90
- remains -= sz
91
- if remains_in_chunk == 0:
92
- if not chunk_size:
93
- # simple md5; only one chunk processed
94
- return hasher.hexdigest()
95
- else:
96
- # push the hash of this chunk + reset
97
- md5s.append(hasher)
98
- hasher = hashlib.md5()
99
- remains_in_chunk = min(chunk_size, remains)
100
-
101
- # multi-part hash (e.g. aws)
102
- digests = b"".join(m.digest() for m in md5s)
103
- digests_md5 = hashlib.md5(digests)
104
- return f"{digests_md5.hexdigest()}-{len(md5s)}"
105
-
106
-
107
- def file_matches_md5sum(local_file: Path, remote_md5: str | None) -> bool:
108
- """
109
- Try a few different md5sums to determine if a local file is identical
110
- to a file that has a given remote md5sum.
111
-
112
- Handles the multi-part md5sums generated by e.g. AWS S3, using values
113
- from the python connector to make educated guesses on chunk size.
114
-
115
- Assumes that upload time would dominate local hashing time.
116
- """
117
- if not remote_md5:
118
- # no hash available
119
- return False
120
-
121
- if is_md5sum(remote_md5):
122
- # regular hash
123
- return compute_md5sum(local_file) == remote_md5
124
-
125
- if md5_and_chunks := parse_multipart_md5sum(remote_md5):
126
- # multi-part hash (e.g. aws)
127
- (_, num_chunks) = md5_and_chunks
128
- file_size = os.path.getsize(local_file)
129
-
130
- # If this file uses the maximum number of parts supported by the cloud backend,
131
- # the chunk size is likely not a clean multiple of a megabyte. Try reverse engineering
132
- # from the file size first, then fall back to the usual detection method.
133
- # At time of writing this logic would trigger for files >= 80GiB (python connector)
134
- if num_chunks == S3_MAX_PARTS:
135
- chunk_size = max(math.ceil(file_size / S3_MAX_PARTS), S3_MIN_PART_SIZE)
136
- if compute_md5sum(local_file, chunk_size) == remote_md5:
137
- return True
138
-
139
- # Estimates the chunk size the multi-part file must have been uploaded with
140
- # by trying chunk sizes that give the most evenly-sized chunks.
141
- #
142
- # First we'll try the chunk size that's a multiple of S3_CHUNK_SIZE (8mb) from
143
- # the python connector that results in num_chunks, then we'll do the same with
144
- # a smaller granularity (1mb) that is used by default in some AWS multi-part
145
- # upload implementations.
146
- #
147
- # We're working backwards from num_chunks here because it's the only value we know.
148
- for chunk_size_alignment in [S3_CHUNK_SIZE, ONE_MEGABYTE]:
149
- # +1 because we need at least one chunk when file_size < num_chunks * chunk_size_alignment
150
- # -1 because we don't want to add an extra chunk when file_size is an exact multiple of num_chunks * chunk_size_alignment
151
- multiplier = 1 + ((file_size - 1) // (num_chunks * chunk_size_alignment))
152
- chunk_size = multiplier * chunk_size_alignment
153
- if compute_md5sum(local_file, chunk_size) == remote_md5:
154
- return True
155
-
156
- # we were unable to figure out the chunk size, or the files are different
157
- log.debug("multi-part md5: %s != %s", remote_md5, local_file)
158
- return False
159
-
160
- raise UnknownMD5FormatError(remote_md5)
@@ -1,30 +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 snowflake.cli._plugins.stage import commands
16
- from snowflake.cli.api.plugins.command import (
17
- SNOWCLI_ROOT_COMMAND_PATH,
18
- CommandSpec,
19
- CommandType,
20
- plugin_hook_impl,
21
- )
22
-
23
-
24
- @plugin_hook_impl
25
- def command_spec():
26
- return CommandSpec(
27
- parent_command_path=SNOWCLI_ROOT_COMMAND_PATH,
28
- command_type=CommandType.COMMAND_GROUP,
29
- typer_instance=commands.app.create_instance(),
30
- )
@@ -1,54 +0,0 @@
1
- from typing import Optional
2
-
3
- from snowflake.cli._plugins.nativeapp.artifacts import BundleMap
4
- from snowflake.cli._plugins.stage.diff import (
5
- DiffResult,
6
- _to_diff_line,
7
- _to_src_dest_pair,
8
- )
9
- from snowflake.cli.api.console import cli_console as cc
10
-
11
-
12
- def print_diff_to_console(
13
- diff: DiffResult,
14
- bundle_map: Optional[BundleMap] = None,
15
- ):
16
- if not diff.has_changes():
17
- cc.message("Your stage is up-to-date with your local deploy root.")
18
- return
19
-
20
- blank_line_needed = False
21
- if diff.only_local or diff.different:
22
- cc.message("Local changes to be deployed:")
23
- messages_to_output = []
24
- for p in diff.different:
25
- src_dest_pair = _to_src_dest_pair(p, bundle_map)
26
- messages_to_output.append(
27
- (
28
- src_dest_pair,
29
- _to_diff_line("modified", src_dest_pair[0], src_dest_pair[1]),
30
- )
31
- )
32
- for p in diff.only_local:
33
- src_dest_pair = _to_src_dest_pair(p, bundle_map)
34
- messages_to_output.append(
35
- (
36
- src_dest_pair,
37
- _to_diff_line("added", src_dest_pair[0], src_dest_pair[1]),
38
- )
39
- )
40
-
41
- with cc.indented():
42
- for key, message in sorted(messages_to_output, key=lambda pair: pair[0]):
43
- cc.message(message)
44
-
45
- blank_line_needed = True
46
-
47
- if diff.only_on_stage:
48
- if blank_line_needed:
49
- cc.message("")
50
- cc.message(f"Deleted paths to be removed from your stage:")
51
- with cc.indented():
52
- for p in sorted(diff.only_on_stage):
53
- diff_line = _to_diff_line("deleted", src=None, dest=str(p))
54
- cc.message(diff_line)
@@ -1,13 +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.
@@ -1,195 +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 logging
18
- from pathlib import Path
19
- from typing import Dict
20
-
21
- import click
22
- import typer
23
- from click import ClickException, UsageError
24
- from snowflake.cli._plugins.object.command_aliases import (
25
- add_object_command_aliases,
26
- scope_option,
27
- )
28
- from snowflake.cli._plugins.streamlit.manager import StreamlitManager
29
- from snowflake.cli._plugins.streamlit.streamlit_entity_model import (
30
- StreamlitEntityModel,
31
- )
32
- from snowflake.cli.api.cli_global_context import get_cli_context
33
- from snowflake.cli.api.commands.decorators import (
34
- with_experimental_behaviour,
35
- with_project_definition,
36
- )
37
- from snowflake.cli.api.commands.flags import (
38
- ReplaceOption,
39
- entity_argument,
40
- identifier_argument,
41
- like_option,
42
- )
43
- from snowflake.cli.api.commands.snow_typer import SnowTyperFactory
44
- from snowflake.cli.api.constants import ObjectType
45
- from snowflake.cli.api.exceptions import NoProjectDefinitionError
46
- from snowflake.cli.api.identifiers import FQN
47
- from snowflake.cli.api.output.types import (
48
- CommandResult,
49
- MessageResult,
50
- SingleQueryResult,
51
- )
52
- from snowflake.cli.api.project.definition_conversion import (
53
- convert_project_definition_to_v2,
54
- )
55
-
56
- app = SnowTyperFactory(
57
- name="streamlit",
58
- help="Manages a Streamlit app in Snowflake.",
59
- )
60
- log = logging.getLogger(__name__)
61
-
62
- StreamlitNameArgument = identifier_argument(
63
- sf_object="Streamlit app", example="my_streamlit"
64
- )
65
- OpenOption = typer.Option(
66
- False,
67
- "--open",
68
- help="Whether to open the Streamlit app in a browser.",
69
- is_flag=True,
70
- )
71
-
72
-
73
- add_object_command_aliases(
74
- app=app,
75
- object_type=ObjectType.STREAMLIT,
76
- name_argument=StreamlitNameArgument,
77
- like_option=like_option(
78
- help_example='`list --like "my%"` lists all streamlit apps that begin with “my”'
79
- ),
80
- scope_option=scope_option(help_example="`list --in database my_db`"),
81
- )
82
-
83
-
84
- @app.command(requires_connection=True)
85
- def execute(
86
- name: FQN = StreamlitNameArgument,
87
- **options,
88
- ):
89
- """
90
- Executes a streamlit in a headless mode.
91
- """
92
- _ = StreamlitManager().execute(app_name=name)
93
- return MessageResult(f"Streamlit {name} executed.")
94
-
95
-
96
- @app.command("share", requires_connection=True)
97
- def streamlit_share(
98
- name: FQN = StreamlitNameArgument,
99
- to_role: str = typer.Argument(
100
- ...,
101
- help="Role with which to share the Streamlit app.",
102
- show_default=False,
103
- ),
104
- **options,
105
- ) -> CommandResult:
106
- """
107
- Shares a Streamlit app with another role.
108
- """
109
- cursor = StreamlitManager().share(streamlit_name=name, to_role=to_role)
110
- return SingleQueryResult(cursor)
111
-
112
-
113
- def _default_file_callback(param_name: str):
114
- from click.core import ParameterSource # type: ignore
115
-
116
- def _check_file_exists_if_not_default(ctx: click.Context, value):
117
- if (
118
- ctx.get_parameter_source(param_name) != ParameterSource.DEFAULT # type: ignore
119
- and value
120
- and not Path(value).exists()
121
- ):
122
- raise ClickException(f"Provided file {value} does not exist")
123
- return Path(value)
124
-
125
- return _check_file_exists_if_not_default
126
-
127
-
128
- @app.command("deploy", requires_connection=True)
129
- @with_project_definition()
130
- @with_experimental_behaviour()
131
- def streamlit_deploy(
132
- replace: bool = ReplaceOption(
133
- help="Replace the Streamlit app if it already exists."
134
- ),
135
- entity_id: str = entity_argument("streamlit"),
136
- open_: bool = OpenOption,
137
- **options,
138
- ) -> CommandResult:
139
- """
140
- Deploys a Streamlit app defined in the project definition file (snowflake.yml). By default, the command uploads
141
- environment.yml and any other pages or folders, if present. If you don’t specify a stage name, the `streamlit`
142
- stage is used. If the specified stage does not exist, the command creates it. If multiple Streamlits are defined
143
- in snowflake.yml and no entity_id is provided then command will raise an error.
144
- """
145
-
146
- cli_context = get_cli_context()
147
- pd = cli_context.project_definition
148
- if not pd.meets_version_requirement("2"):
149
- if not pd.streamlit:
150
- raise NoProjectDefinitionError(
151
- project_type="streamlit", project_root=cli_context.project_root
152
- )
153
- pd = convert_project_definition_to_v2(cli_context.project_root, pd)
154
-
155
- streamlits: Dict[str, StreamlitEntityModel] = pd.get_entities_by_type(
156
- entity_type="streamlit"
157
- )
158
-
159
- if not streamlits:
160
- raise NoProjectDefinitionError(
161
- project_type="streamlit", project_root=cli_context.project_root
162
- )
163
-
164
- if entity_id and entity_id not in streamlits:
165
- raise UsageError(f"No '{entity_id}' entity in project definition file.")
166
-
167
- if len(streamlits.keys()) == 1:
168
- entity_id = list(streamlits.keys())[0]
169
-
170
- if entity_id is None:
171
- raise UsageError(
172
- "Multiple Streamlit apps found. Please provide entity id for the operation."
173
- )
174
-
175
- # Get first streamlit
176
- streamlit: StreamlitEntityModel = streamlits[entity_id]
177
- url = StreamlitManager().deploy(streamlit=streamlit, replace=replace)
178
-
179
- if open_:
180
- typer.launch(url)
181
-
182
- return MessageResult(f"Streamlit successfully deployed and available under {url}")
183
-
184
-
185
- @app.command("get-url", requires_connection=True)
186
- def get_url(
187
- name: FQN = StreamlitNameArgument,
188
- open_: bool = OpenOption,
189
- **options,
190
- ):
191
- """Returns a URL to the specified Streamlit app"""
192
- url = StreamlitManager().get_url(streamlit_name=name)
193
- if open_:
194
- typer.launch(url)
195
- return MessageResult(url)
@@ -1,220 +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 logging
18
- from pathlib import Path
19
- from typing import List, Optional
20
-
21
- from click import ClickException
22
- from snowflake.cli._plugins.connection.util import (
23
- MissingConnectionAccountError,
24
- MissingConnectionRegionError,
25
- make_snowsight_url,
26
- )
27
- from snowflake.cli._plugins.object.manager import ObjectManager
28
- from snowflake.cli._plugins.stage.manager import StageManager
29
- from snowflake.cli._plugins.streamlit.streamlit_entity_model import (
30
- StreamlitEntityModel,
31
- )
32
- from snowflake.cli.api.commands.experimental_behaviour import (
33
- experimental_behaviour_enabled,
34
- )
35
- from snowflake.cli.api.console import cli_console
36
- from snowflake.cli.api.feature_flags import FeatureFlag
37
- from snowflake.cli.api.identifiers import FQN
38
- from snowflake.cli.api.sql_execution import SqlExecutionMixin
39
- from snowflake.connector.cursor import SnowflakeCursor
40
- from snowflake.connector.errors import ProgrammingError
41
-
42
- log = logging.getLogger(__name__)
43
-
44
-
45
- class StreamlitManager(SqlExecutionMixin):
46
- def execute(self, app_name: FQN):
47
- query = f"EXECUTE STREAMLIT {app_name.sql_identifier}()"
48
- return self._execute_query(query=query)
49
-
50
- def share(self, streamlit_name: FQN, to_role: str) -> SnowflakeCursor:
51
- return self._execute_query(
52
- f"grant usage on streamlit {streamlit_name.sql_identifier} to role {to_role}"
53
- )
54
-
55
- def _put_streamlit_files(
56
- self,
57
- root_location: str,
58
- artifacts: Optional[List[Path]] = None,
59
- ):
60
- cli_console.step(f"Deploying files to {root_location}")
61
- if not artifacts:
62
- return
63
- 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
69
-
70
- stage_manager.put(
71
- f"{file.joinpath('*')}", f"{root_location}/{file}", 4, True
72
- )
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
-
78
- def _create_streamlit(
79
- self,
80
- streamlit: StreamlitEntityModel,
81
- replace: Optional[bool] = None,
82
- experimental: Optional[bool] = None,
83
- from_stage_name: Optional[str] = None,
84
- ):
85
- streamlit_id = streamlit.fqn.using_connection(self._conn)
86
- cli_console.step(f"Creating {streamlit_id} Streamlit")
87
- query = []
88
- if replace:
89
- query.append(f"CREATE OR REPLACE STREAMLIT {streamlit_id.sql_identifier}")
90
- elif experimental:
91
- # For experimental behaviour, we need to use CREATE STREAMLIT IF NOT EXISTS
92
- # for a streamlit app with an embedded stage
93
- # because this is analogous to the behavior for non-experimental
94
- # deploy which does CREATE STAGE IF NOT EXISTS
95
- query.append(
96
- f"CREATE STREAMLIT IF NOT EXISTS {streamlit_id.sql_identifier}"
97
- )
98
- else:
99
- query.append(f"CREATE STREAMLIT {streamlit_id.sql_identifier}")
100
-
101
- if from_stage_name:
102
- query.append(f"ROOT_LOCATION = '{from_stage_name}'")
103
-
104
- query.append(f"MAIN_FILE = '{streamlit.main_file}'")
105
- if streamlit.imports:
106
- query.append(streamlit.get_imports_sql())
107
- if streamlit.query_warehouse:
108
- query.append(f"QUERY_WAREHOUSE = {streamlit.query_warehouse}")
109
- if streamlit.title:
110
- query.append(f"TITLE = '{streamlit.title}'")
111
-
112
- if streamlit.comment:
113
- query.append(f"COMMENT = '{streamlit.comment}'")
114
-
115
- if streamlit.external_access_integrations:
116
- query.append(streamlit.get_external_access_integrations_sql())
117
-
118
- if streamlit.secrets:
119
- query.append(streamlit.get_secrets_sql())
120
-
121
- self._execute_query("\n".join(query))
122
-
123
- def deploy(self, streamlit: StreamlitEntityModel, replace: bool = False):
124
- streamlit_id = streamlit.fqn.using_connection(self._conn)
125
- if (
126
- ObjectManager().object_exists(object_type="streamlit", fqn=streamlit_id)
127
- and not replace
128
- ):
129
- raise ClickException(
130
- f"Streamlit {streamlit.fqn} already exist. If you want to replace it use --replace flag."
131
- )
132
-
133
- # for backwards compatibility - quoted stage path might be case-sensitive
134
- # https://docs.snowflake.com/en/sql-reference/identifiers-syntax#double-quoted-identifiers
135
- streamlit_name_for_root_location = streamlit_id.name
136
- use_versioned_stage = FeatureFlag.ENABLE_STREAMLIT_VERSIONED_STAGE.is_enabled()
137
- if (
138
- experimental_behaviour_enabled()
139
- or FeatureFlag.ENABLE_STREAMLIT_EMBEDDED_STAGE.is_enabled()
140
- or use_versioned_stage
141
- ):
142
- """
143
- 1. Create streamlit object
144
- 2. Upload files to embedded stage
145
- """
146
- # TODO: Support from_stage
147
- # from_stage_stmt = f"FROM_STAGE = '{stage_name}'" if stage_name else ""
148
- self._create_streamlit(
149
- streamlit=streamlit,
150
- replace=replace,
151
- experimental=True,
152
- )
153
- try:
154
- if use_versioned_stage:
155
- self._execute_query(
156
- f"ALTER STREAMLIT {streamlit_id.identifier} ADD LIVE VERSION FROM LAST"
157
- )
158
- elif not FeatureFlag.ENABLE_STREAMLIT_NO_CHECKOUTS.is_enabled():
159
- self._execute_query(
160
- f"ALTER streamlit {streamlit_id.identifier} CHECKOUT"
161
- )
162
- except ProgrammingError as e:
163
- # If an error is raised because a CHECKOUT has already occurred or a LIVE VERSION already exists, simply skip it and continue
164
- if "Checkout already exists" in str(
165
- e
166
- ) or "There is already a live version" in str(e):
167
- log.info("Checkout already exists, continuing")
168
- else:
169
- raise
170
-
171
- stage_path = streamlit_id.identifier
172
- embedded_stage_name = f"snow://streamlit/{stage_path}"
173
- if use_versioned_stage:
174
- # "LIVE" is the only supported version for now, but this may change later.
175
- root_location = f"{embedded_stage_name}/versions/live"
176
- else:
177
- root_location = f"{embedded_stage_name}/default_checkout"
178
-
179
- self._put_streamlit_files(
180
- root_location,
181
- streamlit.artifacts,
182
- )
183
- else:
184
- """
185
- 1. Create stage
186
- 2. Upload files to created stage
187
- 3. Create streamlit from stage
188
- """
189
- stage_manager = StageManager()
190
-
191
- stage_name = streamlit.stage or "streamlit"
192
- stage_name = FQN.from_string(stage_name).using_connection(self._conn)
193
-
194
- cli_console.step(f"Creating {stage_name} stage")
195
- stage_manager.create(fqn=stage_name)
196
-
197
- root_location = stage_manager.get_standard_stage_prefix(
198
- f"{stage_name}/{streamlit_name_for_root_location}"
199
- )
200
-
201
- self._put_streamlit_files(root_location, streamlit.artifacts)
202
-
203
- self._create_streamlit(
204
- streamlit=streamlit,
205
- replace=replace,
206
- from_stage_name=root_location,
207
- experimental=False,
208
- )
209
-
210
- return self.get_url(streamlit_name=streamlit_id)
211
-
212
- def get_url(self, streamlit_name: FQN) -> str:
213
- try:
214
- fqn = streamlit_name.using_connection(self._conn)
215
- return make_snowsight_url(
216
- self._conn,
217
- f"/#/streamlit-apps/{fqn.url_identifier}",
218
- )
219
- except (MissingConnectionRegionError, MissingConnectionAccountError) as e:
220
- return "https://app.snowflake.com"