snowflake-connector-python 5.2.0__cp315-cp315-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 (204) hide show
  1. snowflake/connector/__init__.py +148 -0
  2. snowflake/connector/_common/__init__.py +0 -0
  3. snowflake/connector/_common/extras.py +134 -0
  4. snowflake/connector/_common/telemetry.py +221 -0
  5. snowflake/connector/_common/wif_util.py +92 -0
  6. snowflake/connector/_core/sf_core_python.cp315-win_amd64.pyd +0 -0
  7. snowflake/connector/_core/sf_core_python.pyi +79 -0
  8. snowflake/connector/_internal/__init__.py +6 -0
  9. snowflake/connector/_internal/_private_key_helper.py +31 -0
  10. snowflake/connector/_internal/api_client/__init__.py +93 -0
  11. snowflake/connector/_internal/api_client/bridge.py +41 -0
  12. snowflake/connector/_internal/api_client/client_api.py +976 -0
  13. snowflake/connector/_internal/arrow.py +47 -0
  14. snowflake/connector/_internal/arrow_context.py +240 -0
  15. snowflake/connector/_internal/arrow_stream_async.py +266 -0
  16. snowflake/connector/_internal/arrow_stream_iterator.cp311-win_amd64.pyd +0 -0
  17. snowflake/connector/_internal/arrow_stream_iterator.cp312-win_amd64.pyd +0 -0
  18. snowflake/connector/_internal/arrow_stream_iterator.cp313-win_amd64.pyd +0 -0
  19. snowflake/connector/_internal/arrow_stream_iterator.cp314-win_amd64.pyd +0 -0
  20. snowflake/connector/_internal/arrow_stream_iterator.cp314t-win_amd64.pyd +0 -0
  21. snowflake/connector/_internal/arrow_stream_iterator.cp315-win_amd64.pyd +0 -0
  22. snowflake/connector/_internal/arrow_stream_iterator.pyi +42 -0
  23. snowflake/connector/_internal/arrow_stream_utils.py +171 -0
  24. snowflake/connector/_internal/authenticator_type.py +53 -0
  25. snowflake/connector/_internal/backward_compatibility.py +245 -0
  26. snowflake/connector/_internal/binding_converters.py +741 -0
  27. snowflake/connector/_internal/config_utils.py +71 -0
  28. snowflake/connector/_internal/connection/__init__.py +39 -0
  29. snowflake/connector/_internal/connection/connection.py +616 -0
  30. snowflake/connector/_internal/connection/connection_types.py +13 -0
  31. snowflake/connector/_internal/connection/constants.py +25 -0
  32. snowflake/connector/_internal/connection/decorators.py +31 -0
  33. snowflake/connector/_internal/connection/freezable_proxy.py +104 -0
  34. snowflake/connector/_internal/connection/queries.py +10 -0
  35. snowflake/connector/_internal/connection_config_mixin.py +755 -0
  36. snowflake/connector/_internal/cursor/__init__.py +25 -0
  37. snowflake/connector/_internal/cursor/base.py +732 -0
  38. snowflake/connector/_internal/cursor/cursor_types.py +13 -0
  39. snowflake/connector/_internal/cursor/decorators.py +56 -0
  40. snowflake/connector/_internal/cursor/query_result.py +202 -0
  41. snowflake/connector/_internal/cursor/query_result_waiter.py +98 -0
  42. snowflake/connector/_internal/cursor/result_metadata.py +255 -0
  43. snowflake/connector/_internal/cursor/result_set_wrapper.py +66 -0
  44. snowflake/connector/_internal/decorators.py +539 -0
  45. snowflake/connector/_internal/error_kinds.py +111 -0
  46. snowflake/connector/_internal/errorcode.py +35 -0
  47. snowflake/connector/_internal/errorhandler.py +278 -0
  48. snowflake/connector/_internal/fips.py +28 -0
  49. snowflake/connector/_internal/logging/__init__.py +52 -0
  50. snowflake/connector/_internal/logging/config.py +134 -0
  51. snowflake/connector/_internal/logging/core_logger.py +218 -0
  52. snowflake/connector/_internal/logging/native_extension_logger.py +144 -0
  53. snowflake/connector/_internal/logout_config_mapping.py +187 -0
  54. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/ArrayConverter.cpp +57 -0
  55. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/ArrayConverter.hpp +29 -0
  56. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/ArrowTableConverter.cpp +943 -0
  57. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/ArrowTableConverter.hpp +63 -0
  58. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/BinaryConverter.cpp +18 -0
  59. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/BinaryConverter.hpp +26 -0
  60. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/BooleanConverter.cpp +21 -0
  61. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/BooleanConverter.hpp +23 -0
  62. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/CArrowIterator.cpp +118 -0
  63. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/CArrowIterator.hpp +116 -0
  64. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/CArrowStreamIterator.cpp +323 -0
  65. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/CArrowStreamIterator.hpp +133 -0
  66. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/CArrowStreamTableIterator.cpp +275 -0
  67. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/CArrowStreamTableIterator.hpp +168 -0
  68. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/ConverterUtil.cpp +439 -0
  69. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/ConverterUtil.hpp +30 -0
  70. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/DateConverter.cpp +48 -0
  71. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/DateConverter.hpp +46 -0
  72. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/DecFloatConverter.cpp +76 -0
  73. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/DecFloatConverter.hpp +35 -0
  74. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/DecimalConverter.cpp +90 -0
  75. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/DecimalConverter.hpp +63 -0
  76. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/FixedSizeListConverter.cpp +62 -0
  77. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/FixedSizeListConverter.hpp +28 -0
  78. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/FloatConverter.cpp +29 -0
  79. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/FloatConverter.hpp +35 -0
  80. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/IColumnConverter.hpp +17 -0
  81. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/IntConverter.cpp +23 -0
  82. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/IntConverter.hpp +41 -0
  83. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/IntervalConverter.cpp +65 -0
  84. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/IntervalConverter.hpp +54 -0
  85. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/LICENSE.txt +209 -0
  86. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/MapConverter.cpp +79 -0
  87. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/MapConverter.hpp +30 -0
  88. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/ObjectConverter.cpp +52 -0
  89. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/ObjectConverter.hpp +29 -0
  90. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/Python/Common.cpp +8 -0
  91. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/Python/Common.hpp +117 -0
  92. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/Python/Helpers.cpp +54 -0
  93. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/Python/Helpers.hpp +34 -0
  94. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/SnowflakeType.cpp +33 -0
  95. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/SnowflakeType.hpp +48 -0
  96. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/StringConverter.cpp +18 -0
  97. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/StringConverter.hpp +26 -0
  98. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/TimeConverter.cpp +39 -0
  99. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/TimeConverter.hpp +31 -0
  100. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/TimeStampConverter.cpp +326 -0
  101. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/TimeStampConverter.hpp +138 -0
  102. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/Util/macros.hpp +14 -0
  103. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/Util/time.cpp +65 -0
  104. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/Util/time.hpp +68 -0
  105. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/arrow_stream_iterator.cpp +13278 -0
  106. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/arrow_stream_iterator.pyx +204 -0
  107. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_accessors.h +109 -0
  108. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_alloc.h +125 -0
  109. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_assert.h +45 -0
  110. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_builder.h +1914 -0
  111. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_emitter.h +208 -0
  112. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_endian.h +128 -0
  113. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_epilogue.h +7 -0
  114. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_flatbuffers.h +58 -0
  115. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_identifier.h +144 -0
  116. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_iov.h +31 -0
  117. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_prologue.h +8 -0
  118. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_refmap.h +144 -0
  119. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_rtconfig.h +160 -0
  120. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_types.h +98 -0
  121. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_verifier.h +243 -0
  122. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/flatcc_portable.h +14 -0
  123. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/paligned_alloc.h +207 -0
  124. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pattributes.h +80 -0
  125. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic.h +83 -0
  126. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic_pop.h +20 -0
  127. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic_push.h +51 -0
  128. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pendian.h +201 -0
  129. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pendian_detect.h +108 -0
  130. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pinline.h +19 -0
  131. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pinttypes.h +52 -0
  132. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/portable.h +2 -0
  133. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/portable_basic.h +25 -0
  134. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstatic_assert.h +67 -0
  135. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstdalign.h +164 -0
  136. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstdint.h +926 -0
  137. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/punaligned.h +182 -0
  138. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pversion.h +6 -0
  139. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pwarnings.h +52 -0
  140. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc.c +3052 -0
  141. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/nanoarrow.c +3019 -0
  142. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/nanoarrow.h +3354 -0
  143. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/nanoarrow.hpp +344 -0
  144. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/nanoarrow_ipc.c +29305 -0
  145. snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/nanoarrow_ipc.h +381 -0
  146. snowflake/connector/_internal/nanoarrow_cpp/Logging/logging.cpp +108 -0
  147. snowflake/connector/_internal/nanoarrow_cpp/Logging/logging.hpp +47 -0
  148. snowflake/connector/_internal/nanoarrow_cpp/scripts/.clang-format +8 -0
  149. snowflake/connector/_internal/nanoarrow_cpp/scripts/format.sh +8 -0
  150. snowflake/connector/_internal/protobuf_gen/__init__.py +0 -0
  151. snowflake/connector/_internal/protobuf_gen/database_driver_v1_pb2.py +408 -0
  152. snowflake/connector/_internal/protobuf_gen/database_driver_v1_pb2.pyi +4487 -0
  153. snowflake/connector/_internal/protobuf_gen/database_driver_v1_services.py +2088 -0
  154. snowflake/connector/_internal/protobuf_gen/proto_exception.py +17 -0
  155. snowflake/connector/_internal/result_batch.py +150 -0
  156. snowflake/connector/_internal/snowflake_restful.py +222 -0
  157. snowflake/connector/_internal/snowpark_backcompat_helpers.py +31 -0
  158. snowflake/connector/_internal/sqlstate.py +11 -0
  159. snowflake/connector/_internal/statement_utils.py +207 -0
  160. snowflake/connector/_internal/text_utils.py +112 -0
  161. snowflake/connector/_internal/type_codes.py +139 -0
  162. snowflake/connector/_internal/utils.py +65 -0
  163. snowflake/connector/_internal/write_pandas_operation.py +657 -0
  164. snowflake/connector/aio/README.md +168 -0
  165. snowflake/connector/aio/__init__.py +37 -0
  166. snowflake/connector/aio/connection/__init__.py +17 -0
  167. snowflake/connector/aio/connection/_connection.py +506 -0
  168. snowflake/connector/aio/cursor/__init__.py +35 -0
  169. snowflake/connector/aio/cursor/_base.py +1034 -0
  170. snowflake/connector/aio/cursor/_dict_cursor.py +78 -0
  171. snowflake/connector/aio/cursor/_result_set_wrapper.py +35 -0
  172. snowflake/connector/aio/cursor/_snowflake_cursor.py +72 -0
  173. snowflake/connector/aio/pandas_tools.py +279 -0
  174. snowflake/connector/aio/result_batch.py +165 -0
  175. snowflake/connector/auth/__init__.py +0 -0
  176. snowflake/connector/auth/workload_identity.py +5 -0
  177. snowflake/connector/compat.py +22 -0
  178. snowflake/connector/config_manager.py +392 -0
  179. snowflake/connector/connection/__init__.py +30 -0
  180. snowflake/connector/connection/_connection.py +641 -0
  181. snowflake/connector/connection_config.py +517 -0
  182. snowflake/connector/constants.py +52 -0
  183. snowflake/connector/cursor/__init__.py +37 -0
  184. snowflake/connector/cursor/_base.py +1169 -0
  185. snowflake/connector/cursor/_chunked_download_reader.py +75 -0
  186. snowflake/connector/cursor/_dict_cursor.py +78 -0
  187. snowflake/connector/cursor/_result_set_wrapper.py +35 -0
  188. snowflake/connector/cursor/_snowflake_cursor.py +72 -0
  189. snowflake/connector/description.py +7 -0
  190. snowflake/connector/errors.py +492 -0
  191. snowflake/connector/network.py +71 -0
  192. snowflake/connector/pandas_tools.py +191 -0
  193. snowflake/connector/py.typed +0 -0
  194. snowflake/connector/result_batch.py +216 -0
  195. snowflake/connector/telemetry.py +40 -0
  196. snowflake/connector/time_util.py +11 -0
  197. snowflake/connector/types.py +134 -0
  198. snowflake/connector/util_text.py +3 -0
  199. snowflake/connector/version.py +23 -0
  200. snowflake_connector_python-5.2.0.dist-info/DELVEWHEEL +2 -0
  201. snowflake_connector_python-5.2.0.dist-info/METADATA +190 -0
  202. snowflake_connector_python-5.2.0.dist-info/RECORD +204 -0
  203. snowflake_connector_python-5.2.0.dist-info/WHEEL +4 -0
  204. snowflake_connector_python.libs/msvcp140-a4c2229bdc2a2a630acdc095b4d86008.dll +0 -0
