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,159 @@
1
+ #!/usr/bin/env python
2
+ from __future__ import annotations
3
+
4
+ import time
5
+ from logging import getLogger
6
+ from types import TracebackType
7
+ from typing import Callable, Iterator
8
+
9
+ logger = getLogger(__name__)
10
+
11
+ try:
12
+ from threading import _Timer as Timer
13
+ except ImportError:
14
+ from threading import Timer
15
+
16
+ DEFAULT_MASTER_VALIDITY_IN_SECONDS = 4 * 60 * 60 # seconds
17
+
18
+
19
+ class HeartBeatTimer(Timer):
20
+ """A thread which executes a function every client_session_keep_alive_heartbeat_frequency seconds."""
21
+
22
+ def __init__(
23
+ self, client_session_keep_alive_heartbeat_frequency: int, f: Callable
24
+ ) -> None:
25
+ interval = client_session_keep_alive_heartbeat_frequency
26
+ super().__init__(interval, f)
27
+ # Mark this as a daemon thread, so that it won't prevent Python from exiting.
28
+ self.daemon = True
29
+
30
+ def run(self) -> None:
31
+ while not self.finished.is_set():
32
+ self.finished.wait(self.interval)
33
+ if not self.finished.is_set():
34
+ try:
35
+ self.function()
36
+ except Exception as e:
37
+ logger.debug("failed to heartbeat: %s", e)
38
+
39
+
40
+ def get_time_millis() -> int:
41
+ """Returns the current time in milliseconds."""
42
+ return int(time.time() * 1000)
43
+
44
+
45
+ class TimerContextManager:
46
+ """Context manager class to easily measure execution of a code block.
47
+
48
+ Once the context manager finishes, the class should be cast into an int to retrieve
49
+ result.
50
+
51
+ Example:
52
+
53
+ with TimerContextManager() as measured_time:
54
+ pass
55
+ download_metric = measured_time.get_timing_millis()
56
+ """
57
+
58
+ def __init__(self) -> None:
59
+ self._start: int | None = None
60
+ self._end: int | None = None
61
+
62
+ def __enter__(self) -> TimerContextManager:
63
+ self._start = get_time_millis()
64
+ return self
65
+
66
+ def __exit__(
67
+ self,
68
+ exc_type: type[BaseException] | None,
69
+ exc_val: BaseException | None,
70
+ exc_tb: TracebackType | None,
71
+ ) -> None:
72
+ self._end = get_time_millis()
73
+
74
+ def get_timing_millis(self) -> int:
75
+ """Get measured timing in milliseconds."""
76
+ if self._start is None or self._end is None:
77
+ raise Exception(
78
+ "Trying to get timing before TimerContextManager has finished"
79
+ )
80
+ return self._end - self._start
81
+
82
+
83
+ class TimeoutBackoffCtx:
84
+ """Base context for handling timeouts and backoffs on retries"""
85
+
86
+ def __init__(
87
+ self,
88
+ max_retry_attempts: int | None = None,
89
+ timeout: int | None = None,
90
+ backoff_generator: Iterator | None = None,
91
+ ) -> None:
92
+ self._backoff_generator = backoff_generator
93
+
94
+ self._max_retry_attempts = max_retry_attempts
95
+ # in seconds
96
+ self._timeout = timeout
97
+
98
+ self._current_retry_count = 0
99
+ self._current_sleep_time = self._advance_backoff()
100
+ self._start_time_millis = None
101
+
102
+ @property
103
+ def timeout(self) -> int | None:
104
+ return self._timeout
105
+
106
+ @property
107
+ def current_retry_count(self) -> int:
108
+ return int(self._current_retry_count)
109
+
110
+ @property
111
+ def current_sleep_time(self) -> int:
112
+ return int(self._current_sleep_time)
113
+
114
+ @property
115
+ def remaining_time_millis(self) -> int:
116
+ if self._start_time_millis is None:
117
+ raise TypeError(
118
+ "Start time not recorded in remaining_time_millis, call set_start_time first"
119
+ )
120
+
121
+ if self._timeout is None:
122
+ raise TypeError("Timeout is None in remaining_time_millis")
123
+
124
+ timeout_millis = self._timeout * 1000
125
+ elapsed_time_millis = get_time_millis() - self._start_time_millis
126
+ return timeout_millis - elapsed_time_millis
127
+
128
+ @property
129
+ def should_retry(self) -> bool:
130
+ """Decides whether to retry connection."""
131
+ if self._timeout is not None and self._start_time_millis is None:
132
+ logger.warning(
133
+ "Timeout set in TimeoutBackoffCtx, but start time not recorded"
134
+ )
135
+
136
+ timed_out = (
137
+ self.remaining_time_millis < 0 if self._timeout is not None else False
138
+ )
139
+ retry_attempts_exceeded = (
140
+ self._current_retry_count >= self._max_retry_attempts
141
+ if self._max_retry_attempts is not None
142
+ else False
143
+ )
144
+ return not timed_out and not retry_attempts_exceeded
145
+
146
+ def _advance_backoff(self) -> int:
147
+ return (
148
+ next(self._backoff_generator) if self._backoff_generator is not None else 0
149
+ )
150
+
151
+ def set_start_time(self) -> None:
152
+ self._start_time_millis = get_time_millis()
153
+
154
+ def increment(self) -> None:
155
+ """Updates retry count and sleep time for another retry"""
156
+ self._current_retry_count += 1
157
+ self._current_sleep_time = self._advance_backoff()
158
+ logger.debug(f"Update retry count to {self._current_retry_count}")
159
+ logger.debug(f"Update sleep time to {self._current_sleep_time} seconds")
@@ -0,0 +1,402 @@
1
+ from __future__ import annotations
2
+
3
+ import codecs
4
+ import hashlib
5
+ import json
6
+ import logging
7
+ import os
8
+ import stat
9
+ import sys
10
+ from abc import ABC, abstractmethod
11
+ from dataclasses import dataclass
12
+ from enum import Enum
13
+ from pathlib import Path
14
+ from typing import Any, TypeVar
15
+
16
+ from .compat import IS_LINUX, IS_MACOS, IS_WINDOWS
17
+ from .file_lock import FileLock, FileLockError
18
+ from .options import installed_keyring, keyring
19
+
20
+ logger = logging.getLogger(__name__)
21
+ T = TypeVar("T")
22
+
23
+
24
+ class TokenType(Enum):
25
+ ID_TOKEN = "ID_TOKEN"
26
+ MFA_TOKEN = "MFA_TOKEN"
27
+ OAUTH_ACCESS_TOKEN = "OAUTH_ACCESS_TOKEN"
28
+ OAUTH_REFRESH_TOKEN = "OAUTH_REFRESH_TOKEN"
29
+
30
+
31
+ class _InvalidTokenKeyError(Exception):
32
+ pass
33
+
34
+
35
+ @dataclass
36
+ class TokenKey:
37
+ user: str
38
+ host: str
39
+ tokenType: TokenType
40
+
41
+ def string_key(self) -> str:
42
+ if len(self.host) == 0:
43
+ raise _InvalidTokenKeyError("Invalid key, host is empty")
44
+ if len(self.user) == 0:
45
+ raise _InvalidTokenKeyError("Invalid key, user is empty")
46
+ return f"{self.host.upper()}:{self.user.upper()}:{self.tokenType.value}"
47
+
48
+ def hash_key(self) -> str:
49
+ m = hashlib.sha256()
50
+ m.update(self.string_key().encode(encoding="utf-8"))
51
+ return m.hexdigest()
52
+
53
+
54
+ def _warn(warning: str) -> None:
55
+ logger.warning(warning)
56
+ print("Warning: " + warning, file=sys.stderr)
57
+
58
+
59
+ class TokenCache(ABC):
60
+ @staticmethod
61
+ def make() -> TokenCache:
62
+ if IS_MACOS or IS_WINDOWS:
63
+ if not installed_keyring:
64
+ _warn(
65
+ "Dependency 'keyring' is not installed, cannot cache id token. You might experience "
66
+ "multiple authentication pop ups while using ExternalBrowser/OAuth/MFA Authenticator. To avoid "
67
+ "this please install keyring module using the following command:\n"
68
+ " pip install snowflake-connector-python[secure-local-storage]"
69
+ )
70
+ return NoopTokenCache()
71
+ return KeyringTokenCache()
72
+
73
+ if IS_LINUX:
74
+ cache = FileTokenCache.make()
75
+ if cache:
76
+ return cache
77
+ else:
78
+ _warn(
79
+ "Failed to initialize file based token cache. You might experience "
80
+ "multiple authentication pop ups while using ExternalBrowser/OAuth/MFA Authenticator."
81
+ )
82
+ return NoopTokenCache()
83
+
84
+ @abstractmethod
85
+ def store(self, key: TokenKey, token: str) -> None:
86
+ pass
87
+
88
+ @abstractmethod
89
+ def retrieve(self, key: TokenKey) -> str | None:
90
+ pass
91
+
92
+ @abstractmethod
93
+ def remove(self, key: TokenKey) -> None:
94
+ pass
95
+
96
+
97
+ class _FileTokenCacheError(Exception):
98
+ pass
99
+
100
+
101
+ class _OwnershipError(_FileTokenCacheError):
102
+ pass
103
+
104
+
105
+ class _PermissionsTooWideError(_FileTokenCacheError):
106
+ pass
107
+
108
+
109
+ class _CacheDirNotFoundError(_FileTokenCacheError):
110
+ pass
111
+
112
+
113
+ class _InvalidCacheDirError(_FileTokenCacheError):
114
+ pass
115
+
116
+
117
+ class _MalformedCacheFileError(_FileTokenCacheError):
118
+ pass
119
+
120
+
121
+ class _CacheFileReadError(_FileTokenCacheError):
122
+ pass
123
+
124
+
125
+ class _CacheFileWriteError(_FileTokenCacheError):
126
+ pass
127
+
128
+
129
+ class FileTokenCache(TokenCache):
130
+ @staticmethod
131
+ def make() -> FileTokenCache | None:
132
+ cache_dir = FileTokenCache.find_cache_dir()
133
+ if cache_dir is None:
134
+ logging.getLogger(__name__).debug(
135
+ "Failed to find suitable cache directory for token cache. File based token cache initialization failed."
136
+ )
137
+ return None
138
+ else:
139
+ return FileTokenCache(cache_dir)
140
+
141
+ def __init__(self, cache_dir: Path) -> None:
142
+ self.logger = logging.getLogger(__name__)
143
+ self.cache_dir: Path = cache_dir
144
+
145
+ def store(self, key: TokenKey, token: str) -> None:
146
+ try:
147
+ FileTokenCache.validate_cache_dir(self.cache_dir)
148
+ with FileLock(self.lock_file()):
149
+ cache = self._read_cache_file()
150
+ cache["tokens"][key.hash_key()] = token
151
+ self._write_cache_file(cache)
152
+ except _FileTokenCacheError as e:
153
+ self.logger.error(f"Failed to store token: {e=}")
154
+ except FileLockError as e:
155
+ self.logger.error(f"Unable to lock file lock: {e=}")
156
+ except _InvalidTokenKeyError as e:
157
+ self.logger.error(f"Failed to produce token key {e=}")
158
+
159
+ def retrieve(self, key: TokenKey) -> str | None:
160
+ try:
161
+ FileTokenCache.validate_cache_dir(self.cache_dir)
162
+ with FileLock(self.lock_file()):
163
+ cache = self._read_cache_file()
164
+ token = cache["tokens"].get(key.hash_key(), None)
165
+ if isinstance(token, str):
166
+ return token
167
+ else:
168
+ return None
169
+ except _FileTokenCacheError as e:
170
+ self.logger.error(f"Failed to retrieve token: {e=}")
171
+ return None
172
+ except FileLockError as e:
173
+ self.logger.error(f"Unable to lock file lock: {e=}")
174
+ return None
175
+ except _InvalidTokenKeyError as e:
176
+ self.logger.error(f"Failed to produce token key {e=}")
177
+ return None
178
+
179
+ def remove(self, key: TokenKey) -> None:
180
+ try:
181
+ FileTokenCache.validate_cache_dir(self.cache_dir)
182
+ with FileLock(self.lock_file()):
183
+ cache = self._read_cache_file()
184
+ cache["tokens"].pop(key.hash_key(), None)
185
+ self._write_cache_file(cache)
186
+ except _FileTokenCacheError as e:
187
+ self.logger.error(f"Failed to remove token: {e=}")
188
+ except FileLockError as e:
189
+ self.logger.error(f"Unable to lock file lock: {e=}")
190
+ except _InvalidTokenKeyError as e:
191
+ self.logger.error(f"Failed to produce token key {e=}")
192
+
193
+ def cache_file(self) -> Path:
194
+ return self.cache_dir / "credential_cache_v1.json"
195
+
196
+ def lock_file(self) -> Path:
197
+ return self.cache_dir / "credential_cache_v1.json.lck"
198
+
199
+ def _read_cache_file(self) -> dict[str, dict[str, Any]]:
200
+ fd = -1
201
+ json_data = {"tokens": {}}
202
+ try:
203
+ fd = os.open(self.cache_file(), os.O_RDONLY)
204
+ self._ensure_permissions(fd, 0o600)
205
+ size = os.lseek(fd, 0, os.SEEK_END)
206
+ os.lseek(fd, 0, os.SEEK_SET)
207
+ data = os.read(fd, size)
208
+ json_data = json.loads(codecs.decode(data, "utf-8"))
209
+ except FileNotFoundError:
210
+ self.logger.debug(f"{self.cache_file()} not found")
211
+ except json.decoder.JSONDecodeError as e:
212
+ self.logger.warning(
213
+ f"Failed to decode json read from cache file {self.cache_file()}: {e.__class__.__name__}"
214
+ )
215
+ except UnicodeError as e:
216
+ self.logger.warning(
217
+ f"Failed to decode utf-8 read from cache file {self.cache_file()}: {e.__class__.__name__}"
218
+ )
219
+ except OSError as e:
220
+ self.logger.warning(f"Failed to read cache file {self.cache_file()}: {e}")
221
+ finally:
222
+ if fd > 0:
223
+ os.close(fd)
224
+
225
+ if "tokens" not in json_data or not isinstance(json_data["tokens"], dict):
226
+ json_data["tokens"] = {}
227
+
228
+ return json_data
229
+
230
+ def _write_cache_file(self, json_data: dict):
231
+ fd = -1
232
+ self.logger.debug(f"Writing cache file {self.cache_file()}")
233
+ try:
234
+ fd = os.open(
235
+ self.cache_file(), os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600
236
+ )
237
+ self._ensure_permissions(fd, 0o600)
238
+ os.write(fd, codecs.encode(json.dumps(json_data), "utf-8"))
239
+ return json_data
240
+ except OSError as e:
241
+ raise _CacheFileWriteError("Failed to write cache file", e)
242
+ finally:
243
+ if fd > 0:
244
+ os.close(fd)
245
+
246
+ @staticmethod
247
+ def find_cache_dir() -> Path | None:
248
+ def lookup_env_dir(env_var: str, subpath_segments: list[str]) -> Path | None:
249
+ env_val = os.getenv(env_var)
250
+ if env_val is None:
251
+ logger.debug(
252
+ f"Environment variable {env_var} not set. Skipping it in cache directory lookup."
253
+ )
254
+ return None
255
+
256
+ directory = Path(env_val)
257
+
258
+ if len(subpath_segments) > 0:
259
+ if not directory.exists():
260
+ logger.debug(
261
+ f"Path {str(directory)} does not exist. Skipping it in cache directory lookup."
262
+ )
263
+ return None
264
+
265
+ if not directory.is_dir():
266
+ logger.debug(
267
+ f"Path {str(directory)} is not a directory. Skipping it in cache directory lookup."
268
+ )
269
+ return None
270
+
271
+ for subpath in subpath_segments[:-1]:
272
+ directory = directory / subpath
273
+ directory.mkdir(exist_ok=True, mode=0o755)
274
+
275
+ directory = directory / subpath_segments[-1]
276
+ directory.mkdir(exist_ok=True, mode=0o700)
277
+
278
+ try:
279
+ FileTokenCache.validate_cache_dir(directory)
280
+ return directory
281
+ except _FileTokenCacheError as e:
282
+ logger.debug(
283
+ f"Cache directory validation failed for {str(directory)} due to error '{e}'. Skipping it in cache directory lookup."
284
+ )
285
+ return None
286
+
287
+ lookup_functions = [
288
+ lambda: lookup_env_dir("SF_TEMPORARY_CREDENTIAL_CACHE_DIR", []),
289
+ lambda: lookup_env_dir("XDG_CACHE_HOME", ["snowflake"]),
290
+ lambda: lookup_env_dir("HOME", [".cache", "snowflake"]),
291
+ ]
292
+
293
+ for lf in lookup_functions:
294
+ cache_dir = lf()
295
+ if cache_dir:
296
+ return cache_dir
297
+
298
+ return None
299
+
300
+ @staticmethod
301
+ def validate_cache_dir(cache_dir: Path | None) -> None:
302
+ try:
303
+ statinfo = cache_dir.stat()
304
+
305
+ if cache_dir is None:
306
+ raise _CacheDirNotFoundError("Cache dir was not found")
307
+
308
+ if not stat.S_ISDIR(statinfo.st_mode):
309
+ raise _InvalidCacheDirError(f"Cache dir {cache_dir} is not a directory")
310
+
311
+ permissions = stat.S_IMODE(statinfo.st_mode)
312
+ if permissions != 0o700:
313
+ raise _PermissionsTooWideError(
314
+ f"Cache dir {cache_dir} has incorrect permissions. {permissions:o} != 0700"
315
+ )
316
+
317
+ euid = os.geteuid()
318
+ if statinfo.st_uid != euid:
319
+ raise _OwnershipError(
320
+ f"Cache dir {cache_dir} has incorrect owner. {euid} != {statinfo.st_uid}"
321
+ )
322
+
323
+ except FileNotFoundError:
324
+ raise _CacheDirNotFoundError(
325
+ f"Cache dir {cache_dir} was not found. Failed to stat."
326
+ )
327
+
328
+ def _ensure_permissions(self, fd: int, permissions: int) -> None:
329
+ try:
330
+ statinfo = os.fstat(fd)
331
+ actual_permissions = stat.S_IMODE(statinfo.st_mode)
332
+
333
+ if actual_permissions != permissions:
334
+ raise _PermissionsTooWideError(
335
+ f"Cache file {self.cache_file()} has incorrect permissions. {permissions:o} != {actual_permissions:o}"
336
+ )
337
+
338
+ euid = os.geteuid()
339
+ if statinfo.st_uid != euid:
340
+ raise _OwnershipError(
341
+ f"Cache file {self.cache_file()} has incorrect owner. {euid} != {statinfo.st_uid}"
342
+ )
343
+
344
+ except FileNotFoundError:
345
+ pass
346
+
347
+
348
+ class KeyringTokenCache(TokenCache):
349
+ def __init__(self) -> None:
350
+ self.logger = logging.getLogger(__name__)
351
+
352
+ def store(self, key: TokenKey, token: str) -> None:
353
+ try:
354
+ keyring.set_password(
355
+ key.string_key(),
356
+ key.user.upper(),
357
+ token,
358
+ )
359
+ except _InvalidTokenKeyError as e:
360
+ self.logger.error(f"Could not retrieve {key.tokenType} from keyring, {e=}")
361
+ except keyring.errors.KeyringError as ke:
362
+ self.logger.error("Could not store id_token to keyring, %s", str(ke))
363
+
364
+ def retrieve(self, key: TokenKey) -> str | None:
365
+ try:
366
+ return keyring.get_password(
367
+ key.string_key(),
368
+ key.user.upper(),
369
+ )
370
+ except keyring.errors.KeyringError as ke:
371
+ self.logger.error(
372
+ "Could not retrieve {} from secure storage : {}".format(
373
+ key.tokenType.value, str(ke)
374
+ )
375
+ )
376
+ except _InvalidTokenKeyError as e:
377
+ self.logger.error(f"Could not retrieve {key.tokenType} from keyring, {e=}")
378
+
379
+ def remove(self, key: TokenKey) -> None:
380
+ try:
381
+ keyring.delete_password(
382
+ key.string_key(),
383
+ key.user.upper(),
384
+ )
385
+ except _InvalidTokenKeyError as e:
386
+ self.logger.error(f"Could not retrieve {key.tokenType} from keyring, {e=}")
387
+ except Exception as ex:
388
+ self.logger.error(
389
+ "Failed to delete credential in the keyring: err=[%s]", ex
390
+ )
391
+ pass
392
+
393
+
394
+ class NoopTokenCache(TokenCache):
395
+ def store(self, key: TokenKey, token: str) -> None:
396
+ return None
397
+
398
+ def retrieve(self, key: TokenKey) -> str | None:
399
+ return None
400
+
401
+ def remove(self, key: TokenKey) -> None:
402
+ return None
File without changes
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env python
2
+ from __future__ import annotations
3
+
4
+ import os
5
+ import sys
6
+ from os import path
7
+ from typing import TYPE_CHECKING
8
+
9
+ from snowflake.connector.ocsp_asn1crypto import SnowflakeOCSPAsn1Crypto
10
+
11
+ if TYPE_CHECKING:
12
+ from asn1crypto.x509 import Certificate
13
+
14
+
15
+ def main() -> None:
16
+ """Internal Tool: Extract certificate files in PEM."""
17
+
18
+ def help() -> None:
19
+ print(
20
+ "Extract certificate file. The target file can be a single file "
21
+ "or a directory including multiple certificates. The certificate "
22
+ "file format should be PEM."
23
+ )
24
+ print(
25
+ """
26
+ Usage: {} <input file/dir>
27
+ """.format(
28
+ path.basename(sys.argv[0])
29
+ )
30
+ )
31
+ sys.exit(2)
32
+
33
+ if len(sys.argv) < 2:
34
+ help()
35
+
36
+ input_filename = sys.argv[1]
37
+ if path.isdir(input_filename):
38
+ files = [path.join(input_filename, f) for f in os.listdir(input_filename)]
39
+ else:
40
+ files = [input_filename]
41
+
42
+ for f in files:
43
+ open(f)
44
+ extract_certificate_file(f)
45
+
46
+
47
+ def extract_certificate_file(input_filename) -> None:
48
+ ocsp = SnowflakeOCSPAsn1Crypto()
49
+ cert_map: dict[bytes, Certificate] = {}
50
+ ocsp.read_cert_bundle(input_filename, cert_map)
51
+
52
+ for cert in cert_map.values():
53
+ print(f"serial #: {cert.serial_number}, name: {cert.subject.native}")
54
+
55
+
56
+ if __name__ == "__main__":
57
+ main()