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,478 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import shutil
5
+ import tempfile
6
+ import threading
7
+ import time
8
+ from abc import ABC, abstractmethod
9
+ from collections import defaultdict
10
+ from io import BytesIO
11
+ from logging import getLogger
12
+ from math import ceil
13
+ from pathlib import Path
14
+ from typing import TYPE_CHECKING, Any, Callable, NamedTuple
15
+
16
+ import OpenSSL
17
+
18
+ from .constants import (
19
+ HTTP_HEADER_CONTENT_ENCODING,
20
+ REQUEST_CONNECTION_TIMEOUT,
21
+ REQUEST_READ_TIMEOUT,
22
+ FileHeader,
23
+ ResultStatus,
24
+ )
25
+ from .encryption_util import EncryptionMetadata, SnowflakeEncryptionUtil
26
+ from .errors import RequestExceedMaxRetryError
27
+ from .file_util import SnowflakeFileUtil
28
+ from .vendored import requests
29
+ from .vendored.requests import ConnectionError, Timeout
30
+ from .vendored.urllib3 import HTTPResponse
31
+
32
+ if TYPE_CHECKING: # pragma: no cover
33
+ from .file_transfer_agent import SnowflakeFileMeta, StorageCredential
34
+
35
+ logger = getLogger(__name__)
36
+
37
+
38
+ class SnowflakeFileEncryptionMaterial(NamedTuple):
39
+ query_stage_master_key: str # query stage master key
40
+ query_id: str # query id
41
+ smk_id: int # SMK id
42
+
43
+
44
+ METHODS = {
45
+ "GET": requests.get,
46
+ "PUT": requests.put,
47
+ "POST": requests.post,
48
+ "HEAD": requests.head,
49
+ "DELETE": requests.delete,
50
+ }
51
+
52
+
53
+ def remove_content_encoding(resp: requests.Response, **kwargs) -> None:
54
+ """Remove content-encoding header and decoder so decompression is not triggered"""
55
+ if HTTP_HEADER_CONTENT_ENCODING in resp.headers:
56
+ if isinstance(resp.raw, HTTPResponse):
57
+ resp.raw._decoder = None
58
+ resp.raw.headers.pop(HTTP_HEADER_CONTENT_ENCODING)
59
+
60
+
61
+ class SnowflakeStorageClient(ABC):
62
+ TRANSIENT_HTTP_ERR = (408, 429, 500, 502, 503, 504)
63
+
64
+ TRANSIENT_ERRORS = (OpenSSL.SSL.SysCallError, Timeout, ConnectionError)
65
+ SLEEP_MAX = 16.0
66
+ SLEEP_UNIT = 1.0
67
+
68
+ def __init__(
69
+ self,
70
+ meta: SnowflakeFileMeta,
71
+ stage_info: dict[str, Any],
72
+ chunk_size: int,
73
+ chunked_transfer: bool | None = True,
74
+ credentials: StorageCredential | None = None,
75
+ max_retry: int = 5,
76
+ unsafe_file_write: bool = False,
77
+ ) -> None:
78
+ self.meta = meta
79
+ self.stage_info = stage_info
80
+ self.retry_count: dict[int | str, int] = defaultdict(int)
81
+ self.tmp_dir = tempfile.mkdtemp()
82
+ self.data_file: str | None = None
83
+ self.encryption_metadata: EncryptionMetadata | None = None
84
+
85
+ self.max_retry = max_retry # TODO
86
+ self.credentials = credentials
87
+ # UPLOAD
88
+ meta.real_src_file_name = meta.src_file_name
89
+ meta.upload_size = meta.src_file_size
90
+ self.preprocessed = (
91
+ False # so we don't repeat compression/file digest when re-encrypting
92
+ )
93
+ # DOWNLOAD
94
+ self.full_dst_file_name: str | None = (
95
+ os.path.join(
96
+ self.meta.local_location, os.path.basename(self.meta.dst_file_name)
97
+ )
98
+ if self.meta.local_location
99
+ else None
100
+ )
101
+ self.intermediate_dst_path: Path | None = (
102
+ Path(self.full_dst_file_name + ".part")
103
+ if self.meta.local_location
104
+ else None
105
+ )
106
+ # CHUNK
107
+ self.chunked_transfer = chunked_transfer # only true for GCS
108
+ self.chunk_size = chunk_size
109
+ self.num_of_chunks = 0
110
+ self.lock = threading.Lock()
111
+ self.successful_transfers: int = 0
112
+ self.failed_transfers: int = 0
113
+ # only used when PRESIGNED_URL expires
114
+ self.last_err_is_presigned_url = False
115
+ self.unsafe_file_write = unsafe_file_write
116
+
117
+ def compress(self) -> None:
118
+ if self.meta.require_compress:
119
+ meta = self.meta
120
+ logger.debug(f"compressing file={meta.src_file_name}")
121
+ if meta.intermediate_stream:
122
+ (
123
+ meta.src_stream,
124
+ upload_size,
125
+ ) = SnowflakeFileUtil.compress_with_gzip_from_stream(
126
+ meta.intermediate_stream
127
+ )
128
+ else:
129
+ (
130
+ meta.real_src_file_name,
131
+ upload_size,
132
+ ) = SnowflakeFileUtil.compress_file_with_gzip(
133
+ meta.src_file_name, self.tmp_dir
134
+ )
135
+
136
+ def get_digest(self) -> None:
137
+ meta = self.meta
138
+ logger.debug(f"getting digest file={meta.real_src_file_name}")
139
+ if meta.intermediate_stream is None:
140
+ (
141
+ meta.sha256_digest,
142
+ meta.upload_size,
143
+ ) = SnowflakeFileUtil.get_digest_and_size_for_file(meta.real_src_file_name)
144
+ else:
145
+ (
146
+ meta.sha256_digest,
147
+ meta.upload_size,
148
+ ) = SnowflakeFileUtil.get_digest_and_size_for_stream(
149
+ meta.src_stream or meta.intermediate_stream
150
+ )
151
+
152
+ def encrypt(self) -> None:
153
+ meta = self.meta
154
+ logger.debug(f"encrypting file={meta.real_src_file_name}")
155
+ if meta.intermediate_stream is None:
156
+ (
157
+ self.encryption_metadata,
158
+ self.data_file,
159
+ ) = SnowflakeEncryptionUtil.encrypt_file(
160
+ meta.encryption_material,
161
+ meta.real_src_file_name,
162
+ tmp_dir=self.tmp_dir,
163
+ )
164
+ meta.upload_size = os.path.getsize(self.data_file)
165
+ else:
166
+ encrypted_stream = BytesIO()
167
+ src_stream = meta.src_stream or meta.intermediate_stream
168
+ src_stream.seek(0)
169
+ self.encryption_metadata = SnowflakeEncryptionUtil.encrypt_stream(
170
+ meta.encryption_material, src_stream, encrypted_stream
171
+ )
172
+ src_stream.seek(0)
173
+ meta.upload_size = encrypted_stream.seek(0, os.SEEK_END)
174
+ encrypted_stream.seek(0)
175
+ if meta.src_stream is not None:
176
+ meta.src_stream.close()
177
+ meta.src_stream = encrypted_stream
178
+ self.data_file = meta.real_src_file_name
179
+
180
+ @abstractmethod
181
+ def get_file_header(self, filename: str) -> FileHeader | None:
182
+ """Check if file exists in target location and obtain file metadata if exists.
183
+
184
+ Notes:
185
+ Updates meta.result_status.
186
+ """
187
+ pass
188
+
189
+ def preprocess(self) -> None:
190
+ meta = self.meta
191
+ logger.debug(f"Preprocessing {meta.src_file_name}")
192
+
193
+ file_header = self.get_file_header(
194
+ meta.dst_file_name
195
+ ) # check if file exists on remote
196
+ if not meta.overwrite:
197
+ self.get_digest() # self.get_file_header needs digest for multiparts upload when aws is used.
198
+ if meta.result_status == ResultStatus.UPLOADED:
199
+ # Skipped
200
+ logger.debug(
201
+ f'file already exists location="{self.stage_info["location"]}", '
202
+ f'file_name="{meta.dst_file_name}"'
203
+ )
204
+ meta.dst_file_size = 0
205
+ meta.result_status = ResultStatus.SKIPPED
206
+ self.preprocessed = True
207
+ return
208
+ # Uploading
209
+ if meta.require_compress:
210
+ self.compress()
211
+ self.get_digest()
212
+
213
+ if (
214
+ meta.skip_upload_on_content_match
215
+ and file_header
216
+ and meta.sha256_digest == file_header.digest
217
+ ):
218
+ logger.debug(f"same file contents for {meta.name}, skipping upload")
219
+ meta.result_status = ResultStatus.SKIPPED
220
+
221
+ self.preprocessed = True
222
+
223
+ def prepare_upload(self) -> None:
224
+ meta = self.meta
225
+
226
+ if not self.preprocessed:
227
+ self.preprocess()
228
+ elif meta.encryption_material:
229
+ # need to clean up previous encrypted file
230
+ os.remove(self.data_file)
231
+
232
+ logger.debug(f"Preparing to upload {meta.src_file_name}")
233
+
234
+ if meta.encryption_material:
235
+ self.encrypt()
236
+ else:
237
+ self.data_file = meta.real_src_file_name
238
+ logger.debug("finished preprocessing")
239
+ if meta.upload_size < meta.multipart_threshold or not self.chunked_transfer:
240
+ self.num_of_chunks = 1
241
+ else:
242
+ self.num_of_chunks = ceil(meta.upload_size / self.chunk_size)
243
+ logger.debug(f"number of chunks {self.num_of_chunks}")
244
+ # clean up
245
+ self.retry_count = {}
246
+
247
+ for chunk_id in range(self.num_of_chunks):
248
+ self.retry_count[chunk_id] = 0
249
+ if self.chunked_transfer and self.num_of_chunks > 1:
250
+ self._initiate_multipart_upload()
251
+
252
+ def finish_upload(self) -> None:
253
+ meta = self.meta
254
+ if self.successful_transfers == self.num_of_chunks and self.num_of_chunks != 0:
255
+ if self.num_of_chunks > 1:
256
+ self._complete_multipart_upload()
257
+ meta.result_status = ResultStatus.UPLOADED
258
+ meta.dst_file_size = meta.upload_size
259
+ logger.debug(f"{meta.src_file_name} upload is completed.")
260
+ else:
261
+ # TODO: add more error details to result/meta
262
+ meta.dst_file_size = 0
263
+ logger.debug(f"{meta.src_file_name} upload is aborted.")
264
+ if self.num_of_chunks > 1:
265
+ self._abort_multipart_upload()
266
+ meta.result_status = ResultStatus.ERROR
267
+
268
+ @abstractmethod
269
+ def _has_expired_token(self, response: requests.Response) -> bool:
270
+ pass
271
+
272
+ def _send_request_with_retry(
273
+ self,
274
+ verb: str,
275
+ get_request_args: Callable[[], tuple[bytes, dict[str, Any]]],
276
+ retry_id: int,
277
+ ) -> requests.Response:
278
+ rest_call = METHODS[verb]
279
+ url = b""
280
+ conn = None
281
+ if self.meta.sfagent and self.meta.sfagent._cursor.connection:
282
+ conn = self.meta.sfagent._cursor.connection
283
+
284
+ while self.retry_count[retry_id] < self.max_retry:
285
+ logger.debug(f"retry #{self.retry_count[retry_id]}")
286
+ cur_timestamp = self.credentials.timestamp
287
+ url, rest_kwargs = get_request_args()
288
+ rest_kwargs["timeout"] = (REQUEST_CONNECTION_TIMEOUT, REQUEST_READ_TIMEOUT)
289
+ try:
290
+ if conn:
291
+ with conn._rest._use_requests_session(url) as session:
292
+ logger.debug(f"storage client request with session {session}")
293
+ response = session.request(verb, url, **rest_kwargs)
294
+ else:
295
+ logger.debug("storage client request with new session")
296
+ response = rest_call(url, **rest_kwargs)
297
+
298
+ if self._has_expired_presigned_url(response):
299
+ logger.debug(
300
+ "presigned url expired. trying to update presigned url."
301
+ )
302
+ self._update_presigned_url()
303
+ else:
304
+ self.last_err_is_presigned_url = False
305
+ if response.status_code in self.TRANSIENT_HTTP_ERR:
306
+ logger.debug(f"transient error: {response.status_code}")
307
+ time.sleep(
308
+ min(
309
+ # TODO should SLEEP_UNIT come from the parent
310
+ # SnowflakeConnection and be customizable by users?
311
+ (2 ** self.retry_count[retry_id]) * self.SLEEP_UNIT,
312
+ self.SLEEP_MAX,
313
+ )
314
+ )
315
+ self.retry_count[retry_id] += 1
316
+ elif self._has_expired_token(response):
317
+ logger.debug("token is expired. trying to update token")
318
+ self.credentials.update(cur_timestamp)
319
+ self.retry_count[retry_id] += 1
320
+ else:
321
+ return response
322
+ except self.TRANSIENT_ERRORS as e:
323
+ self.last_err_is_presigned_url = False
324
+ time.sleep(
325
+ min(
326
+ (2 ** self.retry_count[retry_id]) * self.SLEEP_UNIT,
327
+ self.SLEEP_MAX,
328
+ )
329
+ )
330
+ logger.warning(f"{verb} with url {url} failed for transient error: {e}")
331
+ self.retry_count[retry_id] += 1
332
+ else:
333
+ raise RequestExceedMaxRetryError(
334
+ f"{verb} with url {url} failed for exceeding maximum retries."
335
+ )
336
+
337
+ def _open_intermediate_dst_path(self, mode):
338
+ if not self.intermediate_dst_path.exists():
339
+ self.intermediate_dst_path.touch(mode=0o600)
340
+ return self.intermediate_dst_path.open(mode)
341
+
342
+ def prepare_download(self) -> None:
343
+ # TODO: add nicer error message for when target directory is not writeable
344
+ # but this should be done before we get here
345
+ base_dir = os.path.dirname(self.full_dst_file_name)
346
+ if not os.path.exists(base_dir):
347
+ os.makedirs(base_dir)
348
+
349
+ # HEAD
350
+ file_header = self.get_file_header(self.meta.real_src_file_name)
351
+
352
+ if file_header and file_header.encryption_metadata:
353
+ self.encryption_metadata = file_header.encryption_metadata
354
+
355
+ self.num_of_chunks = 1
356
+ if file_header and file_header.content_length:
357
+ self.meta.src_file_size = file_header.content_length
358
+ if (
359
+ self.chunked_transfer
360
+ and self.meta.src_file_size > self.meta.multipart_threshold
361
+ ):
362
+ self.num_of_chunks = ceil(file_header.content_length / self.chunk_size)
363
+
364
+ # Preallocate encrypted file.
365
+ with self._open_intermediate_dst_path("wb+") as fd:
366
+ fd.truncate(self.meta.src_file_size)
367
+
368
+ def write_downloaded_chunk(self, chunk_id: int, data: bytes) -> None:
369
+ """Writes given data to the temp location starting at chunk_id * chunk_size."""
370
+ # TODO: should we use chunking and write content in smaller chunks?
371
+ with self._open_intermediate_dst_path("rb+") as fd:
372
+ fd.seek(self.chunk_size * chunk_id)
373
+ fd.write(data)
374
+
375
+ def finish_download(self) -> None:
376
+ meta = self.meta
377
+ if self.num_of_chunks != 0 and self.successful_transfers == self.num_of_chunks:
378
+ meta.result_status = ResultStatus.DOWNLOADED
379
+ if meta.encryption_material:
380
+ logger.debug(f"encrypted data file={self.full_dst_file_name}")
381
+ # For storage utils that do not have the privilege of
382
+ # getting the metadata early, both object and metadata
383
+ # are downloaded at once. In which case, the file meta will
384
+ # be updated with all the metadata that we need, and
385
+ # then we can call get_file_header to get just that and also
386
+ # preserve the idea of getting metadata in the first place.
387
+ # One example of this is the utils that use presigned url
388
+ # for upload/download and not the storage client library.
389
+ if meta.presigned_url is not None:
390
+ file_header = self.get_file_header(meta.src_file_name)
391
+ self.encryption_metadata = file_header.encryption_metadata
392
+
393
+ tmp_dst_file_name = SnowflakeEncryptionUtil.decrypt_file(
394
+ self.encryption_metadata,
395
+ meta.encryption_material,
396
+ str(self.intermediate_dst_path),
397
+ tmp_dir=self.tmp_dir,
398
+ unsafe_file_write=self.unsafe_file_write,
399
+ )
400
+ shutil.move(tmp_dst_file_name, self.full_dst_file_name)
401
+ self.intermediate_dst_path.unlink()
402
+ else:
403
+ logger.debug(f"not encrypted data file={self.full_dst_file_name}")
404
+ shutil.move(str(self.intermediate_dst_path), self.full_dst_file_name)
405
+ stat_info = os.stat(self.full_dst_file_name)
406
+ meta.dst_file_size = stat_info.st_size
407
+ else:
408
+ # TODO: add more error details to result/meta
409
+ if os.path.isfile(self.full_dst_file_name):
410
+ os.unlink(self.full_dst_file_name)
411
+ logger.exception(f"Failed to download a file: {self.full_dst_file_name}")
412
+ meta.dst_file_size = -1
413
+ meta.result_status = ResultStatus.ERROR
414
+
415
+ def upload_chunk(self, chunk_id: int) -> None:
416
+ new_stream = not bool(self.meta.src_stream or self.meta.intermediate_stream)
417
+ fd = (
418
+ self.meta.src_stream
419
+ or self.meta.intermediate_stream
420
+ or open(self.data_file, "rb")
421
+ )
422
+ try:
423
+ if self.num_of_chunks == 1:
424
+ _data = fd.read()
425
+ else:
426
+ fd.seek(chunk_id * self.chunk_size)
427
+ _data = fd.read(self.chunk_size)
428
+ finally:
429
+ if new_stream:
430
+ fd.close()
431
+ logger.debug(f"Uploading chunk {chunk_id} of file {self.data_file}")
432
+ self._upload_chunk(chunk_id, _data)
433
+ logger.debug(f"Successfully uploaded chunk {chunk_id} of file {self.data_file}")
434
+
435
+ @abstractmethod
436
+ def _upload_chunk(self, chunk_id: int, chunk: bytes) -> None:
437
+ pass
438
+
439
+ @abstractmethod
440
+ def download_chunk(self, chunk_id: int) -> None:
441
+ pass
442
+
443
+ # Override in GCS
444
+ def _has_expired_presigned_url(self, response: requests.Response) -> bool:
445
+ return False
446
+
447
+ # Override in GCS
448
+ def _update_presigned_url(self) -> None:
449
+ return
450
+
451
+ # Override in S3
452
+ def _initiate_multipart_upload(self) -> None:
453
+ return
454
+
455
+ # Override in S3
456
+ def _complete_multipart_upload(self) -> None:
457
+ return
458
+
459
+ # Override in S3
460
+ def _abort_multipart_upload(self) -> None:
461
+ return
462
+
463
+ def delete_client_data(self) -> None:
464
+ """Deletes the tmp_dir and closes the source stream belonging to this client.
465
+ This function is idempotent."""
466
+ if os.path.exists(self.tmp_dir):
467
+ logger.debug(f"cleaning up tmp dir: {self.tmp_dir}")
468
+ try:
469
+ shutil.rmtree(self.tmp_dir)
470
+ except OSError as ex:
471
+ # it's ok to ignore the exception here because another thread might
472
+ # have cleaned up the temp directory
473
+ logger.debug(f"Failed to delete {self.tmp_dir}: {ex}")
474
+ if self.meta.src_stream and not self.meta.src_stream.closed:
475
+ self.meta.src_stream.close()
476
+
477
+ def __del__(self) -> None:
478
+ self.delete_client_data()