snowflake-connector-python 4.4.0__cp314-cp314-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 +118 -0
- snowflake/connector/_query_context_cache.py +276 -0
- snowflake/connector/_sql_util.py +48 -0
- snowflake/connector/_utils.py +353 -0
- snowflake/connector/aio/README.md +88 -0
- snowflake/connector/aio/__init__.py +163 -0
- snowflake/connector/aio/_azure_storage_client.py +227 -0
- snowflake/connector/aio/_bind_upload_agent.py +69 -0
- snowflake/connector/aio/_connection.py +1216 -0
- snowflake/connector/aio/_cursor.py +1402 -0
- snowflake/connector/aio/_description.py +5 -0
- snowflake/connector/aio/_direct_file_operation_utils.py +88 -0
- snowflake/connector/aio/_file_transfer_agent.py +316 -0
- snowflake/connector/aio/_gcs_storage_client.py +360 -0
- snowflake/connector/aio/_network.py +860 -0
- snowflake/connector/aio/_ocsp_asn1crypto.py +51 -0
- snowflake/connector/aio/_ocsp_snowflake.py +608 -0
- snowflake/connector/aio/_pandas_tools.py +561 -0
- snowflake/connector/aio/_result_batch.py +442 -0
- snowflake/connector/aio/_result_set.py +309 -0
- snowflake/connector/aio/_s3_storage_client.py +460 -0
- snowflake/connector/aio/_session_manager.py +588 -0
- snowflake/connector/aio/_storage_client.py +331 -0
- snowflake/connector/aio/_telemetry.py +97 -0
- snowflake/connector/aio/_time_util.py +57 -0
- snowflake/connector/aio/_wif_util.py +324 -0
- snowflake/connector/aio/auth/__init__.py +52 -0
- snowflake/connector/aio/auth/_auth.py +406 -0
- snowflake/connector/aio/auth/_by_plugin.py +131 -0
- snowflake/connector/aio/auth/_default.py +28 -0
- snowflake/connector/aio/auth/_idtoken.py +54 -0
- snowflake/connector/aio/auth/_keypair.py +60 -0
- snowflake/connector/aio/auth/_no_auth.py +31 -0
- snowflake/connector/aio/auth/_oauth.py +27 -0
- snowflake/connector/aio/auth/_oauth_code.py +117 -0
- snowflake/connector/aio/auth/_oauth_credentials.py +100 -0
- snowflake/connector/aio/auth/_okta.py +247 -0
- snowflake/connector/aio/auth/_pat.py +27 -0
- snowflake/connector/aio/auth/_usrpwdmfa.py +30 -0
- snowflake/connector/aio/auth/_webbrowser.py +405 -0
- snowflake/connector/aio/auth/_workload_identity.py +60 -0
- snowflake/connector/arrow_context.py +211 -0
- snowflake/connector/auth/__init__.py +53 -0
- snowflake/connector/auth/_auth.py +669 -0
- snowflake/connector/auth/_http_server.py +257 -0
- snowflake/connector/auth/_oauth_base.py +472 -0
- snowflake/connector/auth/by_plugin.py +219 -0
- snowflake/connector/auth/default.py +36 -0
- snowflake/connector/auth/idtoken.py +72 -0
- snowflake/connector/auth/keypair.py +225 -0
- snowflake/connector/auth/no_auth.py +39 -0
- snowflake/connector/auth/oauth.py +49 -0
- snowflake/connector/auth/oauth_code.py +490 -0
- snowflake/connector/auth/oauth_credentials.py +71 -0
- snowflake/connector/auth/okta.py +339 -0
- snowflake/connector/auth/pat.py +40 -0
- snowflake/connector/auth/usrpwdmfa.py +69 -0
- snowflake/connector/auth/webbrowser.py +514 -0
- snowflake/connector/auth/workload_identity.py +110 -0
- snowflake/connector/azure_storage_client.py +278 -0
- snowflake/connector/backoff_policies.py +141 -0
- snowflake/connector/bind_upload_agent.py +89 -0
- snowflake/connector/cache.py +696 -0
- snowflake/connector/compat.py +127 -0
- snowflake/connector/config_manager.py +553 -0
- snowflake/connector/connection.py +2671 -0
- snowflake/connector/connection_diagnostic.py +793 -0
- snowflake/connector/constants.py +460 -0
- snowflake/connector/converter.py +809 -0
- snowflake/connector/converter_issue23517.py +87 -0
- snowflake/connector/converter_null.py +14 -0
- snowflake/connector/converter_snowsql.py +205 -0
- snowflake/connector/crl.py +919 -0
- snowflake/connector/crl_cache.py +734 -0
- snowflake/connector/cursor.py +2099 -0
- snowflake/connector/dbapi.py +53 -0
- snowflake/connector/description.py +21 -0
- snowflake/connector/direct_file_operation_utils.py +88 -0
- snowflake/connector/encryption_util.py +220 -0
- snowflake/connector/errorcode.py +94 -0
- snowflake/connector/errors.py +666 -0
- snowflake/connector/externals_utils/__init__.py +0 -0
- snowflake/connector/externals_utils/externals_setup.py +30 -0
- snowflake/connector/feature.py +4 -0
- snowflake/connector/file_compression_type.py +118 -0
- snowflake/connector/file_lock.py +72 -0
- snowflake/connector/file_transfer_agent.py +1244 -0
- snowflake/connector/file_util.py +153 -0
- snowflake/connector/gcs_storage_client.py +476 -0
- snowflake/connector/gzip_decoder.py +85 -0
- snowflake/connector/interval_util.py +26 -0
- snowflake/connector/local_storage_client.py +90 -0
- snowflake/connector/log_configuration.py +62 -0
- snowflake/connector/logging_utils/__init__.py +0 -0
- snowflake/connector/logging_utils/filters.py +72 -0
- snowflake/connector/minicore/__init__.py +0 -0
- snowflake/connector/minicore/aix_ppc64/libsf_mini_core.a +0 -0
- snowflake/connector/minicore/aix_ppc64/libsf_mini_core_static.a +0 -0
- snowflake/connector/minicore/linux_aarch64_glibc/libsf_mini_core.so +0 -0
- snowflake/connector/minicore/linux_aarch64_musl/libsf_mini_core.so +0 -0
- snowflake/connector/minicore/linux_x86_64_glibc/libsf_mini_core.so +0 -0
- snowflake/connector/minicore/linux_x86_64_musl/libsf_mini_core.so +0 -0
- snowflake/connector/minicore/macos_aarch64/libsf_mini_core.dylib +0 -0
- snowflake/connector/minicore/macos_x86_64/libsf_mini_core.dylib +0 -0
- snowflake/connector/minicore/windows_x86_64/sf_mini_core.dll +0 -0
- snowflake/connector/minicore/windows_x86_64/sf_mini_core_static.lib +0 -0
- snowflake/connector/nanoarrow_arrow_iterator.cp314-win_amd64.pyd +0 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/ArrayConverter.cpp +60 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/ArrayConverter.hpp +29 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/BinaryConverter.cpp +19 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/BinaryConverter.hpp +26 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/BooleanConverter.cpp +21 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/BooleanConverter.hpp +23 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowChunkIterator.cpp +570 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowChunkIterator.hpp +98 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowIterator.cpp +125 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowIterator.hpp +115 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowTableIterator.cpp +1140 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowTableIterator.hpp +138 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/DateConverter.cpp +47 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/DateConverter.hpp +46 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/DecFloatConverter.cpp +83 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/DecFloatConverter.hpp +35 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/DecimalConverter.cpp +97 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/DecimalConverter.hpp +72 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/FixedSizeListConverter.cpp +73 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/FixedSizeListConverter.hpp +28 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/FloatConverter.cpp +30 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/FloatConverter.hpp +35 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/IColumnConverter.hpp +17 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/IntConverter.cpp +23 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/IntConverter.hpp +45 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/IntervalConverter.cpp +75 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/IntervalConverter.hpp +57 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/LICENSE.txt +209 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/MapConverter.cpp +75 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/MapConverter.hpp +30 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/ObjectConverter.cpp +46 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/ObjectConverter.hpp +29 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Common.cpp +8 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Common.hpp +95 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Helpers.cpp +57 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Helpers.hpp +36 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/SnowflakeType.cpp +34 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/SnowflakeType.hpp +48 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/StringConverter.cpp +19 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/StringConverter.hpp +26 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeConverter.cpp +36 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeConverter.hpp +31 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeStampConverter.cpp +346 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeStampConverter.hpp +145 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/Util/macros.hpp +14 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/Util/time.cpp +67 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/Util/time.hpp +68 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_accessors.h +101 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_alloc.h +127 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_assert.h +45 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_builder.h +1908 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_emitter.h +215 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_endian.h +125 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_epilogue.h +7 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_flatbuffers.h +55 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_identifier.h +148 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_iov.h +31 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_prologue.h +8 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_refmap.h +144 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_rtconfig.h +162 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_types.h +97 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_verifier.h +239 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/flatcc_portable.h +14 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/paligned_alloc.h +210 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pattributes.h +84 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic.h +84 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic_pop.h +20 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic_push.h +51 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pendian.h +206 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pendian_detect.h +118 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pinline.h +19 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pinttypes.h +52 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/portable.h +2 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/portable_basic.h +25 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstatic_assert.h +67 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstdalign.h +162 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstdint.h +898 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/punaligned.h +190 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pversion.h +6 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pwarnings.h +52 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc.c +3204 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow.c +3217 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow.h +3618 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow.hpp +379 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_arrow_iterator.pyx +262 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_device.c +512 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_device.h +350 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_ipc.c +33273 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_ipc.h +397 -0
- snowflake/connector/nanoarrow_cpp/Logging/logging.cpp +100 -0
- snowflake/connector/nanoarrow_cpp/Logging/logging.hpp +49 -0
- snowflake/connector/network.py +1227 -0
- snowflake/connector/ocsp_asn1crypto.py +447 -0
- snowflake/connector/ocsp_snowflake.py +1939 -0
- snowflake/connector/options.py +179 -0
- snowflake/connector/os_details.py +88 -0
- snowflake/connector/pandas_tools.py +734 -0
- snowflake/connector/platform_detection.py +555 -0
- snowflake/connector/proxy.py +28 -0
- snowflake/connector/py.typed +0 -0
- snowflake/connector/result_batch.py +917 -0
- snowflake/connector/result_set.py +340 -0
- snowflake/connector/s3_storage_client.py +614 -0
- snowflake/connector/secret_detector.py +181 -0
- snowflake/connector/session_manager.py +650 -0
- snowflake/connector/sf_dirs.py +64 -0
- snowflake/connector/sfbinaryformat.py +35 -0
- snowflake/connector/sfdatetime.py +345 -0
- snowflake/connector/snow_logging.py +144 -0
- snowflake/connector/sqlstate.py +9 -0
- snowflake/connector/ssd_internal_keys.py +30 -0
- snowflake/connector/ssl_wrap_socket.py +281 -0
- snowflake/connector/storage_client.py +486 -0
- snowflake/connector/telemetry.py +254 -0
- snowflake/connector/telemetry_oob.py +544 -0
- snowflake/connector/test_util.py +30 -0
- snowflake/connector/time_util.py +159 -0
- snowflake/connector/token_cache.py +457 -0
- snowflake/connector/tool/__init__.py +0 -0
- snowflake/connector/tool/dump_certs.py +57 -0
- snowflake/connector/tool/dump_ocsp_response.py +139 -0
- snowflake/connector/tool/dump_ocsp_response_cache.py +194 -0
- snowflake/connector/tool/probe_connection.py +69 -0
- snowflake/connector/url_util.py +55 -0
- snowflake/connector/util_text.py +320 -0
- snowflake/connector/vendored/__init__.py +3 -0
- snowflake/connector/vendored/requests/LICENSE +175 -0
- snowflake/connector/vendored/requests/__init__.py +184 -0
- snowflake/connector/vendored/requests/__version__.py +14 -0
- snowflake/connector/vendored/requests/_internal_utils.py +50 -0
- snowflake/connector/vendored/requests/adapters.py +696 -0
- snowflake/connector/vendored/requests/api.py +157 -0
- snowflake/connector/vendored/requests/auth.py +314 -0
- snowflake/connector/vendored/requests/certs.py +17 -0
- snowflake/connector/vendored/requests/compat.py +106 -0
- snowflake/connector/vendored/requests/cookies.py +561 -0
- snowflake/connector/vendored/requests/exceptions.py +151 -0
- snowflake/connector/vendored/requests/help.py +134 -0
- snowflake/connector/vendored/requests/hooks.py +33 -0
- snowflake/connector/vendored/requests/models.py +1039 -0
- snowflake/connector/vendored/requests/packages.py +23 -0
- snowflake/connector/vendored/requests/sessions.py +831 -0
- snowflake/connector/vendored/requests/status_codes.py +128 -0
- snowflake/connector/vendored/requests/structures.py +99 -0
- snowflake/connector/vendored/requests/utils.py +1086 -0
- snowflake/connector/vendored/urllib3/LICENSE.txt +21 -0
- snowflake/connector/vendored/urllib3/__init__.py +211 -0
- snowflake/connector/vendored/urllib3/_base_connection.py +165 -0
- snowflake/connector/vendored/urllib3/_collections.py +487 -0
- snowflake/connector/vendored/urllib3/_request_methods.py +278 -0
- snowflake/connector/vendored/urllib3/_version.py +34 -0
- snowflake/connector/vendored/urllib3/connection.py +1108 -0
- snowflake/connector/vendored/urllib3/connectionpool.py +1178 -0
- snowflake/connector/vendored/urllib3/contrib/__init__.py +0 -0
- snowflake/connector/vendored/urllib3/contrib/emscripten/__init__.py +17 -0
- snowflake/connector/vendored/urllib3/contrib/emscripten/connection.py +260 -0
- snowflake/connector/vendored/urllib3/contrib/emscripten/emscripten_fetch_worker.js +110 -0
- snowflake/connector/vendored/urllib3/contrib/emscripten/fetch.py +726 -0
- snowflake/connector/vendored/urllib3/contrib/emscripten/request.py +22 -0
- snowflake/connector/vendored/urllib3/contrib/emscripten/response.py +277 -0
- snowflake/connector/vendored/urllib3/contrib/pyopenssl.py +564 -0
- snowflake/connector/vendored/urllib3/contrib/socks.py +228 -0
- snowflake/connector/vendored/urllib3/exceptions.py +335 -0
- snowflake/connector/vendored/urllib3/fields.py +341 -0
- snowflake/connector/vendored/urllib3/filepost.py +89 -0
- snowflake/connector/vendored/urllib3/http2/__init__.py +53 -0
- snowflake/connector/vendored/urllib3/http2/connection.py +356 -0
- snowflake/connector/vendored/urllib3/http2/probe.py +87 -0
- snowflake/connector/vendored/urllib3/poolmanager.py +651 -0
- snowflake/connector/vendored/urllib3/py.typed +2 -0
- snowflake/connector/vendored/urllib3/response.py +1480 -0
- snowflake/connector/vendored/urllib3/util/__init__.py +42 -0
- snowflake/connector/vendored/urllib3/util/connection.py +137 -0
- snowflake/connector/vendored/urllib3/util/proxy.py +43 -0
- snowflake/connector/vendored/urllib3/util/request.py +263 -0
- snowflake/connector/vendored/urllib3/util/response.py +101 -0
- snowflake/connector/vendored/urllib3/util/retry.py +549 -0
- snowflake/connector/vendored/urllib3/util/ssl_.py +527 -0
- snowflake/connector/vendored/urllib3/util/ssl_match_hostname.py +159 -0
- snowflake/connector/vendored/urllib3/util/ssltransport.py +271 -0
- snowflake/connector/vendored/urllib3/util/timeout.py +275 -0
- snowflake/connector/vendored/urllib3/util/url.py +469 -0
- snowflake/connector/vendored/urllib3/util/util.py +42 -0
- snowflake/connector/vendored/urllib3/util/wait.py +124 -0
- snowflake/connector/version.py +3 -0
- snowflake/connector/wif_util.py +434 -0
- snowflake_connector_python-4.4.0.dist-info/DELVEWHEEL +2 -0
- snowflake_connector_python-4.4.0.dist-info/METADATA +1591 -0
- snowflake_connector_python-4.4.0.dist-info/RECORD +302 -0
- snowflake_connector_python-4.4.0.dist-info/WHEEL +5 -0
- snowflake_connector_python-4.4.0.dist-info/entry_points.txt +4 -0
- snowflake_connector_python-4.4.0.dist-info/licenses/LICENSE.txt +202 -0
- snowflake_connector_python-4.4.0.dist-info/licenses/NOTICE +8 -0
- snowflake_connector_python-4.4.0.dist-info/top_level.txt +1 -0
- snowflake_connector_python.libs/msvcp140-0f885b509a685d2bbfa652fed26b5fb3.dll +0 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import collections.abc
|
|
5
|
+
import decimal
|
|
6
|
+
import html
|
|
7
|
+
import http.client
|
|
8
|
+
import os
|
|
9
|
+
import platform
|
|
10
|
+
import queue
|
|
11
|
+
import urllib.parse
|
|
12
|
+
import urllib.request
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from . import constants
|
|
16
|
+
|
|
17
|
+
IS_LINUX = platform.system() == "Linux"
|
|
18
|
+
IS_WINDOWS = platform.system() == "Windows"
|
|
19
|
+
IS_MACOS = platform.system() == "Darwin"
|
|
20
|
+
|
|
21
|
+
NUM_DATA_TYPES: tuple[type, ...] = ()
|
|
22
|
+
try:
|
|
23
|
+
import numpy
|
|
24
|
+
|
|
25
|
+
NUM_DATA_TYPES = (
|
|
26
|
+
numpy.int8,
|
|
27
|
+
numpy.int16,
|
|
28
|
+
numpy.int32,
|
|
29
|
+
numpy.int64,
|
|
30
|
+
numpy.float16,
|
|
31
|
+
numpy.float32,
|
|
32
|
+
numpy.float64,
|
|
33
|
+
numpy.uint8,
|
|
34
|
+
numpy.uint16,
|
|
35
|
+
numpy.uint32,
|
|
36
|
+
numpy.uint64,
|
|
37
|
+
numpy.bool_,
|
|
38
|
+
)
|
|
39
|
+
except (ImportError, AttributeError):
|
|
40
|
+
numpy = None
|
|
41
|
+
|
|
42
|
+
GET_CWD = os.getcwd
|
|
43
|
+
BASE_EXCEPTION_CLASS = Exception
|
|
44
|
+
TO_UNICODE = str
|
|
45
|
+
ITERATOR = collections.abc.Iterator
|
|
46
|
+
MAPPING = collections.abc.Mapping
|
|
47
|
+
|
|
48
|
+
urlsplit = urllib.parse.urlsplit
|
|
49
|
+
urlunsplit = urllib.parse.urlunsplit
|
|
50
|
+
parse_qs = urllib.parse.parse_qs
|
|
51
|
+
urlparse = urllib.parse.urlparse
|
|
52
|
+
urlunparse = urllib.parse.urlunparse
|
|
53
|
+
|
|
54
|
+
NUM_DATA_TYPES += (int, float, decimal.Decimal)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def PKCS5_UNPAD(v: bytes) -> bytes:
|
|
58
|
+
return v[0 : -v[-1]]
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def PKCS5_OFFSET(v: bytes) -> int:
|
|
62
|
+
return v[-1]
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def IS_BINARY(v: bytearray | bytes | str) -> bool:
|
|
66
|
+
return isinstance(v, (bytes, bytearray))
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
METHOD_NOT_ALLOWED = http.client.METHOD_NOT_ALLOWED
|
|
70
|
+
BAD_GATEWAY = http.client.BAD_GATEWAY
|
|
71
|
+
BAD_REQUEST = http.client.BAD_REQUEST
|
|
72
|
+
REQUEST_TIMEOUT = http.client.REQUEST_TIMEOUT
|
|
73
|
+
TOO_MANY_REQUESTS = http.client.TOO_MANY_REQUESTS
|
|
74
|
+
SERVICE_UNAVAILABLE = http.client.SERVICE_UNAVAILABLE
|
|
75
|
+
GATEWAY_TIMEOUT = http.client.GATEWAY_TIMEOUT
|
|
76
|
+
FORBIDDEN = http.client.FORBIDDEN
|
|
77
|
+
UNAUTHORIZED = http.client.UNAUTHORIZED
|
|
78
|
+
INTERNAL_SERVER_ERROR = http.client.INTERNAL_SERVER_ERROR
|
|
79
|
+
IncompleteRead = http.client.IncompleteRead
|
|
80
|
+
OK = http.client.OK
|
|
81
|
+
BadStatusLine = http.client.BadStatusLine
|
|
82
|
+
|
|
83
|
+
urlencode = urllib.parse.urlencode
|
|
84
|
+
unquote = urllib.parse.unquote
|
|
85
|
+
quote = urllib.parse.quote
|
|
86
|
+
unescape = html.unescape
|
|
87
|
+
|
|
88
|
+
EmptyQueue = queue.Empty
|
|
89
|
+
Queue = queue.Queue
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def IS_BYTES(v: Any) -> bool:
|
|
93
|
+
return isinstance(v, bytes)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def IS_UNICODE(v: Any) -> bool:
|
|
97
|
+
return isinstance(v, str)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def IS_NUMERIC(v: Any) -> bool:
|
|
101
|
+
return isinstance(v, NUM_DATA_TYPES)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
IS_STR = IS_UNICODE
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def PKCS5_PAD(value: bytes, block_size: int) -> bytes:
|
|
108
|
+
return b"".join(
|
|
109
|
+
[
|
|
110
|
+
value,
|
|
111
|
+
(block_size - len(value) % block_size)
|
|
112
|
+
* chr(block_size - len(value) % block_size).encode(constants.UTF8),
|
|
113
|
+
]
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def PRINT(msg: str) -> None:
|
|
118
|
+
print(msg)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def INPUT(prompt: str) -> str:
|
|
122
|
+
return input(prompt)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def quote_url_piece(piece: str) -> str:
|
|
126
|
+
"""Helper function to urlencode a string and turn it into bytes."""
|
|
127
|
+
return quote(piece)
|
|
@@ -0,0 +1,553 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import itertools
|
|
4
|
+
import logging
|
|
5
|
+
import os
|
|
6
|
+
import stat
|
|
7
|
+
import warnings
|
|
8
|
+
from collections.abc import Iterable
|
|
9
|
+
from operator import methodcaller
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any, Callable, Literal, NamedTuple, TypeVar
|
|
12
|
+
from warnings import warn
|
|
13
|
+
|
|
14
|
+
import tomlkit
|
|
15
|
+
from tomlkit.items import Table
|
|
16
|
+
|
|
17
|
+
from snowflake.connector.compat import IS_WINDOWS
|
|
18
|
+
from snowflake.connector.constants import CONFIG_FILE, CONNECTIONS_FILE
|
|
19
|
+
from snowflake.connector.errors import (
|
|
20
|
+
ConfigManagerError,
|
|
21
|
+
ConfigSourceError,
|
|
22
|
+
Error,
|
|
23
|
+
MissingConfigOptionError,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
_T = TypeVar("_T")
|
|
27
|
+
|
|
28
|
+
LOGGER = logging.getLogger(__name__)
|
|
29
|
+
READABLE_BY_OTHERS = stat.S_IRGRP | stat.S_IROTH
|
|
30
|
+
WRITABLE_BY_OTHERS = stat.S_IWGRP | stat.S_IWOTH
|
|
31
|
+
|
|
32
|
+
DEPRECATED_SKIP_WARNING_ENV_VAR = "SF_SKIP_WARNING_FOR_READ_PERMISSIONS_ON_CONFIG_FILE"
|
|
33
|
+
SKIP_WARNING_ENV_VAR = "SF_SKIP_TOKEN_FILE_PERMISSIONS_VERIFICATION"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _should_skip_warning_for_read_permissions_on_config_file() -> bool:
|
|
37
|
+
"""Check if the warning should be skipped based on environment variable."""
|
|
38
|
+
if SKIP_WARNING_ENV_VAR in os.environ:
|
|
39
|
+
return os.getenv(SKIP_WARNING_ENV_VAR, "false").lower() == "true"
|
|
40
|
+
# Else fallback to old value
|
|
41
|
+
if DEPRECATED_SKIP_WARNING_ENV_VAR in os.environ:
|
|
42
|
+
warn(
|
|
43
|
+
f"{DEPRECATED_SKIP_WARNING_ENV_VAR} is deprecated. Please use {SKIP_WARNING_ENV_VAR} instead."
|
|
44
|
+
)
|
|
45
|
+
return os.getenv(DEPRECATED_SKIP_WARNING_ENV_VAR, "false").lower() == "true"
|
|
46
|
+
return False
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ConfigSliceOptions(NamedTuple):
|
|
50
|
+
"""Class that defines settings individual configuration files."""
|
|
51
|
+
|
|
52
|
+
check_permissions: bool = True
|
|
53
|
+
only_in_slice: bool = False
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class ConfigSlice(NamedTuple):
|
|
57
|
+
path: Path
|
|
58
|
+
options: ConfigSliceOptions
|
|
59
|
+
section: str
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class ConfigOption:
|
|
63
|
+
"""ConfigOption represents a flag/setting.
|
|
64
|
+
|
|
65
|
+
This class knows how to read the value out of all different sources and implements
|
|
66
|
+
order of precedence between them.
|
|
67
|
+
|
|
68
|
+
It also provides value parsing and verification.
|
|
69
|
+
|
|
70
|
+
Attributes:
|
|
71
|
+
name: Name of this ConfigOption.
|
|
72
|
+
parse_str: A function that can turn str to the desired type, useful
|
|
73
|
+
for reading value from environmental variable.
|
|
74
|
+
choices: An iterable of all possible values that are allowed for
|
|
75
|
+
this option.
|
|
76
|
+
env_name: Environmental variable value should be read from, if not
|
|
77
|
+
supplied, we'll construct this. False disables reading from
|
|
78
|
+
environmental variables, None uses the auto generated variable name
|
|
79
|
+
and explicitly provided string overwrites the default one.
|
|
80
|
+
default: The value we should resolve to when the option is not defined
|
|
81
|
+
in any of the sources. When it's None we treat that as there's no
|
|
82
|
+
default value.
|
|
83
|
+
_root_manager: Reference to the root manager. Used to efficiently
|
|
84
|
+
refer to cached config file. Is supplied by the parent
|
|
85
|
+
ConfigManager.
|
|
86
|
+
_nest_path: The names of the ConfigManagers that this option is
|
|
87
|
+
nested in. Used to be able to efficiently resolve where to retrieve
|
|
88
|
+
value out of the configuration file and construct environment
|
|
89
|
+
variable name. This is supplied by the parent ConfigManager.
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
def __init__(
|
|
93
|
+
self,
|
|
94
|
+
*,
|
|
95
|
+
name: str,
|
|
96
|
+
parse_str: Callable[[str], _T] | None = None,
|
|
97
|
+
choices: Iterable[Any] | None = None,
|
|
98
|
+
env_name: str | None | Literal[False] = None,
|
|
99
|
+
default: Any | None = None,
|
|
100
|
+
_root_manager: ConfigManager | None = None,
|
|
101
|
+
_nest_path: list[str] | None,
|
|
102
|
+
) -> None:
|
|
103
|
+
"""Create a config option that can read values from different sources.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
name: Name to assign to this ConfigOption.
|
|
107
|
+
parse_str: String parser function for this instance.
|
|
108
|
+
choices: List of possible values for this instance.
|
|
109
|
+
env_name: Environmental variable name value should be read from.
|
|
110
|
+
Providing a string will use that environment variable, False disables
|
|
111
|
+
reading value from environmental variables and the default None generates
|
|
112
|
+
an environmental variable name for it using the _nest_path and name.
|
|
113
|
+
default: Default value for the option. Used in case the value is
|
|
114
|
+
is not defined in any of the sources.
|
|
115
|
+
_root_manager: Reference to the root manager. Should be supplied by
|
|
116
|
+
the parent ConfigManager.
|
|
117
|
+
_nest_path: The names of the ConfigManagers that this option is
|
|
118
|
+
nested in. This is supplied by the parent ConfigManager.
|
|
119
|
+
"""
|
|
120
|
+
if _root_manager is None:
|
|
121
|
+
raise TypeError("_root_manager cannot be None")
|
|
122
|
+
if _nest_path is None:
|
|
123
|
+
raise TypeError("_nest_path cannot be None")
|
|
124
|
+
self.name = name
|
|
125
|
+
self.parse_str = parse_str
|
|
126
|
+
self.choices = choices
|
|
127
|
+
self._nest_path = _nest_path + [name]
|
|
128
|
+
self._root_manager: ConfigManager = _root_manager
|
|
129
|
+
self.env_name = env_name
|
|
130
|
+
self.default = default
|
|
131
|
+
|
|
132
|
+
def value(self) -> Any:
|
|
133
|
+
"""Retrieve a value of option.
|
|
134
|
+
|
|
135
|
+
This function implements order of precedence between different sources.
|
|
136
|
+
"""
|
|
137
|
+
source = "environment variable"
|
|
138
|
+
loaded_env, value = self._get_env()
|
|
139
|
+
if not loaded_env:
|
|
140
|
+
try:
|
|
141
|
+
value = self._get_config()
|
|
142
|
+
source = "configuration file"
|
|
143
|
+
except MissingConfigOptionError:
|
|
144
|
+
if self.default is not None:
|
|
145
|
+
source = "default_value"
|
|
146
|
+
value = self.default
|
|
147
|
+
else:
|
|
148
|
+
raise
|
|
149
|
+
if self.choices and value not in self.choices:
|
|
150
|
+
raise ConfigSourceError(
|
|
151
|
+
f"The value of {self.option_name} read from "
|
|
152
|
+
f"{source} is not part of {self.choices}"
|
|
153
|
+
)
|
|
154
|
+
return value
|
|
155
|
+
|
|
156
|
+
@property
|
|
157
|
+
def option_name(self) -> str:
|
|
158
|
+
"""User-friendly name of the config option. Includes self._nest_path."""
|
|
159
|
+
return ".".join(self._nest_path[1:])
|
|
160
|
+
|
|
161
|
+
@property
|
|
162
|
+
def default_env_name(self) -> str:
|
|
163
|
+
"""The default environmental variable name for this option."""
|
|
164
|
+
pieces = map(methodcaller("upper"), self._nest_path[1:])
|
|
165
|
+
return f"SNOWFLAKE_{'_'.join(pieces)}"
|
|
166
|
+
|
|
167
|
+
def _get_env(self) -> tuple[bool, str | _T | None]:
|
|
168
|
+
"""Get value from environment variable if possible.
|
|
169
|
+
|
|
170
|
+
Returns whether it was able to load the data and the loaded value
|
|
171
|
+
itself.
|
|
172
|
+
"""
|
|
173
|
+
if self.env_name is False:
|
|
174
|
+
return False, None
|
|
175
|
+
if self.env_name is not None:
|
|
176
|
+
env_name = self.env_name
|
|
177
|
+
else:
|
|
178
|
+
# Generate environment name if it was not explicitly supplied,
|
|
179
|
+
# and isn't disabled
|
|
180
|
+
env_name = self.default_env_name
|
|
181
|
+
env_var = os.environ.get(env_name)
|
|
182
|
+
if env_var is None:
|
|
183
|
+
return False, None
|
|
184
|
+
loaded_var: str | _T = env_var
|
|
185
|
+
if self.parse_str is not None:
|
|
186
|
+
loaded_var = self.parse_str(env_var)
|
|
187
|
+
if isinstance(loaded_var, (Table, tomlkit.TOMLDocument)):
|
|
188
|
+
# If we got a TOML table we probably want it in dictionary form
|
|
189
|
+
return True, loaded_var.value
|
|
190
|
+
return True, loaded_var
|
|
191
|
+
|
|
192
|
+
def _get_config(self) -> Any:
|
|
193
|
+
"""Get value from the cached config file if possible.
|
|
194
|
+
|
|
195
|
+
Since this is the last resource for retrieving the value it raises
|
|
196
|
+
a MissingConfigOptionError if it's unable to find this option.
|
|
197
|
+
"""
|
|
198
|
+
if (
|
|
199
|
+
self._root_manager.conf_file_cache is None
|
|
200
|
+
and self._root_manager.file_path is not None
|
|
201
|
+
):
|
|
202
|
+
self._root_manager.read_config()
|
|
203
|
+
e = self._root_manager.conf_file_cache
|
|
204
|
+
if e is None:
|
|
205
|
+
raise ConfigManagerError(
|
|
206
|
+
f"Root manager '{self._root_manager.name}' is missing file_path",
|
|
207
|
+
)
|
|
208
|
+
for k in self._nest_path[1:]:
|
|
209
|
+
try:
|
|
210
|
+
e = e[k]
|
|
211
|
+
except tomlkit.exceptions.NonExistentKey:
|
|
212
|
+
raise MissingConfigOptionError( # TOOO: maybe a child Exception for missing option?
|
|
213
|
+
f"Configuration option '{self.option_name}' is not defined anywhere, "
|
|
214
|
+
"have you forgotten to set it in a configuration file, "
|
|
215
|
+
"or environmental variable?"
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
if isinstance(e, (Table, tomlkit.TOMLDocument)):
|
|
219
|
+
# If we got a TOML table we probably want it in dictionary form
|
|
220
|
+
return e.value
|
|
221
|
+
return e
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
class ConfigManager:
|
|
225
|
+
"""Read a TOML configuration file with managed multi-source precedence.
|
|
226
|
+
|
|
227
|
+
Note that multi-source precedence is actually implemented by ConfigOption.
|
|
228
|
+
This is done to make sure that special handling can be done for special options.
|
|
229
|
+
As an example, think of not allowing to provide passwords by command line arguments.
|
|
230
|
+
|
|
231
|
+
This class is updatable at run-time, allowing other libraries to add their
|
|
232
|
+
own configuration options and sub-managers before resolution.
|
|
233
|
+
|
|
234
|
+
This class can simply be thought of as nestable containers for ConfigOptions.
|
|
235
|
+
It holds extra information necessary for efficient nesting purposes.
|
|
236
|
+
|
|
237
|
+
Sub-managers allow option groups to exist, e.g. the group "snowflake.cli.output"
|
|
238
|
+
could have 2 options in it: debug (boolean flag) and format (a string like "json",
|
|
239
|
+
or "csv").
|
|
240
|
+
|
|
241
|
+
When a ConfigManager tries to retrieve ConfigOptions' value the _root_manager
|
|
242
|
+
will read and cache the TOML file from the file it's pointing at, afterwards
|
|
243
|
+
updating the read cache can be forced by calling read_config.
|
|
244
|
+
|
|
245
|
+
Attributes:
|
|
246
|
+
name: The name of the ConfigManager. Used for nesting and emitting
|
|
247
|
+
useful error messages.
|
|
248
|
+
file_path: Path to the file where this and all child ConfigManagers
|
|
249
|
+
should read their values out of. Can be omitted for all child
|
|
250
|
+
managers. Root manager could also miss this value, but this will
|
|
251
|
+
result in an exception when a value is read that isn't available from
|
|
252
|
+
a preceding config source.
|
|
253
|
+
conf_file_cache: Cache to store what we read from the TOML file.
|
|
254
|
+
_sub_managers: List of ConfigManagers that are nested under the current manager.
|
|
255
|
+
_sub_parsers: Alias for the old name of _sub_managers in the first release, please use
|
|
256
|
+
the new name now, as this might get deprecated in the future.
|
|
257
|
+
_options: List of ConfigOptions that are under the current manager.
|
|
258
|
+
_root_manager: Reference to the root manager. Used to efficiently propagate to
|
|
259
|
+
child options.
|
|
260
|
+
_nest_path: The names of the ConfigManagers that this manager is nested
|
|
261
|
+
under. Used to efficiently propagate to child options.
|
|
262
|
+
_slices: List of config slices, where optional sections could be read from.
|
|
263
|
+
Note that this feature might become deprecated soon.
|
|
264
|
+
"""
|
|
265
|
+
|
|
266
|
+
def __init__(
|
|
267
|
+
self,
|
|
268
|
+
*,
|
|
269
|
+
name: str,
|
|
270
|
+
file_path: Path | None = None,
|
|
271
|
+
_slices: list[ConfigSlice] | None = None,
|
|
272
|
+
):
|
|
273
|
+
"""Creates a new ConfigManager.
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
name: Name of this ConfigManager.
|
|
277
|
+
file_path: File this manager should read values from. Can be omitted
|
|
278
|
+
for all child managers.
|
|
279
|
+
_slices: List of ConfigSlices to consider. A configuration file's slice is a
|
|
280
|
+
section that can optionally reside in a different file. Note that this
|
|
281
|
+
feature might get deprecated soon.
|
|
282
|
+
"""
|
|
283
|
+
if _slices is None:
|
|
284
|
+
_slices = list()
|
|
285
|
+
self.name = name
|
|
286
|
+
self.file_path = file_path
|
|
287
|
+
self._slices = _slices
|
|
288
|
+
# Objects holding sub-managers and options
|
|
289
|
+
self._options: dict[str, ConfigOption] = dict()
|
|
290
|
+
self._sub_managers: dict[str, ConfigManager] = dict()
|
|
291
|
+
# Dictionary to cache read in config file
|
|
292
|
+
self.conf_file_cache: tomlkit.TOMLDocument | None = None
|
|
293
|
+
# Information necessary to be able to nest elements
|
|
294
|
+
# and add options in O(1)
|
|
295
|
+
self._root_manager: ConfigManager = self
|
|
296
|
+
self._nest_path = [name]
|
|
297
|
+
|
|
298
|
+
@property
|
|
299
|
+
def _sub_parsers(self) -> dict[str, ConfigManager]:
|
|
300
|
+
"""
|
|
301
|
+
Alias for the old name of ``_sub_managers``.
|
|
302
|
+
|
|
303
|
+
This used to be the original name in the first release, please use the
|
|
304
|
+
new name, as this might get deprecated in the future.
|
|
305
|
+
"""
|
|
306
|
+
warnings.warn(
|
|
307
|
+
"_sub_parsers has been deprecated, use _sub_managers instead",
|
|
308
|
+
DeprecationWarning,
|
|
309
|
+
stacklevel=2,
|
|
310
|
+
)
|
|
311
|
+
return self._sub_managers
|
|
312
|
+
|
|
313
|
+
def read_config(
|
|
314
|
+
self,
|
|
315
|
+
skip_file_permissions_check: bool = False,
|
|
316
|
+
) -> None:
|
|
317
|
+
"""Read and cache config file contents.
|
|
318
|
+
|
|
319
|
+
This function should be explicitly called if the ConfigManager's cache is
|
|
320
|
+
outdated. Most likely when someone's doing development and are interactively
|
|
321
|
+
adding new options to their configuration file.
|
|
322
|
+
"""
|
|
323
|
+
if self.file_path is None:
|
|
324
|
+
raise ConfigManagerError(
|
|
325
|
+
"ConfigManager is trying to read config file, but it doesn't "
|
|
326
|
+
"have one"
|
|
327
|
+
)
|
|
328
|
+
read_config_file = tomlkit.TOMLDocument()
|
|
329
|
+
|
|
330
|
+
# Read in all of the config slices
|
|
331
|
+
config_slice_options = ConfigSliceOptions(
|
|
332
|
+
check_permissions=not skip_file_permissions_check
|
|
333
|
+
)
|
|
334
|
+
for filep, sliceoptions, section in itertools.chain(
|
|
335
|
+
((self.file_path, config_slice_options, None),),
|
|
336
|
+
(
|
|
337
|
+
(
|
|
338
|
+
s.path,
|
|
339
|
+
(
|
|
340
|
+
s.options._replace(
|
|
341
|
+
check_permissions=not skip_file_permissions_check
|
|
342
|
+
)
|
|
343
|
+
if skip_file_permissions_check
|
|
344
|
+
else s.options
|
|
345
|
+
),
|
|
346
|
+
s.section,
|
|
347
|
+
)
|
|
348
|
+
for s in self._slices
|
|
349
|
+
),
|
|
350
|
+
):
|
|
351
|
+
if sliceoptions.only_in_slice:
|
|
352
|
+
del read_config_file[section]
|
|
353
|
+
try:
|
|
354
|
+
if not filep.exists():
|
|
355
|
+
# Python 3.14+ (cpython#118243): Path.exists() suppresses
|
|
356
|
+
# PermissionError and returns False instead of raising, so
|
|
357
|
+
# we explicitly check parent directory access.
|
|
358
|
+
if not os.access(filep.parent, os.R_OK | os.X_OK):
|
|
359
|
+
LOGGER.debug(
|
|
360
|
+
f"Fail to read configuration file from {str(filep)} due to no permission on its parent directory"
|
|
361
|
+
)
|
|
362
|
+
continue
|
|
363
|
+
except PermissionError:
|
|
364
|
+
# Python < 3.14: Path.exists() raises PermissionError when
|
|
365
|
+
# the parent directory is not accessible.
|
|
366
|
+
LOGGER.debug(
|
|
367
|
+
f"Fail to read configuration file from {str(filep)} due to no permission on its parent directory"
|
|
368
|
+
)
|
|
369
|
+
continue
|
|
370
|
+
|
|
371
|
+
# Check for writable by others - this should raise an error
|
|
372
|
+
if (
|
|
373
|
+
not IS_WINDOWS # Skip checking on Windows
|
|
374
|
+
and sliceoptions.check_permissions # Skip checking if this file couldn't hold sensitive information
|
|
375
|
+
and filep.stat().st_mode & WRITABLE_BY_OTHERS != 0
|
|
376
|
+
):
|
|
377
|
+
file_stat = filep.stat()
|
|
378
|
+
file_permissions = oct(file_stat.st_mode)[-3:]
|
|
379
|
+
raise ConfigSourceError(
|
|
380
|
+
f"file '{str(filep)}' is writable by group or others — this poses a security risk because it allows unauthorized users to modify sensitive settings. Your Permission: {file_permissions}"
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
# Check for readable by others or wrong ownership - this should warn
|
|
384
|
+
if (
|
|
385
|
+
not IS_WINDOWS # Skip checking on Windows
|
|
386
|
+
and sliceoptions.check_permissions # Skip checking if this file couldn't hold sensitive information
|
|
387
|
+
# Same check as openssh does for permissions
|
|
388
|
+
# https://github.com/openssh/openssh-portable/blob/2709809fd616a0991dc18e3a58dea10fb383c3f0/readconf.c#LL2264C1-L2264C1
|
|
389
|
+
and filep.stat().st_mode & READABLE_BY_OTHERS != 0
|
|
390
|
+
or (
|
|
391
|
+
# Windows doesn't have getuid, skip checking
|
|
392
|
+
hasattr(os, "getuid")
|
|
393
|
+
and filep.stat().st_uid != 0
|
|
394
|
+
and filep.stat().st_uid != os.getuid()
|
|
395
|
+
)
|
|
396
|
+
):
|
|
397
|
+
chmod_message = f'.\n * To change owner, run `chown $USER "{str(filep)}"`.\n * To restrict permissions, run `chmod 0600 "{str(filep)}"`.\n * To skip this warning, set environment variable {SKIP_WARNING_ENV_VAR}=true.\n'
|
|
398
|
+
|
|
399
|
+
if not _should_skip_warning_for_read_permissions_on_config_file():
|
|
400
|
+
warn(f"Bad owner or permissions on {str(filep)}{chmod_message}")
|
|
401
|
+
LOGGER.debug(f"reading configuration file from {str(filep)}")
|
|
402
|
+
try:
|
|
403
|
+
read_config_piece = tomlkit.parse(filep.read_text())
|
|
404
|
+
except Exception as e:
|
|
405
|
+
raise ConfigSourceError(
|
|
406
|
+
"An unknown error happened while loading " f"'{str(filep)}'"
|
|
407
|
+
) from e
|
|
408
|
+
if section is None:
|
|
409
|
+
read_config_file = read_config_piece
|
|
410
|
+
else:
|
|
411
|
+
read_config_file[section] = read_config_piece
|
|
412
|
+
self.conf_file_cache = read_config_file
|
|
413
|
+
|
|
414
|
+
def add_option(
|
|
415
|
+
self,
|
|
416
|
+
*,
|
|
417
|
+
option_cls: type[ConfigOption] = ConfigOption,
|
|
418
|
+
**kwargs,
|
|
419
|
+
) -> None:
|
|
420
|
+
"""Add a ConfigOption to this ConfigManager.
|
|
421
|
+
|
|
422
|
+
Args:
|
|
423
|
+
option_cls: The class that should be instantiated. This class
|
|
424
|
+
should be a child class of ConfigOption. Mainly useful for cases
|
|
425
|
+
where the default ConfigOption needs to be extended, for example
|
|
426
|
+
if a new configuration option source needs to be supported.
|
|
427
|
+
"""
|
|
428
|
+
kwargs["_root_manager"] = self._root_manager
|
|
429
|
+
kwargs["_nest_path"] = self._nest_path
|
|
430
|
+
new_option = option_cls(
|
|
431
|
+
**kwargs,
|
|
432
|
+
)
|
|
433
|
+
self._check_child_conflict(new_option.name)
|
|
434
|
+
self._options[new_option.name] = new_option
|
|
435
|
+
|
|
436
|
+
def _check_child_conflict(self, name: str) -> None:
|
|
437
|
+
"""Check if a sub-manager, or ConfigOption conflicts with given name.
|
|
438
|
+
|
|
439
|
+
Args:
|
|
440
|
+
name: Name to check against children.
|
|
441
|
+
"""
|
|
442
|
+
if name in (self._options.keys() | self._sub_managers.keys()):
|
|
443
|
+
raise ConfigManagerError(
|
|
444
|
+
f"'{name}' sub-manager, or option conflicts with a child element of '{self.name}'"
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
def add_submanager(self, new_child: ConfigManager) -> None:
|
|
448
|
+
"""Nest another ConfigManager under this one.
|
|
449
|
+
|
|
450
|
+
This function recursively updates _nest_path and _root_manager of all
|
|
451
|
+
children under new_child.
|
|
452
|
+
|
|
453
|
+
Args:
|
|
454
|
+
new_child: The ConfigManager to be nested under the current one.
|
|
455
|
+
Notes:
|
|
456
|
+
We currently don't support re-nesting a ConfigManager. Only nest a
|
|
457
|
+
manager under another one once.
|
|
458
|
+
"""
|
|
459
|
+
self._check_child_conflict(new_child.name)
|
|
460
|
+
self._sub_managers[new_child.name] = new_child
|
|
461
|
+
|
|
462
|
+
def _root_setter_helper(node: ConfigManager):
|
|
463
|
+
# Deal with ConfigManagers
|
|
464
|
+
node._root_manager = self._root_manager
|
|
465
|
+
node._nest_path = self._nest_path + node._nest_path
|
|
466
|
+
for sub_manager in node._sub_managers.values():
|
|
467
|
+
_root_setter_helper(sub_manager)
|
|
468
|
+
# Deal with ConfigOptions
|
|
469
|
+
for option in node._options.values():
|
|
470
|
+
option._root_manager = self._root_manager
|
|
471
|
+
option._nest_path = self._nest_path + option._nest_path
|
|
472
|
+
|
|
473
|
+
_root_setter_helper(new_child)
|
|
474
|
+
|
|
475
|
+
def add_subparser(self, *args, **kwargs) -> None:
|
|
476
|
+
warnings.warn(
|
|
477
|
+
"add_subparser has been deprecated, use add_submanager instead",
|
|
478
|
+
DeprecationWarning,
|
|
479
|
+
stacklevel=2,
|
|
480
|
+
)
|
|
481
|
+
return self.add_submanager(*args, **kwargs)
|
|
482
|
+
|
|
483
|
+
def __getitem__(self, name: str) -> ConfigOption | ConfigManager:
|
|
484
|
+
"""Get either sub-manager, or option in this manager with name.
|
|
485
|
+
|
|
486
|
+
If an option is retrieved, we call get() on it to return its value instead.
|
|
487
|
+
|
|
488
|
+
Args:
|
|
489
|
+
name: Name to retrieve.
|
|
490
|
+
"""
|
|
491
|
+
if name in self._options:
|
|
492
|
+
return self._options[name].value()
|
|
493
|
+
if name not in self._sub_managers:
|
|
494
|
+
raise ConfigSourceError(
|
|
495
|
+
"No ConfigManager, or ConfigOption can be found"
|
|
496
|
+
f" with the name '{name}'"
|
|
497
|
+
)
|
|
498
|
+
return self._sub_managers[name]
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
CONFIG_MANAGER = ConfigManager(
|
|
502
|
+
name="CONFIG_MANAGER",
|
|
503
|
+
file_path=CONFIG_FILE,
|
|
504
|
+
_slices=[
|
|
505
|
+
ConfigSlice( # Optional connections file to read in connections from
|
|
506
|
+
CONNECTIONS_FILE,
|
|
507
|
+
ConfigSliceOptions(
|
|
508
|
+
check_permissions=True, # connections could live here, check permissions
|
|
509
|
+
),
|
|
510
|
+
"connections",
|
|
511
|
+
),
|
|
512
|
+
],
|
|
513
|
+
)
|
|
514
|
+
CONFIG_MANAGER.add_option(
|
|
515
|
+
name="connections",
|
|
516
|
+
parse_str=tomlkit.parse,
|
|
517
|
+
default=dict(),
|
|
518
|
+
)
|
|
519
|
+
CONFIG_MANAGER.add_option(
|
|
520
|
+
name="default_connection_name",
|
|
521
|
+
default="default",
|
|
522
|
+
)
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
def _get_default_connection_params() -> dict[str, Any]:
|
|
526
|
+
def_connection_name = CONFIG_MANAGER["default_connection_name"]
|
|
527
|
+
connections = CONFIG_MANAGER["connections"]
|
|
528
|
+
if def_connection_name not in connections:
|
|
529
|
+
raise Error(
|
|
530
|
+
f"Default connection with name '{def_connection_name}' "
|
|
531
|
+
"cannot be found, known ones are "
|
|
532
|
+
f"{list(connections.keys())}"
|
|
533
|
+
)
|
|
534
|
+
return {**connections[def_connection_name]}
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
def __getattr__(name):
|
|
538
|
+
if name == "CONFIG_PARSER":
|
|
539
|
+
warnings.warn(
|
|
540
|
+
"CONFIG_PARSER has been deprecated, use CONFIG_MANAGER instead",
|
|
541
|
+
DeprecationWarning,
|
|
542
|
+
stacklevel=2,
|
|
543
|
+
)
|
|
544
|
+
return CONFIG_MANAGER
|
|
545
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
__all__ = [ # noqa: F822
|
|
549
|
+
"ConfigOption",
|
|
550
|
+
"ConfigManager",
|
|
551
|
+
"CONFIG_MANAGER",
|
|
552
|
+
"CONFIG_PARSER",
|
|
553
|
+
]
|