snowflake-cli-labs 2.8.0rc1__py3-none-any.whl → 2.8.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 (242) hide show
  1. README.md +21 -0
  2. {snowflake_cli_labs-2.8.0rc1.dist-info → snowflake_cli_labs-2.8.2.dist-info}/METADATA +7 -95
  3. snowflake_cli_labs-2.8.2.dist-info/RECORD +5 -0
  4. snowflake/cli/__about__.py +0 -17
  5. snowflake/cli/__init__.py +0 -13
  6. snowflake/cli/api/__init__.py +0 -48
  7. snowflake/cli/api/cli_global_context.py +0 -390
  8. snowflake/cli/api/commands/__init__.py +0 -13
  9. snowflake/cli/api/commands/alias.py +0 -23
  10. snowflake/cli/api/commands/decorators.py +0 -354
  11. snowflake/cli/api/commands/execution_metadata.py +0 -40
  12. snowflake/cli/api/commands/experimental_behaviour.py +0 -19
  13. snowflake/cli/api/commands/flags.py +0 -640
  14. snowflake/cli/api/commands/project_initialisation.py +0 -65
  15. snowflake/cli/api/commands/snow_typer.py +0 -237
  16. snowflake/cli/api/commands/typer_pre_execute.py +0 -26
  17. snowflake/cli/api/config.py +0 -348
  18. snowflake/cli/api/console/__init__.py +0 -17
  19. snowflake/cli/api/console/abc.py +0 -89
  20. snowflake/cli/api/console/console.py +0 -134
  21. snowflake/cli/api/console/enum.py +0 -17
  22. snowflake/cli/api/constants.py +0 -79
  23. snowflake/cli/api/errno.py +0 -27
  24. snowflake/cli/api/exceptions.py +0 -164
  25. snowflake/cli/api/feature_flags.py +0 -55
  26. snowflake/cli/api/identifiers.py +0 -154
  27. snowflake/cli/api/output/__init__.py +0 -13
  28. snowflake/cli/api/output/formats.py +0 -20
  29. snowflake/cli/api/output/types.py +0 -118
  30. snowflake/cli/api/plugins/__init__.py +0 -13
  31. snowflake/cli/api/plugins/command/__init__.py +0 -72
  32. snowflake/cli/api/plugins/command/plugin_hook_specs.py +0 -21
  33. snowflake/cli/api/plugins/plugin_config.py +0 -32
  34. snowflake/cli/api/project/__init__.py +0 -13
  35. snowflake/cli/api/project/definition.py +0 -84
  36. snowflake/cli/api/project/definition_manager.py +0 -134
  37. snowflake/cli/api/project/errors.py +0 -56
  38. snowflake/cli/api/project/project_verification.py +0 -23
  39. snowflake/cli/api/project/schemas/__init__.py +0 -13
  40. snowflake/cli/api/project/schemas/entities/application_entity.py +0 -44
  41. snowflake/cli/api/project/schemas/entities/application_package_entity.py +0 -66
  42. snowflake/cli/api/project/schemas/entities/common.py +0 -78
  43. snowflake/cli/api/project/schemas/entities/entities.py +0 -30
  44. snowflake/cli/api/project/schemas/identifier_model.py +0 -49
  45. snowflake/cli/api/project/schemas/native_app/__init__.py +0 -13
  46. snowflake/cli/api/project/schemas/native_app/application.py +0 -62
  47. snowflake/cli/api/project/schemas/native_app/native_app.py +0 -93
  48. snowflake/cli/api/project/schemas/native_app/package.py +0 -78
  49. snowflake/cli/api/project/schemas/native_app/path_mapping.py +0 -65
  50. snowflake/cli/api/project/schemas/project_definition.py +0 -199
  51. snowflake/cli/api/project/schemas/snowpark/__init__.py +0 -13
  52. snowflake/cli/api/project/schemas/snowpark/argument.py +0 -28
  53. snowflake/cli/api/project/schemas/snowpark/callable.py +0 -69
  54. snowflake/cli/api/project/schemas/snowpark/snowpark.py +0 -36
  55. snowflake/cli/api/project/schemas/streamlit/__init__.py +0 -13
  56. snowflake/cli/api/project/schemas/streamlit/streamlit.py +0 -46
  57. snowflake/cli/api/project/schemas/template.py +0 -77
  58. snowflake/cli/api/project/schemas/updatable_model.py +0 -194
  59. snowflake/cli/api/project/util.py +0 -261
  60. snowflake/cli/api/rendering/__init__.py +0 -13
  61. snowflake/cli/api/rendering/jinja.py +0 -112
  62. snowflake/cli/api/rendering/project_definition_templates.py +0 -39
  63. snowflake/cli/api/rendering/project_templates.py +0 -98
  64. snowflake/cli/api/rendering/sql_templates.py +0 -60
  65. snowflake/cli/api/rest_api.py +0 -172
  66. snowflake/cli/api/sanitizers.py +0 -43
  67. snowflake/cli/api/secure_path.py +0 -362
  68. snowflake/cli/api/secure_utils.py +0 -29
  69. snowflake/cli/api/sql_execution.py +0 -260
  70. snowflake/cli/api/utils/__init__.py +0 -13
  71. snowflake/cli/api/utils/cursor.py +0 -34
  72. snowflake/cli/api/utils/definition_rendering.py +0 -383
  73. snowflake/cli/api/utils/dict_utils.py +0 -73
  74. snowflake/cli/api/utils/error_handling.py +0 -23
  75. snowflake/cli/api/utils/graph.py +0 -97
  76. snowflake/cli/api/utils/models.py +0 -63
  77. snowflake/cli/api/utils/naming_utils.py +0 -13
  78. snowflake/cli/api/utils/path_utils.py +0 -36
  79. snowflake/cli/api/utils/templating_functions.py +0 -144
  80. snowflake/cli/api/utils/types.py +0 -35
  81. snowflake/cli/app/__init__.py +0 -22
  82. snowflake/cli/app/__main__.py +0 -31
  83. snowflake/cli/app/api_impl/__init__.py +0 -13
  84. snowflake/cli/app/api_impl/plugin/__init__.py +0 -13
  85. snowflake/cli/app/api_impl/plugin/plugin_config_provider_impl.py +0 -66
  86. snowflake/cli/app/build_and_push.sh +0 -8
  87. snowflake/cli/app/cli_app.py +0 -243
  88. snowflake/cli/app/commands_registration/__init__.py +0 -33
  89. snowflake/cli/app/commands_registration/builtin_plugins.py +0 -54
  90. snowflake/cli/app/commands_registration/command_plugins_loader.py +0 -169
  91. snowflake/cli/app/commands_registration/commands_registration_with_callbacks.py +0 -105
  92. snowflake/cli/app/commands_registration/exception_logging.py +0 -26
  93. snowflake/cli/app/commands_registration/threadsafe.py +0 -48
  94. snowflake/cli/app/commands_registration/typer_registration.py +0 -153
  95. snowflake/cli/app/constants.py +0 -19
  96. snowflake/cli/app/dev/__init__.py +0 -13
  97. snowflake/cli/app/dev/commands_structure.py +0 -48
  98. snowflake/cli/app/dev/docs/__init__.py +0 -13
  99. snowflake/cli/app/dev/docs/commands_docs_generator.py +0 -100
  100. snowflake/cli/app/dev/docs/generator.py +0 -35
  101. snowflake/cli/app/dev/docs/project_definition_docs_generator.py +0 -58
  102. snowflake/cli/app/dev/docs/project_definition_generate_json_schema.py +0 -227
  103. snowflake/cli/app/dev/docs/template_utils.py +0 -23
  104. snowflake/cli/app/dev/docs/templates/definition_description.rst.jinja2 +0 -38
  105. snowflake/cli/app/dev/docs/templates/overview.rst.jinja2 +0 -9
  106. snowflake/cli/app/dev/docs/templates/usage.rst.jinja2 +0 -57
  107. snowflake/cli/app/dev/pycharm_remote_debug.py +0 -46
  108. snowflake/cli/app/loggers.py +0 -199
  109. snowflake/cli/app/main_typer.py +0 -62
  110. snowflake/cli/app/printing.py +0 -181
  111. snowflake/cli/app/snow_connector.py +0 -243
  112. snowflake/cli/app/telemetry.py +0 -189
  113. snowflake/cli/plugins/__init__.py +0 -13
  114. snowflake/cli/plugins/connection/__init__.py +0 -13
  115. snowflake/cli/plugins/connection/commands.py +0 -330
  116. snowflake/cli/plugins/connection/plugin_spec.py +0 -30
  117. snowflake/cli/plugins/connection/util.py +0 -179
  118. snowflake/cli/plugins/cortex/__init__.py +0 -13
  119. snowflake/cli/plugins/cortex/commands.py +0 -327
  120. snowflake/cli/plugins/cortex/constants.py +0 -17
  121. snowflake/cli/plugins/cortex/manager.py +0 -189
  122. snowflake/cli/plugins/cortex/plugin_spec.py +0 -30
  123. snowflake/cli/plugins/cortex/types.py +0 -22
  124. snowflake/cli/plugins/git/__init__.py +0 -13
  125. snowflake/cli/plugins/git/commands.py +0 -305
  126. snowflake/cli/plugins/git/manager.py +0 -96
  127. snowflake/cli/plugins/git/plugin_spec.py +0 -30
  128. snowflake/cli/plugins/init/__init__.py +0 -13
  129. snowflake/cli/plugins/init/commands.py +0 -244
  130. snowflake/cli/plugins/init/plugin_spec.py +0 -30
  131. snowflake/cli/plugins/nativeapp/__init__.py +0 -13
  132. snowflake/cli/plugins/nativeapp/artifacts.py +0 -742
  133. snowflake/cli/plugins/nativeapp/codegen/__init__.py +0 -13
  134. snowflake/cli/plugins/nativeapp/codegen/artifact_processor.py +0 -91
  135. snowflake/cli/plugins/nativeapp/codegen/compiler.py +0 -130
  136. snowflake/cli/plugins/nativeapp/codegen/sandbox.py +0 -306
  137. snowflake/cli/plugins/nativeapp/codegen/setup/native_app_setup_processor.py +0 -172
  138. snowflake/cli/plugins/nativeapp/codegen/setup/setup_driver.py.source +0 -56
  139. snowflake/cli/plugins/nativeapp/codegen/snowpark/callback_source.py.jinja +0 -181
  140. snowflake/cli/plugins/nativeapp/codegen/snowpark/extension_function_utils.py +0 -217
  141. snowflake/cli/plugins/nativeapp/codegen/snowpark/models.py +0 -61
  142. snowflake/cli/plugins/nativeapp/codegen/snowpark/python_processor.py +0 -528
  143. snowflake/cli/plugins/nativeapp/commands.py +0 -439
  144. snowflake/cli/plugins/nativeapp/common_flags.py +0 -44
  145. snowflake/cli/plugins/nativeapp/constants.py +0 -27
  146. snowflake/cli/plugins/nativeapp/exceptions.py +0 -122
  147. snowflake/cli/plugins/nativeapp/feature_flags.py +0 -24
  148. snowflake/cli/plugins/nativeapp/init.py +0 -345
  149. snowflake/cli/plugins/nativeapp/manager.py +0 -823
  150. snowflake/cli/plugins/nativeapp/plugin_spec.py +0 -30
  151. snowflake/cli/plugins/nativeapp/policy.py +0 -50
  152. snowflake/cli/plugins/nativeapp/project_model.py +0 -195
  153. snowflake/cli/plugins/nativeapp/run_processor.py +0 -389
  154. snowflake/cli/plugins/nativeapp/teardown_processor.py +0 -301
  155. snowflake/cli/plugins/nativeapp/utils.py +0 -98
  156. snowflake/cli/plugins/nativeapp/v2_conversions/v2_to_v1_decorator.py +0 -135
  157. snowflake/cli/plugins/nativeapp/version/__init__.py +0 -13
  158. snowflake/cli/plugins/nativeapp/version/commands.py +0 -170
  159. snowflake/cli/plugins/nativeapp/version/version_processor.py +0 -362
  160. snowflake/cli/plugins/notebook/__init__.py +0 -13
  161. snowflake/cli/plugins/notebook/commands.py +0 -84
  162. snowflake/cli/plugins/notebook/exceptions.py +0 -20
  163. snowflake/cli/plugins/notebook/manager.py +0 -71
  164. snowflake/cli/plugins/notebook/plugin_spec.py +0 -30
  165. snowflake/cli/plugins/notebook/types.py +0 -16
  166. snowflake/cli/plugins/object/__init__.py +0 -13
  167. snowflake/cli/plugins/object/command_aliases.py +0 -94
  168. snowflake/cli/plugins/object/commands.py +0 -174
  169. snowflake/cli/plugins/object/common.py +0 -85
  170. snowflake/cli/plugins/object/manager.py +0 -96
  171. snowflake/cli/plugins/object/plugin_spec.py +0 -30
  172. snowflake/cli/plugins/object_stage_deprecated/__init__.py +0 -15
  173. snowflake/cli/plugins/object_stage_deprecated/commands.py +0 -122
  174. snowflake/cli/plugins/object_stage_deprecated/plugin_spec.py +0 -32
  175. snowflake/cli/plugins/snowpark/__init__.py +0 -13
  176. snowflake/cli/plugins/snowpark/commands.py +0 -548
  177. snowflake/cli/plugins/snowpark/common.py +0 -307
  178. snowflake/cli/plugins/snowpark/manager.py +0 -109
  179. snowflake/cli/plugins/snowpark/models.py +0 -156
  180. snowflake/cli/plugins/snowpark/package/__init__.py +0 -13
  181. snowflake/cli/plugins/snowpark/package/anaconda_packages.py +0 -233
  182. snowflake/cli/plugins/snowpark/package/commands.py +0 -256
  183. snowflake/cli/plugins/snowpark/package/manager.py +0 -43
  184. snowflake/cli/plugins/snowpark/package/utils.py +0 -26
  185. snowflake/cli/plugins/snowpark/package_utils.py +0 -354
  186. snowflake/cli/plugins/snowpark/plugin_spec.py +0 -30
  187. snowflake/cli/plugins/snowpark/snowpark_package_paths.py +0 -65
  188. snowflake/cli/plugins/snowpark/snowpark_shared.py +0 -95
  189. snowflake/cli/plugins/snowpark/zipper.py +0 -81
  190. snowflake/cli/plugins/spcs/__init__.py +0 -35
  191. snowflake/cli/plugins/spcs/common.py +0 -99
  192. snowflake/cli/plugins/spcs/compute_pool/__init__.py +0 -13
  193. snowflake/cli/plugins/spcs/compute_pool/commands.py +0 -240
  194. snowflake/cli/plugins/spcs/compute_pool/manager.py +0 -121
  195. snowflake/cli/plugins/spcs/image_registry/__init__.py +0 -13
  196. snowflake/cli/plugins/spcs/image_registry/commands.py +0 -65
  197. snowflake/cli/plugins/spcs/image_registry/manager.py +0 -105
  198. snowflake/cli/plugins/spcs/image_repository/__init__.py +0 -13
  199. snowflake/cli/plugins/spcs/image_repository/commands.py +0 -196
  200. snowflake/cli/plugins/spcs/image_repository/manager.py +0 -84
  201. snowflake/cli/plugins/spcs/jobs/__init__.py +0 -13
  202. snowflake/cli/plugins/spcs/jobs/commands.py +0 -78
  203. snowflake/cli/plugins/spcs/jobs/manager.py +0 -53
  204. snowflake/cli/plugins/spcs/plugin_spec.py +0 -30
  205. snowflake/cli/plugins/spcs/services/__init__.py +0 -13
  206. snowflake/cli/plugins/spcs/services/commands.py +0 -311
  207. snowflake/cli/plugins/spcs/services/manager.py +0 -170
  208. snowflake/cli/plugins/sql/__init__.py +0 -13
  209. snowflake/cli/plugins/sql/commands.py +0 -83
  210. snowflake/cli/plugins/sql/manager.py +0 -92
  211. snowflake/cli/plugins/sql/plugin_spec.py +0 -30
  212. snowflake/cli/plugins/sql/snowsql_templating.py +0 -28
  213. snowflake/cli/plugins/stage/__init__.py +0 -13
  214. snowflake/cli/plugins/stage/commands.py +0 -261
  215. snowflake/cli/plugins/stage/diff.py +0 -326
  216. snowflake/cli/plugins/stage/manager.py +0 -544
  217. snowflake/cli/plugins/stage/md5.py +0 -160
  218. snowflake/cli/plugins/stage/plugin_spec.py +0 -30
  219. snowflake/cli/plugins/streamlit/__init__.py +0 -13
  220. snowflake/cli/plugins/streamlit/commands.py +0 -186
  221. snowflake/cli/plugins/streamlit/manager.py +0 -222
  222. snowflake/cli/plugins/streamlit/plugin_spec.py +0 -30
  223. snowflake/cli/plugins/workspace/__init__.py +0 -13
  224. snowflake/cli/plugins/workspace/commands.py +0 -35
  225. snowflake/cli/plugins/workspace/plugin_spec.py +0 -30
  226. snowflake/cli/templates/default_snowpark/.gitignore +0 -4
  227. snowflake/cli/templates/default_snowpark/app/__init__.py +0 -0
  228. snowflake/cli/templates/default_snowpark/app/common.py +0 -2
  229. snowflake/cli/templates/default_snowpark/app/functions.py +0 -15
  230. snowflake/cli/templates/default_snowpark/app/procedures.py +0 -22
  231. snowflake/cli/templates/default_snowpark/requirements.txt +0 -1
  232. snowflake/cli/templates/default_snowpark/snowflake.yml +0 -23
  233. snowflake/cli/templates/default_streamlit/.gitignore +0 -4
  234. snowflake/cli/templates/default_streamlit/common/hello.py +0 -2
  235. snowflake/cli/templates/default_streamlit/environment.yml +0 -6
  236. snowflake/cli/templates/default_streamlit/pages/my_page.py +0 -3
  237. snowflake/cli/templates/default_streamlit/snowflake.yml +0 -10
  238. snowflake/cli/templates/default_streamlit/streamlit_app.py +0 -4
  239. snowflake_cli_labs-2.8.0rc1.dist-info/RECORD +0 -240
  240. snowflake_cli_labs-2.8.0rc1.dist-info/entry_points.txt +0 -2
  241. {snowflake_cli_labs-2.8.0rc1.dist-info → snowflake_cli_labs-2.8.2.dist-info}/WHEEL +0 -0
  242. {snowflake_cli_labs-2.8.0rc1.dist-info → snowflake_cli_labs-2.8.2.dist-info}/licenses/LICENSE +0 -0
