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,755 @@
1
+ """Behavioural mixin for the generated :class:`ConnectionConfig`.
2
+
3
+ The concrete :class:`~snowflake.connector.connection_config.ConnectionConfig`
4
+ dataclass is auto-generated from the Rust ``PARAM_DEFS`` registry and contains
5
+ *only* information derivable from that source (field definitions plus the
6
+ alias / name / sensitive-param tables).
7
+
8
+ Everything else — Python-only fields, the ``_extra`` bag, legacy rewrites,
9
+ application-name validation and the ``to_options`` / ``to_proto_options``
10
+ pipeline — lives here, in a hand-written mixin that the generator does not
11
+ touch.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import os
17
+ import re
18
+ import sys
19
+ import warnings
20
+
21
+ from collections.abc import Callable, Iterable, Iterator
22
+ from dataclasses import dataclass, field, fields, replace
23
+ from typing import Any, ClassVar, TypeVar
24
+
25
+ from snowflake.connector._internal._private_key_helper import normalize_private_key
26
+ from snowflake.connector._internal.protobuf_gen.database_driver_v1_pb2 import ConfigSetting
27
+ from snowflake.connector.constants import ENV_VAR_PARTNER
28
+ from snowflake.connector.errors import ProgrammingError
29
+ from snowflake.connector.version import __version__ as _DRIVER_VERSION
30
+
31
+
32
+ _Self = TypeVar("_Self", bound="ConnectionConfigMixin")
33
+
34
+
35
+ OptionsModifier = Callable[[dict[str, Any]], dict[str, Any]]
36
+ """Callable applied to the options dict produced by ``ConnectionConfig.to_options``.
37
+
38
+ A modifier may mutate the dict in place and/or return a new dict. The return
39
+ value is fed into the next modifier in the chain. See
40
+ ``snowflake.connector._internal.logout_config_mapping.logout_config_options_modifier``
41
+ for an example modifier that re-applies legacy ``LogoutConfig`` semantics.
42
+ """
43
+
44
+
45
+ @dataclass
46
+ class ConnectionConfigMixin:
47
+ """Python-only fields and behavioural helpers for :class:`ConnectionConfig`.
48
+
49
+ Fields defined here are never derived from ``PARAM_DEFS`` — they're wrapper-
50
+ specific concerns (Python runtime flags, the ``_extra`` passthrough bag,
51
+ and so on). Class-level constants and methods implement the algorithmic
52
+ pieces (alias resolution, application validation, protobuf conversion)
53
+ that would otherwise need to be regenerated on every ``PARAM_DEFS`` change.
54
+
55
+ The concrete generated :class:`ConnectionConfig` adds the PARAM_DEFS-derived
56
+ fields and the static lookup tables (``_ALIAS_MAP``, ``_PYTHON_TO_RUST_NAME``,
57
+ ``_SENSITIVE_PARAMS``) on top of this mixin.
58
+ """
59
+
60
+ # -- Python-only fields (not in PARAM_DEFS) --------------------------------
61
+ session_parameters: dict[str, Any] | None = None
62
+ """Session parameters to set at connection time."""
63
+
64
+ numpy: bool | None = None
65
+ """Use numpy for result set processing."""
66
+
67
+ use_core_arrow: bool | None = None
68
+ """Use the core Arrow converter for result fetches."""
69
+
70
+ arrow_number_to_decimal: bool | None = None
71
+ """Convert Arrow NUMBER columns to Python Decimal."""
72
+
73
+ force_microsecond_precision: bool | None = None
74
+ """When true, ``fetch_arrow_*`` and ``fetch_pandas_*`` truncate timestamps to microseconds.
75
+
76
+ Those methods use this value when the call omits ``force_microsecond_precision``.
77
+ A per-call argument overrides it. An unset value is false.
78
+ """
79
+
80
+ paramstyle: str | None = None
81
+ """PEP 249 parameter binding style."""
82
+
83
+ autocommit: bool | None = None
84
+ """Enable/disable autocommit at connection time."""
85
+
86
+ mfa_callback: Callable[[], Iterator[None]] | None = None
87
+ """Callable that returns an iterator advanced throughout connection initialization.
88
+
89
+ Each step of the iterator runs until either the iterator is exhausted or
90
+ ``connection_init`` finishes. Exhaustion does not fail the connect; the
91
+ driver then waits for initialization to complete. The iterator is advanced
92
+ for the whole init, not only while a Duo approval is pending.
93
+ """
94
+
95
+ timezone: str | None = None
96
+ """Session TIMEZONE to set at connection time.
97
+
98
+ Merged into ``session_parameters["TIMEZONE"]`` the same way the old
99
+ connector copies ``timezone=`` onto the login session-parameter map.
100
+ When both ``timezone`` and ``session_parameters["TIMEZONE"]`` are set,
101
+ ``timezone`` wins.
102
+ """
103
+
104
+ interpolate_empty_sequences: bool | None = None
105
+ """When True, pyformat/format binding interpolates empty param sequences so
106
+ doubled ``%%`` in the SQL is unescaped. Default False matches the old driver.
107
+ """
108
+
109
+ reuse_results: bool | None = None
110
+ """When True, ``Cursor.reset()`` keeps the current result set so a later
111
+ fetch can re-read the same rows. ``close()`` still releases the handle.
112
+ Default False matches the old driver.
113
+ """
114
+
115
+ connections_file_path: str | None = None
116
+ """Path to the TOML file profiles are resolved from.
117
+
118
+ Sent to the core as ``ConnectionSetOptionsRequest.connections_file_path``,
119
+ not as a config-map setting. It overrides the standard Snowflake config
120
+ location both for a named ``connection_name`` profile and for the default
121
+ profile a bare ``connect(connections_file_path=...)`` falls back to.
122
+ Stored as ``str``; :meth:`from_connection_args` coerces ``os.PathLike``
123
+ input (e.g. ``pathlib.Path``) so the value can go straight onto the
124
+ protobuf string field.
125
+ """
126
+
127
+ auto_cleanup: bool | None = None
128
+ """Whether the connection should auto-close at interpreter shutdown via an
129
+ ``atexit`` handler. Python-only flag preserved for backward compatibility
130
+ with snowflake-connector-python; never forwarded to the Rust core. When
131
+ ``None`` the wrapper treats it as ``True`` (legacy default)."""
132
+
133
+ _extra: dict[str, Any] = field(default_factory=dict, repr=False)
134
+ """Unknown kwargs forwarded to the Rust core for validation.
135
+
136
+ Only values of the protobuf-supported scalar types (``bool``, ``int``,
137
+ ``str``, ``float``, ``bytes``) survive the trip through
138
+ :meth:`to_proto_options`; any other value type raises
139
+ :class:`~snowflake.connector.errors.ProgrammingError` so misconfigurations
140
+ fail loudly instead of being silently dropped.
141
+ """
142
+
143
+ _no_connection_details: bool = field(default=False, repr=False)
144
+ """Wrapper-internal: ``True`` when ``connect()`` was called with no
145
+ connection options other than ``connections_file_path`` (the legacy
146
+ ``is_kwargs_empty`` condition, which a separate parameter never affects).
147
+
148
+ Set by :meth:`from_connection_args` from the raw caller input, *before* any
149
+ bookkeeping params (application, client_app_id, …) are injected — only the
150
+ wrapper can observe true emptiness. Carried to sf_core as the typed
151
+ ``ConnectionSetOptionsRequest.no_connection_details`` field (not via the
152
+ options map), where it drives the default-profile fallback. The leading
153
+ underscore keeps it out of :meth:`_all_field_names`, so it is neither
154
+ settable via ``from_kwargs`` nor forwarded through :meth:`to_options`.
155
+ """
156
+
157
+ # -- Hand-written class constants -----------------------------------------
158
+ _PYTHON_ONLY: ClassVar[frozenset[str]] = frozenset(
159
+ {
160
+ "session_parameters",
161
+ "numpy",
162
+ "arrow_number_to_decimal",
163
+ "force_microsecond_precision",
164
+ "paramstyle",
165
+ "autocommit",
166
+ "mfa_callback",
167
+ "timezone",
168
+ "interpolate_empty_sequences",
169
+ "reuse_results",
170
+ "connections_file_path",
171
+ "auto_cleanup",
172
+ }
173
+ )
174
+ """Fields excluded from :meth:`to_options` / the core param map.
175
+
176
+ ``connections_file_path`` is still sent to the core as
177
+ ``ConnectionSetOptionsRequest.connections_file_path``.
178
+ """
179
+
180
+ _INTERNAL_PARAMS: ClassVar[frozenset[str]] = frozenset(
181
+ {
182
+ "client_app_id",
183
+ "client_app_version",
184
+ }
185
+ )
186
+ """Driver-identity parameters that end users must not override.
187
+
188
+ Mirrors the old connector's treatment of ``internal_application_name`` /
189
+ ``internal_application_version``: these identify the driver family on the
190
+ wire (CLIENT_APP_ID / CLIENT_APP_VERSION) and are owned by the wrapper, not
191
+ the caller. Setting them via user-facing kwargs raises ``ProgrammingError``;
192
+ the wrapper itself assigns them through ``ConnectionConfig`` attribute
193
+ access in :meth:`_finalize`."""
194
+
195
+ _LEGACY_REWRITES: ClassVar[dict[str, str | tuple[str, Callable[[Any], Any]]]] = {
196
+ # Old snowflake-connector-python CRL kwargs -> universal-driver core
197
+ # params. Silent aliases (no DeprecationWarning). A bare string is a
198
+ # rename; a ``(new_name, converter)`` tuple also converts the value
199
+ # (e.g. old unit -> core unit).
200
+ "cert_revocation_check_mode": "crl_check_mode",
201
+ "allow_certificates_without_crl_url": "crl_allow_certificates_without_crl_url",
202
+ "enable_crl_memory_cache": "crl_enable_memory_caching",
203
+ "enable_crl_file_cache": "crl_enable_disk_caching",
204
+ "crl_download_max_size": "crl_max_download_size", # both in bytes
205
+ "crl_connection_timeout_ms": ("crl_connection_timeout", lambda ms: round(ms / 1000)),
206
+ "crl_read_timeout_ms": ("crl_http_timeout", lambda ms: round(ms / 1000)),
207
+ "crl_cache_validity_hours": ("crl_validity_time", lambda h: round(h * 3600)),
208
+ "crl_cache_removal_delay_days": (
209
+ "crl_on_disk_cache_removal_delay",
210
+ lambda d: int(d) * 86400,
211
+ ),
212
+ "crl_cache_cleanup_interval_hours": (
213
+ "crl_cache_cleanup_interval",
214
+ lambda h: round(h * 3600),
215
+ ),
216
+ }
217
+ """Legacy parameter names -> canonical replacements (silent).
218
+
219
+ Values are either a canonical name (pure rename) or a
220
+ ``(canonical_name, converter)`` tuple where ``converter`` maps the old
221
+ value/unit to the core param's unit. Applied silently in
222
+ :meth:`from_kwargs` (no ``DeprecationWarning``)."""
223
+
224
+ _DEPRECATED_REWRITES: ClassVar[dict[str, str]] = {
225
+ "client_fetch_threads": "client_prefetch_threads",
226
+ "client_request_mfa_token": "client_store_temporary_credential",
227
+ "enable_stage_s3_privatelink_for_us_east_1": "use_s3_regional_url",
228
+ "private_key_file_pwd": "private_key_password",
229
+ "private_key_passphrase": "private_key_password",
230
+ "oauth_socket_uri": "oauth_redirect_uri",
231
+ "external_browser_timeout": "authentication_timeout",
232
+ # Old LinuxLocalFileCache internal toggle that leaked out as a
233
+ # connection kwarg; renames to the ``unsafe_`` form so the fan-out
234
+ # block below can handle both names uniformly.
235
+ "skip_file_permissions_check": "unsafe_skip_file_permissions_check",
236
+ }
237
+ """Deprecated parameter names -> canonical replacements.
238
+
239
+ Each hit emits a ``DeprecationWarning``. Unlike ``_LEGACY_REWRITES`` these
240
+ names are not silently supported forever — they exist so users migrating
241
+ from ``snowflake-connector-python`` get a pointer to the new name.
242
+
243
+ ``oauth_socket_uri`` is mapped to ``oauth_redirect_uri``: the legacy
244
+ ``snowflake-connector-python`` allowed the loopback listener to bind
245
+ a different host/port than the URI advertised to the IdP. The
246
+ universal core always binds the listener to ``oauth_redirect_uri``,
247
+ so a caller still using the legacy name receives the warning and the
248
+ value flows through under the canonical name.
249
+ """
250
+
251
+ _UNSUPPORTED_PARAMS: ClassVar[dict[str, str]] = {
252
+ "client_fetch_use_mp": ("not supported; universal core uses a thread pool for chunk fetch."),
253
+ "disable_request_pooling": (
254
+ "not supported; there is no request-session pool to disable "
255
+ "(HTTP keep-alive is managed by the core HTTP client)."
256
+ ),
257
+ "oauth_enable_refresh_tokens": (
258
+ "not supported; the universal core always uses the refresh token "
259
+ "returned by the IdP. Use client_store_temporary_credential to gate caching."
260
+ ),
261
+ "enable_crl_cache": (
262
+ "not supported; the universal core has independent cache toggles."
263
+ "Use enable_crl_memory_cache and enable_crl_file_cache instead"
264
+ ),
265
+ "dsn": "not supported; the old driver accepted dsn but never used it.",
266
+ "session_id": (
267
+ "not supported as a connect parameter; read connection.session_id "
268
+ "after login for the server-assigned session id."
269
+ ),
270
+ "backoff_policy": (
271
+ "not supported; configure retry_backoff_base_ms, retry_backoff_cap_ms, "
272
+ "retry_backoff_factor, and retry_backoff_jitter instead."
273
+ ),
274
+ "inject_client_pause": "not supported; internal test pause is not implemented.",
275
+ "probe_connection": "not supported; connection probing is not implemented.",
276
+ "support_negative_year": ("not supported; the SnowSQL JSON converter this flag controlled is gone."),
277
+ "json_result_force_utf8_decoding": ("not supported; JSON result chunks are not used (results are Arrow)."),
278
+ "debug_arrow_chunk": "not supported; raw Arrow-chunk debug logging is not implemented.",
279
+ "snowflake_server_dop_cap_for_file_transfer": (
280
+ "not supported; internal PUT/GET parallelism cap is not implemented."
281
+ ),
282
+ "reraise_error_in_file_transfer_work_function": (
283
+ "not supported; PUT/GET worker exceptions are not swallowed by a Python thread pool."
284
+ ),
285
+ "iobound_tpe_limit": ("not supported; PUT/GET parallelism is not capped by a Python IO-bound thread pool."),
286
+ "disable_console_login": ("not supported; external browser login always requests the SSO URL from Snowflake."),
287
+ "oauth_disable_console_login": (
288
+ "not supported; external browser login always requests the SSO URL from Snowflake."
289
+ ),
290
+ }
291
+ """Legacy kwargs that are accepted for source compatibility but have no effect."""
292
+
293
+ _SILENTLY_IGNORED_PARAMS: ClassVar[frozenset[str]] = frozenset(
294
+ {
295
+ "log_imported_packages_in_telemetry",
296
+ "log_imported_packages",
297
+ }
298
+ )
299
+
300
+ _REJECTED_PARAMS: ClassVar[dict[str, str]] = {
301
+ "password_callback": (
302
+ "'password_callback' is not supported because the driver cannot invoke "
303
+ "a callback during expired-password login. Change the expired password "
304
+ "outside the driver and reconnect without password_callback."
305
+ ),
306
+ }
307
+
308
+ _APPLICATION_NAME: ClassVar[str] = "PythonConnector"
309
+ """Default application name."""
310
+
311
+ _APPLICATION_RE: ClassVar[re.Pattern[str]] = re.compile(r"^[\w\d_]+")
312
+ """Regex for validating application names."""
313
+
314
+ @staticmethod
315
+ def _detect_application() -> str | None:
316
+ """Infer ``application`` the way snowflake-connector-python does.
317
+
318
+ Priority: ``SF_PARTNER`` env var, then imported-module sniffing
319
+ (streamlit, Jupyter notebook, Snowflake notebooks). Returns ``None``
320
+ when nothing matches so the caller can fall back to PythonConnector.
321
+ """
322
+ if ENV_VAR_PARTNER in os.environ:
323
+ return os.environ[ENV_VAR_PARTNER]
324
+ if "streamlit" in sys.modules:
325
+ return "streamlit"
326
+ if all(jpmod in sys.modules for jpmod in ("ipykernel", "jupyter_core", "jupyter_client")):
327
+ return "jupyter_notebook"
328
+ if "snowbooks" in sys.modules:
329
+ return "snowflake_notebook"
330
+ return None
331
+
332
+ # -- Generated-class hooks (populated by the concrete subclass) -----------
333
+ # These ClassVars are re-declared on the generated ``ConnectionConfig`` so
334
+ # that :meth:`from_kwargs` / :meth:`to_options` / :meth:`redacted_options`
335
+ # can look them up via ``cls`` / ``self`` without the mixin having to know
336
+ # the PARAM_DEFS contents. Empty defaults keep the mixin usable in
337
+ # isolation (tests, mypy) when no subclass is involved.
338
+ _ALIAS_MAP: ClassVar[dict[str, str]] = {}
339
+ """Lowercased alias -> Python field name. Populated by the generated subclass."""
340
+
341
+ _PYTHON_TO_RUST_NAME: ClassVar[dict[str, str]] = {}
342
+ """Python field name -> Rust canonical name. Populated by the generated subclass."""
343
+
344
+ _SENSITIVE_PARAMS: ClassVar[frozenset[str]] = frozenset()
345
+ """Fields that contain secrets. Populated by the generated subclass."""
346
+
347
+ # -- Derived accessors ----------------------------------------------------
348
+ @classmethod
349
+ def _all_field_names(cls) -> frozenset[str]:
350
+ """Names of every dataclass field (Python-only + PARAM_DEFS), sans the private ``_extra`` bag."""
351
+ return frozenset(f.name for f in fields(cls) if not f.name.startswith("_"))
352
+
353
+ # -- Factory methods ------------------------------------------------------
354
+ @classmethod
355
+ def from_kwargs(cls: type[_Self], **kwargs: Any) -> _Self:
356
+ """Create a ConnectionConfig from keyword arguments.
357
+
358
+ Resolves case-insensitive aliases, applies legacy parameter name
359
+ rewrites, and collects unknown keys into ``_extra``.
360
+
361
+ Raises ``ProgrammingError`` if a caller passes any of
362
+ ``cls._INTERNAL_PARAMS`` (``client_app_id`` / ``client_app_version``).
363
+ These identify the driver family on the wire and are wrapper-owned;
364
+ end users must use ``application`` to label their app instead.
365
+ """
366
+ # Apply legacy rewrites first (silent — canonical replacements). A value
367
+ # may be a bare canonical name (rename) or a ``(name, converter)`` tuple
368
+ # that also maps the value to the core param's unit.
369
+ for old_name, spec in cls._LEGACY_REWRITES.items():
370
+ if old_name in kwargs:
371
+ value = kwargs.pop(old_name)
372
+ if isinstance(spec, tuple):
373
+ new_name, converter = spec
374
+ if value is not None:
375
+ value = converter(value)
376
+ else:
377
+ new_name = spec
378
+ if new_name not in kwargs:
379
+ kwargs[new_name] = value
380
+
381
+ # Apply deprecated rewrites with a ``DeprecationWarning`` so callers
382
+ # migrating from snowflake-connector-python see a pointer to the new
383
+ # name. ``stacklevel=4`` surfaces the caller of ``Connection(...)`` /
384
+ # ``ConnectionConfig.from_kwargs(...)`` rather than this file.
385
+ # Call chain: user code → Connection.__init__ → from_connection_args
386
+ # → from_kwargs → warn(); 4 frames to reach user code.
387
+ # Runs before the fan-out block below so that aliases that resolve
388
+ # to ``unsafe_skip_file_permissions_check`` are renamed in time.
389
+ for old_name, new_name in cls._DEPRECATED_REWRITES.items():
390
+ if old_name in kwargs:
391
+ value = kwargs.pop(old_name)
392
+ warnings.warn(
393
+ f"{old_name!r} is deprecated; use {new_name!r} instead.",
394
+ DeprecationWarning,
395
+ stacklevel=4,
396
+ )
397
+ if new_name not in kwargs:
398
+ kwargs[new_name] = value
399
+
400
+ # The old snowflake-connector-python exposed a single
401
+ # ``unsafe_skip_file_permissions_check`` that governed BOTH the
402
+ # config-file and the CRL-cache permission checks. The universal core version
403
+ # splits these into two params, so fan the legacy flag out onto both
404
+ # (silently), unless the caller set a specific one explicitly.
405
+ if "unsafe_skip_file_permissions_check" in kwargs:
406
+ legacy_skip = kwargs.pop("unsafe_skip_file_permissions_check")
407
+ for target in (
408
+ "unsafe_skip_config_file_permissions_check",
409
+ "crl_unsafe_skip_file_permissions_check",
410
+ ):
411
+ if target not in kwargs:
412
+ kwargs[target] = legacy_skip
413
+
414
+ # The old snowflake-connector-python exposes network_timeout (wall-clock,
415
+ # all non-login/query operations) and socket_timeout (per-attempt socket
416
+ # timeout) as single knobs. The universal core driver splits each concept
417
+ # further, so fan the legacy value out onto both underlying params
418
+ # unless the caller set a specific one explicitly. Using the legacy
419
+ # names emits a DeprecationWarning pointing callers at the split params.
420
+ if "network_timeout" in kwargs:
421
+ legacy_network_timeout = kwargs.pop("network_timeout")
422
+ warnings.warn(
423
+ "'network_timeout' is deprecated; use 'query_timeout' and 'request_timeout' instead.",
424
+ DeprecationWarning,
425
+ stacklevel=4,
426
+ )
427
+ for target in ("query_timeout", "request_timeout"):
428
+ if target not in kwargs:
429
+ kwargs[target] = legacy_network_timeout
430
+
431
+ if "socket_timeout" in kwargs:
432
+ legacy_socket_timeout = kwargs.pop("socket_timeout")
433
+ warnings.warn(
434
+ "'socket_timeout' is deprecated; use 'connect_timeout' and 'retry_timeout' instead.",
435
+ DeprecationWarning,
436
+ stacklevel=4,
437
+ )
438
+ for target in ("connect_timeout", "retry_timeout"):
439
+ if target not in kwargs:
440
+ kwargs[target] = legacy_socket_timeout
441
+
442
+ for key in list(kwargs):
443
+ lower_key = key.lower()
444
+ if lower_key in cls._SILENTLY_IGNORED_PARAMS:
445
+ kwargs.pop(key)
446
+ elif lower_key in cls._REJECTED_PARAMS:
447
+ raise ProgrammingError(cls._REJECTED_PARAMS[lower_key])
448
+ elif lower_key in cls._UNSUPPORTED_PARAMS:
449
+ reason = cls._UNSUPPORTED_PARAMS[lower_key]
450
+ warnings.warn(
451
+ f"{key!r} has no effect: {reason}.",
452
+ DeprecationWarning,
453
+ stacklevel=4,
454
+ )
455
+ kwargs.pop(key)
456
+
457
+ pwd_key = next((k for k in kwargs if k.lower() == "private_key_password"), None)
458
+ if pwd_key is not None and isinstance(kwargs[pwd_key], bytes):
459
+ try:
460
+ kwargs[pwd_key] = kwargs[pwd_key].decode()
461
+ except UnicodeDecodeError as err:
462
+ raise ProgrammingError(
463
+ "private_key_password must be a str or UTF-8 bytes. "
464
+ "Decode the passphrase to a string before passing it."
465
+ ) from err
466
+
467
+ for key in kwargs:
468
+ if key.lower() in cls._INTERNAL_PARAMS:
469
+ raise ProgrammingError(
470
+ f"{key!r} is a wrapper-internal parameter and cannot be set "
471
+ f"by the caller. Use ``application`` to label your application."
472
+ )
473
+
474
+ # Legacy connector accepted list[str] for workload_identity_impersonation_path.
475
+ # Rust expects a comma-separated str; coerce here before the field resolves.
476
+ # Empty list coerces to None (same semantics as absent — legacy treats [] as falsy).
477
+ impersonation_path_key = next((k for k in kwargs if k.lower() == "workload_identity_impersonation_path"), None)
478
+ if impersonation_path_key is not None:
479
+ path = kwargs[impersonation_path_key]
480
+ if isinstance(path, list):
481
+ kwargs[impersonation_path_key] = ",".join(str(p) for p in path) if path else None
482
+
483
+ known_fields = cls._all_field_names()
484
+ resolved: dict[str, Any] = {}
485
+ extra: dict[str, Any] = {}
486
+
487
+ for key, value in kwargs.items():
488
+ lower_key = key.lower()
489
+ if lower_key in known_fields:
490
+ resolved[lower_key] = value
491
+ elif lower_key in cls._ALIAS_MAP:
492
+ resolved[cls._ALIAS_MAP[lower_key]] = value
493
+ else:
494
+ extra[key] = value
495
+
496
+ config = cls(**resolved)
497
+ config._extra = extra
498
+ return config
499
+
500
+ def _clone(self: _Self) -> _Self:
501
+ """Return a copy that does not share ``session_parameters`` or ``_extra`` with ``self``."""
502
+ cloned = replace(self)
503
+ if cloned.session_parameters is not None:
504
+ cloned.session_parameters = dict(cloned.session_parameters)
505
+ cloned._extra = dict(cloned._extra)
506
+ return cloned
507
+
508
+ @classmethod
509
+ def _finalize(
510
+ cls: type[_Self],
511
+ config: _Self,
512
+ *,
513
+ application_explicit: bool = False,
514
+ internal_app_name: Any = None,
515
+ internal_app_version: Any = None,
516
+ ) -> _Self:
517
+ """Normalize a config so ``to_options()`` can consume it.
518
+
519
+ Used both after kwargs construction and for ``connect(config=...)``.
520
+ Already-set ``client_app_id`` / ``client_app_version`` are kept so a
521
+ reused SnowSQL/Snow CLI config does not fall back to the Python driver
522
+ identity; missing identity fields still receive the same defaults as
523
+ kwargs ``connect()``.
524
+
525
+ * ``private_key`` - normalises RSAPrivateKey / EllipticCurvePrivateKey /
526
+ bytes / str via :func:`normalize_private_key`.
527
+ * ``application`` - validates against ``_APPLICATION_RE``. When omitted,
528
+ auto-detected from ``SF_PARTNER`` or imported modules (streamlit,
529
+ Jupyter, snowbooks), else ``_APPLICATION_NAME``. Forwarded to the
530
+ Rust core as ``application`` (CLIENT_ENVIRONMENT.APPLICATION).
531
+ ``client_app_id`` (CLIENT_APP_ID) stays the driver name by default
532
+ so server-side feature gating tied to the client type keeps working;
533
+ tools that re-host the driver (SnowSQL, Snow CLI) can override it via
534
+ ``internal_application_name``.
535
+ * ``internal_application_name`` / ``internal_application_version`` -
536
+ mapped to ``client_app_id`` (CLIENT_APP_ID) and ``client_app_version``
537
+ (CLIENT_APP_VERSION). When omitted and the fields are unset,
538
+ ``client_app_id`` defaults to ``_APPLICATION_NAME`` and
539
+ ``client_app_version`` defaults to the Python driver's own
540
+ ``__version__``.
541
+ * ``autocommit`` - type-checked (must be ``bool``), then merged into
542
+ ``session_parameters["AUTOCOMMIT"]``.
543
+ * ``mfa_callback`` - type-checked (must be callable).
544
+ * ``timezone`` - type-checked (must be ``str``), then merged into
545
+ ``session_parameters["TIMEZONE"]``, overwriting any TIMEZONE already
546
+ present in ``session_parameters``.
547
+ * ``interpolate_empty_sequences`` - type-checked (must be ``bool``).
548
+ * ``reuse_results`` - type-checked (must be ``bool``).
549
+ """
550
+ if config.private_key is not None: # type: ignore[attr-defined]
551
+ config.private_key = normalize_private_key(config.private_key) # type: ignore[attr-defined]
552
+
553
+ application = config.application # type: ignore[attr-defined]
554
+ if application is None and not application_explicit:
555
+ detected = cls._detect_application()
556
+ if detected:
557
+ application = detected
558
+ config.application = detected # type: ignore[attr-defined]
559
+ if application is None or (isinstance(application, str) and not application):
560
+ config.application = cls._APPLICATION_NAME # type: ignore[attr-defined]
561
+ elif isinstance(application, str):
562
+ if not cls._APPLICATION_RE.match(application):
563
+ raise ProgrammingError(f"Invalid application name: {application!r}")
564
+ else:
565
+ raise ProgrammingError(f"Invalid application parameter (must be a non-empty string): {application!r}")
566
+
567
+ if isinstance(internal_app_name, str) and internal_app_name:
568
+ config.client_app_id = internal_app_name # type: ignore[attr-defined]
569
+ elif not config.client_app_id: # type: ignore[attr-defined]
570
+ config.client_app_id = cls._APPLICATION_NAME # type: ignore[attr-defined]
571
+ if isinstance(internal_app_version, str) and internal_app_version:
572
+ config.client_app_version = internal_app_version # type: ignore[attr-defined]
573
+ elif not config.client_app_version: # type: ignore[attr-defined]
574
+ config.client_app_version = _DRIVER_VERSION # type: ignore[attr-defined]
575
+
576
+ if config.autocommit is not None:
577
+ if not isinstance(config.autocommit, bool):
578
+ raise ProgrammingError(f"Invalid autocommit parameter: {config.autocommit!r}")
579
+ if config.session_parameters is None:
580
+ config.session_parameters = {}
581
+ config.session_parameters["AUTOCOMMIT"] = str(config.autocommit).lower()
582
+
583
+ if config.mfa_callback is not None and not callable(config.mfa_callback):
584
+ raise ProgrammingError(f"Invalid mfa_callback parameter: {config.mfa_callback!r}. Pass a callable or None.")
585
+
586
+ if config.timezone is not None:
587
+ if not isinstance(config.timezone, str):
588
+ raise ProgrammingError(
589
+ f"Invalid timezone parameter: {config.timezone!r}. "
590
+ "Pass an IANA timezone name as a string "
591
+ "(for example 'America/Los_Angeles')."
592
+ )
593
+ if config.session_parameters is None:
594
+ config.session_parameters = {}
595
+ config.session_parameters["TIMEZONE"] = config.timezone
596
+
597
+ if config.interpolate_empty_sequences is not None and not isinstance(config.interpolate_empty_sequences, bool):
598
+ raise ProgrammingError(
599
+ f"Invalid interpolate_empty_sequences parameter: "
600
+ f"{config.interpolate_empty_sequences!r}. Pass True or False."
601
+ )
602
+
603
+ if config.reuse_results is not None and not isinstance(config.reuse_results, bool):
604
+ raise ProgrammingError(f"Invalid reuse_results parameter: {config.reuse_results!r}. Pass True or False.")
605
+
606
+ return config
607
+
608
+ @classmethod
609
+ def from_connection_args(
610
+ cls: type[_Self],
611
+ connection_name: str | None = None,
612
+ connections_file_path: str | os.PathLike[str] | None = None,
613
+ config: _Self | None = None,
614
+ **kwargs: Any,
615
+ ) -> _Self:
616
+ """Build a ConnectionConfig from Connection constructor arguments.
617
+
618
+ Merges ``connection_name``, ``connections_file_path``, and ``**kwargs``.
619
+ The ``config`` parameter is rejected with ``ProgrammingError`` so a
620
+ pre-built object cannot land in ``_extra``; pass it to
621
+ ``connect(config=...)``, which clones, overlays path fields, and runs
622
+ :meth:`_finalize`.
623
+
624
+ ``connections_file_path`` accepts ``str`` or any ``os.PathLike``
625
+ (``pathlib.Path``, as the old connector typed it) and is stored as
626
+ ``str``.
627
+ """
628
+ if config is not None:
629
+ raise ProgrammingError(
630
+ "from_connection_args() builds a ConnectionConfig from connection "
631
+ "arguments, not from an existing config. Pass a pre-built config to "
632
+ "connect(config=...) instead."
633
+ )
634
+
635
+ # Bare connect() = caller passed no connection options beyond
636
+ # ``connections_file_path``. This is the legacy ``is_kwargs_empty``
637
+ # condition and the sole trigger for the default-profile fallback in
638
+ # sf_core. Computed from the raw caller input below.
639
+ if connections_file_path is not None:
640
+ connections_file_path = os.fspath(connections_file_path)
641
+
642
+ if connection_name is not None:
643
+ kwargs["connection_name"] = connection_name
644
+ # Pop the ``internal_application_*`` overrides before
645
+ # ``from_kwargs`` runs: they are wrapper-internal levers that
646
+ # ultimately populate ``client_app_id`` / ``client_app_version``,
647
+ # which ``_INTERNAL_PARAMS`` forbids end users from setting
648
+ # directly.
649
+ internal_app_name = kwargs.pop("internal_application_name", None)
650
+ internal_app_version = kwargs.pop("internal_application_version", None)
651
+ # Legacy ``SnowflakeConnection.__init__`` only sniffs when
652
+ # ``"application" not in kwargs``. Keep that so ``application=None``
653
+ # still means "use PythonConnector", not "run partner detection".
654
+ application_explicit = any(k.lower() == "application" for k in kwargs)
655
+
656
+ # Capture emptiness AFTER stripping wrapper-internal levers —
657
+ # matching the old driver version ``is_kwargs_empty = not kwargs``
658
+ # (computed before any bookkeeping injection). ``connect(user="alice")``
659
+ # is NOT bare and must not silently load the default profile.
660
+ # ``connections_file_path`` is a parameter of its own in the old
661
+ # driver too, so it never reaches ``kwargs`` and never counts here:
662
+ # ``connect(connections_file_path=p)`` loads the default profile out
663
+ # of ``p``. It is folded into ``kwargs`` only after this point.
664
+ no_connection_details = connection_name is None and not kwargs
665
+ if connections_file_path is not None:
666
+ kwargs["connections_file_path"] = connections_file_path
667
+ config = cls.from_kwargs(**kwargs)
668
+
669
+ config._no_connection_details = no_connection_details
670
+ return cls._finalize(
671
+ config,
672
+ application_explicit=application_explicit,
673
+ internal_app_name=internal_app_name,
674
+ internal_app_version=internal_app_version,
675
+ )
676
+
677
+ # -- Serialisation --------------------------------------------------------
678
+ def to_options(
679
+ self,
680
+ options_modifiers: Iterable[OptionsModifier] | None = None,
681
+ ) -> dict[str, Any]:
682
+ """Return non-None parameters for the Rust core.
683
+
684
+ Excludes Python-only fields and maps Python field names back to
685
+ Rust canonical names where they differ. Includes ``_extra`` items.
686
+
687
+ Args:
688
+ options_modifiers: Optional iterable of callables applied to the
689
+ options dict, in order, after it is built. Each modifier
690
+ receives the current dict and must return a dict (it may
691
+ mutate the input in place). The return value is passed to
692
+ the next modifier. Used to re-apply wrapper-specific
693
+ legacy behaviour (e.g. ``LogoutConfig`` defaults & remap).
694
+ """
695
+ options: dict[str, Any] = {}
696
+
697
+ for field_name in self._all_field_names():
698
+ if field_name in self._PYTHON_ONLY:
699
+ continue
700
+ value = getattr(self, field_name, None)
701
+ if value is None:
702
+ continue
703
+ rust_name = self._PYTHON_TO_RUST_NAME.get(field_name, field_name)
704
+ options[rust_name] = value
705
+
706
+ options.update(self._extra)
707
+
708
+ if options_modifiers:
709
+ for modifier in options_modifiers:
710
+ options = modifier(options)
711
+ return options
712
+
713
+ def redacted_options(
714
+ self,
715
+ options_modifiers: Iterable[OptionsModifier] | None = None,
716
+ ) -> dict[str, Any]:
717
+ """Like ``to_options()`` but with sensitive values replaced by ``'***'``."""
718
+ return {k: ("***" if k in self._SENSITIVE_PARAMS else v) for k, v in self.to_options(options_modifiers).items()}
719
+
720
+ def to_proto_options(
721
+ self,
722
+ options_modifiers: Iterable[OptionsModifier] | None = None,
723
+ ) -> dict[str, ConfigSetting]:
724
+ """Convert ``to_options()`` into protobuf ``ConfigSetting`` messages.
725
+
726
+ Raises:
727
+ ProgrammingError: if any option value is not one of the protobuf-
728
+ supported scalar types (``bool``, ``int``, ``str``, ``float``,
729
+ ``bytes``). This commonly indicates that an unsupported value
730
+ slipped into ``_extra`` (e.g. a list, dict, or custom object);
731
+ surface it instead of silently dropping the entry.
732
+ """
733
+ proto: dict[str, ConfigSetting] = {}
734
+ for key, value in self.to_options(options_modifiers).items():
735
+ # PathLike objects (e.g. pathlib.Path) map to str — convert before type dispatch.
736
+ if isinstance(value, os.PathLike):
737
+ value = str(value)
738
+ # ``bool`` is a subclass of ``int``, so the bool branch has to come first.
739
+ if isinstance(value, bool):
740
+ proto[key] = ConfigSetting(bool_value=value)
741
+ elif isinstance(value, int):
742
+ proto[key] = ConfigSetting(int_value=value)
743
+ elif isinstance(value, str):
744
+ proto[key] = ConfigSetting(string_value=value)
745
+ elif isinstance(value, float):
746
+ proto[key] = ConfigSetting(double_value=value)
747
+ elif isinstance(value, bytes):
748
+ proto[key] = ConfigSetting(bytes_value=value)
749
+ else:
750
+ raise ProgrammingError(
751
+ f"Unsupported connection option type for {key!r}: "
752
+ f"{type(value).__name__}. Expected one of bool, int, "
753
+ f"str, float, or bytes."
754
+ )
755
+ return proto