snowflake-connector-python 3.16.0__cp313-cp313-win_amd64.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 (246) hide show
  1. snowflake/connector/__init__.py +96 -0
  2. snowflake/connector/_query_context_cache.py +276 -0
  3. snowflake/connector/_sql_util.py +48 -0
  4. snowflake/connector/_utils.py +70 -0
  5. snowflake/connector/arrow_context.py +203 -0
  6. snowflake/connector/auth/__init__.py +53 -0
  7. snowflake/connector/auth/_auth.py +586 -0
  8. snowflake/connector/auth/_http_server.py +220 -0
  9. snowflake/connector/auth/_oauth_base.py +400 -0
  10. snowflake/connector/auth/by_plugin.py +219 -0
  11. snowflake/connector/auth/default.py +36 -0
  12. snowflake/connector/auth/idtoken.py +72 -0
  13. snowflake/connector/auth/keypair.py +222 -0
  14. snowflake/connector/auth/no_auth.py +39 -0
  15. snowflake/connector/auth/oauth.py +49 -0
  16. snowflake/connector/auth/oauth_code.py +479 -0
  17. snowflake/connector/auth/oauth_credentials.py +66 -0
  18. snowflake/connector/auth/okta.py +335 -0
  19. snowflake/connector/auth/pat.py +39 -0
  20. snowflake/connector/auth/usrpwdmfa.py +69 -0
  21. snowflake/connector/auth/webbrowser.py +502 -0
  22. snowflake/connector/auth/workload_identity.py +94 -0
  23. snowflake/connector/azure_storage_client.py +276 -0
  24. snowflake/connector/backoff_policies.py +141 -0
  25. snowflake/connector/bind_upload_agent.py +89 -0
  26. snowflake/connector/cache.py +696 -0
  27. snowflake/connector/compat.py +127 -0
  28. snowflake/connector/config_manager.py +496 -0
  29. snowflake/connector/connection.py +2273 -0
  30. snowflake/connector/connection_diagnostic.py +776 -0
  31. snowflake/connector/constants.py +442 -0
  32. snowflake/connector/converter.py +785 -0
  33. snowflake/connector/converter_issue23517.py +87 -0
  34. snowflake/connector/converter_null.py +14 -0
  35. snowflake/connector/converter_snowsql.py +205 -0
  36. snowflake/connector/cursor.py +1951 -0
  37. snowflake/connector/dbapi.py +53 -0
  38. snowflake/connector/description.py +19 -0
  39. snowflake/connector/direct_file_operation_utils.py +88 -0
  40. snowflake/connector/encryption_util.py +220 -0
  41. snowflake/connector/errorcode.py +91 -0
  42. snowflake/connector/errors.py +615 -0
  43. snowflake/connector/externals_utils/__init__.py +0 -0
  44. snowflake/connector/externals_utils/externals_setup.py +27 -0
  45. snowflake/connector/feature.py +4 -0
  46. snowflake/connector/file_compression_type.py +118 -0
  47. snowflake/connector/file_lock.py +72 -0
  48. snowflake/connector/file_transfer_agent.py +1215 -0
  49. snowflake/connector/file_util.py +153 -0
  50. snowflake/connector/gcs_storage_client.py +474 -0
  51. snowflake/connector/gzip_decoder.py +85 -0
  52. snowflake/connector/local_storage_client.py +90 -0
  53. snowflake/connector/log_configuration.py +60 -0
  54. snowflake/connector/logging_utils/__init__.py +0 -0
  55. snowflake/connector/logging_utils/filters.py +72 -0
  56. snowflake/connector/nanoarrow_arrow_iterator.cp313-win_amd64.pyd +0 -0
  57. snowflake/connector/nanoarrow_cpp/ArrowIterator/ArrayConverter.cpp +60 -0
  58. snowflake/connector/nanoarrow_cpp/ArrowIterator/ArrayConverter.hpp +29 -0
  59. snowflake/connector/nanoarrow_cpp/ArrowIterator/BinaryConverter.cpp +19 -0
  60. snowflake/connector/nanoarrow_cpp/ArrowIterator/BinaryConverter.hpp +26 -0
  61. snowflake/connector/nanoarrow_cpp/ArrowIterator/BooleanConverter.cpp +21 -0
  62. snowflake/connector/nanoarrow_cpp/ArrowIterator/BooleanConverter.hpp +23 -0
  63. snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowChunkIterator.cpp +557 -0
  64. snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowChunkIterator.hpp +98 -0
  65. snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowIterator.cpp +125 -0
  66. snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowIterator.hpp +115 -0
  67. snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowTableIterator.cpp +1005 -0
  68. snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowTableIterator.hpp +127 -0
  69. snowflake/connector/nanoarrow_cpp/ArrowIterator/DateConverter.cpp +47 -0
  70. snowflake/connector/nanoarrow_cpp/ArrowIterator/DateConverter.hpp +46 -0
  71. snowflake/connector/nanoarrow_cpp/ArrowIterator/DecFloatConverter.cpp +83 -0
  72. snowflake/connector/nanoarrow_cpp/ArrowIterator/DecFloatConverter.hpp +35 -0
  73. snowflake/connector/nanoarrow_cpp/ArrowIterator/DecimalConverter.cpp +97 -0
  74. snowflake/connector/nanoarrow_cpp/ArrowIterator/DecimalConverter.hpp +72 -0
  75. snowflake/connector/nanoarrow_cpp/ArrowIterator/FixedSizeListConverter.cpp +73 -0
  76. snowflake/connector/nanoarrow_cpp/ArrowIterator/FixedSizeListConverter.hpp +28 -0
  77. snowflake/connector/nanoarrow_cpp/ArrowIterator/FloatConverter.cpp +30 -0
  78. snowflake/connector/nanoarrow_cpp/ArrowIterator/FloatConverter.hpp +35 -0
  79. snowflake/connector/nanoarrow_cpp/ArrowIterator/IColumnConverter.hpp +17 -0
  80. snowflake/connector/nanoarrow_cpp/ArrowIterator/IntConverter.cpp +23 -0
  81. snowflake/connector/nanoarrow_cpp/ArrowIterator/IntConverter.hpp +45 -0
  82. snowflake/connector/nanoarrow_cpp/ArrowIterator/IntervalConverter.cpp +71 -0
  83. snowflake/connector/nanoarrow_cpp/ArrowIterator/IntervalConverter.hpp +56 -0
  84. snowflake/connector/nanoarrow_cpp/ArrowIterator/LICENSE.txt +209 -0
  85. snowflake/connector/nanoarrow_cpp/ArrowIterator/MapConverter.cpp +75 -0
  86. snowflake/connector/nanoarrow_cpp/ArrowIterator/MapConverter.hpp +30 -0
  87. snowflake/connector/nanoarrow_cpp/ArrowIterator/ObjectConverter.cpp +46 -0
  88. snowflake/connector/nanoarrow_cpp/ArrowIterator/ObjectConverter.hpp +29 -0
  89. snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Common.cpp +8 -0
  90. snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Common.hpp +95 -0
  91. snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Helpers.cpp +57 -0
  92. snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Helpers.hpp +36 -0
  93. snowflake/connector/nanoarrow_cpp/ArrowIterator/SnowflakeType.cpp +34 -0
  94. snowflake/connector/nanoarrow_cpp/ArrowIterator/SnowflakeType.hpp +48 -0
  95. snowflake/connector/nanoarrow_cpp/ArrowIterator/StringConverter.cpp +19 -0
  96. snowflake/connector/nanoarrow_cpp/ArrowIterator/StringConverter.hpp +26 -0
  97. snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeConverter.cpp +36 -0
  98. snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeConverter.hpp +31 -0
  99. snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeStampConverter.cpp +346 -0
  100. snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeStampConverter.hpp +145 -0
  101. snowflake/connector/nanoarrow_cpp/ArrowIterator/Util/macros.hpp +14 -0
  102. snowflake/connector/nanoarrow_cpp/ArrowIterator/Util/time.cpp +65 -0
  103. snowflake/connector/nanoarrow_cpp/ArrowIterator/Util/time.hpp +68 -0
  104. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_accessors.h +101 -0
  105. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_alloc.h +127 -0
  106. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_assert.h +45 -0
  107. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_builder.h +1908 -0
  108. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_emitter.h +215 -0
  109. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_endian.h +125 -0
  110. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_epilogue.h +7 -0
  111. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_flatbuffers.h +55 -0
  112. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_identifier.h +148 -0
  113. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_iov.h +31 -0
  114. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_prologue.h +8 -0
  115. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_refmap.h +144 -0
  116. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_rtconfig.h +162 -0
  117. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_types.h +97 -0
  118. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_verifier.h +239 -0
  119. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/flatcc_portable.h +14 -0
  120. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/paligned_alloc.h +210 -0
  121. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pattributes.h +84 -0
  122. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic.h +84 -0
  123. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic_pop.h +20 -0
  124. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic_push.h +51 -0
  125. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pendian.h +206 -0
  126. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pendian_detect.h +118 -0
  127. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pinline.h +19 -0
  128. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pinttypes.h +52 -0
  129. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/portable.h +2 -0
  130. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/portable_basic.h +25 -0
  131. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstatic_assert.h +67 -0
  132. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstdalign.h +162 -0
  133. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstdint.h +898 -0
  134. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/punaligned.h +190 -0
  135. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pversion.h +6 -0
  136. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pwarnings.h +52 -0
  137. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc.c +3204 -0
  138. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow.c +3217 -0
  139. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow.h +3618 -0
  140. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow.hpp +379 -0
  141. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_arrow_iterator.pyx +256 -0
  142. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_device.c +512 -0
  143. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_device.h +350 -0
  144. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_ipc.c +33273 -0
  145. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_ipc.h +397 -0
  146. snowflake/connector/nanoarrow_cpp/Logging/logging.cpp +100 -0
  147. snowflake/connector/nanoarrow_cpp/Logging/logging.hpp +49 -0
  148. snowflake/connector/network.py +1297 -0
  149. snowflake/connector/ocsp_asn1crypto.py +447 -0
  150. snowflake/connector/ocsp_snowflake.py +1883 -0
  151. snowflake/connector/options.py +131 -0
  152. snowflake/connector/pandas_tools.py +732 -0
  153. snowflake/connector/proxy.py +43 -0
  154. snowflake/connector/py.typed +0 -0
  155. snowflake/connector/result_batch.py +786 -0
  156. snowflake/connector/result_set.py +319 -0
  157. snowflake/connector/s3_storage_client.py +605 -0
  158. snowflake/connector/secret_detector.py +181 -0
  159. snowflake/connector/sf_dirs.py +64 -0
  160. snowflake/connector/sfbinaryformat.py +35 -0
  161. snowflake/connector/sfdatetime.py +345 -0
  162. snowflake/connector/snow_logging.py +144 -0
  163. snowflake/connector/sqlstate.py +9 -0
  164. snowflake/connector/ssd_internal_keys.py +30 -0
  165. snowflake/connector/ssl_wrap_socket.py +134 -0
  166. snowflake/connector/storage_client.py +478 -0
  167. snowflake/connector/telemetry.py +249 -0
  168. snowflake/connector/telemetry_oob.py +543 -0
  169. snowflake/connector/test_util.py +30 -0
  170. snowflake/connector/time_util.py +159 -0
  171. snowflake/connector/token_cache.py +402 -0
  172. snowflake/connector/tool/__init__.py +0 -0
  173. snowflake/connector/tool/dump_certs.py +57 -0
  174. snowflake/connector/tool/dump_ocsp_response.py +139 -0
  175. snowflake/connector/tool/dump_ocsp_response_cache.py +194 -0
  176. snowflake/connector/tool/probe_connection.py +69 -0
  177. snowflake/connector/url_util.py +49 -0
  178. snowflake/connector/util_text.py +301 -0
  179. snowflake/connector/vendored/__init__.py +3 -0
  180. snowflake/connector/vendored/requests/LICENSE +175 -0
  181. snowflake/connector/vendored/requests/__init__.py +169 -0
  182. snowflake/connector/vendored/requests/__version__.py +14 -0
  183. snowflake/connector/vendored/requests/_internal_utils.py +50 -0
  184. snowflake/connector/vendored/requests/adapters.py +537 -0
  185. snowflake/connector/vendored/requests/api.py +157 -0
  186. snowflake/connector/vendored/requests/auth.py +315 -0
  187. snowflake/connector/vendored/requests/certs.py +17 -0
  188. snowflake/connector/vendored/requests/compat.py +79 -0
  189. snowflake/connector/vendored/requests/cookies.py +561 -0
  190. snowflake/connector/vendored/requests/exceptions.py +140 -0
  191. snowflake/connector/vendored/requests/help.py +134 -0
  192. snowflake/connector/vendored/requests/hooks.py +33 -0
  193. snowflake/connector/vendored/requests/models.py +1033 -0
  194. snowflake/connector/vendored/requests/sessions.py +833 -0
  195. snowflake/connector/vendored/requests/status_codes.py +128 -0
  196. snowflake/connector/vendored/requests/structures.py +99 -0
  197. snowflake/connector/vendored/requests/utils.py +1093 -0
  198. snowflake/connector/vendored/urllib3/LICENSE.txt +21 -0
  199. snowflake/connector/vendored/urllib3/__init__.py +85 -0
  200. snowflake/connector/vendored/urllib3/_collections.py +355 -0
  201. snowflake/connector/vendored/urllib3/_version.py +2 -0
  202. snowflake/connector/vendored/urllib3/connection.py +572 -0
  203. snowflake/connector/vendored/urllib3/connectionpool.py +1137 -0
  204. snowflake/connector/vendored/urllib3/contrib/__init__.py +0 -0
  205. snowflake/connector/vendored/urllib3/contrib/_appengine_environ.py +36 -0
  206. snowflake/connector/vendored/urllib3/contrib/_securetransport/__init__.py +0 -0
  207. snowflake/connector/vendored/urllib3/contrib/_securetransport/bindings.py +519 -0
  208. snowflake/connector/vendored/urllib3/contrib/_securetransport/low_level.py +397 -0
  209. snowflake/connector/vendored/urllib3/contrib/appengine.py +314 -0
  210. snowflake/connector/vendored/urllib3/contrib/ntlmpool.py +130 -0
  211. snowflake/connector/vendored/urllib3/contrib/pyopenssl.py +509 -0
  212. snowflake/connector/vendored/urllib3/contrib/securetransport.py +920 -0
  213. snowflake/connector/vendored/urllib3/contrib/socks.py +216 -0
  214. snowflake/connector/vendored/urllib3/exceptions.py +323 -0
  215. snowflake/connector/vendored/urllib3/fields.py +274 -0
  216. snowflake/connector/vendored/urllib3/filepost.py +98 -0
  217. snowflake/connector/vendored/urllib3/packages/__init__.py +0 -0
  218. snowflake/connector/vendored/urllib3/packages/backports/__init__.py +0 -0
  219. snowflake/connector/vendored/urllib3/packages/backports/makefile.py +51 -0
  220. snowflake/connector/vendored/urllib3/packages/backports/weakref_finalize.py +155 -0
  221. snowflake/connector/vendored/urllib3/packages/six.py +1076 -0
  222. snowflake/connector/vendored/urllib3/poolmanager.py +540 -0
  223. snowflake/connector/vendored/urllib3/request.py +191 -0
  224. snowflake/connector/vendored/urllib3/response.py +885 -0
  225. snowflake/connector/vendored/urllib3/util/__init__.py +49 -0
  226. snowflake/connector/vendored/urllib3/util/connection.py +156 -0
  227. snowflake/connector/vendored/urllib3/util/proxy.py +57 -0
  228. snowflake/connector/vendored/urllib3/util/queue.py +22 -0
  229. snowflake/connector/vendored/urllib3/util/request.py +146 -0
  230. snowflake/connector/vendored/urllib3/util/response.py +107 -0
  231. snowflake/connector/vendored/urllib3/util/retry.py +620 -0
  232. snowflake/connector/vendored/urllib3/util/ssl_.py +495 -0
  233. snowflake/connector/vendored/urllib3/util/ssl_match_hostname.py +159 -0
  234. snowflake/connector/vendored/urllib3/util/ssltransport.py +221 -0
  235. snowflake/connector/vendored/urllib3/util/timeout.py +271 -0
  236. snowflake/connector/vendored/urllib3/util/url.py +435 -0
  237. snowflake/connector/vendored/urllib3/util/wait.py +152 -0
  238. snowflake/connector/version.py +3 -0
  239. snowflake/connector/wif_util.py +407 -0
  240. snowflake_connector_python-3.16.0.dist-info/METADATA +1475 -0
  241. snowflake_connector_python-3.16.0.dist-info/RECORD +246 -0
  242. snowflake_connector_python-3.16.0.dist-info/WHEEL +5 -0
  243. snowflake_connector_python-3.16.0.dist-info/entry_points.txt +4 -0
  244. snowflake_connector_python-3.16.0.dist-info/licenses/LICENSE.txt +202 -0
  245. snowflake_connector_python-3.16.0.dist-info/licenses/NOTICE +8 -0
  246. snowflake_connector_python-3.16.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,276 @@