@@ -1,172 +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 json
18
- import logging
19
- from typing import Any, Dict, Optional
20
-
21
- from click import ClickException
22
- from snowflake.cli.api.constants import SF_REST_API_URL_PREFIX
23
- from snowflake.connector.connection import SnowflakeConnection
24
- from snowflake.connector.errors import BadRequest, InterfaceError
25
- from snowflake.connector.network import SnowflakeRestful
26
-
27
- log = logging.getLogger(__name__)
28
-
29
-
30
- def _pluralize_object_type(object_type: str) -> str:
31
- """
32
- Pluralize object type without depending on OBJECT_TO_NAMES.
33
- """
34
- if object_type.endswith("y"):
35
- return object_type[:-1].lower() + "ies"
36
- else:
37
- return object_type.lower() + "s"
38
-
39
-
40
- class RestApi:
41
- def __init__(self, connection: SnowflakeConnection):
42
- self.conn = connection
43
- self.rest: SnowflakeRestful = connection.rest
44
-
45
- def get_endpoint_exists(self, url: str) -> bool:
46
- """
47
- Check whether [get] endpoint exists under given URL.
48
- """
49
- try:
50
- result = self.send_rest_request(url, method="get")
51
- return bool(result) or result == []
52
- except InterfaceError as err:
53
- if "404 Not Found" in str(err):
54
- return False
55
- raise err
56
-
57
- def _fetch_endpoint_exists(self, url: str) -> bool:
58
- try:
59
- result = self.send_rest_request(url, method="get")
60
- return bool(result)
61
- except BadRequest:
62
- return False
63
-
64
- def send_rest_request(
65
- self, url: str, method: str, data: Optional[Dict[str, Any]] = None
66
- ):
67
- """
68
- Executes rest request via snowflake.connector.network.SnowflakeRestful
69
- """
70
- # SnowflakeRestful.request assumes that API response is always a dict,
71
- # which is not true in case of this API, so we need to do this workaround:
72
- from snowflake.connector.network import (
73
- CONTENT_TYPE_APPLICATION_JSON,
74
- HTTP_HEADER_ACCEPT,
75
- HTTP_HEADER_CONTENT_TYPE,
76
- HTTP_HEADER_USER_AGENT,
77
- PYTHON_CONNECTOR_USER_AGENT,
78
- )
79
-
80
- log.debug("Sending %s request to %s", method, url)
81
- full_url = f"{self.rest.server_url}{url}"
82
- headers = {
83
- HTTP_HEADER_CONTENT_TYPE: CONTENT_TYPE_APPLICATION_JSON,
84
- HTTP_HEADER_ACCEPT: CONTENT_TYPE_APPLICATION_JSON,
85
- HTTP_HEADER_USER_AGENT: PYTHON_CONNECTOR_USER_AGENT,
86
- }
87
- return self.rest.fetch(
88
- method=method,
89
- full_url=full_url,
90
- headers=headers,
91
- token=self.rest.token,
92
- data=json.dumps(data if data else {}),
93
- no_retry=True,
94
- )
95
-
96
- def _database_exists(self, db_name: str) -> bool:
97
- url = f"{SF_REST_API_URL_PREFIX}/databases/{db_name}"
98
- return self._fetch_endpoint_exists(url)
99
-
100
- def _schema_exists(self, db_name: str, schema_name: str) -> bool:
101
- url = f"{SF_REST_API_URL_PREFIX}/databases/{db_name}/schemas/{schema_name}"
102
- return self._fetch_endpoint_exists(url)
103
-
104
- def determine_url_for_create_query(self, object_type: str) -> str:
105
- """
106
- Determine an url for creating an object of given type via REST API.
107
- If URL cannot be determined, the function throws CannotDetermineCreateURLException exception.
108
-
109
- URLs we check:
110
- * /api/v2/<type>/
111
- * /api/v2/databases/<database>/<type>/
112
- * /api/v2/databases/<database>/schemas/<schema>/<type>/
113
-
114
- We assume that the URLs for CREATE and LIST are the same for every type of object
115
- (endpoints differ by method: POST vs GET, accordingly).
116
- To check whether an URL exists, we send read-only GET request (LIST endpoint,
117
- which should imply CREATE endpoint).
118
- """
119
- plural_object_type = _pluralize_object_type(object_type)
120
-
121
- if self.get_endpoint_exists(
122
- url := f"{SF_REST_API_URL_PREFIX}/{plural_object_type}/"
123
- ):
124
- return url
125
-
126
- db = self.conn.database
127
- if not db:
128
- raise DatabaseNotDefinedException(
129
- "Database not defined in connection. Please try again with `--database` flag."
130
- )
131
- if not self._database_exists(db):
132
- raise DatabaseNotExistsException(f"Database '{db}' does not exist.")
133
- if self.get_endpoint_exists(
134
- url := f"{SF_REST_API_URL_PREFIX}/databases/{db}/{plural_object_type}/"
135
- ):
136
- return url
137
-
138
- schema = self.conn.schema
139
- if not schema:
140
- raise SchemaNotDefinedException(
141
- "Schema not defined in connection. Please try again with `--schema` flag."
142
- )
143
- if not self._schema_exists(db_name=db, schema_name=schema):
144
- raise SchemaNotExistsException(f"Schema '{schema}' does not exist.")
145
- if self.get_endpoint_exists(
146
- url := f"{SF_REST_API_URL_PREFIX}/databases/{self.conn.database}/schemas/{self.conn.schema}/{plural_object_type}/"
147
- ):
148
- return url
149
-
150
- raise CannotDetermineCreateURLException(
151
- f"Create operation for type {object_type} is not supported. Try using `sql -q 'CREATE ...'` command."
152
- )
153
-
154
-
155
- class DatabaseNotDefinedException(ClickException):
156
- pass
157
-
158
-
159
- class SchemaNotDefinedException(ClickException):
160
- pass
161
-
162
-
163
- class DatabaseNotExistsException(ClickException):
164
- pass
165
-
166
-
167
- class SchemaNotExistsException(ClickException):
168
- pass
169
-
170
-
171
- class CannotDetermineCreateURLException(ClickException):
172
- pass
@@ -1,43 +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
-
19
- # 7-bit C1 ANSI sequences
20
- _ANSI_ESCAPE = re.compile(
21
- r"""
22
- \x1B # ESC
23
- (?: # 7-bit C1 Fe (except CSI)
24
- [@-Z\\-_]
25
- | # or [ for CSI, followed by a control sequence
26
- \[
27
- [0-?]* # Parameter bytes
28
- [ -/]* # Intermediate bytes
29
- [@-~] # Final byte
30
- )
31
- """,
32
- re.VERBOSE,
33
- )
34
-
35
-
36
- def sanitize_for_terminal(text: str) -> str | None:
37
- """
38
- Escape ASCII escape codes in string. This should be always used
39
- when printing output to terminal.
40
- """
41
- if text is None:
42
- return None
43
- return _ANSI_ESCAPE.sub("", text)
@@ -1,362 +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 errno
18
- import logging
19
- import os
20
- import shutil
21
- import tempfile
22
- from contextlib import contextmanager
23
- from pathlib import Path
24
- from typing import Optional, Union
25
-
26
- from snowflake.cli.api.exceptions import DirectoryIsNotEmptyError, FileTooLargeError
27
-
28
- log = logging.getLogger(__name__)
29
-
30
- UNLIMITED = -1
31
-
32
-
33
- class SecurePath:
34
- def __init__(self, path: Union[Path, str]):
35
- self._path = Path(path)
36
-
37
- def __repr__(self):
38
- return f'SecurePath("{self._path}")'
39
-
40
- def __truediv__(self, key):
41
- return SecurePath(self._path / key)
42
-
43
- @property
44
- def path(self) -> Path:
45
- """
46
- Returns itself in pathlib.Path format
47
- """
48
- return self._path
49
-
50
- @property
51
- def parent(self):
52
- """
53
- The logical parent of the path. For details, check pathlib.Path.parent
54
- """
55
- return SecurePath(self._path.parent)
56
-
57
- def absolute(self):
58
- """
59
- Make the path absolute, without normalization or resolving symlinks.
60
- """
61
- return SecurePath(self._path.absolute())
62
-
63
- def iterdir(self):
64
- """
65
- When the path points to a directory, yield path objects of the directory contents.
66
- Otherwise, NotADirectoryError is raised.
67
- If the location does not exist, FileNotFoundError is raised.
68
-
69
- For details, check pathlib.Path.iterdir()
70
- """
71
- self.assert_exists()
72
- self.assert_is_directory()
73
- return (SecurePath(p) for p in self._path.iterdir())
74
-
75
- def exists(self) -> bool:
76
- """
77
- Return True if the path points to an existing file or directory.
78
- """
79
- return self._path.exists()
80
-
81
- def is_dir(self) -> bool:
82
- """
83
- Return True if the path points to a directory (or a symbolic link pointing to a directory),
84
- False if it points to another kind of file.
85
- """
86
- return self._path.is_dir()
87
-
88
- def is_file(self) -> bool:
89
- """
90
- Return True if the path points to a regular file (or a symbolic link pointing to a regular file),
91
- False if it points to another kind of file.
92
- """
93
- return self._path.is_file()
94
-
95
- @property
96
- def name(self) -> str:
97
- """A string representing the final path component."""
98
- return self._path.name
99
-
100
- def chmod(self, permissions_mask: int) -> None:
101
- """
102
- Change the file mode and permissions, like os.chmod().
103
- """
104
- log.info(
105
- "Update permissions of file %s to %s", self._path, oct(permissions_mask)
106
- )
107
- self._path.chmod(permissions_mask)
108
-
109
- def restrict_permissions(self) -> None:
110
- """
111
- Restrict file/directory permissions to owner-only.
112
- """
113
- import stat
114
-
115
- owner_permissions = (
116
- # https://docs.python.org/3/library/stat.html
117
- stat.S_IRUSR # readable by owner
118
- | stat.S_IWUSR # writeable by owner
119
- | stat.S_IXUSR # executable by owner
120
- )
121
- self.chmod(self._path.stat().st_mode & owner_permissions)
122
-
123
- def touch(self, permissions_mask: int = 0o600, exist_ok: bool = True) -> None:
124
- """
125
- Create a file at this given path. For details, check pathlib.Path.touch()
126
- """
127
- if not self.exists():
128
- log.info("Creating file %s", str(self._path))
129
- self._path.touch(mode=permissions_mask, exist_ok=exist_ok)
130
-
131
- def mkdir(
132
- self,
133
- permissions_mask: int = 0o700,
134
- parents: bool = False,
135
- exist_ok: bool = False,
136
- ) -> None:
137
- """
138
- Create a directory at this given path. For details, check pathlib.Path.mkdir()
139
- """
140
- if parents and not self.parent.exists():
141
- self.parent.mkdir(
142
- permissions_mask=permissions_mask, exist_ok=exist_ok, parents=True
143
- )
144
- if not self.exists():
145
- log.info("Creating directory %s", str(self._path))
146
- self._path.mkdir(mode=permissions_mask, exist_ok=exist_ok)
147
-
148
- def read_text(self, file_size_limit_mb: int, *args, **kwargs) -> str:
149
- """
150
- Return the decoded contents of the file as a string.
151
- Raises an error of the file exceeds the specified size limit.
152
- For details, check pathlib.Path.read_text()
153
- """
154
- self._assert_exists_and_is_file()
155
- self._assert_file_size_limit(file_size_limit_mb)
156
- log.info("Reading file %s", self._path)
157
- return self._path.read_text(*args, **kwargs)
158
-
159
- def write_text(self, *args, **kwargs):
160
- """
161
- Open the file pointed to in text mode, write data to it, and close the file.
162
- """
163
- if not self.exists():
164
- self.touch()
165
- log.info("Writing to file %s", self._path)
166
- self.path.write_text(*args, **kwargs)
167
-
168
- @contextmanager
169
- def open( # noqa: A003
170
- self,
171
- mode="r",
172
- read_file_limit_mb: Optional[int] = None,
173
- **open_kwargs,
174
- ):
175
- """
176
- Open the file pointed by this path and return a file object, as
177
- the built-in open() function does.
178
- If the file is opened for reading, [read_file_limit_kb] parameter must be provided.
179
- Raises error if the read file exceeds the specified size limit.
180
- """
181
- opened_for_reading = "r" in mode
182
- if opened_for_reading:
183
- assert (
184
- read_file_limit_mb is not None
185
- ), "For reading mode ('r') read_file_limit_mb must be provided"
186
- self._assert_exists_and_is_file()
187
- self._assert_file_size_limit(read_file_limit_mb)
188
-
189
- if self.exists():
190
- self.assert_is_file()
191
- else:
192
- self.touch() # makes sure permissions of freshly-created file are strict
193
-
194
- log.info("Opening file %s in mode '%s'", self._path, mode)
195
- with self._path.open(mode=mode, **open_kwargs) as fd:
196
- yield fd
197
- log.info("Closing file %s", self._path)
198
-
199
- def move(self, destination: Union[Path, str]) -> "SecurePath":
200
- """Recursively move a file or directory (src) to another location and return the destination.
201
-
202
- If dst is an existing directory or a symlink to a directory, then src is moved inside that directory.
203
- The destination path in that directory must not already exist.
204
- """
205
- destination = Path(destination)
206
- if destination.is_dir():
207
- destination = destination / self._path.name
208
- if destination.exists():
209
- _raise_file_exists_error(destination)
210
- log.info("Moving %s to %s", str(self._path), destination.resolve())
211
- return SecurePath(shutil.move(str(self._path), destination))
212
-
213
- def copy(
214
- self, destination: Union[Path, str], dirs_exist_ok: bool = False
215
- ) -> "SecurePath":
216
- """
217
- Copy the file/directory into the destination.
218
- If source is a directory, its whole content is copied recursively.
219
- Permissions of the copy are limited only to the owner.
220
-
221
- If destination is an existing directory, the copy will be created inside it,
222
- unless dirs_exist_ok is true and the destination has the same name as this path.
223
-
224
- Otherwise, the copied file/base directory will be renamed to match destination.
225
- If dirs_exist_ok is false (the default) and dst already exists,
226
- a FileExistsError is raised. If dirs_exist_ok is true,
227
- the copying operation will continue if it encounters existing directories,
228
- and files within the destination tree will be overwritten by corresponding
229
- files from the src tree.
230
- """
231
- self.assert_exists()
232
-
233
- destination = Path(destination)
234
- if destination.exists():
235
- if destination.is_dir() and (
236
- destination.name != self._path.name or self.path.is_file()
237
- ):
238
- destination = destination / self._path.name
239
-
240
- if destination.exists():
241
- if not all([destination.is_dir(), self._path.is_dir(), dirs_exist_ok]):
242
- raise FileExistsError(
243
- errno.EEXIST, os.strerror(errno.EEXIST), self._path.resolve()
244
- )
245
-
246
- def _recursive_check_for_conflicts(src: Path, dst: Path):
247
- if dst.exists() and not dirs_exist_ok:
248
- _raise_file_exists_error(dst)
249
- if dst.is_file() and not src.is_file():
250
- _raise_not_a_directory_error(dst)
251
- if dst.is_dir() and not src.is_dir():
252
- _raise_is_a_directory_error(dst)
253
- if src.is_dir():
254
- for child in src.iterdir():
255
- _recursive_check_for_conflicts(child, dst / child.name)
256
-
257
- def _recursive_copy(src: SecurePath, dst: SecurePath):
258
- if src.is_file():
259
- log.info("Copying file %s into %s", src.path, dst.path)
260
- if dst.exists():
261
- dst.unlink()
262
- shutil.copyfile(src.path, dst.path)
263
- dst.restrict_permissions()
264
- if src.is_dir():
265
- dst.mkdir(exist_ok=True)
266
- for child in src.iterdir():
267
- _recursive_copy(child, dst / child.name)
268
-
269
- _recursive_check_for_conflicts(self._path, destination)
270
- _recursive_copy(self, self.__class__(destination))
271
-
272
- return SecurePath(destination)
273
-
274
- def unlink(self, missing_ok=False):
275
- """
276
- Remove this file or symbolic link.
277
- If the path points to a directory, use SecurePath.rmdir() instead.
278
-
279
- Check pathlib.Path.unlink() for details.
280
- """
281
- if not self.exists():
282
- if not missing_ok:
283
- self.assert_exists()
284
- return
285
-
286
- self.assert_is_file()
287
- log.info("Removing file %s", self._path)
288
- self._path.unlink()
289
-
290
- def rmdir(self, recursive=False, missing_ok=False):
291
- """
292
- Remove this directory.
293
- If the path points to a file, use SecurePath.unlink() instead.
294
-
295
- If path points to a file, NotADirectoryError will be raised.
296
- If directory does not exist, FileNotFoundError will be raised unless [missing_ok] is True.
297
- If the directory is not empty, DirectoryNotEmpty will be raised unless [recursive] is True.
298
- """
299
- if not self.exists():
300
- if not missing_ok:
301
- self.assert_exists()
302
- return
303
-
304
- self.assert_is_directory()
305
-
306
- if not recursive and any(self._path.iterdir()):
307
- raise DirectoryIsNotEmptyError(self._path.resolve())
308
-
309
- log.info("Removing directory %s", self._path)
310
- shutil.rmtree(str(self._path))
311
-
312
- @classmethod
313
- @contextmanager
314
- def temporary_directory(cls):
315
- """
316
- Creates a temporary directory in the most secure manner possible.
317
- The directory is readable, writable, and searchable only by the creating user ID.
318
- Yields SecurePath pointing to the absolute location of created directory.
319
-
320
- Works similarly to tempfile.TemporaryDirectory
321
- """
322
- with tempfile.TemporaryDirectory(prefix="snowflake-cli") as tmpdir:
323
- log.info("Created temporary directory %s", tmpdir)
324
- yield SecurePath(tmpdir)
325
- log.info("Removing temporary directory %s", tmpdir)
326
-
327
- def _assert_exists_and_is_file(self) -> None:
328
- self.assert_exists()
329
- self.assert_is_file()
330
-
331
- def assert_exists(self) -> None:
332
- if not self.exists():
333
- raise FileNotFoundError(
334
- errno.ENOENT, os.strerror(errno.ENOENT), self._path.resolve()
335
- )
336
-
337
- def assert_is_file(self) -> None:
338
- if not self._path.is_file():
339
- _raise_is_a_directory_error(self._path.resolve())
340
-
341
- def assert_is_directory(self) -> None:
342
- if not self._path.is_dir():
343
- _raise_not_a_directory_error(self._path.resolve())
344
-
345
- def _assert_file_size_limit(self, size_limit_in_mb):
346
- if (
347
- size_limit_in_mb != UNLIMITED
348
- and self._path.stat().st_size > size_limit_in_mb * 1024 * 1024
349
- ):
350
- raise FileTooLargeError(self._path.resolve(), size_limit_in_mb)
351
-
352
-
353
- def _raise_file_exists_error(path: Path):
354
- raise FileExistsError(errno.EEXIST, os.strerror(errno.EEXIST), path)
355
-
356
-
357
- def _raise_is_a_directory_error(path: Path):
358
- raise IsADirectoryError(errno.EISDIR, os.strerror(errno.EISDIR), path)
359
-
360
-
361
- def _raise_not_a_directory_error(path: Path):
362
- raise NotADirectoryError(errno.ENOTDIR, os.strerror(errno.ENOTDIR), path)
@@ -1,29 +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
- import stat
16
- from pathlib import Path
17
-
18
-
19
- def file_permissions_are_strict(file_path: Path) -> bool:
20
- accessible_by_others = (
21
- # https://docs.python.org/3/library/stat.html
22
- stat.S_IRGRP # readable by group
23
- | stat.S_IROTH # readable by others
24
- | stat.S_IWGRP # writeable by group
25
- | stat.S_IWOTH # writeable by others
26
- | stat.S_IXGRP # executable by group
27
- | stat.S_IXOTH # executable by others
28
- )
29
- return (file_path.stat().st_mode & accessible_by_others) == 0