@@ -0,0 +1,47 @@
1
+ """Shared protocols for Arrow row and table iterators (Cython nanoarrow and native PyO3)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import AsyncIterator
6
+ from typing import Any, Protocol
7
+
8
+
9
+ class ArrowRowIterator(Protocol):
10
+ """Row iterator produced by ``create_row_iterator``.
11
+
12
+ Both the Cython ``ArrowStreamIterator`` and the PyO3
13
+ ``sf_core_python.ArrowStreamIterator`` (``native-arrow`` builds) satisfy
14
+ this protocol, so call sites do not branch on the backend.
15
+ """
16
+
17
+ def __iter__(self) -> ArrowRowIterator: ...
18
+ def __next__(self) -> Any: ...
19
+ def fetch_many(self, size: int) -> list[Any]: ...
20
+ def fetch_all(self) -> list[Any]: ...
21
+
22
+
23
+ class AsyncArrowRowIterator(Protocol):
24
+ """Async row iterator used by the aio cursor and ResultBatch.
25
+
26
+ The ``to_thread`` wrapper over a sync iterator and the driver over the
27
+ PyO3 batch reader (``native-arrow`` builds) both satisfy this protocol.
28
+ """
29
+
30
+ def __aiter__(self) -> AsyncIterator[Any]: ...
31
+ async def __anext__(self) -> Any: ...
32
+ async def fetch_next(self, default: object = None) -> Any: ...
33
+ async def fetch_many(self, size: int) -> list[Any]: ...
34
+ async def fetch_all(self) -> list[Any]: ...
35
+
36
+
37
+ class ArrowTableIterator(Protocol):
38
+ """RecordBatch iterator produced by ``create_table_iterator``.
39
+
40
+ Both the Cython ``ArrowStreamTableIterator`` and the PyO3
41
+ ``sf_core_python.ArrowStreamTableIterator`` (``native-arrow`` builds)
42
+ satisfy this protocol, so call sites do not branch on the backend.
43
+ """
44
+
45
+ def __iter__(self) -> ArrowTableIterator: ...
46
+ def __next__(self) -> Any: ...
47
+ def get_converted_schema(self) -> Any: ...
@@ -0,0 +1,240 @@
1
+ """Arrow converter context for type conversions.
2
+
3
+ This module is based on the original snowflake-connector-python arrow_context.py
4
+ and provides Python helper functions for Arrow conversions in the C++ converters.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import decimal
10
+
11
+ from datetime import datetime, timedelta, timezone, tzinfo
12
+ from sys import byteorder
13
+ from typing import TYPE_CHECKING, Any, Callable
14
+
15
+ import pytz
16
+
17
+ from .logging import get_logger
18
+ from .._common.extras import numpy as np
19
+ from .._common.extras import tzlocal, MissingOptionalDependency
20
+
21
+ if TYPE_CHECKING:
22
+ from numpy import datetime64, float64, int64, timedelta64
23
+
24
+ from ..aio.connection import Connection as AsyncConnection
25
+ from ..connection import Connection
26
+
27
+
28
+ ZERO_EPOCH = datetime.fromtimestamp(0, timezone.utc).replace(tzinfo=None)
29
+ PARAMETER_TIMEZONE = "TIMEZONE"
30
+ logger = get_logger(__name__)
31
+
32
+
33
+ def _generate_tzinfo_from_tzoffset(tzoffset_minutes: int) -> tzinfo:
34
+ """Generate tzinfo object from tzoffset."""
35
+ return pytz.FixedOffset(tzoffset_minutes)
36
+
37
+
38
+ def interval_year_month_to_string(interval: int, scale: int) -> str:
39
+ """Convert a year-month interval to a string.
40
+
41
+ Args:
42
+ interval: The year-month interval value in months.
43
+ scale: The scale of the interval which represents subtype as follows:
44
+ 0: INTERVAL YEAR TO MONTH
45
+ 1: INTERVAL YEAR
46
+ 2: INTERVAL MONTH
47
+
48
+ Returns:
49
+ The string representation of the interval.
50
+ """
51
+ sign = "+" if interval >= 0 else "-"
52
+ interval = abs(interval)
53
+ if scale == 2: # INTERVAL MONTH
54
+ return f"{sign}{interval}"
55
+ years = interval // 12
56
+ if scale == 1: # INTERVAL YEAR
57
+ return f"{sign}{years}"
58
+ # INTERVAL YEAR TO MONTH
59
+ months = interval % 12
60
+ return f"{sign}{years}-{months:02}"
61
+
62
+
63
+ class ArrowConverterContext:
64
+ """Python helper functions for arrow conversions.
65
+
66
+ Windows timestamp functions are necessary because Windows cannot handle -ve timestamps.
67
+ Putting the OS check into the non-windows function would probably take up more CPU cycles then
68
+ just deciding this at compile time.
69
+ """
70
+
71
+ def __init__(
72
+ self,
73
+ timezone: str | None = None,
74
+ *,
75
+ timezone_supplier: Callable[[], str | None] | None = None,
76
+ ) -> None:
77
+ # An explicit ``timezone`` takes precedence and marks the value resolved.
78
+ # Otherwise, ``timezone_supplier`` (if any) is invoked lazily on first access.
79
+ self._tz_value = timezone
80
+ self._tz_supplier = timezone_supplier if timezone is None else None
81
+
82
+ @classmethod
83
+ def create(cls, connection: Connection | AsyncConnection) -> ArrowConverterContext:
84
+ """Create a context whose timezone resolves lazily from the session."""
85
+ return cls(
86
+ timezone_supplier=lambda: connection._session_parameters._get_string(PARAMETER_TIMEZONE),
87
+ )
88
+
89
+ @property
90
+ def _timezone(self) -> str | None:
91
+ """Resolved session timezone, invoking the supplier once on first read.
92
+
93
+ Named ``_timezone`` because the C++ table iterator reads this attribute
94
+ directly via ``PyObject_GetAttrString(context, "_timezone")``.
95
+ """
96
+ supplier = self._tz_supplier
97
+ if supplier is not None:
98
+ self._tz_value = supplier()
99
+ self._tz_supplier = None
100
+ return self._tz_value
101
+
102
+ @property
103
+ def timezone(self) -> str | None:
104
+ return self._timezone
105
+
106
+ @timezone.setter
107
+ def timezone(self, tz: str | None) -> None:
108
+ self._tz_value = tz
109
+ self._tz_supplier = None
110
+
111
+ def __getstate__(self) -> dict[str, Any]:
112
+ # resolve timezone before pickling
113
+ return {"timezone": self._timezone}
114
+
115
+ def __setstate__(self, state: dict[str, Any]) -> None:
116
+ self._tz_value = state.get("timezone")
117
+ self._tz_supplier = None
118
+
119
+ def _get_session_tz(self) -> tzinfo:
120
+ """Get the session timezone or use the local computer's timezone."""
121
+ try:
122
+ tz = "UTC" if not self.timezone else str(self.timezone)
123
+ return pytz.timezone(tz)
124
+ except pytz.exceptions.UnknownTimeZoneError:
125
+ logger.warning("converting to tzinfo failed")
126
+ if not isinstance(tzlocal, MissingOptionalDependency):
127
+ return tzlocal.get_localzone() # type: ignore[no-any-return]
128
+ else:
129
+ return timezone.utc
130
+
131
+ def TIMESTAMP_TZ_to_python(self, epoch: int, microseconds: int, tz: int) -> datetime:
132
+ tzinfo = _generate_tzinfo_from_tzoffset(tz - 1440)
133
+ return datetime.fromtimestamp(epoch, tz=tzinfo) + timedelta(microseconds=microseconds)
134
+
135
+ def TIMESTAMP_TZ_to_python_windows(self, epoch: int, microseconds: int, tz: int) -> datetime:
136
+ tzinfo = _generate_tzinfo_from_tzoffset(tz - 1440)
137
+ t = ZERO_EPOCH + timedelta(seconds=epoch, microseconds=microseconds)
138
+ if pytz.utc != tzinfo:
139
+ offset = tzinfo.utcoffset(t)
140
+ if offset is not None:
141
+ t += offset
142
+ return t.replace(tzinfo=tzinfo)
143
+
144
+ def TIMESTAMP_NTZ_to_python(self, epoch: int, microseconds: int) -> datetime:
145
+ return datetime.fromtimestamp(epoch, timezone.utc).replace(tzinfo=None) + timedelta(microseconds=microseconds)
146
+
147
+ def TIMESTAMP_NTZ_to_python_windows(self, epoch: int, microseconds: int) -> datetime:
148
+ return ZERO_EPOCH + timedelta(seconds=epoch, microseconds=microseconds)
149
+
150
+ def TIMESTAMP_LTZ_to_python(self, epoch: int, microseconds: int) -> datetime:
151
+ tzinfo = self._get_session_tz()
152
+ return datetime.fromtimestamp(epoch, tz=tzinfo) + timedelta(microseconds=microseconds)
153
+
154
+ def TIMESTAMP_LTZ_to_python_windows(self, epoch: int, microseconds: int) -> datetime:
155
+ tzinfo = self._get_session_tz()
156
+ ts = ZERO_EPOCH + timedelta(seconds=epoch, microseconds=microseconds)
157
+ return pytz.utc.localize(ts, is_dst=False).astimezone(tzinfo)
158
+
159
+ def REAL_to_numpy_float64(self, py_double: float) -> float64:
160
+ return np.float64(py_double) # type: ignore[no-any-return]
161
+
162
+ def FIXED_to_numpy_int64(self, py_long: int) -> int64:
163
+ return np.int64(py_long) # type: ignore[no-any-return]
164
+
165
+ def FIXED_to_numpy_float64(self, py_long: int, scale: int) -> float64:
166
+ return np.float64(decimal.Decimal(py_long).scaleb(-scale)) # type: ignore[no-any-return]
167
+
168
+ def DATE_to_numpy_datetime64(self, py_days: int) -> datetime64:
169
+ return np.datetime64(py_days, "D") # type: ignore[no-any-return]
170
+
171
+ def TIMESTAMP_NTZ_ONE_FIELD_to_numpy_datetime64(self, value: int, scale: int) -> datetime64:
172
+ nanoseconds = int(decimal.Decimal(value).scaleb(9 - scale))
173
+ return np.datetime64(nanoseconds, "ns") # type: ignore[no-any-return]
174
+
175
+ def TIMESTAMP_NTZ_TWO_FIELD_to_numpy_datetime64(self, epoch: int, fraction: int) -> datetime64:
176
+ nanoseconds = int(decimal.Decimal(epoch).scaleb(9) + decimal.Decimal(fraction))
177
+ return np.datetime64(nanoseconds, "ns") # type: ignore[no-any-return]
178
+
179
+ def DECIMAL128_to_decimal_or_int(self, int128_bytes: bytes, scale: int) -> decimal.Decimal | int:
180
+ """When scale=0 (integer), returns Python int. When scale>0 (decimal), returns Python Decimal.
181
+
182
+ Large scale integers (int128) fall back here as they're not supported by native C++ conversion.
183
+ """
184
+ int128 = int.from_bytes(int128_bytes, byteorder=byteorder, signed=True)
185
+ if scale == 0:
186
+ return int128
187
+ digits = [int(digit) for digit in str(int128) if digit != "-"]
188
+ sign = int128 < 0
189
+ return decimal.Decimal((sign, digits, -scale))
190
+
191
+ def DECFLOAT_to_decimal(self, exponent: int, significand: bytes) -> decimal.Decimal:
192
+ # significand is two's complement big endian.
193
+ significand_int = int.from_bytes(significand, byteorder="big", signed=True)
194
+ return decimal.Decimal(significand_int).scaleb(exponent)
195
+
196
+ def DECFLOAT_to_numpy_float64(self, exponent: int, significand: bytes) -> float64:
197
+ return np.float64(self.DECFLOAT_to_decimal(exponent, significand)) # type: ignore[no-any-return]
198
+
199
+ def INTERVAL_YEAR_MONTH_to_str(self, months: int, scale: int) -> str:
200
+ return interval_year_month_to_string(months, scale)
201
+
202
+ def INTERVAL_YEAR_MONTH_to_numpy_timedelta(self, months: int, scale: int) -> timedelta64:
203
+ if scale == 1:
204
+ return np.timedelta64(months // 12, "Y") # type: ignore[no-any-return]
205
+ return np.timedelta64(months, "M") # type: ignore[no-any-return]
206
+
207
+ def INTERVAL_DAY_TIME_int_to_numpy_timedelta(self, nanos: int) -> timedelta64:
208
+ return np.timedelta64(nanos, "ns") # type: ignore[no-any-return]
209
+
210
+ def INTERVAL_DAY_TIME_int_to_timedelta(self, nanos: int) -> timedelta:
211
+ # Python timedelta only supports microsecond precision. We receive value in
212
+ # nanoseconds.
213
+ return timedelta(microseconds=nanos // 1000)
214
+
215
+ def INTERVAL_DAY_TIME_decimal_to_numpy_timedelta(self, value: bytes) -> timedelta64:
216
+ # Snowflake supports up to 9 digits leading field precision for the day-time
217
+ # interval. That when represented in nanoseconds can not be stored in a 64-bit
218
+ # integer. So we send these as Decimal128 from server to client.
219
+ # Arrow uses little-endian by default.
220
+ # https://arrow.apache.org/docs/format/Columnar.html#byte-order-endianness
221
+ nanos = int.from_bytes(value, byteorder="little", signed=True)
222
+ # Numpy timedelta only supports up to 64-bit integers, so we need to change the
223
+ # unit to milliseconds to avoid overflow.
224
+ # Max value received from server
225
+ # = 10**9 * NANOS_PER_DAY - 1
226
+ # = 86399999999999999999999 nanoseconds
227
+ # = 86399999999999999 milliseconds
228
+ # math.log2(86399999999999999) = 56.3 < 64
229
+ return np.timedelta64(nanos // 1_000_000, "ms") # type: ignore[no-any-return]
230
+
231
+ def INTERVAL_DAY_TIME_decimal_to_timedelta(self, value: bytes) -> timedelta:
232
+ # Snowflake supports up to 9 digits leading field precision for the day-time
233
+ # interval. That when represented in nanoseconds can not be stored in a 64-bit
234
+ # integer. So we send these as Decimal128 from server to client.
235
+ # Arrow uses little-endian by default.
236
+ # https://arrow.apache.org/docs/format/Columnar.html#byte-order-endianness
237
+ nanos = int.from_bytes(value, byteorder="little", signed=True)
238
+ # Python timedelta only supports microsecond precision. We receive value in
239
+ # nanoseconds.
240
+ return timedelta(microseconds=nanos // 1000)
@@ -0,0 +1,266 @@
1
+ """Async adapters over the synchronous nanoarrow iterators.
2
+
3
+ Why every advance goes through ``asyncio.to_thread``
4
+ ----------------------------------------------------
5
+ The Arrow stream iterators (:class:`ArrowStreamIterator`,
6
+ the table iterator from :func:`create_table_iterator`) and the helpers in :mod:`arrow_stream_utils`
7
+ expose a purely synchronous API: there is no native async entry point. A single
8
+ ``next()`` does two kinds of work, and both are unfriendly to the event loop:
9
+
10
+ 1. **Thread-blocking I/O at chunk boundaries.** Under the hood the core builds a
11
+ prefetching reader whose ``next()`` calls ``blocking_recv()`` on a channel:
12
+ it parks the *calling OS thread* until a background task has finished
13
+ downloading and parsing the next result chunk. The downloads themselves run
14
+ on the core's own (multi-threaded Tokio) runtime, so they do **not** depend
15
+ on the Python event loop, but the consuming ``next()`` still blocks its
16
+ thread waiting for them.
17
+ 2. **GIL-bound CPU work.** Decoding a row/batch into Python objects (building
18
+ tuples/dicts, running the per-column converters) holds the GIL the entire
19
+ time. Only the chunk-boundary fetch above releases the GIL; the decode does
20
+ not.
21
+
22
+ If we called ``next()`` directly inside a coroutine, both of those would run on
23
+ the event-loop thread: chunk-boundary advances would block the loop on network
24
+ I/O, and every decode would hold the GIL, so no other task could make progress.
25
+ This does *not* deadlock (the downloads complete independently on the core
26
+ runtime), but it defeats the point of an async API. Offloading to a worker
27
+ thread via :func:`asyncio.to_thread` keeps the loop free while a blocking
28
+ advance is in flight.
29
+
30
+ So why offload *every* call rather than just the slow ones? From Python there
31
+ is no reliable way to tell a cheap advance (popping an already-buffered row)
32
+ from an expensive one (a chunk-boundary ``blocking_recv`` or a heavy decode), so
33
+ the uniform, safe choice is to offload them all and accept a thread handoff on
34
+ the cheap ones.
35
+
36
+ Possible modifications (intentionally not done yet)
37
+ ---------------------------------------------------
38
+ The row path pays this hop *per row*: ``__anext__`` / :meth:`fetch_next` offload
39
+ a single ``next()`` each, so a large result set incurs one ``to_thread`` hop
40
+ (and a round of GIL ping-pong) for every row. ``fetch_many`` / ``fetch_all``
41
+ already avoid this by batching many rows into one native call under a single
42
+ hop. Row iteration could be made similarly cheap by backing ``__anext__`` with
43
+ an internal buffer refilled via :meth:`fetch_many` under one hop, instead of one
44
+ hop per row.
45
+
46
+ Alternatively, if performance tests show the offload is unnecessary for the
47
+ common case (e.g. the loop-blocking cost is negligible in practice), the advance
48
+ could simply be a plain synchronous ``next()`` call with no ``to_thread`` hop at
49
+ all. Left as-is for now; revisit either way if async iteration shows up as a
50
+ bottleneck.
51
+
52
+
53
+ This module centralizes those ``to_thread`` hops behind small async wrappers so
54
+ the async cursor and async result batch share one implementation (and one
55
+ place documenting this rationale) instead of repeating
56
+ ``await asyncio.to_thread(...)`` at every call site.
57
+ """
58
+
59
+ from __future__ import annotations
60
+
61
+ import asyncio
62
+
63
+ from collections.abc import AsyncIterator, Sequence
64
+ from typing import TYPE_CHECKING, Any, Protocol
65
+
66
+ from snowflake.connector._core import sf_core_python
67
+
68
+ from .arrow_stream_utils import (
69
+ _missing_native_class,
70
+ collect_arrow_table,
71
+ create_row_iterator,
72
+ )
73
+
74
+
75
+ if TYPE_CHECKING:
76
+ from pyarrow import Table
77
+
78
+ from .arrow import ArrowRowIterator, ArrowTableIterator, AsyncArrowRowIterator
79
+ from .arrow_context import ArrowConverterContext
80
+ from .cursor.result_metadata import ResultMetadata
81
+ from .protobuf_gen.database_driver_v1_pb2 import ResultSetHandle
82
+
83
+
84
+ # Sentinel handed to ``next(iterator, default)`` so a ``StopIteration`` raised
85
+ # inside a worker thread never has to cross the ``to_thread`` boundary (where it
86
+ # would be wrapped in confusing ways). The caller compares identity against it.
87
+ _ITER_DONE = object()
88
+
89
+
90
+ def _next_or_default(iterator: Any, default: object) -> Any:
91
+ """Typed ``next(iterator, default)`` suitable for :func:`asyncio.to_thread`.
92
+
93
+ The builtin ``next`` is overloaded, which mypy refuses to pass directly to
94
+ ``to_thread``. This thin wrapper gives it a concrete, checkable signature.
95
+ """
96
+ return next(iterator, default)
97
+
98
+
99
+ class AsyncArrowStreamIterator(AsyncIterator[Any]):
100
+ """Async facade over a synchronous nanoarrow iterator.
101
+
102
+ Wraps an :class:`ArrowRowIterator` (row iterator) or an
103
+ :class:`ArrowTableIterator` (RecordBatch iterator) and exposes async
104
+ iteration plus bulk ``fetch_many`` / ``fetch_all`` helpers. Every advance of
105
+ the underlying iterator runs on a worker thread via :func:`asyncio.to_thread`
106
+ (see module docstring for why).
107
+ """
108
+
109
+ def __init__(
110
+ self,
111
+ iterator: ArrowRowIterator | ArrowTableIterator,
112
+ ) -> None:
113
+ self._iterator = iterator
114
+
115
+ def __aiter__(self) -> AsyncArrowStreamIterator:
116
+ return self
117
+
118
+ async def __anext__(self) -> Any:
119
+ item = await asyncio.to_thread(_next_or_default, self._iterator, _ITER_DONE)
120
+ if item is _ITER_DONE:
121
+ raise StopAsyncIteration
122
+ return item
123
+
124
+ async def fetch_next(self, default: object = None) -> Any:
125
+ """Return the next item, or *default* when the stream is exhausted.
126
+
127
+ Unlike :meth:`__anext__` this never raises ``StopAsyncIteration``; it
128
+ is the async analogue of ``next(it, default)`` and is convenient for
129
+ manual fetch loops (e.g. ``fetchone``).
130
+ """
131
+ item = await asyncio.to_thread(_next_or_default, self._iterator, _ITER_DONE)
132
+ return default if item is _ITER_DONE else item
133
+
134
+ async def fetch_many(self, size: int) -> list[Any]:
135
+ """Fetch up to *size* rows in a single threaded C++ call."""
136
+ return await asyncio.to_thread(self._iterator.fetch_many, size) # type: ignore[union-attr]
137
+
138
+ async def fetch_all(self) -> list[Any]:
139
+ """Fetch all remaining rows in a single threaded C++ call."""
140
+ return await asyncio.to_thread(self._iterator.fetch_all) # type: ignore[union-attr]
141
+
142
+
143
+ class NativeArrowBatchReader(Protocol):
144
+ """The ``native-arrow`` reader: one awaitable for I/O, the rest synchronous."""
145
+
146
+ async def next_batch(self) -> bool: ...
147
+ def take_row(self, default: object = None) -> Any: ...
148
+ def convert(self, count: int | None = None) -> int: ...
149
+ def take_converted(self) -> list[Any]: ...
150
+
151
+
152
+ class NativeAsyncArrowRowIterator(AsyncIterator[Any]):
153
+ """Row iterator over a :class:`NativeArrowBatchReader`.
154
+
155
+ Only ``next_batch`` suspends, and it does no Python-object work. Every row
156
+ is materialized by a synchronous call, so conversion runs on whichever
157
+ thread drives the coroutine — the event-loop thread — rather than on a
158
+ core runtime worker.
159
+ """
160
+
161
+ def __init__(self, reader: NativeArrowBatchReader) -> None:
162
+ self._reader = reader
163
+
164
+ def __aiter__(self) -> NativeAsyncArrowRowIterator:
165
+ return self
166
+
167
+ async def __anext__(self) -> Any:
168
+ row = await self.fetch_next(_ITER_DONE)
169
+ if row is _ITER_DONE:
170
+ raise StopAsyncIteration
171
+ return row
172
+
173
+ async def fetch_next(self, default: object = None) -> Any:
174
+ while True:
175
+ row = self._reader.take_row(_ITER_DONE)
176
+ if row is not _ITER_DONE:
177
+ return row
178
+ if not await self._reader.next_batch():
179
+ return default
180
+
181
+ async def fetch_many(self, size: int) -> list[Any]:
182
+ remaining = size
183
+ while remaining > 0:
184
+ remaining -= self._reader.convert(remaining)
185
+ if remaining > 0 and not await self._reader.next_batch():
186
+ break
187
+ return self._reader.take_converted()
188
+
189
+ async def fetch_all(self) -> list[Any]:
190
+ while True:
191
+ self._reader.convert()
192
+ if not await self._reader.next_batch():
193
+ return self._reader.take_converted()
194
+
195
+
196
+ async def create_async_row_iterator_from_result_set(
197
+ result_set_handle: ResultSetHandle,
198
+ *,
199
+ context: ArrowConverterContext,
200
+ use_dict_result: bool = False,
201
+ use_numpy: bool = False,
202
+ ) -> AsyncArrowRowIterator:
203
+ """Yield rows from the live result set; upcoming chunks download in the background."""
204
+ iterator_cls = getattr(sf_core_python, "AsyncArrowStreamIterator", None)
205
+ if iterator_cls is None:
206
+ raise _missing_native_class("AsyncArrowStreamIterator")
207
+ reader = await iterator_cls.from_result_set(
208
+ result_set_handle.id,
209
+ result_set_handle.magic,
210
+ session_timezone=context.timezone,
211
+ use_dict_result=use_dict_result,
212
+ use_numpy=use_numpy,
213
+ )
214
+ return NativeAsyncArrowRowIterator(reader)
215
+
216
+
217
+ def create_async_row_iterator_from_stream_ptr(
218
+ stream_ptr: int,
219
+ *,
220
+ context: ArrowConverterContext,
221
+ use_dict_result: bool = False,
222
+ use_numpy: bool = False,
223
+ use_core_arrow: bool = False,
224
+ ) -> AsyncArrowRowIterator:
225
+ """Wrap the sync iterator in ``asyncio.to_thread``.
226
+
227
+ Used when native-arrow is off, and for ResultBatches that still
228
+ arrive as a C stream pointer.
229
+ """
230
+ return AsyncArrowStreamIterator(
231
+ create_row_iterator(
232
+ stream_ptr,
233
+ context=context,
234
+ use_dict_result=use_dict_result,
235
+ use_numpy=use_numpy,
236
+ use_core_arrow=use_core_arrow,
237
+ )
238
+ )
239
+
240
+
241
+ async def collect_arrow_table_async(
242
+ table_iterator: ArrowTableIterator,
243
+ columns_metadata: Sequence[ResultMetadata] | None = None,
244
+ force_return_table: bool = False,
245
+ ) -> Table | None:
246
+ """Async wrapper for :func:`collect_arrow_table`.
247
+
248
+ Draining *table_iterator* into a single Arrow table runs every blocking
249
+ ``next()`` plus the final ``Table.from_batches`` on a worker thread.
250
+ """
251
+ return await asyncio.to_thread(
252
+ collect_arrow_table,
253
+ table_iterator,
254
+ columns_metadata,
255
+ force_return_table,
256
+ )
257
+
258
+
259
+ async def to_pandas_async(table: Table, **kwargs: Any) -> Any:
260
+ """Convert an Arrow table to a pandas DataFrame off the event loop.
261
+
262
+ ``Table.to_pandas`` is CPU-heavy C/Cython work with no async entry point, so
263
+ it is offloaded for the same reason as the fetch path. ``**kwargs`` are
264
+ forwarded to ``Table.to_pandas`` (e.g. ``split_blocks``, ``self_destruct``).
265
+ """
266
+ return await asyncio.to_thread(table.to_pandas, **kwargs)
@@ -0,0 +1,42 @@
1
+ """Type stubs for the Cython arrow_stream_iterator extension module.
2
+
3
+ The actual implementation is compiled from
4
+ nanoarrow_cpp/ArrowIterator/arrow_stream_iterator.pyx at wheel-build time.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from collections.abc import Iterator
10
+ from typing import Any
11
+
12
+ class ArrowStreamIterator(Iterator[Any]):
13
+ """Python wrapper for C++ Arrow stream iterator.
14
+
15
+ Reads directly from an ArrowArrayStream pointer.
16
+ """
17
+
18
+ def __init__(
19
+ self,
20
+ stream_ptr: int,
21
+ arrow_context: Any,
22
+ use_dict_result: bool = False,
23
+ use_numpy: bool = False,
24
+ ) -> None: ...
25
+ def __iter__(self) -> ArrowStreamIterator: ...
26
+ def __next__(self) -> Any: ...
27
+ def fetch_many(self, size: int) -> list[Any]: ...
28
+ def fetch_all(self) -> list[Any]: ...
29
+
30
+ class ArrowStreamTableIterator(Iterator[Any]):
31
+ """Iterator that yields one pyarrow RecordBatch per batch with Snowflake type conversions applied."""
32
+
33
+ def __init__(
34
+ self,
35
+ stream_ptr: int,
36
+ arrow_context: Any,
37
+ number_to_decimal: bool = False,
38
+ force_microsecond_precision: bool = False,
39
+ ) -> None: ...
40
+ def __iter__(self) -> ArrowStreamTableIterator: ...
41
+ def __next__(self) -> Any: ...
42
+ def get_converted_schema(self) -> Any: ...