1
+ from __future__ import annotations
2
+
3
+ import base64
4
+ import json
5
+ import os
6
+ import xml.etree.ElementTree as ET
7
+ from datetime import datetime, timezone
8
+ from logging import getLogger
9
+ from random import choice
10
+ from string import hexdigits
11
+ from typing import TYPE_CHECKING, Any, NamedTuple
12
+
13
+ from .compat import quote
14
+ from .constants import FileHeader, ResultStatus
15
+ from .encryption_util import EncryptionMetadata
16
+ from .storage_client import SnowflakeStorageClient
17
+ from .util_text import get_md5
18
+ from .vendored import requests
19
+
20
+ if TYPE_CHECKING: # pragma: no cover
21
+ from .file_transfer_agent import SnowflakeFileMeta, StorageCredential
22
+
23
+ logger = getLogger(__name__)
24
+
25
+
26
+ class AzureLocation(NamedTuple):
27
+ container_name: str
28
+ path: str
29
+
30
+
31
+ TOKEN_EXPIRATION_ERR_MESSAGE = (
32
+ "Signature not valid in the specified time frame",
33
+ "Server failed to authenticate the request.",
34
+ )
35
+ SFCDIGEST = "x-ms-meta-sfcdigest"
36
+ ENCRYPTION_DATA = "x-ms-meta-encryptiondata"
37
+ MATDESC = "x-ms-meta-matdesc"
38
+
39
+
40
+ class SnowflakeAzureRestClient(SnowflakeStorageClient):
41
+ def __init__(
42
+ self,
43
+ meta: SnowflakeFileMeta,
44
+ credentials: StorageCredential | None,
45
+ chunk_size: int,
46
+ stage_info: dict[str, Any],
47
+ unsafe_file_write: bool = False,
48
+ ) -> None:
49
+ super().__init__(
50
+ meta,
51
+ stage_info,
52
+ chunk_size,
53
+ credentials=credentials,
54
+ unsafe_file_write=unsafe_file_write,
55
+ )
56
+ end_point: str = stage_info["endPoint"]
57
+ if end_point.startswith("blob."):
58
+ end_point = end_point[len("blob.") :]
59
+ self.endpoint = end_point
60
+ self.storage_account: str = stage_info["storageAccount"]
61
+ self.azure_location = self.extract_container_name_and_path(
62
+ stage_info["location"]
63
+ )
64
+ self.block_ids: list[str] = []
65
+
66
+ @staticmethod
67
+ def extract_container_name_and_path(stage_location: str) -> AzureLocation:
68
+ stage_location = os.path.expanduser(stage_location)
69
+ container_name = stage_location
70
+ path = ""
71
+
72
+ # split stage location as bucket name and path
73
+ if "/" in stage_location:
74
+ container_name, _, path = stage_location.partition("/")
75
+ if path and not path.endswith("/"):
76
+ path += "/"
77
+
78
+ return AzureLocation(container_name=container_name, path=path)
79
+
80
+ def _has_expired_token(self, response: requests.Response) -> bool:
81
+ return response.status_code == 403 and any(
82
+ message in response.reason for message in TOKEN_EXPIRATION_ERR_MESSAGE
83
+ )
84
+
85
+ def _send_request_with_authentication_and_retry(
86
+ self,
87
+ verb: str,
88
+ url: str,
89
+ retry_id: int | str,
90
+ headers: dict[str, Any] = None,
91
+ data: bytes = None,
92
+ ) -> requests.Response:
93
+ if not headers:
94
+ headers = {}
95
+
96
+ def generate_authenticated_url_and_rest_args() -> tuple[bytes, dict[str, Any]]:
97
+ curtime = datetime.now(timezone.utc).replace(tzinfo=None)
98
+ timestamp = curtime.strftime("YYYY-MM-DD")
99
+ sas_token = self.credentials.creds["AZURE_SAS_TOKEN"]
100
+ if sas_token and sas_token.startswith("?"):
101
+ sas_token = sas_token[1:]
102
+ if "?" in url:
103
+ _url = url + "&" + sas_token
104
+ else:
105
+ _url = url + "?" + sas_token
106
+ headers["Date"] = timestamp
107
+ rest_args = {"headers": headers}
108
+ if data:
109
+ rest_args["data"] = data
110
+ return _url, rest_args
111
+
112
+ return self._send_request_with_retry(
113
+ verb, generate_authenticated_url_and_rest_args, retry_id
114
+ )
115
+
116
+ def get_file_header(self, filename: str) -> FileHeader | None:
117
+ """Gets Azure file properties."""
118
+ container_name = quote(self.azure_location.container_name)
119
+ path = quote(self.azure_location.path) + quote(filename)
120
+ meta = self.meta
121
+ # HTTP HEAD request
122
+ url = f"https://{self.storage_account}.blob.{self.endpoint}/{container_name}/{path}"
123
+ retry_id = "HEAD"
124
+ self.retry_count[retry_id] = 0
125
+ r = self._send_request_with_authentication_and_retry("HEAD", url, retry_id)
126
+ if r.status_code == 200:
127
+ meta.result_status = ResultStatus.UPLOADED
128
+ enc_data_str = r.headers.get(ENCRYPTION_DATA)
129
+ encryption_data = None if enc_data_str is None else json.loads(enc_data_str)
130
+ encryption_metadata = (
131
+ None
132
+ if not encryption_data
133
+ else EncryptionMetadata(
134
+ key=encryption_data["WrappedContentKey"]["EncryptedKey"],
135
+ iv=encryption_data["ContentEncryptionIV"],
136
+ matdesc=r.headers.get(MATDESC),
137
+ )
138
+ )
139
+ return FileHeader(
140
+ digest=r.headers.get(SFCDIGEST),
141
+ content_length=int(r.headers.get("Content-Length")),
142
+ encryption_metadata=encryption_metadata,
143
+ )
144
+ elif r.status_code == 404:
145
+ meta.result_status = ResultStatus.NOT_FOUND_FILE
146
+ return FileHeader(
147
+ digest=None, content_length=None, encryption_metadata=None
148
+ )
149
+ else:
150
+ r.raise_for_status()
151
+
152
+ def _prepare_file_metadata(self) -> dict[str, str | None]:
153
+ azure_metadata = {
154
+ SFCDIGEST: self.meta.sha256_digest,
155
+ }
156
+ encryption_metadata = self.encryption_metadata
157
+ if encryption_metadata:
158
+ azure_metadata.update(
159
+ {
160
+ ENCRYPTION_DATA: json.dumps(
161
+ {
162
+ "EncryptionMode": "FullBlob",
163
+ "WrappedContentKey": {
164
+ "KeyId": "symmKey1",
165
+ "EncryptedKey": encryption_metadata.key,
166
+ "Algorithm": "AES_CBC_256",
167
+ },
168
+ "EncryptionAgent": {
169
+ "Protocol": "1.0",
170
+ "EncryptionAlgorithm": "AES_CBC_128",
171
+ },
172
+ "ContentEncryptionIV": encryption_metadata.iv,
173
+ "KeyWrappingMetadata": {"EncryptionLibrary": "Java 5.3.0"},
174
+ }
175
+ ),
176
+ MATDESC: encryption_metadata.matdesc,
177
+ }
178
+ )
179
+ return azure_metadata
180
+
181
+ def _initiate_multipart_upload(self) -> None:
182
+ self.block_ids = [
183
+ "".join(choice(hexdigits) for _ in range(20))
184
+ for _ in range(self.num_of_chunks)
185
+ ]
186
+
187
+ def _upload_chunk(self, chunk_id: int, chunk: bytes) -> None:
188
+ container_name = quote(self.azure_location.container_name)
189
+ path = quote(self.azure_location.path + self.meta.dst_file_name.lstrip("/"))
190
+
191
+ if self.num_of_chunks > 1:
192
+ block_id = self.block_ids[chunk_id]
193
+ url = (
194
+ f"https://{self.storage_account}.blob.{self.endpoint}/{container_name}/{path}?comp=block"
195
+ f"&blockid={block_id}"
196
+ )
197
+ headers = {"Content-Length": str(len(chunk))}
198
+ r = self._send_request_with_authentication_and_retry(
199
+ "PUT", url, chunk_id, headers=headers, data=chunk
200
+ )
201
+ else:
202
+ # single request
203
+ azure_metadata = self._prepare_file_metadata()
204
+ url = f"https://{self.storage_account}.blob.{self.endpoint}/{container_name}/{path}"
205
+ headers = {
206
+ "x-ms-blob-type": "BlockBlob",
207
+ "Content-Encoding": "utf-8",
208
+ }
209
+ headers.update(azure_metadata)
210
+ r = self._send_request_with_authentication_and_retry(
211
+ "PUT", url, chunk_id, headers=headers, data=chunk
212
+ )
213
+ r.raise_for_status() # expect status code 201
214
+
215
+ def _complete_multipart_upload(self) -> None:
216
+ container_name = quote(self.azure_location.container_name)
217
+ path = quote(self.azure_location.path + self.meta.dst_file_name.lstrip("/"))
218
+ url = (
219
+ f"https://{self.storage_account}.blob.{self.endpoint}/{container_name}/{path}?comp"
220
+ f"=blocklist"
221
+ )
222
+ root = ET.Element("BlockList")
223
+ for block_id in self.block_ids:
224
+ part = ET.Element("Latest")
225
+ part.text = block_id
226
+ root.append(part)
227
+ # SNOW-1778088: We need to calculate the MD5 sum of this file for Azure Blob storage
228
+ new_stream = not bool(self.meta.src_stream or self.meta.intermediate_stream)
229
+ fd = (
230
+ self.meta.src_stream
231
+ or self.meta.intermediate_stream
232
+ or open(self.meta.real_src_file_name, "rb")
233
+ )
234
+ try:
235
+ if not new_stream:
236
+ # Reset position in file
237
+ fd.seek(0)
238
+ file_content = fd.read()
239
+ finally:
240
+ if new_stream:
241
+ fd.close()
242
+ headers = {
243
+ "x-ms-blob-content-encoding": "utf-8",
244
+ "x-ms-blob-content-md5": base64.b64encode(get_md5(file_content)).decode(
245
+ "utf-8"
246
+ ),
247
+ }
248
+ azure_metadata = self._prepare_file_metadata()
249
+ headers.update(azure_metadata)
250
+ retry_id = "COMPLETE"
251
+ self.retry_count[retry_id] = 0
252
+ r = self._send_request_with_authentication_and_retry(
253
+ "PUT", url, "COMPLETE", headers=headers, data=ET.tostring(root)
254
+ )
255
+ r.raise_for_status() # expects status code 201
256
+
257
+ def download_chunk(self, chunk_id: int) -> None:
258
+ container_name = quote(self.azure_location.container_name)
259
+ path = quote(self.azure_location.path + self.meta.src_file_name.lstrip("/"))
260
+ url = f"https://{self.storage_account}.blob.{self.endpoint}/{container_name}/{path}"
261
+ if self.num_of_chunks > 1:
262
+ chunk_size = self.chunk_size
263
+ if chunk_id < self.num_of_chunks - 1:
264
+ _range = f"{chunk_id * chunk_size}-{(chunk_id+1)*chunk_size-1}"
265
+ else:
266
+ _range = f"{chunk_id * chunk_size}-"
267
+ headers = {"Range": f"bytes={_range}"}
268
+ r = self._send_request_with_authentication_and_retry(
269
+ "GET", url, chunk_id, headers=headers
270
+ ) # expect 206
271
+ else:
272
+ # single request
273
+ r = self._send_request_with_authentication_and_retry("GET", url, chunk_id)
274
+ if r.status_code in (200, 206):
275
+ self.write_downloaded_chunk(chunk_id, r.content)
276
+ r.raise_for_status()
@@ -0,0 +1,141 @@
1
+ from __future__ import annotations
2
+
3
+ import random
4
+ from typing import Callable, Iterator
5
+
6
+ """This module provides common implementations of backoff policies
7
+
8
+ All backoff policies must be implemented as generator functions with the behaviour specified below. These generator
9
+ functions will be called to create iterators yielding backoff durations.
10
+
11
+ Args:
12
+ None
13
+
14
+ Yields:
15
+ int: Next backoff duration in seconds
16
+
17
+ Example:
18
+ This is an example of a valid backoff policy that always yields a backoff duration of 42 seconds.
19
+
20
+ def constant_backoff() -> int:
21
+ while True
22
+ yield 42
23
+
24
+
25
+ Note:
26
+ The functions provided in this module are not backoff policies. They are functions returning backoff policies.
27
+ This is to enable customization of the constants used in backoff computations.
28
+ """
29
+
30
+ DEFAULT_BACKOFF_FACTOR = 2
31
+ DEFAULT_BACKOFF_BASE = 1
32
+ DEFAULT_BACKOFF_CAP = 16
33
+ DEFAULT_ENABLE_JITTER = True
34
+
35
+
36
+ def mixed_backoff(
37
+ factor: int = DEFAULT_BACKOFF_FACTOR,
38
+ base: int = DEFAULT_BACKOFF_BASE,
39
+ cap: int = DEFAULT_BACKOFF_CAP,
40
+ enable_jitter: bool = DEFAULT_ENABLE_JITTER,
41
+ ) -> Callable[..., Iterator[int]]:
42
+ """Randomly chooses between exponential and constant backoff. Uses equal jitter.
43
+
44
+ Args:
45
+ factor (int): Exponential base for the exponential term.
46
+ base (int): Initial backoff time in seconds. Constant coefficient for the exponential term.
47
+ cap (int): Maximum backoff time in seconds.
48
+ enable_jitter (int): Whether to enable equal jitter on computed durations. For details see
49
+ https://www.awsarchitectureblog.com/2015/03/backoff.html
50
+
51
+ Returns:
52
+ Callable: generator function implementing the mixed backoff policy
53
+ """
54
+
55
+ def generator():
56
+ cnt = 0
57
+ sleep = base
58
+
59
+ yield sleep
60
+ while True:
61
+ cnt += 1
62
+
63
+ # equal jitter
64
+ mult_factor = random.choice([-1, 1])
65
+ jitter_amount = 0.5 * sleep * mult_factor if enable_jitter else 0
66
+ sleep = int(
67
+ random.choice(
68
+ [sleep + jitter_amount, base * factor**cnt + jitter_amount]
69
+ )
70
+ )
71
+ sleep = min(cap, sleep)
72
+
73
+ yield sleep
74
+
75
+ return generator
76
+
77
+
78
+ def linear_backoff(
79
+ factor: int = DEFAULT_BACKOFF_FACTOR,
80
+ base: int = DEFAULT_BACKOFF_BASE,
81
+ cap: int = DEFAULT_BACKOFF_CAP,
82
+ enable_jitter: bool = DEFAULT_ENABLE_JITTER,
83
+ ) -> Callable[..., Iterator[int]]:
84
+ """Standard linear backoff. Uses full jitter.
85
+
86
+ Args:
87
+ factor (int): Linear increment every iteration.
88
+ base (int): Initial backoff time in seconds.
89
+ cap (int): Maximum backoff time in seconds.
90
+ enable_jitter (int): Whether to enable full jitter on computed durations. For details see
91
+ https://www.awsarchitectureblog.com/2015/03/backoff.html
92
+
93
+ Returns:
94
+ Callable: generator function implementing the linear backoff policy
95
+ """
96
+
97
+ def generator():
98
+ sleep = base
99
+
100
+ yield sleep
101
+ while True:
102
+ sleep += factor
103
+ sleep = min(cap, sleep)
104
+
105
+ # full jitter
106
+ yield random.randint(0, sleep) if enable_jitter else sleep
107
+
108
+ return generator
109
+
110
+
111
+ def exponential_backoff(
112
+ factor: int = DEFAULT_BACKOFF_FACTOR,
113
+ base: int = DEFAULT_BACKOFF_BASE,
114
+ cap: int = DEFAULT_BACKOFF_CAP,
115
+ enable_jitter: bool = DEFAULT_ENABLE_JITTER,
116
+ ) -> Callable[..., Iterator[int]]:
117
+ """Standard exponential backoff. Uses full jitter.
118
+
119
+ Args:
120
+ factor (int): Exponential base for the exponential term.
121
+ base (int): Initial backoff time in seconds. Constant coefficient for the exponential term.
122
+ cap (int): Maximum backoff time in seconds.
123
+ enable_jitter (int): Whether to enable full jitter on computed durations. For details see
124
+ https://www.awsarchitectureblog.com/2015/03/backoff.html
125
+
126
+ Returns:
127
+ Callable: generator function implementing the exponential backoff policy
128
+ """
129
+
130
+ def generator():
131
+ sleep = base
132
+
133
+ yield sleep
134
+ while True:
135
+ sleep *= factor
136
+ sleep = min(cap, sleep)
137
+
138
+ # full jitter
139
+ yield random.randint(0, sleep) if enable_jitter else sleep
140
+
141
+ return generator
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env python
2
+ from __future__ import annotations
3
+
4
+ import os
5
+ import uuid
6
+ from io import BytesIO
7
+ from logging import getLogger
8
+ from typing import TYPE_CHECKING
9
+
10
+ from ._utils import (
11
+ _PYTHON_SNOWPARK_USE_SCOPED_TEMP_OBJECTS_STRING,
12
+ get_temp_type_for_object,
13
+ )
14
+ from .errors import BindUploadError, Error
15
+
16
+ if TYPE_CHECKING: # pragma: no cover
17
+ from .cursor import SnowflakeCursor
18
+
19
+ logger = getLogger(__name__)
20
+
21
+
22
+ class BindUploadAgent:
23
+
24
+ def __init__(
25
+ self,
26
+ cursor: SnowflakeCursor,
27
+ rows: list[bytes],
28
+ stream_buffer_size: int = 1024 * 1024 * 10,
29
+ ) -> None:
30
+ """Construct an agent that uploads binding parameters as CSV files to a temporary stage.
31
+
32
+ Args:
33
+ cursor: The cursor object.
34
+ rows: Rows of binding parameters in CSV format.
35
+ stream_buffer_size: Size of each file, default to 10MB.
36
+ """
37
+ self._use_scoped_temp_object = (
38
+ cursor.connection._session_parameters.get(
39
+ _PYTHON_SNOWPARK_USE_SCOPED_TEMP_OBJECTS_STRING, False
40
+ )
41
+ if cursor.connection._session_parameters
42
+ else False
43
+ )
44
+ self._STAGE_NAME = (
45
+ "SNOWPARK_TEMP_STAGE_BIND" if self._use_scoped_temp_object else "SYSTEMBIND"
46
+ )
47
+ self.cursor = cursor
48
+ self.rows = rows
49
+ self._stream_buffer_size = stream_buffer_size
50
+ self.stage_path = f"@{self._STAGE_NAME}/{uuid.uuid4().hex}"
51
+
52
+ def _create_stage(self) -> None:
53
+ create_stage_sql = (
54
+ f"create or replace {get_temp_type_for_object(self._use_scoped_temp_object)} stage {self._STAGE_NAME} "
55
+ "file_format=(type=csv field_optionally_enclosed_by='\"')"
56
+ )
57
+ self.cursor.execute(create_stage_sql)
58
+
59
+ def upload(self) -> None:
60
+ try:
61
+ self._create_stage()
62
+ except Error as err:
63
+ self.cursor.connection._session_parameters[
64
+ "CLIENT_STAGE_ARRAY_BINDING_THRESHOLD"
65
+ ] = 0
66
+ logger.debug("Failed to create stage for binding.")
67
+ raise BindUploadError from err
68
+
69
+ row_idx = 0
70
+ while row_idx < len(self.rows):
71
+ f = BytesIO()
72
+ size = 0
73
+ while True:
74
+ f.write(self.rows[row_idx])
75
+ size += len(self.rows[row_idx])
76
+ row_idx += 1
77
+ if row_idx >= len(self.rows) or size >= self._stream_buffer_size:
78
+ break
79
+ try:
80
+ f.seek(0)
81
+ self.cursor._upload_stream(
82
+ input_stream=f,
83
+ stage_location=os.path.join(self.stage_path, f"{row_idx}.csv"),
84
+ options={"source_compression": "auto_detect"},
85
+ )
86
+ except Error as err:
87
+ logger.debug("Failed to upload the bindings file to stage.")
88
+ raise BindUploadError from err
89
+ f.close()