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,268 +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
- import re
19
- from enum import Enum
20
- from typing import Dict, List, Set
21
-
22
- from click import UsageError
23
- from snowflake.cli._plugins.snowpark.models import Requirement
24
- from snowflake.cli._plugins.snowpark.snowpark_entity_model import (
25
- ProcedureEntityModel,
26
- SnowparkEntityModel,
27
- )
28
- from snowflake.cli._plugins.snowpark.snowpark_project_paths import Artefact
29
- from snowflake.cli.api.console import cli_console
30
- from snowflake.cli.api.constants import (
31
- INIT_TEMPLATE_VARIABLE_CLOSING,
32
- INIT_TEMPLATE_VARIABLE_OPENING,
33
- PROJECT_TEMPLATE_VARIABLE_CLOSING,
34
- PROJECT_TEMPLATE_VARIABLE_OPENING,
35
- ObjectType,
36
- )
37
- from snowflake.cli.api.sql_execution import SqlExecutionMixin
38
- from snowflake.connector.cursor import SnowflakeCursor
39
-
40
- log = logging.getLogger(__name__)
41
-
42
- SnowparkEntities = Dict[str, SnowparkEntityModel]
43
- StageToArtefactMapping = Dict[str, set[Artefact]]
44
- EntityToImportPathsMapping = Dict[str, set[str]]
45
-
46
- DEFAULT_RUNTIME = "3.10"
47
-
48
-
49
- class SnowparkObject(Enum):
50
- """This clas is used only for Snowpark execute where choice is limited."""
51
-
52
- PROCEDURE = str(ObjectType.PROCEDURE)
53
- FUNCTION = str(ObjectType.FUNCTION)
54
-
55
-
56
- class SnowparkObjectManager(SqlExecutionMixin):
57
- def execute(
58
- self, execution_identifier: str, object_type: SnowparkObject
59
- ) -> SnowflakeCursor:
60
- if object_type == SnowparkObject.FUNCTION:
61
- return self._execute_query(f"select {execution_identifier}")
62
- if object_type == SnowparkObject.PROCEDURE:
63
- return self._execute_query(f"call {execution_identifier}")
64
- raise UsageError(f"Unknown object type: {object_type}.")
65
-
66
- def create_or_replace(
67
- self,
68
- entity: SnowparkEntityModel,
69
- artifact_files: set[str],
70
- snowflake_dependencies: list[str],
71
- ) -> str:
72
- entity.imports.extend(artifact_files)
73
- imports = [f"'{x}'" for x in entity.imports]
74
- packages_list = ",".join(f"'{p}'" for p in snowflake_dependencies)
75
-
76
- object_type = entity.get_type()
77
-
78
- query = [
79
- f"create or replace {object_type} {entity.udf_sproc_identifier.identifier_for_sql}",
80
- f"copy grants",
81
- f"returns {entity.returns}",
82
- "language python",
83
- f"runtime_version={entity.runtime or DEFAULT_RUNTIME}",
84
- f"imports=({', '.join(imports)})",
85
- f"handler='{entity.handler}'",
86
- f"packages=({packages_list})",
87
- ]
88
-
89
- if entity.external_access_integrations:
90
- query.append(entity.get_external_access_integrations_sql())
91
-
92
- if entity.secrets:
93
- query.append(entity.get_secrets_sql())
94
-
95
- if isinstance(entity, ProcedureEntityModel) and entity.execute_as_caller:
96
- query.append("execute as caller")
97
-
98
- return self._execute_query("\n".join(query))
99
-
100
- def deploy_entity(
101
- self,
102
- entity: SnowparkEntityModel,
103
- existing_objects: Dict[str, SnowflakeCursor],
104
- snowflake_dependencies: List[str],
105
- entities_to_artifact_map: EntityToImportPathsMapping,
106
- ):
107
- cli_console.step(f"Creating {entity.type} {entity.fqn}")
108
- object_exists = entity.entity_id in existing_objects
109
- replace_object = False
110
- if object_exists:
111
- replace_object = _check_if_replace_is_required(
112
- entity=entity,
113
- current_state=existing_objects[entity.entity_id],
114
- snowflake_dependencies=snowflake_dependencies,
115
- stage_artifact_files=entities_to_artifact_map[entity.entity_id],
116
- )
117
-
118
- state = {
119
- "object": entity.udf_sproc_identifier.identifier_with_arg_names_types_defaults,
120
- "type": entity.get_type(),
121
- }
122
- if object_exists and not replace_object:
123
- return {**state, "status": "packages updated"}
124
-
125
- self.create_or_replace(
126
- entity=entity,
127
- artifact_files=entities_to_artifact_map[entity.entity_id],
128
- snowflake_dependencies=snowflake_dependencies,
129
- )
130
- return {
131
- **state,
132
- "status": "created" if not object_exists else "definition updated",
133
- }
134
-
135
-
136
- def _check_if_replace_is_required(
137
- entity: SnowparkEntityModel,
138
- current_state,
139
- snowflake_dependencies: List[str],
140
- stage_artifact_files: set[str],
141
- ) -> bool:
142
- object_type = entity.get_type()
143
- resource_json = _convert_resource_details_to_dict(current_state)
144
- old_dependencies = resource_json["packages"]
145
-
146
- if _snowflake_dependencies_differ(old_dependencies, snowflake_dependencies):
147
- log.info(
148
- "Found difference of package requirements. Replacing the %s.", object_type
149
- )
150
- return True
151
-
152
- if set(entity.external_access_integrations) != set(
153
- resource_json.get("external_access_integrations", [])
154
- ):
155
- log.info(
156
- "Found difference of external access integrations. Replacing the %s.",
157
- object_type,
158
- )
159
- return True
160
-
161
- if (
162
- resource_json["handler"].lower() != entity.handler.lower()
163
- or _sql_to_python_return_type_mapper(resource_json["returns"]).lower()
164
- != entity.returns.lower()
165
- ):
166
- log.info(
167
- "Return type or handler types do not match. Replacing the %s.", object_type
168
- )
169
- return True
170
-
171
- if _compare_imports(resource_json, entity.imports, stage_artifact_files):
172
- log.info("Imports do not match. Replacing the %s", object_type)
173
- return True
174
-
175
- if entity.runtime is not None and entity.runtime != resource_json.get(
176
- "runtime_version", "RUNTIME_NOT_SET"
177
- ):
178
- log.info("Runtime versions do not match. Replacing the %s", object_type)
179
- return True
180
-
181
- if isinstance(entity, ProcedureEntityModel):
182
- if resource_json.get("execute as", "OWNER") != (
183
- "CALLER" if entity.execute_as_caller else "OWNER"
184
- ):
185
- log.info(
186
- "Execute as caller settings do not match. Replacing the %s", object_type
187
- )
188
- return True
189
-
190
- return False
191
-
192
-
193
- def _convert_resource_details_to_dict(function_details: SnowflakeCursor) -> dict:
194
- import json
195
-
196
- function_dict = {}
197
- json_properties = ["packages", "installed_packages"]
198
- for function in function_details:
199
- if function[0] in json_properties:
200
- function_dict[function[0]] = json.loads(
201
- function[1].replace("'", '"'),
202
- )
203
- else:
204
- function_dict[function[0]] = function[1]
205
- return function_dict
206
-
207
-
208
- def _snowflake_dependencies_differ(
209
- old_dependencies: List[str], new_dependencies: List[str]
210
- ) -> bool:
211
- def _standardize(packages: List[str]) -> Set[str]:
212
- return set(
213
- Requirement.parse_line(package).name_and_version for package in packages
214
- )
215
-
216
- return _standardize(old_dependencies) != _standardize(new_dependencies)
217
-
218
-
219
- def _sql_to_python_return_type_mapper(resource_return_type: str) -> str:
220
- """
221
- Some of the Python data types get converted to SQL types, when function/procedure is created.
222
- So, to properly compare types, we use mapping based on:
223
- https://docs.snowflake.com/en/developer-guide/udf-stored-procedure-data-type-mapping#sql-python-data-type-mappings
224
-
225
- Mind you, this only applies to cases, in which Snowflake accepts Python type as return.
226
- Ie. if function returns list, it has to be declared as 'array' during creation,
227
- therefore any conversion is not necessary
228
- """
229
- mapping = {
230
- "number(38,0)": "int",
231
- "timestamp_ntz(9)": "datetime",
232
- "timestamp_tz(9)": "datetime",
233
- "varchar(16777216)": "string",
234
- }
235
-
236
- return mapping.get(resource_return_type.lower(), resource_return_type.lower())
237
-
238
-
239
- def _compare_imports(
240
- resource_json: dict, imports: List[str], artifact_files: set[str]
241
- ) -> bool:
242
- pattern = re.compile(r"(?:\[@?\w+_\w+\.)?(\w+(?:/\w+)+\.\w+)(?:\])?")
243
-
244
- project_imports = {
245
- imp
246
- for import_string in [*imports, *artifact_files]
247
- for imp in pattern.findall(import_string.lower())
248
- }
249
-
250
- if "imports" not in resource_json.keys():
251
- object_imports = set()
252
- else:
253
- object_imports = {
254
- imp.lower()
255
- for imp in pattern.findall(resource_json.get("imports", "").lower())
256
- }
257
-
258
- return project_imports != object_imports
259
-
260
-
261
- def is_name_a_templated_one(name: str) -> bool:
262
- return (
263
- PROJECT_TEMPLATE_VARIABLE_OPENING in name
264
- and PROJECT_TEMPLATE_VARIABLE_CLOSING in name
265
- ) or (
266
- INIT_TEMPLATE_VARIABLE_OPENING in name
267
- and INIT_TEMPLATE_VARIABLE_CLOSING in name
268
- )
@@ -1,150 +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 re
18
- import zipfile
19
- from dataclasses import dataclass
20
- from pathlib import Path
21
- from typing import List
22
-
23
- from requirements import requirement
24
-
25
-
26
- class Requirement(requirement.Requirement):
27
- extra_pattern = re.compile("'([^']*)'")
28
-
29
- def __init__(self, *args, **kwargs):
30
- super().__init__(*args, **kwargs)
31
- self.package_name = None
32
-
33
- @classmethod
34
- def parse_line(cls, line: str) -> Requirement:
35
- if len(line_elements := line.split(";")) > 1:
36
- line = line_elements[0]
37
- result = super().parse_line(line)
38
-
39
- if len(line_elements) > 1:
40
- for element in line_elements[1:]:
41
- if "extra" in element and (extras := cls.extra_pattern.search(element)):
42
- result.extras.extend(extras.groups())
43
-
44
- result.package_name = result.name
45
-
46
- if result.uri and not result.name:
47
- result.name = get_package_name(result.uri)
48
- result.name = cls.standardize_name(result.name)
49
-
50
- return result
51
-
52
- @staticmethod
53
- def standardize_name(name: str) -> str:
54
- return WheelMetadata.to_wheel_name_format(name.lower())
55
-
56
- @property
57
- def formatted_specs(self):
58
- return ",".join(sorted(spec[0] + spec[1] for spec in self.specs))
59
-
60
- @property
61
- def name_and_version(self):
62
- return self.name + self.formatted_specs
63
-
64
-
65
- @dataclass
66
- class RequirementWithFiles:
67
- """A dataclass to hold a requirement and the path to the
68
- downloaded files/folders that belong to it"""
69
-
70
- requirement: Requirement
71
- files: List[str]
72
-
73
-
74
- @dataclass
75
- class RequirementWithWheel:
76
- """A dataclass to hold a requirement and corresponding .whl file."""
77
-
78
- requirement: Requirement
79
- wheel_path: Path | None
80
-
81
- def extract_files(self, destination: Path) -> None:
82
- if self.wheel_path is not None:
83
- zipfile.ZipFile(self.wheel_path).extractall(destination)
84
-
85
- def namelist(self) -> List[str]:
86
- if self.wheel_path is None:
87
- return []
88
- return zipfile.ZipFile(self.wheel_path).namelist()
89
-
90
-
91
- @dataclass
92
- class WheelMetadata:
93
- """A dataclass to hold metadata from .whl file.
94
- [name] is the name of the package standardized according to
95
- https://peps.python.org/pep-0491/#escaping-and-unicode
96
- """
97
-
98
- name: str
99
- wheel_path: Path
100
- dependencies: List[str]
101
-
102
- @classmethod
103
- def from_wheel(cls, wheel_path: Path):
104
- """Parses wheel metadata according to
105
- https://peps.python.org/pep-0491/#file-contents"""
106
- with zipfile.ZipFile(wheel_path, "r") as whl:
107
- metadata_path = [
108
- path for path in whl.namelist() if path.endswith(".dist-info/METADATA")
109
- ]
110
- if len(metadata_path) != 1:
111
- # malformatted wheel package
112
- return None
113
-
114
- root = zipfile.Path(whl)
115
- metadata = (root / metadata_path[0]).read_text(encoding="utf-8")
116
-
117
- dep_keyword = "Requires-Dist:"
118
- dependencies = [
119
- line[len(dep_keyword) :].strip()
120
- for line in metadata.splitlines()
121
- if line.startswith(dep_keyword)
122
- ]
123
- name = cls._get_name_from_wheel_filename(wheel_path.name)
124
-
125
- return cls(name=name, wheel_path=wheel_path, dependencies=dependencies)
126
-
127
- @staticmethod
128
- def _get_name_from_wheel_filename(wheel_filename: str) -> str:
129
- # wheel filename is in format {name}-{version}[-{extra info}]
130
- # https://peps.python.org/pep-0491/#file-name-convention
131
- return wheel_filename.split("-")[0].lower()
132
-
133
- @staticmethod
134
- def to_wheel_name_format(package_name: str) -> str:
135
- # https://peps.python.org/pep-0491/#escaping-and-unicode
136
- return re.sub(r"[^\w\d.]+", "_", package_name, re.UNICODE)
137
-
138
-
139
- def get_package_name(name: str) -> str:
140
- if name.lower().startswith(("git+", "http")):
141
- pattern = re.compile(r"github\.com\/[^\/]+\/([^\/][^.@$/]+)")
142
- if match := pattern.search(name):
143
- return match.group(1)
144
- else:
145
- return name
146
-
147
- elif name.endswith(".zip"):
148
- return name.replace(".zip", "")
149
- else:
150
- return name
@@ -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,199 +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 dataclasses import dataclass
19
- from typing import Dict, List, Set
20
-
21
- from packaging.requirements import InvalidRequirement
22
- from packaging.requirements import Requirement as PkgRequirement
23
- from packaging.version import InvalidVersion, parse
24
- from snowflake.cli._plugins.snowpark.models import Requirement
25
- from snowflake.cli.api.exceptions import SnowflakeSQLExecutionError
26
- from snowflake.cli.api.secure_path import SecurePath
27
- from snowflake.cli.api.sql_execution import SqlExecutionMixin
28
- from snowflake.connector import DictCursor
29
-
30
- log = logging.getLogger(__name__)
31
-
32
-
33
- @dataclass
34
- class FilterRequirementsResult:
35
- """A dataclass to hold the results of parsing requirements files and dividing them into
36
- snowflake-supported vs other packages.
37
- """
38
-
39
- in_snowflake: List[Requirement]
40
- unavailable: List[Requirement]
41
-
42
-
43
- @dataclass
44
- class AvailablePackage:
45
- snowflake_name: str
46
- versions: Set[str]
47
-
48
- def iter_versions(self):
49
- for version in self.versions:
50
- yield parse(version)
51
-
52
- def is_required_version_available(self, requirement: Requirement) -> bool:
53
- try:
54
- package_specifiers = PkgRequirement(requirement.line).specifier
55
- return any(
56
- version in package_specifiers for version in self.iter_versions()
57
- )
58
- except (InvalidVersion, InvalidRequirement):
59
- # fail-safe for non-pep508 formats
60
- return False
61
-
62
-
63
- class AnacondaPackages:
64
- def __init__(self, packages: Dict[str, AvailablePackage]):
65
- """
66
- [packages] should be a dictionary mapping package name to AnacondaPackageData object.
67
- All package names should be provided in wheel escape format:
68
- https://peps.python.org/pep-0491/#escaping-and-unicode
69
- """
70
- self._packages = packages
71
-
72
- @classmethod
73
- def empty(cls):
74
- return cls({})
75
-
76
- def is_package_available(
77
- self, package: Requirement, skip_version_check: bool = False
78
- ) -> bool:
79
- """
80
- Checks of a requirement is available in the Snowflake Anaconda Channel.
81
-
82
- As Snowflake currently doesn't support extra syntax (ex. `jinja2[diagrams]`), if such
83
- extra is present in the dependency, we mark it as unavailable.
84
- """
85
- if not package.name or package.extras:
86
- return False
87
- if package.name not in self._packages:
88
- return False
89
- if skip_version_check or not package.specs:
90
- return True
91
- return self._packages[package.name].is_required_version_available(package)
92
-
93
- def package_latest_version(self, package: Requirement) -> str | None:
94
- """Returns the latest version of the package or None if the latest version can't be determined."""
95
- if package.name not in self._packages:
96
- return None
97
- try:
98
- return str(max(self._packages[package.name].iter_versions()))
99
- except InvalidVersion:
100
- # fail-safe for non-pep8 versions
101
- return None
102
-
103
- def package_versions(self, package: Requirement) -> List[str]:
104
- """Returns list of available versions of the package."""
105
- if package.name not in self._packages:
106
- return []
107
- package_data = self._packages[package.name]
108
- try:
109
- return list(
110
- str(x) for x in sorted(package_data.iter_versions(), reverse=True)
111
- )
112
- except InvalidVersion:
113
- return list(sorted(package_data.versions, reverse=True))
114
-
115
- def filter_available_packages(
116
- self, packages: List[Requirement], skip_version_check: bool = False
117
- ) -> FilterRequirementsResult:
118
- """
119
- Checks if a list of packages are available in the Snowflake Anaconda channel.
120
- Returns an object with two attributes: 'snowflake' and 'other'.
121
- Each key contains a list of Requirement object.
122
-
123
- Parameters:
124
- packages (List[Requirement]) - list of requirements to be checked
125
- skip_version_check (bool) - skip comparing versions of packages
126
-
127
- Returns:
128
- result (FilterRequirementsResult) - object containing two arguments:
129
- - in_snowflake - packages available in conda
130
- - unavailable - packages not available in conda
131
- """
132
- result = FilterRequirementsResult([], [])
133
- for package in packages:
134
- if self.is_package_available(
135
- package, skip_version_check=skip_version_check
136
- ):
137
- result.in_snowflake.append(package)
138
- else:
139
- log.info(
140
- "'%s' not found in Snowflake Anaconda channel (or ignored)...",
141
- package.name,
142
- )
143
- result.unavailable.append(package)
144
- return result
145
-
146
- def write_requirements_file_in_snowflake_format(
147
- self,
148
- file_path: SecurePath,
149
- requirements: List[Requirement],
150
- ):
151
- """Saves requirements to a file in format accepted by Snowflake SQL commands."""
152
- log.info("Writing requirements into file %s", file_path.path)
153
- formatted_requirements = []
154
- for requirement in requirements:
155
- if requirement.name and requirement.name in self._packages:
156
- snowflake_name = self._packages[requirement.name].snowflake_name
157
- formatted_requirements.append(
158
- snowflake_name + requirement.formatted_specs
159
- )
160
-
161
- if formatted_requirements:
162
- file_path.write_text("\n".join(formatted_requirements))
163
-
164
-
165
- class AnacondaPackagesManager(SqlExecutionMixin):
166
- _snowflake_channel_url: str = (
167
- "https://repo.anaconda.com/pkgs/snowflake/channeldata.json"
168
- )
169
-
170
- def find_packages_available_in_snowflake_anaconda(self) -> AnacondaPackages:
171
- """
172
- Finds python packages available in Snowflake to use in functions and stored procedures.
173
- It tries to get the list of packages using SQL query
174
- but if the try fails then the fallback is to parse JSON containing info about Snowflake's Anaconda channel.
175
- """
176
- packages = self._query_snowflake_for_available_packages()
177
- return AnacondaPackages(packages)
178
-
179
- def _query_snowflake_for_available_packages(self) -> dict[str, AvailablePackage]:
180
- cursor = self._execute_query(
181
- "select package_name, version from information_schema.packages where language = 'python'",
182
- cursor_class=DictCursor,
183
- )
184
- if cursor.rowcount is None or cursor.rowcount == 0:
185
- raise SnowflakeSQLExecutionError()
186
- packages: dict[str, AvailablePackage] = {}
187
- for row in cursor:
188
- if not (package_name := row["PACKAGE_NAME"]):
189
- continue
190
- if not (version := row["VERSION"]):
191
- continue
192
- standardized_name = Requirement.standardize_name(package_name)
193
- if standardized_name in packages:
194
- packages[standardized_name].versions.add(version)
195
- else:
196
- packages[standardized_name] = AvailablePackage(
197
- snowflake_name=package_name, versions={version}
198
- )
199
- return packages