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.
- snowflake/connector/__init__.py +148 -0
- snowflake/connector/_common/__init__.py +0 -0
- snowflake/connector/_common/extras.py +134 -0
- snowflake/connector/_common/telemetry.py +221 -0
- snowflake/connector/_common/wif_util.py +92 -0
- snowflake/connector/_core/sf_core_python.cp315-win_amd64.pyd +0 -0
- snowflake/connector/_core/sf_core_python.pyi +79 -0
- snowflake/connector/_internal/__init__.py +6 -0
- snowflake/connector/_internal/_private_key_helper.py +31 -0
- snowflake/connector/_internal/api_client/__init__.py +93 -0
- snowflake/connector/_internal/api_client/bridge.py +41 -0
- snowflake/connector/_internal/api_client/client_api.py +976 -0
- snowflake/connector/_internal/arrow.py +47 -0
- snowflake/connector/_internal/arrow_context.py +240 -0
- snowflake/connector/_internal/arrow_stream_async.py +266 -0
- snowflake/connector/_internal/arrow_stream_iterator.cp311-win_amd64.pyd +0 -0
- snowflake/connector/_internal/arrow_stream_iterator.cp312-win_amd64.pyd +0 -0
- snowflake/connector/_internal/arrow_stream_iterator.cp313-win_amd64.pyd +0 -0
- snowflake/connector/_internal/arrow_stream_iterator.cp314-win_amd64.pyd +0 -0
- snowflake/connector/_internal/arrow_stream_iterator.cp314t-win_amd64.pyd +0 -0
- snowflake/connector/_internal/arrow_stream_iterator.cp315-win_amd64.pyd +0 -0
- snowflake/connector/_internal/arrow_stream_iterator.pyi +42 -0
- snowflake/connector/_internal/arrow_stream_utils.py +171 -0
- snowflake/connector/_internal/authenticator_type.py +53 -0
- snowflake/connector/_internal/backward_compatibility.py +245 -0
- snowflake/connector/_internal/binding_converters.py +741 -0
- snowflake/connector/_internal/config_utils.py +71 -0
- snowflake/connector/_internal/connection/__init__.py +39 -0
- snowflake/connector/_internal/connection/connection.py +616 -0
- snowflake/connector/_internal/connection/connection_types.py +13 -0
- snowflake/connector/_internal/connection/constants.py +25 -0
- snowflake/connector/_internal/connection/decorators.py +31 -0
- snowflake/connector/_internal/connection/freezable_proxy.py +104 -0
- snowflake/connector/_internal/connection/queries.py +10 -0
- snowflake/connector/_internal/connection_config_mixin.py +755 -0
- snowflake/connector/_internal/cursor/__init__.py +25 -0
- snowflake/connector/_internal/cursor/base.py +732 -0
- snowflake/connector/_internal/cursor/cursor_types.py +13 -0
- snowflake/connector/_internal/cursor/decorators.py +56 -0
- snowflake/connector/_internal/cursor/query_result.py +202 -0
- snowflake/connector/_internal/cursor/query_result_waiter.py +98 -0
- snowflake/connector/_internal/cursor/result_metadata.py +255 -0
- snowflake/connector/_internal/cursor/result_set_wrapper.py +66 -0
- snowflake/connector/_internal/decorators.py +539 -0
- snowflake/connector/_internal/error_kinds.py +111 -0
- snowflake/connector/_internal/errorcode.py +35 -0
- snowflake/connector/_internal/errorhandler.py +278 -0
- snowflake/connector/_internal/fips.py +28 -0
- snowflake/connector/_internal/logging/__init__.py +52 -0
- snowflake/connector/_internal/logging/config.py +134 -0
- snowflake/connector/_internal/logging/core_logger.py +218 -0
- snowflake/connector/_internal/logging/native_extension_logger.py +144 -0
- snowflake/connector/_internal/logout_config_mapping.py +187 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/ArrayConverter.cpp +57 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/ArrayConverter.hpp +29 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/ArrowTableConverter.cpp +943 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/ArrowTableConverter.hpp +63 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/BinaryConverter.cpp +18 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/BinaryConverter.hpp +26 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/BooleanConverter.cpp +21 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/BooleanConverter.hpp +23 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/CArrowIterator.cpp +118 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/CArrowIterator.hpp +116 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/CArrowStreamIterator.cpp +323 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/CArrowStreamIterator.hpp +133 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/CArrowStreamTableIterator.cpp +275 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/CArrowStreamTableIterator.hpp +168 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/ConverterUtil.cpp +439 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/ConverterUtil.hpp +30 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/DateConverter.cpp +48 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/DateConverter.hpp +46 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/DecFloatConverter.cpp +76 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/DecFloatConverter.hpp +35 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/DecimalConverter.cpp +90 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/DecimalConverter.hpp +63 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/FixedSizeListConverter.cpp +62 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/FixedSizeListConverter.hpp +28 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/FloatConverter.cpp +29 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/FloatConverter.hpp +35 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/IColumnConverter.hpp +17 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/IntConverter.cpp +23 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/IntConverter.hpp +41 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/IntervalConverter.cpp +65 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/IntervalConverter.hpp +54 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/LICENSE.txt +209 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/MapConverter.cpp +79 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/MapConverter.hpp +30 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/ObjectConverter.cpp +52 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/ObjectConverter.hpp +29 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/Python/Common.cpp +8 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/Python/Common.hpp +117 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/Python/Helpers.cpp +54 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/Python/Helpers.hpp +34 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/SnowflakeType.cpp +33 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/SnowflakeType.hpp +48 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/StringConverter.cpp +18 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/StringConverter.hpp +26 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/TimeConverter.cpp +39 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/TimeConverter.hpp +31 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/TimeStampConverter.cpp +326 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/TimeStampConverter.hpp +138 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/Util/macros.hpp +14 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/Util/time.cpp +65 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/Util/time.hpp +68 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/arrow_stream_iterator.cpp +13278 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/arrow_stream_iterator.pyx +204 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_accessors.h +109 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_alloc.h +125 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_assert.h +45 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_builder.h +1914 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_emitter.h +208 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_endian.h +128 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_epilogue.h +7 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_flatbuffers.h +58 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_identifier.h +144 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_iov.h +31 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_prologue.h +8 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_refmap.h +144 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_rtconfig.h +160 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_types.h +98 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_verifier.h +243 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/flatcc_portable.h +14 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/paligned_alloc.h +207 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pattributes.h +80 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic.h +83 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic_pop.h +20 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic_push.h +51 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pendian.h +201 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pendian_detect.h +108 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pinline.h +19 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pinttypes.h +52 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/portable.h +2 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/portable_basic.h +25 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstatic_assert.h +67 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstdalign.h +164 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstdint.h +926 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/punaligned.h +182 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pversion.h +6 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc/portable/pwarnings.h +52 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/flatcc.c +3052 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/nanoarrow.c +3019 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/nanoarrow.h +3354 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/nanoarrow.hpp +344 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/nanoarrow_ipc.c +29305 -0
- snowflake/connector/_internal/nanoarrow_cpp/ArrowIterator/nanoarrow_ipc.h +381 -0
- snowflake/connector/_internal/nanoarrow_cpp/Logging/logging.cpp +108 -0
- snowflake/connector/_internal/nanoarrow_cpp/Logging/logging.hpp +47 -0
- snowflake/connector/_internal/nanoarrow_cpp/scripts/.clang-format +8 -0
- snowflake/connector/_internal/nanoarrow_cpp/scripts/format.sh +8 -0
- snowflake/connector/_internal/protobuf_gen/__init__.py +0 -0
- snowflake/connector/_internal/protobuf_gen/database_driver_v1_pb2.py +408 -0
- snowflake/connector/_internal/protobuf_gen/database_driver_v1_pb2.pyi +4487 -0
- snowflake/connector/_internal/protobuf_gen/database_driver_v1_services.py +2088 -0
- snowflake/connector/_internal/protobuf_gen/proto_exception.py +17 -0
- snowflake/connector/_internal/result_batch.py +150 -0
- snowflake/connector/_internal/snowflake_restful.py +222 -0
- snowflake/connector/_internal/snowpark_backcompat_helpers.py +31 -0
- snowflake/connector/_internal/sqlstate.py +11 -0
- snowflake/connector/_internal/statement_utils.py +207 -0
- snowflake/connector/_internal/text_utils.py +112 -0
- snowflake/connector/_internal/type_codes.py +139 -0
- snowflake/connector/_internal/utils.py +65 -0
- snowflake/connector/_internal/write_pandas_operation.py +657 -0
- snowflake/connector/aio/README.md +168 -0
- snowflake/connector/aio/__init__.py +37 -0
- snowflake/connector/aio/connection/__init__.py +17 -0
- snowflake/connector/aio/connection/_connection.py +506 -0
- snowflake/connector/aio/cursor/__init__.py +35 -0
- snowflake/connector/aio/cursor/_base.py +1034 -0
- snowflake/connector/aio/cursor/_dict_cursor.py +78 -0
- snowflake/connector/aio/cursor/_result_set_wrapper.py +35 -0
- snowflake/connector/aio/cursor/_snowflake_cursor.py +72 -0
- snowflake/connector/aio/pandas_tools.py +279 -0
- snowflake/connector/aio/result_batch.py +165 -0
- snowflake/connector/auth/__init__.py +0 -0
- snowflake/connector/auth/workload_identity.py +5 -0
- snowflake/connector/compat.py +22 -0
- snowflake/connector/config_manager.py +392 -0
- snowflake/connector/connection/__init__.py +30 -0
- snowflake/connector/connection/_connection.py +641 -0
- snowflake/connector/connection_config.py +517 -0
- snowflake/connector/constants.py +52 -0
- snowflake/connector/cursor/__init__.py +37 -0
- snowflake/connector/cursor/_base.py +1169 -0
- snowflake/connector/cursor/_chunked_download_reader.py +75 -0
- snowflake/connector/cursor/_dict_cursor.py +78 -0
- snowflake/connector/cursor/_result_set_wrapper.py +35 -0
- snowflake/connector/cursor/_snowflake_cursor.py +72 -0
- snowflake/connector/description.py +7 -0
- snowflake/connector/errors.py +492 -0
- snowflake/connector/network.py +71 -0
- snowflake/connector/pandas_tools.py +191 -0
- snowflake/connector/py.typed +0 -0
- snowflake/connector/result_batch.py +216 -0
- snowflake/connector/telemetry.py +40 -0
- snowflake/connector/time_util.py +11 -0
- snowflake/connector/types.py +134 -0
- snowflake/connector/util_text.py +3 -0
- snowflake/connector/version.py +23 -0
- snowflake_connector_python-5.2.0.dist-info/DELVEWHEEL +2 -0
- snowflake_connector_python-5.2.0.dist-info/METADATA +190 -0
- snowflake_connector_python-5.2.0.dist-info/RECORD +204 -0
- snowflake_connector_python-5.2.0.dist-info/WHEEL +4 -0
- 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
|